GIT 875761f279482222ca385443102444fbe9fb0733 git+ssh://master.kernel.org/pub/scm/linux/kernel/git/linville/wireless-2.6.git#mm-master commit 934d49b0ffd1b3f9255ec08b169bebbe584369b9 Author: Daniel Drake Date: Sun Oct 7 16:23:33 2007 +0100 [PATCH] zd1211rw-mac80211: Add ID for TalkTalk SNU5630NS/05 Tested by Su-Jong You zd1211b chip 0471:1237 v4810 high 00-12-bf AL2230_RF pa0 g--N Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit 3b1ed734b2ab3085caa47a7be67c934a9f1be293 Author: Daniel Drake Date: Thu Oct 4 12:35:09 2007 +0100 [PATCH] zd1211rw-mac80211: More complete configure_filter implementation This patch extends the configure_filter implementation and fixes the structures around it. The mac80211 configure_filter work also clarified handling of our own mac address. We now program it in add_interface and remove it in remove_interface. We also no longer need to keep it in the zd_mac structure. With the recent mac80211 changes, has_monitor_interfaces() always returns FALSE because the driver is not directly told about monitor interfaces. I've removed this function and corrected the logic elsewhere. The sniffer is no longer enabled in monitor mode. This is somewhat guesswork, but I think by default we are given frames that have a bad FCS, and enabling the sniffer also passes us frames that have a bad PLCP checksum. However, when the sniffer is enabled, the device stops telling us which frames failed FCS checks, and also does not indicate which frames failed PLCP checks. So, it's best that we disable this functionality for now and only pass good and known-failed-FCS frames to the stack. Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit 7ed6c5d8c89b03c2f50f6f49dd53ce92eb4aa290 Author: John W. Linville Date: Tue Oct 2 19:56:29 2007 -0400 [PATCH] ath5k: remove reference to ALG_NONE Signed-off-by: John W. Linville commit ec9c21c66924ecc8bd15468ff987a404cc5073f2 Author: Ulrich Kunitz Date: Tue Oct 2 18:18:15 2007 +0100 [PATCH] zd1211rw-mac80211: return -EOPNOTSUPP from add_interface Match behaviour of other drivers by returning -EOPNOTSUPP rather than -1. Signed-off-by: Ulrich Kunitz Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit 9bb9c0a5d7b010532ce3263ca05bebbf3c33ef26 Author: Ulrich Kunitz Date: Tue Oct 2 18:18:04 2007 +0100 [PATCH] zd1211rw-mac80211: Add lock around struct zd_mac access Calls to zd_op_config_interface() are not synchronized, added therefore a lock around the access to the configuration variable. Signed-off-by: Ulrich Kunitz Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit 00c5068484c59d5e10d8f13c4224f919f48166fb Author: Ulrich Kunitz Date: Tue Oct 2 18:17:38 2007 +0100 [PATCH] zd1211rw-mac80211: Removed zd_util.c and zd_util.h The kernel now provides a generic hexdump implementation should we need it again, so we can remove it from zd1211rw. After removing that, only one single-user function is left in zd_util. Move that to zd_mac and remove zd_util. Signed-off-by: Ulrich Kunitz Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit 707ad8b19ef0bbea49898d537041423b0b5c7f26 Author: Michael Buesch Date: Sat Sep 29 12:23:20 2007 +0200 [PATCH] zd1211rw-mac80211: Make the LEDs warning debug-only This warning triggers now and then with an -ETIMEDOUT error. Though the LEDs work fine. Make this warning debug-only, as nobody is really interested in a LED failure that happens only now and then. If the LEDs don't work, one can easily enable debugging to see what's going on. This reduces dmesg spamming. Signed-off-by: Michael Buesch Acked-by: Daniel Drake Signed-off-by: John W. Linville commit ec9e5496191cf021c3b756f4aa924534c59fd55b Author: Johannes Berg Date: Fri Sep 28 14:01:25 2007 +0200 [PATCH] zd1211rw-mac80211: add "invalid" interface type Since I cannot convince the lazy driver authors (hello Michael) to stop (ab)using the MGMT interface type internally in their drivers, this patch introduces a new _INVALID type especially for their use and changes all affected drivers to use it. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit 75724a86bf11a5168a563c3e36a139befde7ac80 Author: Pavel Roskin Date: Sun Sep 30 14:41:25 2007 -0400 [PATCH] at76_usb: Add ID for at76c503a based CNETUSB611 Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit f166694e49d01173e149ddd1ddbe63c596784229 Author: Pavel Roskin Date: Sun Sep 30 14:41:18 2007 -0400 [PATCH] at76_usb: Bump version to 0.17 Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit a9451cf56b8d4ec7e173e065a7e8ca400b1b33a9 Author: Pavel Roskin Date: Sun Sep 30 14:41:12 2007 -0400 [PATCH] at76_usb: Use ETH_P_802_2 in monitor mode It's used by most other wireless drivers, including mac80211. ETH_P_80211_RAW is obsolete. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 50c5e2f51d6790d2e79b67c563b630bb06e9e924 Author: Pavel Roskin Date: Sun Sep 30 14:41:06 2007 -0400 [PATCH] at76_usb: Be more verbose on startup Promote some debug messages from at76_dbg() to unconditional KERN_DEBUG. Use dev_printk() before the device is registered, as "wlan%d" doesn't identify the device uniquely. Print firmware name and version when it's loaded from file. Print firmware version reported by the card. Print USB address and network device name on the same line to help identifying the device. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 615b27ceebf4b24de46dc4de349be89d637b48c1 Author: Pavel Roskin Date: Sun Sep 30 14:40:59 2007 -0400 [PATCH] at76_usb: Use dev_printk() where possible dev_printk() prints the USB device name in addition to the driver name, which makes it easier to understand the diagnostics in case of multiple devices. Use &interface->dev, as it's more specific and includes the driver name, but use &udev->dev if interface is not available. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 6b48e08e3108de3f67efffc901a4448da7927e85 Author: Pavel Roskin Date: Sun Sep 30 14:40:53 2007 -0400 [PATCH] at76_usb: Remove incorrect firmware version check The major version of the firmware reported by the device can mismatch the version of the originally loaded firmware, as it is the case for Belkin F5D6050. Remove this check and assume the firmware to be working as long that the external firmware download didn't fail and the firmware version could be read at all. Reported by Corey Pappas Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit f027d04661e6458720444f5d9021d6ff8889a6f1 Author: Pavel Roskin Date: Sun Sep 30 14:40:47 2007 -0400 [PATCH] at76_usb: Revert to network device names starting with "wlan" The change to the default "eth" in 0.16 was unnecessary and broke some setups. Reported by Mark Sansome Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit e6ffb45326746baba9ceb80d9b8d208660d72612 Author: Pavel Roskin Date: Sun Sep 30 14:40:40 2007 -0400 [PATCH] at76_usb: Add ID for Corega Wireless LAN USB-11 mini and mini2 Original patch by Weihua Yao Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit b39a9a54ade91802b27691293d1d3725e4361e52 Author: Pavel Roskin Date: Sun Sep 30 14:40:34 2007 -0400 [PATCH] at76_usb: Avoid dealing with milliseconds when possible, use jiffies Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 7ef745efc7ea2a20d23a86dc8d13f213f749d576 Author: Pavel Roskin Date: Sun Sep 30 14:40:27 2007 -0400 [PATCH] at76_usb: Move some parts from at76_usb.h to at76_usb.c Don't include anything from at76_usb.h. Keep DRIVER_NAME and DRIVER_VERSION together. Define at76_dbg() after DRIVER_NAME. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit d5e2f2fe37001bfd82abc8e93e34e61e21b68b92 Author: Pavel Roskin Date: Sun Sep 30 14:40:21 2007 -0400 [PATCH] at76_usb: Remove unneeded memset() calls Use kzalloc for bss table entries. priv is already zeroized, no need to do it again. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit a3a77550f4438cf0cab43609d7de28305065c4af Author: Pavel Roskin Date: Sun Sep 30 14:40:15 2007 -0400 [PATCH] at76_usb: Clean reserved tx area for mgmt frames like it's done for data Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit cd01ffb2e2de0a94f699b48b4e5a56f155674f1d Author: Pavel Roskin Date: Sun Sep 30 14:40:08 2007 -0400 [PATCH] at76_usb: Make it clear that management frames are sent at 1Mbps Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 0e26a4292c1dc7adc45e080f30b126a9a07dbc19 Author: Pavel Roskin Date: Sun Sep 30 14:40:02 2007 -0400 [PATCH] at76_usb: Eliminate variables used only in at76_dbg() Make it possible to disable at76_dbg() at the compile time without introducing any warnings. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 15a18c861b655d67711cd4974e2a551b2c2e5aea Author: Pavel Roskin Date: Sun Sep 30 14:39:55 2007 -0400 [PATCH] at76_usb: Use string precision to avoid line termination In particular, don't extend ESSID to 33 bytes. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 06e260461bb3c2c860939809e371c561d36fd06a Author: Pavel Roskin Date: Sun Sep 30 14:39:49 2007 -0400 [PATCH] at76_usb: Stop worrying about line termination in ethtool info The driver's responsibility is not to overwrite the buffer and not to leak kernel data. Line termination is the userspace responsibility. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit fe1768d2fe934a7287a8e668fcec37edf3c8e895 Author: Pavel Roskin Date: Sun Sep 30 14:39:42 2007 -0400 [PATCH] at76_usb: Rename tlv to ie for readability "ie" is the standard abbreviation for "information element". Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 2e57df9c2857d24efb57638235a629c01e025e51 Author: Pavel Roskin Date: Sun Sep 30 14:39:36 2007 -0400 [PATCH] at76_usb: Remove commented out code for multicast support If anyone cares to implement it, it should be started anew. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 90aa6ea99eedeb725700e58e7947db362185ae81 Author: Pavel Roskin Date: Sun Sep 30 14:39:30 2007 -0400 [PATCH] at76_usb: Eliminate err(), it's USB specific Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 4a4f2a074c2529222b63f61558dc541e0d853bfa Author: Pavel Roskin Date: Sun Sep 30 14:39:23 2007 -0400 [PATCH] at76_usb: Integrate at76_set_monitor_mode() into at76_dwork_restart() Reorder some statements in at76_dwork_restart() Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 62cd4759798e770a96f9bdb05298bf0e5546da1e Author: Pavel Roskin Date: Sun Sep 30 14:39:17 2007 -0400 [PATCH] at76_usb: Fix turning radio off The Atmel's driver uses command 7 to turn radio off. Also, CMD_GETOPMODE is bogus, remove it. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit e8b30a14ccbf32f08b3089e3cf3851864467b6c5 Author: Pavel Roskin Date: Sun Sep 30 14:39:11 2007 -0400 [PATCH] at76_usb: No need to reserve 72 bytes in set_mib_buffer - we only need 6 Use union for data in set_mib_buffer to guarantee minimally needed buffer length. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit b4a183b0cf1dcfd9e9f0ace0144af7f03ee8ef24 Author: Pavel Roskin Date: Sun Sep 30 14:39:04 2007 -0400 [PATCH] at76_usb: Don't clean priv->mib_buf, just clean data explicitly if needed priv->mib_buf.reserved is 0 when priv is allocated, and stays 0. When setting ibss_change, set data[0] to 0. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 755ff28640b81385200da13661980a99b3100e8f Author: Pavel Roskin Date: Sun Sep 30 14:38:58 2007 -0400 [PATCH] at76_usb: Avoid dubious use of '\0' Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 8470e259e044849fb208ad9deab0a7ecf8093776 Author: Pavel Roskin Date: Sun Sep 30 14:38:51 2007 -0400 [PATCH] at76_usb: Fix some device names in the device table Add vendor where it's missing. Use spelling preferred by the vendors. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit eb4a662a848a1d91b709fc14a1af6a4069bc8a5a Author: Pavel Roskin Date: Sun Sep 30 14:38:45 2007 -0400 [PATCH] at76_usb: Use USB_DEVICE_DATA to improve readability of the device table Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit a57ed07266a415101e043848689bf91b76a76b55 Author: Luis R. Rodriguez Date: Fri Sep 28 13:01:09 2007 -0400 [PATCH] zd1211rw-mac80211: add configure_filter() support Resending.. it seems my mail setup isn't quite right yet.. This patch: * Moves driver to new start() / stop() * Removes the old zd_op_set_multicast_list() * Adds the new configure_filter() support * Fixes compile errors as it depends on the removed DECLARE_MAC_BUF, and print_mac() This has been tested, and applies to the 'everything' branch of wireless-2.6. Cc: Daniel Drake Cc: Ulrich Kunitz Cc: Johannes Berg Cc: Michael Wu Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville commit ed03777f2d6de4e9e10befe71d25efaa0bfb8804 Author: David S. Miller Date: Wed Sep 19 14:33:58 2007 -0700 [PATCH] zd1211rw-mac80211: Fix build with debugging enabled. Some missed MAC_FMT conversions. Signed-off-by: David S. Miller commit a76109dfd9ac3b617581af9cf01161f3408ac0c6 Author: Michael Buesch Date: Wed Sep 19 13:10:35 2007 -0700 [PATCH] zd1211rw-mac80211: Fix TX status reports. Automatic rate scaling does not work on the zd-mac80211 driver. The driver does not properly report succeed and failed frames to mac80211. We need to indicate failed frames with the excessive_retries field (Should we also fake report some high retry count here?) Otherwise the rc algo is not able to scale down. Remove the conditional in REQ_TX_STATUS, as mac80211 handles that internally. Signed-off-by: Michael Buesch Signed-off-by: David S. Miller commit 29d6a10e1a0cfa7c80dcfd89eb179c78725281f5 Author: Daniel Drake Date: Wed Sep 19 10:32:24 2007 -0400 [PATCH] zd1211rw-mac80211: add mac80211-based driver for ZD1211 hardware Port to mac80211 infrastructure done by Michael Wu. Signed-off-by: Michael Wu Signed-off-by: Ulrich Kunitz Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit c6e51e580e58cfbb4d97cc4f4776cf879bd6d187 Author: Michael Buesch Date: Tue Sep 18 15:17:13 2007 -0400 [PATCH] usb: ssb-hosted OHCI driver Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 9deb02cc73be77080b48d8d0521c198706c524a3 Author: Luis R. Rodriguez Date: Fri Sep 28 11:26:16 2007 -0400 [PATCH] ath5k: Fix a few typos in copyright This patch fixes a few typos overseen by the introduction of two new files, intvals.c and phy.c. It also adds the missing warranty on base.c and removes the the last CVS tag from base.h Changes to base.[ch] Changes-licensed-under: 3-clause-BSD Changes to phy.c, initvals.c Changes-licensed-under: ISC Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville commit 5d0c48aeb45018dd2de2112c666dad1019070f36 Author: Luis R. Rodriguez Date: Thu Sep 27 21:04:19 2007 -0400 [PATCH] ath5k: add new configure_filter, compile fix on wireless-2.6 This patch: * removes the old ath_calcrxfilter() and ath_set_multicast_list() * adds new required required configure_filter() ops, this one just stores the filter value in cache for later use. * introduces a cached sc->filter_flags for hw filter flags * moves the driver to mac80211's new required start()/stop() * initializes at add_interface() sc->bintval to a common value, this will later be updated as per mac80211's preference. * Fix compile bug on ath_set_key() (adds enum for set_key_cmd) We'll later port some driver-specific filter stuff onto mac80211. This has been tested. This patch applies to the wireless-2.6 everything branch, after the new ath5k directory move and file renames. Changes-licensed-under: 3-clause-BSD Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville commit 08877c0cdbbec4431cbd1d517b224ab010c43c9b Author: Bradley M. Kuhn Date: Thu Sep 27 18:58:24 2007 -0400 [PATCH] ath5k: final correction of copyright notices The patch below is for application to the wireless-2.6 ath5k branch, and takes into account the latest driver directory move and file renames. I believe this patch handles all copyright notice issues that SFLC is currently aware of. Based on our extensive research (which is summarized in http://www.softwarefreedom.org/resources/2007/ath5k-code-analysis.html) we found that the copyright holders below also have a copyright on some files; the patch below adds their copyright notices. We encourage future patchers to add their copyright notice (and any appropriate additional licensing notices as needed) into the files when they make patches. We also refer developers to our paper at http://www.softwarefreedom.org/resources/2007/gpl-non-gpl-collaboration.html that recommends ways to incorporate permissive-licensed code like this in a GPL-licensed project like Linux. We at SFLC were all very happy to do this work for the Linux Wireless team and are glad that a favorable outcome for all was reached. I believe I've used all the patch-submission tags correctly based on previous discussions and email exchanges with everyone involved, but I obviously don't patch the kernel often (er, at all :), so if I've made a mistake, please let me know. Changes-licensed-under: ISC Signed-off-by: Richard Fontana Signed-off-by: Bradley M. Kuhn Signed-off-by: Matt Norwood Signed-off-by: Karen Sandler Acked-by: Matthew W. S. Bell Acked-by: Pavel Roskin Acked-by: Nick Kossifidis Acked-by: Jiri Slaby Acked-by: Luis Rodriguez Acked-by: Michael Taylor Signed-off-by: John W. Linville commit de50b363e8b429a0cdd11e29dc564391103afbb0 Author: Andrew Morton Date: Thu Sep 20 14:02:31 2007 -0700 [PATCH] ath5k: Remove pointless empty CVS ID string. Cc: Jeff Garzik Cc: Jiri Slaby Signed-off-by: Andrew Morton Signed-off-by: John W. Linville commit 4b4d8d579a6f317985bb9fab947c9cb3be75b761 Author: Stephen Hemminger Date: Tue Sep 18 10:06:47 2007 -0700 [PATCH] ath5k: msi support The atheros on my Fujitsu b6210 supports MSI. Signed-off-by: Stephen Hemminger Signed-off-by: John W. Linville commit a49855f50f2d9108971796b0e8607e5c75530d3c Author: Luis R. Rodriguez Date: Thu Sep 20 17:13:26 2007 -0400 [PATCH] ath5k - Fix filters for AR5212, correct return values for WEP I was not get getting DHCP replies back with ath5k on a AR5212. Turns out out of all filter flags if I enable AR5K_RX_FILTER_PROM I get my replies back. I've tested enabling AR5K_RX_FILTER_BCAST and AR5K_RX_FILTER_MCAST but AR5K_RX_FILTER_PROM only does the trick. This may fix this for other cards if you are not getting DHCP replies please let us know. For now we can enable AR5K_RX_FILTER_PROM on STA and Ad-hoc only for AR5212 until we sort out the filter flags properly. This patch also takes into account new changes to mac80211 for ieee80211_ops's set_key(). Filter API changes to come soon. In summary this patch has these changes: * AR5212 now receives broadcasts (DHCP works now) * ath5k_hw_set_key() was checking against key table size against key->keyid -- this can only be 0, 1, 2 or 3. Check against key->keylen and divide the table size by 8. * return proper values for WEP setting as per mac80211 documenation This patch applies to the ath5k branch of wireless-dev. Changes to ath5k_base.c Changes-licensed-under: 3-clause-BSD Changes to ath5k_hw.c, ath5k_reg.h Changes-licensed-under: ISC Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville commit 7c6db582d195fe52648cfc070358fffa273cdb09 Author: Jiri Slaby Date: Wed Sep 26 14:04:10 2007 -0400 [PATCH] Net: ath5k, remove unused/dead code ath5k, remove unused/dead code Remove ifdeffed code, which is currently (and might not be in the future too) unused. If it was ever really needed, it could be easily ported back from the madwifi-old-openhal repository. Signed-off-by: Jiri Slaby Cc: Luis R. Rodriguez Signed-off-by: John W. Linville commit a736fa769ff7de817b4a76838a4285d83425f626 Author: Jiri Slaby Date: Wed Sep 26 14:03:27 2007 -0400 [PATCH] Net: ath5k, use int as retval ath5k, use int as retval Changes-licensed-under: ISC Signed-off-by: Jiri Slaby Cc: Luis R. Rodriguez Signed-off-by: John W. Linville commit 55c7293d7aae2d2c6a8c66f2e4a1e01b4eedc576 Author: Nick Kossifidis Date: Wed Sep 26 14:02:50 2007 -0400 [PATCH] Net: ath5k, split hw into hw, phy and initvals ath5k, split hw into hw, phy and initvals Separate the hw code into logical pieces hw, phy and initvals for better readability and maintainability. This includes suggested file renames (_hw removal). Changes-licensed-under: ISC Signed-off-by: Nick Kossifidis Signed-off-by: Jiri Slaby Cc: Luis R. Rodriguez Signed-off-by: John W. Linville commit 3341123aa77a5d1889c9f2045906596efb4fe815 Author: Jiri Slaby Date: Wed Sep 26 14:02:11 2007 -0400 [PATCH] Net: ath5k, make its own directory ath5k, make its own directory also remove ath5_ prefix from the filenames Changes-licensed-under: ISC Signed-off-by: Jiri Slaby Cc: Luis R. Rodriguez Signed-off-by: John W. Linville commit 366266df4c66edb01d608390f6f7d46bcb41b086 Author: Luis R. Rodriguez Date: Mon Sep 17 17:31:51 2007 -0400 [PATCH] ath5k: Internalize Atheros Turbo modes This patch: * Internalizes Atheros Turbo modes (MODE_ATHEROS_TURBO, MODE_ATHEROS_TURBOG) * Internatizes Turbo modulation (MODULATION_TURBO) * Adds documention for Atheros Turbo modes (MODE_ATHEROS_TURBO, MODE_ATHEROS_TURBOG), Turbo modulation (MODULATION_TURBO) and XR modulation (MODULATION_XR) Changes-licensed-under: BSD Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville commit 74f50de692a6596690e91384770b60b5568ef449 Author: John W. Linville Date: Mon Sep 17 17:11:29 2007 -0400 [PATCH] ath5k: remove HW_KEY_IDX_INVALID Signed-off-by: John W. Linville commit 8e617aad8033a2349938465a05b56079119d9762 Author: Luis R. Rodriguez Date: Mon Sep 17 00:48:27 2007 -0400 [PATCH] Correct ath5k Kconfig depends This patch correct's ath5k's Kconfig depends line by making it depend on WLAN_80211 and also for now marks it EXPERIMENTAL. This patch applies to the ath5k branch of wireless-dev. Signed-off-by: Luis R. Rodriguez commit 178861b790c8f26a72c2f5ba7f024e353fd087d4 Author: Luis R. Rodriguez Date: Mon Sep 17 00:01:09 2007 -0400 [PATCH] Add Luis to MAINTAINERS for ath5k This patch adds me to the MAINTAINERS entry for ath5k. It applies to the ath5k branch on wireless-dev. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville commit 43395800ab8a7fba9d75c25f746a7c07b0151407 Author: Krzysztof Halasa Date: Mon Sep 3 20:35:52 2007 +0200 [PATCH] Ath5k panic fix Ath5k panics on ath_open() because sc->pdev is never set, fixed. Signed-off-by: Krzysztof Halasa Signed-off-by: John W. Linville commit df42c535877e95bb8057006caa402e38a9f52e81 Author: John W. Linville Date: Sat Sep 15 11:05:32 2007 -0400 [PATCH] ath5k: remove use of IEEE80211_HW_WEP_INCLUDE_IV Signed-off-by: John W. Linville commit 512570b4e5eb78c424304b36b31b4ad4057be7ef Author: Luis R. Rodriguez Date: Mon Sep 10 16:00:38 2007 -0400 [PATCH] ath5k: more license corrections (ath5k_reg.h) This patch corrects the copyright and license on ath5k_reg.h. Specifically, this patch: * Removes the GPL license and restores the ISC license from ath5k_reg.h as Nick indicated. * Adds Reyk's Copyright to ath5k_reg.h Changes-licensed-under: ISC Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville commit 74b0fd15c71eece4fafa8ab876a80229a273df2a Author: Luis R. Rodriguez Date: Thu Sep 6 10:18:22 2007 -0400 [PATCH] ath5k: correct some license damage The attached patch fixes copyright notice for 3 files which need to be corrected regardless of the outcome of the license of the other files. This patch: * Adds warranty notices back to ath5k_regdom.[ch] * Adds Devicescape Copyright to ath5k_base.c Signed-off-by: Luis R. Rodriguez Changes-licensed-under: ISC Signed-off-by: John W. Linville commit c14692dbd6aa796703af95eff4c61b0cdda53720 Author: Jiri Slaby Date: Tue Aug 28 11:59:54 2007 -0400 [PATCH] Net: ath5k, switch to ioread/iowrite ath5k, switch to ioread/iowrite Do not use readl/writel, since iomap retval is platform dependent and needn't be virtual address awaited by readl/writel. Signed-off-by: Jiri Slaby Signed-off-by: John W. Linville commit f55ecef744fe76b7145ed2ec457dbf3db237c164 Author: Jiri Slaby Date: Sat Aug 25 03:59:28 2007 -0400 [PATCH] Net: ath5k, remove some ieee80211 re-defines ath5k, remove some ieee80211 re-defines use mac80211 defines directly instead. this means MODULATION_* to IEEE80211_RATE_* switch. Signed-off-by: Jiri Slaby Cc: Signed-off-by: John W. Linville commit 1c9adbeda0abb21469185a5b2b8645f1677a6de6 Author: Jiri Slaby Date: Sat Aug 25 03:58:47 2007 -0400 [PATCH] Net: ath5k, use short preamble for some rates ath5k, use short preamble for some rates 2, 5.5 and 11 in b/g are now in short preamble mode Jiri Slaby wrote: > Johannes Berg wrote: > > umm, mac80211 needs to be able to choose depending on the network. Hmm, misleading log comment. It should be 'can now be in SP mode'. Signed-off-by: Jiri Slaby Cc: Signed-off-by: John W. Linville commit e09c0cb9abbfff5cbfd3606c2ce29c1decb75f56 Author: Jiri Slaby Date: Sat Aug 25 03:51:51 2007 -0400 [PATCH] Net: ath5k, initial write cleanup ath5k, initial write cleanup The final step of initial writing cleanup. ar5211_rf is going away. Signed-off-by: Jiri Slaby Cc: Signed-off-by: John W. Linville commit 8d266a21d1d904882a62b033bb1cf259389231e0 Author: Jiri Slaby Date: Sat Aug 25 03:57:26 2007 -0400 [PATCH] Net: ath5k, comment some EEPROM registers ath5k, comment some EEPROM registers make some registers meaning clear Signed-off-by: Jiri Slaby Cc: Signed-off-by: John W. Linville commit 4d2fef63597ede5952119d942b65ef880e9960a2 Author: John W. Linville Date: Thu Aug 23 15:23:30 2007 -0400 [PATCH] ath5k: fix build breakage from recent mac80211 changes Signed-off-by: John W. Linville commit 7a0e04e86efee9abd76fe80ebf8f709c86c8e5cd Author: Jiri Slaby Date: Wed Aug 22 22:54:07 2007 +0200 [PATCH] Net: ath5k, remove sysctls ath5k, remove sysctls Syscalls were buggy and defunct in later kernels (due to sysctl check). Signed-off-by: Jiri Slaby Signed-off-by: John W. Linville commit 0658473f185844150d65e5ed73d9443d50c1443b Author: Jiri Slaby Date: Wed Aug 22 22:53:25 2007 +0200 [PATCH] Net: ath5k, no printk after STA->IBSS switch ath5k, no printk after STA->IBSS switch If STA->IBSS switch was done, but beacon_update weas not called so far, do not emit a warning about non-existent skbuf. Signed-off-by: Jiri Slaby Signed-off-by: John W. Linville commit b8e8efa1988fafb88ad38feb9888975bf6130fbb Author: akpm@linux-foundation.org Date: Tue Aug 21 16:53:53 2007 -0700 [PATCH] ath5k: needs PCI drivers/net/wireless/ath5k_base.c: In function `ath_pci_probe': drivers/net/wireless/ath5k_base.c:2279: error: implicit declaration of function `pci_request_region' drivers/net/wireless/ath5k_base.c:2359: error: implicit declaration of function `pci_release_region' Cc: Jiri Slaby Signed-off-by: Andrew Morton Signed-off-by: John W. Linville commit 26d476d29bc182e0b0cda20f31ea4658cfc1e8c0 Author: akpm@linux-foundation.org Date: Tue Aug 21 00:51:20 2007 -0700 [PATCH] ath5k: kconfig fix It needs stuff from net/mac80211/ieee80211.c Cc: Jiri Slaby Signed-off-by: Andrew Morton Signed-off-by: John W. Linville commit e0cc717df74c822a74fb1ebb543e23c76faf5228 Author: akpm@linux-foundation.org Date: Tue Aug 21 00:42:32 2007 -0700 [PATCH] ath5k: printk fix drivers/net/wireless/ath5k_base.c: In function 'ath_txq_setup': drivers/net/wireless/ath5k_base.c:2033: warning: format '%u' expects type 'unsigned int', but argument 3 has type 'long unsigned int' Cc: Jiri Slaby Signed-off-by: Andrew Morton Signed-off-by: John W. Linville commit e2da439bda7d27027f03f2e29964fb207ddc3c07 Author: Jiri Slaby Date: Sun Aug 12 17:33:16 2007 +0200 [PATCH] Net: add ath5k wireless driver add ath5k wireless driver Signed-off-by: Jiri Slaby Signed-off-by: John W. Linville commit e1d4f02abfde9dd92043b8183fa0c059005c1c43 Author: Pavel Roskin Date: Sat Sep 1 00:37:34 2007 -0400 [PATCH] at76_usb: Replace scan_runs with scan_need_any It's more meaningful than a number. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit f1ed02885b9a1302f02a7bb3139ea805707be0b9 Author: Pavel Roskin Date: Sat Sep 1 00:37:29 2007 -0400 [PATCH] at76_usb: Don't do additional MIB dumps if DEBUG is defined They are already printed from at76_startup_device(), which should be enough. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 6a34c483850de1271db8f0abe53d6825aa1092ce Author: Pavel Roskin Date: Sat Sep 1 00:37:23 2007 -0400 [PATCH] at76_usb: Remove international roaming support It's broken beyond repair and legally dubious. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 62a072a880fdd1032e24f287584b259b3507531a Author: Pavel Roskin Date: Sat Sep 1 00:37:17 2007 -0400 [PATCH] at76_usb: Massive cleanup of dump functions Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 138e229ca562fe21b3149815d06534b27f67faad Author: Pavel Roskin Date: Sat Sep 1 00:37:11 2007 -0400 [PATCH] at76_usb: Simplify logic in at76_is_hidden_ssid() Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit ce3422c6760063f90f3ecd8faf33a0ee814d4abf Author: Pavel Roskin Date: Sat Sep 1 00:37:06 2007 -0400 [PATCH] at76_usb: Eliminate pr_debug() in favor of at76_dbg() Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 4b60fe90eb7b952315ff79223ca514657a1ecde5 Author: Pavel Roskin Date: Sat Sep 1 00:37:00 2007 -0400 [PATCH] at76_usb: Eliminate at76_dbg_dumpbuf() in favor of hex2str() Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit cecc5c4bf796c1773632f1d35111898580b2c31f Author: Pavel Roskin Date: Sat Sep 1 00:36:54 2007 -0400 [PATCH] at76_usb: Convert dbg() to at76_dbg() or remove it Using two debug macros is inconsistent. Besides, dbg() only prints something if DEBUG is defined. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 64d72722d5f1880c31fa5aa755ac24e48f9bbf9e Author: Pavel Roskin Date: Sat Sep 1 00:36:49 2007 -0400 [PATCH] at76_usb: Remove unneeded braces, found by checkpatch.pl Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 9616d8698d241c629b85f66dddd4ac90521fdd73 Author: Pavel Roskin Date: Sat Sep 1 00:36:43 2007 -0400 [PATCH] at76_usb: Improve dump of MAC_ADDR Don't require DEBUG to be defined. Dump each group address separately, next to the status. Rename MIB_MAC_ADD to MIB_MAC_ADDR. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit dd61c51db8d13c3523c147070523c235128657a8 Author: Pavel Roskin Date: Sat Sep 1 00:36:37 2007 -0400 [PATCH] at76_usb: Don't dump mib_mdomain while scanning, it's done on device startup Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 29c02a1ad890dfff58086d4de78a099b142ec39d Author: Pavel Roskin Date: Sat Sep 1 00:36:31 2007 -0400 [PATCH] at76_usb: Do implicit scanning only with current ESSID This should speed up association. We don't need non-matching APs if ESSID is set. For user-requested scanning, scan for any essid after that. For international scanning, start with scan_runs 1, as it was meant to be done. Actually use ir_step 0 for international scanning. Adjust debug messages correspondingly. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 615391d6ddc5b8695d6ec11bd82bdd34d4dc78ba Author: Pavel Roskin Date: Sat Sep 1 00:36:26 2007 -0400 [PATCH] at76_usb: Simplify at76_get_mib_mdomain() The "error" label is not needed. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 6b397f82ae645352d1d4918e3f158af990effb85 Author: Pavel Roskin Date: Sat Sep 1 00:36:20 2007 -0400 [PATCH] at76_usb: Only retry resubmitting rx_urb once Other drivers don't even bother resubmitting it with GFP_KERNEL. If it fails with GFP_KERNEL, we have a problem, and restarting the task is not likely to be helpful. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit f98e14433f2ee35e30f55f92b68c5c9d182b97aa Author: Pavel Roskin Date: Sat Sep 1 00:36:14 2007 -0400 [PATCH] at76_usb: Eliminate vendor IDs They are useless next to comments with full device names. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 154ecd11473066dc01eb4c911aaa7a471afab374 Author: Pavel Roskin Date: Sat Sep 1 00:36:09 2007 -0400 [PATCH] at76_usb: Protect at76_iw_handler_set_scan() with mutex This prevents a race condition when a response from an AP comes before we set state to MAC_SCANNING, but after at76_quiesce(). Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit f2835b25679327863c54bc8c5390c12bef1c199b Author: Pavel Roskin Date: Sat Sep 1 00:36:03 2007 -0400 [PATCH] at76_usb: Improve output of the regdomain id Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 5e7d14329b25b97a45ef9f7dc6bd6fe038a1e257 Author: Pavel Roskin Date: Sat Sep 1 00:35:57 2007 -0400 [PATCH] at76_usb: Only cancel correct timeouts for Auth and Assoc replies Don't cancel all delayed works indiscriminately. In case of at76_rx_mgmt_auth(), cancel the timeout regardless of the status of the reply, as long as the reply is for us. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 6c7f43826ba0798dadefb76cdcab217511899054 Author: Pavel Roskin Date: Sat Sep 1 00:35:52 2007 -0400 [PATCH] at76_usb: Start beacon timeout task when connected Limit beacon timeout task to the managed mode. Don't warn of the wrong mode or state, just exit. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 82074aefed229f4a73fecfd75abdddb44ead0e31 Author: Pavel Roskin Date: Sat Sep 1 00:35:46 2007 -0400 [PATCH] at76_usb: Don't disable and enable tasklets, it doesn't work as expected Bringing the interface down causes high CPU utilization. Correct fix should probably disable rx URB, not only the rx tasklet. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 1d315c786a0198ef929885ae36591571e85972b9 Author: Pavel Roskin Date: Sat Sep 1 00:35:40 2007 -0400 [PATCH] at76_usb: Introduce at76_quiesce(), use it to stop network activity Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 2b8b6b19981336929b424bb92f5102b39399bc4a Author: Pavel Roskin Date: Sat Sep 1 00:35:34 2007 -0400 [PATCH] at76_usb: Fix incorrect queue management in at76_tx_mgmt() The old pending buffer should be freed, not the new one. No need to set txbuf to NULL. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 36c5a6e51b5049d2359de381ca4344132696c95f Author: Pavel Roskin Date: Sat Sep 1 00:35:29 2007 -0400 [PATCH] at76_usb: Rename some long functions and fields Mostly try to avoid "bulk", which is a minor technical detail. Also avoid read/write in favor of more common rx/tx. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 140aaba60a3276e853f71b7eb66a7f688b511af6 Author: Pavel Roskin Date: Sat Sep 1 00:35:23 2007 -0400 [PATCH] at76_usb: Fix hex2str() and mac2str() to avoid buffer overlap Declarations for two-dimensional arrays were written backwards. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 883f4f00dba021c8c391b2b20603492a861df86d Author: Pavel Roskin Date: Sat Sep 1 00:35:17 2007 -0400 [PATCH] at76_usb: Simplify logic in at76_get_reg_domain() Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 61d1ac018a4813e22fd71e26e15f23c9751aeb10 Author: Pavel Roskin Date: Sat Sep 1 00:35:11 2007 -0400 [PATCH] at76_usb: Add minimal support to 505AMX Use inline functions for board classification. Drop "_2958" from BOARD_505A_2958, it's implied. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 9b130002739d567bcecc882a0684b8a21eaf14b5 Author: Pavel Roskin Date: Sat Sep 1 00:35:06 2007 -0400 [PATCH] at76_usb: Simplify at76_usbdfu_download() Make it look similar to at76_load_external_fw() Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 88a0886b493220b19f34dfc54e6a4f95136e5fea Author: Pavel Roskin Date: Sat Sep 1 00:35:00 2007 -0400 [PATCH] at76_usb: Merge at76_download_external_fw() into at76_load_external_fw() Simplify logic to ensure that the last block is empty. Unify DFU_PACKETSIZE and EXT_FW_BLOCK_SIZE into FW_BLOCK_SIZE. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit ec501300e04f8113e41767ff8cb793a37ecc745e Author: Pavel Roskin Date: Sat Sep 1 00:34:54 2007 -0400 [PATCH] at76_usb: Don't use shift on numeric constants in usb_control_msg() arguments We don't know the meaning of 0x0a02 and 0x0902 in at76_get_hw_cfg() and at76_get_hw_cfg_intersil() respectively, so don't pretend that we do. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 882d12d6bfecfc8b1f48df6c4f74cd047b96095c Author: Pavel Roskin Date: Sat Sep 1 00:34:49 2007 -0400 [PATCH] at76_usb: Move (de)initialization functions closer to the end of file Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 76545302e02f90a871b87d1f1453b1b1f0bbca02 Author: Pavel Roskin Date: Sat Sep 1 00:34:43 2007 -0400 [PATCH] at76_usb: Add myself to the author list Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 86cb0cca95916994c97402b37a2d2b06facadf34 Author: Pavel Roskin Date: Sat Sep 1 00:34:37 2007 -0400 [PATCH] at76_usb: Avoid overuse of NULL Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 48ceb86bbcd74b620c2f4ddbe0cd00d641431afa Author: Pavel Roskin Date: Sat Sep 1 00:34:31 2007 -0400 [PATCH] at76_usb: Rewrite at76_alloc_urbs() in a more linear fashion Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit a3e233571a1b6b53676dffa73311a0783786e8a1 Author: Pavel Roskin Date: Sat Sep 1 00:34:26 2007 -0400 [PATCH] at76_usb: Use existing macros to find bulk in and bulk out endpoints Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit b50b9624f610cecf3f03a02e7e45499400ff235a Author: Pavel Roskin Date: Sat Sep 1 00:34:20 2007 -0400 [PATCH] at76_usb: Protect at76_get_op_mode() and at76_get_mib() against short reads Don't rely on usb_control_msg() returning -EPIPE in absence of working firmware. In some cases, no error is returned, but no data is read. Interpret short reads as an error, return -EIO. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 302151c9c6deadaab7f71fcbae655f489c0b4fa4 Author: Pavel Roskin Date: Thu Aug 23 14:40:17 2007 -0400 [PATCH] Add at76_usb driver This driver supports USB devices using Atmel at76c503, at76c505 and at76c505a chipsets. Known devices with this chip are Belkin F5D6050, Dynalink/Askey WLL013, Linksys WUSB11 v2.6, Netgear MA101B and many more. All supported devices require firmware, which can be downloaded from http://developer.berlios.de/projects/at76c503a/ Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville Signed-off-by: Andrew Morton --- MAINTAINERS | 27 drivers/net/wireless/Kconfig | 8 drivers/net/wireless/at76_usb.c | 5556 ++++++++++ drivers/net/wireless/at76_usb.h | 619 + drivers/net/wireless/ath5k/Makefile | 2 drivers/net/wireless/ath5k/ath5k.h | 1086 + drivers/net/wireless/ath5k/base.c | 2579 ++++ drivers/net/wireless/ath5k/base.h | 207 drivers/net/wireless/ath5k/hw.c | 4322 +++++++ drivers/net/wireless/ath5k/hw.h | 588 + drivers/net/wireless/ath5k/initvals.c | 1102 + drivers/net/wireless/ath5k/phy.c | 1686 +++ drivers/net/wireless/ath5k/reg.h | 1978 +++ drivers/net/wireless/ath5k/regdom.c | 121 drivers/net/wireless/ath5k/regdom.h | 500 drivers/net/wireless/zd1211rw-mac80211/Kconfig | 18 drivers/net/wireless/zd1211rw-mac80211/Makefile | 10 drivers/net/wireless/zd1211rw-mac80211/zd_chip.c | 1622 ++ drivers/net/wireless/zd1211rw-mac80211/zd_chip.h | 943 + drivers/net/wireless/zd1211rw-mac80211/zd_def.h | 57 drivers/net/wireless/zd1211rw-mac80211/zd_ieee80211.c | 100 drivers/net/wireless/zd1211rw-mac80211/zd_ieee80211.h | 75 drivers/net/wireless/zd1211rw-mac80211/zd_mac.c | 978 + drivers/net/wireless/zd1211rw-mac80211/zd_mac.h | 238 drivers/net/wireless/zd1211rw-mac80211/zd_rf.c | 178 drivers/net/wireless/zd1211rw-mac80211/zd_rf.h | 108 drivers/net/wireless/zd1211rw-mac80211/zd_rf_al2230.c | 439 drivers/net/wireless/zd1211rw-mac80211/zd_rf_al7230b.c | 492 drivers/net/wireless/zd1211rw-mac80211/zd_rf_rf2959.c | 279 drivers/net/wireless/zd1211rw-mac80211/zd_rf_uw2453.c | 534 drivers/net/wireless/zd1211rw-mac80211/zd_usb.c | 1528 ++ drivers/net/wireless/zd1211rw-mac80211/zd_usb.h | 263 drivers/usb/host/Kconfig | 13 drivers/usb/host/ohci-hcd.c | 21 drivers/usb/host/ohci-ssb.c | 254 35 files changed, 28530 insertions(+), 1 deletion(-) diff -puN MAINTAINERS~git-wireless MAINTAINERS --- a/MAINTAINERS~git-wireless +++ a/MAINTAINERS @@ -648,6 +648,14 @@ M: ecashin@coraid.com W: http://www.coraid.com/support/linux S: Supported +ATHEROS ATH5K WIRELESS DRIVER +P: Jiri Slaby +M: jirislaby@gmail.com +P: Luis R. Rodriguez +M: mcgrof@gmail.com +L: linux-wireless@vger.kernel.org +S: Maintained + ATL1 ETHERNET DRIVER P: Jay Cliburn M: jcliburn@gmail.com @@ -691,6 +699,15 @@ W: http://www.thekelleys.org.uk/atmel W: http://atmelwlandriver.sourceforge.net/ S: Maintained +ATMEL USB WIRELESS DRIVER +P: Pavel Roskin +M: proski@gnu.org +L: linux-wireless@vger.kernel.org +L: at76c503a-user@lists.berlios.de +L: at76c503a-develop@lists.berlios.de +W: http://at76c503a.berlios.de/ +S: Maintained + AUDIT SUBSYSTEM P: David Woodhouse M: dwmw2@infradead.org @@ -4331,6 +4348,16 @@ W: http://www.qsl.net/dl1bke/ L: linux-hams@vger.kernel.org S: Maintained +ZD1211-MAC80211 WIRELESS DRIVER +P: Daniel Drake +M: dsd@gentoo.org +P: Ulrich Kunitz +M: kune@deine-taler.de +W: http://zd1211.ath.cx/wiki/DriverRewrite +L: linux-wireless@vger.kernel.org +L: zd1211-devs@lists.sourceforge.net (subscribers-only) +S: Maintained + ZD1211RW WIRELESS DRIVER P: Daniel Drake M: dsd@gentoo.org diff -puN drivers/net/wireless/Kconfig~git-wireless drivers/net/wireless/Kconfig --- a/drivers/net/wireless/Kconfig~git-wireless +++ a/drivers/net/wireless/Kconfig @@ -381,6 +381,14 @@ config PCI_HERMES common. Some of the built-in wireless adaptors in laptops are of this variety. +config USB_ATMEL + tristate "Atmel at76c503/at76c505/at76c505a USB cards" + depends on WLAN_80211 && USB + select FW_LOADER + ---help--- + Enable support for USB Wireless devices using Atmel at76c503, + at76c505 or at76c505a chips. + config PCMCIA_HERMES tristate "Hermes PCMCIA card support" depends on PCMCIA && HERMES diff -puN drivers/net/wireless/Makefile~git-wireless drivers/net/wireless/Makefile diff -puN /dev/null drivers/net/wireless/at76_usb.c --- /dev/null +++ a/drivers/net/wireless/at76_usb.c @@ -0,0 +1,5556 @@ +/* + * at76c503/at76c505 USB driver + * + * Copyright (c) 2002 - 2003 Oliver Kurth + * Copyright (c) 2004 Joerg Albert + * Copyright (c) 2004 Nick Jones + * Copyright (c) 2004 Balint Seeber + * Copyright (c) 2007 Guido Guenther + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This file is part of the Berlios driver for WLAN USB devices based on the + * Atmel AT76C503A/505/505A. + * + * Some iw_handler code was taken from airo.c, (C) 1999 Benjamin Reed + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "at76_usb.h" + +/* Version information */ +#define DRIVER_NAME "at76_usb" +#define DRIVER_VERSION "0.17" +#define DRIVER_DESC "Atmel at76x USB Wireless LAN Driver" + +/* at76_debug bits */ +#define DBG_PROGRESS 0x00000001 /* authentication/accociation */ +#define DBG_BSS_TABLE 0x00000002 /* show BSS table after scans */ +#define DBG_IOCTL 0x00000004 /* ioctl calls / settings */ +#define DBG_MAC_STATE 0x00000008 /* MAC state transitions */ +#define DBG_TX_DATA 0x00000010 /* tx header */ +#define DBG_TX_DATA_CONTENT 0x00000020 /* tx content */ +#define DBG_TX_MGMT 0x00000040 /* tx management */ +#define DBG_RX_DATA 0x00000080 /* rx data header */ +#define DBG_RX_DATA_CONTENT 0x00000100 /* rx data content */ +#define DBG_RX_MGMT 0x00000200 /* rx mgmt frame headers */ +#define DBG_RX_BEACON 0x00000400 /* rx beacon */ +#define DBG_RX_CTRL 0x00000800 /* rx control */ +#define DBG_RX_MGMT_CONTENT 0x00001000 /* rx mgmt content */ +#define DBG_RX_FRAGS 0x00002000 /* rx data fragment handling */ +#define DBG_DEVSTART 0x00004000 /* fw download, device start */ +#define DBG_URB 0x00008000 /* rx urb status, ... */ +#define DBG_RX_ATMEL_HDR 0x00010000 /* Atmel-specific Rx headers */ +#define DBG_PROC_ENTRY 0x00020000 /* procedure entries/exits */ +#define DBG_PM 0x00040000 /* power management settings */ +#define DBG_BSS_MATCH 0x00080000 /* BSS match failures */ +#define DBG_PARAMS 0x00100000 /* show configured parameters */ +#define DBG_WAIT_COMPLETE 0x00200000 /* command completion */ +#define DBG_RX_FRAGS_SKB 0x00400000 /* skb header of Rx fragments */ +#define DBG_BSS_TABLE_RM 0x00800000 /* purging bss table entries */ +#define DBG_MONITOR_MODE 0x01000000 /* monitor mode */ +#define DBG_MIB 0x02000000 /* dump all MIBs on startup */ +#define DBG_MGMT_TIMER 0x04000000 /* dump mgmt_timer ops */ +#define DBG_WE_EVENTS 0x08000000 /* dump wireless events */ +#define DBG_FW 0x10000000 /* firmware download */ +#define DBG_DFU 0x20000000 /* device firmware upgrade */ + +#define DBG_DEFAULTS 0 + +/* Use our own dbg macro */ +#define at76_dbg(bits, format, arg...) \ + do { \ + if (at76_debug & (bits)) \ + printk(KERN_DEBUG DRIVER_NAME ": " format "\n" , ## arg); \ + } while (0) + +static int at76_debug = DBG_DEFAULTS; + +/* Protect against concurrent firmware loading and parsing */ +static struct mutex fw_mutex; + +static struct fwentry firmwares[] = { + [0] = {""}, + [BOARD_503_ISL3861] = {"atmel_at76c503-i3861.bin"}, + [BOARD_503_ISL3863] = {"atmel_at76c503-i3863.bin"}, + [BOARD_503] = {"atmel_at76c503-rfmd.bin"}, + [BOARD_503_ACC] = {"atmel_at76c503-rfmd-acc.bin"}, + [BOARD_505] = {"atmel_at76c505-rfmd.bin"}, + [BOARD_505_2958] = {"atmel_at76c505-rfmd2958.bin"}, + [BOARD_505A] = {"atmel_at76c505a-rfmd2958.bin"}, + [BOARD_505AMX] = {"atmel_at76c505amx-rfmd.bin"}, +}; + +#define USB_DEVICE_DATA(__ops) .driver_info = (kernel_ulong_t)(__ops) + +static struct usb_device_id dev_table[] = { + /* + * at76c503-i3861 + */ + /* Generic AT76C503/3861 device */ + {USB_DEVICE(0x03eb, 0x7603), USB_DEVICE_DATA(BOARD_503_ISL3861)}, + /* Linksys WUSB11 v2.1/v2.6 */ + {USB_DEVICE(0x066b, 0x2211), USB_DEVICE_DATA(BOARD_503_ISL3861)}, + /* Netgear MA101 rev. A */ + {USB_DEVICE(0x0864, 0x4100), USB_DEVICE_DATA(BOARD_503_ISL3861)}, + /* Tekram U300C / Allnet ALL0193 */ + {USB_DEVICE(0x0b3b, 0x1612), USB_DEVICE_DATA(BOARD_503_ISL3861)}, + /* HP HN210W J7801A */ + {USB_DEVICE(0x03f0, 0x011c), USB_DEVICE_DATA(BOARD_503_ISL3861)}, + /* Sitecom/Z-Com/Zyxel M4Y-750 */ + {USB_DEVICE(0x0cde, 0x0001), USB_DEVICE_DATA(BOARD_503_ISL3861)}, + /* Dynalink/Askey WLL013 (intersil) */ + {USB_DEVICE(0x069a, 0x0320), USB_DEVICE_DATA(BOARD_503_ISL3861)}, + /* EZ connect 11Mpbs Wireless USB Adapter SMC2662W v1 */ + {USB_DEVICE(0x0d5c, 0xa001), USB_DEVICE_DATA(BOARD_503_ISL3861)}, + /* BenQ AWL300 */ + {USB_DEVICE(0x04a5, 0x9000), USB_DEVICE_DATA(BOARD_503_ISL3861)}, + /* Addtron AWU-120, Compex WLU11 */ + {USB_DEVICE(0x05dd, 0xff31), USB_DEVICE_DATA(BOARD_503_ISL3861)}, + /* Intel AP310 AnyPoint II USB */ + {USB_DEVICE(0x8086, 0x0200), USB_DEVICE_DATA(BOARD_503_ISL3861)}, + /* Dynalink L11U */ + {USB_DEVICE(0x0d8e, 0x7100), USB_DEVICE_DATA(BOARD_503_ISL3861)}, + /* Arescom WL-210, FCC id 07J-GL2411USB */ + {USB_DEVICE(0x0d8e, 0x7110), USB_DEVICE_DATA(BOARD_503_ISL3861)}, + /* I-O DATA WN-B11/USB */ + {USB_DEVICE(0x04bb, 0x0919), USB_DEVICE_DATA(BOARD_503_ISL3861)}, + /* BT Voyager 1010 */ + {USB_DEVICE(0x069a, 0x0821), USB_DEVICE_DATA(BOARD_503_ISL3861)}, + /* + * at76c503-i3863 + */ + /* Generic AT76C503/3863 device */ + {USB_DEVICE(0x03eb, 0x7604), USB_DEVICE_DATA(BOARD_503_ISL3863)}, + /* Samsung SWL-2100U */ + {USB_DEVICE(0x055d, 0xa000), USB_DEVICE_DATA(BOARD_503_ISL3863)}, + /* + * at76c503-rfmd + */ + /* Generic AT76C503/RFMD device */ + {USB_DEVICE(0x03eb, 0x7605), USB_DEVICE_DATA(BOARD_503)}, + /* Dynalink/Askey WLL013 (rfmd) */ + {USB_DEVICE(0x069a, 0x0321), USB_DEVICE_DATA(BOARD_503)}, + /* Linksys WUSB11 v2.6 */ + {USB_DEVICE(0x077b, 0x2219), USB_DEVICE_DATA(BOARD_503)}, + /* Network Everywhere NWU11B */ + {USB_DEVICE(0x077b, 0x2227), USB_DEVICE_DATA(BOARD_503)}, + /* Netgear MA101 rev. B */ + {USB_DEVICE(0x0864, 0x4102), USB_DEVICE_DATA(BOARD_503)}, + /* D-Link DWL-120 rev. E */ + {USB_DEVICE(0x2001, 0x3200), USB_DEVICE_DATA(BOARD_503)}, + /* Actiontec 802UAT1, HWU01150-01UK */ + {USB_DEVICE(0x1668, 0x7605), USB_DEVICE_DATA(BOARD_503)}, + /* AirVast W-Buddie WN210 */ + {USB_DEVICE(0x03eb, 0x4102), USB_DEVICE_DATA(BOARD_503)}, + /* Dick Smith Electronics XH1153 802.11b USB adapter */ + {USB_DEVICE(0x1371, 0x5743), USB_DEVICE_DATA(BOARD_503)}, + /* CNet CNUSB611 */ + {USB_DEVICE(0x1371, 0x0001), USB_DEVICE_DATA(BOARD_503)}, + /* FiberLine FL-WL200U */ + {USB_DEVICE(0x1371, 0x0002), USB_DEVICE_DATA(BOARD_503)}, + /* BenQ AWL400 USB stick */ + {USB_DEVICE(0x04a5, 0x9001), USB_DEVICE_DATA(BOARD_503)}, + /* 3Com 3CRSHEW696 */ + {USB_DEVICE(0x0506, 0x0a01), USB_DEVICE_DATA(BOARD_503)}, + /* Siemens Santis ADSL WLAN USB adapter WLL 013 */ + {USB_DEVICE(0x0681, 0x001b), USB_DEVICE_DATA(BOARD_503)}, + /* Belkin F5D6050, version 2 */ + {USB_DEVICE(0x050d, 0x0050), USB_DEVICE_DATA(BOARD_503)}, + /* iBlitzz, BWU613 (not *B or *SB) */ + {USB_DEVICE(0x07b8, 0xb000), USB_DEVICE_DATA(BOARD_503)}, + /* Gigabyte GN-WLBM101 */ + {USB_DEVICE(0x1044, 0x8003), USB_DEVICE_DATA(BOARD_503)}, + /* Planex GW-US11S */ + {USB_DEVICE(0x2019, 0x3220), USB_DEVICE_DATA(BOARD_503)}, + /* Internal WLAN adapter in h5[4,5]xx series iPAQs */ + {USB_DEVICE(0x049f, 0x0032), USB_DEVICE_DATA(BOARD_503)}, + /* Corega Wireless LAN USB-11 mini */ + {USB_DEVICE(0x07aa, 0x0011), USB_DEVICE_DATA(BOARD_503)}, + /* Corega Wireless LAN USB-11 mini2 */ + {USB_DEVICE(0x07aa, 0x0018), USB_DEVICE_DATA(BOARD_503)}, + /* + * at76c503-rfmd-acc + */ + /* SMC2664W */ + {USB_DEVICE(0x083a, 0x3501), USB_DEVICE_DATA(BOARD_503_ACC)}, + /* Belkin F5D6050, SMC2662W v2, SMC2662W-AR */ + {USB_DEVICE(0x0d5c, 0xa002), USB_DEVICE_DATA(BOARD_503_ACC)}, + /* + * at76c505-rfmd + */ + /* Generic AT76C505/RFMD */ + {USB_DEVICE(0x03eb, 0x7606), USB_DEVICE_DATA(BOARD_505)}, + /* + * at76c505-rfmd2958 + */ + /* Generic AT76C505/RFMD, OvisLink WL-1130USB */ + {USB_DEVICE(0x03eb, 0x7613), USB_DEVICE_DATA(BOARD_505_2958)}, + /* Fiberline FL-WL240U */ + {USB_DEVICE(0x1371, 0x0014), USB_DEVICE_DATA(BOARD_505_2958)}, + /* CNet CNUSB-611G */ + {USB_DEVICE(0x1371, 0x0013), USB_DEVICE_DATA(BOARD_505_2958)}, + /* Linksys WUSB11 v2.8 */ + {USB_DEVICE(0x1915, 0x2233), USB_DEVICE_DATA(BOARD_505_2958)}, + /* Xterasys XN-2122B, IBlitzz BWU613B/BWU613SB */ + {USB_DEVICE(0x12fd, 0x1001), USB_DEVICE_DATA(BOARD_505_2958)}, + /* Corega WLAN USB Stick 11 */ + {USB_DEVICE(0x07aa, 0x7613), USB_DEVICE_DATA(BOARD_505_2958)}, + /* Microstar MSI Box MS6978 */ + {USB_DEVICE(0x0db0, 0x1020), USB_DEVICE_DATA(BOARD_505_2958)}, + /* + * at76c505a-rfmd2958 + */ + /* Generic AT76C505A device */ + {USB_DEVICE(0x03eb, 0x7614), USB_DEVICE_DATA(BOARD_505A)}, + /* Generic AT76C505AS device */ + {USB_DEVICE(0x03eb, 0x7617), USB_DEVICE_DATA(BOARD_505A)}, + /* Siemens Gigaset USB WLAN Adapter 11 */ + {USB_DEVICE(0x1690, 0x0701), USB_DEVICE_DATA(BOARD_505A)}, + /* + * at76c505amx-rfmd + */ + /* Generic AT76C505AMX device */ + {USB_DEVICE(0x03eb, 0x7615), USB_DEVICE_DATA(BOARD_505AMX)}, + {} +}; + +MODULE_DEVICE_TABLE(usb, dev_table); + +/* Supported rates of this hardware, bit 7 marks basic rates */ +static const u8 hw_rates[] = { 0x82, 0x84, 0x0b, 0x16 }; + +/* Frequency of each channel in MHz */ +static const long channel_frequency[] = { + 2412, 2417, 2422, 2427, 2432, 2437, 2442, + 2447, 2452, 2457, 2462, 2467, 2472, 2484 +}; + +#define NUM_CHANNELS ARRAY_SIZE(channel_frequency) + +static const char *const preambles[] = { "long", "short", "auto" }; + +static const char *const mac_states[] = { + [MAC_INIT] = "INIT", + [MAC_SCANNING] = "SCANNING", + [MAC_AUTH] = "AUTH", + [MAC_ASSOC] = "ASSOC", + [MAC_JOINING] = "JOINING", + [MAC_CONNECTED] = "CONNECTED", + [MAC_OWN_IBSS] = "OWN_IBSS" +}; + +/* Firmware download */ +/* DFU states */ +#define STATE_IDLE 0x00 +#define STATE_DETACH 0x01 +#define STATE_DFU_IDLE 0x02 +#define STATE_DFU_DOWNLOAD_SYNC 0x03 +#define STATE_DFU_DOWNLOAD_BUSY 0x04 +#define STATE_DFU_DOWNLOAD_IDLE 0x05 +#define STATE_DFU_MANIFEST_SYNC 0x06 +#define STATE_DFU_MANIFEST 0x07 +#define STATE_DFU_MANIFEST_WAIT_RESET 0x08 +#define STATE_DFU_UPLOAD_IDLE 0x09 +#define STATE_DFU_ERROR 0x0a + +/* DFU commands */ +#define DFU_DETACH 0 +#define DFU_DNLOAD 1 +#define DFU_UPLOAD 2 +#define DFU_GETSTATUS 3 +#define DFU_CLRSTATUS 4 +#define DFU_GETSTATE 5 +#define DFU_ABORT 6 + +#define FW_BLOCK_SIZE 1024 + +struct dfu_status { + unsigned char status; + unsigned char poll_timeout[3]; + unsigned char state; + unsigned char string; +} __attribute__((packed)); + +static inline int at76_is_intersil(enum board_type board) +{ + return (board == BOARD_503_ISL3861 || board == BOARD_503_ISL3863); +} + +static inline int at76_is_503rfmd(enum board_type board) +{ + return (board == BOARD_503 || board == BOARD_503_ACC); +} + +static inline int at76_is_505a(enum board_type board) +{ + return (board == BOARD_505A || board == BOARD_505AMX); +} + +/* Load a block of the first (internal) part of the firmware */ +static int at76_load_int_fw_block(struct usb_device *udev, int blockno, + void *block, int size) +{ + return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), DFU_DNLOAD, + USB_TYPE_CLASS | USB_DIR_OUT | + USB_RECIP_INTERFACE, blockno, 0, block, size, + USB_CTRL_GET_TIMEOUT); +} + +static int at76_dfu_get_status(struct usb_device *udev, + struct dfu_status *status) +{ + int ret; + + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), DFU_GETSTATUS, + USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE, + 0, 0, status, sizeof(struct dfu_status), + USB_CTRL_GET_TIMEOUT); + return ret; +} + +static u8 at76_dfu_get_state(struct usb_device *udev, u8 *state) +{ + int ret; + + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), DFU_GETSTATE, + USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE, + 0, 0, state, 1, USB_CTRL_GET_TIMEOUT); + return ret; +} + +/* Convert timeout from the DFU status to jiffies */ +static inline unsigned long at76_get_timeout(struct dfu_status *s) +{ + return msecs_to_jiffies((s->poll_timeout[2] << 16) + | (s->poll_timeout[1] << 8) + | (s->poll_timeout[0])); +} + +/* Load internal firmware from the buffer. If manifest_sync_timeout > 0, use + * its value in jiffies in the MANIFEST_SYNC state. */ +static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size, + int manifest_sync_timeout) +{ + u8 *block; + struct dfu_status dfu_stat_buf; + int ret = 0; + int need_dfu_state = 1; + int is_done = 0; + u8 dfu_state = 0; + u32 dfu_timeout = 0; + int bsize = 0; + int blockno = 0; + + at76_dbg(DBG_DFU, "%s( %p, %u, %d)", __func__, buf, size, + manifest_sync_timeout); + + if (!size) { + dev_printk(KERN_ERR, &udev->dev, "FW buffer length invalid!\n"); + return -EINVAL; + } + + block = kmalloc(FW_BLOCK_SIZE, GFP_KERNEL); + if (!block) + return -ENOMEM; + + do { + if (need_dfu_state) { + ret = at76_dfu_get_state(udev, &dfu_state); + if (ret < 0) { + dev_printk(KERN_ERR, &udev->dev, + "cannot get DFU state: %d\n", ret); + goto exit; + } + need_dfu_state = 0; + } + + switch (dfu_state) { + case STATE_DFU_DOWNLOAD_SYNC: + at76_dbg(DBG_DFU, "STATE_DFU_DOWNLOAD_SYNC"); + ret = at76_dfu_get_status(udev, &dfu_stat_buf); + if (ret >= 0) { + dfu_state = dfu_stat_buf.state; + dfu_timeout = at76_get_timeout(&dfu_stat_buf); + need_dfu_state = 0; + } else + dev_printk(KERN_ERR, &udev->dev, + "at76_dfu_get_status returned %d\n", + ret); + break; + + case STATE_DFU_DOWNLOAD_BUSY: + at76_dbg(DBG_DFU, "STATE_DFU_DOWNLOAD_BUSY"); + need_dfu_state = 1; + + at76_dbg(DBG_DFU, "DFU: Resetting device"); + schedule_timeout_interruptible(dfu_timeout); + break; + + case STATE_DFU_DOWNLOAD_IDLE: + at76_dbg(DBG_DFU, "DOWNLOAD..."); + /* fall through */ + case STATE_DFU_IDLE: + at76_dbg(DBG_DFU, "DFU IDLE"); + + bsize = min_t(int, size, FW_BLOCK_SIZE); + memcpy(block, buf, bsize); + at76_dbg(DBG_DFU, "int fw, size left = %5d, " + "bsize = %4d, blockno = %2d", size, bsize, + blockno); + ret = + at76_load_int_fw_block(udev, blockno, block, bsize); + buf += bsize; + size -= bsize; + blockno++; + + if (ret != bsize) + dev_printk(KERN_ERR, &udev->dev, + "at76_load_int_fw_block " + "returned %d\n", ret); + need_dfu_state = 1; + break; + + case STATE_DFU_MANIFEST_SYNC: + at76_dbg(DBG_DFU, "STATE_DFU_MANIFEST_SYNC"); + + ret = at76_dfu_get_status(udev, &dfu_stat_buf); + if (ret < 0) + break; + + dfu_state = dfu_stat_buf.state; + dfu_timeout = at76_get_timeout(&dfu_stat_buf); + need_dfu_state = 0; + + /* override the timeout from the status response, + needed for AT76C505A */ + if (manifest_sync_timeout > 0) + dfu_timeout = manifest_sync_timeout; + + at76_dbg(DBG_DFU, "DFU: Waiting for manifest phase"); + schedule_timeout_interruptible(dfu_timeout); + break; + + case STATE_DFU_MANIFEST: + at76_dbg(DBG_DFU, "STATE_DFU_MANIFEST"); + is_done = 1; + break; + + case STATE_DFU_MANIFEST_WAIT_RESET: + at76_dbg(DBG_DFU, "STATE_DFU_MANIFEST_WAIT_RESET"); + is_done = 1; + break; + + case STATE_DFU_UPLOAD_IDLE: + at76_dbg(DBG_DFU, "STATE_DFU_UPLOAD_IDLE"); + break; + + case STATE_DFU_ERROR: + at76_dbg(DBG_DFU, "STATE_DFU_ERROR"); + ret = -EPIPE; + break; + + default: + at76_dbg(DBG_DFU, "DFU UNKNOWN STATE (%d)", dfu_state); + ret = -EINVAL; + break; + } + } while (!is_done && (ret >= 0)); + +exit: + kfree(block); + if (ret >= 0) + ret = 0; + + return ret; +} + +/* Report that the scan results are ready */ +static inline void at76_iwevent_scan_complete(struct net_device *netdev) +{ + union iwreq_data wrqu; + wrqu.data.length = 0; + wrqu.data.flags = 0; + wireless_send_event(netdev, SIOCGIWSCAN, &wrqu, NULL); + at76_dbg(DBG_WE_EVENTS, "%s: SIOCGIWSCAN sent", netdev->name); +} + +static inline void at76_iwevent_bss_connect(struct net_device *netdev, + u8 *bssid) +{ + union iwreq_data wrqu; + wrqu.data.length = 0; + wrqu.data.flags = 0; + memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(netdev, SIOCGIWAP, &wrqu, NULL); + at76_dbg(DBG_WE_EVENTS, "%s: %s: SIOCGIWAP sent", netdev->name, + __func__); +} + +static inline void at76_iwevent_bss_disconnect(struct net_device *netdev) +{ + union iwreq_data wrqu; + wrqu.data.length = 0; + wrqu.data.flags = 0; + memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(netdev, SIOCGIWAP, &wrqu, NULL); + at76_dbg(DBG_WE_EVENTS, "%s: %s: SIOCGIWAP sent", netdev->name, + __func__); +} + +#define HEX2STR_BUFFERS 4 +#define HEX2STR_MAX_LEN 64 +#define BIN2HEX(x) ((x) < 10 ? '0' + (x) : (x) + 'A' - 10) + +/* Convert binary data into hex string */ +static char *hex2str(void *buf, int len) +{ + static atomic_t a = ATOMIC_INIT(0); + static char bufs[HEX2STR_BUFFERS][3 * HEX2STR_MAX_LEN + 1]; + char *ret = bufs[atomic_inc_return(&a) & (HEX2STR_BUFFERS - 1)]; + char *obuf = ret; + u8 *ibuf = buf; + + if (len > HEX2STR_MAX_LEN) + len = HEX2STR_MAX_LEN; + + if (len <= 0) { + ret[0] = '\0'; + return ret; + } + + while (len--) { + *obuf++ = BIN2HEX(*ibuf >> 4); + *obuf++ = BIN2HEX(*ibuf & 0xf); + *obuf++ = '-'; + ibuf++; + } + *(--obuf) = '\0'; + + return ret; +} + +#define MAC2STR_BUFFERS 4 + +static inline char *mac2str(u8 *mac) +{ + static atomic_t a = ATOMIC_INIT(0); + static char bufs[MAC2STR_BUFFERS][6 * 3]; + char *str; + + str = bufs[atomic_inc_return(&a) & (MAC2STR_BUFFERS - 1)]; + sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + return str; +} + +/* LED trigger */ +static int tx_activity; +static void at76_ledtrig_tx_timerfunc(unsigned long data); +static DEFINE_TIMER(ledtrig_tx_timer, at76_ledtrig_tx_timerfunc, 0, 0); +DEFINE_LED_TRIGGER(ledtrig_tx); + +static void at76_ledtrig_tx_timerfunc(unsigned long data) +{ + static int tx_lastactivity; + + if (tx_lastactivity != tx_activity) { + tx_lastactivity = tx_activity; + led_trigger_event(ledtrig_tx, LED_FULL); + mod_timer(&ledtrig_tx_timer, jiffies + HZ / 4); + } else + led_trigger_event(ledtrig_tx, LED_OFF); +} + +static void at76_ledtrig_tx_activity(void) +{ + tx_activity++; + if (!timer_pending(&ledtrig_tx_timer)) + mod_timer(&ledtrig_tx_timer, jiffies + HZ / 4); +} + +/* Check if the given ssid is hidden */ +static inline int at76_is_hidden_ssid(u8 *ssid, int length) +{ + static const u8 zeros[32]; + + if (length == 0) + return 1; + + if (length == 1 && ssid[0] == ' ') + return 1; + + return (memcmp(ssid, zeros, length) == 0); +} + +static inline void at76_free_bss_list(struct at76_priv *priv) +{ + struct list_head *next, *ptr; + unsigned long flags; + + spin_lock_irqsave(&priv->bss_list_spinlock, flags); + + priv->curr_bss = NULL; + + list_for_each_safe(ptr, next, &priv->bss_list) { + list_del(ptr); + kfree(list_entry(ptr, struct bss_info, list)); + } + + spin_unlock_irqrestore(&priv->bss_list_spinlock, flags); +} + +static int at76_remap(struct usb_device *udev) +{ + int ret; + ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x0a, + USB_TYPE_VENDOR | USB_DIR_OUT | + USB_RECIP_INTERFACE, 0, 0, NULL, 0, + USB_CTRL_GET_TIMEOUT); + if (ret < 0) + return ret; + return 0; +} + +static int at76_get_op_mode(struct usb_device *udev) +{ + int ret; + u8 op_mode; + + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x33, + USB_TYPE_VENDOR | USB_DIR_IN | + USB_RECIP_INTERFACE, 0x01, 0, &op_mode, 1, + USB_CTRL_GET_TIMEOUT); + if (ret < 0) + return ret; + else if (ret < 1) + return -EIO; + else + return op_mode; +} + +/* Load a block of the second ("external") part of the firmware */ +static inline int at76_load_ext_fw_block(struct usb_device *udev, int blockno, + void *block, int size) +{ + return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x0e, + USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, + 0x0802, blockno, block, size, + USB_CTRL_GET_TIMEOUT); +} + +static inline int at76_get_hw_cfg(struct usb_device *udev, + union at76_hwcfg *buf, int buf_size) +{ + return usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x33, + USB_TYPE_VENDOR | USB_DIR_IN | + USB_RECIP_INTERFACE, 0x0a02, 0, + buf, buf_size, USB_CTRL_GET_TIMEOUT); +} + +/* Intersil boards use a different "value" for GetHWConfig requests */ +static inline int at76_get_hw_cfg_intersil(struct usb_device *udev, + union at76_hwcfg *buf, int buf_size) +{ + return usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x33, + USB_TYPE_VENDOR | USB_DIR_IN | + USB_RECIP_INTERFACE, 0x0902, 0, + buf, buf_size, USB_CTRL_GET_TIMEOUT); +} + +/* Get the hardware configuration for the adapter and put it to the appropriate + * fields of 'priv' (the GetHWConfig request and interpretation of the result + * depends on the board type) */ +static int at76_get_hw_config(struct at76_priv *priv) +{ + int ret; + union at76_hwcfg *hwcfg = kmalloc(sizeof(*hwcfg), GFP_KERNEL); + + if (!hwcfg) + return -ENOMEM; + + if (at76_is_intersil(priv->board_type)) { + ret = at76_get_hw_cfg_intersil(priv->udev, hwcfg, + sizeof(hwcfg->i)); + if (ret < 0) + goto exit; + memcpy(priv->mac_addr, hwcfg->i.mac_addr, ETH_ALEN); + priv->regulatory_domain = hwcfg->i.regulatory_domain; + } else if (at76_is_503rfmd(priv->board_type)) { + ret = at76_get_hw_cfg(priv->udev, hwcfg, sizeof(hwcfg->r3)); + if (ret < 0) + goto exit; + memcpy(priv->mac_addr, hwcfg->r3.mac_addr, ETH_ALEN); + priv->regulatory_domain = hwcfg->r3.regulatory_domain; + } else { + ret = at76_get_hw_cfg(priv->udev, hwcfg, sizeof(hwcfg->r5)); + if (ret < 0) + goto exit; + memcpy(priv->mac_addr, hwcfg->r5.mac_addr, ETH_ALEN); + priv->regulatory_domain = hwcfg->r5.regulatory_domain; + } + +exit: + kfree(hwcfg); + if (ret < 0) + printk(KERN_ERR "%s: cannot get HW Config (error %d)\n", + priv->netdev->name, ret); + + return ret; +} + +static struct reg_domain const *at76_get_reg_domain(u16 code) +{ + int i; + static struct reg_domain const fd_tab[] = { + {0x10, "FCC (USA)", 0x7ff}, /* ch 1-11 */ + {0x20, "IC (Canada)", 0x7ff}, /* ch 1-11 */ + {0x30, "ETSI (most of Europe)", 0x1fff}, /* ch 1-13 */ + {0x31, "Spain", 0x600}, /* ch 10-11 */ + {0x32, "France", 0x1e00}, /* ch 10-13 */ + {0x40, "MKK (Japan)", 0x2000}, /* ch 14 */ + {0x41, "MKK1 (Japan)", 0x3fff}, /* ch 1-14 */ + {0x50, "Israel", 0x3fc}, /* ch 3-9 */ + {0x00, "", 0xffffffff} /* ch 1-32 */ + }; + + /* Last entry is fallback for unknown domain code */ + for (i = 0; i < ARRAY_SIZE(fd_tab) - 1; i++) + if (code == fd_tab[i].code) + break; + + return &fd_tab[i]; +} + +static inline int at76_get_mib(struct usb_device *udev, u16 mib, void *buf, + int buf_size) +{ + int ret; + + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x33, + USB_TYPE_VENDOR | USB_DIR_IN | + USB_RECIP_INTERFACE, mib << 8, 0, buf, buf_size, + USB_CTRL_GET_TIMEOUT); + if (ret >= 0 && ret != buf_size) + return -EIO; + return ret; +} + +/* Return positive number for status, negative for an error */ +static inline int at76_get_cmd_status(struct usb_device *udev, u8 cmd) +{ + u8 stat_buf[40]; + int ret; + + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x22, + USB_TYPE_VENDOR | USB_DIR_IN | + USB_RECIP_INTERFACE, cmd, 0, stat_buf, + sizeof(stat_buf), USB_CTRL_GET_TIMEOUT); + if (ret < 0) + return ret; + + return stat_buf[5]; +} + +static int at76_set_card_command(struct usb_device *udev, int cmd, void *buf, + int buf_size) +{ + int ret; + struct at76_command *cmd_buf = kmalloc(sizeof(struct at76_command) + + buf_size, GFP_KERNEL); + + if (!cmd_buf) + return -ENOMEM; + + cmd_buf->cmd = cmd; + cmd_buf->reserved = 0; + cmd_buf->size = cpu_to_le16(buf_size); + memcpy(cmd_buf->data, buf, buf_size); + + ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x0e, + USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, + 0, 0, cmd_buf, + sizeof(struct at76_command) + buf_size, + USB_CTRL_GET_TIMEOUT); + kfree(cmd_buf); + return ret; +} + +#define MAKE_CMD_STATUS_CASE(c) case (c): return #c +static const char *at76_get_cmd_status_string(u8 cmd_status) +{ + switch (cmd_status) { + MAKE_CMD_STATUS_CASE(CMD_STATUS_IDLE); + MAKE_CMD_STATUS_CASE(CMD_STATUS_COMPLETE); + MAKE_CMD_STATUS_CASE(CMD_STATUS_UNKNOWN); + MAKE_CMD_STATUS_CASE(CMD_STATUS_INVALID_PARAMETER); + MAKE_CMD_STATUS_CASE(CMD_STATUS_FUNCTION_NOT_SUPPORTED); + MAKE_CMD_STATUS_CASE(CMD_STATUS_TIME_OUT); + MAKE_CMD_STATUS_CASE(CMD_STATUS_IN_PROGRESS); + MAKE_CMD_STATUS_CASE(CMD_STATUS_HOST_FAILURE); + MAKE_CMD_STATUS_CASE(CMD_STATUS_SCAN_FAILED); + } + + return "UNKNOWN"; +} + +/* Wait until the command is completed */ +static int at76_wait_completion(struct at76_priv *priv, int cmd) +{ + int status = 0; + unsigned long timeout = jiffies + CMD_COMPLETION_TIMEOUT; + + do { + status = at76_get_cmd_status(priv->udev, cmd); + if (status < 0) { + printk(KERN_ERR "%s: at76_get_cmd_status failed: %d\n", + priv->netdev->name, status); + break; + } + + at76_dbg(DBG_WAIT_COMPLETE, + "%s: Waiting on cmd %d, status = %d (%s)", + priv->netdev->name, cmd, status, + at76_get_cmd_status_string(status)); + + if (status != CMD_STATUS_IN_PROGRESS + && status != CMD_STATUS_IDLE) + break; + + schedule_timeout_interruptible(HZ / 10); /* 100 ms */ + if (time_after(jiffies, timeout)) { + printk(KERN_ERR + "%s: completion timeout for command %d\n", + priv->netdev->name, cmd); + status = -ETIMEDOUT; + break; + } + } while (1); + + return status; +} + +static int at76_set_mib(struct at76_priv *priv, struct set_mib_buffer *buf) +{ + int ret; + + ret = at76_set_card_command(priv->udev, CMD_SET_MIB, buf, + offsetof(struct set_mib_buffer, + data) + buf->size); + if (ret < 0) + return ret; + + ret = at76_wait_completion(priv, CMD_SET_MIB); + if (ret != CMD_STATUS_COMPLETE) { + printk(KERN_INFO + "%s: set_mib: at76_wait_completion failed " + "with %d\n", priv->netdev->name, ret); + ret = -EIO; + } + + return ret; +} + +/* Return < 0 on error, == 0 if no command sent, == 1 if cmd sent */ +static int at76_set_radio(struct at76_priv *priv, int enable) +{ + int ret; + int cmd; + + if (priv->radio_on == enable) + return 0; + + cmd = enable ? CMD_RADIO_ON : CMD_RADIO_OFF; + + ret = at76_set_card_command(priv->udev, cmd, NULL, 0); + if (ret < 0) + printk(KERN_ERR "%s: at76_set_card_command(%d) failed: %d\n", + priv->netdev->name, cmd, ret); + else + ret = 1; + + priv->radio_on = enable; + return ret; +} + +/* Set current power save mode (AT76_PM_OFF/AT76_PM_ON/AT76_PM_SMART) */ +static int at76_set_pm_mode(struct at76_priv *priv) +{ + int ret = 0; + + priv->mib_buf.type = MIB_MAC_MGMT; + priv->mib_buf.size = 1; + priv->mib_buf.index = offsetof(struct mib_mac_mgmt, power_mgmt_mode); + priv->mib_buf.data.byte = priv->pm_mode; + + ret = at76_set_mib(priv, &priv->mib_buf); + if (ret < 0) + printk(KERN_ERR "%s: set_mib (pm_mode) failed: %d\n", + priv->netdev->name, ret); + + return ret; +} + +/* Set the association id for power save mode */ +static int at76_set_associd(struct at76_priv *priv, u16 id) +{ + int ret = 0; + + priv->mib_buf.type = MIB_MAC_MGMT; + priv->mib_buf.size = 2; + priv->mib_buf.index = offsetof(struct mib_mac_mgmt, station_id); + priv->mib_buf.data.word = cpu_to_le16(id); + + ret = at76_set_mib(priv, &priv->mib_buf); + if (ret < 0) + printk(KERN_ERR "%s: set_mib (associd) failed: %d\n", + priv->netdev->name, ret); + + return ret; +} + +/* Set the listen interval for power save mode */ +static int at76_set_listen_interval(struct at76_priv *priv, u16 interval) +{ + int ret = 0; + + priv->mib_buf.type = MIB_MAC; + priv->mib_buf.size = 2; + priv->mib_buf.index = offsetof(struct mib_mac, listen_interval); + priv->mib_buf.data.word = cpu_to_le16(interval); + + ret = at76_set_mib(priv, &priv->mib_buf); + if (ret < 0) + printk(KERN_ERR + "%s: set_mib (listen_interval) failed: %d\n", + priv->netdev->name, ret); + + return ret; +} + +static int at76_set_preamble(struct at76_priv *priv, u8 type) +{ + int ret = 0; + + priv->mib_buf.type = MIB_LOCAL; + priv->mib_buf.size = 1; + priv->mib_buf.index = offsetof(struct mib_local, preamble_type); + priv->mib_buf.data.byte = type; + + ret = at76_set_mib(priv, &priv->mib_buf); + if (ret < 0) + printk(KERN_ERR "%s: set_mib (preamble) failed: %d\n", + priv->netdev->name, ret); + + return ret; +} + +static int at76_set_frag(struct at76_priv *priv, u16 size) +{ + int ret = 0; + + priv->mib_buf.type = MIB_MAC; + priv->mib_buf.size = 2; + priv->mib_buf.index = offsetof(struct mib_mac, frag_threshold); + priv->mib_buf.data.word = cpu_to_le16(size); + + ret = at76_set_mib(priv, &priv->mib_buf); + if (ret < 0) + printk(KERN_ERR "%s: set_mib (frag threshold) failed: %d\n", + priv->netdev->name, ret); + + return ret; +} + +static int at76_set_rts(struct at76_priv *priv, u16 size) +{ + int ret = 0; + + priv->mib_buf.type = MIB_MAC; + priv->mib_buf.size = 2; + priv->mib_buf.index = offsetof(struct mib_mac, rts_threshold); + priv->mib_buf.data.word = cpu_to_le16(size); + + ret = at76_set_mib(priv, &priv->mib_buf); + if (ret < 0) + printk(KERN_ERR "%s: set_mib (rts) failed: %d\n", + priv->netdev->name, ret); + + return ret; +} + +static int at76_set_autorate_fallback(struct at76_priv *priv, int onoff) +{ + int ret = 0; + + priv->mib_buf.type = MIB_LOCAL; + priv->mib_buf.size = 1; + priv->mib_buf.index = offsetof(struct mib_local, txautorate_fallback); + priv->mib_buf.data.byte = onoff; + + ret = at76_set_mib(priv, &priv->mib_buf); + if (ret < 0) + printk(KERN_ERR "%s: set_mib (autorate fallback) failed: %d\n", + priv->netdev->name, ret); + + return ret; +} + +static int at76_add_mac_address(struct at76_priv *priv, void *addr) +{ + int ret = 0; + + priv->mib_buf.type = MIB_MAC_ADDR; + priv->mib_buf.size = ETH_ALEN; + priv->mib_buf.index = offsetof(struct mib_mac_addr, mac_addr); + memcpy(priv->mib_buf.data.addr, addr, ETH_ALEN); + + ret = at76_set_mib(priv, &priv->mib_buf); + if (ret < 0) + printk(KERN_ERR "%s: set_mib (MAC_ADDR, mac_addr) failed: %d\n", + priv->netdev->name, ret); + + return ret; +} + +static void at76_dump_mib_mac_addr(struct at76_priv *priv) +{ + int i; + int ret; + struct mib_mac_addr *m = kmalloc(sizeof(struct mib_mac_addr), + GFP_KERNEL); + + if (!m) + return; + + ret = at76_get_mib(priv->udev, MIB_MAC_ADDR, m, + sizeof(struct mib_mac_addr)); + if (ret < 0) { + printk(KERN_ERR "%s: at76_get_mib (MAC_ADDR) failed: %d\n", + priv->netdev->name, ret); + goto exit; + } + + at76_dbg(DBG_MIB, "%s: MIB MAC_ADDR: mac_addr %s res 0x%x 0x%x", + priv->netdev->name, + mac2str(m->mac_addr), m->res[0], m->res[1]); + for (i = 0; i < ARRAY_SIZE(m->group_addr); i++) + at76_dbg(DBG_MIB, "%s: MIB MAC_ADDR: group addr %d: %s, " + "status %d", priv->netdev->name, i, + mac2str(m->group_addr[i]), m->group_addr_status[i]); +exit: + kfree(m); +} + +static void at76_dump_mib_mac_wep(struct at76_priv *priv) +{ + int i; + int ret; + int key_len; + struct mib_mac_wep *m = kmalloc(sizeof(struct mib_mac_wep), GFP_KERNEL); + + if (!m) + return; + + ret = at76_get_mib(priv->udev, MIB_MAC_WEP, m, + sizeof(struct mib_mac_wep)); + if (ret < 0) { + printk(KERN_ERR "%s: at76_get_mib (MAC_WEP) failed: %d\n", + priv->netdev->name, ret); + goto exit; + } + + at76_dbg(DBG_MIB, "%s: MIB MAC_WEP: priv_invoked %u def_key_id %u " + "key_len %u excl_unencr %u wep_icv_err %u wep_excluded %u " + "encr_level %u key %d", priv->netdev->name, + m->privacy_invoked, m->wep_default_key_id, + m->wep_key_mapping_len, m->exclude_unencrypted, + le32_to_cpu(m->wep_icv_error_count), + le32_to_cpu(m->wep_excluded_count), m->encryption_level, + m->wep_default_key_id); + + key_len = (m->encryption_level == 1) ? + WEP_SMALL_KEY_LEN : WEP_LARGE_KEY_LEN; + + for (i = 0; i < WEP_KEYS; i++) + at76_dbg(DBG_MIB, "%s: MIB MAC_WEP: key %d: %s", + priv->netdev->name, i, + hex2str(m->wep_default_keyvalue[i], key_len)); +exit: + kfree(m); +} + +static void at76_dump_mib_mac_mgmt(struct at76_priv *priv) +{ + int ret; + struct mib_mac_mgmt *m = kmalloc(sizeof(struct mib_mac_mgmt), + GFP_KERNEL); + + if (!m) + return; + + ret = at76_get_mib(priv->udev, MIB_MAC_MGMT, m, + sizeof(struct mib_mac_mgmt)); + if (ret < 0) { + printk(KERN_ERR "%s: at76_get_mib (MAC_MGMT) failed: %d\n", + priv->netdev->name, ret); + goto exit; + } + + at76_dbg(DBG_MIB, "%s: MIB MAC_MGMT: beacon_period %d CFP_max_duration " + "%d medium_occupancy_limit %d station_id 0x%x ATIM_window %d " + "CFP_mode %d privacy_opt_impl %d DTIM_period %d CFP_period %d " + "current_bssid %s current_essid %s current_bss_type %d " + "pm_mode %d ibss_change %d res %d " + "multi_domain_capability_implemented %d " + "international_roaming %d country_string %.3s", + priv->netdev->name, le16_to_cpu(m->beacon_period), + le16_to_cpu(m->CFP_max_duration), + le16_to_cpu(m->medium_occupancy_limit), + le16_to_cpu(m->station_id), le16_to_cpu(m->ATIM_window), + m->CFP_mode, m->privacy_option_implemented, m->DTIM_period, + m->CFP_period, mac2str(m->current_bssid), + hex2str(m->current_essid, IW_ESSID_MAX_SIZE), + m->current_bss_type, m->power_mgmt_mode, m->ibss_change, + m->res, m->multi_domain_capability_implemented, + m->multi_domain_capability_enabled, m->country_string); +exit: + kfree(m); +} + +static void at76_dump_mib_mac(struct at76_priv *priv) +{ + int ret; + struct mib_mac *m = kmalloc(sizeof(struct mib_mac), GFP_KERNEL); + + if (!m) + return; + + ret = at76_get_mib(priv->udev, MIB_MAC, m, sizeof(struct mib_mac)); + if (ret < 0) { + printk(KERN_ERR "%s: at76_get_mib (MAC) failed: %d\n", + priv->netdev->name, ret); + goto exit; + } + + at76_dbg(DBG_MIB, "%s: MIB MAC: max_tx_msdu_lifetime %d " + "max_rx_lifetime %d frag_threshold %d rts_threshold %d " + "cwmin %d cwmax %d short_retry_time %d long_retry_time %d " + "scan_type %d scan_channel %d probe_delay %u " + "min_channel_time %d max_channel_time %d listen_int %d " + "desired_ssid %s desired_bssid %s desired_bsstype %d", + priv->netdev->name, le32_to_cpu(m->max_tx_msdu_lifetime), + le32_to_cpu(m->max_rx_lifetime), + le16_to_cpu(m->frag_threshold), le16_to_cpu(m->rts_threshold), + le16_to_cpu(m->cwmin), le16_to_cpu(m->cwmax), + m->short_retry_time, m->long_retry_time, m->scan_type, + m->scan_channel, le16_to_cpu(m->probe_delay), + le16_to_cpu(m->min_channel_time), + le16_to_cpu(m->max_channel_time), + le16_to_cpu(m->listen_interval), + hex2str(m->desired_ssid, IW_ESSID_MAX_SIZE), + mac2str(m->desired_bssid), m->desired_bsstype); +exit: + kfree(m); +} + +static void at76_dump_mib_phy(struct at76_priv *priv) +{ + int ret; + struct mib_phy *m = kmalloc(sizeof(struct mib_phy), GFP_KERNEL); + + if (!m) + return; + + ret = at76_get_mib(priv->udev, MIB_PHY, m, sizeof(struct mib_phy)); + if (ret < 0) { + printk(KERN_ERR "%s: at76_get_mib (PHY) failed: %d\n", + priv->netdev->name, ret); + goto exit; + } + + at76_dbg(DBG_MIB, "%s: MIB PHY: ed_threshold %d slot_time %d " + "sifs_time %d preamble_length %d plcp_header_length %d " + "mpdu_max_length %d cca_mode_supported %d operation_rate_set " + "0x%x 0x%x 0x%x 0x%x channel_id %d current_cca_mode %d " + "phy_type %d current_reg_domain %d", + priv->netdev->name, le32_to_cpu(m->ed_threshold), + le16_to_cpu(m->slot_time), le16_to_cpu(m->sifs_time), + le16_to_cpu(m->preamble_length), + le16_to_cpu(m->plcp_header_length), + le16_to_cpu(m->mpdu_max_length), + le16_to_cpu(m->cca_mode_supported), m->operation_rate_set[0], + m->operation_rate_set[1], m->operation_rate_set[2], + m->operation_rate_set[3], m->channel_id, m->current_cca_mode, + m->phy_type, m->current_reg_domain); +exit: + kfree(m); +} + +static void at76_dump_mib_local(struct at76_priv *priv) +{ + int ret; + struct mib_local *m = kmalloc(sizeof(struct mib_phy), GFP_KERNEL); + + if (!m) + return; + + ret = at76_get_mib(priv->udev, MIB_LOCAL, m, sizeof(struct mib_local)); + if (ret < 0) { + printk(KERN_ERR "%s: at76_get_mib (LOCAL) failed: %d\n", + priv->netdev->name, ret); + goto exit; + } + + at76_dbg(DBG_MIB, "%s: MIB LOCAL: beacon_enable %d " + "txautorate_fallback %d ssid_size %d promiscuous_mode %d " + "preamble_type %d", priv->netdev->name, m->beacon_enable, + m->txautorate_fallback, m->ssid_size, m->promiscuous_mode, + m->preamble_type); +exit: + kfree(m); +} + +static void at76_dump_mib_mdomain(struct at76_priv *priv) +{ + int ret; + struct mib_mdomain *m = kmalloc(sizeof(struct mib_mdomain), GFP_KERNEL); + + if (!m) + return; + + ret = at76_get_mib(priv->udev, MIB_MDOMAIN, m, + sizeof(struct mib_mdomain)); + if (ret < 0) { + printk(KERN_ERR "%s: at76_get_mib (MDOMAIN) failed: %d\n", + priv->netdev->name, ret); + goto exit; + } + + at76_dbg(DBG_MIB, "%s: MIB MDOMAIN: channel_list %s", + priv->netdev->name, + hex2str(m->channel_list, sizeof(m->channel_list))); + + at76_dbg(DBG_MIB, "%s: MIB MDOMAIN: tx_powerlevel %s", + priv->netdev->name, + hex2str(m->tx_powerlevel, sizeof(m->tx_powerlevel))); +exit: + kfree(m); +} + +static int at76_get_current_bssid(struct at76_priv *priv) +{ + int ret = 0; + struct mib_mac_mgmt *mac_mgmt = + kmalloc(sizeof(struct mib_mac_mgmt), GFP_KERNEL); + + if (!mac_mgmt) { + ret = -ENOMEM; + goto exit; + } + + ret = at76_get_mib(priv->udev, MIB_MAC_MGMT, mac_mgmt, + sizeof(struct mib_mac_mgmt)); + if (ret < 0) { + printk(KERN_ERR "%s: at76_get_mib failed: %d\n", + priv->netdev->name, ret); + goto error; + } + memcpy(priv->bssid, mac_mgmt->current_bssid, ETH_ALEN); + printk(KERN_INFO "%s: using BSSID %s\n", priv->netdev->name, + mac2str(priv->bssid)); +error: + kfree(mac_mgmt); +exit: + return ret; +} + +static int at76_get_current_channel(struct at76_priv *priv) +{ + int ret = 0; + struct mib_phy *phy = kmalloc(sizeof(struct mib_phy), GFP_KERNEL); + + if (!phy) { + ret = -ENOMEM; + goto exit; + } + ret = at76_get_mib(priv->udev, MIB_PHY, phy, sizeof(struct mib_phy)); + if (ret < 0) { + printk(KERN_ERR "%s: at76_get_mib(MIB_PHY) failed: %d\n", + priv->netdev->name, ret); + goto error; + } + priv->channel = phy->channel_id; +error: + kfree(phy); +exit: + return ret; +} + +/** + * at76_start_scan - start a scan + * + * @use_essid - use the configured ESSID in non passive mode + */ +static int at76_start_scan(struct at76_priv *priv, int use_essid) +{ + struct at76_req_scan scan; + + memset(&scan, 0, sizeof(struct at76_req_scan)); + memset(scan.bssid, 0xff, ETH_ALEN); + + if (use_essid) { + memcpy(scan.essid, priv->essid, IW_ESSID_MAX_SIZE); + scan.essid_size = priv->essid_size; + } else + scan.essid_size = 0; + + /* jal: why should we start at a certain channel? we do scan the whole + range allowed by reg domain. */ + scan.channel = priv->channel; + + /* atmelwlandriver differs between scan type 0 and 1 (active/passive) + For ad-hoc mode, it uses type 0 only. */ + scan.scan_type = priv->scan_mode; + + /* INFO: For probe_delay, not multiplying by 1024 as this will be + slightly less than min_channel_time + (per spec: probe delay < min. channel time) */ + scan.min_channel_time = cpu_to_le16(priv->scan_min_time); + scan.max_channel_time = cpu_to_le16(priv->scan_max_time); + scan.probe_delay = cpu_to_le16(priv->scan_min_time * 1000); + scan.international_scan = 0; + + /* other values are set to 0 for type 0 */ + + at76_dbg(DBG_PROGRESS, "%s: start_scan (use_essid = %d, intl = %d, " + "channel = %d, probe_delay = %d, scan_min_time = %d, " + "scan_max_time = %d)", + priv->netdev->name, use_essid, + scan.international_scan, scan.channel, + le16_to_cpu(scan.probe_delay), + le16_to_cpu(scan.min_channel_time), + le16_to_cpu(scan.max_channel_time)); + + return at76_set_card_command(priv->udev, CMD_SCAN, &scan, sizeof(scan)); +} + +/* Enable monitor mode */ +static int at76_start_monitor(struct at76_priv *priv) +{ + struct at76_req_scan scan; + int ret; + + memset(&scan, 0, sizeof(struct at76_req_scan)); + memset(scan.bssid, 0xff, ETH_ALEN); + + scan.channel = priv->channel; + scan.scan_type = SCAN_TYPE_PASSIVE; + scan.international_scan = 0; + + ret = at76_set_card_command(priv->udev, CMD_SCAN, &scan, sizeof(scan)); + if (ret >= 0) + ret = at76_get_cmd_status(priv->udev, CMD_SCAN); + + return ret; +} + +static int at76_start_ibss(struct at76_priv *priv) +{ + struct at76_req_ibss bss; + int ret; + + WARN_ON(priv->mac_state != MAC_OWN_IBSS); + if (priv->mac_state != MAC_OWN_IBSS) + return -EBUSY; + + memset(&bss, 0, sizeof(struct at76_req_ibss)); + memset(bss.bssid, 0xff, ETH_ALEN); + memcpy(bss.essid, priv->essid, IW_ESSID_MAX_SIZE); + bss.essid_size = priv->essid_size; + bss.bss_type = ADHOC_MODE; + bss.channel = priv->channel; + + ret = at76_set_card_command(priv->udev, CMD_START_IBSS, &bss, + sizeof(struct at76_req_ibss)); + if (ret < 0) { + printk(KERN_ERR "%s: start_ibss failed: %d\n", + priv->netdev->name, ret); + return ret; + } + + ret = at76_wait_completion(priv, CMD_START_IBSS); + if (ret != CMD_STATUS_COMPLETE) { + printk(KERN_ERR "%s: start_ibss failed to complete, %d\n", + priv->netdev->name, ret); + return ret; + } + + ret = at76_get_current_bssid(priv); + if (ret < 0) + return ret; + + ret = at76_get_current_channel(priv); + if (ret < 0) + return ret; + + /* not sure what this is good for ??? */ + priv->mib_buf.type = MIB_MAC_MGMT; + priv->mib_buf.size = 1; + priv->mib_buf.index = offsetof(struct mib_mac_mgmt, ibss_change); + priv->mib_buf.data.byte = 0; + + ret = at76_set_mib(priv, &priv->mib_buf); + if (ret < 0) { + printk(KERN_ERR "%s: set_mib (ibss change ok) failed: %d\n", + priv->netdev->name, ret); + return ret; + } + + netif_carrier_on(priv->netdev); + netif_start_queue(priv->netdev); + return 0; +} + +/* Request card to join BSS in managed or ad-hoc mode */ +static int at76_join_bss(struct at76_priv *priv, struct bss_info *ptr) +{ + struct at76_req_join join; + + BUG_ON(!ptr); + + memset(&join, 0, sizeof(struct at76_req_join)); + memcpy(join.bssid, ptr->bssid, ETH_ALEN); + memcpy(join.essid, ptr->ssid, ptr->ssid_len); + join.essid_size = ptr->ssid_len; + join.bss_type = (priv->iw_mode == IW_MODE_ADHOC ? 1 : 2); + join.channel = ptr->channel; + join.timeout = cpu_to_le16(2000); + + at76_dbg(DBG_PROGRESS, + "%s join addr %s ssid %s type %d ch %d timeout %d", + priv->netdev->name, mac2str(join.bssid), join.essid, + join.bss_type, join.channel, le16_to_cpu(join.timeout)); + return at76_set_card_command(priv->udev, CMD_JOIN, &join, + sizeof(struct at76_req_join)); +} + +/* Calculate padding from txbuf->wlength (which excludes the USB TX header), + likely to compensate a flaw in the AT76C503A USB part ... */ +static inline int at76_calc_padding(int wlen) +{ + /* add the USB TX header */ + wlen += AT76_TX_HDRLEN; + + wlen = wlen % 64; + + if (wlen < 50) + return 50 - wlen; + + if (wlen >= 61) + return 64 + 50 - wlen; + + return 0; +} + +/* We are doing a lot of things here in an interrupt. Need + a bh handler (Watching TV with a TV card is probably + a good test: if you see flickers, we are doing too much. + Currently I do see flickers... even with our tasklet :-( ) + Maybe because the bttv driver and usb-uhci use the same interrupt +*/ +/* Or maybe because our BH handler is preempting bttv's BH handler.. BHs don't + * solve everything.. (alex) */ +static void at76_rx_callback(struct urb *urb) +{ + struct at76_priv *priv = urb->context; + + priv->rx_tasklet.data = (unsigned long)urb; + tasklet_schedule(&priv->rx_tasklet); + return; +} + +static void at76_tx_callback(struct urb *urb) +{ + struct at76_priv *priv = urb->context; + struct net_device_stats *stats = &priv->stats; + unsigned long flags; + struct at76_tx_buffer *mgmt_buf; + int ret; + + switch (urb->status) { + case 0: + stats->tx_packets++; + break; + case -ENOENT: + case -ECONNRESET: + /* urb has been unlinked */ + return; + default: + at76_dbg(DBG_URB, "%s - nonzero tx status received: %d", + __func__, urb->status); + stats->tx_errors++; + break; + } + + spin_lock_irqsave(&priv->mgmt_spinlock, flags); + mgmt_buf = priv->next_mgmt_bulk; + priv->next_mgmt_bulk = NULL; + spin_unlock_irqrestore(&priv->mgmt_spinlock, flags); + + if (!mgmt_buf) { + netif_wake_queue(priv->netdev); + return; + } + + /* we don't copy the padding bytes, but add them + to the length */ + memcpy(priv->bulk_out_buffer, mgmt_buf, + le16_to_cpu(mgmt_buf->wlength) + AT76_TX_HDRLEN); + usb_fill_bulk_urb(priv->tx_urb, priv->udev, priv->tx_pipe, + priv->bulk_out_buffer, + le16_to_cpu(mgmt_buf->wlength) + mgmt_buf->padding + + AT76_TX_HDRLEN, at76_tx_callback, priv); + ret = usb_submit_urb(priv->tx_urb, GFP_ATOMIC); + if (ret) + printk(KERN_ERR "%s: error in tx submit urb: %d\n", + priv->netdev->name, ret); + + kfree(mgmt_buf); +} + +/* Send a management frame on bulk-out. txbuf->wlength must be set */ +static int at76_tx_mgmt(struct at76_priv *priv, struct at76_tx_buffer *txbuf) +{ + unsigned long flags; + int ret; + int urb_status; + void *oldbuf = NULL; + + netif_carrier_off(priv->netdev); /* stop netdev watchdog */ + netif_stop_queue(priv->netdev); /* stop tx data packets */ + + spin_lock_irqsave(&priv->mgmt_spinlock, flags); + + urb_status = priv->tx_urb->status; + if (urb_status == -EINPROGRESS) { + /* cannot transmit now, put in the queue */ + oldbuf = priv->next_mgmt_bulk; + priv->next_mgmt_bulk = txbuf; + } + spin_unlock_irqrestore(&priv->mgmt_spinlock, flags); + + if (oldbuf) { + /* a data/mgmt tx is already pending in the URB - + if this is no error in some situations we must + implement a queue or silently modify the old msg */ + printk(KERN_ERR "%s: removed pending mgmt buffer %s\n", + priv->netdev->name, hex2str(oldbuf, 64)); + kfree(oldbuf); + return 0; + } + + txbuf->tx_rate = TX_RATE_1MBIT; + txbuf->padding = at76_calc_padding(le16_to_cpu(txbuf->wlength)); + memset(txbuf->reserved, 0, sizeof(txbuf->reserved)); + + if (priv->next_mgmt_bulk) + printk(KERN_ERR "%s: URB status %d, but mgmt is pending\n", + priv->netdev->name, urb_status); + + at76_dbg(DBG_TX_MGMT, + "%s: tx mgmt: wlen %d tx_rate %d pad %d %s", + priv->netdev->name, le16_to_cpu(txbuf->wlength), + txbuf->tx_rate, txbuf->padding, + hex2str(txbuf->packet, le16_to_cpu(txbuf->wlength))); + + /* txbuf was not consumed above -> send mgmt msg immediately */ + memcpy(priv->bulk_out_buffer, txbuf, + le16_to_cpu(txbuf->wlength) + AT76_TX_HDRLEN); + usb_fill_bulk_urb(priv->tx_urb, priv->udev, priv->tx_pipe, + priv->bulk_out_buffer, + le16_to_cpu(txbuf->wlength) + txbuf->padding + + AT76_TX_HDRLEN, at76_tx_callback, priv); + ret = usb_submit_urb(priv->tx_urb, GFP_ATOMIC); + if (ret) + printk(KERN_ERR "%s: error in tx submit urb: %d\n", + priv->netdev->name, ret); + + kfree(txbuf); + + return ret; +} + +/* Go to the next information element */ +static inline void next_ie(struct ieee80211_info_element **ie) +{ + *ie = (struct ieee80211_info_element *)(&(*ie)->data[(*ie)->len]); +} + +/* Challenge is the challenge string (in TLV format) + we got with seq_nr 2 for shared secret authentication only and + send in seq_nr 3 WEP encrypted to prove we have the correct WEP key; + otherwise it is NULL */ +static int at76_auth_req(struct at76_priv *priv, struct bss_info *bss, + int seq_nr, struct ieee80211_info_element *challenge) +{ + struct at76_tx_buffer *tx_buffer; + struct ieee80211_hdr_3addr *mgmt; + struct ieee80211_auth *req; + int buf_len = (seq_nr != 3 ? AUTH_FRAME_SIZE : + AUTH_FRAME_SIZE + 1 + 1 + challenge->len); + + BUG_ON(!bss); + BUG_ON(seq_nr == 3 && !challenge); + tx_buffer = kmalloc(buf_len + MAX_PADDING_SIZE, GFP_ATOMIC); + if (!tx_buffer) + return -ENOMEM; + + req = (struct ieee80211_auth *)tx_buffer->packet; + mgmt = &req->header; + + /* make wireless header */ + /* first auth msg is not encrypted, only the second (seq_nr == 3) */ + mgmt->frame_ctl = + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH | + (seq_nr == 3 ? IEEE80211_FCTL_PROTECTED : 0)); + + mgmt->duration_id = cpu_to_le16(0x8000); + memcpy(mgmt->addr1, bss->bssid, ETH_ALEN); + memcpy(mgmt->addr2, priv->netdev->dev_addr, ETH_ALEN); + memcpy(mgmt->addr3, bss->bssid, ETH_ALEN); + mgmt->seq_ctl = cpu_to_le16(0); + + req->algorithm = cpu_to_le16(priv->auth_mode); + req->transaction = cpu_to_le16(seq_nr); + req->status = cpu_to_le16(0); + + if (seq_nr == 3) + memcpy(req->info_element, challenge, 1 + 1 + challenge->len); + + /* init. at76_priv tx header */ + tx_buffer->wlength = cpu_to_le16(buf_len - AT76_TX_HDRLEN); + at76_dbg(DBG_TX_MGMT, "%s: AuthReq bssid %s alg %d seq_nr %d", + priv->netdev->name, mac2str(mgmt->addr3), + le16_to_cpu(req->algorithm), le16_to_cpu(req->transaction)); + if (seq_nr == 3) + at76_dbg(DBG_TX_MGMT, "%s: AuthReq challenge: %s ...", + priv->netdev->name, hex2str(req->info_element, 18)); + + /* either send immediately (if no data tx is pending + or put it in pending list */ + return at76_tx_mgmt(priv, tx_buffer); +} + +static int at76_assoc_req(struct at76_priv *priv, struct bss_info *bss) +{ + struct at76_tx_buffer *tx_buffer; + struct ieee80211_hdr_3addr *mgmt; + struct ieee80211_assoc_request *req; + struct ieee80211_info_element *ie; + char *essid; + int essid_len; + u16 capa; + + BUG_ON(!bss); + + tx_buffer = kmalloc(ASSOCREQ_MAX_SIZE + MAX_PADDING_SIZE, GFP_ATOMIC); + if (!tx_buffer) + return -ENOMEM; + + req = (struct ieee80211_assoc_request *)tx_buffer->packet; + mgmt = &req->header; + ie = req->info_element; + + /* make wireless header */ + mgmt->frame_ctl = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_ASSOC_REQ); + + mgmt->duration_id = cpu_to_le16(0x8000); + memcpy(mgmt->addr1, bss->bssid, ETH_ALEN); + memcpy(mgmt->addr2, priv->netdev->dev_addr, ETH_ALEN); + memcpy(mgmt->addr3, bss->bssid, ETH_ALEN); + mgmt->seq_ctl = cpu_to_le16(0); + + /* we must set the Privacy bit in the capabilities to assure an + Agere-based AP with optional WEP transmits encrypted frames + to us. AP only set the Privacy bit in their capabilities + if WEP is mandatory in the BSS! */ + capa = bss->capa; + if (priv->wep_enabled) + capa |= WLAN_CAPABILITY_PRIVACY; + if (priv->preamble_type != PREAMBLE_TYPE_LONG) + capa |= WLAN_CAPABILITY_SHORT_PREAMBLE; + req->capability = cpu_to_le16(capa); + + req->listen_interval = cpu_to_le16(2 * bss->beacon_interval); + + /* write TLV data elements */ + + ie->id = MFIE_TYPE_SSID; + ie->len = bss->ssid_len; + memcpy(ie->data, bss->ssid, bss->ssid_len); + next_ie(&ie); + + ie->id = MFIE_TYPE_RATES; + ie->len = sizeof(hw_rates); + memcpy(ie->data, hw_rates, sizeof(hw_rates)); + next_ie(&ie); /* ie points behind the supp_rates field */ + + /* init. at76_priv tx header */ + tx_buffer->wlength = cpu_to_le16((u8 *)ie - (u8 *)mgmt); + + ie = req->info_element; + essid = ie->data; + essid_len = min_t(int, IW_ESSID_MAX_SIZE, ie->len); + + next_ie(&ie); /* points to IE of rates now */ + at76_dbg(DBG_TX_MGMT, + "%s: AssocReq bssid %s capa 0x%04x ssid %.*s rates %s", + priv->netdev->name, mac2str(mgmt->addr3), + le16_to_cpu(req->capability), essid_len, essid, + hex2str(ie->data, ie->len)); + + /* either send immediately (if no data tx is pending + or put it in pending list */ + return at76_tx_mgmt(priv, tx_buffer); +} + +/* We got to check the bss_list for old entries */ +static void at76_bss_list_timeout(unsigned long par) +{ + struct at76_priv *priv = (struct at76_priv *)par; + unsigned long flags; + struct list_head *lptr, *nptr; + struct bss_info *ptr; + + spin_lock_irqsave(&priv->bss_list_spinlock, flags); + + list_for_each_safe(lptr, nptr, &priv->bss_list) { + + ptr = list_entry(lptr, struct bss_info, list); + + if (ptr != priv->curr_bss + && time_after(jiffies, ptr->last_rx + BSS_LIST_TIMEOUT)) { + at76_dbg(DBG_BSS_TABLE_RM, + "%s: bss_list: removing old BSS %s ch %d", + priv->netdev->name, mac2str(ptr->bssid), + ptr->channel); + list_del(&ptr->list); + kfree(ptr); + } + } + spin_unlock_irqrestore(&priv->bss_list_spinlock, flags); + /* restart the timer */ + mod_timer(&priv->bss_list_timer, jiffies + BSS_LIST_TIMEOUT); +} + +static inline void at76_set_mac_state(struct at76_priv *priv, + enum mac_state mac_state) +{ + at76_dbg(DBG_MAC_STATE, "%s state: %s", priv->netdev->name, + mac_states[mac_state]); + priv->mac_state = mac_state; +} + +static void at76_dump_bss_table(struct at76_priv *priv) +{ + struct bss_info *ptr; + unsigned long flags; + struct list_head *lptr; + + spin_lock_irqsave(&priv->bss_list_spinlock, flags); + + at76_dbg(DBG_BSS_TABLE, "%s BSS table (curr=%p):", priv->netdev->name, + priv->curr_bss); + + list_for_each(lptr, &priv->bss_list) { + ptr = list_entry(lptr, struct bss_info, list); + at76_dbg(DBG_BSS_TABLE, "0x%p: bssid %s channel %d ssid %.*s " + "(%s) capa 0x%04x rates %s rssi %d link %d noise %d", + ptr, mac2str(ptr->bssid), ptr->channel, ptr->ssid_len, + ptr->ssid, hex2str(ptr->ssid, ptr->ssid_len), + ptr->capa, hex2str(ptr->rates, ptr->rates_len), + ptr->rssi, ptr->link_qual, ptr->noise_level); + } + spin_unlock_irqrestore(&priv->bss_list_spinlock, flags); +} + +/* Called upon successful association to mark interface as connected */ +static void at76_work_assoc_done(struct work_struct *work) +{ + struct at76_priv *priv = container_of(work, struct at76_priv, + work_assoc_done); + + mutex_lock(&priv->mtx); + + WARN_ON(priv->mac_state != MAC_ASSOC); + WARN_ON(!priv->curr_bss); + if (priv->mac_state != MAC_ASSOC || !priv->curr_bss) + goto exit; + + if (priv->iw_mode == IW_MODE_INFRA) { + if (priv->pm_mode != AT76_PM_OFF) { + /* calculate the listen interval in units of + beacon intervals of the curr_bss */ + u32 pm_period_beacon = (priv->pm_period >> 10) / + priv->curr_bss->beacon_interval; + + pm_period_beacon = max(pm_period_beacon, 2u); + pm_period_beacon = min(pm_period_beacon, 0xffffu); + + at76_dbg(DBG_PM, + "%s: pm_mode %d assoc id 0x%x listen int %d", + priv->netdev->name, priv->pm_mode, + priv->assoc_id, pm_period_beacon); + + at76_set_associd(priv, priv->assoc_id); + at76_set_listen_interval(priv, (u16)pm_period_beacon); + } + schedule_delayed_work(&priv->dwork_beacon, BEACON_TIMEOUT); + } + at76_set_pm_mode(priv); + + netif_carrier_on(priv->netdev); + netif_wake_queue(priv->netdev); + at76_set_mac_state(priv, MAC_CONNECTED); + at76_iwevent_bss_connect(priv->netdev, priv->curr_bss->bssid); + at76_dbg(DBG_PROGRESS, "%s: connected to BSSID %s", + priv->netdev->name, mac2str(priv->curr_bss->bssid)); + +exit: + mutex_unlock(&priv->mtx); +} + +/* We only store the new mac address in netdev struct, + it gets set when the netdev is opened. */ +static int at76_set_mac_address(struct net_device *netdev, void *addr) +{ + struct sockaddr *mac = addr; + memcpy(netdev->dev_addr, mac->sa_data, ETH_ALEN); + return 1; +} + +static struct net_device_stats *at76_get_stats(struct net_device *netdev) +{ + struct at76_priv *priv = netdev_priv(netdev); + return &priv->stats; +} + +static struct iw_statistics *at76_get_wireless_stats(struct net_device *netdev) +{ + struct at76_priv *priv = netdev_priv(netdev); + + at76_dbg(DBG_IOCTL, "RETURN qual %d level %d noise %d updated %d", + priv->wstats.qual.qual, priv->wstats.qual.level, + priv->wstats.qual.noise, priv->wstats.qual.updated); + + return &priv->wstats; +} + +static void at76_set_multicast(struct net_device *netdev) +{ + struct at76_priv *priv = netdev_priv(netdev); + int promisc; + + promisc = ((netdev->flags & IFF_PROMISC) != 0); + if (promisc != priv->promisc) { + /* This gets called in interrupt, must reschedule */ + priv->promisc = promisc; + schedule_work(&priv->work_set_promisc); + } +} + +/* Stop all network activity, flush all pending tasks */ +static void at76_quiesce(struct at76_priv *priv) +{ + unsigned long flags; + + netif_stop_queue(priv->netdev); + netif_carrier_off(priv->netdev); + + at76_set_mac_state(priv, MAC_INIT); + + cancel_delayed_work(&priv->dwork_get_scan); + cancel_delayed_work(&priv->dwork_beacon); + cancel_delayed_work(&priv->dwork_auth); + cancel_delayed_work(&priv->dwork_assoc); + cancel_delayed_work(&priv->dwork_restart); + + spin_lock_irqsave(&priv->mgmt_spinlock, flags); + kfree(priv->next_mgmt_bulk); + priv->next_mgmt_bulk = NULL; + spin_unlock_irqrestore(&priv->mgmt_spinlock, flags); +} + +/******************************************************************************* + * at76_priv implementations of iw_handler functions: + */ +static int at76_iw_handler_commit(struct net_device *netdev, + struct iw_request_info *info, + void *null, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + + at76_dbg(DBG_IOCTL, "%s %s: restarting the device", netdev->name, + __func__); + + if (priv->mac_state != MAC_INIT) + at76_quiesce(priv); + + /* Wait half second before the restart to process subsequent + * requests from the same iwconfig in a single restart */ + schedule_delayed_work(&priv->dwork_restart, HZ / 2); + + return 0; +} + +static int at76_iw_handler_get_name(struct net_device *netdev, + struct iw_request_info *info, + char *name, char *extra) +{ + strcpy(name, "IEEE 802.11b"); + at76_dbg(DBG_IOCTL, "%s: SIOCGIWNAME - name %s", netdev->name, name); + return 0; +} + +static int at76_iw_handler_set_freq(struct net_device *netdev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + int chan = -1; + int ret = -EIWCOMMIT; + at76_dbg(DBG_IOCTL, "%s: SIOCSIWFREQ - freq.m %d freq.e %d", + netdev->name, freq->m, freq->e); + + if ((freq->e == 0) && (freq->m <= 1000)) + /* Setting by channel number */ + chan = freq->m; + else { + /* Setting by frequency - search the table */ + int mult = 1; + int i; + + for (i = 0; i < (6 - freq->e); i++) + mult *= 10; + + for (i = 0; i < NUM_CHANNELS; i++) { + if (freq->m == (channel_frequency[i] * mult)) + chan = i + 1; + } + } + + if (chan < 1 || !priv->domain) + /* non-positive channels are invalid + * we need a domain info to set the channel + * either that or an invalid frequency was + * provided by the user */ + ret = -EINVAL; + else if (!(priv->domain->channel_map & (1 << (chan - 1)))) { + printk(KERN_INFO "%s: channel %d not allowed for domain %s\n", + priv->netdev->name, chan, priv->domain->name); + ret = -EINVAL; + } + + if (ret == -EIWCOMMIT) { + priv->channel = chan; + at76_dbg(DBG_IOCTL, "%s: SIOCSIWFREQ - ch %d", netdev->name, + chan); + } + + return ret; +} + +static int at76_iw_handler_get_freq(struct net_device *netdev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + + freq->m = priv->channel; + freq->e = 0; + + if (priv->channel) + at76_dbg(DBG_IOCTL, "%s: SIOCGIWFREQ - freq %ld x 10e%d", + netdev->name, channel_frequency[priv->channel - 1], 6); + + at76_dbg(DBG_IOCTL, "%s: SIOCGIWFREQ - ch %d", netdev->name, + priv->channel); + + return 0; +} + +static int at76_iw_handler_set_mode(struct net_device *netdev, + struct iw_request_info *info, + __u32 *mode, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + + at76_dbg(DBG_IOCTL, "%s: SIOCSIWMODE - %d", netdev->name, *mode); + + if ((*mode != IW_MODE_ADHOC) && (*mode != IW_MODE_INFRA) && + (*mode != IW_MODE_MONITOR)) + return -EINVAL; + + priv->iw_mode = *mode; + if (priv->iw_mode != IW_MODE_INFRA) + priv->pm_mode = AT76_PM_OFF; + + return -EIWCOMMIT; +} + +static int at76_iw_handler_get_mode(struct net_device *netdev, + struct iw_request_info *info, + __u32 *mode, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + + *mode = priv->iw_mode; + + at76_dbg(DBG_IOCTL, "%s: SIOCGIWMODE - %d", netdev->name, *mode); + + return 0; +} + +static int at76_iw_handler_get_range(struct net_device *netdev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + /* inspired by atmel.c */ + struct at76_priv *priv = netdev_priv(netdev); + struct iw_range *range = (struct iw_range *)extra; + int i; + + data->length = sizeof(struct iw_range); + memset(range, 0, sizeof(struct iw_range)); + + /* TODO: range->throughput = xxxxxx; */ + + range->min_nwid = 0x0000; + range->max_nwid = 0x0000; + + /* this driver doesn't maintain sensitivity information */ + range->sensitivity = 0; + + range->max_qual.qual = 100; + range->max_qual.level = 100; + range->max_qual.noise = 0; + range->max_qual.updated = IW_QUAL_NOISE_INVALID; + + range->avg_qual.qual = 50; + range->avg_qual.level = 50; + range->avg_qual.noise = 0; + range->avg_qual.updated = IW_QUAL_NOISE_INVALID; + + range->bitrate[0] = 1000000; + range->bitrate[1] = 2000000; + range->bitrate[2] = 5500000; + range->bitrate[3] = 11000000; + range->num_bitrates = 4; + + range->min_rts = 0; + range->max_rts = MAX_RTS_THRESHOLD; + + range->min_frag = MIN_FRAG_THRESHOLD; + range->max_frag = MAX_FRAG_THRESHOLD; + + range->pmp_flags = IW_POWER_PERIOD; + range->pmt_flags = IW_POWER_ON; + range->pm_capa = IW_POWER_PERIOD | IW_POWER_ALL_R; + + range->encoding_size[0] = WEP_SMALL_KEY_LEN; + range->encoding_size[1] = WEP_LARGE_KEY_LEN; + range->num_encoding_sizes = 2; + range->max_encoding_tokens = WEP_KEYS; + + /* both WL-240U and Linksys WUSB11 v2.6 specify 15 dBm as output power + - take this for all (ignore antenna gains) */ + range->txpower[0] = 15; + range->num_txpower = 1; + range->txpower_capa = IW_TXPOW_DBM; + + range->we_version_source = WIRELESS_EXT; + range->we_version_compiled = WIRELESS_EXT; + + /* same as the values used in atmel.c */ + range->retry_capa = IW_RETRY_LIMIT; + range->retry_flags = IW_RETRY_LIMIT; + range->r_time_flags = 0; + range->min_retry = 1; + range->max_retry = 255; + + range->num_channels = NUM_CHANNELS; + range->num_frequency = 0; + + for (i = 0; i < NUM_CHANNELS; i++) { + /* test if channel map bit is raised */ + if (priv->domain->channel_map & (0x1 << i)) { + range->num_frequency += 1; + + range->freq[i].i = i + 1; + range->freq[i].m = channel_frequency[i] * 100000; + range->freq[i].e = 1; /* freq * 10^1 */ + } + } + + at76_dbg(DBG_IOCTL, "%s: SIOCGIWRANGE", netdev->name); + + return 0; +} + +static int at76_iw_handler_set_spy(struct net_device *netdev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + int ret = 0; + + at76_dbg(DBG_IOCTL, "%s: SIOCSIWSPY - number of addresses %d", + netdev->name, data->length); + + spin_lock_bh(&priv->spy_spinlock); + ret = iw_handler_set_spy(priv->netdev, info, (union iwreq_data *)data, + extra); + spin_unlock_bh(&priv->spy_spinlock); + + return ret; +} + +static int at76_iw_handler_get_spy(struct net_device *netdev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + + struct at76_priv *priv = netdev_priv(netdev); + int ret = 0; + + spin_lock_bh(&priv->spy_spinlock); + ret = iw_handler_get_spy(priv->netdev, info, + (union iwreq_data *)data, extra); + spin_unlock_bh(&priv->spy_spinlock); + + at76_dbg(DBG_IOCTL, "%s: SIOCGIWSPY - number of addresses %d", + netdev->name, data->length); + + return ret; +} + +static int at76_iw_handler_set_thrspy(struct net_device *netdev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + int ret; + + at76_dbg(DBG_IOCTL, "%s: SIOCSIWTHRSPY - number of addresses %d)", + netdev->name, data->length); + + spin_lock_bh(&priv->spy_spinlock); + ret = iw_handler_set_thrspy(netdev, info, (union iwreq_data *)data, + extra); + spin_unlock_bh(&priv->spy_spinlock); + + return ret; +} + +static int at76_iw_handler_get_thrspy(struct net_device *netdev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + int ret; + + spin_lock_bh(&priv->spy_spinlock); + ret = iw_handler_get_thrspy(netdev, info, (union iwreq_data *)data, + extra); + spin_unlock_bh(&priv->spy_spinlock); + + at76_dbg(DBG_IOCTL, "%s: SIOCGIWTHRSPY - number of addresses %d)", + netdev->name, data->length); + + return ret; +} + +static int at76_iw_handler_set_wap(struct net_device *netdev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + + at76_dbg(DBG_IOCTL, "%s: SIOCSIWAP - wap/bssid %s", netdev->name, + mac2str(ap_addr->sa_data)); + + /* if the incoming address == ff:ff:ff:ff:ff:ff, the user has + chosen any or auto AP preference */ + if (is_broadcast_ether_addr(ap_addr->sa_data) + || is_zero_ether_addr(ap_addr->sa_data)) + priv->wanted_bssid_valid = 0; + else { + /* user wants to set a preferred AP address */ + priv->wanted_bssid_valid = 1; + memcpy(priv->wanted_bssid, ap_addr->sa_data, ETH_ALEN); + } + + return -EIWCOMMIT; +} + +static int at76_iw_handler_get_wap(struct net_device *netdev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + + ap_addr->sa_family = ARPHRD_ETHER; + memcpy(ap_addr->sa_data, priv->bssid, ETH_ALEN); + + at76_dbg(DBG_IOCTL, "%s: SIOCGIWAP - wap/bssid %s", netdev->name, + mac2str(ap_addr->sa_data)); + + return 0; +} + +static int at76_iw_handler_set_scan(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + int ret = 0; + + at76_dbg(DBG_IOCTL, "%s: SIOCSIWSCAN", netdev->name); + + if (mutex_lock_interruptible(&priv->mtx)) + return -EINTR; + + if (!netif_running(netdev)) { + ret = -ENETDOWN; + goto exit; + } + + /* jal: we don't allow "iwlist ethX scan" while we are + in monitor mode */ + if (priv->iw_mode == IW_MODE_MONITOR) { + ret = -EBUSY; + goto exit; + } + + /* Discard old scan results */ + if ((jiffies - priv->last_scan) > (20 * HZ)) + priv->scan_state = SCAN_IDLE; + priv->last_scan = jiffies; + + /* Initiate a scan command */ + if (priv->scan_state == SCAN_IN_PROGRESS) { + ret = -EBUSY; + goto exit; + } + + priv->scan_state = SCAN_IN_PROGRESS; + + at76_quiesce(priv); + + /* Try to do passive or active scan if WE asks as. */ + if (wrqu->data.length + && wrqu->data.length == sizeof(struct iw_scan_req)) { + struct iw_scan_req *req = (struct iw_scan_req *)extra; + + if (req->scan_type == IW_SCAN_TYPE_PASSIVE) + priv->scan_mode = SCAN_TYPE_PASSIVE; + else if (req->scan_type == IW_SCAN_TYPE_ACTIVE) + priv->scan_mode = SCAN_TYPE_ACTIVE; + + /* Sanity check values? */ + if (req->min_channel_time > 0) + priv->scan_min_time = req->min_channel_time; + + if (req->max_channel_time > 0) + priv->scan_max_time = req->max_channel_time; + } + + /* change to scanning state */ + at76_set_mac_state(priv, MAC_SCANNING); + schedule_work(&priv->work_start_scan); + +exit: + mutex_unlock(&priv->mtx); + return ret; +} + +static int at76_iw_handler_get_scan(struct net_device *netdev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + unsigned long flags; + struct list_head *lptr, *nptr; + struct bss_info *curr_bss; + struct iw_event *iwe = kmalloc(sizeof(struct iw_event), GFP_KERNEL); + char *curr_val, *curr_pos = extra; + int i; + + at76_dbg(DBG_IOCTL, "%s: SIOCGIWSCAN", netdev->name); + + if (!iwe) + return -ENOMEM; + + if (priv->scan_state != SCAN_COMPLETED) + /* scan not yet finished */ + return -EAGAIN; + + spin_lock_irqsave(&priv->bss_list_spinlock, flags); + + list_for_each_safe(lptr, nptr, &priv->bss_list) { + curr_bss = list_entry(lptr, struct bss_info, list); + + iwe->cmd = SIOCGIWAP; + iwe->u.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(iwe->u.ap_addr.sa_data, curr_bss->bssid, 6); + curr_pos = iwe_stream_add_event(curr_pos, + extra + IW_SCAN_MAX_DATA, iwe, + IW_EV_ADDR_LEN); + + iwe->u.data.length = curr_bss->ssid_len; + iwe->cmd = SIOCGIWESSID; + iwe->u.data.flags = 1; + + curr_pos = iwe_stream_add_point(curr_pos, + extra + IW_SCAN_MAX_DATA, iwe, + curr_bss->ssid); + + iwe->cmd = SIOCGIWMODE; + iwe->u.mode = (curr_bss->capa & WLAN_CAPABILITY_IBSS) ? + IW_MODE_ADHOC : + (curr_bss->capa & WLAN_CAPABILITY_ESS) ? + IW_MODE_MASTER : IW_MODE_AUTO; + /* IW_MODE_AUTO = 0 which I thought is + * the most logical value to return in this case */ + curr_pos = iwe_stream_add_event(curr_pos, + extra + IW_SCAN_MAX_DATA, iwe, + IW_EV_UINT_LEN); + + iwe->cmd = SIOCGIWFREQ; + iwe->u.freq.m = curr_bss->channel; + iwe->u.freq.e = 0; + curr_pos = iwe_stream_add_event(curr_pos, + extra + IW_SCAN_MAX_DATA, iwe, + IW_EV_FREQ_LEN); + + iwe->cmd = SIOCGIWENCODE; + if (curr_bss->capa & WLAN_CAPABILITY_PRIVACY) + iwe->u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; + else + iwe->u.data.flags = IW_ENCODE_DISABLED; + + iwe->u.data.length = 0; + curr_pos = iwe_stream_add_point(curr_pos, + extra + IW_SCAN_MAX_DATA, iwe, + NULL); + + /* Add quality statistics */ + iwe->cmd = IWEVQUAL; + iwe->u.qual.noise = 0; + iwe->u.qual.updated = + IW_QUAL_NOISE_INVALID | IW_QUAL_LEVEL_UPDATED; + iwe->u.qual.level = (curr_bss->rssi * 100 / 42); + if (iwe->u.qual.level > 100) + iwe->u.qual.level = 100; + if (at76_is_intersil(priv->board_type)) + iwe->u.qual.qual = curr_bss->link_qual; + else { + iwe->u.qual.qual = 0; + iwe->u.qual.updated |= IW_QUAL_QUAL_INVALID; + } + /* Add new value to event */ + curr_pos = iwe_stream_add_event(curr_pos, + extra + IW_SCAN_MAX_DATA, iwe, + IW_EV_QUAL_LEN); + + /* Rate: stuffing multiple values in a single event requires + * a bit more of magic - Jean II */ + curr_val = curr_pos + IW_EV_LCP_LEN; + + iwe->cmd = SIOCGIWRATE; + /* Those two flags are ignored... */ + iwe->u.bitrate.fixed = 0; + iwe->u.bitrate.disabled = 0; + /* Max 8 values */ + for (i = 0; i < curr_bss->rates_len; i++) { + /* Bit rate given in 500 kb/s units (+ 0x80) */ + iwe->u.bitrate.value = + ((curr_bss->rates[i] & 0x7f) * 500000); + /* Add new value to event */ + curr_val = iwe_stream_add_value(curr_pos, curr_val, + extra + + IW_SCAN_MAX_DATA, iwe, + IW_EV_PARAM_LEN); + } + + /* Check if we added any event */ + if ((curr_val - curr_pos) > IW_EV_LCP_LEN) + curr_pos = curr_val; + + /* more information may be sent back using IWECUSTOM */ + + } + + spin_unlock_irqrestore(&priv->bss_list_spinlock, flags); + + data->length = (curr_pos - extra); + data->flags = 0; + + kfree(iwe); + return 0; +} + +static int at76_iw_handler_set_essid(struct net_device *netdev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + + at76_dbg(DBG_IOCTL, "%s: SIOCSIWESSID - %s", netdev->name, extra); + + if (data->flags) { + memcpy(priv->essid, extra, data->length); + priv->essid_size = data->length; + } else + priv->essid_size = 0; /* Use any SSID */ + + return -EIWCOMMIT; +} + +static int at76_iw_handler_get_essid(struct net_device *netdev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + + if (priv->essid_size) { + /* not the ANY ssid in priv->essid */ + data->flags = 1; + data->length = priv->essid_size; + memcpy(extra, priv->essid, data->length); + } else { + /* the ANY ssid was specified */ + if (priv->mac_state == MAC_CONNECTED && priv->curr_bss) { + /* report the SSID we have found */ + data->flags = 1; + data->length = priv->curr_bss->ssid_len; + memcpy(extra, priv->curr_bss->ssid, data->length); + } else { + /* report ANY back */ + data->flags = 0; + data->length = 0; + } + } + + at76_dbg(DBG_IOCTL, "%s: SIOCGIWESSID - %.*s", netdev->name, + data->length, extra); + + return 0; +} + +static int at76_iw_handler_set_rate(struct net_device *netdev, + struct iw_request_info *info, + struct iw_param *bitrate, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + int ret = -EIWCOMMIT; + + at76_dbg(DBG_IOCTL, "%s: SIOCSIWRATE - %d", netdev->name, + bitrate->value); + + switch (bitrate->value) { + case -1: + priv->txrate = TX_RATE_AUTO; + break; /* auto rate */ + case 1000000: + priv->txrate = TX_RATE_1MBIT; + break; + case 2000000: + priv->txrate = TX_RATE_2MBIT; + break; + case 5500000: + priv->txrate = TX_RATE_5_5MBIT; + break; + case 11000000: + priv->txrate = TX_RATE_11MBIT; + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int at76_iw_handler_get_rate(struct net_device *netdev, + struct iw_request_info *info, + struct iw_param *bitrate, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + int ret = 0; + + switch (priv->txrate) { + /* return max rate if RATE_AUTO */ + case TX_RATE_AUTO: + bitrate->value = 11000000; + break; + case TX_RATE_1MBIT: + bitrate->value = 1000000; + break; + case TX_RATE_2MBIT: + bitrate->value = 2000000; + break; + case TX_RATE_5_5MBIT: + bitrate->value = 5500000; + break; + case TX_RATE_11MBIT: + bitrate->value = 11000000; + break; + default: + ret = -EINVAL; + } + + bitrate->fixed = (priv->txrate != TX_RATE_AUTO); + bitrate->disabled = 0; + + at76_dbg(DBG_IOCTL, "%s: SIOCGIWRATE - %d", netdev->name, + bitrate->value); + + return ret; +} + +static int at76_iw_handler_set_rts(struct net_device *netdev, + struct iw_request_info *info, + struct iw_param *rts, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + int ret = -EIWCOMMIT; + int rthr = rts->value; + + at76_dbg(DBG_IOCTL, "%s: SIOCSIWRTS - value %d disabled %s", + netdev->name, rts->value, (rts->disabled) ? "true" : "false"); + + if (rts->disabled) + rthr = MAX_RTS_THRESHOLD; + + if ((rthr < 0) || (rthr > MAX_RTS_THRESHOLD)) + ret = -EINVAL; + else + priv->rts_threshold = rthr; + + return ret; +} + +static int at76_iw_handler_get_rts(struct net_device *netdev, + struct iw_request_info *info, + struct iw_param *rts, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + + rts->value = priv->rts_threshold; + rts->disabled = (rts->value >= MAX_RTS_THRESHOLD); + rts->fixed = 1; + + at76_dbg(DBG_IOCTL, "%s: SIOCGIWRTS - value %d disabled %s", + netdev->name, rts->value, (rts->disabled) ? "true" : "false"); + + return 0; +} + +static int at76_iw_handler_set_frag(struct net_device *netdev, + struct iw_request_info *info, + struct iw_param *frag, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + int ret = -EIWCOMMIT; + int fthr = frag->value; + + at76_dbg(DBG_IOCTL, "%s: SIOCSIWFRAG - value %d, disabled %s", + netdev->name, frag->value, + (frag->disabled) ? "true" : "false"); + + if (frag->disabled) + fthr = MAX_FRAG_THRESHOLD; + + if ((fthr < MIN_FRAG_THRESHOLD) || (fthr > MAX_FRAG_THRESHOLD)) + ret = -EINVAL; + else + priv->frag_threshold = fthr & ~0x1; /* get an even value */ + + return ret; +} + +static int at76_iw_handler_get_frag(struct net_device *netdev, + struct iw_request_info *info, + struct iw_param *frag, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + + frag->value = priv->frag_threshold; + frag->disabled = (frag->value >= MAX_FRAG_THRESHOLD); + frag->fixed = 1; + + at76_dbg(DBG_IOCTL, "%s: SIOCGIWFRAG - value %d, disabled %s", + netdev->name, frag->value, + (frag->disabled) ? "true" : "false"); + + return 0; +} + +static int at76_iw_handler_get_txpow(struct net_device *netdev, + struct iw_request_info *info, + struct iw_param *power, char *extra) +{ + power->value = 15; + power->fixed = 1; /* No power control */ + power->disabled = 0; + power->flags = IW_TXPOW_DBM; + + at76_dbg(DBG_IOCTL, "%s: SIOCGIWTXPOW - txpow %d dBm", netdev->name, + power->value); + + return 0; +} + +/* jal: short retry is handled by the firmware (at least 0.90.x), + while long retry is not (?) */ +static int at76_iw_handler_set_retry(struct net_device *netdev, + struct iw_request_info *info, + struct iw_param *retry, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + int ret = -EIWCOMMIT; + + at76_dbg(DBG_IOCTL, "%s: SIOCSIWRETRY disabled %d flags 0x%x val %d", + netdev->name, retry->disabled, retry->flags, retry->value); + + if (!retry->disabled && (retry->flags & IW_RETRY_LIMIT)) { + if ((retry->flags & IW_RETRY_MIN) || + !(retry->flags & IW_RETRY_MAX)) + priv->short_retry_limit = retry->value; + else + ret = -EINVAL; + } else + ret = -EINVAL; + + return ret; +} + +/* Adapted (ripped) from atmel.c */ +static int at76_iw_handler_get_retry(struct net_device *netdev, + struct iw_request_info *info, + struct iw_param *retry, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + + at76_dbg(DBG_IOCTL, "%s: SIOCGIWRETRY", netdev->name); + + retry->disabled = 0; /* Can't be disabled */ + retry->flags = IW_RETRY_LIMIT; + retry->value = priv->short_retry_limit; + + return 0; +} + +static int at76_iw_handler_set_encode(struct net_device *netdev, + struct iw_request_info *info, + struct iw_point *encoding, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + int index = (encoding->flags & IW_ENCODE_INDEX) - 1; + int len = encoding->length; + + at76_dbg(DBG_IOCTL, "%s: SIOCSIWENCODE - enc.flags %08x " + "pointer %p len %d", netdev->name, encoding->flags, + encoding->pointer, encoding->length); + at76_dbg(DBG_IOCTL, + "%s: SIOCSIWENCODE - old wepstate: enabled %s key_id %d " + "auth_mode %s", netdev->name, + (priv->wep_enabled) ? "true" : "false", priv->wep_key_id, + (priv->auth_mode == + WLAN_AUTH_SHARED_KEY) ? "restricted" : "open"); + + /* take the old default key if index is invalid */ + if ((index < 0) || (index >= WEP_KEYS)) + index = priv->wep_key_id; + + if (len > 0) { + if (len > WEP_LARGE_KEY_LEN) + len = WEP_LARGE_KEY_LEN; + + memset(priv->wep_keys[index], 0, WEP_KEY_LEN); + memcpy(priv->wep_keys[index], extra, len); + priv->wep_keys_len[index] = (len <= WEP_SMALL_KEY_LEN) ? + WEP_SMALL_KEY_LEN : WEP_LARGE_KEY_LEN; + priv->wep_enabled = 1; + } + + priv->wep_key_id = index; + priv->wep_enabled = ((encoding->flags & IW_ENCODE_DISABLED) == 0); + + if (encoding->flags & IW_ENCODE_RESTRICTED) + priv->auth_mode = WLAN_AUTH_SHARED_KEY; + if (encoding->flags & IW_ENCODE_OPEN) + priv->auth_mode = WLAN_AUTH_OPEN; + + at76_dbg(DBG_IOCTL, + "%s: SIOCSIWENCODE - new wepstate: enabled %s key_id %d " + "key_len %d auth_mode %s", netdev->name, + (priv->wep_enabled) ? "true" : "false", priv->wep_key_id + 1, + priv->wep_keys_len[priv->wep_key_id], + (priv->auth_mode == + WLAN_AUTH_SHARED_KEY) ? "restricted" : "open"); + + return -EIWCOMMIT; +} + +static int at76_iw_handler_get_encode(struct net_device *netdev, + struct iw_request_info *info, + struct iw_point *encoding, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + int index = (encoding->flags & IW_ENCODE_INDEX) - 1; + + if ((index < 0) || (index >= WEP_KEYS)) + index = priv->wep_key_id; + + encoding->flags = + (priv->auth_mode == WLAN_AUTH_SHARED_KEY) ? + IW_ENCODE_RESTRICTED : IW_ENCODE_OPEN; + + if (!priv->wep_enabled) + encoding->flags |= IW_ENCODE_DISABLED; + + if (encoding->pointer) { + encoding->length = priv->wep_keys_len[index]; + + memcpy(extra, priv->wep_keys[index], priv->wep_keys_len[index]); + + encoding->flags |= (index + 1); + } + + at76_dbg(DBG_IOCTL, "%s: SIOCGIWENCODE - enc.flags %08x " + "pointer %p len %d", netdev->name, encoding->flags, + encoding->pointer, encoding->length); + at76_dbg(DBG_IOCTL, + "%s: SIOCGIWENCODE - wepstate: enabled %s key_id %d " + "key_len %d auth_mode %s", netdev->name, + (priv->wep_enabled) ? "true" : "false", priv->wep_key_id + 1, + priv->wep_keys_len[priv->wep_key_id], + (priv->auth_mode == + WLAN_AUTH_SHARED_KEY) ? "restricted" : "open"); + + return 0; +} + +static int at76_iw_handler_set_power(struct net_device *netdev, + struct iw_request_info *info, + struct iw_param *prq, char *extra) +{ + int err = -EIWCOMMIT; + struct at76_priv *priv = netdev_priv(netdev); + + at76_dbg(DBG_IOCTL, + "%s: SIOCSIWPOWER - disabled %s flags 0x%x value 0x%x", + netdev->name, (prq->disabled) ? "true" : "false", prq->flags, + prq->value); + + if (prq->disabled) + priv->pm_mode = AT76_PM_OFF; + else { + switch (prq->flags & IW_POWER_MODE) { + case IW_POWER_ALL_R: + case IW_POWER_ON: + break; + default: + err = -EINVAL; + goto exit; + } + if (prq->flags & IW_POWER_PERIOD) + priv->pm_period = prq->value; + + if (prq->flags & IW_POWER_TIMEOUT) { + err = -EINVAL; + goto exit; + } + priv->pm_mode = AT76_PM_ON; + } +exit: + return err; +} + +static int at76_iw_handler_get_power(struct net_device *netdev, + struct iw_request_info *info, + struct iw_param *power, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + + power->disabled = (priv->pm_mode == AT76_PM_OFF); + if (!power->disabled) { + power->flags = IW_POWER_PERIOD | IW_POWER_ALL_R; + power->value = priv->pm_period; + } + + at76_dbg(DBG_IOCTL, "%s: SIOCGIWPOWER - %s flags 0x%x value 0x%x", + netdev->name, power->disabled ? "disabled" : "enabled", + power->flags, power->value); + + return 0; +} + +/******************************************************************************* + * Private IOCTLS + */ +static int at76_iw_set_short_preamble(struct net_device *netdev, + struct iw_request_info *info, char *name, + char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + int val = *((int *)name); + int ret = -EIWCOMMIT; + + at76_dbg(DBG_IOCTL, "%s: AT76_SET_SHORT_PREAMBLE, %d", + netdev->name, val); + + if (val < PREAMBLE_TYPE_LONG || val > PREAMBLE_TYPE_AUTO) + ret = -EINVAL; + else + priv->preamble_type = val; + + return ret; +} + +static int at76_iw_get_short_preamble(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + + snprintf(wrqu->name, sizeof(wrqu->name), "%s (%d)", + preambles[priv->preamble_type], priv->preamble_type); + return 0; +} + +static int at76_iw_set_debug(struct net_device *netdev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + char *ptr; + u32 val; + + if (data->length > 0) { + val = simple_strtol(extra, &ptr, 0); + + if (ptr == extra) + val = DBG_DEFAULTS; + + at76_dbg(DBG_IOCTL, "%s: AT76_SET_DEBUG input %d: %s -> 0x%x", + netdev->name, data->length, extra, val); + } else + val = DBG_DEFAULTS; + + at76_dbg(DBG_IOCTL, "%s: AT76_SET_DEBUG, old 0x%x, new 0x%x", + netdev->name, at76_debug, val); + + /* jal: some more output to pin down lockups */ + at76_dbg(DBG_IOCTL, "%s: netif running %d queue_stopped %d " + "carrier_ok %d", netdev->name, netif_running(netdev), + netif_queue_stopped(netdev), netif_carrier_ok(netdev)); + + at76_debug = val; + + return 0; +} + +static int at76_iw_get_debug(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + snprintf(wrqu->name, sizeof(wrqu->name), "0x%08x", at76_debug); + return 0; +} + +static int at76_iw_set_powersave_mode(struct net_device *netdev, + struct iw_request_info *info, char *name, + char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + int val = *((int *)name); + int ret = -EIWCOMMIT; + + at76_dbg(DBG_IOCTL, "%s: AT76_SET_POWERSAVE_MODE, %d (%s)", + netdev->name, val, + val == AT76_PM_OFF ? "active" : val == AT76_PM_ON ? "save" : + val == AT76_PM_SMART ? "smart save" : ""); + if (val < AT76_PM_OFF || val > AT76_PM_SMART) + ret = -EINVAL; + else + priv->pm_mode = val; + + return ret; +} + +static int at76_iw_get_powersave_mode(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + int *param = (int *)extra; + + param[0] = priv->pm_mode; + return 0; +} + +static int at76_iw_set_scan_times(struct net_device *netdev, + struct iw_request_info *info, char *name, + char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + int mint = *((int *)name); + int maxt = *((int *)name + 1); + int ret = -EIWCOMMIT; + + at76_dbg(DBG_IOCTL, "%s: AT76_SET_SCAN_TIMES - min %d max %d", + netdev->name, mint, maxt); + if (mint <= 0 || maxt <= 0 || mint > maxt) + ret = -EINVAL; + else { + priv->scan_min_time = mint; + priv->scan_max_time = maxt; + } + + return ret; +} + +static int at76_iw_get_scan_times(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + int *param = (int *)extra; + + param[0] = priv->scan_min_time; + param[1] = priv->scan_max_time; + return 0; +} + +static int at76_iw_set_scan_mode(struct net_device *netdev, + struct iw_request_info *info, char *name, + char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + int val = *((int *)name); + int ret = -EIWCOMMIT; + + at76_dbg(DBG_IOCTL, "%s: AT76_SET_SCAN_MODE - mode %s", + netdev->name, (val = SCAN_TYPE_ACTIVE) ? "active" : + (val = SCAN_TYPE_PASSIVE) ? "passive" : ""); + + if (val != SCAN_TYPE_ACTIVE && val != SCAN_TYPE_PASSIVE) + ret = -EINVAL; + else + priv->scan_mode = val; + + return ret; +} + +static int at76_iw_get_scan_mode(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct at76_priv *priv = netdev_priv(netdev); + int *param = (int *)extra; + + param[0] = priv->scan_mode; + return 0; +} + +#define AT76_SET_HANDLER(h, f) [h - SIOCIWFIRST] = (iw_handler) f + +/* Standard wireless handlers */ +static const iw_handler at76_handlers[] = { + AT76_SET_HANDLER(SIOCSIWCOMMIT, at76_iw_handler_commit), + AT76_SET_HANDLER(SIOCGIWNAME, at76_iw_handler_get_name), + AT76_SET_HANDLER(SIOCSIWFREQ, at76_iw_handler_set_freq), + AT76_SET_HANDLER(SIOCGIWFREQ, at76_iw_handler_get_freq), + AT76_SET_HANDLER(SIOCSIWMODE, at76_iw_handler_set_mode), + AT76_SET_HANDLER(SIOCGIWMODE, at76_iw_handler_get_mode), + AT76_SET_HANDLER(SIOCGIWRANGE, at76_iw_handler_get_range), + AT76_SET_HANDLER(SIOCSIWSPY, at76_iw_handler_set_spy), + AT76_SET_HANDLER(SIOCGIWSPY, at76_iw_handler_get_spy), + AT76_SET_HANDLER(SIOCSIWTHRSPY, at76_iw_handler_set_thrspy), + AT76_SET_HANDLER(SIOCGIWTHRSPY, at76_iw_handler_get_thrspy), + AT76_SET_HANDLER(SIOCSIWAP, at76_iw_handler_set_wap), + AT76_SET_HANDLER(SIOCGIWAP, at76_iw_handler_get_wap), + AT76_SET_HANDLER(SIOCSIWSCAN, at76_iw_handler_set_scan), + AT76_SET_HANDLER(SIOCGIWSCAN, at76_iw_handler_get_scan), + AT76_SET_HANDLER(SIOCSIWESSID, at76_iw_handler_set_essid), + AT76_SET_HANDLER(SIOCGIWESSID, at76_iw_handler_get_essid), + AT76_SET_HANDLER(SIOCSIWRATE, at76_iw_handler_set_rate), + AT76_SET_HANDLER(SIOCGIWRATE, at76_iw_handler_get_rate), + AT76_SET_HANDLER(SIOCSIWRTS, at76_iw_handler_set_rts), + AT76_SET_HANDLER(SIOCGIWRTS, at76_iw_handler_get_rts), + AT76_SET_HANDLER(SIOCSIWFRAG, at76_iw_handler_set_frag), + AT76_SET_HANDLER(SIOCGIWFRAG, at76_iw_handler_get_frag), + AT76_SET_HANDLER(SIOCGIWTXPOW, at76_iw_handler_get_txpow), + AT76_SET_HANDLER(SIOCSIWRETRY, at76_iw_handler_set_retry), + AT76_SET_HANDLER(SIOCGIWRETRY, at76_iw_handler_get_retry), + AT76_SET_HANDLER(SIOCSIWENCODE, at76_iw_handler_set_encode), + AT76_SET_HANDLER(SIOCGIWENCODE, at76_iw_handler_get_encode), + AT76_SET_HANDLER(SIOCSIWPOWER, at76_iw_handler_set_power), + AT76_SET_HANDLER(SIOCGIWPOWER, at76_iw_handler_get_power) +}; + +#define AT76_SET_PRIV(h, f) [h - SIOCIWFIRSTPRIV] = (iw_handler) f + +/* Private wireless handlers */ +static const iw_handler at76_priv_handlers[] = { + AT76_SET_PRIV(AT76_SET_SHORT_PREAMBLE, at76_iw_set_short_preamble), + AT76_SET_PRIV(AT76_GET_SHORT_PREAMBLE, at76_iw_get_short_preamble), + AT76_SET_PRIV(AT76_SET_DEBUG, at76_iw_set_debug), + AT76_SET_PRIV(AT76_GET_DEBUG, at76_iw_get_debug), + AT76_SET_PRIV(AT76_SET_POWERSAVE_MODE, at76_iw_set_powersave_mode), + AT76_SET_PRIV(AT76_GET_POWERSAVE_MODE, at76_iw_get_powersave_mode), + AT76_SET_PRIV(AT76_SET_SCAN_TIMES, at76_iw_set_scan_times), + AT76_SET_PRIV(AT76_GET_SCAN_TIMES, at76_iw_get_scan_times), + AT76_SET_PRIV(AT76_SET_SCAN_MODE, at76_iw_set_scan_mode), + AT76_SET_PRIV(AT76_GET_SCAN_MODE, at76_iw_get_scan_mode), +}; + +/* Names and arguments of private wireless handlers */ +static const struct iw_priv_args at76_priv_args[] = { + /* 0 - long, 1 - short */ + {AT76_SET_SHORT_PREAMBLE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_preamble"}, + + {AT76_GET_SHORT_PREAMBLE, + 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 10, "get_preamble"}, + + /* we must pass the new debug mask as a string, because iwpriv cannot + * parse hex numbers starting with 0x :-( */ + {AT76_SET_DEBUG, + IW_PRIV_TYPE_CHAR | 10, 0, "set_debug"}, + + {AT76_GET_DEBUG, + 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 10, "get_debug"}, + + /* 1 - active, 2 - power save, 3 - smart power save */ + {AT76_SET_POWERSAVE_MODE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_powersave"}, + + {AT76_GET_POWERSAVE_MODE, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_powersave"}, + + /* min_channel_time, max_channel_time */ + {AT76_SET_SCAN_TIMES, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "set_scan_times"}, + + {AT76_GET_SCAN_TIMES, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, "get_scan_times"}, + + /* 0 - active, 1 - passive scan */ + {AT76_SET_SCAN_MODE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_scan_mode"}, + + {AT76_GET_SCAN_MODE, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_scan_mode"}, +}; + +static const struct iw_handler_def at76_handler_def = { + .num_standard = ARRAY_SIZE(at76_handlers), + .num_private = ARRAY_SIZE(at76_priv_handlers), + .num_private_args = ARRAY_SIZE(at76_priv_args), + .standard = at76_handlers, + .private = at76_priv_handlers, + .private_args = at76_priv_args, + .get_wireless_stats = at76_get_wireless_stats, +}; + +static const u8 snapsig[] = { 0xaa, 0xaa, 0x03 }; + +/* RFC 1042 encapsulates Ethernet frames in 802.2 SNAP (0xaa, 0xaa, 0x03) with + * a SNAP OID of 0 (0x00, 0x00, 0x00) */ +static const u8 rfc1042sig[] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; + +static int at76_tx(struct sk_buff *skb, struct net_device *netdev) +{ + struct at76_priv *priv = netdev_priv(netdev); + struct net_device_stats *stats = &priv->stats; + int ret = 0; + int wlen; + int submit_len; + struct at76_tx_buffer *tx_buffer = priv->bulk_out_buffer; + struct ieee80211_hdr_3addr *i802_11_hdr = + (struct ieee80211_hdr_3addr *)tx_buffer->packet; + u8 *payload = i802_11_hdr->payload; + struct ethhdr *eh = (struct ethhdr *)skb->data; + + if (netif_queue_stopped(netdev)) { + printk(KERN_ERR "%s: %s called while netdev is stopped\n", + netdev->name, __func__); + /* skip this packet */ + dev_kfree_skb(skb); + return 0; + } + + if (priv->tx_urb->status == -EINPROGRESS) { + printk(KERN_ERR "%s: %s called while tx urb is pending\n", + netdev->name, __func__); + /* skip this packet */ + dev_kfree_skb(skb); + return 0; + } + + if (skb->len < ETH_HLEN) { + printk(KERN_ERR "%s: %s: skb too short (%d)\n", + netdev->name, __func__, skb->len); + dev_kfree_skb(skb); + return 0; + } + + at76_ledtrig_tx_activity(); /* tell ledtrigger we send a packet */ + + /* we can get rid of memcpy if we set netdev->hard_header_len to + reserve enough space, but we would need to keep the skb around */ + + if (ntohs(eh->h_proto) <= ETH_DATA_LEN) { + /* this is a 802.3 packet */ + if (skb->len >= ETH_HLEN + sizeof(rfc1042sig) + && skb->data[ETH_HLEN] == rfc1042sig[0] + && skb->data[ETH_HLEN + 1] == rfc1042sig[1]) { + /* higher layer delivered SNAP header - keep it */ + memcpy(payload, skb->data + ETH_HLEN, + skb->len - ETH_HLEN); + wlen = IEEE80211_3ADDR_LEN + skb->len - ETH_HLEN; + } else { + printk(KERN_ERR "%s: dropping non-SNAP 802.2 packet " + "(DSAP 0x%02x SSAP 0x%02x cntrl 0x%02x)\n", + priv->netdev->name, skb->data[ETH_HLEN], + skb->data[ETH_HLEN + 1], + skb->data[ETH_HLEN + 2]); + dev_kfree_skb(skb); + return 0; + } + } else { + /* add RFC 1042 header in front */ + memcpy(payload, rfc1042sig, sizeof(rfc1042sig)); + memcpy(payload + sizeof(rfc1042sig), &eh->h_proto, + skb->len - offsetof(struct ethhdr, h_proto)); + wlen = IEEE80211_3ADDR_LEN + sizeof(rfc1042sig) + skb->len - + offsetof(struct ethhdr, h_proto); + } + + /* make wireless header */ + i802_11_hdr->frame_ctl = + cpu_to_le16(IEEE80211_FTYPE_DATA | + (priv->wep_enabled ? IEEE80211_FCTL_PROTECTED : 0) | + (priv->iw_mode == + IW_MODE_INFRA ? IEEE80211_FCTL_TODS : 0)); + + if (priv->iw_mode == IW_MODE_ADHOC) { + memcpy(i802_11_hdr->addr1, eh->h_dest, ETH_ALEN); + memcpy(i802_11_hdr->addr2, eh->h_source, ETH_ALEN); + memcpy(i802_11_hdr->addr3, priv->bssid, ETH_ALEN); + } else if (priv->iw_mode == IW_MODE_INFRA) { + memcpy(i802_11_hdr->addr1, priv->bssid, ETH_ALEN); + memcpy(i802_11_hdr->addr2, eh->h_source, ETH_ALEN); + memcpy(i802_11_hdr->addr3, eh->h_dest, ETH_ALEN); + } + + i802_11_hdr->duration_id = cpu_to_le16(0); + i802_11_hdr->seq_ctl = cpu_to_le16(0); + + /* setup 'Atmel' header */ + tx_buffer->wlength = cpu_to_le16(wlen); + tx_buffer->tx_rate = priv->txrate; + /* for broadcast destination addresses, the firmware 0.100.x + seems to choose the highest rate set with CMD_STARTUP in + basic_rate_set replacing this value */ + + memset(tx_buffer->reserved, 0, sizeof(tx_buffer->reserved)); + + tx_buffer->padding = at76_calc_padding(wlen); + submit_len = wlen + AT76_TX_HDRLEN + tx_buffer->padding; + + at76_dbg(DBG_TX_DATA_CONTENT, "%s skb->data %s", priv->netdev->name, + hex2str(skb->data, 32)); + at76_dbg(DBG_TX_DATA, "%s tx: wlen 0x%x pad 0x%x rate %d hdr %s", + priv->netdev->name, + le16_to_cpu(tx_buffer->wlength), + tx_buffer->padding, tx_buffer->tx_rate, + hex2str(i802_11_hdr, sizeof(*i802_11_hdr))); + at76_dbg(DBG_TX_DATA_CONTENT, "%s payload %s", priv->netdev->name, + hex2str(payload, 48)); + + /* send stuff */ + netif_stop_queue(netdev); + netdev->trans_start = jiffies; + + usb_fill_bulk_urb(priv->tx_urb, priv->udev, priv->tx_pipe, tx_buffer, + submit_len, at76_tx_callback, priv); + ret = usb_submit_urb(priv->tx_urb, GFP_ATOMIC); + if (ret) { + stats->tx_errors++; + printk(KERN_ERR "%s: error in tx submit urb: %d\n", + netdev->name, ret); + if (ret == -EINVAL) + printk(KERN_ERR + "%s: -EINVAL: tx urb %p hcpriv %p complete %p\n", + priv->netdev->name, priv->tx_urb, + priv->tx_urb->hcpriv, priv->tx_urb->complete); + } else { + stats->tx_bytes += skb->len; + dev_kfree_skb(skb); + } + + return ret; +} + +static void at76_tx_timeout(struct net_device *netdev) +{ + struct at76_priv *priv = netdev_priv(netdev); + + if (!priv) + return; + warn("%s: tx timeout.", netdev->name); + + usb_unlink_urb(priv->tx_urb); + priv->stats.tx_errors++; +} + +static int at76_submit_rx_urb(struct at76_priv *priv) +{ + int ret; + int size; + struct sk_buff *skb = priv->rx_skb; + + if (!priv->rx_urb) { + printk(KERN_ERR "%s: %s: priv->rx_urb is NULL\n", + priv->netdev->name, __func__); + return -EFAULT; + } + + if (!skb) { + skb = dev_alloc_skb(sizeof(struct at76_rx_buffer)); + if (!skb) { + printk(KERN_ERR "%s: cannot allocate rx skbuff\n", + priv->netdev->name); + ret = -ENOMEM; + goto exit; + } + priv->rx_skb = skb; + } else { + skb_push(skb, skb_headroom(skb)); + skb_trim(skb, 0); + } + + size = skb_tailroom(skb); + usb_fill_bulk_urb(priv->rx_urb, priv->udev, priv->rx_pipe, + skb_put(skb, size), size, at76_rx_callback, priv); + ret = usb_submit_urb(priv->rx_urb, GFP_ATOMIC); + if (ret < 0) { + if (ret == -ENODEV) + at76_dbg(DBG_DEVSTART, + "usb_submit_urb returned -ENODEV"); + else + printk(KERN_ERR "%s: rx, usb_submit_urb failed: %d\n", + priv->netdev->name, ret); + } + +exit: + if (ret < 0 && ret != -ENODEV) + printk(KERN_ERR "%s: cannot submit rx urb - please unload the " + "driver and/or power cycle the device\n", + priv->netdev->name); + + return ret; +} + +static int at76_open(struct net_device *netdev) +{ + struct at76_priv *priv = netdev_priv(netdev); + int ret = 0; + + at76_dbg(DBG_PROC_ENTRY, "%s(): entry", __func__); + + if (mutex_lock_interruptible(&priv->mtx)) + return -EINTR; + + /* if netdev->dev_addr != priv->mac_addr we must + set the mac address in the device ! */ + if (compare_ether_addr(netdev->dev_addr, priv->mac_addr)) { + if (at76_add_mac_address(priv, netdev->dev_addr) >= 0) + at76_dbg(DBG_PROGRESS, "%s: set new MAC addr %s", + netdev->name, mac2str(netdev->dev_addr)); + } + + priv->scan_state = SCAN_IDLE; + priv->last_scan = jiffies; + + ret = at76_submit_rx_urb(priv); + if (ret < 0) { + printk(KERN_ERR "%s: open: submit_rx_urb failed: %d\n", + netdev->name, ret); + goto error; + } + + schedule_delayed_work(&priv->dwork_restart, 0); + + at76_dbg(DBG_PROC_ENTRY, "%s(): end", __func__); +error: + mutex_unlock(&priv->mtx); + return ret < 0 ? ret : 0; +} + +static int at76_stop(struct net_device *netdev) +{ + struct at76_priv *priv = netdev_priv(netdev); + + at76_dbg(DBG_DEVSTART, "%s: ENTER", __func__); + + if (mutex_lock_interruptible(&priv->mtx)) + return -EINTR; + + at76_quiesce(priv); + + if (!priv->device_unplugged) { + /* We are called by "ifconfig ethX down", not because the + * device is not available anymore. */ + at76_set_radio(priv, 0); + + /* We unlink rx_urb because at76_open() re-submits it. + * If unplugged, at76_delete_device() takes care of it. */ + usb_kill_urb(priv->rx_urb); + } + + /* free the bss_list */ + at76_free_bss_list(priv); + + mutex_unlock(&priv->mtx); + at76_dbg(DBG_DEVSTART, "%s: EXIT", __func__); + + return 0; +} + +static void at76_ethtool_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *info) +{ + struct at76_priv *priv = netdev_priv(netdev); + + strncpy(info->driver, DRIVER_NAME, sizeof(info->driver)); + strncpy(info->version, DRIVER_VERSION, sizeof(info->version)); + + usb_make_path(priv->udev, info->bus_info, sizeof(info->bus_info)); + + snprintf(info->fw_version, sizeof(info->fw_version), "%d.%d.%d-%d", + priv->fw_version.major, priv->fw_version.minor, + priv->fw_version.patch, priv->fw_version.build); +} + +static u32 at76_ethtool_get_link(struct net_device *netdev) +{ + struct at76_priv *priv = netdev_priv(netdev); + return priv->mac_state == MAC_CONNECTED; +} + +static struct ethtool_ops at76_ethtool_ops = { + .get_drvinfo = at76_ethtool_get_drvinfo, + .get_link = at76_ethtool_get_link, +}; + +/* Download external firmware */ +static int at76_load_external_fw(struct usb_device *udev, struct fwentry *fwe) +{ + int ret; + int op_mode; + int blockno = 0; + int bsize; + u8 *block; + u8 *buf = fwe->extfw; + int size = fwe->extfw_size; + + if (!buf || !size) + return -ENOENT; + + op_mode = at76_get_op_mode(udev); + at76_dbg(DBG_DEVSTART, "opmode %d", op_mode); + + if (op_mode != OPMODE_NORMAL_NIC_WITHOUT_FLASH) { + dev_printk(KERN_ERR, &udev->dev, "unexpected opmode %d\n", + op_mode); + return -EINVAL; + } + + block = kmalloc(FW_BLOCK_SIZE, GFP_KERNEL); + if (!block) + return -ENOMEM; + + at76_dbg(DBG_DEVSTART, "downloading external firmware"); + + /* for fw >= 0.100, the device needs an extra empty block */ + do { + bsize = min_t(int, size, FW_BLOCK_SIZE); + memcpy(block, buf, bsize); + at76_dbg(DBG_DEVSTART, + "ext fw, size left = %5d, bsize = %4d, blockno = %2d", + size, bsize, blockno); + ret = at76_load_ext_fw_block(udev, blockno, block, bsize); + if (ret != bsize) { + dev_printk(KERN_ERR, &udev->dev, + "loading %dth firmware block failed: %d\n", + blockno, ret); + goto exit; + } + buf += bsize; + size -= bsize; + blockno++; + } while (bsize > 0); + + if (at76_is_505a(fwe->board_type)) { + at76_dbg(DBG_DEVSTART, "200 ms delay for 505a"); + schedule_timeout_interruptible(HZ / 5 + 1); + } + +exit: + kfree(block); + if (ret < 0) + dev_printk(KERN_ERR, &udev->dev, + "downloading external firmware failed: %d\n", ret); + return ret; +} + +/* Download internal firmware */ +static int at76_load_internal_fw(struct usb_device *udev, struct fwentry *fwe) +{ + int ret; + int need_remap = !at76_is_505a(fwe->board_type); + + ret = at76_usbdfu_download(udev, fwe->intfw, fwe->intfw_size, + need_remap ? 0 : 2 * HZ); + + if (ret < 0) { + dev_printk(KERN_ERR, &udev->dev, + "downloading internal fw failed with %d\n", ret); + goto exit; + } + + at76_dbg(DBG_DEVSTART, "sending REMAP"); + + /* no REMAP for 505A (see SF driver) */ + if (need_remap) { + ret = at76_remap(udev); + if (ret < 0) { + dev_printk(KERN_ERR, &udev->dev, + "sending REMAP failed with %d\n", ret); + goto exit; + } + } + + at76_dbg(DBG_DEVSTART, "sleeping for 2 seconds"); + schedule_timeout_interruptible(2 * HZ + 1); + usb_reset_device(udev); + +exit: + return ret; +} + +static int at76_match_essid(struct at76_priv *priv, struct bss_info *ptr) +{ + /* common criteria for both modi */ + + int ret = (priv->essid_size == 0 /* ANY ssid */ || + (priv->essid_size == ptr->ssid_len && + !memcmp(priv->essid, ptr->ssid, ptr->ssid_len))); + if (!ret) + at76_dbg(DBG_BSS_MATCH, + "%s bss table entry %p: essid didn't match", + priv->netdev->name, ptr); + return ret; +} + +static inline int at76_match_mode(struct at76_priv *priv, struct bss_info *ptr) +{ + int ret; + + if (priv->iw_mode == IW_MODE_ADHOC) + ret = ptr->capa & WLAN_CAPABILITY_IBSS; + else + ret = ptr->capa & WLAN_CAPABILITY_ESS; + if (!ret) + at76_dbg(DBG_BSS_MATCH, + "%s bss table entry %p: mode didn't match", + priv->netdev->name, ptr); + return ret; +} + +static int at76_match_rates(struct at76_priv *priv, struct bss_info *ptr) +{ + int i; + + for (i = 0; i < ptr->rates_len; i++) { + u8 rate = ptr->rates[i]; + + if (!(rate & 0x80)) + continue; + + /* this is a basic rate we have to support + (see IEEE802.11, ch. 7.3.2.2) */ + if (rate != (0x80 | hw_rates[0]) + && rate != (0x80 | hw_rates[1]) + && rate != (0x80 | hw_rates[2]) + && rate != (0x80 | hw_rates[3])) { + at76_dbg(DBG_BSS_MATCH, + "%s: bss table entry %p: basic rate %02x not " + "supported", priv->netdev->name, ptr, rate); + return 0; + } + } + + /* if we use short preamble, the bss must support it */ + if (priv->preamble_type == PREAMBLE_TYPE_SHORT && + !(ptr->capa & WLAN_CAPABILITY_SHORT_PREAMBLE)) { + at76_dbg(DBG_BSS_MATCH, + "%s: %p does not support short preamble", + priv->netdev->name, ptr); + return 0; + } else + return 1; +} + +static inline int at76_match_wep(struct at76_priv *priv, struct bss_info *ptr) +{ + if (!priv->wep_enabled && ptr->capa & WLAN_CAPABILITY_PRIVACY) { + /* we have disabled WEP, but the BSS signals privacy */ + at76_dbg(DBG_BSS_MATCH, + "%s: bss table entry %p: requires encryption", + priv->netdev->name, ptr); + return 0; + } + /* otherwise if the BSS does not signal privacy it may well + accept encrypted packets from us ... */ + return 1; +} + +static inline int at76_match_bssid(struct at76_priv *priv, struct bss_info *ptr) +{ + if (!priv->wanted_bssid_valid || + !compare_ether_addr(ptr->bssid, priv->wanted_bssid)) + return 1; + + at76_dbg(DBG_BSS_MATCH, + "%s: requested bssid - %s does not match", + priv->netdev->name, mac2str(priv->wanted_bssid)); + at76_dbg(DBG_BSS_MATCH, + " AP bssid - %s of bss table entry %p", + mac2str(ptr->bssid), ptr); + return 0; +} + +/** + * at76_match_bss - try to find a matching bss in priv->bss + * + * last - last bss tried + * + * last == NULL signals a new round starting with priv->bss_list.next + * this function must be called inside an acquired priv->bss_list_spinlock + * otherwise the timeout on bss may remove the newly chosen entry + */ +static struct bss_info *at76_match_bss(struct at76_priv *priv, + struct bss_info *last) +{ + struct bss_info *ptr = NULL; + struct list_head *curr; + + curr = last ? last->list.next : priv->bss_list.next; + while (curr != &priv->bss_list) { + ptr = list_entry(curr, struct bss_info, list); + if (at76_match_essid(priv, ptr) && at76_match_mode(priv, ptr) + && at76_match_wep(priv, ptr) && at76_match_rates(priv, ptr) + && at76_match_bssid(priv, ptr)) + break; + curr = curr->next; + } + + if (curr == &priv->bss_list) + ptr = NULL; + /* otherwise ptr points to the struct bss_info we have chosen */ + + at76_dbg(DBG_BSS_TABLE, "%s %s: returned %p", priv->netdev->name, + __func__, ptr); + return ptr; +} + +/* Start joining a matching BSS, or create own IBSS */ +static void at76_work_join(struct work_struct *work) +{ + struct at76_priv *priv = container_of(work, struct at76_priv, + work_join); + int ret; + unsigned long flags; + + mutex_lock(&priv->mtx); + + WARN_ON(priv->mac_state != MAC_JOINING); + if (priv->mac_state != MAC_JOINING) + goto exit; + + /* secure the access to priv->curr_bss ! */ + spin_lock_irqsave(&priv->bss_list_spinlock, flags); + priv->curr_bss = at76_match_bss(priv, priv->curr_bss); + spin_unlock_irqrestore(&priv->bss_list_spinlock, flags); + + if (!priv->curr_bss) { + /* here we haven't found a matching (i)bss ... */ + if (priv->iw_mode == IW_MODE_ADHOC) { + at76_set_mac_state(priv, MAC_OWN_IBSS); + at76_start_ibss(priv); + goto exit; + } + /* haven't found a matching BSS in infra mode - try again */ + at76_set_mac_state(priv, MAC_SCANNING); + schedule_work(&priv->work_start_scan); + goto exit; + } + + ret = at76_join_bss(priv, priv->curr_bss); + if (ret < 0) { + printk(KERN_ERR "%s: join_bss failed with %d\n", + priv->netdev->name, ret); + goto exit; + } + + ret = at76_wait_completion(priv, CMD_JOIN); + if (ret != CMD_STATUS_COMPLETE) { + if (ret != CMD_STATUS_TIME_OUT) + printk(KERN_ERR "%s: join_bss completed with %d\n", + priv->netdev->name, ret); + else + printk(KERN_INFO "%s: join_bss ssid %s timed out\n", + priv->netdev->name, + mac2str(priv->curr_bss->bssid)); + + /* retry next BSS immediately */ + schedule_work(&priv->work_join); + goto exit; + } + + /* here we have joined the (I)BSS */ + if (priv->iw_mode == IW_MODE_ADHOC) { + struct bss_info *bptr = priv->curr_bss; + at76_set_mac_state(priv, MAC_CONNECTED); + /* get ESSID, BSSID and channel for priv->curr_bss */ + priv->essid_size = bptr->ssid_len; + memcpy(priv->essid, bptr->ssid, bptr->ssid_len); + memcpy(priv->bssid, bptr->bssid, ETH_ALEN); + priv->channel = bptr->channel; + at76_iwevent_bss_connect(priv->netdev, bptr->bssid); + netif_carrier_on(priv->netdev); + netif_start_queue(priv->netdev); + /* just to be sure */ + cancel_delayed_work(&priv->dwork_get_scan); + cancel_delayed_work(&priv->dwork_auth); + cancel_delayed_work(&priv->dwork_assoc); + } else { + /* send auth req */ + priv->retries = AUTH_RETRIES; + at76_set_mac_state(priv, MAC_AUTH); + at76_auth_req(priv, priv->curr_bss, 1, NULL); + at76_dbg(DBG_MGMT_TIMER, + "%s:%d: starting mgmt_timer + HZ", __func__, __LINE__); + schedule_delayed_work(&priv->dwork_auth, AUTH_TIMEOUT); + } + +exit: + mutex_unlock(&priv->mtx); +} + +/* Reap scan results */ +static void at76_dwork_get_scan(struct work_struct *work) +{ + int status; + int ret; + struct at76_priv *priv = container_of(work, struct at76_priv, + dwork_get_scan.work); + + mutex_lock(&priv->mtx); + WARN_ON(priv->mac_state != MAC_SCANNING); + if (priv->mac_state != MAC_SCANNING) + goto exit; + + status = at76_get_cmd_status(priv->udev, CMD_SCAN); + if (status < 0) { + printk(KERN_ERR "%s: %s: at76_get_cmd_status failed with %d\n", + priv->netdev->name, __func__, status); + status = CMD_STATUS_IN_PROGRESS; + /* INFO: Hope it was a one off error - if not, scanning + further down the line and stop this cycle */ + } + at76_dbg(DBG_PROGRESS, + "%s %s: got cmd_status %d (state %s, need_any %d)", + priv->netdev->name, __func__, status, + mac_states[priv->mac_state], priv->scan_need_any); + + if (status != CMD_STATUS_COMPLETE) { + if ((status != CMD_STATUS_IN_PROGRESS) && + (status != CMD_STATUS_IDLE)) + printk(KERN_ERR "%s: %s: Bad scan status: %s\n", + priv->netdev->name, __func__, + at76_get_cmd_status_string(status)); + + /* the first cmd status after scan start is always a IDLE -> + start the timer to poll again until COMPLETED */ + at76_dbg(DBG_MGMT_TIMER, + "%s:%d: starting mgmt_timer for %d ticks", + __func__, __LINE__, SCAN_POLL_INTERVAL); + schedule_delayed_work(&priv->dwork_get_scan, + SCAN_POLL_INTERVAL); + goto exit; + } + + if (at76_debug & DBG_BSS_TABLE) + at76_dump_bss_table(priv); + + if (priv->scan_need_any) { + ret = at76_start_scan(priv, 0); + if (ret < 0) + printk(KERN_ERR + "%s: %s: start_scan (ANY) failed with %d\n", + priv->netdev->name, __func__, ret); + at76_dbg(DBG_MGMT_TIMER, + "%s:%d: starting mgmt_timer for %d ticks", __func__, + __LINE__, SCAN_POLL_INTERVAL); + schedule_delayed_work(&priv->dwork_get_scan, + SCAN_POLL_INTERVAL); + priv->scan_need_any = 0; + } else { + priv->scan_state = SCAN_COMPLETED; + /* report the end of scan to user space */ + at76_iwevent_scan_complete(priv->netdev); + at76_set_mac_state(priv, MAC_JOINING); + schedule_work(&priv->work_join); + } + +exit: + mutex_unlock(&priv->mtx); +} + +/* Handle loss of beacons from the AP */ +static void at76_dwork_beacon(struct work_struct *work) +{ + struct at76_priv *priv = container_of(work, struct at76_priv, + dwork_beacon.work); + + mutex_lock(&priv->mtx); + if (priv->mac_state != MAC_CONNECTED || priv->iw_mode != IW_MODE_INFRA) + goto exit; + + /* We haven't received any beacons from out AP for BEACON_TIMEOUT */ + printk(KERN_INFO "%s: lost beacon bssid %s\n", + priv->netdev->name, mac2str(priv->curr_bss->bssid)); + + netif_carrier_off(priv->netdev); + netif_stop_queue(priv->netdev); + at76_iwevent_bss_disconnect(priv->netdev); + at76_set_mac_state(priv, MAC_SCANNING); + schedule_work(&priv->work_start_scan); + +exit: + mutex_unlock(&priv->mtx); +} + +/* Handle authentication response timeout */ +static void at76_dwork_auth(struct work_struct *work) +{ + struct at76_priv *priv = container_of(work, struct at76_priv, + dwork_auth.work); + + mutex_lock(&priv->mtx); + WARN_ON(priv->mac_state != MAC_AUTH); + if (priv->mac_state != MAC_AUTH) + goto exit; + + at76_dbg(DBG_PROGRESS, "%s: authentication response timeout", + priv->netdev->name); + + if (priv->retries-- >= 0) { + at76_auth_req(priv, priv->curr_bss, 1, NULL); + at76_dbg(DBG_MGMT_TIMER, "%s:%d: starting mgmt_timer + HZ", + __func__, __LINE__); + schedule_delayed_work(&priv->dwork_auth, AUTH_TIMEOUT); + } else { + /* try to get next matching BSS */ + at76_set_mac_state(priv, MAC_JOINING); + schedule_work(&priv->work_join); + } + +exit: + mutex_unlock(&priv->mtx); +} + +/* Handle association response timeout */ +static void at76_dwork_assoc(struct work_struct *work) +{ + struct at76_priv *priv = container_of(work, struct at76_priv, + dwork_assoc.work); + + mutex_lock(&priv->mtx); + WARN_ON(priv->mac_state != MAC_ASSOC); + if (priv->mac_state != MAC_ASSOC) + goto exit; + + at76_dbg(DBG_PROGRESS, "%s: association response timeout", + priv->netdev->name); + + if (priv->retries-- >= 0) { + at76_assoc_req(priv, priv->curr_bss); + at76_dbg(DBG_MGMT_TIMER, "%s:%d: starting mgmt_timer + HZ", + __func__, __LINE__); + schedule_delayed_work(&priv->dwork_assoc, ASSOC_TIMEOUT); + } else { + /* try to get next matching BSS */ + at76_set_mac_state(priv, MAC_JOINING); + schedule_work(&priv->work_join); + } + +exit: + mutex_unlock(&priv->mtx); +} + +/* Read new bssid in ad-hoc mode */ +static void at76_work_new_bss(struct work_struct *work) +{ + struct at76_priv *priv = container_of(work, struct at76_priv, + work_new_bss); + int ret; + struct mib_mac_mgmt mac_mgmt; + + mutex_lock(&priv->mtx); + + ret = at76_get_mib(priv->udev, MIB_MAC_MGMT, &mac_mgmt, + sizeof(struct mib_mac_mgmt)); + if (ret < 0) { + printk(KERN_ERR "%s: at76_get_mib failed: %d\n", + priv->netdev->name, ret); + goto exit; + } + + at76_dbg(DBG_PROGRESS, "ibss_change = 0x%2x", mac_mgmt.ibss_change); + memcpy(priv->bssid, mac_mgmt.current_bssid, ETH_ALEN); + at76_dbg(DBG_PROGRESS, "using BSSID %s", mac2str(priv->bssid)); + + at76_iwevent_bss_connect(priv->netdev, priv->bssid); + + priv->mib_buf.type = MIB_MAC_MGMT; + priv->mib_buf.size = 1; + priv->mib_buf.index = offsetof(struct mib_mac_mgmt, ibss_change); + priv->mib_buf.data.byte = 0; + + ret = at76_set_mib(priv, &priv->mib_buf); + if (ret < 0) + printk(KERN_ERR "%s: set_mib (ibss change ok) failed: %d\n", + priv->netdev->name, ret); + +exit: + mutex_unlock(&priv->mtx); +} + +static int at76_startup_device(struct at76_priv *priv) +{ + struct at76_card_config *ccfg = &priv->card_config; + int ret; + + at76_dbg(DBG_PARAMS, + "%s param: ssid %.*s (%s) mode %s ch %d wep %s key %d " + "keylen %d", priv->netdev->name, priv->essid_size, priv->essid, + hex2str(priv->essid, IW_ESSID_MAX_SIZE), + priv->iw_mode == IW_MODE_ADHOC ? "adhoc" : "infra", + priv->channel, priv->wep_enabled ? "enabled" : "disabled", + priv->wep_key_id, priv->wep_keys_len[priv->wep_key_id]); + at76_dbg(DBG_PARAMS, + "%s param: preamble %s rts %d retry %d frag %d " + "txrate %s auth_mode %d", priv->netdev->name, + preambles[priv->preamble_type], priv->rts_threshold, + priv->short_retry_limit, priv->frag_threshold, + priv->txrate == TX_RATE_1MBIT ? "1MBit" : priv->txrate == + TX_RATE_2MBIT ? "2MBit" : priv->txrate == + TX_RATE_5_5MBIT ? "5.5MBit" : priv->txrate == + TX_RATE_11MBIT ? "11MBit" : priv->txrate == + TX_RATE_AUTO ? "auto" : "", priv->auth_mode); + at76_dbg(DBG_PARAMS, + "%s param: pm_mode %d pm_period %d auth_mode %s " + "scan_times %d %d scan_mode %s", + priv->netdev->name, priv->pm_mode, priv->pm_period, + priv->auth_mode == WLAN_AUTH_OPEN ? "open" : "shared_secret", + priv->scan_min_time, priv->scan_max_time, + priv->scan_mode == SCAN_TYPE_ACTIVE ? "active" : "passive"); + + memset(ccfg, 0, sizeof(struct at76_card_config)); + ccfg->promiscuous_mode = 0; + ccfg->short_retry_limit = priv->short_retry_limit; + + if (priv->wep_enabled) { + if (priv->wep_keys_len[priv->wep_key_id] > WEP_SMALL_KEY_LEN) + ccfg->encryption_type = 2; + else + ccfg->encryption_type = 1; + + /* jal: always exclude unencrypted if WEP is active */ + ccfg->exclude_unencrypted = 1; + } else { + ccfg->exclude_unencrypted = 0; + ccfg->encryption_type = 0; + } + + ccfg->rts_threshold = cpu_to_le16(priv->rts_threshold); + ccfg->fragmentation_threshold = cpu_to_le16(priv->frag_threshold); + + memcpy(ccfg->basic_rate_set, hw_rates, 4); + /* jal: really needed, we do a set_mib for autorate later ??? */ + ccfg->auto_rate_fallback = (priv->txrate == TX_RATE_AUTO ? 1 : 0); + ccfg->channel = priv->channel; + ccfg->privacy_invoked = priv->wep_enabled; + memcpy(ccfg->current_ssid, priv->essid, IW_ESSID_MAX_SIZE); + ccfg->ssid_len = priv->essid_size; + + ccfg->wep_default_key_id = priv->wep_key_id; + memcpy(ccfg->wep_default_key_value, priv->wep_keys, 4 * WEP_KEY_LEN); + + ccfg->short_preamble = priv->preamble_type; + ccfg->beacon_period = cpu_to_le16(priv->beacon_period); + + ret = at76_set_card_command(priv->udev, CMD_STARTUP, &priv->card_config, + sizeof(struct at76_card_config)); + if (ret < 0) { + printk(KERN_ERR "%s: at76_set_card_command failed: %d\n", + priv->netdev->name, ret); + return ret; + } + + at76_wait_completion(priv, CMD_STARTUP); + + /* remove BSSID from previous run */ + memset(priv->bssid, 0, ETH_ALEN); + + if (at76_set_radio(priv, 1) == 1) + at76_wait_completion(priv, CMD_RADIO_ON); + + ret = at76_set_preamble(priv, priv->preamble_type); + if (ret < 0) + return ret; + + ret = at76_set_frag(priv, priv->frag_threshold); + if (ret < 0) + return ret; + + ret = at76_set_rts(priv, priv->rts_threshold); + if (ret < 0) + return ret; + + ret = at76_set_autorate_fallback(priv, + priv->txrate == TX_RATE_AUTO ? 1 : 0); + if (ret < 0) + return ret; + + ret = at76_set_pm_mode(priv); + if (ret < 0) + return ret; + + if (at76_debug & DBG_MIB) { + at76_dump_mib_mac(priv); + at76_dump_mib_mac_addr(priv); + at76_dump_mib_mac_mgmt(priv); + at76_dump_mib_mac_wep(priv); + at76_dump_mib_mdomain(priv); + at76_dump_mib_phy(priv); + at76_dump_mib_local(priv); + } + + return 0; +} + +/* Restart the interface */ +static void at76_dwork_restart(struct work_struct *work) +{ + struct at76_priv *priv = container_of(work, struct at76_priv, + dwork_restart.work); + + mutex_lock(&priv->mtx); + + netif_carrier_off(priv->netdev); /* stop netdev watchdog */ + netif_stop_queue(priv->netdev); /* stop tx data packets */ + + at76_startup_device(priv); + + if (priv->iw_mode != IW_MODE_MONITOR) { + priv->netdev->type = ARPHRD_ETHER; + at76_set_mac_state(priv, MAC_SCANNING); + schedule_work(&priv->work_start_scan); + } else { + priv->netdev->type = ARPHRD_IEEE80211_RADIOTAP; + at76_start_monitor(priv); + } + + mutex_unlock(&priv->mtx); +} + +/* Initiate scanning */ +static void at76_work_start_scan(struct work_struct *work) +{ + struct at76_priv *priv = container_of(work, struct at76_priv, + work_start_scan); + int ret; + + mutex_lock(&priv->mtx); + + WARN_ON(priv->mac_state != MAC_SCANNING); + if (priv->mac_state != MAC_SCANNING) + goto exit; + + /* only clear the bss list when a scan is actively initiated, + * otherwise simply rely on at76_bss_list_timeout */ + if (priv->scan_state == SCAN_IN_PROGRESS) { + at76_free_bss_list(priv); + priv->scan_need_any = 1; + } else + priv->scan_need_any = 0; + + ret = at76_start_scan(priv, 1); + + if (ret < 0) + printk(KERN_ERR "%s: %s: start_scan failed with %d\n", + priv->netdev->name, __func__, ret); + else { + at76_dbg(DBG_MGMT_TIMER, + "%s:%d: starting mgmt_timer for %d ticks", + __func__, __LINE__, SCAN_POLL_INTERVAL); + schedule_delayed_work(&priv->dwork_get_scan, + SCAN_POLL_INTERVAL); + } + +exit: + mutex_unlock(&priv->mtx); +} + +/* Enable or disable promiscuous mode */ +static void at76_work_set_promisc(struct work_struct *work) +{ + struct at76_priv *priv = container_of(work, struct at76_priv, + work_set_promisc); + int ret = 0; + + mutex_lock(&priv->mtx); + + priv->mib_buf.type = MIB_LOCAL; + priv->mib_buf.size = 1; + priv->mib_buf.index = offsetof(struct mib_local, promiscuous_mode); + priv->mib_buf.data.byte = priv->promisc ? 1 : 0; + + ret = at76_set_mib(priv, &priv->mib_buf); + if (ret < 0) + printk(KERN_ERR "%s: set_mib (promiscuous_mode) failed: %d\n", + priv->netdev->name, ret); + + mutex_unlock(&priv->mtx); +} + +/* Submit Rx urb back to the device */ +static void at76_work_submit_rx(struct work_struct *work) +{ + struct at76_priv *priv = container_of(work, struct at76_priv, + work_submit_rx); + + mutex_lock(&priv->mtx); + at76_submit_rx_urb(priv); + mutex_unlock(&priv->mtx); +} + +/* We got an association response */ +static void at76_rx_mgmt_assoc(struct at76_priv *priv, + struct at76_rx_buffer *buf) +{ + struct ieee80211_assoc_response *resp = + (struct ieee80211_assoc_response *)buf->packet; + u16 assoc_id = le16_to_cpu(resp->aid); + u16 status = le16_to_cpu(resp->status); + + at76_dbg(DBG_RX_MGMT, "%s: rx AssocResp bssid %s capa 0x%04x status " + "0x%04x assoc_id 0x%04x rates %s", priv->netdev->name, + mac2str(resp->header.addr3), le16_to_cpu(resp->capability), + status, assoc_id, hex2str(resp->info_element->data, + resp->info_element->len)); + + if (priv->mac_state != MAC_ASSOC) { + printk(KERN_INFO "%s: AssocResp in state %s ignored\n", + priv->netdev->name, mac_states[priv->mac_state]); + return; + } + + BUG_ON(!priv->curr_bss); + + cancel_delayed_work(&priv->dwork_assoc); + if (status == WLAN_STATUS_SUCCESS) { + struct bss_info *ptr = priv->curr_bss; + priv->assoc_id = assoc_id & 0x3fff; + /* update iwconfig params */ + memcpy(priv->bssid, ptr->bssid, ETH_ALEN); + memcpy(priv->essid, ptr->ssid, ptr->ssid_len); + priv->essid_size = ptr->ssid_len; + priv->channel = ptr->channel; + schedule_work(&priv->work_assoc_done); + } else { + at76_set_mac_state(priv, MAC_JOINING); + schedule_work(&priv->work_join); + } +} + +/* Process disassociation request from the AP */ +static void at76_rx_mgmt_disassoc(struct at76_priv *priv, + struct at76_rx_buffer *buf) +{ + struct ieee80211_disassoc *resp = + (struct ieee80211_disassoc *)buf->packet; + struct ieee80211_hdr_3addr *mgmt = &resp->header; + + at76_dbg(DBG_RX_MGMT, + "%s: rx DisAssoc bssid %s reason 0x%04x destination %s", + priv->netdev->name, mac2str(mgmt->addr3), + le16_to_cpu(resp->reason), mac2str(mgmt->addr1)); + + /* We are not connected, ignore */ + if (priv->mac_state == MAC_SCANNING || priv->mac_state == MAC_INIT + || !priv->curr_bss) + return; + + /* Not our BSSID, ignore */ + if (compare_ether_addr(mgmt->addr3, priv->curr_bss->bssid)) + return; + + /* Not for our STA and not broadcast, ignore */ + if (compare_ether_addr(priv->netdev->dev_addr, mgmt->addr1) + && !is_broadcast_ether_addr(mgmt->addr1)) + return; + + if (priv->mac_state != MAC_ASSOC && priv->mac_state != MAC_CONNECTED + && priv->mac_state != MAC_JOINING) { + printk(KERN_INFO "%s: DisAssoc in state %s ignored\n", + priv->netdev->name, mac_states[priv->mac_state]); + return; + } + + if (priv->mac_state == MAC_CONNECTED) { + netif_carrier_off(priv->netdev); + netif_stop_queue(priv->netdev); + at76_iwevent_bss_disconnect(priv->netdev); + } + cancel_delayed_work(&priv->dwork_get_scan); + cancel_delayed_work(&priv->dwork_beacon); + cancel_delayed_work(&priv->dwork_auth); + cancel_delayed_work(&priv->dwork_assoc); + at76_set_mac_state(priv, MAC_JOINING); + schedule_work(&priv->work_join); +} + +static void at76_rx_mgmt_auth(struct at76_priv *priv, + struct at76_rx_buffer *buf) +{ + struct ieee80211_auth *resp = (struct ieee80211_auth *)buf->packet; + struct ieee80211_hdr_3addr *mgmt = &resp->header; + int seq_nr = le16_to_cpu(resp->transaction); + int alg = le16_to_cpu(resp->algorithm); + int status = le16_to_cpu(resp->status); + + at76_dbg(DBG_RX_MGMT, + "%s: rx AuthFrame bssid %s alg %d seq_nr %d status %d " + "destination %s", priv->netdev->name, mac2str(mgmt->addr3), + alg, seq_nr, status, mac2str(mgmt->addr1)); + + if (alg == WLAN_AUTH_SHARED_KEY && seq_nr == 2) + at76_dbg(DBG_RX_MGMT, "%s: AuthFrame challenge %s ...", + priv->netdev->name, hex2str(resp->info_element, 18)); + + if (priv->mac_state != MAC_AUTH) { + printk(KERN_INFO "%s: ignored AuthFrame in state %s\n", + priv->netdev->name, mac_states[priv->mac_state]); + return; + } + if (priv->auth_mode != alg) { + printk(KERN_INFO "%s: ignored AuthFrame for alg %d\n", + priv->netdev->name, alg); + return; + } + + BUG_ON(!priv->curr_bss); + + /* Not our BSSID or not for our STA, ignore */ + if (compare_ether_addr(mgmt->addr3, priv->curr_bss->bssid) + || compare_ether_addr(priv->netdev->dev_addr, mgmt->addr1)) + return; + + cancel_delayed_work(&priv->dwork_auth); + if (status != WLAN_STATUS_SUCCESS) { + /* try to join next bss */ + at76_set_mac_state(priv, MAC_JOINING); + schedule_work(&priv->work_join); + return; + } + + if (priv->auth_mode == WLAN_AUTH_OPEN || seq_nr == 4) { + priv->retries = ASSOC_RETRIES; + at76_set_mac_state(priv, MAC_ASSOC); + at76_assoc_req(priv, priv->curr_bss); + at76_dbg(DBG_MGMT_TIMER, + "%s:%d: starting mgmt_timer + HZ", __func__, __LINE__); + schedule_delayed_work(&priv->dwork_assoc, ASSOC_TIMEOUT); + return; + } + + WARN_ON(seq_nr != 2); + at76_auth_req(priv, priv->curr_bss, seq_nr + 1, resp->info_element); + at76_dbg(DBG_MGMT_TIMER, "%s:%d: starting mgmt_timer + HZ", __func__, + __LINE__); + schedule_delayed_work(&priv->dwork_auth, AUTH_TIMEOUT); +} + +static void at76_rx_mgmt_deauth(struct at76_priv *priv, + struct at76_rx_buffer *buf) +{ + struct ieee80211_disassoc *resp = + (struct ieee80211_disassoc *)buf->packet; + struct ieee80211_hdr_3addr *mgmt = &resp->header; + + at76_dbg(DBG_RX_MGMT | DBG_PROGRESS, + "%s: rx DeAuth bssid %s reason 0x%04x destination %s", + priv->netdev->name, mac2str(mgmt->addr3), + le16_to_cpu(resp->reason), mac2str(mgmt->addr1)); + + if (priv->mac_state != MAC_AUTH && priv->mac_state != MAC_ASSOC + && priv->mac_state != MAC_CONNECTED) { + printk(KERN_INFO "%s: DeAuth in state %s ignored\n", + priv->netdev->name, mac_states[priv->mac_state]); + return; + } + + BUG_ON(!priv->curr_bss); + + /* Not our BSSID, ignore */ + if (compare_ether_addr(mgmt->addr3, priv->curr_bss->bssid)) + return; + + /* Not for our STA and not broadcast, ignore */ + if (compare_ether_addr(priv->netdev->dev_addr, mgmt->addr1) + && !is_broadcast_ether_addr(mgmt->addr1)) + return; + + if (priv->mac_state == MAC_CONNECTED) + at76_iwevent_bss_disconnect(priv->netdev); + + at76_set_mac_state(priv, MAC_JOINING); + schedule_work(&priv->work_join); + cancel_delayed_work(&priv->dwork_get_scan); + cancel_delayed_work(&priv->dwork_beacon); + cancel_delayed_work(&priv->dwork_auth); + cancel_delayed_work(&priv->dwork_assoc); +} + +static void at76_rx_mgmt_beacon(struct at76_priv *priv, + struct at76_rx_buffer *buf) +{ + int varpar_len; + /* beacon content */ + struct ieee80211_beacon *bdata = (struct ieee80211_beacon *)buf->packet; + struct ieee80211_hdr_3addr *mgmt = &bdata->header; + + struct list_head *lptr; + struct bss_info *match; /* entry matching addr3 with its bssid */ + int new_entry = 0; + int len; + struct ieee80211_info_element *ie; + int have_ssid = 0; + int have_rates = 0; + int have_channel = 0; + int keep_going = 1; + unsigned long flags; + + spin_lock_irqsave(&priv->bss_list_spinlock, flags); + if (priv->mac_state == MAC_CONNECTED) { + /* in state MAC_CONNECTED we use the mgmt_timer to control + the beacon of the BSS */ + BUG_ON(!priv->curr_bss); + + if (!compare_ether_addr(priv->curr_bss->bssid, mgmt->addr3)) { + /* We got our AP's beacon, defer the timeout handler. + Kill pending work first, as schedule_delayed_work() + won't do it. */ + cancel_delayed_work(&priv->dwork_beacon); + schedule_delayed_work(&priv->dwork_beacon, + BEACON_TIMEOUT); + priv->curr_bss->rssi = buf->rssi; + priv->beacons_received++; + goto exit; + } + } + + /* look if we have this BSS already in the list */ + match = NULL; + + if (!list_empty(&priv->bss_list)) { + list_for_each(lptr, &priv->bss_list) { + struct bss_info *bss_ptr = + list_entry(lptr, struct bss_info, list); + if (!compare_ether_addr(bss_ptr->bssid, mgmt->addr3)) { + match = bss_ptr; + break; + } + } + } + + if (!match) { + /* BSS not in the list - append it */ + match = kzalloc(sizeof(struct bss_info), GFP_ATOMIC); + if (!match) { + at76_dbg(DBG_BSS_TABLE, + "%s: cannot kmalloc new bss info (%zd byte)", + priv->netdev->name, sizeof(struct bss_info)); + goto exit; + } + new_entry = 1; + list_add_tail(&match->list, &priv->bss_list); + } + + match->capa = le16_to_cpu(bdata->capability); + match->beacon_interval = le16_to_cpu(bdata->beacon_interval); + match->rssi = buf->rssi; + match->link_qual = buf->link_quality; + match->noise_level = buf->noise_level; + memcpy(match->bssid, mgmt->addr3, ETH_ALEN); + at76_dbg(DBG_RX_BEACON, "%s: bssid %s", priv->netdev->name, + mac2str(match->bssid)); + + ie = bdata->info_element; + + /* length of var length beacon parameters */ + varpar_len = min_t(int, le16_to_cpu(buf->wlength) - + sizeof(struct ieee80211_beacon), + BEACON_MAX_DATA_LENGTH); + + /* This routine steps through the bdata->data array to get + * some useful information about the access point. + * Currently, this implementation supports receipt of: SSID, + * supported transfer rates and channel, in any order, with some + * tolerance for intermittent unknown codes (although this + * functionality may not be necessary as the useful information will + * usually arrive in consecutively, but there have been some + * reports of some of the useful information fields arriving in a + * different order). + * It does not support any more IE types although MFIE_TYPE_TIM may + * be supported (on my AP at least). + * The bdata->data array is about 1500 bytes long but only ~36 of those + * bytes are useful, hence the have_ssid etc optimizations. */ + + while (keep_going && + ((&ie->data[ie->len] - (u8 *)bdata->info_element) <= + varpar_len)) { + + switch (ie->id) { + + case MFIE_TYPE_SSID: + if (have_ssid) + break; + + len = min_t(int, IW_ESSID_MAX_SIZE, ie->len); + + /* we copy only if this is a new entry, + or the incoming SSID is not a hidden SSID. This + will protect us from overwriting a real SSID read + in a ProbeResponse with a hidden one from a + following beacon. */ + if (!new_entry && at76_is_hidden_ssid(ie->data, len)) { + have_ssid = 1; + break; + } + + match->ssid_len = len; + memcpy(match->ssid, ie->data, len); + at76_dbg(DBG_RX_BEACON, "%s: SSID - %.*s", + priv->netdev->name, len, match->ssid); + have_ssid = 1; + break; + + case MFIE_TYPE_RATES: + if (have_rates) + break; + + match->rates_len = + min_t(int, sizeof(match->rates), ie->len); + memcpy(match->rates, ie->data, match->rates_len); + have_rates = 1; + at76_dbg(DBG_RX_BEACON, "%s: SUPPORTED RATES %s", + priv->netdev->name, + hex2str(ie->data, ie->len)); + break; + + case MFIE_TYPE_DS_SET: + if (have_channel) + break; + + match->channel = ie->data[0]; + have_channel = 1; + at76_dbg(DBG_RX_BEACON, "%s: CHANNEL - %d", + priv->netdev->name, match->channel); + break; + + case MFIE_TYPE_CF_SET: + case MFIE_TYPE_TIM: + case MFIE_TYPE_IBSS_SET: + default: + at76_dbg(DBG_RX_BEACON, "%s: beacon IE id %d len %d %s", + priv->netdev->name, ie->id, ie->len, + hex2str(ie->data, ie->len)); + break; + } + + /* advance to the next informational element */ + next_ie(&ie); + + /* Optimization: after all, the bdata->data array is + * varpar_len bytes long, whereas we get all of the useful + * information after only ~36 bytes, this saves us a lot of + * time (and trouble as the remaining portion of the array + * could be full of junk) + * Comment this out if you want to see what other information + * comes from the AP - although little of it may be useful */ + } + + at76_dbg(DBG_RX_BEACON, "%s: Finished processing beacon data", + priv->netdev->name); + + match->last_rx = jiffies; /* record last rx of beacon */ + +exit: + spin_unlock_irqrestore(&priv->bss_list_spinlock, flags); +} + +/* Calculate the link level from a given rx_buffer */ +static void at76_calc_level(struct at76_priv *priv, struct at76_rx_buffer *buf, + struct iw_quality *qual) +{ + /* just a guess for now, might be different for other chips */ + int max_rssi = 42; + + qual->level = (buf->rssi * 100 / max_rssi); + if (qual->level > 100) + qual->level = 100; + qual->updated |= IW_QUAL_LEVEL_UPDATED; +} + +/* Calculate the link quality from a given rx_buffer */ +static void at76_calc_qual(struct at76_priv *priv, struct at76_rx_buffer *buf, + struct iw_quality *qual) +{ + if (at76_is_intersil(priv->board_type)) + qual->qual = buf->link_quality; + else { + unsigned long elapsed; + + /* Update qual at most once a second */ + elapsed = jiffies - priv->beacons_last_qual; + if (elapsed < 1 * HZ) + return; + + qual->qual = qual->level * priv->beacons_received * + msecs_to_jiffies(priv->beacon_period) / elapsed; + + priv->beacons_last_qual = jiffies; + priv->beacons_received = 0; + } + qual->qual = (qual->qual > 100) ? 100 : qual->qual; + qual->updated |= IW_QUAL_QUAL_UPDATED; +} + +/* Calculate the noise quality from a given rx_buffer */ +static void at76_calc_noise(struct at76_priv *priv, struct at76_rx_buffer *buf, + struct iw_quality *qual) +{ + qual->noise = 0; + qual->updated |= IW_QUAL_NOISE_INVALID; +} + +static void at76_update_wstats(struct at76_priv *priv, + struct at76_rx_buffer *buf) +{ + struct iw_quality *qual = &priv->wstats.qual; + + if (buf->rssi && priv->mac_state == MAC_CONNECTED) { + qual->updated = 0; + at76_calc_level(priv, buf, qual); + at76_calc_qual(priv, buf, qual); + at76_calc_noise(priv, buf, qual); + } else { + qual->qual = 0; + qual->level = 0; + qual->noise = 0; + qual->updated = IW_QUAL_ALL_INVALID; + } +} + +static void at76_rx_mgmt(struct at76_priv *priv, struct at76_rx_buffer *buf) +{ + struct ieee80211_hdr_3addr *mgmt = + (struct ieee80211_hdr_3addr *)buf->packet; + u16 framectl = le16_to_cpu(mgmt->frame_ctl); + + /* update wstats */ + if (priv->mac_state != MAC_INIT && priv->mac_state != MAC_SCANNING) { + /* jal: this is a dirty hack needed by Tim in ad-hoc mode */ + /* Data packets always seem to have a 0 link level, so we + only read link quality info from management packets. + Atmel driver actually averages the present, and previous + values, we just present the raw value at the moment - TJS */ + if (priv->iw_mode == IW_MODE_ADHOC + || (priv->curr_bss + && !compare_ether_addr(mgmt->addr3, + priv->curr_bss->bssid))) + at76_update_wstats(priv, buf); + } + + at76_dbg(DBG_RX_MGMT_CONTENT, "%s rx mgmt framectl 0x%x %s", + priv->netdev->name, framectl, + hex2str(mgmt, le16_to_cpu(buf->wlength))); + + switch (framectl & IEEE80211_FCTL_STYPE) { + case IEEE80211_STYPE_BEACON: + case IEEE80211_STYPE_PROBE_RESP: + at76_rx_mgmt_beacon(priv, buf); + break; + + case IEEE80211_STYPE_ASSOC_RESP: + at76_rx_mgmt_assoc(priv, buf); + break; + + case IEEE80211_STYPE_DISASSOC: + at76_rx_mgmt_disassoc(priv, buf); + break; + + case IEEE80211_STYPE_AUTH: + at76_rx_mgmt_auth(priv, buf); + break; + + case IEEE80211_STYPE_DEAUTH: + at76_rx_mgmt_deauth(priv, buf); + break; + + default: + printk(KERN_DEBUG "%s: ignoring frame with framectl 0x%04x\n", + priv->netdev->name, framectl); + } + + return; +} + +/* Convert the 802.11 header into an ethernet-style header, make skb + * ready for consumption by netif_rx() */ +static void at76_ieee80211_to_eth(struct sk_buff *skb, int iw_mode) +{ + struct ieee80211_hdr_3addr *i802_11_hdr; + struct ethhdr *eth_hdr_p; + u8 *src_addr; + u8 *dest_addr; + + i802_11_hdr = (struct ieee80211_hdr_3addr *)skb->data; + + /* That would be the ethernet header if the hardware converted + * the frame for us. Make sure the source and the destination + * match the 802.11 header. Which hardware does it? */ + eth_hdr_p = (struct ethhdr *)skb_pull(skb, IEEE80211_3ADDR_LEN); + + dest_addr = i802_11_hdr->addr1; + if (iw_mode == IW_MODE_ADHOC) + src_addr = i802_11_hdr->addr2; + else + src_addr = i802_11_hdr->addr3; + + if (!compare_ether_addr(eth_hdr_p->h_source, src_addr) && + !compare_ether_addr(eth_hdr_p->h_dest, dest_addr)) + /* Yes, we already have an ethernet header */ + skb_reset_mac_header(skb); + else { + u16 len; + + /* Need to build an ethernet header */ + if (!memcmp(skb->data, snapsig, sizeof(snapsig))) { + /* SNAP frame - decapsulate, keep proto */ + skb_push(skb, offsetof(struct ethhdr, h_proto) - + sizeof(rfc1042sig)); + len = 0; + } else { + /* 802.3 frame, proto is length */ + len = skb->len; + skb_push(skb, ETH_HLEN); + } + + skb_reset_mac_header(skb); + eth_hdr_p = eth_hdr(skb); + /* This needs to be done in this order (eth_hdr_p->h_dest may + * overlap src_addr) */ + memcpy(eth_hdr_p->h_source, src_addr, ETH_ALEN); + memcpy(eth_hdr_p->h_dest, dest_addr, ETH_ALEN); + if (len) + eth_hdr_p->h_proto = htons(len); + } + + skb->protocol = eth_type_trans(skb, skb->dev); +} + +/* Check for fragmented data in priv->rx_skb. If the packet was no fragment + or it was the last of a fragment set a skb containing the whole packet + is returned for further processing. Otherwise we get NULL and are + done and the packet is either stored inside the fragment buffer + or thrown away. Every returned skb starts with the ieee802_11 header + and contains _no_ FCS at the end */ +static struct sk_buff *at76_check_for_rx_frags(struct at76_priv *priv) +{ + struct sk_buff *skb = priv->rx_skb; + struct at76_rx_buffer *buf = (struct at76_rx_buffer *)skb->data; + struct ieee80211_hdr_3addr *i802_11_hdr = + (struct ieee80211_hdr_3addr *)buf->packet; + /* seq_ctrl, fragment_number, sequence number of new packet */ + u16 sctl = le16_to_cpu(i802_11_hdr->seq_ctl); + u16 fragnr = sctl & 0xf; + u16 seqnr = sctl >> 4; + u16 frame_ctl = le16_to_cpu(i802_11_hdr->frame_ctl); + + /* Length including the IEEE802.11 header, but without the trailing + * FCS and without the Atmel Rx header */ + int length = le16_to_cpu(buf->wlength) - IEEE80211_FCS_LEN; + + /* where does the data payload start in skb->data ? */ + u8 *data = i802_11_hdr->payload; + + /* length of payload, excl. the trailing FCS */ + int data_len = length - IEEE80211_3ADDR_LEN; + + int i; + struct rx_data_buf *bptr, *optr; + unsigned long oldest = ~0UL; + + at76_dbg(DBG_RX_FRAGS, + "%s: rx data frame_ctl %04x addr2 %s seq/frag %d/%d " + "length %d data %d: %s ...", priv->netdev->name, frame_ctl, + mac2str(i802_11_hdr->addr2), seqnr, fragnr, length, data_len, + hex2str(data, 32)); + + at76_dbg(DBG_RX_FRAGS_SKB, "%s: incoming skb: head %p data %p " + "tail %p end %p len %d", priv->netdev->name, skb->head, + skb->data, skb_tail_pointer(skb), skb_end_pointer(skb), + skb->len); + + if (data_len < 0) { + /* make sure data starts in the buffer */ + printk(KERN_INFO "%s: data frame too short\n", + priv->netdev->name); + return NULL; + } + + WARN_ON(length <= AT76_RX_HDRLEN); + if (length <= AT76_RX_HDRLEN) + return NULL; + + /* remove the at76_rx_buffer header - we don't need it anymore */ + /* we need the IEEE802.11 header (for the addresses) if this packet + is the first of a chain */ + skb_pull(skb, AT76_RX_HDRLEN); + + /* remove FCS at end */ + skb_trim(skb, length); + + at76_dbg(DBG_RX_FRAGS_SKB, "%s: trimmed skb: head %p data %p tail %p " + "end %p len %d data %p data_len %d", priv->netdev->name, + skb->head, skb->data, skb_tail_pointer(skb), + skb_end_pointer(skb), skb->len, data, data_len); + + if (fragnr == 0 && !(frame_ctl & IEEE80211_FCTL_MOREFRAGS)) { + /* unfragmented packet received */ + /* Use a new skb for the next receive */ + priv->rx_skb = NULL; + at76_dbg(DBG_RX_FRAGS, "%s: unfragmented", priv->netdev->name); + return skb; + } + + /* look if we've got a chain for the sender address. + afterwards optr points to first free or the oldest entry, + or, if i < NR_RX_DATA_BUF, bptr points to the entry for the + sender address */ + /* determining the oldest entry doesn't cope with jiffies wrapping + but I don't care to delete a young entry at these rare moments ... */ + + bptr = priv->rx_data; + optr = NULL; + for (i = 0; i < NR_RX_DATA_BUF; i++, bptr++) { + if (!bptr->skb) { + optr = bptr; + oldest = 0UL; + continue; + } + + if (!compare_ether_addr(i802_11_hdr->addr2, bptr->sender)) + break; + + if (!optr) { + optr = bptr; + oldest = bptr->last_rx; + } else if (bptr->last_rx < oldest) + optr = bptr; + } + + if (i < NR_RX_DATA_BUF) { + + at76_dbg(DBG_RX_FRAGS, "%s: %d. cacheentry (seq/frag = %d/%d) " + "matched sender addr", + priv->netdev->name, i, bptr->seqnr, bptr->fragnr); + + /* bptr points to an entry for the sender address */ + if (bptr->seqnr == seqnr) { + int left; + /* the fragment has the current sequence number */ + if (((bptr->fragnr + 1) & 0xf) != fragnr) { + /* wrong fragment number -> ignore it */ + /* is & 0xf necessary above ??? */ + at76_dbg(DBG_RX_FRAGS, + "%s: frag nr mismatch: %d + 1 != %d", + priv->netdev->name, bptr->fragnr, + fragnr); + return NULL; + } + bptr->last_rx = jiffies; + /* the next following fragment number -> + add the data at the end */ + + /* for test only ??? */ + left = skb_tailroom(bptr->skb); + if (left < data_len) + printk(KERN_INFO + "%s: only %d byte free (need %d)\n", + priv->netdev->name, left, data_len); + else + memcpy(skb_put(bptr->skb, data_len), data, + data_len); + + bptr->fragnr = fragnr; + if (frame_ctl & IEEE80211_FCTL_MOREFRAGS) + return NULL; + + /* this was the last fragment - send it */ + skb = bptr->skb; + bptr->skb = NULL; /* free the entry */ + at76_dbg(DBG_RX_FRAGS, "%s: last frag of seq %d", + priv->netdev->name, seqnr); + return skb; + } + + /* got another sequence number */ + if (fragnr == 0) { + /* it's the start of a new chain - replace the + old one by this */ + /* bptr->sender has the correct value already */ + at76_dbg(DBG_RX_FRAGS, + "%s: start of new seq %d, removing old seq %d", + priv->netdev->name, seqnr, bptr->seqnr); + bptr->seqnr = seqnr; + bptr->fragnr = 0; + bptr->last_rx = jiffies; + /* swap bptr->skb and priv->rx_skb */ + skb = bptr->skb; + bptr->skb = priv->rx_skb; + priv->rx_skb = skb; + } else { + /* it from the middle of a new chain -> + delete the old entry and skip the new one */ + at76_dbg(DBG_RX_FRAGS, + "%s: middle of new seq %d (%d) " + "removing old seq %d", + priv->netdev->name, seqnr, fragnr, + bptr->seqnr); + dev_kfree_skb(bptr->skb); + bptr->skb = NULL; + } + return NULL; + } + + /* if we didn't find a chain for the sender address, optr + points either to the first free or the oldest entry */ + + if (fragnr != 0) { + /* this is not the begin of a fragment chain ... */ + at76_dbg(DBG_RX_FRAGS, + "%s: no chain for non-first fragment (%d)", + priv->netdev->name, fragnr); + return NULL; + } + + BUG_ON(!optr); + if (optr->skb) { + /* swap the skb's */ + skb = optr->skb; + optr->skb = priv->rx_skb; + priv->rx_skb = skb; + + at76_dbg(DBG_RX_FRAGS, + "%s: free old contents: sender %s seq/frag %d/%d", + priv->netdev->name, mac2str(optr->sender), + optr->seqnr, optr->fragnr); + + } else { + /* take the skb from priv->rx_skb */ + optr->skb = priv->rx_skb; + /* let at76_submit_rx_urb() allocate a new skb */ + priv->rx_skb = NULL; + + at76_dbg(DBG_RX_FRAGS, "%s: use a free entry", + priv->netdev->name); + } + memcpy(optr->sender, i802_11_hdr->addr2, ETH_ALEN); + optr->seqnr = seqnr; + optr->fragnr = 0; + optr->last_rx = jiffies; + + return NULL; +} + +/* Rx interrupt: we expect the complete data buffer in priv->rx_skb */ +static void at76_rx_data(struct at76_priv *priv) +{ + struct net_device *netdev = priv->netdev; + struct net_device_stats *stats = &priv->stats; + struct sk_buff *skb = priv->rx_skb; + struct at76_rx_buffer *buf = (struct at76_rx_buffer *)skb->data; + struct ieee80211_hdr_3addr *i802_11_hdr; + int length = le16_to_cpu(buf->wlength); + + at76_dbg(DBG_RX_DATA, "%s received data packet: %s", netdev->name, + hex2str(skb->data, AT76_RX_HDRLEN)); + + at76_dbg(DBG_RX_DATA_CONTENT, "rx packet: %s", + hex2str(skb->data + AT76_RX_HDRLEN, length)); + + skb = at76_check_for_rx_frags(priv); + if (!skb) + return; + + /* Atmel header and the FCS are already removed */ + i802_11_hdr = (struct ieee80211_hdr_3addr *)skb->data; + + skb->dev = netdev; + skb->ip_summed = CHECKSUM_NONE; /* TODO: should check CRC */ + + if (is_broadcast_ether_addr(i802_11_hdr->addr1)) { + if (!compare_ether_addr(i802_11_hdr->addr1, netdev->broadcast)) + skb->pkt_type = PACKET_BROADCAST; + else + skb->pkt_type = PACKET_MULTICAST; + } else if (compare_ether_addr(i802_11_hdr->addr1, netdev->dev_addr)) + skb->pkt_type = PACKET_OTHERHOST; + + at76_ieee80211_to_eth(skb, priv->iw_mode); + + netdev->last_rx = jiffies; + netif_rx(skb); + stats->rx_packets++; + stats->rx_bytes += length; + + return; +} + +static void at76_rx_monitor_mode(struct at76_priv *priv) +{ + struct at76_rx_radiotap *rt; + u8 *payload; + int skblen; + struct net_device *netdev = priv->netdev; + struct at76_rx_buffer *buf = + (struct at76_rx_buffer *)priv->rx_skb->data; + /* length including the IEEE802.11 header and the trailing FCS, + but not at76_rx_buffer */ + int length = le16_to_cpu(buf->wlength); + struct sk_buff *skb = priv->rx_skb; + struct net_device_stats *stats = &priv->stats; + + if (length < IEEE80211_FCS_LEN) { + /* buffer contains no data */ + at76_dbg(DBG_MONITOR_MODE, + "%s: MONITOR MODE: rx skb without data", + priv->netdev->name); + return; + } + + skblen = sizeof(struct at76_rx_radiotap) + length; + + skb = dev_alloc_skb(skblen); + if (!skb) { + printk(KERN_ERR "%s: MONITOR MODE: dev_alloc_skb for radiotap " + "header returned NULL\n", priv->netdev->name); + return; + } + + skb_put(skb, skblen); + + rt = (struct at76_rx_radiotap *)skb->data; + payload = skb->data + sizeof(struct at76_rx_radiotap); + + rt->rt_hdr.it_version = 0; + rt->rt_hdr.it_pad = 0; + rt->rt_hdr.it_len = cpu_to_le16(sizeof(struct at76_rx_radiotap)); + rt->rt_hdr.it_present = cpu_to_le32(AT76_RX_RADIOTAP_PRESENT); + + rt->rt_tsft = cpu_to_le64(le32_to_cpu(buf->rx_time)); + rt->rt_rate = hw_rates[buf->rx_rate] & (~0x80); + rt->rt_signal = buf->rssi; + rt->rt_noise = buf->noise_level; + rt->rt_flags = IEEE80211_RADIOTAP_F_FCS; + if (buf->fragmentation) + rt->rt_flags |= IEEE80211_RADIOTAP_F_FRAG; + + memcpy(payload, buf->packet, length); + skb->dev = netdev; + skb->ip_summed = CHECKSUM_NONE; + skb_reset_mac_header(skb); + skb->pkt_type = PACKET_OTHERHOST; + skb->protocol = htons(ETH_P_802_2); + + netdev->last_rx = jiffies; + netif_rx(skb); + stats->rx_packets++; + stats->rx_bytes += length; +} + +/* Check if we spy on the sender address in buf and update stats */ +static void at76_iwspy_update(struct at76_priv *priv, + struct at76_rx_buffer *buf) +{ + struct ieee80211_hdr_3addr *hdr = + (struct ieee80211_hdr_3addr *)buf->packet; + struct iw_quality qual; + + /* We can only set the level here */ + qual.updated = IW_QUAL_QUAL_INVALID | IW_QUAL_NOISE_INVALID; + qual.level = 0; + qual.noise = 0; + at76_calc_level(priv, buf, &qual); + + spin_lock_bh(&priv->spy_spinlock); + + if (priv->spy_data.spy_number > 0) + wireless_spy_update(priv->netdev, hdr->addr2, &qual); + + spin_unlock_bh(&priv->spy_spinlock); +} + +static void at76_rx_tasklet(unsigned long param) +{ + struct urb *urb = (struct urb *)param; + struct at76_priv *priv = urb->context; + struct net_device *netdev = priv->netdev; + struct at76_rx_buffer *buf; + struct ieee80211_hdr_3addr *i802_11_hdr; + u16 frame_ctl; + + if (priv->device_unplugged) { + at76_dbg(DBG_DEVSTART, "device unplugged"); + if (urb) + at76_dbg(DBG_DEVSTART, "urb status %d", urb->status); + return; + } + + if (!priv->rx_skb || !netdev || !priv->rx_skb->data) + return; + + buf = (struct at76_rx_buffer *)priv->rx_skb->data; + + i802_11_hdr = (struct ieee80211_hdr_3addr *)buf->packet; + + frame_ctl = le16_to_cpu(i802_11_hdr->frame_ctl); + + if (urb->status != 0) { + if (urb->status != -ENOENT && urb->status != -ECONNRESET) + at76_dbg(DBG_URB, + "%s %s: - nonzero Rx bulk status received: %d", + __func__, netdev->name, urb->status); + return; + } + + at76_dbg(DBG_RX_ATMEL_HDR, + "%s: rx frame: rate %d rssi %d noise %d link %d %s", + priv->netdev->name, buf->rx_rate, buf->rssi, buf->noise_level, + buf->link_quality, hex2str(i802_11_hdr, 48)); + if (priv->iw_mode == IW_MODE_MONITOR) { + at76_rx_monitor_mode(priv); + goto exit; + } + + /* there is a new bssid around, accept it: */ + if (buf->newbss && priv->iw_mode == IW_MODE_ADHOC) { + at76_dbg(DBG_PROGRESS, "%s: rx newbss", netdev->name); + schedule_work(&priv->work_new_bss); + } + + switch (frame_ctl & IEEE80211_FCTL_FTYPE) { + case IEEE80211_FTYPE_DATA: + at76_rx_data(priv); + break; + + case IEEE80211_FTYPE_MGMT: + /* jal: TODO: find out if we can update iwspy also on + other frames than management (might depend on the + radio chip / firmware version !) */ + + at76_iwspy_update(priv, buf); + + at76_rx_mgmt(priv, buf); + break; + + case IEEE80211_FTYPE_CTL: + at76_dbg(DBG_RX_CTRL, "%s: ignored ctrl frame: %04x", + priv->netdev->name, frame_ctl); + break; + + default: + printk(KERN_DEBUG "%s: ignoring frame with framectl 0x%04x\n", + priv->netdev->name, frame_ctl); + } +exit: + at76_submit_rx_urb(priv); +} + +/* Load firmware into kernel memory and parse it */ +static struct fwentry *at76_load_firmware(struct usb_device *udev, + enum board_type board_type) +{ + int ret; + char *str; + struct at76_fw_header *fwh; + struct fwentry *fwe = &firmwares[board_type]; + + mutex_lock(&fw_mutex); + + if (fwe->loaded) { + at76_dbg(DBG_FW, "re-using previously loaded fw"); + goto exit; + } + + at76_dbg(DBG_FW, "downloading firmware %s", fwe->fwname); + ret = request_firmware(&fwe->fw, fwe->fwname, &udev->dev); + if (ret < 0) { + dev_printk(KERN_ERR, &udev->dev, "firmware %s not found!\n", + fwe->fwname); + dev_printk(KERN_ERR, &udev->dev, + "you may need to download the firmware from " + "http://developer.berlios.de/projects/at76c503a/"); + goto exit; + } + + at76_dbg(DBG_FW, "got it."); + fwh = (struct at76_fw_header *)(fwe->fw->data); + + if (fwe->fw->size <= sizeof(*fwh)) { + dev_printk(KERN_ERR, &udev->dev, + "firmware is too short (0x%zx)\n", fwe->fw->size); + goto exit; + } + + /* CRC currently not checked */ + fwe->board_type = le32_to_cpu(fwh->board_type); + if (fwe->board_type != board_type) { + dev_printk(KERN_ERR, &udev->dev, + "board type mismatch, requested %u, got %u\n", + board_type, fwe->board_type); + goto exit; + } + + fwe->fw_version.major = fwh->major; + fwe->fw_version.minor = fwh->minor; + fwe->fw_version.patch = fwh->patch; + fwe->fw_version.build = fwh->build; + + str = (char *)fwh + le32_to_cpu(fwh->str_offset); + fwe->intfw = (u8 *)fwh + le32_to_cpu(fwh->int_fw_offset); + fwe->intfw_size = le32_to_cpu(fwh->int_fw_len); + fwe->extfw = (u8 *)fwh + le32_to_cpu(fwh->ext_fw_offset); + fwe->extfw_size = le32_to_cpu(fwh->ext_fw_len); + + fwe->loaded = 1; + + dev_printk(KERN_DEBUG, &udev->dev, + "using firmware %s (version %d.%d.%d-%d)\n", + fwe->fwname, fwh->major, fwh->minor, fwh->patch, fwh->build); + + at76_dbg(DBG_DEVSTART, "board %u, int %d:%d, ext %d:%d", board_type, + le32_to_cpu(fwh->int_fw_offset), le32_to_cpu(fwh->int_fw_len), + le32_to_cpu(fwh->ext_fw_offset), le32_to_cpu(fwh->ext_fw_len)); + at76_dbg(DBG_DEVSTART, "firmware id %s", str); + +exit: + mutex_unlock(&fw_mutex); + + if (fwe->loaded) + return fwe; + else + return NULL; +} + +/* Allocate network device and initialize private data */ +static struct at76_priv *at76_alloc_new_device(struct usb_device *udev) +{ + struct net_device *netdev; + struct at76_priv *priv; + int i; + + /* allocate memory for our device state and initialize it */ + netdev = alloc_etherdev(sizeof(struct at76_priv)); + if (!netdev) { + dev_printk(KERN_ERR, &udev->dev, "out of memory\n"); + return NULL; + } + + priv = netdev_priv(netdev); + + priv->udev = udev; + priv->netdev = netdev; + + mutex_init(&priv->mtx); + INIT_WORK(&priv->work_assoc_done, at76_work_assoc_done); + INIT_WORK(&priv->work_join, at76_work_join); + INIT_WORK(&priv->work_new_bss, at76_work_new_bss); + INIT_WORK(&priv->work_start_scan, at76_work_start_scan); + INIT_WORK(&priv->work_set_promisc, at76_work_set_promisc); + INIT_WORK(&priv->work_submit_rx, at76_work_submit_rx); + INIT_DELAYED_WORK(&priv->dwork_restart, at76_dwork_restart); + INIT_DELAYED_WORK(&priv->dwork_get_scan, at76_dwork_get_scan); + INIT_DELAYED_WORK(&priv->dwork_beacon, at76_dwork_beacon); + INIT_DELAYED_WORK(&priv->dwork_auth, at76_dwork_auth); + INIT_DELAYED_WORK(&priv->dwork_assoc, at76_dwork_assoc); + + spin_lock_init(&priv->mgmt_spinlock); + priv->next_mgmt_bulk = NULL; + priv->mac_state = MAC_INIT; + + /* initialize empty BSS list */ + priv->curr_bss = NULL; + INIT_LIST_HEAD(&priv->bss_list); + spin_lock_init(&priv->bss_list_spinlock); + + init_timer(&priv->bss_list_timer); + priv->bss_list_timer.data = (unsigned long)priv; + priv->bss_list_timer.function = at76_bss_list_timeout; + + spin_lock_init(&priv->spy_spinlock); + + /* mark all rx data entries as unused */ + for (i = 0; i < NR_RX_DATA_BUF; i++) + priv->rx_data[i].skb = NULL; + + priv->rx_tasklet.func = at76_rx_tasklet; + priv->rx_tasklet.data = 0; + + priv->pm_mode = AT76_PM_OFF; + priv->pm_period = 0; + + return priv; +} + +static int at76_alloc_urbs(struct at76_priv *priv, + struct usb_interface *interface) +{ + struct usb_endpoint_descriptor *endpoint, *ep_in, *ep_out; + int i; + int buffer_size; + struct usb_host_interface *iface_desc; + + at76_dbg(DBG_PROC_ENTRY, "%s: ENTER", __func__); + + at76_dbg(DBG_URB, "%s: NumEndpoints %d ", __func__, + interface->altsetting[0].desc.bNumEndpoints); + + ep_in = NULL; + ep_out = NULL; + iface_desc = interface->cur_altsetting; + for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) { + endpoint = &iface_desc->endpoint[i].desc; + + at76_dbg(DBG_URB, "%s: %d. endpoint: addr 0x%x attr 0x%x", + __func__, i, endpoint->bEndpointAddress, + endpoint->bmAttributes); + + if (!ep_in && usb_endpoint_is_bulk_in(endpoint)) + ep_in = endpoint; + + if (!ep_out && usb_endpoint_is_bulk_out(endpoint)) + ep_out = endpoint; + } + + if (!ep_in || !ep_out) { + dev_printk(KERN_ERR, &interface->dev, + "bulk endpoints missing\n"); + return -ENXIO; + } + + priv->rx_pipe = usb_rcvbulkpipe(priv->udev, ep_in->bEndpointAddress); + priv->tx_pipe = usb_sndbulkpipe(priv->udev, ep_out->bEndpointAddress); + + priv->rx_urb = usb_alloc_urb(0, GFP_KERNEL); + priv->tx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!priv->rx_urb || !priv->tx_urb) { + dev_printk(KERN_ERR, &interface->dev, "cannot allocate URB\n"); + return -ENOMEM; + } + + buffer_size = sizeof(struct at76_tx_buffer) + MAX_PADDING_SIZE; + priv->bulk_out_buffer = kmalloc(buffer_size, GFP_KERNEL); + if (!priv->bulk_out_buffer) { + dev_printk(KERN_ERR, &interface->dev, + "cannot allocate output buffer\n"); + return -ENOMEM; + } + + at76_dbg(DBG_PROC_ENTRY, "%s: EXIT", __func__); + + return 0; +} + +/* Register network device and initialize the hardware */ +static int at76_init_new_device(struct at76_priv *priv, + struct usb_interface *interface) +{ + struct net_device *netdev = priv->netdev; + int ret; + + /* set up the endpoint information */ + /* check out the endpoints */ + + at76_dbg(DBG_DEVSTART, "USB interface: %d endpoints", + interface->cur_altsetting->desc.bNumEndpoints); + + ret = at76_alloc_urbs(priv, interface); + if (ret < 0) + goto exit; + + /* MAC address */ + ret = at76_get_hw_config(priv); + if (ret < 0) { + dev_printk(KERN_ERR, &interface->dev, + "cannot get MAC address\n"); + goto exit; + } + + priv->domain = at76_get_reg_domain(priv->regulatory_domain); + /* init. netdev->dev_addr */ + memcpy(netdev->dev_addr, priv->mac_addr, ETH_ALEN); + + priv->channel = DEF_CHANNEL; + priv->iw_mode = IW_MODE_INFRA; + priv->rts_threshold = DEF_RTS_THRESHOLD; + priv->frag_threshold = DEF_FRAG_THRESHOLD; + priv->short_retry_limit = DEF_SHORT_RETRY_LIMIT; + priv->txrate = TX_RATE_AUTO; + priv->preamble_type = PREAMBLE_TYPE_LONG; + priv->beacon_period = 100; + priv->beacons_last_qual = jiffies; + priv->auth_mode = WLAN_AUTH_OPEN; + priv->scan_min_time = DEF_SCAN_MIN_TIME; + priv->scan_max_time = DEF_SCAN_MAX_TIME; + priv->scan_mode = SCAN_TYPE_ACTIVE; + + netdev->flags &= ~IFF_MULTICAST; /* not yet or never */ + netdev->open = at76_open; + netdev->stop = at76_stop; + netdev->get_stats = at76_get_stats; + netdev->ethtool_ops = &at76_ethtool_ops; + + /* Add pointers to enable iwspy support. */ + priv->wireless_data.spy_data = &priv->spy_data; + netdev->wireless_data = &priv->wireless_data; + + netdev->hard_start_xmit = at76_tx; + netdev->tx_timeout = at76_tx_timeout; + netdev->watchdog_timeo = 2 * HZ; + netdev->wireless_handlers = &at76_handler_def; + netdev->set_multicast_list = at76_set_multicast; + netdev->set_mac_address = at76_set_mac_address; + dev_alloc_name(netdev, "wlan%d"); + + ret = register_netdev(priv->netdev); + if (ret) { + dev_printk(KERN_ERR, &interface->dev, + "cannot register netdevice (status %d)!\n", ret); + goto exit; + } + priv->netdev_registered = 1; + + printk(KERN_INFO "%s: USB %s, MAC %s, firmware %d.%d.%d-%d\n", + netdev->name, interface->dev.bus_id, mac2str(priv->mac_addr), + priv->fw_version.major, priv->fw_version.minor, + priv->fw_version.patch, priv->fw_version.build); + printk(KERN_INFO "%s: regulatory domain 0x%02x: %s\n", netdev->name, + priv->regulatory_domain, priv->domain->name); + + /* we let this timer run the whole time this driver instance lives */ + mod_timer(&priv->bss_list_timer, jiffies + BSS_LIST_TIMEOUT); + +exit: + return ret; +} + +static void at76_delete_device(struct at76_priv *priv) +{ + int i; + + at76_dbg(DBG_PROC_ENTRY, "%s: ENTER", __func__); + + /* The device is gone, don't bother turning it off */ + priv->device_unplugged = 1; + + if (priv->netdev_registered) + unregister_netdev(priv->netdev); + + /* assuming we used keventd, it must quiesce too */ + flush_scheduled_work(); + + kfree(priv->bulk_out_buffer); + + if (priv->tx_urb) { + usb_kill_urb(priv->tx_urb); + usb_free_urb(priv->tx_urb); + } + if (priv->rx_urb) { + usb_kill_urb(priv->rx_urb); + usb_free_urb(priv->rx_urb); + } + + at76_dbg(DBG_PROC_ENTRY, "%s: unlinked urbs", __func__); + + if (priv->rx_skb) + kfree_skb(priv->rx_skb); + + at76_free_bss_list(priv); + del_timer_sync(&priv->bss_list_timer); + cancel_delayed_work(&priv->dwork_get_scan); + cancel_delayed_work(&priv->dwork_beacon); + cancel_delayed_work(&priv->dwork_auth); + cancel_delayed_work(&priv->dwork_assoc); + + if (priv->mac_state == MAC_CONNECTED) + at76_iwevent_bss_disconnect(priv->netdev); + + for (i = 0; i < NR_RX_DATA_BUF; i++) + if (priv->rx_data[i].skb) { + dev_kfree_skb(priv->rx_data[i].skb); + priv->rx_data[i].skb = NULL; + } + usb_put_dev(priv->udev); + + at76_dbg(DBG_PROC_ENTRY, "%s: before freeing priv/netdev", __func__); + free_netdev(priv->netdev); /* priv is in netdev */ + + at76_dbg(DBG_PROC_ENTRY, "%s: EXIT", __func__); +} + +static int at76_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + int ret; + struct at76_priv *priv; + struct fwentry *fwe; + struct usb_device *udev; + int op_mode; + int need_ext_fw = 0; + struct mib_fw_version fwv; + int board_type = (int)id->driver_info; + + udev = usb_get_dev(interface_to_usbdev(interface)); + + /* Load firmware into kernel memory */ + fwe = at76_load_firmware(udev, board_type); + if (!fwe) { + ret = -ENOENT; + goto error; + } + + op_mode = at76_get_op_mode(udev); + + at76_dbg(DBG_DEVSTART, "opmode %d", op_mode); + + /* we get OPMODE_NONE with 2.4.23, SMC2662W-AR ??? + we get 204 with 2.4.23, Fiberline FL-WL240u (505A+RFMD2958) ??? */ + + if (op_mode == OPMODE_HW_CONFIG_MODE) { + dev_printk(KERN_ERR, &interface->dev, + "cannot handle a device in HW_CONFIG_MODE\n"); + ret = -EBUSY; + goto error; + } + + if (op_mode != OPMODE_NORMAL_NIC_WITH_FLASH + && op_mode != OPMODE_NORMAL_NIC_WITHOUT_FLASH) { + /* download internal firmware part */ + dev_printk(KERN_DEBUG, &interface->dev, + "downloading internal firmware\n"); + ret = at76_load_internal_fw(udev, fwe); + if (ret < 0) { + dev_printk(KERN_ERR, &interface->dev, + "error %d downloading internal firmware\n", + ret); + goto error; + } + usb_put_dev(udev); + return ret; + } + + /* Internal firmware already inside the device. Get firmware + * version to test if external firmware is loaded. + * This works only for newer firmware, e.g. the Intersil 0.90.x + * says "control timeout on ep0in" and subsequent + * at76_get_op_mode() fail too :-( */ + + /* if version >= 0.100.x.y or device with built-in flash we can + * query the device for the fw version */ + if ((fwe->fw_version.major > 0 || fwe->fw_version.minor >= 100) + || (op_mode == OPMODE_NORMAL_NIC_WITH_FLASH)) { + ret = at76_get_mib(udev, MIB_FW_VERSION, &fwv, sizeof(fwv)); + if (ret < 0 || (fwv.major | fwv.minor) == 0) + need_ext_fw = 1; + } else + /* No way to check firmware version, reload to be sure */ + need_ext_fw = 1; + + if (need_ext_fw) { + dev_printk(KERN_DEBUG, &interface->dev, + "downloading external firmware\n"); + + ret = at76_load_external_fw(udev, fwe); + if (ret) + goto error; + + /* Re-check firmware version */ + ret = at76_get_mib(udev, MIB_FW_VERSION, &fwv, sizeof(fwv)); + if (ret < 0) { + dev_printk(KERN_ERR, &interface->dev, + "error %d getting firmware version\n", ret); + goto error; + } + } + + priv = at76_alloc_new_device(udev); + if (!priv) { + ret = -ENOMEM; + goto error; + } + + SET_NETDEV_DEV(priv->netdev, &interface->dev); + usb_set_intfdata(interface, priv); + + memcpy(&priv->fw_version, &fwv, sizeof(struct mib_fw_version)); + priv->board_type = board_type; + + ret = at76_init_new_device(priv, interface); + if (ret < 0) + at76_delete_device(priv); + + return ret; + +error: + usb_put_dev(udev); + return ret; +} + +static void at76_disconnect(struct usb_interface *interface) +{ + struct at76_priv *priv; + + priv = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + + /* Disconnect after loading internal firmware */ + if (!priv) + return; + + printk(KERN_INFO "%s: disconnecting\n", priv->netdev->name); + at76_delete_device(priv); + dev_printk(KERN_INFO, &interface->dev, "disconnected\n"); +} + +/* Structure for registering this driver with the USB subsystem */ +static struct usb_driver at76_driver = { + .name = DRIVER_NAME, + .probe = at76_probe, + .disconnect = at76_disconnect, + .id_table = dev_table, +}; + +static int __init at76_mod_init(void) +{ + int result; + + printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION " loading\n"); + + mutex_init(&fw_mutex); + + /* register this driver with the USB subsystem */ + result = usb_register(&at76_driver); + if (result < 0) + printk(KERN_ERR DRIVER_NAME + ": usb_register failed (status %d)\n", result); + + led_trigger_register_simple("at76_usb-tx", &ledtrig_tx); + return result; +} + +static void __exit at76_mod_exit(void) +{ + int i; + + printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION " unloading\n"); + usb_deregister(&at76_driver); + for (i = 0; i < ARRAY_SIZE(firmwares); i++) { + if (firmwares[i].fw) + release_firmware(firmwares[i].fw); + } + led_trigger_unregister_simple(ledtrig_tx); +} + +module_param_named(debug, at76_debug, int, 0600); +MODULE_PARM_DESC(debug, "Debugging level"); + +module_init(at76_mod_init); +module_exit(at76_mod_exit); + +MODULE_AUTHOR("Oliver Kurth "); +MODULE_AUTHOR("Joerg Albert "); +MODULE_AUTHOR("Alex "); +MODULE_AUTHOR("Nick Jones"); +MODULE_AUTHOR("Balint Seeber "); +MODULE_AUTHOR("Pavel Roskin "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff -puN /dev/null drivers/net/wireless/at76_usb.h --- /dev/null +++ a/drivers/net/wireless/at76_usb.h @@ -0,0 +1,619 @@ +/* + * Copyright (c) 2002,2003 Oliver Kurth + * (c) 2003,2004 Joerg Albert + * (c) 2007 Guido Guenther + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This driver was based on information from the Sourceforge driver + * released and maintained by Atmel: + * + * http://sourceforge.net/projects/atmelwlandriver/ + * + * Although the code was completely re-written, + * it would have been impossible without Atmel's decision to + * release an Open Source driver (unfortunately the firmware was + * kept binary only). Thanks for that decision to Atmel! + */ + +#ifndef _AT76_USB_H +#define _AT76_USB_H + +/* Board types */ +enum board_type { + BOARD_503_ISL3861 = 1, + BOARD_503_ISL3863 = 2, + BOARD_503 = 3, + BOARD_503_ACC = 4, + BOARD_505 = 5, + BOARD_505_2958 = 6, + BOARD_505A = 7, + BOARD_505AMX = 8 +}; + +/* our private ioctl's */ +/* preamble length (0 - long, 1 - short, 2 - auto) */ +#define AT76_SET_SHORT_PREAMBLE (SIOCIWFIRSTPRIV + 0) +#define AT76_GET_SHORT_PREAMBLE (SIOCIWFIRSTPRIV + 1) +/* which debug channels are enabled */ +#define AT76_SET_DEBUG (SIOCIWFIRSTPRIV + 2) +#define AT76_GET_DEBUG (SIOCIWFIRSTPRIV + 3) +/* power save mode (incl. the Atmel proprietary smart save mode) */ +#define AT76_SET_POWERSAVE_MODE (SIOCIWFIRSTPRIV + 4) +#define AT76_GET_POWERSAVE_MODE (SIOCIWFIRSTPRIV + 5) +/* min and max channel times for scan */ +#define AT76_SET_SCAN_TIMES (SIOCIWFIRSTPRIV + 6) +#define AT76_GET_SCAN_TIMES (SIOCIWFIRSTPRIV + 7) +/* scan mode (0 - active, 1 - passive) */ +#define AT76_SET_SCAN_MODE (SIOCIWFIRSTPRIV + 8) +#define AT76_GET_SCAN_MODE (SIOCIWFIRSTPRIV + 9) + +#define CMD_STATUS_IDLE 0x00 +#define CMD_STATUS_COMPLETE 0x01 +#define CMD_STATUS_UNKNOWN 0x02 +#define CMD_STATUS_INVALID_PARAMETER 0x03 +#define CMD_STATUS_FUNCTION_NOT_SUPPORTED 0x04 +#define CMD_STATUS_TIME_OUT 0x07 +#define CMD_STATUS_IN_PROGRESS 0x08 +#define CMD_STATUS_HOST_FAILURE 0xff +#define CMD_STATUS_SCAN_FAILED 0xf0 + +/* answers to get op mode */ +#define OPMODE_NONE 0x00 +#define OPMODE_NORMAL_NIC_WITH_FLASH 0x01 +#define OPMODE_HW_CONFIG_MODE 0x02 +#define OPMODE_DFU_MODE_WITH_FLASH 0x03 +#define OPMODE_NORMAL_NIC_WITHOUT_FLASH 0x04 + +#define CMD_SET_MIB 0x01 +#define CMD_GET_MIB 0x02 +#define CMD_SCAN 0x03 +#define CMD_JOIN 0x04 +#define CMD_START_IBSS 0x05 +#define CMD_RADIO_ON 0x06 +#define CMD_RADIO_OFF 0x07 +#define CMD_STARTUP 0x0B + +#define MIB_LOCAL 0x01 +#define MIB_MAC_ADDR 0x02 +#define MIB_MAC 0x03 +#define MIB_MAC_MGMT 0x05 +#define MIB_MAC_WEP 0x06 +#define MIB_PHY 0x07 +#define MIB_FW_VERSION 0x08 +#define MIB_MDOMAIN 0x09 + +#define ADHOC_MODE 1 +#define INFRASTRUCTURE_MODE 2 + +/* values for struct mib_local, field preamble_type */ +#define PREAMBLE_TYPE_LONG 0 +#define PREAMBLE_TYPE_SHORT 1 +#define PREAMBLE_TYPE_AUTO 2 + +/* values for tx_rate */ +#define TX_RATE_1MBIT 0 +#define TX_RATE_2MBIT 1 +#define TX_RATE_5_5MBIT 2 +#define TX_RATE_11MBIT 3 +#define TX_RATE_AUTO 4 + +/* power management modes */ +#define AT76_PM_OFF 1 +#define AT76_PM_ON 2 +#define AT76_PM_SMART 3 + +struct hwcfg_r505 { + u8 cr39_values[14]; + u8 reserved1[14]; + u8 bb_cr[14]; + u8 pidvid[4]; + u8 mac_addr[ETH_ALEN]; + u8 regulatory_domain; + u8 reserved2[14]; + u8 cr15_values[14]; + u8 reserved3[3]; +} __attribute__((packed)); + +struct hwcfg_rfmd { + u8 cr20_values[14]; + u8 cr21_values[14]; + u8 bb_cr[14]; + u8 pidvid[4]; + u8 mac_addr[ETH_ALEN]; + u8 regulatory_domain; + u8 low_power_values[14]; + u8 normal_power_values[14]; + u8 reserved1[3]; +} __attribute__((packed)); + +struct hwcfg_intersil { + u8 mac_addr[ETH_ALEN]; + u8 cr31_values[14]; + u8 cr58_values[14]; + u8 pidvid[4]; + u8 regulatory_domain; + u8 reserved[1]; +} __attribute__((packed)); + +union at76_hwcfg { + struct hwcfg_intersil i; + struct hwcfg_rfmd r3; + struct hwcfg_r505 r5; +}; + +#define WEP_SMALL_KEY_LEN (40 / 8) +#define WEP_LARGE_KEY_LEN (104 / 8) + +struct at76_card_config { + u8 exclude_unencrypted; + u8 promiscuous_mode; + u8 short_retry_limit; + u8 encryption_type; + __le16 rts_threshold; + __le16 fragmentation_threshold; /* 256..2346 */ + u8 basic_rate_set[4]; + u8 auto_rate_fallback; /* 0,1 */ + u8 channel; + u8 privacy_invoked; + u8 wep_default_key_id; /* 0..3 */ + u8 current_ssid[32]; + u8 wep_default_key_value[4][WEP_KEY_LEN]; + u8 ssid_len; + u8 short_preamble; + __le16 beacon_period; +} __attribute__((packed)); + +struct at76_command { + u8 cmd; + u8 reserved; + __le16 size; + u8 data[0]; +} __attribute__((packed)); + +/* Length of Atmel-specific Rx header before 802.11 frame */ +#define AT76_RX_HDRLEN offsetof(struct at76_rx_buffer, packet) + +struct at76_rx_buffer { + __le16 wlength; + u8 rx_rate; + u8 newbss; + u8 fragmentation; + u8 rssi; + u8 link_quality; + u8 noise_level; + __le32 rx_time; + u8 packet[IEEE80211_FRAME_LEN + IEEE80211_FCS_LEN]; +} __attribute__((packed)); + +/* Length of Atmel-specific Tx header before 802.11 frame */ +#define AT76_TX_HDRLEN offsetof(struct at76_tx_buffer, packet) + +struct at76_tx_buffer { + __le16 wlength; + u8 tx_rate; + u8 padding; + u8 reserved[4]; + u8 packet[IEEE80211_FRAME_LEN + IEEE80211_FCS_LEN]; +} __attribute__((packed)); + +/* defines for scan_type below */ +#define SCAN_TYPE_ACTIVE 0 +#define SCAN_TYPE_PASSIVE 1 + +struct at76_req_scan { + u8 bssid[ETH_ALEN]; + u8 essid[32]; + u8 scan_type; + u8 channel; + __le16 probe_delay; + __le16 min_channel_time; + __le16 max_channel_time; + u8 essid_size; + u8 international_scan; +} __attribute__((packed)); + +struct at76_req_ibss { + u8 bssid[ETH_ALEN]; + u8 essid[32]; + u8 bss_type; + u8 channel; + u8 essid_size; + u8 reserved[3]; +} __attribute__((packed)); + +struct at76_req_join { + u8 bssid[ETH_ALEN]; + u8 essid[32]; + u8 bss_type; + u8 channel; + __le16 timeout; + u8 essid_size; + u8 reserved; +} __attribute__((packed)); + +struct set_mib_buffer { + u8 type; + u8 size; + u8 index; + u8 reserved; + union { + u8 byte; + __le16 word; + u8 addr[ETH_ALEN]; + } data; +} __attribute__((packed)); + +struct mib_local { + u16 reserved0; + u8 beacon_enable; + u8 txautorate_fallback; + u8 reserved1; + u8 ssid_size; + u8 promiscuous_mode; + u16 reserved2; + u8 preamble_type; + u16 reserved3; +} __attribute__((packed)); + +struct mib_mac_addr { + u8 mac_addr[ETH_ALEN]; + u8 res[2]; /* ??? */ + u8 group_addr[4][ETH_ALEN]; + u8 group_addr_status[4]; +} __attribute__((packed)); + +struct mib_mac { + __le32 max_tx_msdu_lifetime; + __le32 max_rx_lifetime; + __le16 frag_threshold; + __le16 rts_threshold; + __le16 cwmin; + __le16 cwmax; + u8 short_retry_time; + u8 long_retry_time; + u8 scan_type; /* active or passive */ + u8 scan_channel; + __le16 probe_delay; /* delay before ProbeReq in active scan, RO */ + __le16 min_channel_time; + __le16 max_channel_time; + __le16 listen_interval; + u8 desired_ssid[32]; + u8 desired_bssid[ETH_ALEN]; + u8 desired_bsstype; /* ad-hoc or infrastructure */ + u8 reserved2; +} __attribute__((packed)); + +struct mib_mac_mgmt { + __le16 beacon_period; + __le16 CFP_max_duration; + __le16 medium_occupancy_limit; + __le16 station_id; /* assoc id */ + __le16 ATIM_window; + u8 CFP_mode; + u8 privacy_option_implemented; + u8 DTIM_period; + u8 CFP_period; + u8 current_bssid[ETH_ALEN]; + u8 current_essid[32]; + u8 current_bss_type; + u8 power_mgmt_mode; + /* rfmd and 505 */ + u8 ibss_change; + u8 res; + u8 multi_domain_capability_implemented; + u8 multi_domain_capability_enabled; + u8 country_string[3]; + u8 reserved[3]; +} __attribute__((packed)); + +struct mib_mac_wep { + u8 privacy_invoked; /* 0 disable encr., 1 enable encr */ + u8 wep_default_key_id; + u8 wep_key_mapping_len; + u8 exclude_unencrypted; + __le32 wep_icv_error_count; + __le32 wep_excluded_count; + u8 wep_default_keyvalue[WEP_KEYS][WEP_KEY_LEN]; + u8 encryption_level; /* 1 for 40bit, 2 for 104bit encryption */ +} __attribute__((packed)); + +struct mib_phy { + __le32 ed_threshold; + + __le16 slot_time; + __le16 sifs_time; + __le16 preamble_length; + __le16 plcp_header_length; + __le16 mpdu_max_length; + __le16 cca_mode_supported; + + u8 operation_rate_set[4]; + u8 channel_id; + u8 current_cca_mode; + u8 phy_type; + u8 current_reg_domain; +} __attribute__((packed)); + +struct mib_fw_version { + u8 major; + u8 minor; + u8 patch; + u8 build; +} __attribute__((packed)); + +struct mib_mdomain { + u8 tx_powerlevel[14]; + u8 channel_list[14]; /* 0 for invalid channels */ +} __attribute__((packed)); + +struct at76_fw_header { + __le32 crc; /* CRC32 of the whole image */ + __le32 board_type; /* firmware compatibility code */ + u8 build; /* firmware build number */ + u8 patch; /* firmware patch level */ + u8 minor; /* firmware minor version */ + u8 major; /* firmware major version */ + __le32 str_offset; /* offset of the copyright string */ + __le32 int_fw_offset; /* internal firmware image offset */ + __le32 int_fw_len; /* internal firmware image length */ + __le32 ext_fw_offset; /* external firmware image offset */ + __le32 ext_fw_len; /* external firmware image length */ +} __attribute__((packed)); + +enum mac_state { + MAC_INIT, + MAC_SCANNING, + MAC_AUTH, + MAC_ASSOC, + MAC_JOINING, + MAC_CONNECTED, + MAC_OWN_IBSS +}; + +/* a description of a regulatory domain and the allowed channels */ +struct reg_domain { + u16 code; + char const *name; + u32 channel_map; /* if bit N is set, channel (N+1) is allowed */ +}; + +/* how long do we keep a (I)BSS in the bss_list in jiffies + this should be long enough for the user to retrieve the table + (by iwlist ?) after the device started, because all entries from + other channels than the one the device locks on get removed, too */ +#define BSS_LIST_TIMEOUT (120 * HZ) +/* struct to store BSS info found during scan */ +#define BSS_LIST_MAX_RATE_LEN 32 /* 32 rates should be enough ... */ + +struct bss_info { + struct list_head list; + + u8 bssid[ETH_ALEN]; /* bssid */ + u8 ssid[IW_ESSID_MAX_SIZE]; /* essid */ + u8 ssid_len; /* length of ssid above */ + u8 channel; + u16 capa; /* BSS capabilities */ + u16 beacon_interval; /* beacon interval, Kus (1024 microseconds) */ + u8 rates[BSS_LIST_MAX_RATE_LEN]; /* supported rates in units of + 500 kbps, ORed with 0x80 for + basic rates */ + u8 rates_len; + + /* quality of received beacon */ + u8 rssi; + u8 link_qual; + u8 noise_level; + + unsigned long last_rx; /* time (jiffies) of last beacon received */ +}; + +/* a rx data buffer to collect rx fragments */ +struct rx_data_buf { + u8 sender[ETH_ALEN]; /* sender address */ + u16 seqnr; /* sequence number */ + u16 fragnr; /* last fragment received */ + unsigned long last_rx; /* jiffies of last rx */ + struct sk_buff *skb; /* == NULL if entry is free */ +}; + +#define NR_RX_DATA_BUF 8 + +/* Data for one loaded firmware file */ +struct fwentry { + const char *const fwname; + const struct firmware *fw; + int extfw_size; + int intfw_size; + /* pointer to loaded firmware, no need to free */ + u8 *extfw; /* external firmware, extfw_size bytes long */ + u8 *intfw; /* internal firmware, intfw_size bytes long */ + enum board_type board_type; /* board type */ + struct mib_fw_version fw_version; + int loaded; /* Loaded and parsed successfully */ +}; + +struct at76_priv { + struct usb_device *udev; /* USB device pointer */ + struct net_device *netdev; /* net device pointer */ + struct net_device_stats stats; /* net device stats */ + struct iw_statistics wstats; /* wireless stats */ + + struct sk_buff *rx_skb; /* skbuff for receiving data */ + void *bulk_out_buffer; /* buffer for sending data */ + + struct urb *tx_urb; /* URB for sending data */ + struct urb *rx_urb; /* URB for receiving data */ + + unsigned int tx_pipe; /* bulk out pipe */ + unsigned int rx_pipe; /* bulk in pipe */ + + struct mutex mtx; /* locks this structure */ + + /* work queues */ + struct work_struct work_assoc_done; + struct work_struct work_join; + struct work_struct work_new_bss; + struct work_struct work_start_scan; + struct work_struct work_set_promisc; + struct work_struct work_submit_rx; + struct delayed_work dwork_restart; + struct delayed_work dwork_get_scan; + struct delayed_work dwork_beacon; + struct delayed_work dwork_auth; + struct delayed_work dwork_assoc; + + struct tasklet_struct rx_tasklet; + + /* the WEP stuff */ + int wep_enabled; /* 1 if WEP is enabled */ + int wep_key_id; /* key id to be used */ + u8 wep_keys[WEP_KEYS][WEP_KEY_LEN]; /* the four WEP keys, + 5 or 13 bytes are used */ + u8 wep_keys_len[WEP_KEYS]; /* the length of the above keys */ + + int channel; + int iw_mode; + u8 bssid[ETH_ALEN]; + u8 essid[IW_ESSID_MAX_SIZE]; + int essid_size; + int radio_on; + int promisc; + + int preamble_type; /* 0 - long, 1 - short, 2 - auto */ + int auth_mode; /* authentication type: 0 open, 1 shared key */ + int txrate; /* 0,1,2,3 = 1,2,5.5,11 Mbps, 4 is auto */ + int frag_threshold; /* threshold for fragmentation of tx packets */ + int rts_threshold; /* threshold for RTS mechanism */ + int short_retry_limit; + + int scan_min_time; /* scan min channel time */ + int scan_max_time; /* scan max channel time */ + int scan_mode; /* SCAN_TYPE_ACTIVE, SCAN_TYPE_PASSIVE */ + int scan_need_any; /* if set, need to scan for any ESSID */ + + /* the list we got from scanning */ + spinlock_t bss_list_spinlock; /* protects bss_list operations */ + struct list_head bss_list; /* list of BSS we got beacons from */ + struct timer_list bss_list_timer; /* timer to purge old entries + from bss_list */ + struct bss_info *curr_bss; /* current BSS */ + u16 assoc_id; /* current association ID, if associated */ + + u8 wanted_bssid[ETH_ALEN]; + int wanted_bssid_valid; /* != 0 if wanted_bssid is to be used */ + + /* some data for infrastructure mode only */ + spinlock_t mgmt_spinlock; /* this spinlock protects access to + next_mgmt_bulk */ + + struct at76_tx_buffer *next_mgmt_bulk; /* pending management msg to + send via bulk out */ + enum mac_state mac_state; + enum { + SCAN_IDLE, + SCAN_IN_PROGRESS, + SCAN_COMPLETED + } scan_state; + time_t last_scan; + + int retries; /* remaining retries in case of timeout when + * sending AuthReq or AssocReq */ + u8 pm_mode; /* power management mode */ + u32 pm_period; /* power management period in microseconds */ + + struct reg_domain const *domain; /* reg domain description */ + + /* iwspy support */ + spinlock_t spy_spinlock; + struct iw_spy_data spy_data; + + struct iw_public_data wireless_data; + + /* These fields contain HW config provided by the device (not all of + * these fields are used by all board types) */ + u8 mac_addr[ETH_ALEN]; + u8 regulatory_domain; + + struct at76_card_config card_config; + + /* store rx fragments until complete */ + struct rx_data_buf rx_data[NR_RX_DATA_BUF]; + + enum board_type board_type; + struct mib_fw_version fw_version; + + unsigned int device_unplugged:1; + unsigned int netdev_registered:1; + struct set_mib_buffer mib_buf; /* global buffer for set_mib calls */ + + /* beacon counting */ + int beacon_period; /* period of mgmt beacons, Kus */ + int beacons_received; + unsigned long beacons_last_qual; /* time we restarted counting + beacons */ +}; + +struct at76_rx_radiotap { + struct ieee80211_radiotap_header rt_hdr; + __le64 rt_tsft; + u8 rt_flags; + u8 rt_rate; + s8 rt_signal; + s8 rt_noise; +}; + +#define AT76_RX_RADIOTAP_PRESENT \ + ((1 << IEEE80211_RADIOTAP_TSFT) | \ + (1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL) | \ + (1 << IEEE80211_RADIOTAP_DB_ANTNOISE)) + +#define BEACON_MAX_DATA_LENGTH 1500 + +/* the maximum size of an AssocReq packet */ +#define ASSOCREQ_MAX_SIZE \ + (AT76_TX_HDRLEN + sizeof(struct ieee80211_assoc_request) + \ + 1 + 1 + IW_ESSID_MAX_SIZE + 1 + 1 + 4) + +/* for shared secret auth, add the challenge text size */ +#define AUTH_FRAME_SIZE (AT76_TX_HDRLEN + sizeof(struct ieee80211_auth)) + +/* Maximal number of AuthReq retries */ +#define AUTH_RETRIES 3 + +/* Maximal number of AssocReq retries */ +#define ASSOC_RETRIES 3 + +/* Beacon timeout in managed mode when we are connected */ +#define BEACON_TIMEOUT (10 * HZ) + +/* Timeout for authentication response */ +#define AUTH_TIMEOUT (1 * HZ) + +/* Timeout for association response */ +#define ASSOC_TIMEOUT (1 * HZ) + +/* Polling interval when scan is running */ +#define SCAN_POLL_INTERVAL (HZ / 4) + +/* Command completion timeout */ +#define CMD_COMPLETION_TIMEOUT (5 * HZ) + +#define DEF_RTS_THRESHOLD 1536 +#define DEF_FRAG_THRESHOLD 1536 +#define DEF_SHORT_RETRY_LIMIT 8 +#define DEF_CHANNEL 10 +#define DEF_SCAN_MIN_TIME 10 +#define DEF_SCAN_MAX_TIME 120 + +#define MAX_RTS_THRESHOLD (MAX_FRAG_THRESHOLD + 1) + +/* the max padding size for tx in bytes (see calc_padding) */ +#define MAX_PADDING_SIZE 53 + +#endif /* _AT76_USB_H */ diff -puN /dev/null drivers/net/wireless/ath5k/Makefile --- /dev/null +++ a/drivers/net/wireless/ath5k/Makefile @@ -0,0 +1,2 @@ +ath5k-objs = base.o hw.o regdom.o initvals.o phy.o +obj-$(CONFIG_ATH5K) += ath5k.o diff -puN /dev/null drivers/net/wireless/ath5k/ath5k.h --- /dev/null +++ a/drivers/net/wireless/ath5k/ath5k.h @@ -0,0 +1,1086 @@ +/* + * Copyright (c) 2004-2007 Reyk Floeter + * Copyright (c) 2006-2007 Nick Kossifidis + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _ATH5K_H +#define _ATH5K_H + +/* Set this to 1 to disable regulatory domain restrictions for channel tests. + * WARNING: This is for debuging only and has side effects (eg. scan takes too + * long and results timeouts). It's also illegal to tune to some of the + * supported frequencies in some countries, so use this at your own risk, + * you've been warned. */ +#define CHAN_DEBUG 0 + +/* Uncomment this for debuging (warning that it results in TOO much output) */ +/*#define AR5K_DEBUG 1 */ + +#include +#include +#include + +#include "hw.h" +#include "regdom.h" + +/* PCI IDs */ +#define PCI_DEVICE_ID_ATHEROS_AR5210 0x0007 /* AR5210 */ +#define PCI_DEVICE_ID_ATHEROS_AR5311 0x0011 /* AR5311 */ +#define PCI_DEVICE_ID_ATHEROS_AR5211 0x0012 /* AR5211 */ +#define PCI_DEVICE_ID_ATHEROS_AR5212 0x0013 /* AR5212 */ +#define PCI_DEVICE_ID_3COM_3CRDAG675 0x0013 /* 3CRDAG675 (Atheros AR5212) */ +#define PCI_DEVICE_ID_3COM_2_3CRPAG175 0x0013 /* 3CRPAG175 (Atheros AR5212) */ +#define PCI_DEVICE_ID_ATHEROS_AR5210_AP 0x0207 /* AR5210 (Early) */ +#define PCI_DEVICE_ID_ATHEROS_AR5212_IBM 0x1014 /* AR5212 (IBM MiniPCI) */ +#define PCI_DEVICE_ID_ATHEROS_AR5210_DEFAULT 0x1107 /* AR5210 (no eeprom) */ +#define PCI_DEVICE_ID_ATHEROS_AR5212_DEFAULT 0x1113 /* AR5212 (no eeprom) */ +#define PCI_DEVICE_ID_ATHEROS_AR5211_DEFAULT 0x1112 /* AR5211 (no eeprom) */ +#define PCI_DEVICE_ID_ATHEROS_AR5212_FPGA 0xf013 /* AR5212 (emulation board) */ +#define PCI_DEVICE_ID_ATHEROS_AR5211_LEGACY 0xff12 /* AR5211 (emulation board) */ +#define PCI_DEVICE_ID_ATHEROS_AR5211_FPGA11B 0xf11b /* AR5211 (emulation board) */ +#define PCI_DEVICE_ID_ATHEROS_AR5212_REV2 0x0052 /* AR5312 WMAC (AP31) */ +#define PCI_DEVICE_ID_ATHEROS_AR5212_REV7 0x0057 /* AR5312 WMAC (AP30-040) */ +#define PCI_DEVICE_ID_ATHEROS_AR5212_REV8 0x0058 /* AR5312 WMAC (AP43-030) */ +#define PCI_DEVICE_ID_ATHEROS_AR5212_0014 0x0014 /* AR5212 compatible */ +#define PCI_DEVICE_ID_ATHEROS_AR5212_0015 0x0015 /* AR5212 compatible */ +#define PCI_DEVICE_ID_ATHEROS_AR5212_0016 0x0016 /* AR5212 compatible */ +#define PCI_DEVICE_ID_ATHEROS_AR5212_0017 0x0017 /* AR5212 compatible */ +#define PCI_DEVICE_ID_ATHEROS_AR5212_0018 0x0018 /* AR5212 compatible */ +#define PCI_DEVICE_ID_ATHEROS_AR5212_0019 0x0019 /* AR5212 compatible */ +#define PCI_DEVICE_ID_ATHEROS_AR2413 0x001a /* AR2413 (Griffin-lite) */ +#define PCI_DEVICE_ID_ATHEROS_AR5413 0x001b /* AR5413 (Eagle) */ +#define PCI_DEVICE_ID_ATHEROS_AR5424 0x001c /* AR5424 (Condor PCI-E) */ + +/****************************\ + GENERIC DRIVER DEFINITIONS +\****************************/ + +#define AR5K_PRINTF(fmt, ...) printk("%s: " fmt, __func__, ##__VA_ARGS__) +#define AR5K_PRINT(fmt) printk("%s: " fmt, __func__) +#ifdef AR5K_DEBUG +#define AR5K_TRACE printk(KERN_DEBUG "%s:%d\n", __func__, __LINE__) +#else +#define AR5K_TRACE +#endif + +/* + * Some tuneable values (these should be changeable by the user) + */ +#define AR5K_TUNE_DMA_BEACON_RESP 2 +#define AR5K_TUNE_SW_BEACON_RESP 10 +#define AR5K_TUNE_ADDITIONAL_SWBA_BACKOFF 0 +#define AR5K_TUNE_RADAR_ALERT false +#define AR5K_TUNE_MIN_TX_FIFO_THRES 1 +#define AR5K_TUNE_MAX_TX_FIFO_THRES ((IEEE80211_MAX_LEN / 64) + 1) +#define AR5K_TUNE_RSSI_THRES 1792 +#define AR5K_TUNE_REGISTER_TIMEOUT 20000 +#define AR5K_TUNE_REGISTER_DWELL_TIME 20000 +#define AR5K_TUNE_BEACON_INTERVAL 100 +#define AR5K_TUNE_AIFS 2 +#define AR5K_TUNE_AIFS_11B 2 +#define AR5K_TUNE_AIFS_XR 0 +#define AR5K_TUNE_CWMIN 15 +#define AR5K_TUNE_CWMIN_11B 31 +#define AR5K_TUNE_CWMIN_XR 3 +#define AR5K_TUNE_CWMAX 1023 +#define AR5K_TUNE_CWMAX_11B 1023 +#define AR5K_TUNE_CWMAX_XR 7 +#define AR5K_TUNE_NOISE_FLOOR -72 +#define AR5K_TUNE_MAX_TXPOWER 60 +#define AR5K_TUNE_DEFAULT_TXPOWER 30 +#define AR5K_TUNE_TPC_TXPOWER true +#define AR5K_TUNE_ANT_DIVERSITY true +#define AR5K_TUNE_HWTXTRIES 4 + +/* token to use for aifs, cwmin, cwmax in MadWiFi */ +#define AR5K_TXQ_USEDEFAULT ((u32) -1) + +/* GENERIC CHIPSET DEFINITIONS */ + +/* MAC Chips */ +enum ath5k_version { + AR5K_AR5210 = 0, + AR5K_AR5211 = 1, + AR5K_AR5212 = 2, +}; + +/* PHY Chips */ +enum ath5k_radio { + AR5K_RF5110 = 0, + AR5K_RF5111 = 1, + AR5K_RF5112 = 2, +}; + +/* + * Common silicon revision/version values + */ +#define AR5K_SREV_UNKNOWN 0xffff + +#define AR5K_SREV_VER_AR5210 0x00 +#define AR5K_SREV_VER_AR5311 0x10 +#define AR5K_SREV_VER_AR5311A 0x20 +#define AR5K_SREV_VER_AR5311B 0x30 +#define AR5K_SREV_VER_AR5211 0x40 +#define AR5K_SREV_VER_AR5212 0x50 +#define AR5K_SREV_VER_AR5213 0x55 +#define AR5K_SREV_VER_UNSUPP 0x60 + +#define AR5K_SREV_RAD_5110 0x00 +#define AR5K_SREV_RAD_5111 0x10 +#define AR5K_SREV_RAD_5111A 0x15 +#define AR5K_SREV_RAD_2111 0x20 +#define AR5K_SREV_RAD_5112 0x30 +#define AR5K_SREV_RAD_5112A 0x35 +#define AR5K_SREV_RAD_2112 0x40 +#define AR5K_SREV_RAD_2112A 0x45 +#define AR5K_SREV_RAD_UNSUPP 0x50 + +/* IEEE defs */ + +#define IEEE80211_MAX_LEN 2500 + +/* TODO add support to mac80211 for vendor-specific rates and modes */ + +/* + * Some of this information is based on Documentation from: + * + * http://madwifi.org/wiki/ChipsetFeatures/SuperAG + * + * Modulation for Atheros' eXtended Range - range enhancing extension that is + * supposed to double the distance an Atheros client device can keep a + * connection with an Atheros access point. This is achieved by increasing + * the receiver sensitivity up to, -105dBm, which is about 20dB above what + * the 802.11 specifications demand. In addition, new (proprietary) data rates + * are introduced: 3, 2, 1, 0.5 and 0.25 MBit/s. + * + * Please note that can you either use XR or TURBO but you cannot use both, + * they are exclusive. + * + */ +#define MODULATION_XR 0x00000200 +/* + * Modulation for Atheros' Turbo G and Turbo A, its supposed to provide a + * throughput transmission speed up to 40Mbit/s-60Mbit/s at a 108Mbit/s + * signaling rate achieved through the bonding of two 54Mbit/s 802.11g + * channels. To use this feature your Access Point must also suport it. + * There is also a distinction between "static" and "dynamic" turbo modes: + * + * - Static: is the dumb version: devices set to this mode stick to it until + * the mode is turned off. + * - Dynamic: is the intelligent version, the network decides itself if it + * is ok to use turbo. As soon as traffic is detected on adjacent channels + * (which would get used in turbo mode), or when a non-turbo station joins + * the network, turbo mode won't be used until the situation changes again. + * Dynamic mode is achieved by Atheros' Adaptive Radio (AR) feature which + * monitors the used radio band in order to decide whether turbo mode may + * be used or not. + * + * This article claims Super G sticks to bonding of channels 5 and 6 for + * USA: + * + * http://www.pcworld.com/article/id,113428-page,1/article.html + * + * The channel bonding seems to be driver specific though. In addition to + * deciding what channels will be used, these "Turbo" modes are accomplished + * by also enabling the following features: + * + * - Bursting: allows multiple frames to be sent at once, rather than pausing + * after each frame. Bursting is a standards-compliant feature that can be + * used with any Access Point. + * - Fast frames: increases the amount of information that can be sent per + * frame, also resulting in a reduction of transmission overhead. It is a + * proprietary feature that needs to be supported by the Access Point. + * - Compression: data frames are compressed in real time using a Lempel Ziv + * algorithm. This is done transparently. Once this feature is enabled, + * compression and decompression takes place inside the chipset, without + * putting additional load on the host CPU. + */ +#define MODULATION_TURBO 0x00000080 + +enum ath5k_vendor_mode { + MODE_ATHEROS_TURBO = NUM_IEEE80211_MODES+1, + MODE_ATHEROS_TURBOG +}; + +/* adding this flag to rate_code enables short preamble, see ar5212_reg.h */ +#define AR5K_SET_SHORT_PREAMBLE 0x04 + +#define HAS_SHPREAMBLE(_ix) (rt->rates[_ix].modulation == IEEE80211_RATE_CCK_2) +#define SHPREAMBLE_FLAG(_ix) (HAS_SHPREAMBLE(_ix) ? AR5K_SET_SHORT_PREAMBLE : 0) + +/****************\ + TX DEFINITIONS +\****************/ + +/* + * Tx Descriptor + */ +struct ath_tx_status { + u16 ts_seqnum; + u16 ts_tstamp; + u8 ts_status; + u8 ts_rate; + s8 ts_rssi; + u8 ts_shortretry; + u8 ts_longretry; + u8 ts_virtcol; + u8 ts_antenna; +}; + +#define AR5K_TXSTAT_ALTRATE 0x80 +#define AR5K_TXERR_XRETRY 0x01 +#define AR5K_TXERR_FILT 0x02 +#define AR5K_TXERR_FIFO 0x04 + +/* + * Queue types used to classify tx queues. + */ +enum ath5k_tx_queue { + AR5K_TX_QUEUE_INACTIVE = 0, /* q is unused -- see ath5k_hw_release_tx_queue */ + AR5K_TX_QUEUE_DATA, /*A normal data queue*/ + AR5K_TX_QUEUE_XR_DATA, /*An XR-data queue*/ + AR5K_TX_QUEUE_BEACON, /*The beacon queue*/ + AR5K_TX_QUEUE_CAB, /*The ater-beacon queue*/ + AR5K_TX_QUEUE_UAPSD, /*Unscheduled Automatic Power Save Delivery queue*/ +}; + +#define AR5K_NUM_TX_QUEUES 10 +#define AR5K_NUM_TX_QUEUES_NOQCU 2 + +/* + * Queue syb-types to classify normal data queues. + * These are the 4 Access Categories as defined in + * WME spec. 0 is the lowest priority and 4 is the + * highest. Normal data that hasn't been classified + * goes to the Best Effort AC. + */ +enum ath5k_tx_queue_subtype { + AR5K_WME_AC_BK = 0, /*Background traffic*/ + AR5K_WME_AC_BE, /*Best-effort (normal) traffic)*/ + AR5K_WME_AC_VI, /*Video traffic*/ + AR5K_WME_AC_VO, /*Voice traffic*/ +}; + +/* + * Queue ID numbers as returned by the HAL, each number + * represents a hw queue. If hw does not support hw queues + * (eg 5210) all data goes in one queue. These match + * d80211 definitions (net80211/MadWiFi don't use them). + */ +enum ath5k_tx_queue_id { + AR5K_TX_QUEUE_ID_NOQCU_DATA = 0, + AR5K_TX_QUEUE_ID_NOQCU_BEACON = 1, + AR5K_TX_QUEUE_ID_DATA_MIN = 0, /*IEEE80211_TX_QUEUE_DATA0*/ + AR5K_TX_QUEUE_ID_DATA_MAX = 4, /*IEEE80211_TX_QUEUE_DATA4*/ + AR5K_TX_QUEUE_ID_DATA_SVP = 5, /*IEEE80211_TX_QUEUE_SVP - Spectralink Voice Protocol*/ + AR5K_TX_QUEUE_ID_CAB = 6, /*IEEE80211_TX_QUEUE_AFTER_BEACON*/ + AR5K_TX_QUEUE_ID_BEACON = 7, /*IEEE80211_TX_QUEUE_BEACON*/ + AR5K_TX_QUEUE_ID_UAPSD = 8, + AR5K_TX_QUEUE_ID_XR_DATA = 9, +}; + + +/* + * Flags to set hw queue's parameters... + */ +#define AR5K_TXQ_FLAG_TXINT_ENABLE 0x0001 /* Enable TXOK and TXERR interrupts -not used- */ +#define AR5K_TXQ_FLAG_TXDESCINT_ENABLE 0x0002 /* Enable TXDESC interrupt -not implemented- */ +#define AR5K_TXQ_FLAG_BACKOFF_DISABLE 0x0004 /* Disable random post-backoff */ +#define AR5K_TXQ_FLAG_COMPRESSION_ENABLE 0x0008 /* Enable hw compression -not implemented-*/ +#define AR5K_TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE 0x0010 /* Enable ready time expiry policy (?)*/ +#define AR5K_TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE 0x0020 /* Enable backoff while bursting */ +#define AR5K_TXQ_FLAG_POST_FR_BKOFF_DIS 0x0040 /* Disable backoff while bursting */ +#define AR5K_TXQ_FLAG_TXEOLINT_ENABLE 0x0080 /* Enable TXEOL interrupt -not implemented-*/ + +/* + * A struct to hold tx queue's parameters + */ +struct ath5k_txq_info { + enum ath5k_tx_queue tqi_type; + enum ath5k_tx_queue_subtype tqi_subtype; + u16 tqi_flags; /* Tx queue flags (see above) */ + u32 tqi_aifs; /* Arbitrated Interframe Space */ + s32 tqi_cw_min; /* Minimum Contention Window */ + s32 tqi_cw_max; /* Maximum Contention Window */ + u32 tqi_cbr_period; /* Constant bit rate period */ + u32 tqi_cbr_overflow_limit; + u32 tqi_burst_time; + u32 tqi_ready_time; /* Not used */ + u32 tqi_comp_buffer;/* Compression Buffer's phys addr */ +}; + +/* + * Transmit packet types. + * These are not fully used inside OpenHAL yet + */ +enum ath5k_pkt_type { + AR5K_PKT_TYPE_NORMAL = 0, + AR5K_PKT_TYPE_ATIM = 1, + AR5K_PKT_TYPE_PSPOLL = 2, + AR5K_PKT_TYPE_BEACON = 3, + AR5K_PKT_TYPE_PROBE_RESP = 4, + AR5K_PKT_TYPE_PIFS = 5, +}; + +/* + * TX power and TPC settings + */ +#define AR5K_TXPOWER_OFDM(_r, _v) ( \ + ((0 & 1) << ((_v) + 6)) | \ + (((hal->ah_txpower.txp_rates[(_r)]) & 0x3f) << (_v)) \ +) + +#define AR5K_TXPOWER_CCK(_r, _v) ( \ + (hal->ah_txpower.txp_rates[(_r)] & 0x3f) << (_v) \ +) + +/* + * Used to compute TX times + */ +#define AR5K_CCK_SIFS_TIME 10 +#define AR5K_CCK_PREAMBLE_BITS 144 +#define AR5K_CCK_PLCP_BITS 48 + +#define AR5K_OFDM_SIFS_TIME 16 +#define AR5K_OFDM_PREAMBLE_TIME 20 +#define AR5K_OFDM_PLCP_BITS 22 +#define AR5K_OFDM_SYMBOL_TIME 4 + +#define AR5K_TURBO_SIFS_TIME 8 +#define AR5K_TURBO_PREAMBLE_TIME 14 +#define AR5K_TURBO_PLCP_BITS 22 +#define AR5K_TURBO_SYMBOL_TIME 4 + +#define AR5K_XR_SIFS_TIME 16 +#define AR5K_XR_PLCP_BITS 22 +#define AR5K_XR_SYMBOL_TIME 4 + +/* CCK */ +#define AR5K_CCK_NUM_BITS(_frmlen) (_frmlen << 3) + +#define AR5K_CCK_PHY_TIME(_sp) (_sp ? \ + ((AR5K_CCK_PREAMBLE_BITS + AR5K_CCK_PLCP_BITS) >> 1) : \ + (AR5K_CCK_PREAMBLE_BITS + AR5K_CCK_PLCP_BITS)) + +#define AR5K_CCK_TX_TIME(_kbps, _frmlen, _sp) \ + (AR5K_CCK_PHY_TIME(_sp) + \ + ((AR5K_CCK_NUM_BITS(_frmlen) * 1000) / _kbps) + \ + AR5K_CCK_SIFS_TIME) + +/* OFDM */ +#define AR5K_OFDM_NUM_BITS(_frmlen) (AR5K_OFDM_PLCP_BITS + (_frmlen << 3)) + +#define AR5K_OFDM_NUM_BITS_PER_SYM(_kbps) ((_kbps * \ + AR5K_OFDM_SYMBOL_TIME) / 1000) + +#define AR5K_OFDM_NUM_BITS(_frmlen) (AR5K_OFDM_PLCP_BITS + (_frmlen << 3)) + +#define AR5K_OFDM_NUM_SYMBOLS(_kbps, _frmlen) \ + DIV_ROUND_UP(AR5K_OFDM_NUM_BITS(_frmlen), \ + AR5K_OFDM_NUM_BITS_PER_SYM(_kbps)) + +#define AR5K_OFDM_TX_TIME(_kbps, _frmlen) \ + (AR5K_OFDM_PREAMBLE_TIME + AR5K_OFDM_SIFS_TIME + \ + (AR5K_OFDM_NUM_SYMBOLS(_kbps, _frmlen) * AR5K_OFDM_SYMBOL_TIME)) + +/* TURBO */ +#define AR5K_TURBO_NUM_BITS(_frmlen) (AR5K_TURBO_PLCP_BITS + (_frmlen << 3)) + +#define AR5K_TURBO_NUM_BITS_PER_SYM(_kbps) (((_kbps << 1) * \ + AR5K_TURBO_SYMBOL_TIME) / 1000) + +#define AR5K_TURBO_NUM_BITS(_frmlen) (AR5K_TURBO_PLCP_BITS + (_frmlen << 3)) + +#define AR5K_TURBO_NUM_SYMBOLS(_kbps, _frmlen) \ + DIV_ROUND_UP(AR5K_TURBO_NUM_BITS(_frmlen), \ + AR5K_TURBO_NUM_BITS_PER_SYM(_kbps)) + +#define AR5K_TURBO_TX_TIME(_kbps, _frmlen) \ + (AR5K_TURBO_PREAMBLE_TIME + AR5K_TURBO_SIFS_TIME + \ + (AR5K_TURBO_NUM_SYMBOLS(_kbps, _frmlen) * AR5K_TURBO_SYMBOL_TIME)) + +/* eXtendent Range (?)*/ +#define AR5K_XR_PREAMBLE_TIME(_kbps) (((_kbps) < 1000) ? 173 : 76) + +#define AR5K_XR_NUM_BITS_PER_SYM(_kbps) ((_kbps * \ + AR5K_XR_SYMBOL_TIME) / 1000) + +#define AR5K_XR_NUM_BITS(_frmlen) (AR5K_XR_PLCP_BITS + (_frmlen << 3)) + +#define AR5K_XR_NUM_SYMBOLS(_kbps, _frmlen) \ + DIV_ROUND_UP(AR5K_XR_NUM_BITS(_frmlen), AR5K_XR_NUM_BITS_PER_SYM(_kbps)) + +#define AR5K_XR_TX_TIME(_kbps, _frmlen) \ + (AR5K_XR_PREAMBLE_TIME(_kbps) + AR5K_XR_SIFS_TIME + \ + (AR5K_XR_NUM_SYMBOLS(_kbps, _frmlen) * AR5K_XR_SYMBOL_TIME)) + +/* + * DMA size definitions (2^n+2) + */ +enum ath5k_dmasize { + AR5K_DMASIZE_4B = 0, + AR5K_DMASIZE_8B, + AR5K_DMASIZE_16B, + AR5K_DMASIZE_32B, + AR5K_DMASIZE_64B, + AR5K_DMASIZE_128B, + AR5K_DMASIZE_256B, + AR5K_DMASIZE_512B +}; + + +/****************\ + RX DEFINITIONS +\****************/ + +/* + * Rx Descriptor + */ +struct ath_rx_status { + u16 rs_datalen; + u16 rs_tstamp; + u8 rs_status; + u8 rs_phyerr; + s8 rs_rssi; + u8 rs_keyix; + u8 rs_rate; + u8 rs_antenna; + u8 rs_more; +}; + +#define AR5K_RXERR_CRC 0x01 +#define AR5K_RXERR_PHY 0x02 +#define AR5K_RXERR_FIFO 0x04 +#define AR5K_RXERR_DECRYPT 0x08 +#define AR5K_RXERR_MIC 0x10 +#define AR5K_RXKEYIX_INVALID ((u8) - 1) +#define AR5K_TXKEYIX_INVALID ((u32) - 1) + +struct ath5k_mib_stats { + u32 ackrcv_bad; + u32 rts_bad; + u32 rts_good; + u32 fcs_bad; + u32 beacons; +}; + + + + +/**************************\ + BEACON TIMERS DEFINITIONS +\**************************/ + +#define AR5K_BEACON_PERIOD 0x0000ffff +#define AR5K_BEACON_ENA 0x00800000 /*enable beacon xmit*/ +#define AR5K_BEACON_RESET_TSF 0x01000000 /*force a TSF reset*/ + +/* + * Per-station beacon timer state. + */ +struct ath5k_beacon_state { + u32 bs_next_beacon; + u32 bs_next_dtim; + u32 bs_interval; /*in TU's -see net80211/ieee80211_var.h- + can also include the above flags*/ + u8 bs_dtim_period; + u8 bs_cfp_period; + u16 bs_cfp_max_duration; /*if non-zero hw is setup to coexist with + a Point Coordination Function capable AP*/ + u16 bs_cfp_du_remain; + u16 bs_tim_offset; + u16 bs_sleep_duration; + u16 bs_bmiss_threshold; + u32 bs_cfp_next; +}; + + + + +/********************\ + COMMON DEFINITIONS +\********************/ + +/* + * Atheros descriptor + */ +struct ath_desc { + u32 ds_link; + u32 ds_data; + u32 ds_ctl0; + u32 ds_ctl1; + u32 ds_hw[4]; + + union { + struct ath_rx_status rx; + struct ath_tx_status tx; + } ds_us; + +#define ds_rxstat ds_us.rx +#define ds_txstat ds_us.tx + +} __packed; + +#define AR5K_RXDESC_INTREQ 0x0020 + +#define AR5K_TXDESC_CLRDMASK 0x0001 +#define AR5K_TXDESC_NOACK 0x0002 /*[5211+]*/ +#define AR5K_TXDESC_RTSENA 0x0004 +#define AR5K_TXDESC_CTSENA 0x0008 +#define AR5K_TXDESC_INTREQ 0x0010 +#define AR5K_TXDESC_VEOL 0x0020 /*[5211+]*/ + +#define AR5K_SLOT_TIME_9 396 +#define AR5K_SLOT_TIME_20 880 +#define AR5K_SLOT_TIME_MAX 0xffff + +/* channel_flags */ +#define CHANNEL_CW_INT 0x0008 /* Contention Window interference detected */ +#define CHANNEL_TURBO 0x0010 /* Turbo Channel */ +#define CHANNEL_CCK 0x0020 /* CCK channel */ +#define CHANNEL_OFDM 0x0040 /* OFDM channel */ +#define CHANNEL_2GHZ 0x0080 /* 2GHz channel. */ +#define CHANNEL_5GHZ 0x0100 /* 5GHz channel */ +#define CHANNEL_PASSIVE 0x0200 /* Only passive scan allowed */ +#define CHANNEL_DYN 0x0400 /* Dynamic CCK-OFDM channel (for g operation) */ +#define CHANNEL_XR 0x0800 /* XR channel */ + +#define CHANNEL_A (CHANNEL_5GHZ|CHANNEL_OFDM) +#define CHANNEL_B (CHANNEL_2GHZ|CHANNEL_CCK) +#define CHANNEL_G (CHANNEL_2GHZ|CHANNEL_OFDM) +#define CHANNEL_T (CHANNEL_5GHZ|CHANNEL_OFDM|CHANNEL_TURBO) +#define CHANNEL_TG (CHANNEL_2GHZ|CHANNEL_OFDM|CHANNEL_TURBO) +#define CHANNEL_108A CHANNEL_T +#define CHANNEL_108G CHANNEL_TG +#define CHANNEL_X (CHANNEL_5GHZ|CHANNEL_OFDM|CHANNEL_XR) + +#define CHANNEL_ALL (CHANNEL_OFDM|CHANNEL_CCK|CHANNEL_2GHZ|CHANNEL_5GHZ| \ + CHANNEL_TURBO) + +#define CHANNEL_ALL_NOTURBO (CHANNEL_ALL & ~CHANNEL_TURBO) +#define CHANNEL_MODES CHANNEL_ALL + +/* + * Used internaly in OpenHAL (ar5211.c/ar5212.c + * for reset_tx_queue). Also see struct struct ieee80211_channel. + */ +#define IS_CHAN_XR(_c) ((_c.val & CHANNEL_XR) != 0) +#define IS_CHAN_B(_c) ((_c.val & CHANNEL_B) != 0) + +/* + * The following structure will be used to map 2GHz channels to + * 5GHz Atheros channels. + */ +struct ath5k_athchan_2ghz { + u32 a2_flags; + u16 a2_athchan; +}; + +/* + * Rate definitions + */ + +#define AR5K_MAX_RATES 32 /*max number of rates on the rate table*/ + +struct ath5k_rate { + u8 valid; /* Valid for rate control */ + u32 modulation; + u16 rate_kbps; + u8 rate_code; /* Rate mapping for h/w descriptors */ + u8 dot11_rate; + u8 control_rate; + u16 lp_ack_duration;/* long preamble ACK duration */ + u16 sp_ack_duration;/* short preamble ACK duration*/ +}; + +struct ath5k_rate_table { + u16 rate_count; + u8 rate_code_to_index[AR5K_MAX_RATES]; /* Back-mapping */ + struct ath5k_rate rates[AR5K_MAX_RATES]; +}; + +/* + * Rate tables... + */ +#define AR5K_RATES_11A { 8, { \ + 255, 255, 255, 255, 255, 255, 255, 255, 6, 4, 2, 0, \ + 7, 5, 3, 1, 255, 255, 255, 255, 255, 255, 255, 255, \ + 255, 255, 255, 255, 255, 255, 255, 255 }, { \ + { 1, IEEE80211_RATE_OFDM, 6000, 11, 140, 0 }, \ + { 1, IEEE80211_RATE_OFDM, 9000, 15, 18, 0 }, \ + { 1, IEEE80211_RATE_OFDM, 12000, 10, 152, 2 }, \ + { 1, IEEE80211_RATE_OFDM, 18000, 14, 36, 2 }, \ + { 1, IEEE80211_RATE_OFDM, 24000, 9, 176, 4 }, \ + { 1, IEEE80211_RATE_OFDM, 36000, 13, 72, 4 }, \ + { 1, IEEE80211_RATE_OFDM, 48000, 8, 96, 4 }, \ + { 1, IEEE80211_RATE_OFDM, 54000, 12, 108, 4 } } \ +} + +#define AR5K_RATES_11B { 4, { \ + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, \ + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, \ + 3, 2, 1, 0, 255, 255, 255, 255 }, { \ + { 1, IEEE80211_RATE_CCK, 1000, 27, 130, 0 }, \ + { 1, IEEE80211_RATE_CCK_2, 2000, 26, 132, 1 }, \ + { 1, IEEE80211_RATE_CCK_2, 5500, 25, 139, 1 }, \ + { 1, IEEE80211_RATE_CCK_2, 11000, 24, 150, 1 } } \ +} + +#define AR5K_RATES_11G { 12, { \ + 255, 255, 255, 255, 255, 255, 255, 255, 10, 8, 6, 4, \ + 11, 9, 7, 5, 255, 255, 255, 255, 255, 255, 255, 255, \ + 3, 2, 1, 0, 255, 255, 255, 255 }, { \ + { 1, IEEE80211_RATE_CCK, 1000, 27, 2, 0 }, \ + { 1, IEEE80211_RATE_CCK_2, 2000, 26, 4, 1 }, \ + { 1, IEEE80211_RATE_CCK_2, 5500, 25, 11, 1 }, \ + { 1, IEEE80211_RATE_CCK_2, 11000, 24, 22, 1 }, \ + { 0, IEEE80211_RATE_OFDM, 6000, 11, 12, 4 }, \ + { 0, IEEE80211_RATE_OFDM, 9000, 15, 18, 4 }, \ + { 1, IEEE80211_RATE_OFDM, 12000, 10, 24, 6 }, \ + { 1, IEEE80211_RATE_OFDM, 18000, 14, 36, 6 }, \ + { 1, IEEE80211_RATE_OFDM, 24000, 9, 48, 8 }, \ + { 1, IEEE80211_RATE_OFDM, 36000, 13, 72, 8 }, \ + { 1, IEEE80211_RATE_OFDM, 48000, 8, 96, 8 }, \ + { 1, IEEE80211_RATE_OFDM, 54000, 12, 108, 8 } } \ +} + +#define AR5K_RATES_TURBO { 8, { \ + 255, 255, 255, 255, 255, 255, 255, 255, 6, 4, 2, 0, \ + 7, 5, 3, 1, 255, 255, 255, 255, 255, 255, 255, 255, \ + 255, 255, 255, 255, 255, 255, 255, 255 }, { \ + { 1, MODULATION_TURBO, 6000, 11, 140, 0 }, \ + { 1, MODULATION_TURBO, 9000, 15, 18, 0 }, \ + { 1, MODULATION_TURBO, 12000, 10, 152, 2 }, \ + { 1, MODULATION_TURBO, 18000, 14, 36, 2 }, \ + { 1, MODULATION_TURBO, 24000, 9, 176, 4 }, \ + { 1, MODULATION_TURBO, 36000, 13, 72, 4 }, \ + { 1, MODULATION_TURBO, 48000, 8, 96, 4 }, \ + { 1, MODULATION_TURBO, 54000, 12, 108, 4 } } \ +} + +#define AR5K_RATES_XR { 12, { \ + 255, 3, 1, 255, 255, 255, 2, 0, 10, 8, 6, 4, \ + 11, 9, 7, 5, 255, 255, 255, 255, 255, 255, 255, 255, \ + 255, 255, 255, 255, 255, 255, 255, 255 }, { \ + { 1, MODULATION_XR, 500, 7, 129, 0 }, \ + { 1, MODULATION_XR, 1000, 2, 139, 1 }, \ + { 1, MODULATION_XR, 2000, 6, 150, 2 }, \ + { 1, MODULATION_XR, 3000, 1, 150, 3 }, \ + { 1, IEEE80211_RATE_OFDM, 6000, 11, 140, 4 }, \ + { 1, IEEE80211_RATE_OFDM, 9000, 15, 18, 4 }, \ + { 1, IEEE80211_RATE_OFDM, 12000, 10, 152, 6 }, \ + { 1, IEEE80211_RATE_OFDM, 18000, 14, 36, 6 }, \ + { 1, IEEE80211_RATE_OFDM, 24000, 9, 176, 8 }, \ + { 1, IEEE80211_RATE_OFDM, 36000, 13, 72, 8 }, \ + { 1, IEEE80211_RATE_OFDM, 48000, 8, 96, 8 }, \ + { 1, IEEE80211_RATE_OFDM, 54000, 12, 108, 8 } } \ +} + +/* + * Crypto definitions + */ + +#define AR5K_KEYCACHE_SIZE 8 + +/***********************\ + HW RELATED DEFINITIONS +\***********************/ + +/* + * Misc definitions + */ +#define AR5K_RSSI_EP_MULTIPLIER (1<<7) + +#define AR5K_ASSERT_ENTRY(_e, _s) do { \ + if (_e >= _s) \ + return (false); \ +} while (0) + + +enum ath5k_ant_setting { + AR5K_ANT_VARIABLE = 0, /* variable by programming */ + AR5K_ANT_FIXED_A = 1, /* fixed to 11a frequencies */ + AR5K_ANT_FIXED_B = 2, /* fixed to 11b frequencies */ + AR5K_ANT_MAX = 3, +}; + +/* + * HAL interrupt abstraction + */ + +/* + * These are mapped to take advantage of some common bits + * between the MAC chips, to be able to set intr properties + * easier. Some of them are not used yet inside OpenHAL. + */ +enum ath5k_int { + AR5K_INT_RX = 0x00000001, + AR5K_INT_RXDESC = 0x00000002, + AR5K_INT_RXNOFRM = 0x00000008, + AR5K_INT_RXEOL = 0x00000010, + AR5K_INT_RXORN = 0x00000020, + AR5K_INT_TX = 0x00000040, + AR5K_INT_TXDESC = 0x00000080, + AR5K_INT_TXURN = 0x00000800, + AR5K_INT_MIB = 0x00001000, + AR5K_INT_RXPHY = 0x00004000, + AR5K_INT_RXKCM = 0x00008000, + AR5K_INT_SWBA = 0x00010000, + AR5K_INT_BMISS = 0x00040000, + AR5K_INT_BNR = 0x00100000, + AR5K_INT_GPIO = 0x01000000, + AR5K_INT_FATAL = 0x40000000, + AR5K_INT_GLOBAL = 0x80000000, + + /*A sum of all the common bits*/ + AR5K_INT_COMMON = AR5K_INT_RXNOFRM + | AR5K_INT_RXDESC + | AR5K_INT_RXEOL + | AR5K_INT_RXORN + | AR5K_INT_TXURN + | AR5K_INT_TXDESC + | AR5K_INT_MIB + | AR5K_INT_RXPHY + | AR5K_INT_RXKCM + | AR5K_INT_SWBA + | AR5K_INT_BMISS + | AR5K_INT_GPIO, + AR5K_INT_NOCARD = 0xffffffff /*Declare that the card + has been removed*/ +}; + +/* + * Power management + */ +enum ath5k_power_mode { + AR5K_PM_UNDEFINED = 0, + AR5K_PM_AUTO, + AR5K_PM_AWAKE, + AR5K_PM_FULL_SLEEP, + AR5K_PM_NETWORK_SLEEP, +}; + +/* + * These match net80211 definitions (not used in + * d80211). + */ +#define AR5K_LED_INIT 0 /*IEEE80211_S_INIT*/ +#define AR5K_LED_SCAN 1 /*IEEE80211_S_SCAN*/ +#define AR5K_LED_AUTH 2 /*IEEE80211_S_AUTH*/ +#define AR5K_LED_ASSOC 3 /*IEEE80211_S_ASSOC*/ +#define AR5K_LED_RUN 4 /*IEEE80211_S_RUN*/ + +/* GPIO-controlled software LED */ +#define AR5K_SOFTLED_PIN 0 +#define AR5K_SOFTLED_ON 0 +#define AR5K_SOFTLED_OFF 1 + +/* + * Chipset capabilities -see ath_hal_getcapability- + * get_capability function is not yet fully implemented + * in OpenHAL so most of these don't work yet... + */ +enum ath5k_capability_type { + AR5K_CAP_REG_DMN = 0, /* Used to get current reg. domain id */ + AR5K_CAP_TKIP_MIC = 2, /* Can handle TKIP MIC in hardware */ + AR5K_CAP_TKIP_SPLIT = 3, /* TKIP uses split keys */ + AR5K_CAP_PHYCOUNTERS = 4, /* PHY error counters */ + AR5K_CAP_DIVERSITY = 5, /* Supports fast diversity */ + AR5K_CAP_NUM_TXQUEUES = 6, /* Used to get max number of hw txqueues */ + AR5K_CAP_VEOL = 7, /* Supports virtual EOL */ + AR5K_CAP_COMPRESSION = 8, /* Supports compression */ + AR5K_CAP_BURST = 9, /* Supports packet bursting */ + AR5K_CAP_FASTFRAME = 10, /* Supports fast frames */ + AR5K_CAP_TXPOW = 11, /* Used to get global tx power limit */ + AR5K_CAP_TPC = 12, /* Can do per-packet tx power control (needed for 802.11a) */ + AR5K_CAP_BSSIDMASK = 13, /* Supports bssid mask */ + AR5K_CAP_MCAST_KEYSRCH = 14, /* Supports multicast key search */ + AR5K_CAP_TSF_ADJUST = 15, /* Supports beacon tsf adjust */ + AR5K_CAP_XR = 16, /* Supports XR mode */ + AR5K_CAP_WME_TKIPMIC = 17, /* Supports TKIP MIC when using WMM */ + AR5K_CAP_CHAN_HALFRATE = 18, /* Supports half rate channels */ + AR5K_CAP_CHAN_QUARTERRATE = 19, /* Supports quarter rate channels */ + AR5K_CAP_RFSILENT = 20, /* Supports RFsilent */ +}; + +struct ath5k_capabilities { + /* + * Supported PHY modes + * (ie. CHANNEL_A, CHANNEL_B, ...) + */ + DECLARE_BITMAP(cap_mode, NUM_IEEE80211_MODES); + + /* + * Frequency range (without regulation restrictions) + */ + struct { + u16 range_2ghz_min; + u16 range_2ghz_max; + u16 range_5ghz_min; + u16 range_5ghz_max; + } cap_range; + + /* + * Active regulation domain settings + */ + struct { + enum ath5k_regdom reg_current; + enum ath5k_regdom reg_hw; + } cap_regdomain; + + /* + * Values stored in the EEPROM (some of them...) + */ + struct ath5k_eeprom_info cap_eeprom; + + /* + * Queue information + */ + struct { + u8 q_tx_num; + } cap_queues; +}; + + +/***************************************\ + HARDWARE ABSTRACTION LAYER STRUCTURE +\***************************************/ + +/* + * Misc defines + */ + +#define AR5K_MAX_GPIO 10 +#define AR5K_MAX_RF_BANKS 8 + +struct ath_hw { + u32 ah_magic; + + void *ah_sc; + void __iomem *ah_sh; + enum ath5k_countrycode ah_country_code; + + enum ath5k_int ah_imr; + + enum ieee80211_if_types ah_op_mode; + enum ath5k_power_mode ah_power_mode; + struct ieee80211_channel ah_current_channel; + bool ah_turbo; + bool ah_calibration; + bool ah_running; + bool ah_single_chip; + enum ath5k_rfgain ah_rf_gain; + + u32 ah_mac_srev; + u16 ah_mac_version; + u16 ah_mac_revision; + u16 ah_phy_revision; + u16 ah_radio_5ghz_revision; + u16 ah_radio_2ghz_revision; + + enum ath5k_version ah_version; + enum ath5k_radio ah_radio; + u32 ah_phy; + + bool ah_5ghz; + bool ah_2ghz; + +#define ah_regdomain ah_capabilities.cap_regdomain.reg_current +#define ah_regdomain_hw ah_capabilities.cap_regdomain.reg_hw +#define ah_modes ah_capabilities.cap_mode +#define ah_ee_version ah_capabilities.cap_eeprom.ee_version + + u32 ah_atim_window; + u32 ah_aifs; + u32 ah_cw_min; + u32 ah_cw_max; + bool ah_software_retry; + u32 ah_limit_tx_retries; + + u32 ah_antenna[AR5K_EEPROM_N_MODES][AR5K_ANT_MAX]; + bool ah_ant_diversity; + + u8 ah_sta_id[ETH_ALEN]; + u8 ah_bssid[ETH_ALEN]; + + u32 ah_gpio[AR5K_MAX_GPIO]; + int ah_gpio_npins; + + struct ath5k_capabilities ah_capabilities; + + struct ath5k_txq_info ah_txq[AR5K_NUM_TX_QUEUES]; + u32 ah_txq_interrupts; + + u32 *ah_rf_banks; + size_t ah_rf_banks_size; + struct ath5k_gain ah_gain; + u32 ah_offset[AR5K_MAX_RF_BANKS]; + + struct { + u16 txp_pcdac[AR5K_EEPROM_POWER_TABLE_SIZE]; + u16 txp_rates[AR5K_MAX_RATES]; + s16 txp_min; + s16 txp_max; + bool txp_tpc; + s16 txp_ofdm; + } ah_txpower; + + struct { + bool r_enabled; + int r_last_alert; + struct ieee80211_channel r_last_channel; + } ah_radar; + + /* + * Function pointers + */ + int (*ah_setup_tx_desc)(struct ath_hw *, struct ath_desc *, + unsigned int, unsigned int, enum ath5k_pkt_type, unsigned int, + unsigned int, unsigned int, unsigned int, unsigned int, + unsigned int, unsigned int, unsigned int); + bool (*ah_setup_xtx_desc)(struct ath_hw *, struct ath_desc *, + unsigned int, unsigned int, unsigned int, unsigned int, + unsigned int, unsigned int); + int (*ah_fill_tx_desc)(struct ath_hw *, struct ath_desc *, + unsigned int, bool, bool); + int (*ah_proc_tx_desc)(struct ath_hw *, struct ath_desc *); + int (*ah_proc_rx_desc)(struct ath_hw *, struct ath_desc *); +}; + +/* + * Prototypes + */ + +/* General Functions */ +extern int ath5k_hw_register_timeout(struct ath_hw *hal, u32 reg, u32 flag, u32 val, bool is_set); +/* Attach/Detach Functions */ +extern struct ath_hw *ath5k_hw_attach(u16 device, u8 mac_version, void *sc, void __iomem *sh); +extern const struct ath5k_rate_table *ath5k_hw_get_rate_table(struct ath_hw *hal, unsigned int mode); +extern void ath5k_hw_detach(struct ath_hw *hal); +/* Reset Functions */ +extern int ath5k_hw_reset(struct ath_hw *hal, enum ieee80211_if_types op_mode, struct ieee80211_channel *channel, bool change_channel); +/* Power management functions */ +extern int ath5k_hw_set_power(struct ath_hw *hal, enum ath5k_power_mode mode, bool set_chip, u16 sleep_duration); +/* DMA Related Functions */ +extern void ath5k_hw_start_rx(struct ath_hw *hal); +extern int ath5k_hw_stop_rx_dma(struct ath_hw *hal); +extern u32 ath5k_hw_get_rx_buf(struct ath_hw *hal); +extern void ath5k_hw_put_rx_buf(struct ath_hw *hal, u32 phys_addr); +extern int ath5k_hw_tx_start(struct ath_hw *hal, unsigned int queue); +extern int ath5k_hw_stop_tx_dma(struct ath_hw *hal, unsigned int queue); +extern u32 ath5k_hw_get_tx_buf(struct ath_hw *hal, unsigned int queue); +extern int ath5k_hw_put_tx_buf(struct ath_hw *hal, unsigned int queue, u32 phys_addr); +extern int ath5k_hw_update_tx_triglevel(struct ath_hw *hal, bool increase); +/* Interrupt handling */ +extern bool ath5k_hw_is_intr_pending(struct ath_hw *hal); +extern int ath5k_hw_get_isr(struct ath_hw *hal, enum ath5k_int *interrupt_mask); +extern enum ath5k_int ath5k_hw_set_intr(struct ath_hw *hal, enum ath5k_int new_mask); +/* EEPROM access functions */ +extern int ath5k_hw_set_regdomain(struct ath_hw *hal, u16 regdomain); +/* Protocol Control Unit Functions */ +extern int ath5k_hw_set_opmode(struct ath_hw *hal); +/* BSSID Functions */ +extern void ath5k_hw_get_lladdr(struct ath_hw *hal, u8 *mac); +extern int ath5k_hw_set_lladdr(struct ath_hw *hal, const u8 *mac); +extern void ath5k_hw_set_associd(struct ath_hw *hal, const u8 *bssid, u16 assoc_id); +extern int ath5k_hw_set_bssid_mask(struct ath_hw *hal, const u8 *mask); +/* Receive start/stop functions */ +extern void ath5k_hw_start_rx_pcu(struct ath_hw *hal); +extern void ath5k_hw_stop_pcu_recv(struct ath_hw *hal); +/* RX Filter functions */ +extern void ath5k_hw_set_mcast_filter(struct ath_hw *hal, u32 filter0, u32 filter1); +extern int ath5k_hw_set_mcast_filterindex(struct ath_hw *hal, u32 index); +extern int ath5k_hw_clear_mcast_filter_idx(struct ath_hw *hal, u32 index); +extern u32 ath5k_hw_get_rx_filter(struct ath_hw *ah); +extern void ath5k_hw_set_rx_filter(struct ath_hw *ah, u32 filter); +/* Beacon related functions */ +extern u32 ath5k_hw_get_tsf32(struct ath_hw *hal); +extern u64 ath5k_hw_get_tsf64(struct ath_hw *hal); +extern void ath5k_hw_reset_tsf(struct ath_hw *hal); +extern void ath5k_hw_init_beacon(struct ath_hw *hal, u32 next_beacon, u32 interval); +extern int ath5k_hw_set_beacon_timers(struct ath_hw *hal, const struct ath5k_beacon_state *state); +extern void ath5k_hw_reset_beacon(struct ath_hw *hal); +extern int ath5k_hw_wait_for_beacon(struct ath_hw *hal, unsigned long phys_addr); +extern void ath5k_hw_update_mib_counters(struct ath_hw *hal, struct ath5k_mib_stats *statistics); +/* ACK/CTS Timeouts */ +extern int ath5k_hw_set_ack_timeout(struct ath_hw *hal, unsigned int timeout); +extern unsigned int ath5k_hw_get_ack_timeout(struct ath_hw *hal); +extern int ath5k_hw_set_cts_timeout(struct ath_hw *hal, unsigned int timeout); +extern unsigned int ath5k_hw_get_cts_timeout(struct ath_hw *hal); +/* Key table (WEP) functions */ +extern int ath5k_hw_reset_key(struct ath_hw *hal, u16 entry); +extern int ath5k_hw_is_key_valid(struct ath_hw *hal, u16 entry); +extern int ath5k_hw_set_key(struct ath_hw *hal, u16 entry, const struct ieee80211_key_conf *key, const u8 *mac); +extern int ath5k_hw_set_key_lladdr(struct ath_hw *hal, u16 entry, const u8 *mac); +/* Queue Control Unit, DFS Control Unit Functions */ +extern int ath5k_hw_setup_tx_queue(struct ath_hw *hal, enum ath5k_tx_queue queue_type, struct ath5k_txq_info *queue_info); +extern int ath5k_hw_setup_tx_queueprops(struct ath_hw *hal, int queue, const struct ath5k_txq_info *queue_info); +extern int ath5k_hw_get_tx_queueprops(struct ath_hw *hal, int queue, struct ath5k_txq_info *queue_info); +extern void ath5k_hw_release_tx_queue(struct ath_hw *hal, unsigned int queue); +extern int ath5k_hw_reset_tx_queue(struct ath_hw *hal, unsigned int queue); +extern u32 ath5k_hw_num_tx_pending(struct ath_hw *hal, unsigned int queue); +extern int ath5k_hw_set_slot_time(struct ath_hw *hal, unsigned int slot_time); +extern unsigned int ath5k_hw_get_slot_time(struct ath_hw *hal); +/* Hardware Descriptor Functions */ +extern int ath5k_hw_setup_rx_desc(struct ath_hw *hal, struct ath_desc *desc, u32 size, unsigned int flags); +/* GPIO Functions */ +extern void ath5k_hw_set_ledstate(struct ath_hw *hal, unsigned int state); +extern int ath5k_hw_set_gpio_output(struct ath_hw *hal, u32 gpio); +extern int ath5k_hw_set_gpio_input(struct ath_hw *hal, u32 gpio); +extern u32 ath5k_hw_get_gpio(struct ath_hw *hal, u32 gpio); +extern int ath5k_hw_set_gpio(struct ath_hw *hal, u32 gpio, u32 val); +extern void ath5k_hw_set_gpio_intr(struct ath_hw *hal, unsigned int gpio, u32 interrupt_level); +/* Regulatory Domain/Channels Setup */ +extern u16 ath5k_get_regdomain(struct ath_hw *hal); +/* Misc functions */ +extern void ath5k_hw_dump_state(struct ath_hw *hal); +extern int ath5k_hw_get_capability(struct ath_hw *hal, enum ath5k_capability_type cap_type, u32 capability, u32 *result); + + +/* Initial register settings functions */ +extern int ath5k_hw_write_initvals(struct ath_hw *hal, u8 mode, bool change_channel); +/* Initialize RF */ +extern int ath5k_hw_rfregs(struct ath_hw *hal, struct ieee80211_channel *channel, unsigned int mode); +extern int ath5k_hw_rfgain(struct ath_hw *hal, unsigned int freq); +extern enum ath5k_rfgain ath5k_hw_get_rf_gain(struct ath_hw *hal); +extern int ath5k_hw_set_rfgain_opt(struct ath_hw *hal); + + +/* PHY/RF channel functions */ +extern bool ath5k_channel_ok(struct ath_hw *hal, u16 freq, unsigned int flags); +extern int ath5k_hw_channel(struct ath_hw *hal, struct ieee80211_channel *channel); +/* PHY calibration */ +extern int ath5k_hw_phy_calibrate(struct ath_hw *hal, struct ieee80211_channel *channel); +extern int ath5k_hw_phy_disable(struct ath_hw *hal); +/* Misc PHY functions */ +extern u16 ath5k_hw_radio_revision(struct ath_hw *hal, unsigned int chan); +extern void ath5k_hw_set_def_antenna(struct ath_hw *hal, unsigned int ant); +extern unsigned int ath5k_hw_get_def_antenna(struct ath_hw *hal); +/* TX power setup */ +extern int ath5k_hw_txpower(struct ath_hw *hal, struct ieee80211_channel *channel, unsigned int txpower); +extern int ath5k_hw_set_txpower_limit(struct ath_hw *hal, unsigned int power); + + +static inline u32 ath5k_hw_reg_read(struct ath_hw *hal, u16 reg) +{ + return ioread32(hal->ah_sh + reg); +} + +static inline void ath5k_hw_reg_write(struct ath_hw *hal, u32 val, u16 reg) +{ + iowrite32(val, hal->ah_sh + reg); +} + +#endif diff -puN /dev/null drivers/net/wireless/ath5k/base.c --- /dev/null +++ a/drivers/net/wireless/ath5k/base.c @@ -0,0 +1,2579 @@ +/*- + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting + * Copyright (c) 2004-2005 Atheros Communications, Inc. + * Copyright (c) 2006 Devicescape Software, Inc. + * Copyright (c) 2007 Jiri Slaby + * Copyright (c) 2007 Luis R. Rodriguez + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + * + */ +#define ATH_PCI_VERSION "0.9.5.0-BSD" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "base.h" +#include "reg.h" + +#define ATH_DEBUG_MODES 0 /* Show found modes in the log? */ +#define ATH_DUMP_SKB 0 /* show skb contents */ +#define AR_DEBUG 1 + +/* unaligned little endian access */ +#define LE_READ_2(_p) (le16_to_cpu(get_unaligned((__le16 *)(_p)))) +#define LE_READ_4(_p) (le32_to_cpu(get_unaligned((__le32 *)(_p)))) + +#if AR_DEBUG +#define DPRINTF(sc, _m, _fmt...) do { \ + if (unlikely(((sc)->debug & (_m)) && net_ratelimit())) \ + printk(KERN_DEBUG _fmt); \ +} while (0) +#else +static inline int __attribute__ ((format (printf, 3, 4))) +DPRINTF(struct ath_softc *sc, unsigned int m, const char *fmt, ...) +{ + return 0; +} +#endif +enum { + ATH_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ + ATH_DEBUG_RESET = 0x00000020, /* reset processing */ + ATH_DEBUG_MODE = 0x00000040, /* mode init/setup */ + ATH_DEBUG_BEACON = 0x00000080, /* beacon handling */ + ATH_DEBUG_INTR = 0x00001000, /* ISR */ + ATH_DEBUG_BEACON_PROC = 0x00008000, /* beacon ISR proc */ + ATH_DEBUG_CALIBRATE = 0x00010000, /* periodic calibration */ + ATH_DEBUG_LED = 0x00100000, /* led management */ + ATH_DEBUG_FATAL = 0x80000000, /* fatal errors */ + ATH_DEBUG_ANY = 0xffffffff +}; + +enum { + ATH_LED_TX, + ATH_LED_RX, +}; + +static int ath_calinterval = ATH_SHORT_CALIB; + +static int countrycode = CTRY_DEFAULT; +static int outdoor = true; +static int xchanmode = true; +module_param(countrycode, int, 0); +MODULE_PARM_DESC(countrycode, "Override default country code"); +module_param(outdoor, int, 0); +MODULE_PARM_DESC(outdoor, "Enable/disable outdoor use"); +module_param(xchanmode, int, 0); +MODULE_PARM_DESC(xchanmode, "Enable/disable extended channel mode"); + +#if AR_DEBUG +static unsigned int ath_debug; +module_param_named(debug, ath_debug, uint, 0); +#endif + +/* + * User a static table of PCI id's for now. While this is the + * "new way" to do things, we may want to switch back to having + * the HAL check them by defining a probe method. + */ +static struct pci_device_id ath_pci_id_table[] __devinitdata = { + { PCI_VDEVICE(ATHEROS, 0x0207), .driver_data = AR5K_AR5210 }, /* 5210 early */ + { PCI_VDEVICE(ATHEROS, 0x0007), .driver_data = AR5K_AR5210 }, /* 5210 */ + { PCI_VDEVICE(ATHEROS, 0x0011), .driver_data = AR5K_AR5211 }, /* 5311 */ + { PCI_VDEVICE(ATHEROS, 0x0012), .driver_data = AR5K_AR5211 }, /* 5211 */ + { PCI_VDEVICE(ATHEROS, 0x0013), .driver_data = AR5K_AR5212 }, /* 5212 */ + { PCI_VDEVICE(3COM_2, 0x0013), .driver_data = AR5K_AR5212 }, /* 3com 5212 */ + { PCI_VDEVICE(3COM, 0x0013), .driver_data = AR5K_AR5212 }, /* 3com 3CRDAG675 5212 */ + { PCI_VDEVICE(ATHEROS, 0x1014), .driver_data = AR5K_AR5212 }, /* IBM minipci 5212 */ + { PCI_VDEVICE(ATHEROS, 0x0014), .driver_data = AR5K_AR5212 }, + { PCI_VDEVICE(ATHEROS, 0x0015), .driver_data = AR5K_AR5212 }, + { PCI_VDEVICE(ATHEROS, 0x0016), .driver_data = AR5K_AR5212 }, + { PCI_VDEVICE(ATHEROS, 0x0017), .driver_data = AR5K_AR5212 }, + { PCI_VDEVICE(ATHEROS, 0x0018), .driver_data = AR5K_AR5212 }, + { PCI_VDEVICE(ATHEROS, 0x0019), .driver_data = AR5K_AR5212 }, + { PCI_VDEVICE(ATHEROS, 0x001a), .driver_data = AR5K_AR5212 }, /* 2413 Griffin-lite */ + { PCI_VDEVICE(ATHEROS, 0x001b), .driver_data = AR5K_AR5212 }, /* 5413 Eagle */ + { PCI_VDEVICE(ATHEROS, 0x001c), .driver_data = AR5K_AR5212 }, /* 5424 Condor (PCI-E)*/ + { 0 } +}; +MODULE_DEVICE_TABLE(pci, ath_pci_id_table); + +static void ath_led_event(struct ath_softc *, int); +static int ath_reset(struct ieee80211_hw *); + +#if AR_DEBUG +static void ath_printrxbuf(struct ath_buf *bf, int done) +{ + struct ath_desc *ds = bf->desc; + + printk(KERN_DEBUG "R (%p %llx) %08x %08x %08x %08x %08x %08x %c\n", + ds, (unsigned long long)bf->daddr, + ds->ds_link, ds->ds_data, ds->ds_ctl0, ds->ds_ctl1, + ds->ds_hw[0], ds->ds_hw[1], + !done ? ' ' : (ds->ds_rxstat.rs_status == 0) ? '*' : '!'); +} + +static void ath_printtxbuf(struct ath_buf *bf, int done) +{ + struct ath_desc *ds = bf->desc; + + printk(KERN_DEBUG "T (%p %llx) %08x %08x %08x %08x %08x %08x %08x " + "%08x %c\n", ds, (unsigned long long)bf->daddr, ds->ds_link, + ds->ds_data, ds->ds_ctl0, ds->ds_ctl1, + ds->ds_hw[0], ds->ds_hw[1], ds->ds_hw[2], ds->ds_hw[3], + !done ? ' ' : (ds->ds_txstat.ts_status == 0) ? '*' : '!'); +} +#endif + +#if ATH_DUMP_SKB +static inline void ath_dump_skb(struct sk_buff *skb, const char *prefix) +{ + print_hex_dump_bytes(prefix, DUMP_PREFIX_NONE, skb->data, + min(200U, skb->len)); +} +#else +static inline void ath_dump_skb(struct sk_buff *skb, const char *prefix) {} +#endif + +static inline void ath_cleanup_txbuf(struct ath_softc *sc, struct ath_buf *bf) +{ + BUG_ON(!bf); + if (!bf->skb) + return; + pci_unmap_single(sc->pdev, bf->skbaddr, bf->skb->len, + PCI_DMA_TODEVICE); + dev_kfree_skb(bf->skb); + bf->skb = NULL; +} + +static void ath_tasklet_reset(unsigned long data) +{ + struct ath_softc *sc = (void *)data; + + ath_reset(sc->hw); +} + +static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) +{ + struct ieee80211_tx_status txs = {}; + struct ath_buf *bf, *bf0; + struct ath_desc *ds; + struct sk_buff *skb; + int ret; + + spin_lock(&txq->lock); + list_for_each_entry_safe(bf, bf0, &txq->q, list) { + ds = bf->desc; + + /* TODO only one segment */ + pci_dma_sync_single_for_cpu(sc->pdev, sc->desc_daddr, + sc->desc_len, PCI_DMA_FROMDEVICE); + ret = sc->ah->ah_proc_tx_desc(sc->ah, ds); + if (unlikely(ret == -EINPROGRESS)) + break; + else if (unlikely(ret)) { + printk(KERN_ERR "ath: error %d while processing " + "queue %u\n", ret, txq->qnum); + break; + } + + skb = bf->skb; + bf->skb = NULL; + pci_unmap_single(sc->pdev, bf->skbaddr, skb->len, + PCI_DMA_TODEVICE); + + txs.control = bf->ctl; + txs.retry_count = ds->ds_txstat.ts_shortretry + + ds->ds_txstat.ts_longretry / 6; + if (unlikely(ds->ds_txstat.ts_status)) { + sc->ll_stats.dot11ACKFailureCount++; + if (ds->ds_txstat.ts_status & AR5K_TXERR_XRETRY) + txs.excessive_retries = 1; + else if (ds->ds_txstat.ts_status & AR5K_TXERR_FILT) + txs.flags |= IEEE80211_TX_STATUS_TX_FILTERED; + } else { + txs.flags |= IEEE80211_TX_STATUS_ACK; + txs.ack_signal = ds->ds_txstat.ts_rssi; + } + + ieee80211_tx_status(sc->hw, skb, &txs); + sc->tx_stats.data[txq->qnum].count++; + + spin_lock(&sc->txbuflock); + sc->tx_stats.data[txq->qnum].len--; + list_move_tail(&bf->list, &sc->txbuf); + sc->txbuf_len++; + spin_unlock(&sc->txbuflock); + } + if (likely(list_empty(&txq->q))) + txq->link = NULL; + spin_unlock(&txq->lock); + if (sc->txbuf_len > ATH_TXBUF / 5) + ieee80211_wake_queues(sc->hw); +} + +static void ath_tasklet_tx(unsigned long data) +{ + struct ath_softc *sc = (void *)data; + + ath_tx_processq(sc, sc->txq); + + ath_led_event(sc, ATH_LED_TX); +} + +static int ath_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf) +{ + struct ath_hw *ah = sc->ah; + struct sk_buff *skb = bf->skb; + struct ath_desc *ds; + + if (likely(skb == NULL)) { + unsigned int off; + + /* + * Allocate buffer with headroom_needed space for the + * fake physical layer header at the start. + */ + skb = dev_alloc_skb(sc->rxbufsize + sc->cachelsz - 1); + if (unlikely(skb == NULL)) { + printk(KERN_ERR "ath: can't alloc skbuff of size %u\n", + sc->rxbufsize + sc->cachelsz - 1); + return -ENOMEM; + } + /* + * Cache-line-align. This is important (for the + * 5210 at least) as not doing so causes bogus data + * in rx'd frames. + */ + off = ((unsigned long)skb->data) % sc->cachelsz; + if (off != 0) + skb_reserve(skb, sc->cachelsz - off); + + bf->skb = skb; + bf->skbaddr = pci_map_single(sc->pdev, + skb->data, sc->rxbufsize, PCI_DMA_FROMDEVICE); + if (unlikely(pci_dma_mapping_error(bf->skbaddr))) { + printk(KERN_ERR "%s: DMA mapping failed\n", __func__); + dev_kfree_skb(skb); + bf->skb = NULL; + return -ENOMEM; + } + } + + /* + * Setup descriptors. For receive we always terminate + * the descriptor list with a self-linked entry so we'll + * not get overrun under high load (as can happen with a + * 5212 when ANI processing enables PHY error frames). + * + * To insure the last descriptor is self-linked we create + * each descriptor as self-linked and add it to the end. As + * each additional descriptor is added the previous self-linked + * entry is ``fixed'' naturally. This should be safe even + * if DMA is happening. When processing RX interrupts we + * never remove/process the last, self-linked, entry on the + * descriptor list. This insures the hardware always has + * someplace to write a new frame. + */ + ds = bf->desc; + ds->ds_link = bf->daddr; /* link to self */ + ds->ds_data = bf->skbaddr; + ath5k_hw_setup_rx_desc(ah, ds, + skb_tailroom(skb), /* buffer size */ + 0); + + if (sc->rxlink != NULL) + *sc->rxlink = bf->daddr; + sc->rxlink = &ds->ds_link; + return 0; +} + +static unsigned int ath_rx_decrypted(struct ath_softc *sc, + struct ath_desc *ds, struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (void *)skb->data; + unsigned int keyix, hlen = ieee80211_get_hdrlen_from_skb(skb); + + if (!(ds->ds_rxstat.rs_status & AR5K_RXERR_DECRYPT) && + ds->ds_rxstat.rs_keyix != AR5K_RXKEYIX_INVALID) + return RX_FLAG_DECRYPTED; + + /* Apparently when a default key is used to decrypt the packet + the hal does not set the index used to decrypt. In such cases + get the index from the packet. */ + if ((le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_PROTECTED) && + !(ds->ds_rxstat.rs_status & AR5K_RXERR_DECRYPT) && + skb->len >= hlen + 4) { + keyix = skb->data[hlen + 3] >> 6; + + if (test_bit(keyix, sc->keymap)) + return RX_FLAG_DECRYPTED; + } + + return 0; +} + +static inline u64 ath_extend_tsf(struct ath_hw *ah, u32 rstamp) +{ + u64 tsf = ath5k_hw_get_tsf64(ah); + + if ((tsf & 0x7fff) < rstamp) + tsf -= 0x8000; + + return (tsf & ~0x7fff) | rstamp; +} + +static void ath_tasklet_rx(unsigned long data) +{ + struct ieee80211_rx_status rxs = {}; + struct sk_buff *skb; + struct ath_softc *sc = (void *)data; + struct ath_buf *bf; + struct ath_desc *ds; + u16 len; + u8 stat; + int ret; + + spin_lock(&sc->rxbuflock); + do { + if (unlikely(list_empty(&sc->rxbuf))) { + if (net_ratelimit()) + printk(KERN_WARNING "ath: empty rx buf pool\n"); + break; + } + bf = list_first_entry(&sc->rxbuf, struct ath_buf, list); + BUG_ON(bf->skb == NULL); + skb = bf->skb; + ds = bf->desc; + + /* TODO only one segment */ + pci_dma_sync_single_for_cpu(sc->pdev, sc->desc_daddr, + sc->desc_len, PCI_DMA_FROMDEVICE); + + if (unlikely(ds->ds_link == bf->daddr)) /* this is the end */ + break; + + ret = sc->ah->ah_proc_rx_desc(sc->ah, ds); + if (unlikely(ret == -EINPROGRESS)) + break; + else if (unlikely(ret)) { + if (net_ratelimit()) + printk(KERN_ERR "ath: error in processing rx " + "descriptor\n"); + return; + } + + if (unlikely(ds->ds_rxstat.rs_more)) { + if (net_ratelimit()) + printk(KERN_INFO "ath: unsupported jumbo\n"); + goto next; + } + + stat = ds->ds_rxstat.rs_status; + if (unlikely(stat)) { + if (stat & AR5K_RXERR_PHY) + goto next; + if (stat & AR5K_RXERR_DECRYPT) { + /* + * Decrypt error. If the error occurred + * because there was no hardware key, then + * let the frame through so the upper layers + * can process it. This is necessary for 5210 + * parts which have no way to setup a ``clear'' + * key cache entry. + * + * XXX do key cache faulting + */ + if (ds->ds_rxstat.rs_keyix == + AR5K_RXKEYIX_INVALID && + !(stat & AR5K_RXERR_CRC)) + goto accept; + } + if (stat & AR5K_RXERR_MIC) { + rxs.flag |= RX_FLAG_MMIC_ERROR; + goto accept; + } + + /* let crypto-error packets fall through in MNTR */ + if ((stat & ~(AR5K_RXERR_DECRYPT|AR5K_RXERR_MIC)) || + sc->opmode != IEEE80211_IF_TYPE_MNTR) + goto next; + } +accept: + len = ds->ds_rxstat.rs_datalen; + pci_dma_sync_single_for_cpu(sc->pdev, bf->skbaddr, len, + PCI_DMA_FROMDEVICE); + pci_unmap_single(sc->pdev, bf->skbaddr, sc->rxbufsize, + PCI_DMA_FROMDEVICE); + bf->skb = NULL; + + if (unlikely((ieee80211_get_hdrlen_from_skb(skb) & 3) && + net_ratelimit())) + printk(KERN_DEBUG "rx len is not %%4: %u\n", + ieee80211_get_hdrlen_from_skb(skb)); + + skb_put(skb, len); + + if (sc->opmode == IEEE80211_IF_TYPE_MNTR) + rxs.mactime = ath_extend_tsf(sc->ah, + ds->ds_rxstat.rs_tstamp); + else + rxs.mactime = ds->ds_rxstat.rs_tstamp; + rxs.freq = sc->curchan->freq; + rxs.channel = sc->curchan->chan; + rxs.phymode = sc->curmode; + rxs.ssi = ds->ds_rxstat.rs_rssi; + rxs.antenna = ds->ds_rxstat.rs_antenna; + rxs.rate = ds->ds_rxstat.rs_rate; + rxs.flag |= ath_rx_decrypted(sc, ds, skb); + + ath_dump_skb(skb, "r"); + + __ieee80211_rx(sc->hw, skb, &rxs); + sc->led_rxrate = ds->ds_rxstat.rs_rate; + ath_led_event(sc, ATH_LED_RX); +next: + list_move_tail(&bf->list, &sc->rxbuf); + } while (ath_rxbuf_init(sc, bf) == 0); + spin_unlock(&sc->rxbuflock); +} + +/* + * Setup the beacon frame for transmit. + */ +static int ath_beacon_setup(struct ath_softc *sc, struct ath_buf *bf, + struct ieee80211_tx_control *ctl) +{ + struct sk_buff *skb = bf->skb; + struct ath_hw *ah = sc->ah; + struct ath_desc *ds; + int ret, antenna = 0; + u32 flags; + + bf->skbaddr = pci_map_single(sc->pdev, skb->data, skb->len, + PCI_DMA_TODEVICE); + DPRINTF(sc, ATH_DEBUG_BEACON, "%s: skb %p [data %p len %u] " + "skbaddr %llx\n", __func__, skb, skb->data, skb->len, + (unsigned long long)bf->skbaddr); + if (pci_dma_mapping_error(bf->skbaddr)) { + printk(KERN_ERR "ath: beacon DMA mapping failed\n"); + return -EIO; + } + + ds = bf->desc; + + flags = AR5K_TXDESC_NOACK; + if (sc->opmode == IEEE80211_IF_TYPE_IBSS && ath5k_hw_hasveol(ah)) { + ds->ds_link = bf->daddr; /* self-linked */ + flags |= AR5K_TXDESC_VEOL; + /* + * Let hardware handle antenna switching if txantenna is not set + */ + } else { + ds->ds_link = 0; + /* + * Switch antenna every 4 beacons if txantenna is not set + * XXX assumes two antennas + */ + if (antenna == 0) + antenna = sc->bsent & 4 ? 2 : 1; + } + + ds->ds_data = bf->skbaddr; + ret = ah->ah_setup_tx_desc(ah, ds, skb->len + FCS_LEN, + ieee80211_get_hdrlen_from_skb(skb), + AR5K_PKT_TYPE_BEACON, 0xffff, ctl->tx_rate, 1, + AR5K_TXKEYIX_INVALID, antenna, flags, 0, 0); + if (ret) + goto err_unmap; + /* NB: beacon's BufLen must be a multiple of 4 bytes */ + ret = ah->ah_fill_tx_desc(ah, ds, roundup(skb->len, 4), true, true); + if (ret) + goto err_unmap; + + return 0; +err_unmap: + pci_unmap_single(sc->pdev, bf->skbaddr, skb->len, PCI_DMA_TODEVICE); + return ret; +} + +/* + * Transmit a beacon frame at SWBA. Dynamic updates to the + * frame contents are done as needed and the slot time is + * also adjusted based on current state. + * + * this is usually called from interrupt context (ath_intr()) + * but also from ath_beacon_config() in IBSS mode which in turn + * can be called from a tasklet and user context + */ +static void ath_beacon_send(struct ath_softc *sc) +{ + struct ath_buf *bf = sc->bbuf; + struct ath_hw *ah = sc->ah; + + DPRINTF(sc, ATH_DEBUG_BEACON_PROC, "%s\n", __func__); + + if (unlikely(bf->skb == NULL || sc->opmode == IEEE80211_IF_TYPE_STA || + sc->opmode == IEEE80211_IF_TYPE_MNTR)) { + printk(KERN_WARNING "ath: bf=%p bf_skb=%p\n", bf, + bf ? bf->skb : NULL); + return; + } + /* + * Check if the previous beacon has gone out. If + * not don't don't try to post another, skip this + * period and wait for the next. Missed beacons + * indicate a problem and should not occur. If we + * miss too many consecutive beacons reset the device. + */ + if (unlikely(ath5k_hw_num_tx_pending(ah, sc->bhalq) != 0)) { + sc->bmisscount++; + DPRINTF(sc, ATH_DEBUG_BEACON_PROC, + "%s: missed %u consecutive beacons\n", + __func__, sc->bmisscount); + if (sc->bmisscount > 3) { /* NB: 3 is a guess */ + DPRINTF(sc, ATH_DEBUG_BEACON_PROC, + "%s: stuck beacon time (%u missed)\n", + __func__, sc->bmisscount); + tasklet_schedule(&sc->restq); + } + return; + } + if (unlikely(sc->bmisscount != 0)) { + DPRINTF(sc, ATH_DEBUG_BEACON_PROC, + "%s: resume beacon xmit after %u misses\n", + __func__, sc->bmisscount); + sc->bmisscount = 0; + } + + /* + * Stop any current dma and put the new frame on the queue. + * This should never fail since we check above that no frames + * are still pending on the queue. + */ + if (unlikely(ath5k_hw_stop_tx_dma(ah, sc->bhalq))) { + printk(KERN_WARNING "ath: beacon queue %u didn't stop?\n", + sc->bhalq); + /* NB: the HAL still stops DMA, so proceed */ + } + pci_dma_sync_single_for_cpu(sc->pdev, bf->skbaddr, bf->skb->len, + PCI_DMA_TODEVICE); + + ath5k_hw_put_tx_buf(ah, sc->bhalq, bf->daddr); + ath5k_hw_tx_start(ah, sc->bhalq); + DPRINTF(sc, ATH_DEBUG_BEACON_PROC, "%s: TXDP[%u] = %llx (%p)\n", + __func__, sc->bhalq, (unsigned long long)bf->daddr, bf->desc); + + sc->bsent++; +} + +static int ath_beaconq_config(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->ah; + struct ath5k_txq_info qi; + int ret; + + ret = ath5k_hw_get_tx_queueprops(ah, sc->bhalq, &qi); + if (ret) + return ret; + if (sc->opmode == IEEE80211_IF_TYPE_AP || + sc->opmode == IEEE80211_IF_TYPE_IBSS) { + /* + * Always burst out beacon and CAB traffic. + */ + qi.tqi_aifs = ATH_BEACON_AIFS_DEFAULT; + qi.tqi_cw_min = ATH_BEACON_CWMIN_DEFAULT; + qi.tqi_cw_max = ATH_BEACON_CWMAX_DEFAULT; + } + + ret = ath5k_hw_setup_tx_queueprops(ah, sc->bhalq, &qi); + if (ret) { + printk(KERN_ERR "%s: unable to update parameters for beacon " + "hardware queue!\n", __func__); + return ret; + } + + return ath5k_hw_reset_tx_queue(ah, sc->bhalq); /* push to h/w */; +} + +/* + * Configure the beacon and sleep timers. + * + * When operating as an AP this resets the TSF and sets + * up the hardware to notify us when we need to issue beacons. + * + * When operating in station mode this sets up the beacon + * timers according to the timestamp of the last received + * beacon and the current TSF, configures PCF and DTIM + * handling, programs the sleep registers so the hardware + * will wakeup in time to receive beacons, and configures + * the beacon miss handling so we'll receive a BMISS + * interrupt when we stop seeing beacons from the AP + * we've associated with. + */ +static void ath_beacon_config(struct ath_softc *sc) +{ +#define TSF_TO_TU(_h, _l) (((_h) << 22) | ((_l) >> 10)) + struct ath_hw *ah = sc->ah; + u32 uninitialized_var(nexttbtt), intval, tsftu; + u64 tsf; + + intval = sc->bintval & AR5K_BEACON_PERIOD; + if (WARN_ON(!intval)) + return; + + /* current TSF converted to TU */ + tsf = ath5k_hw_get_tsf64(ah); + tsftu = TSF_TO_TU((u32)(tsf >> 32), (u32)tsf); + + DPRINTF(sc, ATH_DEBUG_BEACON, "%s: intval %u hw tsftu %u\n", __func__, + intval, tsftu); + + if (sc->opmode == IEEE80211_IF_TYPE_STA || + (sc->opmode == IEEE80211_IF_TYPE_IBSS && + !sc->bbuf->skb)) { + ath5k_hw_set_intr(ah, 0); + sc->imask |= AR5K_INT_BMISS; + sc->bmisscount = 0; + ath5k_hw_set_intr(ah, sc->imask); + } else if (sc->opmode == IEEE80211_IF_TYPE_IBSS /* TODO || AP */) { + ath5k_hw_set_intr(ah, 0); + if (sc->opmode == IEEE80211_IF_TYPE_IBSS) { + /* + * Pull nexttbtt forward to reflect the current + * TSF. Add one intval otherwise the timespan + * can be too short for ibss merges. + */ + nexttbtt = tsftu + 2 * intval; + + DPRINTF(sc, ATH_DEBUG_BEACON, "%s: nexttbtt %u " + "intval %u\n", __func__, nexttbtt, intval); + + /* + * In IBSS mode enable the beacon timers but only + * enable SWBA interrupts if we need to manually + * prepare beacon frames. Otherwise we use a + * self-linked tx descriptor and let the hardware + * deal with things. + */ + if (!ath5k_hw_hasveol(ah)) + sc->imask |= AR5K_INT_SWBA; + } /* TODO else AP */ + + intval |= AR5K_BEACON_ENA; + + ath_beaconq_config(sc); + ath5k_hw_init_beacon(ah, nexttbtt, intval); + + sc->bmisscount = 0; + ath5k_hw_set_intr(ah, sc->imask); + /* + * When using a self-linked beacon descriptor in + * ibss mode load it once here. + */ + if (sc->opmode == IEEE80211_IF_TYPE_IBSS && + ath5k_hw_hasveol(ah)) + ath_beacon_send(sc); + } +#undef TSF_TO_TU +} + +static void ath_mode_init(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->ah; + u32 rfilt; + + /* configure rx filter */ + rfilt = sc->filter_flags; + ath5k_hw_set_rx_filter(ah, rfilt); + + if (ath5k_hw_hasbssidmask(ah)) + ath5k_hw_set_bssid_mask(ah, sc->bssidmask); + + /* configure operational mode */ + ath5k_hw_set_opmode(ah); + + ath5k_hw_set_mcast_filter(ah, 0, 0); + DPRINTF(sc, ATH_DEBUG_MODE, "%s: RX filter 0x%x\n", __func__, rfilt); +} + +/* + * Enable the receive h/w following a reset. + */ +static int ath_startrecv(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->ah; + struct ath_buf *bf; + int ret; + + sc->rxbufsize = roundup(IEEE80211_MAX_LEN, sc->cachelsz); + + DPRINTF(sc, ATH_DEBUG_RESET, "%s: cachelsz %u rxbufsize %u\n", + __func__, sc->cachelsz, sc->rxbufsize); + + sc->rxlink = NULL; + + spin_lock_bh(&sc->rxbuflock); + list_for_each_entry(bf, &sc->rxbuf, list) { + ret = ath_rxbuf_init(sc, bf); + if (ret != 0) { + spin_unlock_bh(&sc->rxbuflock); + goto err; + } + } + bf = list_first_entry(&sc->rxbuf, struct ath_buf, list); + spin_unlock_bh(&sc->rxbuflock); + + ath5k_hw_put_rx_buf(ah, bf->daddr); + ath5k_hw_start_rx(ah); /* enable recv descriptors */ + ath_mode_init(sc); /* set filters, etc. */ + ath5k_hw_start_rx_pcu(ah); /* re-enable PCU/DMA engine */ + + return 0; +err: + return ret; +} + +static inline void ath_update_txpow(struct ath_softc *sc) +{ + ath5k_hw_set_txpower_limit(sc->ah, 0); +} + +static int ath_stop_locked(struct ath_softc *); + +static int ath_init(struct ath_softc *sc) +{ + int ret; + + mutex_lock(&sc->lock); + + DPRINTF(sc, ATH_DEBUG_RESET, "%s: mode %d\n", __func__, sc->opmode); + + /* + * Stop anything previously setup. This is safe + * no matter this is the first time through or not. + */ + ath_stop_locked(sc); + + /* + * The basic interface to setting the hardware in a good + * state is ``reset''. On return the hardware is known to + * be powered up and with interrupts disabled. This must + * be followed by initialization of the appropriate bits + * and then setup of the interrupt mask. + */ + sc->curchan = sc->hw->conf.chan; + ret = ath5k_hw_reset(sc->ah, sc->opmode, sc->curchan, false); + if (ret) { + printk(KERN_ERR "unable to reset hardware: %d\n", ret); + goto done; + } + /* + * This is needed only to setup initial state + * but it's best done after a reset. + */ + ath_update_txpow(sc); + + /* + * Setup the hardware after reset: the key cache + * is filled as needed and the receive engine is + * set going. Frame transmit is handled entirely + * in the frame output path; there's nothing to do + * here except setup the interrupt mask. + */ + ret = ath_startrecv(sc); + if (ret) + goto done; + + /* + * Enable interrupts. + */ + sc->imask = AR5K_INT_RX | AR5K_INT_TX | AR5K_INT_RXEOL | + AR5K_INT_RXORN | AR5K_INT_FATAL | AR5K_INT_GLOBAL; + + ath5k_hw_set_intr(sc->ah, sc->imask); + + mod_timer(&sc->calib_tim, round_jiffies(jiffies + + msecs_to_jiffies(ath_calinterval * 1000))); + + ret = 0; +done: + mutex_unlock(&sc->lock); + return ret; +} + +/* + * Disable the receive h/w in preparation for a reset. + */ +static void ath_stoprecv(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->ah; + + ath5k_hw_stop_pcu_recv(ah); /* disable PCU */ + ath5k_hw_set_rx_filter(ah, 0); /* clear recv filter */ + ath5k_hw_stop_rx_dma(ah); /* disable DMA engine */ + mdelay(3); /* 3ms is long enough for 1 frame */ +#if AR_DEBUG + if (unlikely(sc->debug & (ATH_DEBUG_RESET | ATH_DEBUG_FATAL))) { + struct ath_desc *ds; + struct ath_buf *bf; + int status; + + printk(KERN_DEBUG "%s: rx queue %x, link %p\n", __func__, + ath5k_hw_get_rx_buf(ah), sc->rxlink); + + spin_lock_bh(&sc->rxbuflock); + list_for_each_entry(bf, &sc->rxbuf, list) { + ds = bf->desc; + status = ah->ah_proc_rx_desc(ah, ds); + if (!status || (sc->debug & ATH_DEBUG_FATAL)) + ath_printrxbuf(bf, status == 0); + } + spin_unlock_bh(&sc->rxbuflock); + } +#endif + sc->rxlink = NULL; /* just in case */ +} + +static void ath_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq) +{ + struct ath_buf *bf, *bf0; + + /* + * NB: this assumes output has been stopped and + * we do not need to block ath_tx_tasklet + */ + spin_lock_bh(&txq->lock); + list_for_each_entry_safe(bf, bf0, &txq->q, list) { +#if AR_DEBUG + if (sc->debug & ATH_DEBUG_RESET) + ath_printtxbuf(bf, !sc->ah->ah_proc_tx_desc(sc->ah, + bf->desc)); +#endif + ath_cleanup_txbuf(sc, bf); + + spin_lock_bh(&sc->txbuflock); + sc->tx_stats.data[txq->qnum].len--; + list_move_tail(&bf->list, &sc->txbuf); + sc->txbuf_len++; + spin_unlock_bh(&sc->txbuflock); + } + txq->link = NULL; + spin_unlock_bh(&txq->lock); +} + +/* + * Drain the transmit queues and reclaim resources. + */ +static void ath_draintxq(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->ah; + int i; + + /* XXX return value */ + if (likely(!test_bit(ATH_STAT_INVALID, sc->status))) { + /* don't touch the hardware if marked invalid */ + (void)ath5k_hw_stop_tx_dma(ah, sc->bhalq); + DPRINTF(sc, ATH_DEBUG_RESET, "%s: beacon queue %x\n", __func__, + ath5k_hw_get_tx_buf(ah, sc->bhalq)); + for (i = 0; i < ARRAY_SIZE(sc->txqs); i++) + if (sc->txqs[i].setup) { + ath5k_hw_stop_tx_dma(ah, sc->txqs[i].qnum); + DPRINTF(sc, ATH_DEBUG_RESET, "%s: txq [%u] %x, " + "link %p\n", __func__, + sc->txqs[i].qnum, + ath5k_hw_get_tx_buf(ah, + sc->txqs[i].qnum), + sc->txqs[i].link); + } + } + ieee80211_start_queues(sc->hw); /* XXX move to callers */ + + for (i = 0; i < ARRAY_SIZE(sc->txqs); i++) + if (sc->txqs[i].setup) + ath_tx_draintxq(sc, &sc->txqs[i]); +} + +static int ath_stop_locked(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->ah; + + DPRINTF(sc, ATH_DEBUG_RESET, "%s: invalid %u\n", __func__, + test_bit(ATH_STAT_INVALID, sc->status)); + + /* + * Shutdown the hardware and driver: + * stop output from above + * disable interrupts + * turn off timers + * turn off the radio + * clear transmit machinery + * clear receive machinery + * drain and release tx queues + * reclaim beacon resources + * power down hardware + * + * Note that some of this work is not possible if the + * hardware is gone (invalid). + */ + ieee80211_stop_queues(sc->hw); + + if (!test_bit(ATH_STAT_INVALID, sc->status)) { + if (test_bit(ATH_STAT_LEDSOFT, sc->status)) { + del_timer_sync(&sc->led_tim); + ath5k_hw_set_gpio(ah, sc->led_pin, !sc->led_on); + __clear_bit(ATH_STAT_LEDBLINKING, sc->status); + } + ath5k_hw_set_intr(ah, 0); + } + ath_draintxq(sc); + if (!test_bit(ATH_STAT_INVALID, sc->status)) { + ath_stoprecv(sc); + ath5k_hw_phy_disable(ah); + } else + sc->rxlink = NULL; + + return 0; +} + +/* + * Stop the device, grabbing the top-level lock to protect + * against concurrent entry through ath_init (which can happen + * if another thread does a system call and the thread doing the + * stop is preempted). + */ +static int ath_stop_hw(struct ath_softc *sc) +{ + int ret; + + mutex_lock(&sc->lock); + ret = ath_stop_locked(sc); + if (ret == 0 && !test_bit(ATH_STAT_INVALID, sc->status)) { + /* + * Set the chip in full sleep mode. Note that we are + * careful to do this only when bringing the interface + * completely to a stop. When the chip is in this state + * it must be carefully woken up or references to + * registers in the PCI clock domain may freeze the bus + * (and system). This varies by chip and is mostly an + * issue with newer parts that go to sleep more quickly. + */ + if (sc->ah->ah_mac_version >= 7 && + sc->ah->ah_mac_revision >= 8) { + /* + * XXX + * don't put newer MAC revisions > 7.8 to sleep because + * of the above mentioned problems + */ + DPRINTF(sc, ATH_DEBUG_RESET, "%s: mac version > 7.8, " + "not putting device to sleep\n", __func__); + } else { + DPRINTF(sc, ATH_DEBUG_RESET, + "%s: putting device to full sleep\n", __func__); + ath5k_hw_set_power(sc->ah, AR5K_PM_FULL_SLEEP, true, 0); + } + } + ath_cleanup_txbuf(sc, sc->bbuf); + mutex_unlock(&sc->lock); + + del_timer_sync(&sc->calib_tim); + + return ret; +} + +static void ath_setcurmode(struct ath_softc *sc, unsigned int mode) +{ + if (unlikely(test_bit(ATH_STAT_LEDSOFT, sc->status))) { + /* from Atheros NDIS driver, w/ permission */ + static const struct { + u16 rate; /* tx/rx 802.11 rate */ + u16 timeOn; /* LED on time (ms) */ + u16 timeOff; /* LED off time (ms) */ + } blinkrates[] = { + { 108, 40, 10 }, + { 96, 44, 11 }, + { 72, 50, 13 }, + { 48, 57, 14 }, + { 36, 67, 16 }, + { 24, 80, 20 }, + { 22, 100, 25 }, + { 18, 133, 34 }, + { 12, 160, 40 }, + { 10, 200, 50 }, + { 6, 240, 58 }, + { 4, 267, 66 }, + { 2, 400, 100 }, + { 0, 500, 130 } + }; + const struct ath5k_rate_table *rt = + ath5k_hw_get_rate_table(sc->ah, mode); + unsigned int i, j; + + BUG_ON(rt == NULL); + + memset(sc->hwmap, 0, sizeof(sc->hwmap)); + for (i = 0; i < 32; i++) { + u8 ix = rt->rate_code_to_index[i]; + if (ix == 0xff) { + sc->hwmap[i].ledon = msecs_to_jiffies(500); + sc->hwmap[i].ledoff = msecs_to_jiffies(130); + continue; + } + sc->hwmap[i].txflags = IEEE80211_RADIOTAP_F_DATAPAD; + if (SHPREAMBLE_FLAG(ix) || rt->rates[ix].modulation == + IEEE80211_RATE_OFDM) + sc->hwmap[i].txflags |= + IEEE80211_RADIOTAP_F_SHORTPRE; + /* receive frames include FCS */ + sc->hwmap[i].rxflags = sc->hwmap[i].txflags | + IEEE80211_RADIOTAP_F_FCS; + /* setup blink rate table to avoid per-packet lookup */ + for (j = 0; j < ARRAY_SIZE(blinkrates) - 1; j++) + if (blinkrates[j].rate == /* XXX why 7f? */ + (rt->rates[ix].dot11_rate&0x7f)) + break; + + sc->hwmap[i].ledon = msecs_to_jiffies(blinkrates[j]. + timeOn); + sc->hwmap[i].ledoff = msecs_to_jiffies(blinkrates[j]. + timeOff); + } + } + + sc->curmode = mode; +} + +/* + * Set/change channels. If the channel is really being changed, + * it's done by reseting the chip. To accomplish this we must + * first cleanup any pending DMA, then restart stuff after a la + * ath_init. + */ +static int ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan) +{ + struct ath_hw *ah = sc->ah; + int ret; + + DPRINTF(sc, ATH_DEBUG_RESET, "%s: %u (%u MHz) -> %u (%u MHz)\n", + __func__, sc->curchan->chan, sc->curchan->freq, + chan->chan, chan->freq); + + if (chan->freq != sc->curchan->freq || chan->val != sc->curchan->val) { + /* + * To switch channels clear any pending DMA operations; + * wait long enough for the RX fifo to drain, reset the + * hardware at the new frequency, and then re-enable + * the relevant bits of the h/w. + */ + ath5k_hw_set_intr(ah, 0); /* disable interrupts */ + ath_draintxq(sc); /* clear pending tx frames */ + ath_stoprecv(sc); /* turn off frame recv */ + ret = ath5k_hw_reset(ah, sc->opmode, chan, true); + if (ret) { + printk(KERN_ERR "%s: unable to reset channel %u " + "(%u Mhz)\n", __func__, chan->chan, chan->freq); + return ret; + } + sc->curchan = chan; + ath_update_txpow(sc); + + /* + * Re-enable rx framework. + */ + ret = ath_startrecv(sc); + if (ret) { + printk(KERN_ERR "%s: unable to restart recv logic\n", + __func__); + return ret; + } + + /* + * Change channels and update the h/w rate map + * if we're switching; e.g. 11a to 11b/g. + * + * XXX needed? + */ +/* ath_chan_change(sc, chan); */ + + /* + * Re-enable interrupts. + */ + ath5k_hw_set_intr(ah, sc->imask); + } + + return 0; +} + +static int ath_tx_bf(struct ath_softc *sc, struct ath_buf *bf, + struct ieee80211_tx_control *ctl) +{ + struct ath_hw *ah = sc->ah; + struct ath_txq *txq = sc->txq; + struct ath_desc *ds = bf->desc; + struct sk_buff *skb = bf->skb; + unsigned int hdrpad, pktlen, flags, keyidx = AR5K_TXKEYIX_INVALID; + int ret; + + flags = AR5K_TXDESC_INTREQ | AR5K_TXDESC_CLRDMASK; + bf->ctl = *ctl; + /* XXX endianness */ + bf->skbaddr = pci_map_single(sc->pdev, skb->data, skb->len, + PCI_DMA_TODEVICE); + + if (ctl->flags & IEEE80211_TXCTL_NO_ACK) + flags |= AR5K_TXDESC_NOACK; + + if ((ieee80211_get_hdrlen_from_skb(skb) & 3) && net_ratelimit()) + printk(KERN_DEBUG "tx len is not %%4: %u\n", + ieee80211_get_hdrlen_from_skb(skb)); + + hdrpad = 0; + pktlen = skb->len - hdrpad + FCS_LEN; + + if (!(ctl->flags & IEEE80211_TXCTL_DO_NOT_ENCRYPT)) { + keyidx = ctl->key_idx; + pktlen += ctl->icv_len; + } + + ret = ah->ah_setup_tx_desc(ah, ds, pktlen, + ieee80211_get_hdrlen_from_skb(skb), AR5K_PKT_TYPE_NORMAL, + 0xffff, ctl->tx_rate, ctl->retry_limit, keyidx, 0, flags, 0, 0); + if (ret) + goto err_unmap; + + ds->ds_link = 0; + ds->ds_data = bf->skbaddr; + + ret = ah->ah_fill_tx_desc(ah, ds, skb->len, true, true); + if (ret) + goto err_unmap; + + spin_lock_bh(&txq->lock); + list_add_tail(&bf->list, &txq->q); + sc->tx_stats.data[txq->qnum].len++; + if (txq->link == NULL) /* is this first packet? */ + ath5k_hw_put_tx_buf(ah, txq->qnum, bf->daddr); + else /* no, so only link it */ + *txq->link = bf->daddr; + + txq->link = &ds->ds_link; + ath5k_hw_tx_start(ah, txq->qnum); + spin_unlock_bh(&txq->lock); + + return 0; +err_unmap: + pci_unmap_single(sc->pdev, bf->skbaddr, skb->len, PCI_DMA_TODEVICE); + return ret; +} + +static int ath_tx(struct ieee80211_hw *hw, struct sk_buff *skb, + struct ieee80211_tx_control *ctl) +{ + struct ath_softc *sc = hw->priv; + struct ath_buf *bf; + unsigned long flags; + + ath_dump_skb(skb, "t"); + + if (sc->opmode == IEEE80211_IF_TYPE_MNTR) + DPRINTF(sc, ATH_DEBUG_XMIT, "tx in monitor (scan?)\n"); + + sc->led_txrate = ctl->tx_rate; + + spin_lock_irqsave(&sc->txbuflock, flags); + if (list_empty(&sc->txbuf)) { + if (net_ratelimit()) + printk(KERN_ERR "ath: no further txbuf available, " + "dropping packet\n"); + spin_unlock_irqrestore(&sc->txbuflock, flags); + ieee80211_stop_queue(hw, ctl->queue); + return -1; + } + bf = list_first_entry(&sc->txbuf, struct ath_buf, list); + list_del(&bf->list); + sc->txbuf_len--; + if (list_empty(&sc->txbuf)) + ieee80211_stop_queues(hw); + spin_unlock_irqrestore(&sc->txbuflock, flags); + + bf->skb = skb; + + if (ath_tx_bf(sc, bf, ctl)) { + bf->skb = NULL; + spin_lock_irqsave(&sc->txbuflock, flags); + list_add_tail(&bf->list, &sc->txbuf); + sc->txbuf_len++; + spin_unlock_irqrestore(&sc->txbuflock, flags); + dev_kfree_skb_any(skb); + return 0; + } + + return 0; +} + +static int ath_reset(struct ieee80211_hw *hw) +{ + struct ath_softc *sc = hw->priv; + struct ath_hw *ah = sc->ah; + int ret; + + DPRINTF(sc, ATH_DEBUG_RESET, "resetting\n"); + /* + * Convert to a HAL channel description with the flags + * constrained to reflect the current operating mode. + */ + sc->curchan = hw->conf.chan; + + ath5k_hw_set_intr(ah, 0); + ath_draintxq(sc); + ath_stoprecv(sc); + + ret = ath5k_hw_reset(ah, sc->opmode, sc->curchan, true); + if (unlikely(ret)) { + printk(KERN_ERR "ath: can't reset hardware (%d)\n", ret); + goto err; + } + ath_update_txpow(sc); + + ret = ath_startrecv(sc); + if (unlikely(ret)) { + printk(KERN_ERR "ath: can't start recv logic\n"); + goto err; + } + /* + * We may be doing a reset in response to an ioctl + * that changes the channel so update any state that + * might change as a result. + * + * XXX needed? + */ +/* ath_chan_change(sc, c); */ + ath_beacon_config(sc); + /* intrs are started by ath_beacon_config */ + + ieee80211_wake_queues(hw); + + return 0; +err: + return ret; +} + +static int ath_start(struct ieee80211_hw *hw) +{ + return ath_init(hw->priv); +} + +void ath_stop(struct ieee80211_hw *hw) +{ + ath_stop_hw(hw->priv); +} + +static int ath_add_interface(struct ieee80211_hw *hw, + struct ieee80211_if_init_conf *conf) +{ + struct ath_softc *sc = hw->priv; + int ret; + + mutex_lock(&sc->lock); + if (sc->iface_id) { + ret = 0; + goto end; + } + + sc->iface_id = conf->if_id; + + switch (conf->type) { + case IEEE80211_IF_TYPE_STA: + case IEEE80211_IF_TYPE_IBSS: + case IEEE80211_IF_TYPE_MNTR: + sc->opmode = conf->type; + break; + default: + ret = -EOPNOTSUPP; + goto end; + } + ret = 0; +end: + mutex_unlock(&sc->lock); + return ret; +} + +static void ath_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_if_init_conf *conf) +{ + struct ath_softc *sc = hw->priv; + + mutex_lock(&sc->lock); + if (sc->iface_id != conf->if_id) + goto end; + + sc->iface_id = 0; +end: + mutex_unlock(&sc->lock); +} + +static int ath_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf) +{ + struct ath_softc *sc = hw->priv; + + sc->bintval = conf->beacon_int * 1000 / 1024; + ath_setcurmode(sc, conf->phymode); + + return ath_chan_set(sc, conf->chan); +} + +static int ath_config_interface(struct ieee80211_hw *hw, int if_id, + struct ieee80211_if_conf *conf) +{ + struct ath_softc *sc = hw->priv; + int ret; + + /* Set to a reasonable value. Note that this will + * be set to mac80211's value at ath_config(). */ + sc->bintval = 1000 * 1000 / 1024; + mutex_lock(&sc->lock); + if (sc->iface_id != if_id) { + ret = -EIO; + goto unlock; + } + if (conf->bssid) + ath5k_hw_set_associd(sc->ah, conf->bssid, 0 /* FIXME: aid */); + mutex_unlock(&sc->lock); + + return ath_reset(hw); +unlock: + mutex_unlock(&sc->lock); + return ret; +} + +#define SUPPORTED_FIF_FLAGS \ + FIF_PROMISC_IN_BSS | FIF_ALLMULTI | FIF_FCSFAIL | \ + FIF_PLCPFAIL | FIF_CONTROL | FIF_OTHER_BSS | \ + FIF_BCN_PRBRESP_PROMISC +/* + * o always accept unicast, broadcast, and multicast traffic + * o maintain current state of phy error reception (the hal + * may enable phy error frames for noise immunity work) + * o probe request frames are accepted only when operating in + * hostap, adhoc, or monitor modes + * o enable promiscuous mode according to the interface state + * o accept beacons: + * - when operating in adhoc mode so the 802.11 layer creates + * node table entries for peers, + * - when operating in station mode for collecting rssi data when + * the station is otherwise quiet, or + * - when scanning + */ +static void ath_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *new_flags, + int mc_count, struct dev_mc_list *mclist) +{ + struct ath_softc *sc = hw->priv; + struct ath_hw *ah = sc->ah; + u32 rfilt; + + /* Only deal with supported flags */ + changed_flags &= SUPPORTED_FIF_FLAGS; + *new_flags &= SUPPORTED_FIF_FLAGS; + + /* XXX: Start by enabling broadcasts and Unicast, move this later + * to mac802111 and add a flag for these */ + rfilt = AR5K_RX_FILTER_UCAST | AR5K_RX_FILTER_BCAST; + + if (changed_flags & (FIF_PROMISC_IN_BSS | FIF_OTHER_BSS)) { + if (*new_flags & FIF_PROMISC_IN_BSS) { + rfilt |= AR5K_RX_FILTER_PROM; + __set_bit(ATH_STAT_PROMISC, sc->status); + } + else + __clear_bit(ATH_STAT_PROMISC, sc->status); + } + + if (*new_flags & FIF_ALLMULTI) + rfilt |= AR5K_RX_FILTER_MCAST; + /* This is the best we can do */ + if (*new_flags & (FIF_FCSFAIL | FIF_PLCPFAIL)) + rfilt |= AR5K_RX_FILTER_PHYERR; + /* FIF_BCN_PRBRESP_PROMISC really means to enable beacons + * and probes for any BSSID, this needs testing */ + if (*new_flags & FIF_BCN_PRBRESP_PROMISC) + rfilt |= AR5K_RX_FILTER_BEACON | AR5K_RX_FILTER_PROBEREQ; + /* FIF_CONTROL doc says that FIF_PROMISC_IN_BSS is not set we should + * only pass on control frames for this station. This needs testing. + * I believe right now this enables *all* control frames */ + if (*new_flags & FIF_CONTROL) + rfilt |= AR5K_RX_FILTER_CONTROL; + + /* Additional settings per mode -- this is per ath5k */ + + /* XXX move these to mac80211, and add a beacon IFF flag to mac80211 */ + + if (sc->opmode == IEEE80211_IF_TYPE_MNTR) + rfilt |= AR5K_RX_FILTER_CONTROL | AR5K_RX_FILTER_BEACON | + AR5K_RX_FILTER_PROBEREQ | AR5K_RX_FILTER_PROM; + if (sc->opmode != IEEE80211_IF_TYPE_STA) + rfilt |= AR5K_RX_FILTER_PROBEREQ; + if (sc->opmode != IEEE80211_IF_TYPE_AP && + test_bit(ATH_STAT_PROMISC, sc->status)) + rfilt |= AR5K_RX_FILTER_PROM; + if (sc->opmode == IEEE80211_IF_TYPE_STA || + sc->opmode == IEEE80211_IF_TYPE_IBSS) { + rfilt |= AR5K_RX_FILTER_BEACON; + /* Note: AR5212 requires AR5K_RX_FILTER_PROM to receive broadcasts, + * perhaps the flags are off, for now to be safe we'll enable it for + * STA and ADHOC until we have this properly mapped */ + if (ah->ah_version == AR5K_AR5212) + rfilt |= AR5K_RX_FILTER_PROM; + } + + /* Set the cached hw filter flags, this will alter actually + * be set in HW */ + sc->filter_flags = rfilt; +} + +static int ath_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + const u8 *local_addr, const u8 *addr, + struct ieee80211_key_conf *key) +{ + struct ath_softc *sc = hw->priv; + int ret = 0; + + switch(key->alg) { + case ALG_WEP: + break; + case ALG_TKIP: + case ALG_CCMP: + return -EOPNOTSUPP; + default: + WARN_ON(1); + return -EINVAL; + } + + mutex_lock(&sc->lock); + + switch (cmd) { + case SET_KEY: + ret = ath5k_hw_set_key(sc->ah, key->keyidx, key, addr); + if (ret) { + printk(KERN_ERR "ath: can't set the key\n"); + goto unlock; + } + __set_bit(key->keyidx, sc->keymap); + key->hw_key_idx = key->keyidx; + break; + case DISABLE_KEY: + ath5k_hw_reset_key(sc->ah, key->keyidx); + __clear_bit(key->keyidx, sc->keymap); + break; + default: + ret = -EINVAL; + goto unlock; + } + +unlock: + mutex_unlock(&sc->lock); + return ret; +} + +static int ath_get_stats(struct ieee80211_hw *hw, + struct ieee80211_low_level_stats *stats) +{ + struct ath_softc *sc = hw->priv; + + memcpy(stats, &sc->ll_stats, sizeof(sc->ll_stats)); + + return 0; +} + +static int ath_get_tx_stats(struct ieee80211_hw *hw, + struct ieee80211_tx_queue_stats *stats) +{ + struct ath_softc *sc = hw->priv; + + memcpy(stats, &sc->tx_stats, sizeof(sc->tx_stats)); + + return 0; +} + +static u64 ath_get_tsf(struct ieee80211_hw *hw) +{ + struct ath_softc *sc = hw->priv; + + return ath5k_hw_get_tsf64(sc->ah); +} + +static void ath_reset_tsf(struct ieee80211_hw *hw) +{ + struct ath_softc *sc = hw->priv; + + ath5k_hw_reset_tsf(sc->ah); +} + +static int ath_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb, + struct ieee80211_tx_control *ctl) +{ + struct ath_softc *sc = hw->priv; + int ret; + + ath_dump_skb(skb, "b"); + + mutex_lock(&sc->lock); + + if (sc->opmode != IEEE80211_IF_TYPE_IBSS) { + ret = -EIO; + goto end; + } + + ath_cleanup_txbuf(sc, sc->bbuf); + sc->bbuf->skb = skb; + ret = ath_beacon_setup(sc, sc->bbuf, ctl); + if (ret) + sc->bbuf->skb = NULL; + +end: + mutex_unlock(&sc->lock); + return ret; +} + +static struct ieee80211_ops ath_hw_ops = { + .tx = ath_tx, + .start = ath_start, + .stop = ath_stop, + .add_interface = ath_add_interface, + .remove_interface = ath_remove_interface, + .config = ath_config, + .config_interface = ath_config_interface, + .configure_filter = ath_configure_filter, + .set_key = ath_set_key, + .get_stats = ath_get_stats, + .conf_tx = NULL, + .get_tx_stats = ath_get_tx_stats, + .get_tsf = ath_get_tsf, + .reset_tsf = ath_reset_tsf, + .beacon_update = ath_beacon_update, +}; + +/* + * Periodically recalibrate the PHY to account + * for temperature/environment changes. + */ +static void ath_calibrate(unsigned long data) +{ + struct ath_softc *sc = (void *)data; + struct ath_hw *ah = sc->ah; + + DPRINTF(sc, ATH_DEBUG_CALIBRATE, "ath: channel %u/%x\n", + sc->curchan->chan, sc->curchan->val); + + if (ath5k_hw_get_rf_gain(ah) == AR5K_RFGAIN_NEED_CHANGE) { + /* + * Rfgain is out of bounds, reset the chip + * to load new gain values. + */ + DPRINTF(sc, ATH_DEBUG_RESET, "calibration, resetting\n"); + ath_reset(sc->hw); + } + if (ath5k_hw_phy_calibrate(ah, sc->curchan)) + printk(KERN_ERR "ath: calibration of channel %u failed\n", + sc->curchan->chan); + + mod_timer(&sc->calib_tim, round_jiffies(jiffies + + msecs_to_jiffies(ath_calinterval * 1000))); +} + +static void ath_led_off(unsigned long data) +{ + struct ath_softc *sc = (void *)data; + + if (test_bit(ATH_STAT_LEDENDBLINK, sc->status)) + __clear_bit(ATH_STAT_LEDBLINKING, sc->status); + else { + __set_bit(ATH_STAT_LEDENDBLINK, sc->status); + ath5k_hw_set_gpio(sc->ah, sc->led_pin, !sc->led_on); + mod_timer(&sc->led_tim, jiffies + sc->led_off); + } +} + +/* + * Blink the LED according to the specified on/off times. + */ +static void ath_led_blink(struct ath_softc *sc, unsigned int on, + unsigned int off) +{ + DPRINTF(sc, ATH_DEBUG_LED, "%s: on %u off %u\n", __func__, on, off); + ath5k_hw_set_gpio(sc->ah, sc->led_pin, sc->led_on); + __set_bit(ATH_STAT_LEDBLINKING, sc->status); + __clear_bit(ATH_STAT_LEDENDBLINK, sc->status); + sc->led_off = off; + mod_timer(&sc->led_tim, jiffies + on); +} + +static void ath_led_event(struct ath_softc *sc, int event) +{ + if (likely(!test_bit(ATH_STAT_LEDSOFT, sc->status))) + return; + if (unlikely(test_bit(ATH_STAT_LEDBLINKING, sc->status))) + return; /* don't interrupt active blink */ + switch (event) { + case ATH_LED_TX: + ath_led_blink(sc, sc->hwmap[sc->led_txrate].ledon, + sc->hwmap[sc->led_txrate].ledoff); + break; + case ATH_LED_RX: + ath_led_blink(sc, sc->hwmap[sc->led_rxrate].ledon, + sc->hwmap[sc->led_rxrate].ledoff); + break; + } +} + +static irqreturn_t ath_intr(int irq, void *dev_id) +{ + struct ath_softc *sc = dev_id; + struct ath_hw *ah = sc->ah; + enum ath5k_int status; + unsigned int counter = 1000; + + if (unlikely(test_bit(ATH_STAT_INVALID, sc->status) || + !ath5k_hw_is_intr_pending(ah))) + return IRQ_NONE; + + do { + /* + * Figure out the reason(s) for the interrupt. Note + * that the hal returns a pseudo-ISR that may include + * bits we haven't explicitly enabled so we mask the + * value to insure we only process bits we requested. + */ + ath5k_hw_get_isr(ah, &status); /* NB: clears IRQ too */ + DPRINTF(sc, ATH_DEBUG_INTR, "%s: status 0x%x/0x%x\n", __func__, + status, sc->imask); + status &= sc->imask; /* discard unasked for bits */ + if (unlikely(status & AR5K_INT_FATAL)) { + /* + * Fatal errors are unrecoverable. Typically + * these are caused by DMA errors. Unfortunately + * the exact reason is not (presently) returned + * by the hal. + */ + tasklet_schedule(&sc->restq); + } else if (unlikely(status & AR5K_INT_RXORN)) { + tasklet_schedule(&sc->restq); + } else { + if (status & AR5K_INT_SWBA) { + /* + * Software beacon alert--time to send a beacon. + * Handle beacon transmission directly; deferring + * this is too slow to meet timing constraints + * under load. + */ + ath_beacon_send(sc); + } + if (status & AR5K_INT_RXEOL) { + /* + * NB: the hardware should re-read the link when + * RXE bit is written, but it doesn't work at + * least on older hardware revs. + */ + sc->rxlink = NULL; + } + if (status & AR5K_INT_TXURN) { + /* bump tx trigger level */ + ath5k_hw_update_tx_triglevel(ah, true); + } + if (status & AR5K_INT_RX) + tasklet_schedule(&sc->rxtq); + if (status & AR5K_INT_TX) + tasklet_schedule(&sc->txtq); + if (status & AR5K_INT_BMISS) { + } + if (status & AR5K_INT_MIB) { + /* TODO */ + } + } + } while (ath5k_hw_is_intr_pending(ah) && counter-- > 0); + + if (unlikely(!counter && net_ratelimit())) + printk(KERN_WARNING "ath: too many interrupts, giving up for " + "now\n"); + + return IRQ_HANDLED; +} + +/* + * Convert IEEE channel number to MHz frequency. + */ +static inline short ath_ieee2mhz(short chan) +{ + if (chan <= 14 || chan >= 27) + return ieee80211chan2mhz(chan); + else + return 2212 + chan * 20; +} + +static unsigned int ath_copy_rates(struct ieee80211_rate *rates, + const struct ath5k_rate_table *rt, unsigned int max) +{ + unsigned int i, count; + + if (rt == NULL) + return 0; + + for (i = 0, count = 0; i < rt->rate_count && max > 0; i++) { + if (!rt->rates[i].valid) + continue; + rates->rate = rt->rates[i].rate_kbps / 100; + rates->val = rt->rates[i].rate_code; + rates->flags = rt->rates[i].modulation; + rates++; + count++; + max--; + } + + return count; +} + +static unsigned int ath_copy_channels(struct ath_hw *ah, + struct ieee80211_channel *channels, unsigned int mode, + unsigned int max) +{ + static const struct { unsigned int mode, mask, chan; } map[] = { + [MODE_IEEE80211A] = { CHANNEL_OFDM, CHANNEL_OFDM | CHANNEL_TURBO, CHANNEL_A }, + [MODE_ATHEROS_TURBO] = { CHANNEL_OFDM|CHANNEL_TURBO, CHANNEL_OFDM | CHANNEL_TURBO, CHANNEL_T }, + [MODE_IEEE80211B] = { CHANNEL_CCK, CHANNEL_CCK, CHANNEL_B }, + [MODE_IEEE80211G] = { CHANNEL_OFDM, CHANNEL_OFDM, CHANNEL_G }, + [MODE_ATHEROS_TURBOG] = { CHANNEL_OFDM | CHANNEL_TURBO, CHANNEL_OFDM | CHANNEL_TURBO, CHANNEL_TG }, + }; + static const struct ath5k_regchannel chans_2ghz[] = + IEEE80211_CHANNELS_2GHZ; + static const struct ath5k_regchannel chans_5ghz[] = + IEEE80211_CHANNELS_5GHZ; + const struct ath5k_regchannel *chans; + enum ath5k_regdom dmn; + unsigned int i, count, size, chfreq, all, f, ch; + + if (!test_bit(mode, ah->ah_modes)) + return 0; + + all = ah->ah_regdomain == DMN_DEFAULT || CHAN_DEBUG == 1; + + switch (mode) { + case MODE_IEEE80211A: + case MODE_ATHEROS_TURBO: + /* 1..220, but 2GHz frequencies are filtered by check_channel */ + size = all ? 220 : ARRAY_SIZE(chans_5ghz); + chans = chans_5ghz; + dmn = ath5k_regdom2flag(ah->ah_regdomain, + IEEE80211_CHANNELS_5GHZ_MIN); + chfreq = CHANNEL_5GHZ; + break; + case MODE_IEEE80211B: + case MODE_IEEE80211G: + case MODE_ATHEROS_TURBOG: + size = all ? 26 : ARRAY_SIZE(chans_2ghz); + chans = chans_2ghz; + dmn = ath5k_regdom2flag(ah->ah_regdomain, + IEEE80211_CHANNELS_2GHZ_MIN); + chfreq = CHANNEL_2GHZ; + break; + default: + printk(KERN_WARNING "bad mode, not copying channels\n"); + return 0; + } + + for (i = 0, count = 0; i < size && max > 0; i++) { + ch = all ? i + 1 : chans[i].chan; + f = ath_ieee2mhz(ch); + /* Check if channel is supported by the chipset */ + if (!ath5k_channel_ok(ah, f, chfreq)) + continue; + + /* Match regulation domain */ + if (!all && !(IEEE80211_DMN(chans[i].domain) & + IEEE80211_DMN(dmn))) + continue; + + if (!all && (chans[i].mode & map[mode].mask) != map[mode].mode) + continue; + + /* Write channel and increment counter */ + channels->chan = ch; + channels->freq = f; + channels->val = map[mode].chan; + channels++; + count++; + max--; + } + + return count; +} + +#if ATH_DEBUG_MODES +static void ath_dump_modes(struct ieee80211_hw_mode *modes) +{ + unsigned int m, i; + + for (m = 0; m < NUM_IEEE80211_MODES; m++) { + printk(KERN_DEBUG "Mode %u: channels %d, rates %d\n", m, + modes[m].num_channels, modes[m].num_rates); + printk(KERN_DEBUG " channels:\n"); + for (i = 0; i < modes[m].num_channels; i++) + printk(KERN_DEBUG " %3d %d %.4x %.4x\n", + modes[m].channels[i].chan, + modes[m].channels[i].freq, + modes[m].channels[i].val, + modes[m].channels[i].flag); + printk(KERN_DEBUG " rates:\n"); + for (i = 0; i < modes[m].num_rates; i++) + printk(KERN_DEBUG " %4d %.4x %.4x %.4x\n", + modes[m].rates[i].rate, + modes[m].rates[i].val, + modes[m].rates[i].flags, + modes[m].rates[i].val2); + } +} +#else +static inline void ath_dump_modes(struct ieee80211_hw_mode *modes) {} +#endif + +static int ath_getchannels(struct ieee80211_hw *hw) +{ + struct ath_softc *sc = hw->priv; + struct ath_hw *ah = sc->ah; + struct ieee80211_hw_mode *modes = sc->modes; + unsigned int i, max; + int ret; + enum { + A = MODE_IEEE80211A, + B = MODE_IEEE80211G, /* this is not a typo, but workaround */ + G = MODE_IEEE80211B, /* to prefer g over b */ + T = MODE_ATHEROS_TURBO, + TG = MODE_ATHEROS_TURBOG, + }; + + BUILD_BUG_ON(ARRAY_SIZE(sc->modes) < 3); + + ah->ah_country_code = countrycode; + + modes[A].mode = MODE_IEEE80211A; + modes[B].mode = MODE_IEEE80211B; + modes[G].mode = MODE_IEEE80211G; + + max = ARRAY_SIZE(sc->rates); + modes[A].rates = sc->rates; + max -= modes[A].num_rates = ath_copy_rates(modes[A].rates, + ath5k_hw_get_rate_table(ah, MODE_IEEE80211A), max); + modes[B].rates = &modes[A].rates[modes[A].num_rates]; + max -= modes[B].num_rates = ath_copy_rates(modes[B].rates, + ath5k_hw_get_rate_table(ah, MODE_IEEE80211B), max); + modes[G].rates = &modes[B].rates[modes[B].num_rates]; + max -= modes[G].num_rates = ath_copy_rates(modes[G].rates, + ath5k_hw_get_rate_table(ah, MODE_IEEE80211G), max); + + if (!max) + printk(KERN_WARNING "yet another rates found, but there is not " + "sufficient space to store them\n"); + + max = ARRAY_SIZE(sc->channels); + modes[A].channels = sc->channels; + max -= modes[A].num_channels = ath_copy_channels(ah, modes[A].channels, + MODE_IEEE80211A, max); + modes[B].channels = &modes[A].channels[modes[A].num_channels]; + max -= modes[B].num_channels = ath_copy_channels(ah, modes[B].channels, + MODE_IEEE80211B, max); + modes[G].channels = &modes[B].channels[modes[B].num_channels]; + max -= modes[G].num_channels = ath_copy_channels(ah, modes[G].channels, + MODE_IEEE80211G, max); + + if (!max) + printk(KERN_WARNING "yet another modes found, but there is not " + "sufficient space to store them\n"); + + for (i = 0; i < ARRAY_SIZE(sc->modes); i++) + if (modes[i].num_channels) { + ret = ieee80211_register_hwmode(hw, &modes[i]); + if (ret) { + printk(KERN_ERR "can't register hwmode %u\n",i); + goto err; + } + } + ath_dump_modes(modes); + + return 0; +err: + return ret; +} + +static int ath_desc_alloc(struct ath_softc *sc, struct pci_dev *pdev) +{ + struct ath_desc *ds; + struct ath_buf *bf; + dma_addr_t da; + unsigned int i; + int ret; + + /* allocate descriptors */ + sc->desc_len = sizeof(struct ath_desc) * + (ATH_TXBUF * ATH_TXDESC + ATH_RXBUF + ATH_BCBUF + 1); + sc->desc = pci_alloc_consistent(pdev, sc->desc_len, &sc->desc_daddr); + if (sc->desc == NULL) { + dev_err(&pdev->dev, "can't allocate descriptors\n"); + ret = -ENOMEM; + goto err; + } + ds = sc->desc; + da = sc->desc_daddr; + DPRINTF(sc, ATH_DEBUG_ANY, "%s: DMA map: %p (%zu) -> %llx\n", + __func__, ds, sc->desc_len, (unsigned long long)sc->desc_daddr); + + bf = kcalloc(1 + ATH_TXBUF + ATH_RXBUF + ATH_BCBUF, + sizeof(struct ath_buf), GFP_KERNEL); + if (bf == NULL) { + dev_err(&pdev->dev, "can't allocate bufptr\n"); + ret = -ENOMEM; + goto err_free; + } + sc->bufptr = bf; + + INIT_LIST_HEAD(&sc->rxbuf); + for (i = 0; i < ATH_RXBUF; i++, bf++, ds++, da += sizeof(*ds)) { + bf->desc = ds; + bf->daddr = da; + list_add_tail(&bf->list, &sc->rxbuf); + } + + INIT_LIST_HEAD(&sc->txbuf); + sc->txbuf_len = ATH_TXBUF; + for (i = 0; i < ATH_TXBUF; i++, bf++, ds += ATH_TXDESC, + da += ATH_TXDESC * sizeof(*ds)) { + bf->desc = ds; + bf->daddr = da; + list_add_tail(&bf->list, &sc->txbuf); + } + + /* beacon buffer */ + bf->desc = ds; + bf->daddr = da; + sc->bbuf = bf; + + return 0; +err_free: + pci_free_consistent(pdev, sc->desc_len, sc->desc, sc->desc_daddr); +err: + sc->desc = NULL; + return ret; +} + +static void ath_desc_free(struct ath_softc *sc, struct pci_dev *pdev) +{ + struct ath_buf *bf; + + ath_cleanup_txbuf(sc, sc->bbuf); + list_for_each_entry(bf, &sc->txbuf, list) + ath_cleanup_txbuf(sc, bf); + list_for_each_entry(bf, &sc->rxbuf, list) + ath_cleanup_txbuf(sc, bf); + + /* Free memory associated with all descriptors */ + pci_free_consistent(pdev, sc->desc_len, sc->desc, sc->desc_daddr); + + kfree(sc->bufptr); + sc->bufptr = NULL; +} + +static int ath_beaconq_setup(struct ath_hw *ah) +{ + struct ath5k_txq_info qi = { + .tqi_aifs = AR5K_TXQ_USEDEFAULT, + .tqi_cw_min = AR5K_TXQ_USEDEFAULT, + .tqi_cw_max = AR5K_TXQ_USEDEFAULT, + /* NB: for dynamic turbo, don't enable any other interrupts */ + .tqi_flags = AR5K_TXQ_FLAG_TXDESCINT_ENABLE + }; + + return ath5k_hw_setup_tx_queue(ah, AR5K_TX_QUEUE_BEACON, &qi); +} + +static struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, + int subtype) +{ + struct ath_hw *ah = sc->ah; + struct ath_txq *txq; + struct ath5k_txq_info qi = { + .tqi_subtype = subtype, + .tqi_aifs = AR5K_TXQ_USEDEFAULT, + .tqi_cw_min = AR5K_TXQ_USEDEFAULT, + .tqi_cw_max = AR5K_TXQ_USEDEFAULT + }; + int qnum; + + /* + * Enable interrupts only for EOL and DESC conditions. + * We mark tx descriptors to receive a DESC interrupt + * when a tx queue gets deep; otherwise waiting for the + * EOL to reap descriptors. Note that this is done to + * reduce interrupt load and this only defers reaping + * descriptors, never transmitting frames. Aside from + * reducing interrupts this also permits more concurrency. + * The only potential downside is if the tx queue backs + * up in which case the top half of the kernel may backup + * due to a lack of tx descriptors. + */ + qi.tqi_flags = AR5K_TXQ_FLAG_TXEOLINT_ENABLE | + AR5K_TXQ_FLAG_TXDESCINT_ENABLE; + qnum = ath5k_hw_setup_tx_queue(ah, qtype, &qi); + if (qnum < 0) { + /* + * NB: don't print a message, this happens + * normally on parts with too few tx queues + */ + return ERR_PTR(qnum); + } + if (qnum >= ARRAY_SIZE(sc->txqs)) { + printk(KERN_ERR "hal qnum %u out of range, max %tu!\n", + qnum, ARRAY_SIZE(sc->txqs)); + ath5k_hw_release_tx_queue(ah, qnum); + return ERR_PTR(-EINVAL); + } + txq = &sc->txqs[qnum]; + if (!txq->setup) { + txq->qnum = qnum; + txq->link = NULL; + INIT_LIST_HEAD(&txq->q); + spin_lock_init(&txq->lock); + txq->setup = true; + } + return &sc->txqs[qnum]; +} + +static void ath_tx_cleanup(struct ath_softc *sc) +{ + struct ath_txq *txq = sc->txqs; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(sc->txqs); i++, txq++) + if (txq->setup) { + ath5k_hw_release_tx_queue(sc->ah, txq->qnum); + txq->setup = false; + } +} + +static int ath_attach(struct pci_dev *pdev, struct ieee80211_hw *hw) +{ + struct ath_softc *sc = hw->priv; + struct ath_hw *ah = sc->ah; + u8 mac[ETH_ALEN]; + unsigned int i; + int ret; + + DPRINTF(sc, ATH_DEBUG_ANY, "%s: devid 0x%x\n", __func__, pdev->device); + + /* + * Check if the MAC has multi-rate retry support. + * We do this by trying to setup a fake extended + * descriptor. MAC's that don't have support will + * return false w/o doing anything. MAC's that do + * support it will return true w/o doing anything. + */ + if (ah->ah_setup_xtx_desc(ah, NULL, 0, 0, 0, 0, 0, 0)) + __set_bit(ATH_STAT_MRRETRY, sc->status); + + /* + * Reset the key cache since some parts do not + * reset the contents on initial power up. + */ + for (i = 0; i < AR5K_KEYCACHE_SIZE; i++) + ath5k_hw_reset_key(ah, i); + + /* + * Collect the channel list using the default country + * code and including outdoor channels. The 802.11 layer + * is resposible for filtering this list based on settings + * like the phy mode. + */ + ret = ath_getchannels(hw); + if (ret) { + dev_err(&pdev->dev, "can't get channels\n"); + goto err; + } + + /* NB: setup here so ath_rate_update is happy */ + if (test_bit(MODE_IEEE80211A, ah->ah_modes)) + ath_setcurmode(sc, MODE_IEEE80211A); + else + ath_setcurmode(sc, MODE_IEEE80211B); + + /* + * Allocate tx+rx descriptors and populate the lists. + */ + ret = ath_desc_alloc(sc, pdev); + if (ret) { + dev_err(&pdev->dev, "can't allocate descriptors\n"); + goto err; + } + + /* + * Allocate hardware transmit queues: one queue for + * beacon frames and one data queue for each QoS + * priority. Note that the hal handles reseting + * these queues at the needed time. + */ + ret = ath_beaconq_setup(ah); + if (ret < 0) { + dev_err(&pdev->dev, "can't setup a beacon xmit queue\n"); + goto err_desc; + } + sc->bhalq = ret; + + sc->txq = ath_txq_setup(sc, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_BK); + if (IS_ERR(sc->txq)) { + dev_err(&pdev->dev, "can't setup xmit queue\n"); + ret = PTR_ERR(sc->txq); + goto err_bhal; + } + + tasklet_init(&sc->rxtq, ath_tasklet_rx, (unsigned long)sc); + tasklet_init(&sc->txtq, ath_tasklet_tx, (unsigned long)sc); + tasklet_init(&sc->restq, ath_tasklet_reset, (unsigned long)sc); + setup_timer(&sc->calib_tim, ath_calibrate, (unsigned long)sc); + setup_timer(&sc->led_tim, ath_led_off, (unsigned long)sc); + + sc->led_on = 0; /* low true */ + /* + * Auto-enable soft led processing for IBM cards and for + * 5211 minipci cards. Users can also manually enable/disable + * support with a sysctl. + */ + if (pdev->device == PCI_DEVICE_ID_ATHEROS_AR5212_IBM || + pdev->device == PCI_DEVICE_ID_ATHEROS_AR5211) { + __set_bit(ATH_STAT_LEDSOFT, sc->status); + sc->led_pin = 0; + } + /* Enable softled on PIN1 on HP Compaq nc6xx, nc4000 & nx5000 laptops */ + if (pdev->subsystem_vendor == PCI_VENDOR_ID_COMPAQ) { + __set_bit(ATH_STAT_LEDSOFT, sc->status); + sc->led_pin = 0; + } + if (test_bit(ATH_STAT_LEDSOFT, sc->status)) { + ath5k_hw_set_gpio_output(ah, sc->led_pin); + ath5k_hw_set_gpio(ah, sc->led_pin, !sc->led_on); + } + + ath5k_hw_get_lladdr(ah, mac); + SET_IEEE80211_PERM_ADDR(hw, mac); + if (ath5k_hw_hasbssidmask(ah)) { + memset(sc->bssidmask, 0xff, ETH_ALEN); + ath5k_hw_set_bssid_mask(ah, sc->bssidmask); + } + + ret = ieee80211_register_hw(hw); + if (ret) { + dev_err(&pdev->dev, "can't register ieee80211 hw\n"); + goto err_queues; + } + + return 0; +err_queues: + ath_tx_cleanup(sc); +err_bhal: + ath5k_hw_release_tx_queue(ah, sc->bhalq); +err_desc: + ath_desc_free(sc, pdev); +err: + return ret; +} + +static void ath_detach(struct pci_dev *pdev, struct ieee80211_hw *hw) +{ + struct ath_softc *sc = hw->priv; + + /* + * NB: the order of these is important: + * o call the 802.11 layer before detaching the hal to + * insure callbacks into the driver to delete global + * key cache entries can be handled + * o reclaim the tx queue data structures after calling + * the 802.11 layer as we'll get called back to reclaim + * node state and potentially want to use them + * o to cleanup the tx queues the hal is called, so detach + * it last + * Other than that, it's straightforward... + */ + ieee80211_unregister_hw(hw); + ath_desc_free(sc, pdev); + ath_tx_cleanup(sc); + ath5k_hw_release_tx_queue(sc->ah, sc->bhalq); + + /* + * NB: can't reclaim these until after ieee80211_ifdetach + * returns because we'll get called back to reclaim node + * state and potentially want to use them. + */ +} + +static const char *ath_chip_name(u8 mac_version) +{ + switch (mac_version) { + case AR5K_AR5210: + return "AR5210"; + case AR5K_AR5211: + return "AR5211"; + case AR5K_AR5212: + return "AR5212"; + } + return "Unknown"; +} + +static int __devinit ath_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + void __iomem *mem; + struct ath_softc *sc; + struct ieee80211_hw *hw; + int ret; + u8 csz; + + ret = pci_enable_device(pdev); + if (ret) { + dev_err(&pdev->dev, "can't enable device\n"); + goto err; + } + + /* XXX 32-bit addressing only */ + ret = pci_set_dma_mask(pdev, DMA_32BIT_MASK); + if (ret) { + dev_err(&pdev->dev, "32-bit DMA not available\n"); + goto err_dis; + } + + /* + * Cache line size is used to size and align various + * structures used to communicate with the hardware. + */ + pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &csz); + if (csz == 0) { + /* + * Linux 2.4.18 (at least) writes the cache line size + * register as a 16-bit wide register which is wrong. + * We must have this setup properly for rx buffer + * DMA to work so force a reasonable value here if it + * comes up zero. + */ + csz = L1_CACHE_BYTES / sizeof(u32); + pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, csz); + } + /* + * The default setting of latency timer yields poor results, + * set it to the value used by other systems. It may be worth + * tweaking this setting more. + */ + pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xa8); + + pci_set_master(pdev); + + /* + * Disable the RETRY_TIMEOUT register (0x41) to keep + * PCI Tx retries from interfering with C3 CPU state. + */ + pci_write_config_byte(pdev, 0x41, 0); + + ret = pci_request_region(pdev, 0, "ath"); + if (ret) { + dev_err(&pdev->dev, "cannot reserve PCI memory region\n"); + goto err_dis; + } + + mem = pci_iomap(pdev, 0, 0); + if (!mem) { + dev_err(&pdev->dev, "cannot remap PCI memory region\n") ; + ret = -EIO; + goto err_reg; + } + + hw = ieee80211_alloc_hw(sizeof(*sc), &ath_hw_ops); + if (hw == NULL) { + dev_err(&pdev->dev, "cannot allocate ieee80211_hw\n"); + ret = -ENOMEM; + goto err_map; + } + + SET_IEEE80211_DEV(hw, &pdev->dev); + hw->flags = IEEE80211_HW_RX_INCLUDES_FCS; + hw->extra_tx_headroom = 2; + hw->channel_change_time = 5000; + hw->max_rssi = 127; /* FIXME: get a real value for this. */ + sc = hw->priv; + sc->hw = hw; + sc->pdev = pdev; + + /* + * Mark the device as detached to avoid processing + * interrupts until setup is complete. + */ +#if AR_DEBUG + sc->debug = ath_debug; +#endif + __set_bit(ATH_STAT_INVALID, sc->status); + sc->iobase = mem; + sc->cachelsz = csz * sizeof(u32); /* convert to bytes */ + sc->opmode = IEEE80211_IF_TYPE_STA; + mutex_init(&sc->lock); + spin_lock_init(&sc->rxbuflock); + spin_lock_init(&sc->txbuflock); + + pci_set_drvdata(pdev, hw); + + pci_enable_msi(pdev); + + ret = request_irq(pdev->irq, ath_intr, IRQF_SHARED, "ath", sc); + if (ret) { + dev_err(&pdev->dev, "request_irq failed\n"); + goto err_free; + } + + sc->ah = ath5k_hw_attach(pdev->device, id->driver_data, sc, sc->iobase); + if (IS_ERR(sc->ah)) { + ret = PTR_ERR(sc->ah); + goto err_irq; + } + + ret = ath_attach(pdev, hw); + if (ret) + goto err_ah; + + dev_info(&pdev->dev, "%s chip found: mac %d.%d phy %d.%d\n", + ath_chip_name(id->driver_data), sc->ah->ah_mac_version, + sc->ah->ah_mac_version, sc->ah->ah_phy_revision >> 4, + sc->ah->ah_phy_revision & 0xf); + + /* ready to process interrupts */ + __clear_bit(ATH_STAT_INVALID, sc->status); + + return 0; +err_ah: + ath5k_hw_detach(sc->ah); +err_irq: + free_irq(pdev->irq, sc); +err_free: + pci_disable_msi(pdev); + ieee80211_free_hw(hw); +err_map: + pci_iounmap(pdev, mem); +err_reg: + pci_release_region(pdev, 0); +err_dis: + pci_disable_device(pdev); +err: + return ret; +} + +static void __devexit ath_pci_remove(struct pci_dev *pdev) +{ + struct ieee80211_hw *hw = pci_get_drvdata(pdev); + struct ath_softc *sc = hw->priv; + + ath_detach(pdev, hw); + ath5k_hw_detach(sc->ah); + free_irq(pdev->irq, sc); + pci_disable_msi(pdev); + pci_iounmap(pdev, sc->iobase); + pci_release_region(pdev, 0); + pci_disable_device(pdev); + ieee80211_free_hw(hw); +} + +#ifdef CONFIG_PM +static int ath_pci_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct ieee80211_hw *hw = pci_get_drvdata(pdev); + struct ath_softc *sc = hw->priv; + + if (test_bit(ATH_STAT_LEDSOFT, sc->status)) + ath5k_hw_set_gpio(sc->ah, sc->led_pin, 1); + + ath_stop_hw(sc); + pci_save_state(pdev); + pci_disable_device(pdev); + pci_set_power_state(pdev, PCI_D3hot); + + return 0; +} + +static int ath_pci_resume(struct pci_dev *pdev) +{ + struct ieee80211_hw *hw = pci_get_drvdata(pdev); + struct ath_softc *sc = hw->priv; + int err; + + err = pci_set_power_state(pdev, PCI_D0); + if (err) + return err; + + err = pci_enable_device(pdev); + if (err) + return err; + + pci_restore_state(pdev); + /* + * Suspend/Resume resets the PCI configuration space, so we have to + * re-disable the RETRY_TIMEOUT register (0x41) to keep + * PCI Tx retries from interfering with C3 CPU state + */ + pci_write_config_byte(pdev, 0x41, 0); + + ath_init(sc); + if (test_bit(ATH_STAT_LEDSOFT, sc->status)) { + ath5k_hw_set_gpio_output(sc->ah, sc->led_pin); + ath5k_hw_set_gpio(sc->ah, sc->led_pin, 0); + } + + return 0; +} +#else +#define ath_pci_suspend NULL +#define ath_pci_resume NULL +#endif /* CONFIG_PM */ + +static struct pci_driver ath_pci_drv_id = { + .name = "ath_pci", + .id_table = ath_pci_id_table, + .probe = ath_pci_probe, + .remove = __devexit_p(ath_pci_remove), + .suspend = ath_pci_suspend, + .resume = ath_pci_resume, +}; + +static int mincalibrate = 1; +static int maxcalibrate = INT_MAX / 1000; +#define CTL_AUTO -2 /* cannot be CTL_ANY or CTL_NONE */ + +static ctl_table ath_static_sysctls[] = { +#if AR_DEBUG + { + .procname = "debug", + .mode = 0644, + .data = &ath_debug, + .maxlen = sizeof(ath_debug), + .proc_handler = proc_dointvec + }, +#endif + { + .procname = "countrycode", + .mode = 0444, + .data = &countrycode, + .maxlen = sizeof(countrycode), + .proc_handler = proc_dointvec + }, + { + .procname = "outdoor", + .mode = 0444, + .data = &outdoor, + .maxlen = sizeof(outdoor), + .proc_handler = proc_dointvec + }, + { + .procname = "xchanmode", + .mode = 0444, + .data = &xchanmode, + .maxlen = sizeof(xchanmode), + .proc_handler = proc_dointvec + }, + { + .procname = "calibrate", + .mode = 0644, + .data = &ath_calinterval, + .maxlen = sizeof(ath_calinterval), + .extra1 = &mincalibrate, + .extra2 = &maxcalibrate, + .proc_handler = proc_dointvec_minmax + }, + { 0 } +}; +static ctl_table ath_ath_table[] = { + { + .procname = "ath", + .mode = 0555, + .child = ath_static_sysctls + }, { 0 } +}; +static ctl_table ath_root_table[] = { + { + .ctl_name = CTL_DEV, + .procname = "dev", + .mode = 0555, + .child = ath_ath_table + }, { 0 } +}; +static struct ctl_table_header *ath_sysctl_header; + +static int __init init_ath_pci(void) +{ + int ret; + + ret = pci_register_driver(&ath_pci_drv_id); + if (ret) { + printk(KERN_ERR "ath_pci: can't register pci driver\n"); + return ret; + } + ath_sysctl_header = register_sysctl_table(ath_root_table); + + return 0; +} + +static void __exit exit_ath_pci(void) +{ + if (ath_sysctl_header) + unregister_sysctl_table(ath_sysctl_header); + pci_unregister_driver(&ath_pci_drv_id); +} + +module_init(init_ath_pci); +module_exit(exit_ath_pci); + +MODULE_AUTHOR("Jiri Slaby"); +MODULE_DESCRIPTION("Support for Atheros 802.11 wireless LAN cards."); +MODULE_SUPPORTED_DEVICE("Atheros WLAN cards"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_VERSION(ATH_PCI_VERSION " (EXPERIMENTAL)"); diff -puN /dev/null drivers/net/wireless/ath5k/base.h --- /dev/null +++ a/drivers/net/wireless/ath5k/base.h @@ -0,0 +1,207 @@ +/*- + * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +/* + * Defintions for the Atheros Wireless LAN controller driver. + */ +#ifndef _DEV_ATH_ATHVAR_H +#define _DEV_ATH_ATHVAR_H + +#include +#include +#include +#include + +#include "ath5k.h" + +#define ATH_TIMEOUT 1000 + +#define ATH_LONG_CALIB 30 /* seconds */ +#define ATH_SHORT_CALIB 1 + +/* + * Maximum acceptable MTU + * MAXFRAMEBODY - WEP - QOS - RSN/WPA: + * 2312 - 8 - 2 - 12 = 2290 + */ +#define ATH_MAX_MTU 2290 +#define ATH_MIN_MTU 32 + +#define ATH_RXBUF 40 /* number of RX buffers */ +#define ATH_TXBUF 200 /* number of TX buffers */ +#define ATH_TXDESC 1 /* number of descriptors per buffer */ +#define ATH_BCBUF 1 /* number of beacon buffers */ +#define ATH_TXMAXTRY 11 /* max number of transmit attempts */ +#define ATH_TXINTR_PERIOD 5 /* max number of batched tx descriptors */ + +#define ATH_BEACON_AIFS_DEFAULT 0 /* default aifs for ap beacon q */ +#define ATH_BEACON_CWMIN_DEFAULT 0 /* default cwmin for ap beacon q */ +#define ATH_BEACON_CWMAX_DEFAULT 0 /* default cwmax for ap beacon q */ + +#define ATH_RSSI_LPF_LEN 10 +#define ATH_RSSI_DUMMY_MARKER 0x127 +#define ATH_EP_MUL(x, mul) ((x) * (mul)) +#define ATH_RSSI_IN(x) (ATH_EP_MUL((x), AR5K_RSSI_EP_MULTIPLIER)) +#define ATH_LPF_RSSI(x, y, len) \ + ((x != ATH_RSSI_DUMMY_MARKER) ? (((x) * ((len) - 1) + (y)) / (len)) : (y)) +#define ATH_RSSI_LPF(x, y) do { \ + if ((y) >= -20) \ + x = ATH_LPF_RSSI((x), ATH_RSSI_IN((y)), ATH_RSSI_LPF_LEN); \ +} while (0) + +struct ath_buf { + struct list_head list; + unsigned int flags; /* tx descriptor flags */ + struct ath_desc *desc; /* virtual addr of desc */ + dma_addr_t daddr; /* physical addr of desc */ + struct sk_buff *skb; /* skbuff for buf */ + dma_addr_t skbaddr;/* physical addr of skb data */ + struct ieee80211_tx_control ctl; +}; + +/* + * Data transmit queue state. One of these exists for each + * hardware transmit queue. Packets sent to us from above + * are assigned to queues based on their priority. Not all + * devices support a complete set of hardware transmit queues. + * For those devices the array sc_ac2q will map multiple + * priorities to fewer hardware queues (typically all to one + * hardware queue). + */ +struct ath_txq { + unsigned int qnum; /* hardware q number */ + u32 *link; /* link ptr in last TX desc */ + struct list_head q; /* transmit queue */ + spinlock_t lock; /* lock on q and link */ + bool setup; +}; + +#if CHAN_DEBUG +#define ATH_CHAN_MAX (26+26+26+200+200) +#else +#define ATH_CHAN_MAX (14+14+14+252+20) /* XXX what's the max? */ +#endif + + +/* Software Carrier, keeps track of the driver state + * associated with an instance of a device */ +struct ath_softc { + struct pci_dev *pdev; /* for dma mapping */ + void __iomem *iobase; /* address of the device */ + struct mutex lock; /* dev-level lock */ + struct ieee80211_tx_queue_stats tx_stats; + struct ieee80211_low_level_stats ll_stats; + struct ieee80211_hw *hw; /* IEEE 802.11 common */ + struct ieee80211_hw_mode modes[NUM_IEEE80211_MODES]; + struct ieee80211_channel channels[ATH_CHAN_MAX]; + struct ieee80211_rate rates[AR5K_MAX_RATES * NUM_IEEE80211_MODES]; + enum ieee80211_if_types opmode; + struct ath_hw *ah; /* Atheros HW */ + + int debug; + + struct ath_buf *bufptr; /* allocated buffer ptr */ + struct ath_desc *desc; /* TX/RX descriptors */ + dma_addr_t desc_daddr; /* DMA (physical) address */ + size_t desc_len; /* size of TX/RX descriptors */ + u16 cachelsz; /* cache line size */ + + DECLARE_BITMAP(status, 6); +#define ATH_STAT_INVALID 0 /* disable hardware accesses */ +#define ATH_STAT_MRRETRY 1 /* multi-rate retry support */ +#define ATH_STAT_PROMISC 2 +#define ATH_STAT_LEDBLINKING 3 /* LED blink operation active */ +#define ATH_STAT_LEDENDBLINK 4 /* finish LED blink operation */ +#define ATH_STAT_LEDSOFT 5 /* enable LED gpio status */ + + unsigned int filter_flags; /* HW flags, AR5K_RX_FILTER_* */ + unsigned int curmode; /* current phy mode */ + struct ieee80211_channel *curchan; /* current h/w channel */ + + int iface_id; /* add/remove_interface id */ + + struct { + u8 rxflags; /* radiotap rx flags */ + u8 txflags; /* radiotap tx flags */ + u16 ledon; /* softled on time */ + u16 ledoff; /* softled off time */ + } hwmap[32]; /* h/w rate ix mappings */ + + enum ath5k_int imask; /* interrupt mask copy */ + + DECLARE_BITMAP(keymap, AR5K_KEYCACHE_SIZE); /* key use bit map */ + + u8 bssidmask[ETH_ALEN]; + + unsigned int led_pin, /* GPIO pin for driving LED */ + led_on, /* pin setting for LED on */ + led_off; /* off time for current blink */ + struct timer_list led_tim; /* led off timer */ + u8 led_rxrate; /* current rx rate for LED */ + u8 led_txrate; /* current tx rate for LED */ + + struct tasklet_struct restq; /* reset tasklet */ + + unsigned int rxbufsize; /* rx size based on mtu */ + struct list_head rxbuf; /* receive buffer */ + spinlock_t rxbuflock; + u32 *rxlink; /* link ptr in last RX desc */ + struct tasklet_struct rxtq; /* rx intr tasklet */ + + struct list_head txbuf; /* transmit buffer */ + spinlock_t txbuflock; + unsigned int txbuf_len; /* buf count in txbuf list */ + struct ath_txq txqs[2]; /* beacon and tx */ + + struct ath_txq *txq; /* beacon and tx*/ + struct tasklet_struct txtq; /* tx intr tasklet */ + + struct ath_buf *bbuf; /* beacon buffer */ + unsigned int bhalq, /* HAL q for outgoing beacons */ + bmisscount, /* missed beacon transmits */ + bintval, /* beacon interval */ + bsent; + + struct timer_list calib_tim; /* calibration timer */ +}; + +#define ath5k_hw_hasbssidmask(_ah) \ + (ath5k_hw_get_capability(_ah, AR5K_CAP_BSSIDMASK, 0, NULL) == 0) +#define ath5k_hw_hasveol(_ah) \ + (ath5k_hw_get_capability(_ah, AR5K_CAP_VEOL, 0, NULL) == 0) + +#endif diff -puN /dev/null drivers/net/wireless/ath5k/hw.c --- /dev/null +++ a/drivers/net/wireless/ath5k/hw.c @@ -0,0 +1,4322 @@ + /* + * Copyright (c) 2004-2007 Reyk Floeter + * Copyright (c) 2006-2007 Nick Kossifidis + * Copyright (c) 2007 Matthew W. S. Bell + * Copyright (c) 2007 Luis Rodriguez + * Copyright (c) 2007 Pavel Roskin + * Copyright (c) 2007 Jiri Slaby + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +/* + * HAL interface for Atheros Wireless LAN devices. + */ + +#include +#include + +#include "ath5k.h" +#include "reg.h" + +/*Rate tables*/ +static const struct ath5k_rate_table ath5k_rt_11a = AR5K_RATES_11A; +static const struct ath5k_rate_table ath5k_rt_11b = AR5K_RATES_11B; +static const struct ath5k_rate_table ath5k_rt_11g = AR5K_RATES_11G; +static const struct ath5k_rate_table ath5k_rt_turbo = AR5K_RATES_TURBO; +static const struct ath5k_rate_table ath5k_rt_xr = AR5K_RATES_XR; + +/*Prototypes*/ +static int ath5k_hw_nic_reset(struct ath_hw *, u32); +static int ath5k_hw_nic_wakeup(struct ath_hw *, int, bool); +static int ath5k_hw_setup_4word_tx_desc(struct ath_hw *, struct ath_desc *, + unsigned int, unsigned int, enum ath5k_pkt_type, unsigned int, + unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, + unsigned int, unsigned int); +static bool ath5k_hw_setup_xr_tx_desc(struct ath_hw *, struct ath_desc *, + unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, + unsigned int); +static int ath5k_hw_fill_4word_tx_desc(struct ath_hw *, struct ath_desc *, + unsigned int, bool, bool); +static int ath5k_hw_proc_4word_tx_status(struct ath_hw *, struct ath_desc *); +static int ath5k_hw_setup_2word_tx_desc(struct ath_hw *, struct ath_desc *, + unsigned int, unsigned int, enum ath5k_pkt_type, unsigned int, + unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, + unsigned int, unsigned int); +static int ath5k_hw_fill_2word_tx_desc(struct ath_hw *, struct ath_desc *, + unsigned int, bool, bool); +static int ath5k_hw_proc_2word_tx_status(struct ath_hw *, struct ath_desc *); +static int ath5k_hw_proc_new_rx_status(struct ath_hw *, struct ath_desc *); +static int ath5k_hw_proc_old_rx_status(struct ath_hw *, struct ath_desc *); +static int ath5k_hw_get_capabilities(struct ath_hw *); + +static int ath5k_eeprom_init(struct ath_hw *); +static int ath5k_eeprom_read_mac(struct ath_hw *, u8 *); + +static int ath5k_hw_enable_pspoll(struct ath_hw *, u8 *, u16); +static int ath5k_hw_disable_pspoll(struct ath_hw *); + +/* + * Enable to overwrite the country code (use "00" for debug) + */ +#if 0 +#define COUNTRYCODE "00" +#endif + +/*******************\ + General Functions +\*******************/ + + +/* + * Calculate transmition time of a frame + * TODO: Left here for combatibility, change it in ath5k + */ +static u16 /*TODO: Is this really hardware dependent ?*/ +ath_hal_computetxtime(struct ath_hw *hal, const struct ath5k_rate_table *rates, + u32 frame_length, u16 rate_index, bool short_preamble) +{ + const struct ath5k_rate *rate; + u32 value; + + AR5K_ASSERT_ENTRY(rate_index, rates->rate_count); + + /* + * Get rate by index + */ + rate = &rates->rates[rate_index]; + + /* + * Calculate the transmission time by operation (PHY) mode + */ + switch (rate->modulation) { + /* Standard rates */ + case IEEE80211_RATE_CCK: + /* + * CCK / DS mode (802.11b) + */ + value = AR5K_CCK_TX_TIME(rate->rate_kbps, frame_length, + short_preamble && + rate->modulation == IEEE80211_RATE_CCK_2); + break; + + case IEEE80211_RATE_OFDM: + /* + * Orthogonal Frequency Division Multiplexing + */ + if (AR5K_OFDM_NUM_BITS_PER_SYM(rate->rate_kbps) == 0) + return 0; + value = AR5K_OFDM_TX_TIME(rate->rate_kbps, frame_length); + break; + + /* Vendor-specific rates */ + case MODULATION_TURBO: + /* + * Orthogonal Frequency Division Multiplexing + * Atheros "Turbo Mode" (doubled rates) + */ + if (AR5K_TURBO_NUM_BITS_PER_SYM(rate->rate_kbps) == 0) + return 0; + value = AR5K_TURBO_TX_TIME(rate->rate_kbps, frame_length); + break; + + case MODULATION_XR: + /* + * Orthogonal Frequency Division Multiplexing + * Atheros "eXtended Range" (XR) + */ + if (AR5K_XR_NUM_BITS_PER_SYM(rate->rate_kbps) == 0) + return 0; + value = AR5K_XR_TX_TIME(rate->rate_kbps, frame_length); + break; + + default: + return 0; + } + + return value; +} + +/* + * Functions used internaly + */ + +static inline unsigned int ath5k_hw_htoclock(unsigned int usec, bool turbo) +{ + return turbo == true ? (usec * 80) : (usec * 40); +} + +static inline unsigned int ath5k_hw_clocktoh(unsigned int clock, bool turbo) +{ + return turbo == true ? (clock / 80) : (clock / 40); +} + +/* + * Check if a register write has been completed + */ +int ath5k_hw_register_timeout(struct ath_hw *hal, u32 reg, u32 flag, u32 val, + bool is_set) +{ + int i; + u32 data; + + for (i = AR5K_TUNE_REGISTER_TIMEOUT; i > 0; i--) { + data = ath5k_hw_reg_read(hal, reg); + if ((is_set == true) && (data & flag)) + break; + else if ((data & flag) == val) + break; + udelay(15); + } + + return (i <= 0) ? -EAGAIN : 0; +} + + +/***************************************\ + Attach/Detach Functions +\***************************************/ + +/* + * Check if the device is supported and initialize the needed structs + */ +struct ath_hw *ath5k_hw_attach(u16 device, u8 mac_version, void *sc, + void __iomem *sh) +{ + struct ath_hw *hal; + u8 mac[ETH_ALEN]; + int ret; + u32 srev; + + /*If we passed the test malloc a hal struct*/ + hal = kzalloc(sizeof(struct ath_hw), GFP_KERNEL); + if (hal == NULL) { + ret = -ENOMEM; + AR5K_PRINT("out of memory\n"); + goto err; + } + + hal->ah_sc = sc; + hal->ah_sh = sh; + + /* + * HAL information + */ + + /* Regulation Stuff */ + hal->ah_country_code = AR5K_TUNE_CTRY; + ath5k_get_regdomain(hal); + + hal->ah_op_mode = IEEE80211_IF_TYPE_STA; + hal->ah_radar.r_enabled = AR5K_TUNE_RADAR_ALERT; + hal->ah_turbo = false; + hal->ah_txpower.txp_tpc = AR5K_TUNE_TPC_TXPOWER; + hal->ah_imr = 0; + hal->ah_atim_window = 0; + hal->ah_aifs = AR5K_TUNE_AIFS; + hal->ah_cw_min = AR5K_TUNE_CWMIN; + hal->ah_limit_tx_retries = AR5K_INIT_TX_RETRY; + hal->ah_software_retry = false; + hal->ah_ant_diversity = AR5K_TUNE_ANT_DIVERSITY; + + switch (device) { + case PCI_DEVICE_ID_ATHEROS_AR2413: + case PCI_DEVICE_ID_ATHEROS_AR5413: + case PCI_DEVICE_ID_ATHEROS_AR5424: + /* + * Known single chip solutions + */ + hal->ah_single_chip = true; + break; + default: + /* + * Multi chip solutions + */ + hal->ah_single_chip = false; + break; + } + + /* + * Set the mac revision based on the pci id + */ + hal->ah_version = mac_version; + + /*Fill the hal struct with the needed functions*/ + if (hal->ah_version == AR5K_AR5212) + hal->ah_magic = AR5K_EEPROM_MAGIC_5212; + else if (hal->ah_version == AR5K_AR5211) + hal->ah_magic = AR5K_EEPROM_MAGIC_5211; + + if (hal->ah_version == AR5K_AR5212) { + hal->ah_setup_tx_desc = ath5k_hw_setup_4word_tx_desc; + hal->ah_setup_xtx_desc = ath5k_hw_setup_xr_tx_desc; + hal->ah_fill_tx_desc = ath5k_hw_fill_4word_tx_desc; + hal->ah_proc_tx_desc = ath5k_hw_proc_4word_tx_status; + } else { + hal->ah_setup_tx_desc = ath5k_hw_setup_2word_tx_desc; + hal->ah_setup_xtx_desc = ath5k_hw_setup_xr_tx_desc; + hal->ah_fill_tx_desc = ath5k_hw_fill_2word_tx_desc; + hal->ah_proc_tx_desc = ath5k_hw_proc_2word_tx_status; + } + + if (hal->ah_version == AR5K_AR5212) + hal->ah_proc_rx_desc = ath5k_hw_proc_new_rx_status; + else if (hal->ah_version <= AR5K_AR5211) + hal->ah_proc_rx_desc = ath5k_hw_proc_old_rx_status; + + /* Bring device out of sleep and reset it's units */ + ret = ath5k_hw_nic_wakeup(hal, AR5K_INIT_MODE, true); + if (ret) + goto err_free; + + /* Get MAC, PHY and RADIO revisions */ + srev = ath5k_hw_reg_read(hal, AR5K_SREV); + hal->ah_mac_srev = srev; + hal->ah_mac_version = AR5K_REG_MS(srev, AR5K_SREV_VER); + hal->ah_mac_revision = AR5K_REG_MS(srev, AR5K_SREV_REV); + hal->ah_phy_revision = ath5k_hw_reg_read(hal, AR5K_PHY_CHIP_ID) & + 0xffffffff; + hal->ah_radio_5ghz_revision = ath5k_hw_radio_revision(hal, + CHANNEL_5GHZ); + + if (hal->ah_version == AR5K_AR5210) + hal->ah_radio_2ghz_revision = 0; + else + hal->ah_radio_2ghz_revision = ath5k_hw_radio_revision(hal, + CHANNEL_2GHZ); + + /* Single chip radio */ + if (hal->ah_radio_2ghz_revision == hal->ah_radio_5ghz_revision) + hal->ah_radio_2ghz_revision = 0; + + /* Identify the radio chip*/ + if (hal->ah_version == AR5K_AR5210) + hal->ah_radio = AR5K_RF5110; + else + hal->ah_radio = hal->ah_radio_5ghz_revision < + AR5K_SREV_RAD_5112 ? AR5K_RF5111 : AR5K_RF5112; + + hal->ah_phy = AR5K_PHY(0); + + /* Set MAC to bcast: ff:ff:ff:ff:ff:ff, this is using 'mac' as a + * temporary variable for setting our BSSID. Right bellow we update + * it with ath5k_hw_get_lladdr() */ + memset(mac, 0xff, ETH_ALEN); + ath5k_hw_set_associd(hal, mac, 0); + + ath5k_hw_get_lladdr(hal, mac); + ath5k_hw_set_opmode(hal); + +#ifdef AR5K_DEBUG + ath5k_hw_dump_state(hal); +#endif + + /* + * Get card capabilities, values, ... + */ + + ret = ath5k_eeprom_init(hal); + if (ret) { + AR5K_PRINT("unable to init EEPROM\n"); + goto err_free; + } + + /* Get misc capabilities */ + ret = ath5k_hw_get_capabilities(hal); + if (ret) { + AR5K_PRINTF("unable to get device capabilities: 0x%04x\n", + device); + goto err_free; + } + + /* Get MAC address */ + ret = ath5k_eeprom_read_mac(hal, mac); + if (ret) { + AR5K_PRINTF("unable to read address from EEPROM: 0x%04x\n", + device); + goto err_free; + } + + ath5k_hw_set_lladdr(hal, mac); + + ath5k_hw_set_rfgain_opt(hal); + + return hal; +err_free: + kfree(hal); +err: + return ERR_PTR(ret); +} + +/* + * Bring up MAC + PHY Chips + */ +static int ath5k_hw_nic_wakeup(struct ath_hw *hal, int flags, bool initial) +{ + u32 turbo, mode, clock; + int ret; + + turbo = 0; + mode = 0; + clock = 0; + + AR5K_TRACE; + + if (hal->ah_version != AR5K_AR5210) { + /* + * Get channel mode flags + */ + + if (hal->ah_radio >= AR5K_RF5112) { + mode = AR5K_PHY_MODE_RAD_RF5112; + clock = AR5K_PHY_PLL_RF5112; + } else { + mode = AR5K_PHY_MODE_RAD_RF5111; /*Zero*/ + clock = AR5K_PHY_PLL_RF5111; /*Zero*/ + } + + if (flags & CHANNEL_2GHZ) { + mode |= AR5K_PHY_MODE_FREQ_2GHZ; + clock |= AR5K_PHY_PLL_44MHZ; + + if (flags & CHANNEL_CCK) { + mode |= AR5K_PHY_MODE_MOD_CCK; + } else if (flags & CHANNEL_OFDM) { + /* XXX Dynamic OFDM/CCK is not supported by the + * AR5211 so we set MOD_OFDM for plain g (no + * CCK headers) operation. We need to test + * this, 5211 might support ofdm-only g after + * all, there are also initial register values + * in the code for g mode (see ath5k_hw.h). */ + if (hal->ah_version == AR5K_AR5211) + mode |= AR5K_PHY_MODE_MOD_OFDM; + else + mode |= AR5K_PHY_MODE_MOD_DYN; + } else { + AR5K_PRINT("invalid radio modulation mode\n"); + return -EINVAL; + } + } else if (flags & CHANNEL_5GHZ) { + mode |= AR5K_PHY_MODE_FREQ_5GHZ; + clock |= AR5K_PHY_PLL_40MHZ; + + if (flags & CHANNEL_OFDM) + mode |= AR5K_PHY_MODE_MOD_OFDM; + else { + AR5K_PRINT("invalid radio modulation mode\n"); + return -EINVAL; + } + } else { + AR5K_PRINT("invalid radio frequency mode\n"); + return -EINVAL; + } + + if (flags & CHANNEL_TURBO) + turbo = AR5K_PHY_TURBO_MODE | AR5K_PHY_TURBO_SHORT; + } else { /* Reset and wakeup the device */ + if (initial == true) { + /* ...reset hardware */ + if (ath5k_hw_nic_reset(hal, AR5K_RESET_CTL_PCI)) { + AR5K_PRINT("failed to reset the PCI chipset\n"); + return -EIO; + } + + mdelay(1); + } + + /* ...wakeup */ + ret = ath5k_hw_set_power(hal, AR5K_PM_AWAKE, true, 0); + if (ret) { + AR5K_PRINT("failed to resume the MAC Chip\n"); + return ret; + } + + /* ...enable Atheros turbo mode if requested */ + if (flags & CHANNEL_TURBO) + ath5k_hw_reg_write(hal, AR5K_PHY_TURBO_MODE, + AR5K_PHY_TURBO); + + /* ...reset chipset */ + if (ath5k_hw_nic_reset(hal, AR5K_RESET_CTL_CHIP)) { + AR5K_PRINT("failed to reset the AR5210 chipset\n"); + return -EIO; + } + + mdelay(1); + } + + /* ...reset chipset and PCI device */ + if (hal->ah_single_chip == false && ath5k_hw_nic_reset(hal, + AR5K_RESET_CTL_CHIP | AR5K_RESET_CTL_PCI)) { + AR5K_PRINT("failed to reset the MAC Chip + PCI\n"); + return -EIO; + } + + if (hal->ah_version == AR5K_AR5210) + udelay(2300); + + /* ...wakeup */ + ret = ath5k_hw_set_power(hal, AR5K_PM_AWAKE, true, 0); + if (ret) { + AR5K_PRINT("failed to resume the MAC Chip\n"); + return ret; + } + + /* ...final warm reset */ + if (ath5k_hw_nic_reset(hal, 0)) { + AR5K_PRINT("failed to warm reset the MAC Chip\n"); + return -EIO; + } + + if (hal->ah_version != AR5K_AR5210) { + /* ...set the PHY operating mode */ + ath5k_hw_reg_write(hal, clock, AR5K_PHY_PLL); + udelay(300); + + ath5k_hw_reg_write(hal, mode, AR5K_PHY_MODE); + ath5k_hw_reg_write(hal, turbo, AR5K_PHY_TURBO); + } + + return 0; +} + +/* + * Get the rate table for a specific operation mode + */ +const struct ath5k_rate_table *ath5k_hw_get_rate_table(struct ath_hw *hal, + unsigned int mode) +{ + AR5K_TRACE; + + if (!test_bit(mode, hal->ah_capabilities.cap_mode)) + return NULL; + + /* Get rate tables */ + switch (mode) { + case MODE_IEEE80211A: + return &ath5k_rt_11a; + case MODE_ATHEROS_TURBO: + return &ath5k_rt_turbo; + case MODE_IEEE80211B: + return &ath5k_rt_11b; + case MODE_IEEE80211G: + return &ath5k_rt_11g; + case MODE_ATHEROS_TURBOG: + return &ath5k_rt_xr; + } + + return NULL; +} + +/* + * Free the hal struct + */ +void ath5k_hw_detach(struct ath_hw *hal) +{ + AR5K_TRACE; + + if (hal->ah_rf_banks != NULL) + kfree(hal->ah_rf_banks); + + /* assume interrupts are down */ + kfree(hal); +} + +/*******************************\ + Reset Functions +\*******************************/ + +/* + * Main reset function + */ +int ath5k_hw_reset(struct ath_hw *hal, enum ieee80211_if_types op_mode, + struct ieee80211_channel *channel, bool change_channel) +{ + const struct ath5k_rate_table *rt; + struct ath5k_eeprom_info *ee = &hal->ah_capabilities.cap_eeprom; + u32 data, noise_floor, s_seq, s_ant, s_led[3]; + u8 mac[ETH_ALEN]; + unsigned int i, mode, freq, ee_mode, ant[2]; + int ret; + + AR5K_TRACE; + + s_seq = 0; + s_ant = 1; + ee_mode = 0; + freq = 0; + mode = 0; + + /* + * Save some registers before a reset + */ + /*DCU/Antenna selection not available on 5210*/ + if (hal->ah_version != AR5K_AR5210) { + if (change_channel == true) { + /* Seq number for queue 0 -do this for all queues ? */ + s_seq = ath5k_hw_reg_read(hal, + AR5K_QUEUE_DFS_SEQNUM(0)); + /*Default antenna*/ + s_ant = ath5k_hw_reg_read(hal, AR5K_DEFAULT_ANTENNA); + } + } + + /*GPIOs*/ + s_led[0] = ath5k_hw_reg_read(hal, AR5K_PCICFG) & AR5K_PCICFG_LEDSTATE; + s_led[1] = ath5k_hw_reg_read(hal, AR5K_GPIOCR); + s_led[2] = ath5k_hw_reg_read(hal, AR5K_GPIODO); + + if (change_channel == true && hal->ah_rf_banks != NULL) + ath5k_hw_get_rf_gain(hal); + + + /*Wakeup the device*/ + ret = ath5k_hw_nic_wakeup(hal, channel->val, false); + if (ret) + return ret; + + /* + * Initialize operating mode + */ + hal->ah_op_mode = op_mode; + + /* + * 5111/5112 Settings + * 5210 only comes with RF5110 + */ + if (hal->ah_version != AR5K_AR5210) { + if (hal->ah_radio != AR5K_RF5111 && + hal->ah_radio != AR5K_RF5112) { + AR5K_PRINTF("invalid phy radio: %u\n", hal->ah_radio); + return -EINVAL; + } + + switch (channel->val & CHANNEL_MODES) { + case CHANNEL_A: + mode = AR5K_INI_VAL_11A; + freq = AR5K_INI_RFGAIN_5GHZ; + ee_mode = AR5K_EEPROM_MODE_11A; + break; + case CHANNEL_B: + mode = AR5K_INI_VAL_11B; + freq = AR5K_INI_RFGAIN_2GHZ; + ee_mode = AR5K_EEPROM_MODE_11B; + break; + /* Is this ok on 5211 too ? */ + case CHANNEL_G: + mode = AR5K_INI_VAL_11G; + freq = AR5K_INI_RFGAIN_2GHZ; + ee_mode = AR5K_EEPROM_MODE_11G; + break; + case CHANNEL_T: + mode = AR5K_INI_VAL_11A_TURBO; + freq = AR5K_INI_RFGAIN_5GHZ; + ee_mode = AR5K_EEPROM_MODE_11A; + break; + /*Is this ok on 5211 too ?*/ + case CHANNEL_TG: + mode = AR5K_INI_VAL_11G_TURBO; + freq = AR5K_INI_RFGAIN_2GHZ; + ee_mode = AR5K_EEPROM_MODE_11G; + break; + case CHANNEL_XR: + if (hal->ah_version == AR5K_AR5211) { + AR5K_PRINTF("XR mode not available on 5211"); + return -EINVAL; + } + mode = AR5K_INI_VAL_XR; + freq = AR5K_INI_RFGAIN_5GHZ; + ee_mode = AR5K_EEPROM_MODE_11A; + break; + default: + AR5K_PRINTF("invalid channel: %d\n", channel->freq); + return -EINVAL; + } + + /* PHY access enable */ + ath5k_hw_reg_write(hal, AR5K_PHY_SHIFT_5GHZ, AR5K_PHY(0)); + + } + + ath5k_hw_write_initvals(hal, mode, change_channel); + + /* + * 5211/5212 Specific + */ + if (hal->ah_version != AR5K_AR5210) { + /* + * Write initial RF gain settings + * This should work for both 5111/5112 + */ + ret = ath5k_hw_rfgain(hal, freq); + if (ret) + return ret; + + mdelay(1); + + /* + * Set rate duration table on 5212 + */ + if (hal->ah_version == AR5K_AR5212) { + + /*For 802.11b*/ + if (!(channel->val & CHANNEL_B)) { + + /*Get rate table for this operation mode*/ + rt = ath5k_hw_get_rate_table(hal, + MODE_IEEE80211B); + + /*Write rate duration table*/ + for (i = 0; i < rt->rate_count; i++) { + data = AR5K_RATE_DUR(rt->rates[i].rate_code); + ath5k_hw_reg_write(hal, + ath_hal_computetxtime(hal, rt, + 14, rt->rates[i].control_rate, + false), data); + if (HAS_SHPREAMBLE(i)) + ath5k_hw_reg_write(hal, + ath_hal_computetxtime(hal, + rt, 14, + rt->rates[i].control_rate, + false), data + + (AR5K_SET_SHORT_PREAMBLE << 2)); + } + + } else { + /* For 802.11a/g Turbo/XR mode (AR5K_MODE_XR here is + * O.K. for both a/g - OFDM) */ + + /* Get rate table for this operation mode */ + rt = ath5k_hw_get_rate_table(hal, + channel->val & CHANNEL_TURBO ? + MODE_ATHEROS_TURBO : MODE_ATHEROS_TURBOG); + + /* Write rate duration table */ + for (i = 0; i < rt->rate_count; i++) + ath5k_hw_reg_write(hal, + ath_hal_computetxtime(hal, rt, + 14, rt->rates[i].control_rate, + false), + AR5K_RATE_DUR(rt->rates[i].rate_code)); + + } + } + + /* Fix for first revision of the RF5112 RF chipset */ + if (hal->ah_radio >= AR5K_RF5112 && + hal->ah_radio_5ghz_revision < + AR5K_SREV_RAD_5112A) { + ath5k_hw_reg_write(hal, AR5K_PHY_CCKTXCTL_WORLD, + AR5K_PHY_CCKTXCTL); + if (channel->val & CHANNEL_A) + data = 0xffb81020; + else + data = 0xffb80d20; + ath5k_hw_reg_write(hal, data, AR5K_PHY_FRAME_CTL); + } + + /* + * Set TX power (XXX use txpower from net80211) + */ + ret = ath5k_hw_txpower(hal, channel, AR5K_TUNE_DEFAULT_TXPOWER); + if (ret) + return ret; + + /* + * Write RF registers + * TODO:Does this work on 5211 (5111) ? + */ + ret = ath5k_hw_rfregs(hal, channel, mode); + if (ret) + return ret; + + /* + * Configure additional registers + */ + + /* Write OFDM timings on 5212*/ + if (hal->ah_version == AR5K_AR5212) { + if (channel->val & CHANNEL_OFDM) { + u32 coef_scaled, coef_exp, coef_man, + ds_coef_exp, ds_coef_man, clock; + + clock = channel->val & CHANNEL_T ? 80 : 40; + coef_scaled = ((5 * (clock << 24)) / 2) / + channel->freq; + + for (coef_exp = 31; coef_exp > 0; coef_exp--) + if ((coef_scaled >> coef_exp) & 0x1) + break; + + if (!coef_exp) + return -EINVAL; + + coef_exp = 14 - (coef_exp - 24); + coef_man = coef_scaled + + (1 << (24 - coef_exp - 1)); + ds_coef_man = coef_man >> (24 - coef_exp); + ds_coef_exp = coef_exp - 16; + + AR5K_REG_WRITE_BITS(hal, AR5K_PHY_TIMING_3, + AR5K_PHY_TIMING_3_DSC_MAN, ds_coef_man); + AR5K_REG_WRITE_BITS(hal, AR5K_PHY_TIMING_3, + AR5K_PHY_TIMING_3_DSC_EXP, ds_coef_exp); + } + } + + /*Enable/disable 802.11b mode on 5111 + (enable 2111 frequency converter + CCK)*/ + if (hal->ah_radio == AR5K_RF5111) { + if (channel->val & CHANNEL_B) + AR5K_REG_ENABLE_BITS(hal, AR5K_TXCFG, + AR5K_TXCFG_B_MODE); + else + AR5K_REG_DISABLE_BITS(hal, AR5K_TXCFG, + AR5K_TXCFG_B_MODE); + } + + /* Set antenna mode */ + AR5K_REG_MASKED_BITS(hal, AR5K_PHY(0x44), + hal->ah_antenna[ee_mode][0], 0xfffffc06); + + if (freq == AR5K_INI_RFGAIN_2GHZ) + ant[0] = ant[1] = AR5K_ANT_FIXED_B; + else + ant[0] = ant[1] = AR5K_ANT_FIXED_A; + + ath5k_hw_reg_write(hal, hal->ah_antenna[ee_mode][ant[0]], + AR5K_PHY_ANT_SWITCH_TABLE_0); + ath5k_hw_reg_write(hal, hal->ah_antenna[ee_mode][ant[1]], + AR5K_PHY_ANT_SWITCH_TABLE_1); + + /* Commit values from EEPROM */ + if (hal->ah_radio == AR5K_RF5111) + AR5K_REG_WRITE_BITS(hal, AR5K_PHY_FRAME_CTL, + AR5K_PHY_FRAME_CTL_TX_CLIP, ee->ee_tx_clip); + + ath5k_hw_reg_write(hal, + AR5K_PHY_NF_SVAL(ee->ee_noise_floor_thr[ee_mode]), + AR5K_PHY(0x5a)); + + AR5K_REG_MASKED_BITS(hal, AR5K_PHY(0x11), + (ee->ee_switch_settling[ee_mode] << 7) & 0x3f80, + 0xffffc07f); + AR5K_REG_MASKED_BITS(hal, AR5K_PHY(0x12), + (ee->ee_ant_tx_rx[ee_mode] << 12) & 0x3f000, + 0xfffc0fff); + AR5K_REG_MASKED_BITS(hal, AR5K_PHY(0x14), + (ee->ee_adc_desired_size[ee_mode] & 0x00ff) | + ((ee->ee_pga_desired_size[ee_mode] << 8) & 0xff00), + 0xffff0000); + + ath5k_hw_reg_write(hal, + (ee->ee_tx_end2xpa_disable[ee_mode] << 24) | + (ee->ee_tx_end2xpa_disable[ee_mode] << 16) | + (ee->ee_tx_frm2xpa_enable[ee_mode] << 8) | + (ee->ee_tx_frm2xpa_enable[ee_mode]), AR5K_PHY(0x0d)); + + AR5K_REG_MASKED_BITS(hal, AR5K_PHY(0x0a), + ee->ee_tx_end2xlna_enable[ee_mode] << 8, 0xffff00ff); + AR5K_REG_MASKED_BITS(hal, AR5K_PHY(0x19), + (ee->ee_thr_62[ee_mode] << 12) & 0x7f000, 0xfff80fff); + AR5K_REG_MASKED_BITS(hal, AR5K_PHY(0x49), 4, 0xffffff01); + + AR5K_REG_ENABLE_BITS(hal, AR5K_PHY_IQ, + AR5K_PHY_IQ_CORR_ENABLE | + (ee->ee_i_cal[ee_mode] << AR5K_PHY_IQ_CORR_Q_I_COFF_S) | + ee->ee_q_cal[ee_mode]); + + if (hal->ah_ee_version >= AR5K_EEPROM_VERSION_4_1) + AR5K_REG_WRITE_BITS(hal, AR5K_PHY_GAIN_2GHZ, + AR5K_PHY_GAIN_2GHZ_MARGIN_TXRX, + ee->ee_margin_tx_rx[ee_mode]); + + } else { + mdelay(1); + /* Disable phy and wait */ + ath5k_hw_reg_write(hal, AR5K_PHY_ACT_DISABLE, AR5K_PHY_ACT); + mdelay(1); + } + + /* + * Restore saved values + */ + /*DCU/Antenna selection not available on 5210*/ + if (hal->ah_version != AR5K_AR5210) { + ath5k_hw_reg_write(hal, s_seq, AR5K_QUEUE_DFS_SEQNUM(0)); + ath5k_hw_reg_write(hal, s_ant, AR5K_DEFAULT_ANTENNA); + } + AR5K_REG_ENABLE_BITS(hal, AR5K_PCICFG, s_led[0]); + ath5k_hw_reg_write(hal, s_led[1], AR5K_GPIOCR); + ath5k_hw_reg_write(hal, s_led[2], AR5K_GPIODO); + + /* + * Misc + */ + memset(mac, 0xff, ETH_ALEN); + ath5k_hw_set_associd(hal, mac, 0); + ath5k_hw_set_opmode(hal); + /*PISR/SISR Not available on 5210*/ + if (hal->ah_version != AR5K_AR5210) { + ath5k_hw_reg_write(hal, 0xffffffff, AR5K_PISR); + /* XXX: AR5K_RSSI_THR has masks and shifts defined for it, so + * direct write using ath5k_hw_reg_write seems wrong. Test with: + * AR5K_REG_WRITE_BITS(hal, AR5K_RSSI_THR, + * AR5K_RSSI_THR_BMISS, AR5K_TUNE_RSSI_THRES); + * with different variables and check results compared + * to ath5k_hw_reg_write(hal, ) */ + ath5k_hw_reg_write(hal, AR5K_TUNE_RSSI_THRES, AR5K_RSSI_THR); + } + + /* + * Set Rx/Tx DMA Configuration + *(passing dma size not available on 5210) + */ + if (hal->ah_version != AR5K_AR5210) { + AR5K_REG_WRITE_BITS(hal, AR5K_TXCFG, AR5K_TXCFG_SDMAMR, + AR5K_DMASIZE_512B | AR5K_TXCFG_DMASIZE); + AR5K_REG_WRITE_BITS(hal, AR5K_RXCFG, AR5K_RXCFG_SDMAMW, + AR5K_DMASIZE_512B); + } + + /* + * Set channel and calibrate the PHY + */ + ret = ath5k_hw_channel(hal, channel); + if (ret) + return ret; + + /* + * Enable the PHY and wait until completion + */ + ath5k_hw_reg_write(hal, AR5K_PHY_ACT_ENABLE, AR5K_PHY_ACT); + + /* + * 5111/5112 Specific + */ + if (hal->ah_version != AR5K_AR5210) { + data = ath5k_hw_reg_read(hal, AR5K_PHY_RX_DELAY) & + AR5K_PHY_RX_DELAY_M; + data = (channel->val & CHANNEL_CCK) ? + ((data << 2) / 22) : (data / 10); + + udelay(100 + data); + } else { + mdelay(1); + } + + /* + * Enable calibration and wait until completion + */ + AR5K_REG_ENABLE_BITS(hal, AR5K_PHY_AGCCTL, + AR5K_PHY_AGCCTL_CAL); + + if (ath5k_hw_register_timeout(hal, AR5K_PHY_AGCCTL, + AR5K_PHY_AGCCTL_CAL, 0, false)) { + AR5K_PRINTF("calibration timeout (%uMHz)\n", channel->freq); + return -EAGAIN; + } + + /* + * Enable noise floor calibration and wait until completion + */ + AR5K_REG_ENABLE_BITS(hal, AR5K_PHY_AGCCTL, + AR5K_PHY_AGCCTL_NF); + + if (ath5k_hw_register_timeout(hal, AR5K_PHY_AGCCTL, + AR5K_PHY_AGCCTL_NF, 0, false)) { + AR5K_PRINTF("noise floor calibration timeout (%uMHz)\n", + channel->freq); + return -EAGAIN; + } + + /* Wait until the noise floor is calibrated and read the value */ + for (i = 20; i > 0; i--) { + mdelay(1); + noise_floor = ath5k_hw_reg_read(hal, AR5K_PHY_NF); + + if (AR5K_PHY_NF_RVAL(noise_floor) & + AR5K_PHY_NF_ACTIVE) + noise_floor = AR5K_PHY_NF_AVAL(noise_floor); + + if (noise_floor <= AR5K_TUNE_NOISE_FLOOR) + break; + } + + if (noise_floor > AR5K_TUNE_NOISE_FLOOR) { + AR5K_PRINTF("noise floor calibration failed (%uMHz)\n", + channel->freq); + return -EIO; + } + + hal->ah_calibration = false; + + if (!(channel->val & CHANNEL_B)) { + hal->ah_calibration = true; + AR5K_REG_WRITE_BITS(hal, AR5K_PHY_IQ, + AR5K_PHY_IQ_CAL_NUM_LOG_MAX, 15); + AR5K_REG_ENABLE_BITS(hal, AR5K_PHY_IQ, + AR5K_PHY_IQ_RUN); + } + + /* + * Reset queues and start beacon timers at the end of the reset routine + */ + for (i = 0; i < hal->ah_capabilities.cap_queues.q_tx_num; i++) { + /*No QCU on 5210*/ + if (hal->ah_version != AR5K_AR5210) + AR5K_REG_WRITE_Q(hal, AR5K_QUEUE_QCUMASK(i), i); + + ret = ath5k_hw_reset_tx_queue(hal, i); + if (ret) { + AR5K_PRINTF("failed to reset TX queue #%d\n", i); + return ret; + } + } + + /* Pre-enable interrupts on 5211/5212*/ + if (hal->ah_version != AR5K_AR5210) + ath5k_hw_set_intr(hal, AR5K_INT_RX | AR5K_INT_TX | + AR5K_INT_FATAL); + + /* + * Set RF kill flags if supported by the device (read from the EEPROM) + * Disable gpio_intr for now since it results system hang. + * TODO: Handle this in ath_intr + */ +#if 0 + if (AR5K_EEPROM_HDR_RFKILL(hal->ah_capabilities.cap_eeprom.ee_header)) { + ath5k_hw_set_gpio_input(hal, 0); + hal->ah_gpio[0] = ath5k_hw_get_gpio(hal, 0); + if (hal->ah_gpio[0] == 0) + ath5k_hw_set_gpio_intr(hal, 0, 1); + else + ath5k_hw_set_gpio_intr(hal, 0, 0); + } +#endif + + /* + * Set the 32MHz reference clock on 5212 phy clock sleep register + */ + if (hal->ah_version == AR5K_AR5212) { + ath5k_hw_reg_write(hal, AR5K_PHY_SCR_32MHZ, AR5K_PHY_SCR); + ath5k_hw_reg_write(hal, AR5K_PHY_SLMT_32MHZ, AR5K_PHY_SLMT); + ath5k_hw_reg_write(hal, AR5K_PHY_SCAL_32MHZ, AR5K_PHY_SCAL); + ath5k_hw_reg_write(hal, AR5K_PHY_SCLOCK_32MHZ, AR5K_PHY_SCLOCK); + ath5k_hw_reg_write(hal, AR5K_PHY_SDELAY_32MHZ, AR5K_PHY_SDELAY); + ath5k_hw_reg_write(hal, hal->ah_radio == AR5K_RF5111 ? + AR5K_PHY_SPENDING_RF5111 : AR5K_PHY_SPENDING_RF5112, + AR5K_PHY_SPENDING); + } + + /* + * Disable beacons and reset the register + */ + AR5K_REG_DISABLE_BITS(hal, AR5K_BEACON, AR5K_BEACON_ENABLE | + AR5K_BEACON_RESET_TSF); + + return 0; +} + +/* + * Reset chipset + */ +static int ath5k_hw_nic_reset(struct ath_hw *hal, u32 val) +{ + int ret; + u32 mask = val ? val : ~0; + + AR5K_TRACE; + + /* Read-and-clear RX Descriptor Pointer*/ + ath5k_hw_reg_read(hal, AR5K_RXDP); + + /* + * Reset the device and wait until success + */ + ath5k_hw_reg_write(hal, val, AR5K_RESET_CTL); + + /* Wait at least 128 PCI clocks */ + udelay(15); + + if (hal->ah_version == AR5K_AR5210) { + val &= AR5K_RESET_CTL_CHIP; + mask &= AR5K_RESET_CTL_CHIP; + } else { + val &= AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_BASEBAND; + mask &= AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_BASEBAND; + } + + ret = ath5k_hw_register_timeout(hal, AR5K_RESET_CTL, mask, val, false); + + /* + * Reset configuration register (for hw byte-swap) + */ + if ((val & AR5K_RESET_CTL_PCU) == 0) + ath5k_hw_reg_write(hal, AR5K_INIT_CFG, AR5K_CFG); + + return ret; +} + +/* + * Power management functions + */ + +/* + * Sleep control + */ +int ath5k_hw_set_power(struct ath_hw *hal, enum ath5k_power_mode mode, + bool set_chip, u16 sleep_duration) +{ + unsigned int i; + u32 staid; + + AR5K_TRACE; + staid = ath5k_hw_reg_read(hal, AR5K_STA_ID1); + + switch (mode) { + case AR5K_PM_AUTO: + staid &= ~AR5K_STA_ID1_DEFAULT_ANTENNA; + /* fallthrough */ + case AR5K_PM_NETWORK_SLEEP: + if (set_chip == true) + ath5k_hw_reg_write(hal, + AR5K_SLEEP_CTL_SLE | sleep_duration, + AR5K_SLEEP_CTL); + + staid |= AR5K_STA_ID1_PWR_SV; + break; + + case AR5K_PM_FULL_SLEEP: + if (set_chip == true) + ath5k_hw_reg_write(hal, AR5K_SLEEP_CTL_SLE_SLP, + AR5K_SLEEP_CTL); + + staid |= AR5K_STA_ID1_PWR_SV; + break; + + case AR5K_PM_AWAKE: + if (set_chip == false) + goto commit; + + ath5k_hw_reg_write(hal, AR5K_SLEEP_CTL_SLE_WAKE, + AR5K_SLEEP_CTL); + + for (i = 5000; i > 0; i--) { + /* Check if the chip did wake up */ + if ((ath5k_hw_reg_read(hal, AR5K_PCICFG) & + AR5K_PCICFG_SPWR_DN) == 0) + break; + + /* Wait a bit and retry */ + udelay(200); + ath5k_hw_reg_write(hal, AR5K_SLEEP_CTL_SLE_WAKE, + AR5K_SLEEP_CTL); + } + + /* Fail if the chip didn't wake up */ + if (i <= 0) + return -EIO; + + staid &= ~AR5K_STA_ID1_PWR_SV; + break; + + default: + return -EINVAL; + } + +commit: + hal->ah_power_mode = mode; + ath5k_hw_reg_write(hal, staid, AR5K_STA_ID1); + + return 0; +} + +/***********************\ + DMA Related Functions +\***********************/ + +/* + * Receive functions + */ + +/* + * Start DMA receive + */ +void ath5k_hw_start_rx(struct ath_hw *hal) +{ + AR5K_TRACE; + ath5k_hw_reg_write(hal, AR5K_CR_RXE, AR5K_CR); +} + +/* + * Stop DMA receive + */ +int ath5k_hw_stop_rx_dma(struct ath_hw *hal) +{ + unsigned int i; + + AR5K_TRACE; + ath5k_hw_reg_write(hal, AR5K_CR_RXD, AR5K_CR); + + /* + * It may take some time to disable the DMA receive unit + */ + for (i = 2000; i > 0 && + (ath5k_hw_reg_read(hal, AR5K_CR) & AR5K_CR_RXE) != 0; + i--) + udelay(10); + + return i ? 0 : -EBUSY; +} + +/* + * Get the address of the RX Descriptor + */ +u32 ath5k_hw_get_rx_buf(struct ath_hw *hal) +{ + return ath5k_hw_reg_read(hal, AR5K_RXDP); +} + +/* + * Set the address of the RX Descriptor + */ +void ath5k_hw_put_rx_buf(struct ath_hw *hal, u32 phys_addr) +{ + AR5K_TRACE; + + /*TODO:Shouldn't we check if RX is enabled first ?*/ + ath5k_hw_reg_write(hal, phys_addr, AR5K_RXDP); +} + +/* + * Transmit functions + */ + +/* + * Start DMA transmit for a specific queue + * (see also QCU/DCU functions) + */ +int ath5k_hw_tx_start(struct ath_hw *hal, unsigned int queue) +{ + u32 tx_queue; + + AR5K_TRACE; + AR5K_ASSERT_ENTRY(queue, hal->ah_capabilities.cap_queues.q_tx_num); + + /* Return if queue is declared inactive */ + if (hal->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE) + return -EIO; + + if (hal->ah_version == AR5K_AR5210) { + tx_queue = ath5k_hw_reg_read(hal, AR5K_CR); + + /* + * Set the queue by type on 5210 + */ + switch (hal->ah_txq[queue].tqi_type) { + case AR5K_TX_QUEUE_DATA: + tx_queue |= AR5K_CR_TXE0 & ~AR5K_CR_TXD0; + break; + case AR5K_TX_QUEUE_BEACON: + tx_queue |= AR5K_CR_TXE1 & ~AR5K_CR_TXD1; + ath5k_hw_reg_write(hal, AR5K_BCR_TQ1V | AR5K_BCR_BDMAE, + AR5K_BSR); + break; + case AR5K_TX_QUEUE_CAB: + tx_queue |= AR5K_CR_TXE1 & ~AR5K_CR_TXD1; + ath5k_hw_reg_write(hal, AR5K_BCR_TQ1FV | AR5K_BCR_TQ1V | + AR5K_BCR_BDMAE, AR5K_BSR); + break; + default: + return -EINVAL; + } + /* Start queue */ + ath5k_hw_reg_write(hal, tx_queue, AR5K_CR); + } else { + /* Return if queue is disabled */ + if (AR5K_REG_READ_Q(hal, AR5K_QCU_TXD, queue)) + return -EIO; + + /* Start queue */ + AR5K_REG_WRITE_Q(hal, AR5K_QCU_TXE, queue); + } + + return 0; +} + +/* + * Stop DMA transmit for a specific queue + * (see also QCU/DCU functions) + */ +int ath5k_hw_stop_tx_dma(struct ath_hw *hal, unsigned int queue) +{ + unsigned int i = 100; + u32 tx_queue, pending; + + AR5K_TRACE; + AR5K_ASSERT_ENTRY(queue, hal->ah_capabilities.cap_queues.q_tx_num); + + /* Return if queue is declared inactive */ + if (hal->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE) + return -EIO; + + if (hal->ah_version == AR5K_AR5210) { + tx_queue = ath5k_hw_reg_read(hal, AR5K_CR); + + /* + * Set by queue type + */ + switch (hal->ah_txq[queue].tqi_type) { + case AR5K_TX_QUEUE_DATA: + tx_queue |= AR5K_CR_TXD0 & ~AR5K_CR_TXE0; + break; + case AR5K_TX_QUEUE_BEACON: + case AR5K_TX_QUEUE_CAB: + /* XXX Fix me... */ + tx_queue |= AR5K_CR_TXD1 & ~AR5K_CR_TXD1; + ath5k_hw_reg_write(hal, 0, AR5K_BSR); + break; + default: + return -EINVAL; + } + + /* Stop queue */ + ath5k_hw_reg_write(hal, tx_queue, AR5K_CR); + } else { + /* + * Schedule TX disable and wait until queue is empty + */ + AR5K_REG_WRITE_Q(hal, AR5K_QCU_TXD, queue); + + /*Check for pending frames*/ + do { + pending = ath5k_hw_reg_read(hal, + AR5K_QUEUE_STATUS(queue)) & + AR5K_QCU_STS_FRMPENDCNT; + udelay(100); + } while (--i && pending); + + /* Clear register */ + ath5k_hw_reg_write(hal, 0, AR5K_QCU_TXD); + } + + /* TODO: Check for success else return error */ + return 0; +} + +/* + * Get the address of the TX Descriptor for a specific queue + * (see also QCU/DCU functions) + */ +u32 ath5k_hw_get_tx_buf(struct ath_hw *hal, unsigned int queue) +{ + u16 tx_reg; + + AR5K_TRACE; + AR5K_ASSERT_ENTRY(queue, hal->ah_capabilities.cap_queues.q_tx_num); + + /* + * Get the transmit queue descriptor pointer from the selected queue + */ + /*5210 doesn't have QCU*/ + if (hal->ah_version == AR5K_AR5210) { + switch (hal->ah_txq[queue].tqi_type) { + case AR5K_TX_QUEUE_DATA: + tx_reg = AR5K_NOQCU_TXDP0; + break; + case AR5K_TX_QUEUE_BEACON: + case AR5K_TX_QUEUE_CAB: + tx_reg = AR5K_NOQCU_TXDP1; + break; + default: + return 0xffffffff; + } + } else { + tx_reg = AR5K_QUEUE_TXDP(queue); + } + + return ath5k_hw_reg_read(hal, tx_reg); +} + +/* + * Set the address of the TX Descriptor for a specific queue + * (see also QCU/DCU functions) + */ +int ath5k_hw_put_tx_buf(struct ath_hw *hal, unsigned int queue, u32 phys_addr) +{ + u16 tx_reg; + + AR5K_TRACE; + AR5K_ASSERT_ENTRY(queue, hal->ah_capabilities.cap_queues.q_tx_num); + + /* + * Set the transmit queue descriptor pointer register by type + * on 5210 + */ + if (hal->ah_version == AR5K_AR5210) { + switch (hal->ah_txq[queue].tqi_type) { + case AR5K_TX_QUEUE_DATA: + tx_reg = AR5K_NOQCU_TXDP0; + break; + case AR5K_TX_QUEUE_BEACON: + case AR5K_TX_QUEUE_CAB: + tx_reg = AR5K_NOQCU_TXDP1; + break; + default: + return -EINVAL; + } + } else { + /* + * Set the transmit queue descriptor pointer for + * the selected queue on QCU for 5211+ + * (this won't work if the queue is still active) + */ + if (AR5K_REG_READ_Q(hal, AR5K_QCU_TXE, queue)) + return -EIO; + + tx_reg = AR5K_QUEUE_TXDP(queue); + } + + /* Set descriptor pointer */ + ath5k_hw_reg_write(hal, phys_addr, tx_reg); + + return 0; +} + +/* + * Update tx trigger level + */ +int ath5k_hw_update_tx_triglevel(struct ath_hw *hal, bool increase) +{ + u32 trigger_level, imr; + int ret = -EIO; + + AR5K_TRACE; + + /* + * Disable interrupts by setting the mask + */ + imr = ath5k_hw_set_intr(hal, hal->ah_imr & ~AR5K_INT_GLOBAL); + + /*TODO: Boundary check on trigger_level*/ + trigger_level = AR5K_REG_MS(ath5k_hw_reg_read(hal, AR5K_TXCFG), + AR5K_TXCFG_TXFULL); + + if (increase == false) { + if (--trigger_level < AR5K_TUNE_MIN_TX_FIFO_THRES) + goto done; + } else + trigger_level += + ((AR5K_TUNE_MAX_TX_FIFO_THRES - trigger_level) / 2); + + /* + * Update trigger level on success + */ + if (hal->ah_version == AR5K_AR5210) + ath5k_hw_reg_write(hal, trigger_level, AR5K_TRIG_LVL); + else + AR5K_REG_WRITE_BITS(hal, AR5K_TXCFG, + AR5K_TXCFG_TXFULL, trigger_level); + + ret = 0; + +done: + /* + * Restore interrupt mask + */ + ath5k_hw_set_intr(hal, imr); + + return ret; +} + +/* + * Interrupt handling + */ + +/* + * Check if we have pending interrupts + */ +bool ath5k_hw_is_intr_pending(struct ath_hw *hal) +{ + AR5K_TRACE; + return ath5k_hw_reg_read(hal, AR5K_INTPEND); +} + +/* + * Get interrupt mask (ISR) + */ +int ath5k_hw_get_isr(struct ath_hw *hal, enum ath5k_int *interrupt_mask) +{ + u32 data; + + AR5K_TRACE; + + /* + * Read interrupt status from the Interrupt Status register + * on 5210 + */ + if (hal->ah_version == AR5K_AR5210) { + data = ath5k_hw_reg_read(hal, AR5K_ISR); + if (unlikely(data == AR5K_INT_NOCARD)) { + *interrupt_mask = data; + return -ENODEV; + } + } + + /* + * Read interrupt status from the Read-And-Clear shadow register + */ + data = ath5k_hw_reg_read(hal, AR5K_RAC_PISR); + + /* + * Get abstract interrupt mask (HAL-compatible) + */ + *interrupt_mask = (data & AR5K_INT_COMMON) & hal->ah_imr; + + if (unlikely(data == AR5K_INT_NOCARD)) + return -ENODEV; + + if (data & (AR5K_ISR_RXOK | AR5K_ISR_RXERR)) + *interrupt_mask |= AR5K_INT_RX; + + if (data & (AR5K_ISR_TXOK | AR5K_ISR_TXERR)) + *interrupt_mask |= AR5K_INT_TX; + + if (hal->ah_version != AR5K_AR5210) { + /*HIU = Host Interface Unit (PCI etc)*/ + if (unlikely(data & (AR5K_ISR_HIUERR))) + *interrupt_mask |= AR5K_INT_FATAL; + + /*Beacon Not Ready*/ + if (unlikely(data & (AR5K_ISR_BNR))) + *interrupt_mask |= AR5K_INT_BNR; + } + + /* + * XXX: BMISS interrupts may occur after association. + * I found this on 5210 code but it needs testing + */ +#if 0 + interrupt_mask &= ~AR5K_INT_BMISS; +#endif + + /* + * In case we didn't handle anything, + * print the register value. + */ + if (unlikely(*interrupt_mask == 0 && net_ratelimit())) + AR5K_PRINTF("0x%08x\n", data); + + return 0; +} + +/* + * Set interrupt mask + */ +enum ath5k_int ath5k_hw_set_intr(struct ath_hw *hal, enum ath5k_int new_mask) +{ + enum ath5k_int old_mask, int_mask; + + /* + * Disable card interrupts to prevent any race conditions + * (they will be re-enabled afterwards). + */ + ath5k_hw_reg_write(hal, AR5K_IER_DISABLE, AR5K_IER); + + old_mask = hal->ah_imr; + + /* + * Add additional, chipset-dependent interrupt mask flags + * and write them to the IMR (interrupt mask register). + */ + int_mask = new_mask & AR5K_INT_COMMON; + + if (new_mask & AR5K_INT_RX) + int_mask |= AR5K_IMR_RXOK | AR5K_IMR_RXERR | AR5K_IMR_RXORN | + AR5K_IMR_RXDESC; + + if (new_mask & AR5K_INT_TX) + int_mask |= AR5K_IMR_TXOK | AR5K_IMR_TXERR | AR5K_IMR_TXDESC | + AR5K_IMR_TXURN; + + if (hal->ah_version != AR5K_AR5210) { + if (new_mask & AR5K_INT_FATAL) { + int_mask |= AR5K_IMR_HIUERR; + AR5K_REG_ENABLE_BITS(hal, AR5K_SIMR2, AR5K_SIMR2_MCABT | + AR5K_SIMR2_SSERR | AR5K_SIMR2_DPERR); + } + } + + ath5k_hw_reg_write(hal, int_mask, AR5K_PIMR); + + /* Store new interrupt mask */ + hal->ah_imr = new_mask; + + /* ..re-enable interrupts */ + ath5k_hw_reg_write(hal, AR5K_IER_ENABLE, AR5K_IER); + + return old_mask; +} + + +/*************************\ + EEPROM access functions +\*************************/ + +/* + * Read from eeprom + */ +static int ath5k_hw_eeprom_read(struct ath_hw *hal, u32 offset, u16 *data) +{ + u32 status, timeout; + + AR5K_TRACE; + /* + * Initialize EEPROM access + */ + if (hal->ah_version == AR5K_AR5210) { + AR5K_REG_ENABLE_BITS(hal, AR5K_PCICFG, AR5K_PCICFG_EEAE); + (void)ath5k_hw_reg_read(hal, AR5K_EEPROM_BASE + (4 * offset)); + } else { + ath5k_hw_reg_write(hal, offset, AR5K_EEPROM_BASE); + AR5K_REG_ENABLE_BITS(hal, AR5K_EEPROM_CMD, + AR5K_EEPROM_CMD_READ); + } + + for (timeout = AR5K_TUNE_REGISTER_TIMEOUT; timeout > 0; timeout--) { + status = ath5k_hw_reg_read(hal, AR5K_EEPROM_STATUS); + if (status & AR5K_EEPROM_STAT_RDDONE) { + if (status & AR5K_EEPROM_STAT_RDERR) + return -EIO; + *data = (u16)(ath5k_hw_reg_read(hal, AR5K_EEPROM_DATA) & + 0xffff); + return 0; + } + udelay(15); + } + + return -ETIMEDOUT; +} + +/* + * Write to eeprom - currently disabled, use at your own risk + */ +static int ath5k_hw_eeprom_write(struct ath_hw *hal, u32 offset, u16 data) +{ +#if 0 + u32 status, timeout; + + AR5K_TRACE; + + /* + * Initialize eeprom access + */ + + if (hal->ah_version == AR5K_AR5210) { + AR5K_REG_ENABLE_BITS(hal, AR5K_PCICFG, AR5K_PCICFG_EEAE); + } else { + AR5K_REG_ENABLE_BITS(hal, AR5K_EEPROM_CMD, + AR5K_EEPROM_CMD_RESET); + } + + /* + * Write data to data register + */ + + if (hal->ah_version == AR5K_AR5210) { + ath5k_hw_reg_write(hal, data, AR5K_EEPROM_BASE + (4 * offset)); + } else { + ath5k_hw_reg_write(hal, offset, AR5K_EEPROM_BASE); + ath5k_hw_reg_write(hal, data, AR5K_EEPROM_DATA); + AR5K_REG_ENABLE_BITS(hal, AR5K_EEPROM_CMD, + AR5K_EEPROM_CMD_WRITE); + } + + /* + * Check status + */ + + for (timeout = AR5K_TUNE_REGISTER_TIMEOUT; timeout > 0; timeout--) { + status = ath5k_hw_reg_read(hal, AR5K_EEPROM_STATUS); + if (status & AR5K_EEPROM_STAT_WRDONE) { + if (status & AR5K_EEPROM_STAT_WRERR) + return EIO; + return 0; + } + udelay(15); + } +#endif + AR5K_PRINTF("EEPROM Write is disabled!"); + return -EIO; +} + +/* + * Translate binary channel representation in EEPROM to frequency + */ +static u16 ath5k_eeprom_bin2freq(struct ath_hw *hal, u16 bin, unsigned int mode) +{ + u16 val; + + if (bin == AR5K_EEPROM_CHANNEL_DIS) + return bin; + + if (mode == AR5K_EEPROM_MODE_11A) { + if (hal->ah_ee_version > AR5K_EEPROM_VERSION_3_2) + val = (5 * bin) + 4800; + else + val = bin > 62 ? (10 * 62) + (5 * (bin - 62)) + 5100 : + (bin * 10) + 5100; + } else { + if (hal->ah_ee_version > AR5K_EEPROM_VERSION_3_2) + val = bin + 2300; + else + val = bin + 2400; + } + + return val; +} + +/* + * Read antenna infos from eeprom + */ +static int ath5k_eeprom_read_ants(struct ath_hw *hal, u32 *offset, + unsigned int mode) +{ + struct ath5k_eeprom_info *ee = &hal->ah_capabilities.cap_eeprom; + u32 o = *offset; + u16 val; + int ret, i = 0; + + AR5K_EEPROM_READ(o++, val); + ee->ee_switch_settling[mode] = (val >> 8) & 0x7f; + ee->ee_ant_tx_rx[mode] = (val >> 2) & 0x3f; + ee->ee_ant_control[mode][i] = (val << 4) & 0x3f; + + AR5K_EEPROM_READ(o++, val); + ee->ee_ant_control[mode][i++] |= (val >> 12) & 0xf; + ee->ee_ant_control[mode][i++] = (val >> 6) & 0x3f; + ee->ee_ant_control[mode][i++] = val & 0x3f; + + AR5K_EEPROM_READ(o++, val); + ee->ee_ant_control[mode][i++] = (val >> 10) & 0x3f; + ee->ee_ant_control[mode][i++] = (val >> 4) & 0x3f; + ee->ee_ant_control[mode][i] = (val << 2) & 0x3f; + + AR5K_EEPROM_READ(o++, val); + ee->ee_ant_control[mode][i++] |= (val >> 14) & 0x3; + ee->ee_ant_control[mode][i++] = (val >> 8) & 0x3f; + ee->ee_ant_control[mode][i++] = (val >> 2) & 0x3f; + ee->ee_ant_control[mode][i] = (val << 4) & 0x3f; + + AR5K_EEPROM_READ(o++, val); + ee->ee_ant_control[mode][i++] |= (val >> 12) & 0xf; + ee->ee_ant_control[mode][i++] = (val >> 6) & 0x3f; + ee->ee_ant_control[mode][i++] = val & 0x3f; + + /* Get antenna modes */ + hal->ah_antenna[mode][0] = + (ee->ee_ant_control[mode][0] << 4) | 0x1; + hal->ah_antenna[mode][AR5K_ANT_FIXED_A] = + ee->ee_ant_control[mode][1] | + (ee->ee_ant_control[mode][2] << 6) | + (ee->ee_ant_control[mode][3] << 12) | + (ee->ee_ant_control[mode][4] << 18) | + (ee->ee_ant_control[mode][5] << 24); + hal->ah_antenna[mode][AR5K_ANT_FIXED_B] = + ee->ee_ant_control[mode][6] | + (ee->ee_ant_control[mode][7] << 6) | + (ee->ee_ant_control[mode][8] << 12) | + (ee->ee_ant_control[mode][9] << 18) | + (ee->ee_ant_control[mode][10] << 24); + + /* return new offset */ + *offset = o; + + return 0; +} + +/* + * Read supported modes from eeprom + */ +static int ath5k_eeprom_read_modes(struct ath_hw *hal, u32 *offset, + unsigned int mode) +{ + struct ath5k_eeprom_info *ee = &hal->ah_capabilities.cap_eeprom; + u32 o = *offset; + u16 val; + int ret; + + AR5K_EEPROM_READ(o++, val); + ee->ee_tx_end2xlna_enable[mode] = (val >> 8) & 0xff; + ee->ee_thr_62[mode] = val & 0xff; + + if (hal->ah_ee_version <= AR5K_EEPROM_VERSION_3_2) + ee->ee_thr_62[mode] = mode == AR5K_EEPROM_MODE_11A ? 15 : 28; + + AR5K_EEPROM_READ(o++, val); + ee->ee_tx_end2xpa_disable[mode] = (val >> 8) & 0xff; + ee->ee_tx_frm2xpa_enable[mode] = val & 0xff; + + AR5K_EEPROM_READ(o++, val); + ee->ee_pga_desired_size[mode] = (val >> 8) & 0xff; + + if ((val & 0xff) & 0x80) + ee->ee_noise_floor_thr[mode] = -((((val & 0xff) ^ 0xff)) + 1); + else + ee->ee_noise_floor_thr[mode] = val & 0xff; + + if (hal->ah_ee_version <= AR5K_EEPROM_VERSION_3_2) + ee->ee_noise_floor_thr[mode] = + mode == AR5K_EEPROM_MODE_11A ? -54 : -1; + + AR5K_EEPROM_READ(o++, val); + ee->ee_xlna_gain[mode] = (val >> 5) & 0xff; + ee->ee_x_gain[mode] = (val >> 1) & 0xf; + ee->ee_xpd[mode] = val & 0x1; + + if (hal->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) + ee->ee_fixed_bias[mode] = (val >> 13) & 0x1; + + if (hal->ah_ee_version >= AR5K_EEPROM_VERSION_3_3) { + AR5K_EEPROM_READ(o++, val); + ee->ee_false_detect[mode] = (val >> 6) & 0x7f; + + if (mode == AR5K_EEPROM_MODE_11A) + ee->ee_xr_power[mode] = val & 0x3f; + else { + ee->ee_ob[mode][0] = val & 0x7; + ee->ee_db[mode][0] = (val >> 3) & 0x7; + } + } + + if (hal->ah_ee_version < AR5K_EEPROM_VERSION_3_4) { + ee->ee_i_gain[mode] = AR5K_EEPROM_I_GAIN; + ee->ee_cck_ofdm_power_delta = AR5K_EEPROM_CCK_OFDM_DELTA; + } else { + ee->ee_i_gain[mode] = (val >> 13) & 0x7; + + AR5K_EEPROM_READ(o++, val); + ee->ee_i_gain[mode] |= (val << 3) & 0x38; + + if (mode == AR5K_EEPROM_MODE_11G) + ee->ee_cck_ofdm_power_delta = (val >> 3) & 0xff; + } + + if (hal->ah_ee_version >= AR5K_EEPROM_VERSION_4_0 && + mode == AR5K_EEPROM_MODE_11A) { + ee->ee_i_cal[mode] = (val >> 8) & 0x3f; + ee->ee_q_cal[mode] = (val >> 3) & 0x1f; + } + + if (hal->ah_ee_version >= AR5K_EEPROM_VERSION_4_6 && + mode == AR5K_EEPROM_MODE_11G) + ee->ee_scaled_cck_delta = (val >> 11) & 0x1f; + + /* return new offset */ + *offset = o; + + return 0; +} + +/* + * Initialize eeprom & capabilities structs + */ +static int ath5k_eeprom_init(struct ath_hw *hal) +{ + struct ath5k_eeprom_info *ee = &hal->ah_capabilities.cap_eeprom; + unsigned int mode, i; + int ret; + u32 offset; + u16 val; + + /* Initial TX thermal adjustment values */ + ee->ee_tx_clip = 4; + ee->ee_pwd_84 = ee->ee_pwd_90 = 1; + ee->ee_gain_select = 1; + + /* + * Read values from EEPROM and store them in the capability structure + */ + AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MAGIC, ee_magic); + AR5K_EEPROM_READ_HDR(AR5K_EEPROM_PROTECT, ee_protect); + AR5K_EEPROM_READ_HDR(AR5K_EEPROM_REG_DOMAIN, ee_regdomain); + AR5K_EEPROM_READ_HDR(AR5K_EEPROM_VERSION, ee_version); + AR5K_EEPROM_READ_HDR(AR5K_EEPROM_HDR, ee_header); + + /* Return if we have an old EEPROM */ + if (hal->ah_ee_version < AR5K_EEPROM_VERSION_3_0) + return 0; + +#ifdef notyet + /* + * Validate the checksum of the EEPROM date. There are some + * devices with invalid EEPROMs. + */ + for (cksum = 0, offset = 0; offset < AR5K_EEPROM_INFO_MAX; offset++) { + AR5K_EEPROM_READ(AR5K_EEPROM_INFO(offset), val); + cksum ^= val; + } + if (cksum != AR5K_EEPROM_INFO_CKSUM) { + AR5K_PRINTF("Invalid EEPROM checksum 0x%04x\n", cksum); + return -EIO; + } +#endif + + AR5K_EEPROM_READ_HDR(AR5K_EEPROM_ANT_GAIN(hal->ah_ee_version), + ee_ant_gain); + + if (hal->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) { + AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC0, ee_misc0); + AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC1, ee_misc1); + } + + if (hal->ah_ee_version < AR5K_EEPROM_VERSION_3_3) { + AR5K_EEPROM_READ(AR5K_EEPROM_OBDB0_2GHZ, val); + ee->ee_ob[AR5K_EEPROM_MODE_11B][0] = val & 0x7; + ee->ee_db[AR5K_EEPROM_MODE_11B][0] = (val >> 3) & 0x7; + + AR5K_EEPROM_READ(AR5K_EEPROM_OBDB1_2GHZ, val); + ee->ee_ob[AR5K_EEPROM_MODE_11G][0] = val & 0x7; + ee->ee_db[AR5K_EEPROM_MODE_11G][0] = (val >> 3) & 0x7; + } + + /* + * Get conformance test limit values + */ + offset = AR5K_EEPROM_CTL(hal->ah_ee_version); + ee->ee_ctls = AR5K_EEPROM_N_CTLS(hal->ah_ee_version); + + for (i = 0; i < ee->ee_ctls; i++) { + AR5K_EEPROM_READ(offset++, val); + ee->ee_ctl[i] = (val >> 8) & 0xff; + ee->ee_ctl[i + 1] = val & 0xff; + } + + /* + * Get values for 802.11a (5GHz) + */ + mode = AR5K_EEPROM_MODE_11A; + + ee->ee_turbo_max_power[mode] = + AR5K_EEPROM_HDR_T_5GHZ_DBM(ee->ee_header); + + offset = AR5K_EEPROM_MODES_11A(hal->ah_ee_version); + + ret = ath5k_eeprom_read_ants(hal, &offset, mode); + if (ret) + return ret; + + AR5K_EEPROM_READ(offset++, val); + ee->ee_adc_desired_size[mode] = (s8)((val >> 8) & 0xff); + ee->ee_ob[mode][3] = (val >> 5) & 0x7; + ee->ee_db[mode][3] = (val >> 2) & 0x7; + ee->ee_ob[mode][2] = (val << 1) & 0x7; + + AR5K_EEPROM_READ(offset++, val); + ee->ee_ob[mode][2] |= (val >> 15) & 0x1; + ee->ee_db[mode][2] = (val >> 12) & 0x7; + ee->ee_ob[mode][1] = (val >> 9) & 0x7; + ee->ee_db[mode][1] = (val >> 6) & 0x7; + ee->ee_ob[mode][0] = (val >> 3) & 0x7; + ee->ee_db[mode][0] = val & 0x7; + + ret = ath5k_eeprom_read_modes(hal, &offset, mode); + if (ret) + return ret; + + if (hal->ah_ee_version >= AR5K_EEPROM_VERSION_4_1) { + AR5K_EEPROM_READ(offset++, val); + ee->ee_margin_tx_rx[mode] = val & 0x3f; + } + + /* + * Get values for 802.11b (2.4GHz) + */ + mode = AR5K_EEPROM_MODE_11B; + offset = AR5K_EEPROM_MODES_11B(hal->ah_ee_version); + + ret = ath5k_eeprom_read_ants(hal, &offset, mode); + if (ret) + return ret; + + AR5K_EEPROM_READ(offset++, val); + ee->ee_adc_desired_size[mode] = (s8)((val >> 8) & 0xff); + ee->ee_ob[mode][1] = (val >> 4) & 0x7; + ee->ee_db[mode][1] = val & 0x7; + + ret = ath5k_eeprom_read_modes(hal, &offset, mode); + if (ret) + return ret; + + if (hal->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) { + AR5K_EEPROM_READ(offset++, val); + ee->ee_cal_pier[mode][0] = + ath5k_eeprom_bin2freq(hal, val & 0xff, mode); + ee->ee_cal_pier[mode][1] = + ath5k_eeprom_bin2freq(hal, (val >> 8) & 0xff, mode); + + AR5K_EEPROM_READ(offset++, val); + ee->ee_cal_pier[mode][2] = + ath5k_eeprom_bin2freq(hal, val & 0xff, mode); + } + + if (hal->ah_ee_version >= AR5K_EEPROM_VERSION_4_1) + ee->ee_margin_tx_rx[mode] = (val >> 8) & 0x3f; + + /* + * Get values for 802.11g (2.4GHz) + */ + mode = AR5K_EEPROM_MODE_11G; + offset = AR5K_EEPROM_MODES_11G(hal->ah_ee_version); + + ret = ath5k_eeprom_read_ants(hal, &offset, mode); + if (ret) + return ret; + + AR5K_EEPROM_READ(offset++, val); + ee->ee_adc_desired_size[mode] = (s8)((val >> 8) & 0xff); + ee->ee_ob[mode][1] = (val >> 4) & 0x7; + ee->ee_db[mode][1] = val & 0x7; + + ret = ath5k_eeprom_read_modes(hal, &offset, mode); + if (ret) + return ret; + + if (hal->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) { + AR5K_EEPROM_READ(offset++, val); + ee->ee_cal_pier[mode][0] = + ath5k_eeprom_bin2freq(hal, val & 0xff, mode); + ee->ee_cal_pier[mode][1] = + ath5k_eeprom_bin2freq(hal, (val >> 8) & 0xff, mode); + + AR5K_EEPROM_READ(offset++, val); + ee->ee_turbo_max_power[mode] = val & 0x7f; + ee->ee_xr_power[mode] = (val >> 7) & 0x3f; + + AR5K_EEPROM_READ(offset++, val); + ee->ee_cal_pier[mode][2] = + ath5k_eeprom_bin2freq(hal, val & 0xff, mode); + + if (hal->ah_ee_version >= AR5K_EEPROM_VERSION_4_1) + ee->ee_margin_tx_rx[mode] = (val >> 8) & 0x3f; + + AR5K_EEPROM_READ(offset++, val); + ee->ee_i_cal[mode] = (val >> 8) & 0x3f; + ee->ee_q_cal[mode] = (val >> 3) & 0x1f; + + if (hal->ah_ee_version >= AR5K_EEPROM_VERSION_4_2) { + AR5K_EEPROM_READ(offset++, val); + ee->ee_cck_ofdm_gain_delta = val & 0xff; + } + } + + /* + * Read 5GHz EEPROM channels + */ + + return 0; +} + +/* + * Read the MAC address from eeprom + */ +static int ath5k_eeprom_read_mac(struct ath_hw *hal, u8 *mac) +{ + u8 mac_d[ETH_ALEN]; + u32 total, offset; + u16 data; + int octet, ret; + + memset(mac, 0, ETH_ALEN); + memset(mac_d, 0, ETH_ALEN); + + ret = ath5k_hw_eeprom_read(hal, 0x20, &data); + if (ret) + return ret; + + for (offset = 0x1f, octet = 0, total = 0; offset >= 0x1d; offset--) { + ret = ath5k_hw_eeprom_read(hal, offset, &data); + if (ret) + return ret; + + total += data; + mac_d[octet + 1] = data & 0xff; + mac_d[octet] = data >> 8; + octet += 2; + } + + memcpy(mac, mac_d, ETH_ALEN); + + if (!total || total == 3 * 0xffff) + return -EINVAL; + + return 0; +} + +/* + * Read/Write regulatory domain + */ +static bool ath5k_eeprom_regulation_domain(struct ath_hw *hal, bool write, + enum ath5k_regdom *regdomain) +{ + u16 ee_regdomain; + + /* Read current value */ + if (write != true) { + ee_regdomain = hal->ah_capabilities.cap_eeprom.ee_regdomain; + *regdomain = ath5k_regdom_to_ieee(ee_regdomain); + return true; + } + + ee_regdomain = ath5k_regdom_from_ieee(*regdomain); + + /* Try to write a new value */ + if (hal->ah_capabilities.cap_eeprom.ee_protect & + AR5K_EEPROM_PROTECT_WR_128_191) + return false; + if (ath5k_hw_eeprom_write(hal, AR5K_EEPROM_REG_DOMAIN, ee_regdomain)!=0) + return false; + + hal->ah_capabilities.cap_eeprom.ee_regdomain = ee_regdomain; + + return true; +} + +/* + * Use the above to write a new regulatory domain + */ +int ath5k_hw_set_regdomain(struct ath_hw *hal, u16 regdomain) +{ + enum ath5k_regdom ieee_regdomain; + + ieee_regdomain = ath5k_regdom_to_ieee(regdomain); + + if (ath5k_eeprom_regulation_domain(hal, true, &ieee_regdomain) == true) + return 0; + + return -EIO; +} + +/* + * Fill the capabilities struct + */ +static int ath5k_hw_get_capabilities(struct ath_hw *hal) +{ + u16 ee_header; + + AR5K_TRACE; + /* Capabilities stored in the EEPROM */ + ee_header = hal->ah_capabilities.cap_eeprom.ee_header; + + if (hal->ah_version == AR5K_AR5210) { + /* + * Set radio capabilities + * (The AR5110 only supports the middle 5GHz band) + */ + hal->ah_capabilities.cap_range.range_5ghz_min = 5120; + hal->ah_capabilities.cap_range.range_5ghz_max = 5430; + hal->ah_capabilities.cap_range.range_2ghz_min = 0; + hal->ah_capabilities.cap_range.range_2ghz_max = 0; + + /* Set supported modes */ + set_bit(MODE_IEEE80211A, hal->ah_capabilities.cap_mode); + set_bit(MODE_ATHEROS_TURBO, hal->ah_capabilities.cap_mode); + } else { + /* + * XXX The tranceiver supports frequencies from 4920 to 6100GHz + * XXX and from 2312 to 2732GHz. There are problems with the + * XXX current ieee80211 implementation because the IEEE + * XXX channel mapping does not support negative channel + * XXX numbers (2312MHz is channel -19). Of course, this + * XXX doesn't matter because these channels are out of range + * XXX but some regulation domains like MKK (Japan) will + * XXX support frequencies somewhere around 4.8GHz. + */ + + /* + * Set radio capabilities + */ + + if (AR5K_EEPROM_HDR_11A(ee_header)) { + hal->ah_capabilities.cap_range.range_5ghz_min = 5005; /* 4920 */ + hal->ah_capabilities.cap_range.range_5ghz_max = 6100; + + /* Set supported modes */ + set_bit(MODE_IEEE80211A, hal->ah_capabilities.cap_mode); + set_bit(MODE_ATHEROS_TURBO, + hal->ah_capabilities.cap_mode); + if (hal->ah_version == AR5K_AR5212) + set_bit(MODE_ATHEROS_TURBOG, + hal->ah_capabilities.cap_mode); + } + + /* Enable 802.11b if a 2GHz capable radio (2111/5112) is + * connected */ + if (AR5K_EEPROM_HDR_11B(ee_header) || + AR5K_EEPROM_HDR_11G(ee_header)) { + hal->ah_capabilities.cap_range.range_2ghz_min = 2412; /* 2312 */ + hal->ah_capabilities.cap_range.range_2ghz_max = 2732; + + if (AR5K_EEPROM_HDR_11B(ee_header)) + set_bit(MODE_IEEE80211B, + hal->ah_capabilities.cap_mode); + + if (AR5K_EEPROM_HDR_11G(ee_header)) + set_bit(MODE_IEEE80211G, + hal->ah_capabilities.cap_mode); + } + } + + /* GPIO */ + hal->ah_gpio_npins = AR5K_NUM_GPIO; + + /* Set number of supported TX queues */ + if (hal->ah_version == AR5K_AR5210) + hal->ah_capabilities.cap_queues.q_tx_num = + AR5K_NUM_TX_QUEUES_NOQCU; + else + hal->ah_capabilities.cap_queues.q_tx_num = AR5K_NUM_TX_QUEUES; + + return 0; +} + +/*********************************\ + Protocol Control Unit Functions +\*********************************/ + +/* + * Set Operation mode + */ +int ath5k_hw_set_opmode(struct ath_hw *hal) +{ + u32 pcu_reg, beacon_reg, low_id, high_id; + + pcu_reg = 0; + beacon_reg = 0; + + AR5K_TRACE; + + switch (hal->ah_op_mode) { + case IEEE80211_IF_TYPE_IBSS: + pcu_reg |= AR5K_STA_ID1_ADHOC | AR5K_STA_ID1_DESC_ANTENNA | + (hal->ah_version == AR5K_AR5210 ? + AR5K_STA_ID1_NO_PSPOLL : 0); + beacon_reg |= AR5K_BCR_ADHOC; + break; + + case IEEE80211_IF_TYPE_AP: + pcu_reg |= AR5K_STA_ID1_AP | AR5K_STA_ID1_RTS_DEF_ANTENNA | + (hal->ah_version == AR5K_AR5210 ? + AR5K_STA_ID1_NO_PSPOLL : 0); + beacon_reg |= AR5K_BCR_AP; + break; + + case IEEE80211_IF_TYPE_STA: + pcu_reg |= AR5K_STA_ID1_DEFAULT_ANTENNA | + (hal->ah_version == AR5K_AR5210 ? + AR5K_STA_ID1_PWR_SV : 0); + case IEEE80211_IF_TYPE_MNTR: + pcu_reg |= AR5K_STA_ID1_DEFAULT_ANTENNA | + (hal->ah_version == AR5K_AR5210 ? + AR5K_STA_ID1_NO_PSPOLL : 0); + break; + + default: + return -EINVAL; + } + + /* + * Set PCU registers + */ + low_id = AR5K_LOW_ID(hal->ah_sta_id); + high_id = AR5K_HIGH_ID(hal->ah_sta_id); + ath5k_hw_reg_write(hal, low_id, AR5K_STA_ID0); + ath5k_hw_reg_write(hal, pcu_reg | high_id, AR5K_STA_ID1); + + /* + * Set Beacon Control Register on 5210 + */ + if (hal->ah_version == AR5K_AR5210) + ath5k_hw_reg_write(hal, beacon_reg, AR5K_BCR); + + return 0; +} + +/* + * BSSID Functions + */ + +/* + * Get station id + */ +void ath5k_hw_get_lladdr(struct ath_hw *hal, u8 *mac) +{ + AR5K_TRACE; + memcpy(mac, hal->ah_sta_id, ETH_ALEN); +} + +/* + * Set station id + */ +int ath5k_hw_set_lladdr(struct ath_hw *hal, const u8 *mac) +{ + u32 low_id, high_id; + + AR5K_TRACE; + /* Set new station ID */ + memcpy(hal->ah_sta_id, mac, ETH_ALEN); + + low_id = AR5K_LOW_ID(mac); + high_id = AR5K_HIGH_ID(mac); + + ath5k_hw_reg_write(hal, low_id, AR5K_STA_ID0); + ath5k_hw_reg_write(hal, high_id, AR5K_STA_ID1); + + return 0; +} + +/* + * Set BSSID + */ +void ath5k_hw_set_associd(struct ath_hw *hal, const u8 *bssid, u16 assoc_id) +{ + u32 low_id, high_id; + u16 tim_offset = 0; + + /* + * Set simple BSSID mask on 5212 + */ + if (hal->ah_version == AR5K_AR5212) { + ath5k_hw_reg_write(hal, 0xfffffff, AR5K_BSS_IDM0); + ath5k_hw_reg_write(hal, 0xfffffff, AR5K_BSS_IDM1); + } + + /* + * Set BSSID which triggers the "SME Join" operation + */ + low_id = AR5K_LOW_ID(bssid); + high_id = AR5K_HIGH_ID(bssid); + ath5k_hw_reg_write(hal, low_id, AR5K_BSS_ID0); + ath5k_hw_reg_write(hal, high_id | ((assoc_id & 0x3fff) << + AR5K_BSS_ID1_AID_S), AR5K_BSS_ID1); + memcpy(&hal->ah_bssid, bssid, ETH_ALEN); + + if (assoc_id == 0) { + ath5k_hw_disable_pspoll(hal); + return; + } + + AR5K_REG_WRITE_BITS(hal, AR5K_BEACON, AR5K_BEACON_TIM, + tim_offset ? tim_offset + 4 : 0); + + ath5k_hw_enable_pspoll(hal, NULL, 0); +} + +/* + * Set BSSID mask on 5212 + */ +int ath5k_hw_set_bssid_mask(struct ath_hw *hal, const u8 *mask) +{ + u32 low_id, high_id; + AR5K_TRACE; + + if (hal->ah_version == AR5K_AR5212) { + low_id = AR5K_LOW_ID(mask); + high_id = AR5K_HIGH_ID(mask); + + ath5k_hw_reg_write(hal, low_id, AR5K_BSS_IDM0); + ath5k_hw_reg_write(hal, high_id, AR5K_BSS_IDM1); + + return 0; + } + + return -EIO; +} + +/* + * Receive start/stop functions + */ + +/* + * Start receive on PCU + */ +void ath5k_hw_start_rx_pcu(struct ath_hw *hal) +{ + AR5K_TRACE; + AR5K_REG_DISABLE_BITS(hal, AR5K_DIAG_SW, AR5K_DIAG_SW_DIS_RX); +} + +/* + * Stop receive on PCU + */ +void ath5k_hw_stop_pcu_recv(struct ath_hw *hal) +{ + AR5K_TRACE; + AR5K_REG_ENABLE_BITS(hal, AR5K_DIAG_SW, AR5K_DIAG_SW_DIS_RX); +} + +/* + * RX Filter functions + */ + +/* + * Set multicast filter + */ +void ath5k_hw_set_mcast_filter(struct ath_hw *hal, u32 filter0, u32 filter1) +{ + AR5K_TRACE; + /* Set the multicat filter */ + ath5k_hw_reg_write(hal, filter0, AR5K_MCAST_FILTER0); + ath5k_hw_reg_write(hal, filter1, AR5K_MCAST_FILTER1); +} + +/* + * Set multicast filter by index + */ +int ath5k_hw_set_mcast_filterindex(struct ath_hw *hal, u32 index) +{ + + AR5K_TRACE; + if (index >= 64) + return -EINVAL; + else if (index >= 32) + AR5K_REG_ENABLE_BITS(hal, AR5K_MCAST_FILTER1, + (1 << (index - 32))); + else + AR5K_REG_ENABLE_BITS(hal, AR5K_MCAST_FILTER0, (1 << index)); + + return 0; +} + +/* + * Clear Multicast filter by index + */ +int ath5k_hw_clear_mcast_filter_idx(struct ath_hw *hal, u32 index) +{ + + AR5K_TRACE; + if (index >= 64) + return -EINVAL; + else if (index >= 32) + AR5K_REG_DISABLE_BITS(hal, AR5K_MCAST_FILTER1, + (1 << (index - 32))); + else + AR5K_REG_DISABLE_BITS(hal, AR5K_MCAST_FILTER0, (1 << index)); + + return 0; +} + +/* + * Get current rx filter + */ +u32 ath5k_hw_get_rx_filter(struct ath_hw *ah) +{ + u32 data, filter = 0; + + AR5K_TRACE; + filter = ath5k_hw_reg_read(ah, AR5K_RX_FILTER); + + /*Radar detection for 5212*/ + if (ah->ah_version == AR5K_AR5212) { + data = ath5k_hw_reg_read(ah, AR5K_PHY_ERR_FIL); + + if (data & AR5K_PHY_ERR_FIL_RADAR) + filter |= AR5K_RX_FILTER_RADARERR; + if (data & (AR5K_PHY_ERR_FIL_OFDM | AR5K_PHY_ERR_FIL_CCK)) + filter |= AR5K_RX_FILTER_PHYERR; + } + + return filter; +} + +/* + * Set rx filter + */ +void ath5k_hw_set_rx_filter(struct ath_hw *ah, u32 filter) +{ + u32 data = 0; + + AR5K_TRACE; + + /* Set PHY error filter register on 5212*/ + if (ah->ah_version == AR5K_AR5212) { + if (filter & AR5K_RX_FILTER_RADARERR) + data |= AR5K_PHY_ERR_FIL_RADAR; + if (filter & AR5K_RX_FILTER_PHYERR) + data |= AR5K_PHY_ERR_FIL_OFDM | AR5K_PHY_ERR_FIL_CCK; + } + + /* + * The AR5210 uses promiscous mode to detect radar activity + */ + if (ah->ah_version == AR5K_AR5210 && + (filter & AR5K_RX_FILTER_RADARERR)) { + filter &= ~AR5K_RX_FILTER_RADARERR; + filter |= AR5K_RX_FILTER_PROM; + } + + /*Zero length DMA*/ + if (data) + AR5K_REG_ENABLE_BITS(ah, AR5K_RXCFG, AR5K_RXCFG_ZLFDMA); + else + AR5K_REG_DISABLE_BITS(ah, AR5K_RXCFG, AR5K_RXCFG_ZLFDMA); + + /*Write RX Filter register*/ + ath5k_hw_reg_write(ah, filter & 0xff, AR5K_RX_FILTER); + + /*Write PHY error filter register on 5212*/ + if (ah->ah_version == AR5K_AR5212) + ath5k_hw_reg_write(ah, data, AR5K_PHY_ERR_FIL); + +} + +/* + * Beacon related functions + */ + +/* + * Get a 32bit TSF + */ +u32 ath5k_hw_get_tsf32(struct ath_hw *hal) +{ + AR5K_TRACE; + return ath5k_hw_reg_read(hal, AR5K_TSF_L32); +} + +/* + * Get the full 64bit TSF + */ +u64 ath5k_hw_get_tsf64(struct ath_hw *hal) +{ + u64 tsf = ath5k_hw_reg_read(hal, AR5K_TSF_U32); + AR5K_TRACE; + + return ath5k_hw_reg_read(hal, AR5K_TSF_L32) | (tsf << 32); +} + +/* + * Force a TSF reset + */ +void ath5k_hw_reset_tsf(struct ath_hw *hal) +{ + AR5K_TRACE; + AR5K_REG_ENABLE_BITS(hal, AR5K_BEACON, AR5K_BEACON_RESET_TSF); +} + +/* + * Initialize beacon timers + */ +void ath5k_hw_init_beacon(struct ath_hw *hal, u32 next_beacon, u32 interval) +{ + u32 timer1, timer2, timer3; + + AR5K_TRACE; + /* + * Set the additional timers by mode + */ + switch (hal->ah_op_mode) { + case IEEE80211_IF_TYPE_STA: + if (hal->ah_version == AR5K_AR5210) { + timer1 = 0xffffffff; + timer2 = 0xffffffff; + } else { + timer1 = 0x0000ffff; + timer2 = 0x0007ffff; + } + break; + + default: + timer1 = (next_beacon - AR5K_TUNE_DMA_BEACON_RESP) << + 0x00000003; + timer2 = (next_beacon - AR5K_TUNE_SW_BEACON_RESP) << + 0x00000003; + } + + timer3 = next_beacon + (hal->ah_atim_window ? hal->ah_atim_window : 1); + + /* + * Set the beacon register and enable all timers. + * (next beacon, DMA beacon, software beacon, ATIM window time) + */ + ath5k_hw_reg_write(hal, next_beacon, AR5K_TIMER0); + ath5k_hw_reg_write(hal, timer1, AR5K_TIMER1); + ath5k_hw_reg_write(hal, timer2, AR5K_TIMER2); + ath5k_hw_reg_write(hal, timer3, AR5K_TIMER3); + + ath5k_hw_reg_write(hal, interval & (AR5K_BEACON_PERIOD | + AR5K_BEACON_RESET_TSF | AR5K_BEACON_ENABLE), + AR5K_BEACON); +} + +/* + * Set beacon timers + */ +int ath5k_hw_set_beacon_timers(struct ath_hw *hal, + const struct ath5k_beacon_state *state) +{ + u32 cfp_period, next_cfp, dtim, interval, next_beacon; + + /* + * TODO: should be changed through *state + * review struct ath5k_beacon_state struct + * + * XXX: These are used for cfp period bellow, are they + * ok ? Is it O.K. for tsf here to be 0 or should we use + * get_tsf ? + */ + u32 dtim_count = 0; /* XXX */ + u32 cfp_count = 0; /* XXX */ + u32 tsf = 0; /* XXX */ + + AR5K_TRACE; + /* Return on an invalid beacon state */ + if (state->bs_interval < 1) + return -EINVAL; + + interval = state->bs_interval; + dtim = state->bs_dtim_period; + + /* + * PCF support? + */ + if (state->bs_cfp_period > 0) { + /* + * Enable PCF mode and set the CFP + * (Contention Free Period) and timer registers + */ + cfp_period = state->bs_cfp_period * state->bs_dtim_period * + state->bs_interval; + next_cfp = (cfp_count * state->bs_dtim_period + dtim_count) * + state->bs_interval; + + AR5K_REG_ENABLE_BITS(hal, AR5K_STA_ID1, + AR5K_STA_ID1_DEFAULT_ANTENNA | + AR5K_STA_ID1_PCF); + ath5k_hw_reg_write(hal, cfp_period, AR5K_CFP_PERIOD); + ath5k_hw_reg_write(hal, state->bs_cfp_max_duration, + AR5K_CFP_DUR); + ath5k_hw_reg_write(hal, (tsf + (next_cfp == 0 ? cfp_period : + next_cfp)) << 3, AR5K_TIMER2); + } else { + /* Disable PCF mode */ + AR5K_REG_DISABLE_BITS(hal, AR5K_STA_ID1, + AR5K_STA_ID1_DEFAULT_ANTENNA | + AR5K_STA_ID1_PCF); + } + + /* + * Enable the beacon timer register + */ + ath5k_hw_reg_write(hal, state->bs_next_beacon, AR5K_TIMER0); + + /* + * Start the beacon timers + */ + ath5k_hw_reg_write(hal, (ath5k_hw_reg_read(hal, AR5K_BEACON) &~ + (AR5K_BEACON_PERIOD | AR5K_BEACON_TIM)) | + AR5K_REG_SM(state->bs_tim_offset ? state->bs_tim_offset + 4 : 0, + AR5K_BEACON_TIM) | AR5K_REG_SM(state->bs_interval, + AR5K_BEACON_PERIOD), AR5K_BEACON); + + /* + * Write new beacon miss threshold, if it appears to be valid + * XXX: Figure out right values for min <= bs_bmiss_threshold <= max + * and return if its not in range. We can test this by reading value and + * setting value to a largest value and seeing which values register. + */ + + AR5K_REG_WRITE_BITS(hal, AR5K_RSSI_THR, AR5K_RSSI_THR_BMISS, + state->bs_bmiss_threshold); + + /* + * Set sleep control register + * XXX: Didn't find this in 5210 code but since this register + * exists also in ar5k's 5210 headers i leave it as common code. + */ + AR5K_REG_WRITE_BITS(hal, AR5K_SLEEP_CTL, AR5K_SLEEP_CTL_SLDUR, + (state->bs_sleep_duration - 3) << 3); + + /* + * Set enhanced sleep registers on 5212 + */ + if (hal->ah_version == AR5K_AR5212) { + if (state->bs_sleep_duration > state->bs_interval && + roundup(state->bs_sleep_duration, interval) == + state->bs_sleep_duration) + interval = state->bs_sleep_duration; + + if (state->bs_sleep_duration > dtim && (dtim == 0 || + roundup(state->bs_sleep_duration, dtim) == + state->bs_sleep_duration)) + dtim = state->bs_sleep_duration; + + if (interval > dtim) + return -EINVAL; + + next_beacon = interval == dtim ? state->bs_next_dtim : + state->bs_next_beacon; + + ath5k_hw_reg_write(hal, + AR5K_REG_SM((state->bs_next_dtim - 3) << 3, + AR5K_SLEEP0_NEXT_DTIM) | + AR5K_REG_SM(10, AR5K_SLEEP0_CABTO) | + AR5K_SLEEP0_ENH_SLEEP_EN | + AR5K_SLEEP0_ASSUME_DTIM, AR5K_SLEEP0); + + ath5k_hw_reg_write(hal, AR5K_REG_SM((next_beacon - 3) << 3, + AR5K_SLEEP1_NEXT_TIM) | + AR5K_REG_SM(10, AR5K_SLEEP1_BEACON_TO), AR5K_SLEEP1); + + ath5k_hw_reg_write(hal, + AR5K_REG_SM(interval, AR5K_SLEEP2_TIM_PER) | + AR5K_REG_SM(dtim, AR5K_SLEEP2_DTIM_PER), AR5K_SLEEP2); + } + + return 0; +} + +/* + * Reset beacon timers + */ +void ath5k_hw_reset_beacon(struct ath_hw *hal) +{ + AR5K_TRACE; + /* + * Disable beacon timer + */ + ath5k_hw_reg_write(hal, 0, AR5K_TIMER0); + + /* + * Disable some beacon register values + */ + AR5K_REG_DISABLE_BITS(hal, AR5K_STA_ID1, + AR5K_STA_ID1_DEFAULT_ANTENNA | AR5K_STA_ID1_PCF); + ath5k_hw_reg_write(hal, AR5K_BEACON_PERIOD, AR5K_BEACON); +} + +/* + * Wait for beacon queue to finish + * TODO: This function's name is misleading, rename + */ +int ath5k_hw_wait_for_beacon(struct ath_hw *hal, unsigned long phys_addr) +{ + unsigned int i; + int ret; + + AR5K_TRACE; + + /* 5210 doesn't have QCU*/ + if (hal->ah_version == AR5K_AR5210) { + /* + * Wait for beaconn queue to finish by checking + * Control Register and Beacon Status Register. + */ + for (i = AR5K_TUNE_BEACON_INTERVAL / 2; i > 0; i--) { + if (!(ath5k_hw_reg_read(hal, AR5K_BSR) & AR5K_BSR_TXQ1F) + || + !(ath5k_hw_reg_read(hal, AR5K_CR) & AR5K_BSR_TXQ1F)) + break; + udelay(10); + } + + /* Timeout... */ + if (i <= 0) { + /* + * Re-schedule the beacon queue + */ + ath5k_hw_reg_write(hal, phys_addr, AR5K_NOQCU_TXDP1); + ath5k_hw_reg_write(hal, AR5K_BCR_TQ1V | AR5K_BCR_BDMAE, + AR5K_BCR); + + return -EIO; + } + ret = 0; + } else { + /*5211/5212*/ + ret = ath5k_hw_register_timeout(hal, + AR5K_QUEUE_STATUS(AR5K_TX_QUEUE_ID_BEACON), + AR5K_QCU_STS_FRMPENDCNT, 0, false); + + if (AR5K_REG_READ_Q(hal, AR5K_QCU_TXE, AR5K_TX_QUEUE_ID_BEACON)) + return -EIO; + } + + return ret; +} + +/* + * Update mib counters (statistics) + */ +void ath5k_hw_update_mib_counters(struct ath_hw *hal, + struct ath5k_mib_stats *statistics) +{ + AR5K_TRACE; + /* Read-And-Clear */ + statistics->ackrcv_bad += ath5k_hw_reg_read(hal, AR5K_ACK_FAIL); + statistics->rts_bad += ath5k_hw_reg_read(hal, AR5K_RTS_FAIL); + statistics->rts_good += ath5k_hw_reg_read(hal, AR5K_RTS_OK); + statistics->fcs_bad += ath5k_hw_reg_read(hal, AR5K_FCS_FAIL); + statistics->beacons += ath5k_hw_reg_read(hal, AR5K_BEACON_CNT); + + /* Reset profile count registers on 5212*/ + if (hal->ah_version == AR5K_AR5212) { + ath5k_hw_reg_write(hal, 0, AR5K_PROFCNT_TX); + ath5k_hw_reg_write(hal, 0, AR5K_PROFCNT_RX); + ath5k_hw_reg_write(hal, 0, AR5K_PROFCNT_RXCLR); + ath5k_hw_reg_write(hal, 0, AR5K_PROFCNT_CYCLE); + } +} + +/* + * ACK/CTS Timeouts + */ + +/* + * Set ACK timeout on PCU + */ +int ath5k_hw_set_ack_timeout(struct ath_hw *hal, unsigned int timeout) +{ + AR5K_TRACE; + if (ath5k_hw_clocktoh(AR5K_REG_MS(0xffffffff, AR5K_TIME_OUT_ACK), + hal->ah_turbo) <= timeout) + return -EINVAL; + + AR5K_REG_WRITE_BITS(hal, AR5K_TIME_OUT, AR5K_TIME_OUT_ACK, + ath5k_hw_htoclock(timeout, hal->ah_turbo)); + + return 0; +} + +/* + * Read the ACK timeout from PCU + */ +unsigned int ath5k_hw_get_ack_timeout(struct ath_hw *hal) +{ + AR5K_TRACE; + + return ath5k_hw_clocktoh(AR5K_REG_MS(ath5k_hw_reg_read(hal, + AR5K_TIME_OUT), AR5K_TIME_OUT_ACK), hal->ah_turbo); +} + +/* + * Set CTS timeout on PCU + */ +int ath5k_hw_set_cts_timeout(struct ath_hw *hal, unsigned int timeout) +{ + AR5K_TRACE; + if (ath5k_hw_clocktoh(AR5K_REG_MS(0xffffffff, AR5K_TIME_OUT_CTS), + hal->ah_turbo) <= timeout) + return -EINVAL; + + AR5K_REG_WRITE_BITS(hal, AR5K_TIME_OUT, AR5K_TIME_OUT_CTS, + ath5k_hw_htoclock(timeout, hal->ah_turbo)); + + return 0; +} + +/* + * Read CTS timeout from PCU + */ +unsigned int ath5k_hw_get_cts_timeout(struct ath_hw *hal) +{ + AR5K_TRACE; + return ath5k_hw_clocktoh(AR5K_REG_MS(ath5k_hw_reg_read(hal, + AR5K_TIME_OUT), AR5K_TIME_OUT_CTS), hal->ah_turbo); +} + +/* + * Key table (WEP) functions + */ + +int ath5k_hw_reset_key(struct ath_hw *hal, u16 entry) +{ + unsigned int i; + + AR5K_TRACE; + AR5K_ASSERT_ENTRY(entry, AR5K_KEYTABLE_SIZE); + + for (i = 0; i < AR5K_KEYCACHE_SIZE; i++) + ath5k_hw_reg_write(hal, 0, AR5K_KEYTABLE_OFF(entry, i)); + + /* Set NULL encryption on non-5210*/ + if (hal->ah_version != AR5K_AR5210) + ath5k_hw_reg_write(hal, AR5K_KEYTABLE_TYPE_NULL, + AR5K_KEYTABLE_TYPE(entry)); + + return 0; +} + +int ath5k_hw_is_key_valid(struct ath_hw *hal, u16 entry) +{ + AR5K_TRACE; + AR5K_ASSERT_ENTRY(entry, AR5K_KEYTABLE_SIZE); + + /* Check the validation flag at the end of the entry */ + return ath5k_hw_reg_read(hal, AR5K_KEYTABLE_MAC1(entry)) & + AR5K_KEYTABLE_VALID; +} + +int ath5k_hw_set_key(struct ath_hw *hal, u16 entry, + const struct ieee80211_key_conf *key, const u8 *mac) +{ + unsigned int i; + __le32 key_v[5] = {}; + u32 keytype; + + AR5K_TRACE; + + /* key->keylen comes in from mac80211 in bytes */ + + if (key->keylen > AR5K_KEYTABLE_SIZE / 8) + return -EOPNOTSUPP; + + switch (key->keylen) { + /* WEP 40-bit = 40-bit entered key + 24 bit IV = 64-bit */ + case 40 / 8: + memcpy(&key_v[0], key->key, 5); + keytype = AR5K_KEYTABLE_TYPE_40; + break; + + /* WEP 104-bit = 104-bit entered key + 24-bit IV = 128-bit */ + case 104 / 8: + memcpy(&key_v[0], &key->key[0], 6); + memcpy(&key_v[2], &key->key[6], 6); + memcpy(&key_v[4], &key->key[12], 1); + keytype = AR5K_KEYTABLE_TYPE_104; + break; + /* WEP 128-bit = 128-bit entered key + 24 bit IV = 152-bit */ + case 128 / 8: + memcpy(&key_v[0], &key->key[0], 6); + memcpy(&key_v[2], &key->key[6], 6); + memcpy(&key_v[4], &key->key[12], 4); + keytype = AR5K_KEYTABLE_TYPE_128; + break; + + default: + return -EINVAL; /* shouldn't happen */ + } + + for (i = 0; i < ARRAY_SIZE(key_v); i++) + ath5k_hw_reg_write(hal, le32_to_cpu(key_v[i]), + AR5K_KEYTABLE_OFF(entry, i)); + + ath5k_hw_reg_write(hal, keytype, AR5K_KEYTABLE_TYPE(entry)); + + return ath5k_hw_set_key_lladdr(hal, entry, mac); +} + +int ath5k_hw_set_key_lladdr(struct ath_hw *hal, u16 entry, const u8 *mac) +{ + u32 low_id, high_id; + + AR5K_TRACE; + /* Invalid entry (key table overflow) */ + AR5K_ASSERT_ENTRY(entry, AR5K_KEYTABLE_SIZE); + + /* MAC may be NULL if it's a broadcast key. In this case no need to + * to compute AR5K_LOW_ID and AR5K_HIGH_ID as we already know it. */ + if (unlikely(mac == NULL)) { + low_id = 0xffffffff; + high_id = 0xffff | AR5K_KEYTABLE_VALID; + } else { + low_id = AR5K_LOW_ID(mac); + high_id = AR5K_HIGH_ID(mac) | AR5K_KEYTABLE_VALID; + } + + ath5k_hw_reg_write(hal, low_id, AR5K_KEYTABLE_MAC0(entry)); + ath5k_hw_reg_write(hal, high_id, AR5K_KEYTABLE_MAC1(entry)); + + return 0; +} + + +/********************************************\ +Queue Control Unit, DFS Control Unit Functions +\********************************************/ + +/* + * Initialize a transmit queue + */ +int ath5k_hw_setup_tx_queue(struct ath_hw *hal, enum ath5k_tx_queue queue_type, + struct ath5k_txq_info *queue_info) +{ + unsigned int queue; + int ret; + + AR5K_TRACE; + + /* + * Get queue by type + */ + /*5210 only has 2 queues*/ + if (hal->ah_version == AR5K_AR5210) { + switch (queue_type) { + case AR5K_TX_QUEUE_DATA: + queue = AR5K_TX_QUEUE_ID_NOQCU_DATA; + break; + case AR5K_TX_QUEUE_BEACON: + case AR5K_TX_QUEUE_CAB: + queue = AR5K_TX_QUEUE_ID_NOQCU_BEACON; + break; + default: + return -EINVAL; + } + } else { + switch (queue_type) { + case AR5K_TX_QUEUE_DATA: + for (queue = AR5K_TX_QUEUE_ID_DATA_MIN; + hal->ah_txq[queue].tqi_type != + AR5K_TX_QUEUE_INACTIVE; queue++) { + + if (queue > AR5K_TX_QUEUE_ID_DATA_MAX) + return -EINVAL; + } + break; + case AR5K_TX_QUEUE_UAPSD: + queue = AR5K_TX_QUEUE_ID_UAPSD; + break; + case AR5K_TX_QUEUE_BEACON: + queue = AR5K_TX_QUEUE_ID_BEACON; + break; + case AR5K_TX_QUEUE_CAB: + queue = AR5K_TX_QUEUE_ID_CAB; + break; + case AR5K_TX_QUEUE_XR_DATA: + if (hal->ah_version != AR5K_AR5212) + AR5K_PRINTF("XR data queues only supported in " + "5212!\n"); + queue = AR5K_TX_QUEUE_ID_XR_DATA; + break; + default: + return -EINVAL; + } + } + + /* + * Setup internal queue structure + */ + memset(&hal->ah_txq[queue], 0, sizeof(struct ath5k_txq_info)); + hal->ah_txq[queue].tqi_type = queue_type; + + if (queue_info != NULL) { + queue_info->tqi_type = queue_type; + ret = ath5k_hw_setup_tx_queueprops(hal, queue, queue_info); + if (ret) + return ret; + } + /* + * We use ah_txq_interrupts to hold a temp value for + * the Secondary interrupt mask registers on 5211+ + * check out ath5k_hw_reset_tx_queue + */ + AR5K_Q_ENABLE_BITS(hal->ah_txq_interrupts, queue); + + return queue; +} + +/* + * Setup a transmit queue + */ +int ath5k_hw_setup_tx_queueprops(struct ath_hw *hal, int queue, + const struct ath5k_txq_info *queue_info) +{ + AR5K_TRACE; + AR5K_ASSERT_ENTRY(queue, hal->ah_capabilities.cap_queues.q_tx_num); + + if (hal->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE) + return -EIO; + + memcpy(&hal->ah_txq[queue], queue_info, sizeof(struct ath5k_txq_info)); + + /*XXX: Is this supported on 5210 ?*/ + if ((queue_info->tqi_type == AR5K_TX_QUEUE_DATA && + ((queue_info->tqi_subtype == AR5K_WME_AC_VI) || + (queue_info->tqi_subtype == AR5K_WME_AC_VO))) || + queue_info->tqi_type == AR5K_TX_QUEUE_UAPSD) + hal->ah_txq[queue].tqi_flags |= AR5K_TXQ_FLAG_POST_FR_BKOFF_DIS; + + return 0; +} + +/* + * Get properties for a specific transmit queue + */ +int ath5k_hw_get_tx_queueprops(struct ath_hw *hal, int queue, + struct ath5k_txq_info *queue_info) +{ + AR5K_TRACE; + memcpy(queue_info, &hal->ah_txq[queue], sizeof(struct ath5k_txq_info)); + return 0; +} + +/* + * Set a transmit queue inactive + */ +void ath5k_hw_release_tx_queue(struct ath_hw *hal, unsigned int queue) +{ + AR5K_TRACE; + if (WARN_ON(queue >= hal->ah_capabilities.cap_queues.q_tx_num)) + return; + + /* This queue will be skipped in further operations */ + hal->ah_txq[queue].tqi_type = AR5K_TX_QUEUE_INACTIVE; + /*For SIMR setup*/ + AR5K_Q_DISABLE_BITS(hal->ah_txq_interrupts, queue); +} + +/* + * Set DFS params for a transmit queue + */ +int ath5k_hw_reset_tx_queue(struct ath_hw *hal, unsigned int queue) +{ + u32 cw_min, cw_max, retry_lg, retry_sh; + struct ath5k_txq_info *tq = &hal->ah_txq[queue]; + + AR5K_TRACE; + AR5K_ASSERT_ENTRY(queue, hal->ah_capabilities.cap_queues.q_tx_num); + + tq = &hal->ah_txq[queue]; + + if (tq->tqi_type == AR5K_TX_QUEUE_INACTIVE) + return 0; + + if (hal->ah_version == AR5K_AR5210) { + /* Only handle data queues, others will be ignored */ + if (tq->tqi_type != AR5K_TX_QUEUE_DATA) + return -EINVAL; + + /* Set Slot time */ + ath5k_hw_reg_write(hal, hal->ah_turbo == true ? + AR5K_INIT_SLOT_TIME_TURBO : AR5K_INIT_SLOT_TIME, + AR5K_SLOT_TIME); + /* Set ACK_CTS timeout */ + ath5k_hw_reg_write(hal, hal->ah_turbo == true ? + AR5K_INIT_ACK_CTS_TIMEOUT_TURBO : + AR5K_INIT_ACK_CTS_TIMEOUT, AR5K_SLOT_TIME); + /* Set Transmit Latency */ + ath5k_hw_reg_write(hal, hal->ah_turbo == true ? + AR5K_INIT_TRANSMIT_LATENCY_TURBO : + AR5K_INIT_TRANSMIT_LATENCY, AR5K_USEC_5210); + /* Set IFS0 */ + if (hal->ah_turbo == true) + ath5k_hw_reg_write(hal, ((AR5K_INIT_SIFS_TURBO + + (hal->ah_aifs + tq->tqi_aifs) * + AR5K_INIT_SLOT_TIME_TURBO) << + AR5K_IFS0_DIFS_S) | AR5K_INIT_SIFS_TURBO, + AR5K_IFS0); + else + ath5k_hw_reg_write(hal, ((AR5K_INIT_SIFS + + (hal->ah_aifs + tq->tqi_aifs) * + AR5K_INIT_SLOT_TIME) << AR5K_IFS0_DIFS_S) | + AR5K_INIT_SIFS, AR5K_IFS0); + + /* Set IFS1 */ + ath5k_hw_reg_write(hal, hal->ah_turbo == true ? + AR5K_INIT_PROTO_TIME_CNTRL_TURBO : + AR5K_INIT_PROTO_TIME_CNTRL, AR5K_IFS1); + /* Set PHY register 0x9844 (??) */ + ath5k_hw_reg_write(hal, hal->ah_turbo == true ? + (ath5k_hw_reg_read(hal, AR5K_PHY(17)) & ~0x7F) | 0x38 : + (ath5k_hw_reg_read(hal, AR5K_PHY(17)) & ~0x7F) | 0x1C, + AR5K_PHY(17)); + /* Set Frame Control Register */ + ath5k_hw_reg_write(hal, hal->ah_turbo == true ? + (AR5K_PHY_FRAME_CTL_INI | AR5K_PHY_TURBO_MODE | + AR5K_PHY_TURBO_SHORT | 0x2020) : + (AR5K_PHY_FRAME_CTL_INI | 0x1020), + AR5K_PHY_FRAME_CTL_5210); + } + + /* + * Calculate cwmin/max by channel mode + */ + cw_min = hal->ah_cw_min = AR5K_TUNE_CWMIN; + cw_max = hal->ah_cw_max = AR5K_TUNE_CWMAX; + hal->ah_aifs = AR5K_TUNE_AIFS; + /*XR is only supported on 5212*/ + if (IS_CHAN_XR(hal->ah_current_channel) && + hal->ah_version == AR5K_AR5212) { + cw_min = hal->ah_cw_min = AR5K_TUNE_CWMIN_XR; + cw_max = hal->ah_cw_max = AR5K_TUNE_CWMAX_XR; + hal->ah_aifs = AR5K_TUNE_AIFS_XR; + /*B mode is not supported on 5210*/ + } else if (IS_CHAN_B(hal->ah_current_channel) && + hal->ah_version != AR5K_AR5210) { + cw_min = hal->ah_cw_min = AR5K_TUNE_CWMIN_11B; + cw_max = hal->ah_cw_max = AR5K_TUNE_CWMAX_11B; + hal->ah_aifs = AR5K_TUNE_AIFS_11B; + } + + cw_min = 1; + while (cw_min < hal->ah_cw_min) + cw_min = (cw_min << 1) | 1; + + cw_min = tq->tqi_cw_min < 0 ? (cw_min >> (-tq->tqi_cw_min)) : + ((cw_min << tq->tqi_cw_min) + (1 << tq->tqi_cw_min) - 1); + cw_max = tq->tqi_cw_max < 0 ? (cw_max >> (-tq->tqi_cw_max)) : + ((cw_max << tq->tqi_cw_max) + (1 << tq->tqi_cw_max) - 1); + + /* + * Calculate and set retry limits + */ + if (hal->ah_software_retry == true) { + /* XXX Need to test this */ + retry_lg = hal->ah_limit_tx_retries; + retry_sh = retry_lg = retry_lg > AR5K_DCU_RETRY_LMT_SH_RETRY ? + AR5K_DCU_RETRY_LMT_SH_RETRY : retry_lg; + } else { + retry_lg = AR5K_INIT_LG_RETRY; + retry_sh = AR5K_INIT_SH_RETRY; + } + + /*No QCU/DCU [5210]*/ + if (hal->ah_version == AR5K_AR5210) { + ath5k_hw_reg_write(hal, + (cw_min << AR5K_NODCU_RETRY_LMT_CW_MIN_S) + | AR5K_REG_SM(AR5K_INIT_SLG_RETRY, + AR5K_NODCU_RETRY_LMT_SLG_RETRY) + | AR5K_REG_SM(AR5K_INIT_SSH_RETRY, + AR5K_NODCU_RETRY_LMT_SSH_RETRY) + | AR5K_REG_SM(retry_lg, AR5K_NODCU_RETRY_LMT_LG_RETRY) + | AR5K_REG_SM(retry_sh, AR5K_NODCU_RETRY_LMT_SH_RETRY), + AR5K_NODCU_RETRY_LMT); + } else { + /*QCU/DCU [5211+]*/ + ath5k_hw_reg_write(hal, + AR5K_REG_SM(AR5K_INIT_SLG_RETRY, + AR5K_DCU_RETRY_LMT_SLG_RETRY) | + AR5K_REG_SM(AR5K_INIT_SSH_RETRY, + AR5K_DCU_RETRY_LMT_SSH_RETRY) | + AR5K_REG_SM(retry_lg, AR5K_DCU_RETRY_LMT_LG_RETRY) | + AR5K_REG_SM(retry_sh, AR5K_DCU_RETRY_LMT_SH_RETRY), + AR5K_QUEUE_DFS_RETRY_LIMIT(queue)); + + /*===Rest is also for QCU/DCU only [5211+]===*/ + + /* + * Set initial content window (cw_min/cw_max) + * and arbitrated interframe space (aifs)... + */ + ath5k_hw_reg_write(hal, + AR5K_REG_SM(cw_min, AR5K_DCU_LCL_IFS_CW_MIN) | + AR5K_REG_SM(cw_max, AR5K_DCU_LCL_IFS_CW_MAX) | + AR5K_REG_SM(hal->ah_aifs + tq->tqi_aifs, + AR5K_DCU_LCL_IFS_AIFS), + AR5K_QUEUE_DFS_LOCAL_IFS(queue)); + + /* + * Set misc registers + */ + ath5k_hw_reg_write(hal, AR5K_QCU_MISC_DCU_EARLY, + AR5K_QUEUE_MISC(queue)); + + if (tq->tqi_cbr_period) { + ath5k_hw_reg_write(hal, AR5K_REG_SM(tq->tqi_cbr_period, + AR5K_QCU_CBRCFG_INTVAL) | + AR5K_REG_SM(tq->tqi_cbr_overflow_limit, + AR5K_QCU_CBRCFG_ORN_THRES), + AR5K_QUEUE_CBRCFG(queue)); + AR5K_REG_ENABLE_BITS(hal, AR5K_QUEUE_MISC(queue), + AR5K_QCU_MISC_FRSHED_CBR); + if (tq->tqi_cbr_overflow_limit) + AR5K_REG_ENABLE_BITS(hal, + AR5K_QUEUE_MISC(queue), + AR5K_QCU_MISC_CBR_THRES_ENABLE); + } + + if (tq->tqi_ready_time) + ath5k_hw_reg_write(hal, AR5K_REG_SM(tq->tqi_ready_time, + AR5K_QCU_RDYTIMECFG_INTVAL) | + AR5K_QCU_RDYTIMECFG_ENABLE, + AR5K_QUEUE_RDYTIMECFG(queue)); + + if (tq->tqi_burst_time) { + ath5k_hw_reg_write(hal, AR5K_REG_SM(tq->tqi_burst_time, + AR5K_DCU_CHAN_TIME_DUR) | + AR5K_DCU_CHAN_TIME_ENABLE, + AR5K_QUEUE_DFS_CHANNEL_TIME(queue)); + + if (tq->tqi_flags & AR5K_TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE) + AR5K_REG_ENABLE_BITS(hal, + AR5K_QUEUE_MISC(queue), + AR5K_QCU_MISC_TXE); + } + + if (tq->tqi_flags & AR5K_TXQ_FLAG_BACKOFF_DISABLE) + ath5k_hw_reg_write(hal, AR5K_DCU_MISC_POST_FR_BKOFF_DIS, + AR5K_QUEUE_DFS_MISC(queue)); + + if (tq->tqi_flags & AR5K_TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE) + ath5k_hw_reg_write(hal, AR5K_DCU_MISC_BACKOFF_FRAG, + AR5K_QUEUE_DFS_MISC(queue)); + + /* + * Set registers by queue type + */ + switch (tq->tqi_type) { + case AR5K_TX_QUEUE_BEACON: + AR5K_REG_ENABLE_BITS(hal, AR5K_QUEUE_MISC(queue), + AR5K_QCU_MISC_FRSHED_DBA_GT | + AR5K_QCU_MISC_CBREXP_BCN | + AR5K_QCU_MISC_BCN_ENABLE); + + AR5K_REG_ENABLE_BITS(hal, AR5K_QUEUE_DFS_MISC(queue), + (AR5K_DCU_MISC_ARBLOCK_CTL_GLOBAL << + AR5K_DCU_MISC_ARBLOCK_CTL_S) | + AR5K_DCU_MISC_POST_FR_BKOFF_DIS | + AR5K_DCU_MISC_BCN_ENABLE); + + ath5k_hw_reg_write(hal, ((AR5K_TUNE_BEACON_INTERVAL - + (AR5K_TUNE_SW_BEACON_RESP - + AR5K_TUNE_DMA_BEACON_RESP) - + AR5K_TUNE_ADDITIONAL_SWBA_BACKOFF) * 1024) | + AR5K_QCU_RDYTIMECFG_ENABLE, + AR5K_QUEUE_RDYTIMECFG(queue)); + break; + + case AR5K_TX_QUEUE_CAB: + AR5K_REG_ENABLE_BITS(hal, AR5K_QUEUE_MISC(queue), + AR5K_QCU_MISC_FRSHED_DBA_GT | + AR5K_QCU_MISC_CBREXP | + AR5K_QCU_MISC_CBREXP_BCN); + + AR5K_REG_ENABLE_BITS(hal, AR5K_QUEUE_DFS_MISC(queue), + (AR5K_DCU_MISC_ARBLOCK_CTL_GLOBAL << + AR5K_DCU_MISC_ARBLOCK_CTL_S)); + break; + + case AR5K_TX_QUEUE_UAPSD: + AR5K_REG_ENABLE_BITS(hal, AR5K_QUEUE_MISC(queue), + AR5K_QCU_MISC_CBREXP); + break; + + case AR5K_TX_QUEUE_DATA: + default: + break; + } + + /* + * Enable tx queue in the secondary interrupt mask registers + */ + ath5k_hw_reg_write(hal, AR5K_REG_SM(hal->ah_txq_interrupts, + AR5K_SIMR0_QCU_TXOK) | + AR5K_REG_SM(hal->ah_txq_interrupts, + AR5K_SIMR0_QCU_TXDESC), AR5K_SIMR0); + ath5k_hw_reg_write(hal, AR5K_REG_SM(hal->ah_txq_interrupts, + AR5K_SIMR1_QCU_TXERR), AR5K_SIMR1); + ath5k_hw_reg_write(hal, AR5K_REG_SM(hal->ah_txq_interrupts, + AR5K_SIMR2_QCU_TXURN), AR5K_SIMR2); + } + + return 0; +} + +/* + * Get number of pending frames + * for a specific queue [5211+] + */ +u32 ath5k_hw_num_tx_pending(struct ath_hw *hal, unsigned int queue) { + AR5K_TRACE; + AR5K_ASSERT_ENTRY(queue, hal->ah_capabilities.cap_queues.q_tx_num); + + /* Return if queue is declared inactive */ + if (hal->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE) + return false; + + /* XXX: How about AR5K_CFG_TXCNT ? */ + if (hal->ah_version == AR5K_AR5210) + return false; + + return AR5K_QUEUE_STATUS(queue) & AR5K_QCU_STS_FRMPENDCNT; +} + +/* + * Set slot time + */ +int ath5k_hw_set_slot_time(struct ath_hw *hal, unsigned int slot_time) +{ + AR5K_TRACE; + if (slot_time < AR5K_SLOT_TIME_9 || slot_time > AR5K_SLOT_TIME_MAX) + return -EINVAL; + + if (hal->ah_version == AR5K_AR5210) + ath5k_hw_reg_write(hal, ath5k_hw_htoclock(slot_time, + hal->ah_turbo), AR5K_SLOT_TIME); + else + ath5k_hw_reg_write(hal, slot_time, AR5K_DCU_GBL_IFS_SLOT); + + return 0; +} + +/* + * Get slot time + */ +unsigned int ath5k_hw_get_slot_time(struct ath_hw *hal) +{ + AR5K_TRACE; + if (hal->ah_version == AR5K_AR5210) + return ath5k_hw_clocktoh(ath5k_hw_reg_read(hal, + AR5K_SLOT_TIME) & 0xffff, hal->ah_turbo); + else + return ath5k_hw_reg_read(hal, AR5K_DCU_GBL_IFS_SLOT) & 0xffff; +} + + +/******************************\ + Hardware Descriptor Functions +\******************************/ + +/* + * TX Descriptor + */ + +/* + * Initialize the 2-word tx descriptor on 5210/5211 + */ +static int +ath5k_hw_setup_2word_tx_desc(struct ath_hw *hal, struct ath_desc *desc, + unsigned int pkt_len, unsigned int hdr_len, enum ath5k_pkt_type type, + unsigned int tx_power, unsigned int tx_rate0, unsigned int tx_tries0, + unsigned int key_index, unsigned int antenna_mode, unsigned int flags, + unsigned int rtscts_rate, unsigned int rtscts_duration) +{ + u32 frame_type; + struct ath5k_hw_2w_tx_desc *tx_desc; + + tx_desc = (struct ath5k_hw_2w_tx_desc *)&desc->ds_ctl0; + + if (tx_tries0 == 0) + return -EINVAL; + + /* Initialize control descriptor */ + tx_desc->tx_control_0 = 0; + tx_desc->tx_control_1 = 0; + + /* Setup control descriptor */ + + /*Verify packet length*/ + tx_desc->tx_control_0 = pkt_len & AR5K_2W_TX_DESC_CTL0_FRAME_LEN; + if (tx_desc->tx_control_0 != pkt_len) + return -EINVAL; + /* + * Verify header length + * XXX: I only found that on 5210 code, does it work on 5211 ? + */ + if (hal->ah_version == AR5K_AR5210) { + tx_desc->tx_control_0 = hdr_len & + AR5K_2W_TX_DESC_CTL0_HEADER_LEN; + if (tx_desc->tx_control_0 != hdr_len) + return -EINVAL; + } + + /*Diferences between 5210-5211*/ + if (hal->ah_version == AR5K_AR5210) { + switch (type) { + case AR5K_PKT_TYPE_BEACON: + case AR5K_PKT_TYPE_PROBE_RESP: + frame_type = AR5K_AR5210_TX_DESC_FRAME_TYPE_NO_DELAY; + case AR5K_PKT_TYPE_PIFS: + frame_type = AR5K_AR5210_TX_DESC_FRAME_TYPE_PIFS; + default: + frame_type = type /*<< 2 ?*/; + } + + tx_desc->tx_control_0 = + AR5K_REG_SM(frame_type, AR5K_2W_TX_DESC_CTL0_FRAME_TYPE) | + AR5K_REG_SM(tx_rate0, AR5K_2W_TX_DESC_CTL0_XMIT_RATE); + } else { + tx_desc->tx_control_0 |= + AR5K_REG_SM(tx_rate0, AR5K_2W_TX_DESC_CTL0_XMIT_RATE) | + AR5K_REG_SM(antenna_mode, AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT); + tx_desc->tx_control_1 = + AR5K_REG_SM(type, AR5K_2W_TX_DESC_CTL1_FRAME_TYPE); + } +#define _TX_FLAGS(_c, _flag) \ + if (flags & AR5K_TXDESC_##_flag) \ + tx_desc->tx_control_##_c |= \ + AR5K_2W_TX_DESC_CTL##_c##_##_flag + + _TX_FLAGS(0, CLRDMASK); + _TX_FLAGS(0, VEOL); + _TX_FLAGS(0, INTREQ); + _TX_FLAGS(0, RTSENA); + _TX_FLAGS(1, NOACK); + +#undef _TX_FLAGS + + /* + * WEP crap + */ + if (key_index != AR5K_TXKEYIX_INVALID) { + tx_desc->tx_control_0 |= + AR5K_2W_TX_DESC_CTL0_ENCRYPT_KEY_VALID; + tx_desc->tx_control_1 |= + AR5K_REG_SM(key_index, + AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX); + } + + /* + * RTS/CTS Duration [5210 ?] + */ + if ((hal->ah_version == AR5K_AR5210) && + (flags & (AR5K_TXDESC_RTSENA | AR5K_TXDESC_CTSENA))) + tx_desc->tx_control_1 |= rtscts_duration & + AR5K_2W_TX_DESC_CTL1_RTS_DURATION; + + return 0; +} + +/* + * Initialize the 4-word tx descriptor on 5212 + */ +static int ath5k_hw_setup_4word_tx_desc(struct ath_hw *hal, + struct ath_desc *desc, unsigned int pkt_len, unsigned int hdr_len, + enum ath5k_pkt_type type, unsigned int tx_power, unsigned int tx_rate0, + unsigned int tx_tries0, unsigned int key_index, + unsigned int antenna_mode, unsigned int flags, unsigned int rtscts_rate, + unsigned int rtscts_duration) +{ + struct ath5k_hw_4w_tx_desc *tx_desc; + + AR5K_TRACE; + + tx_desc = (struct ath5k_hw_4w_tx_desc *)&desc->ds_ctl0; + + /* + * Validate input + */ + if (tx_tries0 == 0) + return -EINVAL; + + /* Initialize status descriptor */ + tx_desc->tx_control_0 = 0; + tx_desc->tx_control_1 = 0; + tx_desc->tx_control_2 = 0; + tx_desc->tx_control_3 = 0; + + /* Setup status descriptor */ + tx_desc->tx_control_0 = pkt_len & AR5K_4W_TX_DESC_CTL0_FRAME_LEN; + if (tx_desc->tx_control_0 != pkt_len) + return -EINVAL; + + tx_desc->tx_control_0 |= + AR5K_REG_SM(tx_power, AR5K_4W_TX_DESC_CTL0_XMIT_POWER) | + AR5K_REG_SM(antenna_mode, AR5K_4W_TX_DESC_CTL0_ANT_MODE_XMIT); + tx_desc->tx_control_1 = AR5K_REG_SM(type, + AR5K_4W_TX_DESC_CTL1_FRAME_TYPE); + tx_desc->tx_control_2 = AR5K_REG_SM(tx_tries0 + AR5K_TUNE_HWTXTRIES, + AR5K_4W_TX_DESC_CTL2_XMIT_TRIES0); + tx_desc->tx_control_3 = tx_rate0 & AR5K_4W_TX_DESC_CTL3_XMIT_RATE0; + +#define _TX_FLAGS(_c, _flag) \ + if (flags & AR5K_TXDESC_##_flag) \ + tx_desc->tx_control_##_c |= \ + AR5K_4W_TX_DESC_CTL##_c##_##_flag + + _TX_FLAGS(0, CLRDMASK); + _TX_FLAGS(0, VEOL); + _TX_FLAGS(0, INTREQ); + _TX_FLAGS(0, RTSENA); + _TX_FLAGS(0, CTSENA); + _TX_FLAGS(1, NOACK); + +#undef _TX_FLAGS + + /* + * WEP crap + */ + if (key_index != AR5K_TXKEYIX_INVALID) { + tx_desc->tx_control_0 |= AR5K_4W_TX_DESC_CTL0_ENCRYPT_KEY_VALID; + tx_desc->tx_control_1 |= AR5K_REG_SM(key_index, + AR5K_4W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX); + } + + /* + * RTS/CTS + */ + if (flags & (AR5K_TXDESC_RTSENA | AR5K_TXDESC_CTSENA)) { + if ((flags & AR5K_TXDESC_RTSENA) && + (flags & AR5K_TXDESC_CTSENA)) + return -EINVAL; + tx_desc->tx_control_2 |= rtscts_duration & + AR5K_4W_TX_DESC_CTL2_RTS_DURATION; + tx_desc->tx_control_3 |= AR5K_REG_SM(rtscts_rate, + AR5K_4W_TX_DESC_CTL3_RTS_CTS_RATE); + } + + return 0; +} + +/* + * Initialize a 4-word XR tx descriptor on 5212 + */ +static bool +ath5k_hw_setup_xr_tx_desc(struct ath_hw *hal, struct ath_desc *desc, + unsigned int tx_rate1, u_int tx_tries1, u_int tx_rate2, u_int tx_tries2, + unsigned int tx_rate3, u_int tx_tries3) +{ + struct ath5k_hw_4w_tx_desc *tx_desc; + + if (hal->ah_version == AR5K_AR5212) { + tx_desc = (struct ath5k_hw_4w_tx_desc *)&desc->ds_ctl0; + +#define _XTX_TRIES(_n) \ + if (tx_tries##_n) { \ + tx_desc->tx_control_2 |= \ + AR5K_REG_SM(tx_tries##_n, \ + AR5K_4W_TX_DESC_CTL2_XMIT_TRIES##_n); \ + tx_desc->tx_control_3 |= \ + AR5K_REG_SM(tx_rate##_n, \ + AR5K_4W_TX_DESC_CTL3_XMIT_RATE##_n); \ + } + + _XTX_TRIES(1); + _XTX_TRIES(2); + _XTX_TRIES(3); + +#undef _XTX_TRIES + + return true; + } + + return false; +} + +/* + * Fill the 2-word tx descriptor on 5210/5211 + */ +static int ath5k_hw_fill_2word_tx_desc(struct ath_hw *hal, + struct ath_desc *desc, unsigned int segment_length, + bool first_segment, bool last_segment) +{ + struct ath5k_hw_2w_tx_desc *tx_desc; + + tx_desc = (struct ath5k_hw_2w_tx_desc *)&desc->ds_ctl0; + + /* Clear status descriptor */ + memset(desc->ds_hw, 0, sizeof(desc->ds_hw)); + + /* Validate segment length and initialize the descriptor */ + tx_desc->tx_control_1 = segment_length & AR5K_2W_TX_DESC_CTL1_BUF_LEN; + if (tx_desc->tx_control_1 != segment_length) + return -EINVAL; + + if (first_segment != true) + tx_desc->tx_control_0 &= ~AR5K_2W_TX_DESC_CTL0_FRAME_LEN; + + if (last_segment != true) + tx_desc->tx_control_1 |= AR5K_2W_TX_DESC_CTL1_MORE; + + return 0; +} + +/* + * Fill the 4-word tx descriptor on 5212 + * XXX: Added an argument *last_desc -need revision + */ +static int ath5k_hw_fill_4word_tx_desc(struct ath_hw *hal, + struct ath_desc *desc, unsigned int segment_length, + bool first_segment, bool last_segment) +{ + struct ath5k_hw_4w_tx_desc *tx_desc; + struct ath5k_hw_tx_status *tx_status; + + AR5K_TRACE; + tx_desc = (struct ath5k_hw_4w_tx_desc *)&desc->ds_ctl0; + tx_status = (struct ath5k_hw_tx_status *)&desc->ds_hw[2]; + + /* Clear status descriptor */ + memset(tx_status, 0, sizeof(struct ath5k_hw_tx_status)); + + /* Validate segment length and initialize the descriptor */ + tx_desc->tx_control_1 = segment_length & AR5K_4W_TX_DESC_CTL1_BUF_LEN; + if (tx_desc->tx_control_1 != segment_length) + return -EINVAL; + + if (first_segment != true) + tx_desc->tx_control_0 &= ~AR5K_4W_TX_DESC_CTL0_FRAME_LEN; + + if (last_segment != true) + tx_desc->tx_control_1 |= AR5K_4W_TX_DESC_CTL1_MORE; + + return 0; +} + +/* + * Proccess the tx status descriptor on 5210/5211 + */ +static int ath5k_hw_proc_2word_tx_status(struct ath_hw *hal, + struct ath_desc *desc) +{ + struct ath5k_hw_tx_status *tx_status; + struct ath5k_hw_2w_tx_desc *tx_desc; + + tx_desc = (struct ath5k_hw_2w_tx_desc *)&desc->ds_ctl0; + tx_status = (struct ath5k_hw_tx_status *)&desc->ds_hw[0]; + + /* No frame has been send or error */ + if (unlikely((tx_status->tx_status_1 & AR5K_DESC_TX_STATUS1_DONE) == 0)) + return -EINPROGRESS; + + /* + * Get descriptor status + */ + desc->ds_us.tx.ts_tstamp = AR5K_REG_MS(tx_status->tx_status_0, + AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP); + desc->ds_us.tx.ts_shortretry = AR5K_REG_MS(tx_status->tx_status_0, + AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT); + desc->ds_us.tx.ts_longretry = AR5K_REG_MS(tx_status->tx_status_0, + AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT); + /*TODO: desc->ds_us.tx.ts_virtcol + test*/ + desc->ds_us.tx.ts_seqnum = AR5K_REG_MS(tx_status->tx_status_1, + AR5K_DESC_TX_STATUS1_SEQ_NUM); + desc->ds_us.tx.ts_rssi = AR5K_REG_MS(tx_status->tx_status_1, + AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH); + desc->ds_us.tx.ts_antenna = 1; + desc->ds_us.tx.ts_status = 0; + desc->ds_us.tx.ts_rate = AR5K_REG_MS(tx_desc->tx_control_0, + AR5K_2W_TX_DESC_CTL0_XMIT_RATE); + + if ((tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FRAME_XMIT_OK) == 0){ + if (tx_status->tx_status_0 & + AR5K_DESC_TX_STATUS0_EXCESSIVE_RETRIES) + desc->ds_us.tx.ts_status |= AR5K_TXERR_XRETRY; + + if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FIFO_UNDERRUN) + desc->ds_us.tx.ts_status |= AR5K_TXERR_FIFO; + + if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FILTERED) + desc->ds_us.tx.ts_status |= AR5K_TXERR_FILT; + } + + return 0; +} + +/* + * Proccess a tx descriptor on 5212 + */ +static int ath5k_hw_proc_4word_tx_status(struct ath_hw *hal, + struct ath_desc *desc) +{ + struct ath5k_hw_tx_status *tx_status; + struct ath5k_hw_4w_tx_desc *tx_desc; + + AR5K_TRACE; + tx_desc = (struct ath5k_hw_4w_tx_desc *)&desc->ds_ctl0; + tx_status = (struct ath5k_hw_tx_status *)&desc->ds_hw[2]; + + /* No frame has been send or error */ + if (unlikely((tx_status->tx_status_1 & AR5K_DESC_TX_STATUS1_DONE) == 0)) + return -EINPROGRESS; + + /* + * Get descriptor status + */ + desc->ds_us.tx.ts_tstamp = AR5K_REG_MS(tx_status->tx_status_0, + AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP); + desc->ds_us.tx.ts_shortretry = AR5K_REG_MS(tx_status->tx_status_0, + AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT); + desc->ds_us.tx.ts_longretry = AR5K_REG_MS(tx_status->tx_status_0, + AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT); + desc->ds_us.tx.ts_seqnum = AR5K_REG_MS(tx_status->tx_status_1, + AR5K_DESC_TX_STATUS1_SEQ_NUM); + desc->ds_us.tx.ts_rssi = AR5K_REG_MS(tx_status->tx_status_1, + AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH); + desc->ds_us.tx.ts_antenna = (tx_status->tx_status_1 & + AR5K_DESC_TX_STATUS1_XMIT_ANTENNA) ? 2 : 1; + desc->ds_us.tx.ts_status = 0; + + switch (AR5K_REG_MS(tx_status->tx_status_1, + AR5K_DESC_TX_STATUS1_FINAL_TS_INDEX)) { + case 0: + desc->ds_us.tx.ts_rate = tx_desc->tx_control_3 & + AR5K_4W_TX_DESC_CTL3_XMIT_RATE0; + break; + case 1: + desc->ds_us.tx.ts_rate = AR5K_REG_MS(tx_desc->tx_control_3, + AR5K_4W_TX_DESC_CTL3_XMIT_RATE1); + desc->ds_us.tx.ts_longretry +=AR5K_REG_MS(tx_desc->tx_control_2, + AR5K_4W_TX_DESC_CTL2_XMIT_TRIES1); + break; + case 2: + desc->ds_us.tx.ts_rate = AR5K_REG_MS(tx_desc->tx_control_3, + AR5K_4W_TX_DESC_CTL3_XMIT_RATE2); + desc->ds_us.tx.ts_longretry +=AR5K_REG_MS(tx_desc->tx_control_2, + AR5K_4W_TX_DESC_CTL2_XMIT_TRIES2); + break; + case 3: + desc->ds_us.tx.ts_rate = AR5K_REG_MS(tx_desc->tx_control_3, + AR5K_4W_TX_DESC_CTL3_XMIT_RATE3); + desc->ds_us.tx.ts_longretry +=AR5K_REG_MS(tx_desc->tx_control_2, + AR5K_4W_TX_DESC_CTL2_XMIT_TRIES3); + break; + } + + if ((tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FRAME_XMIT_OK) == 0){ + if (tx_status->tx_status_0 & + AR5K_DESC_TX_STATUS0_EXCESSIVE_RETRIES) + desc->ds_us.tx.ts_status |= AR5K_TXERR_XRETRY; + + if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FIFO_UNDERRUN) + desc->ds_us.tx.ts_status |= AR5K_TXERR_FIFO; + + if (tx_status->tx_status_0 & AR5K_DESC_TX_STATUS0_FILTERED) + desc->ds_us.tx.ts_status |= AR5K_TXERR_FILT; + } + + return 0; +} + +/* + * RX Descriptor + */ + +/* + * Initialize an rx descriptor + */ +int ath5k_hw_setup_rx_desc(struct ath_hw *hal, struct ath_desc *desc, + u32 size, unsigned int flags) +{ + struct ath5k_rx_desc *rx_desc; + + AR5K_TRACE; + rx_desc = (struct ath5k_rx_desc *)&desc->ds_ctl0; + + /* + *Clear ds_hw + * If we don't clean the status descriptor, + * while scanning we get too many results, + * most of them virtual, after some secs + * of scanning system hangs. M.F. + */ + memset(desc->ds_hw, 0, sizeof(desc->ds_hw)); + + /*Initialize rx descriptor*/ + rx_desc->rx_control_0 = 0; + rx_desc->rx_control_1 = 0; + + /* Setup descriptor */ + rx_desc->rx_control_1 = size & AR5K_DESC_RX_CTL1_BUF_LEN; + if (unlikely(rx_desc->rx_control_1 != size)) + return -EINVAL; + + if (flags & AR5K_RXDESC_INTREQ) + rx_desc->rx_control_1 |= AR5K_DESC_RX_CTL1_INTREQ; + + return 0; +} + +/* + * Proccess the rx status descriptor on 5210/5211 + */ +static int ath5k_hw_proc_old_rx_status(struct ath_hw *hal, + struct ath_desc *desc) +{ + struct ath5k_hw_old_rx_status *rx_status; + + rx_status = (struct ath5k_hw_old_rx_status *)&desc->ds_hw[0]; + + /* No frame received / not ready */ + if (unlikely((rx_status->rx_status_1 & AR5K_OLD_RX_DESC_STATUS1_DONE) + == 0)) + return -EINPROGRESS; + + /* + * Frame receive status + */ + desc->ds_us.rx.rs_datalen = rx_status->rx_status_0 & + AR5K_OLD_RX_DESC_STATUS0_DATA_LEN; + desc->ds_us.rx.rs_rssi = AR5K_REG_MS(rx_status->rx_status_0, + AR5K_OLD_RX_DESC_STATUS0_RECEIVE_SIGNAL); + desc->ds_us.rx.rs_rate = AR5K_REG_MS(rx_status->rx_status_0, + AR5K_OLD_RX_DESC_STATUS0_RECEIVE_RATE); + desc->ds_us.rx.rs_antenna = rx_status->rx_status_0 & + AR5K_OLD_RX_DESC_STATUS0_RECEIVE_ANTENNA; + desc->ds_us.rx.rs_more = rx_status->rx_status_0 & + AR5K_OLD_RX_DESC_STATUS0_MORE; + desc->ds_us.rx.rs_tstamp = AR5K_REG_MS(rx_status->rx_status_1, + AR5K_OLD_RX_DESC_STATUS1_RECEIVE_TIMESTAMP); + desc->ds_us.rx.rs_status = 0; + + /* + * Key table status + */ + if (rx_status->rx_status_1 & AR5K_OLD_RX_DESC_STATUS1_KEY_INDEX_VALID) + desc->ds_us.rx.rs_keyix = AR5K_REG_MS(rx_status->rx_status_1, + AR5K_OLD_RX_DESC_STATUS1_KEY_INDEX); + else + desc->ds_us.rx.rs_keyix = AR5K_RXKEYIX_INVALID; + + /* + * Receive/descriptor errors + */ + if ((rx_status->rx_status_1 & AR5K_OLD_RX_DESC_STATUS1_FRAME_RECEIVE_OK) + == 0) { + if (rx_status->rx_status_1 & AR5K_OLD_RX_DESC_STATUS1_CRC_ERROR) + desc->ds_us.rx.rs_status |= AR5K_RXERR_CRC; + + if (rx_status->rx_status_1 & + AR5K_OLD_RX_DESC_STATUS1_FIFO_OVERRUN) + desc->ds_us.rx.rs_status |= AR5K_RXERR_FIFO; + + if (rx_status->rx_status_1 & + AR5K_OLD_RX_DESC_STATUS1_PHY_ERROR) { + desc->ds_us.rx.rs_status |= AR5K_RXERR_PHY; + desc->ds_us.rx.rs_phyerr = + AR5K_REG_MS(rx_status->rx_status_1, + AR5K_OLD_RX_DESC_STATUS1_PHY_ERROR); + } + + if (rx_status->rx_status_1 & + AR5K_OLD_RX_DESC_STATUS1_DECRYPT_CRC_ERROR) + desc->ds_us.rx.rs_status |= AR5K_RXERR_DECRYPT; + } + + return 0; +} + +/* + * Proccess the rx status descriptor on 5212 + */ +static int ath5k_hw_proc_new_rx_status(struct ath_hw *hal, + struct ath_desc *desc) +{ + struct ath5k_hw_new_rx_status *rx_status; + struct ath5k_hw_rx_error *rx_err; + + AR5K_TRACE; + rx_status = (struct ath5k_hw_new_rx_status *)&desc->ds_hw[0]; + + /* Overlay on error */ + rx_err = (struct ath5k_hw_rx_error *)&desc->ds_hw[0]; + + /* No frame received / not ready */ + if (unlikely((rx_status->rx_status_1 & AR5K_NEW_RX_DESC_STATUS1_DONE) + == 0)) + return -EINPROGRESS; + + /* + * Frame receive status + */ + desc->ds_us.rx.rs_datalen = rx_status->rx_status_0 & + AR5K_NEW_RX_DESC_STATUS0_DATA_LEN; + desc->ds_us.rx.rs_rssi = AR5K_REG_MS(rx_status->rx_status_0, + AR5K_NEW_RX_DESC_STATUS0_RECEIVE_SIGNAL); + desc->ds_us.rx.rs_rate = AR5K_REG_MS(rx_status->rx_status_0, + AR5K_NEW_RX_DESC_STATUS0_RECEIVE_RATE); + desc->ds_us.rx.rs_antenna = rx_status->rx_status_0 & + AR5K_NEW_RX_DESC_STATUS0_RECEIVE_ANTENNA; + desc->ds_us.rx.rs_more = rx_status->rx_status_0 & + AR5K_NEW_RX_DESC_STATUS0_MORE; + desc->ds_us.rx.rs_tstamp = AR5K_REG_MS(rx_status->rx_status_1, + AR5K_NEW_RX_DESC_STATUS1_RECEIVE_TIMESTAMP); + desc->ds_us.rx.rs_status = 0; + + /* + * Key table status + */ + if (rx_status->rx_status_1 & AR5K_NEW_RX_DESC_STATUS1_KEY_INDEX_VALID) + desc->ds_us.rx.rs_keyix = AR5K_REG_MS(rx_status->rx_status_1, + AR5K_NEW_RX_DESC_STATUS1_KEY_INDEX); + else + desc->ds_us.rx.rs_keyix = AR5K_RXKEYIX_INVALID; + + /* + * Receive/descriptor errors + */ + if ((rx_status->rx_status_1 & + AR5K_NEW_RX_DESC_STATUS1_FRAME_RECEIVE_OK) == 0) { + if (rx_status->rx_status_1 & AR5K_NEW_RX_DESC_STATUS1_CRC_ERROR) + desc->ds_us.rx.rs_status |= AR5K_RXERR_CRC; + + if (rx_status->rx_status_1 & + AR5K_NEW_RX_DESC_STATUS1_PHY_ERROR) { + desc->ds_us.rx.rs_status |= AR5K_RXERR_PHY; + desc->ds_us.rx.rs_phyerr = + AR5K_REG_MS(rx_err->rx_error_1, + AR5K_RX_DESC_ERROR1_PHY_ERROR_CODE); + } + + if (rx_status->rx_status_1 & + AR5K_NEW_RX_DESC_STATUS1_DECRYPT_CRC_ERROR) + desc->ds_us.rx.rs_status |= AR5K_RXERR_DECRYPT; + + if (rx_status->rx_status_1 & AR5K_NEW_RX_DESC_STATUS1_MIC_ERROR) + desc->ds_us.rx.rs_status |= AR5K_RXERR_MIC; + } + + return 0; +} + + +/****************\ + GPIO Functions +\****************/ + +/* + * Set led state + */ +void ath5k_hw_set_ledstate(struct ath_hw *hal, unsigned int state) +{ + u32 led; + /*5210 has different led mode handling*/ + u32 led_5210; + + AR5K_TRACE; + + /*Reset led status*/ + if (hal->ah_version != AR5K_AR5210) + AR5K_REG_DISABLE_BITS(hal, AR5K_PCICFG, + AR5K_PCICFG_LEDMODE | AR5K_PCICFG_LED); + else + AR5K_REG_DISABLE_BITS(hal, AR5K_PCICFG, AR5K_PCICFG_LED); + + /* + * Some blinking values, define at your wish + */ + switch (state) { + case AR5K_LED_SCAN: + case AR5K_LED_AUTH: + led = AR5K_PCICFG_LEDMODE_PROP | AR5K_PCICFG_LED_PEND; + led_5210 = AR5K_PCICFG_LED_PEND | AR5K_PCICFG_LED_BCTL; + break; + + case AR5K_LED_INIT: + led = AR5K_PCICFG_LEDMODE_PROP | AR5K_PCICFG_LED_NONE; + led_5210 = AR5K_PCICFG_LED_PEND; + break; + + case AR5K_LED_ASSOC: + case AR5K_LED_RUN: + led = AR5K_PCICFG_LEDMODE_PROP | AR5K_PCICFG_LED_ASSOC; + led_5210 = AR5K_PCICFG_LED_ASSOC; + break; + + default: + led = AR5K_PCICFG_LEDMODE_PROM | AR5K_PCICFG_LED_NONE; + led_5210 = AR5K_PCICFG_LED_PEND; + break; + } + + /*Write new status to the register*/ + if (hal->ah_version != AR5K_AR5210) + AR5K_REG_ENABLE_BITS(hal, AR5K_PCICFG, led); + else + AR5K_REG_ENABLE_BITS(hal, AR5K_PCICFG, led_5210); +} + +/* + * Set GPIO outputs + */ +int ath5k_hw_set_gpio_output(struct ath_hw *hal, u32 gpio) +{ + AR5K_TRACE; + if (gpio > AR5K_NUM_GPIO) + return -EINVAL; + + ath5k_hw_reg_write(hal, (ath5k_hw_reg_read(hal, AR5K_GPIOCR) &~ + AR5K_GPIOCR_OUT(gpio)) | AR5K_GPIOCR_OUT(gpio), AR5K_GPIOCR); + + return 0; +} + +/* + * Set GPIO inputs + */ +int ath5k_hw_set_gpio_input(struct ath_hw *hal, u32 gpio) +{ + AR5K_TRACE; + if (gpio > AR5K_NUM_GPIO) + return -EINVAL; + + ath5k_hw_reg_write(hal, (ath5k_hw_reg_read(hal, AR5K_GPIOCR) &~ + AR5K_GPIOCR_OUT(gpio)) | AR5K_GPIOCR_IN(gpio), AR5K_GPIOCR); + + return 0; +} + +/* + * Get GPIO state + */ +u32 ath5k_hw_get_gpio(struct ath_hw *hal, u32 gpio) +{ + AR5K_TRACE; + if (gpio > AR5K_NUM_GPIO) + return 0xffffffff; + + /* GPIO input magic */ + return ((ath5k_hw_reg_read(hal, AR5K_GPIODI) & AR5K_GPIODI_M) >> gpio) & + 0x1; +} + +/* + * Set GPIO state + */ +int ath5k_hw_set_gpio(struct ath_hw *hal, u32 gpio, u32 val) +{ + u32 data; + AR5K_TRACE; + + if (gpio > AR5K_NUM_GPIO) + return -EINVAL; + + /* GPIO output magic */ + data = ath5k_hw_reg_read(hal, AR5K_GPIODO); + + data &= ~(1 << gpio); + data |= (val & 1) << gpio; + + ath5k_hw_reg_write(hal, data, AR5K_GPIODO); + + return 0; +} + +/* + * Initialize the GPIO interrupt (RFKill switch) + */ +void ath5k_hw_set_gpio_intr(struct ath_hw *hal, unsigned int gpio, + u32 interrupt_level) +{ + u32 data; + + AR5K_TRACE; + if (gpio > AR5K_NUM_GPIO) + return; + + /* + * Set the GPIO interrupt + */ + data = (ath5k_hw_reg_read(hal, AR5K_GPIOCR) & + ~(AR5K_GPIOCR_INT_SEL(gpio) | AR5K_GPIOCR_INT_SELH | + AR5K_GPIOCR_INT_ENA | AR5K_GPIOCR_OUT(gpio))) | + (AR5K_GPIOCR_INT_SEL(gpio) | AR5K_GPIOCR_INT_ENA); + + ath5k_hw_reg_write(hal, interrupt_level ? data : + (data | AR5K_GPIOCR_INT_SELH), AR5K_GPIOCR); + + hal->ah_imr |= AR5K_IMR_GPIO; + + /* Enable GPIO interrupts */ + AR5K_REG_ENABLE_BITS(hal, AR5K_PIMR, AR5K_IMR_GPIO); +} + + +/*********************************\ + Regulatory Domain/Channels Setup +\*********************************/ + +u16 ath5k_get_regdomain(struct ath_hw *hal) +{ + u16 regdomain; + enum ath5k_regdom ieee_regdomain; +#ifdef COUNTRYCODE + u16 code; +#endif + + ath5k_eeprom_regulation_domain(hal, false, &ieee_regdomain); + hal->ah_capabilities.cap_regdomain.reg_hw = ieee_regdomain; + +#ifdef COUNTRYCODE + /* + * Get the regulation domain by country code. This will ignore + * the settings found in the EEPROM. + */ + code = ieee80211_name2countrycode(COUNTRYCODE); + ieee_regdomain = ieee80211_countrycode2regdomain(code); +#endif + + regdomain = ath5k_regdom_from_ieee(ieee_regdomain); + hal->ah_capabilities.cap_regdomain.reg_current = regdomain; + + return regdomain; +} + + + +/****************\ + Misc functions +\****************/ + +void /*O.K.*/ +ath5k_hw_dump_state(struct ath_hw *hal) +{ +#ifdef AR5K_DEBUG +#define AR5K_PRINT_REGISTER(_x) \ + AR5K_PRINTF("(%s: %08x) ", #_x, ath5k_hw_reg_read(hal, AR5K_##_x)); + + AR5K_PRINT("MAC registers:\n"); + AR5K_PRINT_REGISTER(CR); + AR5K_PRINT_REGISTER(CFG); + AR5K_PRINT_REGISTER(IER); + AR5K_PRINT_REGISTER(TXCFG); + AR5K_PRINT_REGISTER(RXCFG); + AR5K_PRINT_REGISTER(MIBC); + AR5K_PRINT_REGISTER(TOPS); + AR5K_PRINT_REGISTER(RXNOFRM); + AR5K_PRINT_REGISTER(RPGTO); + AR5K_PRINT_REGISTER(RFCNT); + AR5K_PRINT_REGISTER(MISC); + AR5K_PRINT_REGISTER(PISR); + AR5K_PRINT_REGISTER(SISR0); + AR5K_PRINT_REGISTER(SISR1); + AR5K_PRINT_REGISTER(SISR3); + AR5K_PRINT_REGISTER(SISR4); + AR5K_PRINT_REGISTER(DCM_ADDR); + AR5K_PRINT_REGISTER(DCM_DATA); + AR5K_PRINT_REGISTER(DCCFG); + AR5K_PRINT_REGISTER(CCFG); + AR5K_PRINT_REGISTER(CCFG_CUP); + AR5K_PRINT_REGISTER(CPC0); + AR5K_PRINT_REGISTER(CPC1); + AR5K_PRINT_REGISTER(CPC2); + AR5K_PRINT_REGISTER(CPCORN); + AR5K_PRINT_REGISTER(QCU_TXE); + AR5K_PRINT_REGISTER(QCU_TXD); + AR5K_PRINT_REGISTER(DCU_GBL_IFS_SIFS); + AR5K_PRINT_REGISTER(DCU_GBL_IFS_SLOT); + AR5K_PRINT_REGISTER(DCU_FP); + AR5K_PRINT_REGISTER(DCU_TXP); + AR5K_PRINT_REGISTER(DCU_TX_FILTER); + AR5K_PRINT_REGISTER(INTPEND); + AR5K_PRINT_REGISTER(PCICFG); + AR5K_PRINT_REGISTER(GPIOCR); + AR5K_PRINT_REGISTER(GPIODO); + AR5K_PRINT_REGISTER(SREV); + AR5K_PRINT_REGISTER(EEPROM_BASE); + AR5K_PRINT_REGISTER(EEPROM_DATA); + AR5K_PRINT_REGISTER(EEPROM_CMD); + AR5K_PRINT_REGISTER(EEPROM_CFG); + AR5K_PRINT_REGISTER(PCU_MIN); + AR5K_PRINT_REGISTER(STA_ID0); + AR5K_PRINT_REGISTER(STA_ID1); + AR5K_PRINT_REGISTER(BSS_ID0); + AR5K_PRINT_REGISTER(SLOT_TIME); + AR5K_PRINT_REGISTER(TIME_OUT); + AR5K_PRINT_REGISTER(RSSI_THR); + AR5K_PRINT_REGISTER(BEACON); + AR5K_PRINT_REGISTER(CFP_PERIOD); + AR5K_PRINT_REGISTER(TIMER0); + AR5K_PRINT_REGISTER(TIMER2); + AR5K_PRINT_REGISTER(TIMER3); + AR5K_PRINT_REGISTER(CFP_DUR); + AR5K_PRINT_REGISTER(MCAST_FILTER0); + AR5K_PRINT_REGISTER(MCAST_FILTER1); + AR5K_PRINT_REGISTER(DIAG_SW); + AR5K_PRINT_REGISTER(TSF_U32); + AR5K_PRINT_REGISTER(ADDAC_TEST); + AR5K_PRINT_REGISTER(DEFAULT_ANTENNA); + AR5K_PRINT_REGISTER(LAST_TSTP); + AR5K_PRINT_REGISTER(NAV); + AR5K_PRINT_REGISTER(RTS_OK); + AR5K_PRINT_REGISTER(ACK_FAIL); + AR5K_PRINT_REGISTER(FCS_FAIL); + AR5K_PRINT_REGISTER(BEACON_CNT); + AR5K_PRINT_REGISTER(TSF_PARM); + AR5K_PRINT("\n"); + + AR5K_PRINT("PHY registers:\n"); + AR5K_PRINT_REGISTER(PHY_TURBO); + AR5K_PRINT_REGISTER(PHY_AGC); + AR5K_PRINT_REGISTER(PHY_TIMING_3); + AR5K_PRINT_REGISTER(PHY_CHIP_ID); + AR5K_PRINT_REGISTER(PHY_AGCCTL); + AR5K_PRINT_REGISTER(PHY_NF); + AR5K_PRINT_REGISTER(PHY_SCR); + AR5K_PRINT_REGISTER(PHY_SLMT); + AR5K_PRINT_REGISTER(PHY_SCAL); + AR5K_PRINT_REGISTER(PHY_RX_DELAY); + AR5K_PRINT_REGISTER(PHY_IQ); + AR5K_PRINT_REGISTER(PHY_PAPD_PROBE); + AR5K_PRINT_REGISTER(PHY_TXPOWER_RATE1); + AR5K_PRINT_REGISTER(PHY_TXPOWER_RATE2); + AR5K_PRINT_REGISTER(PHY_RADAR); + AR5K_PRINT_REGISTER(PHY_ANT_SWITCH_TABLE_0); + AR5K_PRINT_REGISTER(PHY_ANT_SWITCH_TABLE_1); + AR5K_PRINT("\n"); +#endif +} + +int ath5k_hw_get_capability(struct ath_hw *hal, + enum ath5k_capability_type cap_type, + u32 capability, u32 *result) +{ + AR5K_TRACE; + + switch (cap_type) { + case AR5K_CAP_NUM_TXQUEUES: + if (result) { + if (hal->ah_version == AR5K_AR5210) + *result = AR5K_NUM_TX_QUEUES_NOQCU; + else + *result = AR5K_NUM_TX_QUEUES; + goto yes; + } + case AR5K_CAP_VEOL: + goto yes; + case AR5K_CAP_COMPRESSION: + if (hal->ah_version == AR5K_AR5212) + goto yes; + else + goto no; + case AR5K_CAP_BURST: + goto yes; + case AR5K_CAP_TPC: + goto yes; + case AR5K_CAP_BSSIDMASK: + if (hal->ah_version == AR5K_AR5212) + goto yes; + else + goto no; + case AR5K_CAP_XR: + if (hal->ah_version == AR5K_AR5212) + goto yes; + else + goto no; + default: + goto no; + } + +no: + return -EINVAL; +yes: + return 0; +} + +static int ath5k_hw_enable_pspoll(struct ath_hw *hal, u8 *bssid, + u16 assoc_id) +{ + AR5K_TRACE; + + if (hal->ah_version == AR5K_AR5210) { + AR5K_REG_DISABLE_BITS(hal, AR5K_STA_ID1, + AR5K_STA_ID1_NO_PSPOLL | AR5K_STA_ID1_DEFAULT_ANTENNA); + return 0; + } + + return -EIO; +} + +static int ath5k_hw_disable_pspoll(struct ath_hw *hal) +{ + AR5K_TRACE; + + if (hal->ah_version == AR5K_AR5210) { + AR5K_REG_ENABLE_BITS(hal, AR5K_STA_ID1, + AR5K_STA_ID1_NO_PSPOLL | AR5K_STA_ID1_DEFAULT_ANTENNA); + return 0; + } + + return -EIO; +} diff -puN /dev/null drivers/net/wireless/ath5k/hw.h --- /dev/null +++ a/drivers/net/wireless/ath5k/hw.h @@ -0,0 +1,588 @@ +/* + * Copyright (c) 2004-2007 Reyk Floeter + * Copyright (c) 2006-2007 Nick Kossifidis + * Copyright (c) 2007 Matthew W. S. Bell + * Copyright (c) 2007 Luis Rodriguez + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +/* + * Gain settings + */ + +enum ath5k_rfgain { + AR5K_RFGAIN_INACTIVE = 0, + AR5K_RFGAIN_READ_REQUESTED, + AR5K_RFGAIN_NEED_CHANGE, +}; + +#define AR5K_GAIN_CRN_FIX_BITS_5111 4 +#define AR5K_GAIN_CRN_FIX_BITS_5112 7 +#define AR5K_GAIN_CRN_MAX_FIX_BITS AR5K_GAIN_CRN_FIX_BITS_5112 +#define AR5K_GAIN_DYN_ADJUST_HI_MARGIN 15 +#define AR5K_GAIN_DYN_ADJUST_LO_MARGIN 20 +#define AR5K_GAIN_CCK_PROBE_CORR 5 +#define AR5K_GAIN_CCK_OFDM_GAIN_DELTA 15 +#define AR5K_GAIN_STEP_COUNT 10 +#define AR5K_GAIN_PARAM_TX_CLIP 0 +#define AR5K_GAIN_PARAM_PD_90 1 +#define AR5K_GAIN_PARAM_PD_84 2 +#define AR5K_GAIN_PARAM_GAIN_SEL 3 +#define AR5K_GAIN_PARAM_MIX_ORN 0 +#define AR5K_GAIN_PARAM_PD_138 1 +#define AR5K_GAIN_PARAM_PD_137 2 +#define AR5K_GAIN_PARAM_PD_136 3 +#define AR5K_GAIN_PARAM_PD_132 4 +#define AR5K_GAIN_PARAM_PD_131 5 +#define AR5K_GAIN_PARAM_PD_130 6 +#define AR5K_GAIN_CHECK_ADJUST(_g) \ + ((_g)->g_current <= (_g)->g_low || (_g)->g_current >= (_g)->g_high) + +struct ath5k_gain_opt_step { + s16 gos_param[AR5K_GAIN_CRN_MAX_FIX_BITS]; + s32 gos_gain; +}; + +struct ath5k_gain { + u32 g_step_idx; + u32 g_current; + u32 g_target; + u32 g_low; + u32 g_high; + u32 g_f_corr; + u32 g_active; + const struct ath5k_gain_opt_step *g_step; +}; + + +/* + * HW SPECIFIC STRUCTS + */ + +/* Some EEPROM defines */ +#define AR5K_EEPROM_EEP_SCALE 100 +#define AR5K_EEPROM_EEP_DELTA 10 +#define AR5K_EEPROM_N_MODES 3 +#define AR5K_EEPROM_N_5GHZ_CHAN 10 +#define AR5K_EEPROM_N_2GHZ_CHAN 3 +#define AR5K_EEPROM_MAX_CHAN 10 +#define AR5K_EEPROM_N_PCDAC 11 +#define AR5K_EEPROM_N_TEST_FREQ 8 +#define AR5K_EEPROM_N_EDGES 8 +#define AR5K_EEPROM_N_INTERCEPTS 11 +#define AR5K_EEPROM_FREQ_M(_v) AR5K_EEPROM_OFF(_v, 0x7f, 0xff) +#define AR5K_EEPROM_PCDAC_M 0x3f +#define AR5K_EEPROM_PCDAC_START 1 +#define AR5K_EEPROM_PCDAC_STOP 63 +#define AR5K_EEPROM_PCDAC_STEP 1 +#define AR5K_EEPROM_NON_EDGE_M 0x40 +#define AR5K_EEPROM_CHANNEL_POWER 8 +#define AR5K_EEPROM_N_OBDB 4 +#define AR5K_EEPROM_OBDB_DIS 0xffff +#define AR5K_EEPROM_CHANNEL_DIS 0xff +#define AR5K_EEPROM_SCALE_OC_DELTA(_x) (((_x) * 2) / 10) +#define AR5K_EEPROM_N_CTLS(_v) AR5K_EEPROM_OFF(_v, 16, 32) +#define AR5K_EEPROM_MAX_CTLS 32 +#define AR5K_EEPROM_N_XPD_PER_CHANNEL 4 +#define AR5K_EEPROM_N_XPD0_POINTS 4 +#define AR5K_EEPROM_N_XPD3_POINTS 3 +#define AR5K_EEPROM_N_INTERCEPT_10_2GHZ 35 +#define AR5K_EEPROM_N_INTERCEPT_10_5GHZ 55 +#define AR5K_EEPROM_POWER_M 0x3f +#define AR5K_EEPROM_POWER_MIN 0 +#define AR5K_EEPROM_POWER_MAX 3150 +#define AR5K_EEPROM_POWER_STEP 50 +#define AR5K_EEPROM_POWER_TABLE_SIZE 64 +#define AR5K_EEPROM_N_POWER_LOC_11B 4 +#define AR5K_EEPROM_N_POWER_LOC_11G 6 +#define AR5K_EEPROM_I_GAIN 10 +#define AR5K_EEPROM_CCK_OFDM_DELTA 15 +#define AR5K_EEPROM_N_IQ_CAL 2 + +/* Struct to hold EEPROM calibration data */ +struct ath5k_eeprom_info { + u16 ee_magic; + u16 ee_protect; + u16 ee_regdomain; + u16 ee_version; + u16 ee_header; + u16 ee_ant_gain; + u16 ee_misc0; + u16 ee_misc1; + u16 ee_cck_ofdm_gain_delta; + u16 ee_cck_ofdm_power_delta; + u16 ee_scaled_cck_delta; + + /* Used for tx thermal adjustment (eeprom_init, rfregs) */ + u16 ee_tx_clip; + u16 ee_pwd_84; + u16 ee_pwd_90; + u16 ee_gain_select; + + /* RF Calibration settings (reset, rfregs) */ + u16 ee_i_cal[AR5K_EEPROM_N_MODES]; + u16 ee_q_cal[AR5K_EEPROM_N_MODES]; + u16 ee_fixed_bias[AR5K_EEPROM_N_MODES]; + u16 ee_turbo_max_power[AR5K_EEPROM_N_MODES]; + u16 ee_xr_power[AR5K_EEPROM_N_MODES]; + u16 ee_switch_settling[AR5K_EEPROM_N_MODES]; + u16 ee_ant_tx_rx[AR5K_EEPROM_N_MODES]; + u16 ee_ant_control[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_PCDAC]; + u16 ee_ob[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_OBDB]; + u16 ee_db[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_OBDB]; + u16 ee_tx_end2xlna_enable[AR5K_EEPROM_N_MODES]; + u16 ee_tx_end2xpa_disable[AR5K_EEPROM_N_MODES]; + u16 ee_tx_frm2xpa_enable[AR5K_EEPROM_N_MODES]; + u16 ee_thr_62[AR5K_EEPROM_N_MODES]; + u16 ee_xlna_gain[AR5K_EEPROM_N_MODES]; + u16 ee_xpd[AR5K_EEPROM_N_MODES]; + u16 ee_x_gain[AR5K_EEPROM_N_MODES]; + u16 ee_i_gain[AR5K_EEPROM_N_MODES]; + u16 ee_margin_tx_rx[AR5K_EEPROM_N_MODES]; + + /* Unused */ + u16 ee_false_detect[AR5K_EEPROM_N_MODES]; + u16 ee_cal_pier[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_2GHZ_CHAN]; + u16 ee_channel[AR5K_EEPROM_N_MODES][AR5K_EEPROM_MAX_CHAN]; /*empty*/ + + /* Conformance test limits (Unused) */ + u16 ee_ctls; + u16 ee_ctl[AR5K_EEPROM_MAX_CTLS]; + + /* Noise Floor Calibration settings */ + s16 ee_noise_floor_thr[AR5K_EEPROM_N_MODES]; + s8 ee_adc_desired_size[AR5K_EEPROM_N_MODES]; + s8 ee_pga_desired_size[AR5K_EEPROM_N_MODES]; +}; + +/* + * Internal RX/TX descriptor structures + * (rX: reserved fields possibily used by future versions of the ar5k chipset) + */ + +struct ath5k_rx_desc { + u32 rx_control_0; /* RX control word 0 */ + +#define AR5K_DESC_RX_CTL0 0x00000000 + + u32 rx_control_1; /* RX control word 1 */ + +#define AR5K_DESC_RX_CTL1_BUF_LEN 0x00000fff +#define AR5K_DESC_RX_CTL1_INTREQ 0x00002000 +} __packed; + +/* + * 5210/5211 rx status descriptor + */ +struct ath5k_hw_old_rx_status { + u32 rx_status_0; /* RX status word 0 */ + +#define AR5K_OLD_RX_DESC_STATUS0_DATA_LEN 0x00000fff +#define AR5K_OLD_RX_DESC_STATUS0_MORE 0x00001000 +#define AR5K_OLD_RX_DESC_STATUS0_RECEIVE_RATE 0x00078000 +#define AR5K_OLD_RX_DESC_STATUS0_RECEIVE_RATE_S 15 +#define AR5K_OLD_RX_DESC_STATUS0_RECEIVE_SIGNAL 0x07f80000 +#define AR5K_OLD_RX_DESC_STATUS0_RECEIVE_SIGNAL_S 19 +#define AR5K_OLD_RX_DESC_STATUS0_RECEIVE_ANTENNA 0x38000000 +#define AR5K_OLD_RX_DESC_STATUS0_RECEIVE_ANTENNA_S 27 + + u32 rx_status_1; /* RX status word 1 */ + +#define AR5K_OLD_RX_DESC_STATUS1_DONE 0x00000001 +#define AR5K_OLD_RX_DESC_STATUS1_FRAME_RECEIVE_OK 0x00000002 +#define AR5K_OLD_RX_DESC_STATUS1_CRC_ERROR 0x00000004 +#define AR5K_OLD_RX_DESC_STATUS1_FIFO_OVERRUN 0x00000008 +#define AR5K_OLD_RX_DESC_STATUS1_DECRYPT_CRC_ERROR 0x00000010 +#define AR5K_OLD_RX_DESC_STATUS1_PHY_ERROR 0x000000e0 +#define AR5K_OLD_RX_DESC_STATUS1_PHY_ERROR_S 5 +#define AR5K_OLD_RX_DESC_STATUS1_KEY_INDEX_VALID 0x00000100 +#define AR5K_OLD_RX_DESC_STATUS1_KEY_INDEX 0x00007e00 +#define AR5K_OLD_RX_DESC_STATUS1_KEY_INDEX_S 9 +#define AR5K_OLD_RX_DESC_STATUS1_RECEIVE_TIMESTAMP 0x0fff8000 +#define AR5K_OLD_RX_DESC_STATUS1_RECEIVE_TIMESTAMP_S 15 +#define AR5K_OLD_RX_DESC_STATUS1_KEY_CACHE_MISS 0x10000000 +} __packed; + +/* + * 5212 rx status descriptor + */ +struct ath5k_hw_new_rx_status { + u32 rx_status_0; /* RX status word 0 */ + +#define AR5K_NEW_RX_DESC_STATUS0_DATA_LEN 0x00000fff +#define AR5K_NEW_RX_DESC_STATUS0_MORE 0x00001000 +#define AR5K_NEW_RX_DESC_STATUS0_DECOMP_CRC_ERROR 0x00002000 +#define AR5K_NEW_RX_DESC_STATUS0_RECEIVE_RATE 0x000f8000 +#define AR5K_NEW_RX_DESC_STATUS0_RECEIVE_RATE_S 15 +#define AR5K_NEW_RX_DESC_STATUS0_RECEIVE_SIGNAL 0x0ff00000 +#define AR5K_NEW_RX_DESC_STATUS0_RECEIVE_SIGNAL_S 20 +#define AR5K_NEW_RX_DESC_STATUS0_RECEIVE_ANTENNA 0xf0000000 +#define AR5K_NEW_RX_DESC_STATUS0_RECEIVE_ANTENNA_S 28 + + u32 rx_status_1; /* RX status word 1 */ + +#define AR5K_NEW_RX_DESC_STATUS1_DONE 0x00000001 +#define AR5K_NEW_RX_DESC_STATUS1_FRAME_RECEIVE_OK 0x00000002 +#define AR5K_NEW_RX_DESC_STATUS1_CRC_ERROR 0x00000004 +#define AR5K_NEW_RX_DESC_STATUS1_DECRYPT_CRC_ERROR 0x00000008 +#define AR5K_NEW_RX_DESC_STATUS1_PHY_ERROR 0x00000010 +#define AR5K_NEW_RX_DESC_STATUS1_MIC_ERROR 0x00000020 +#define AR5K_NEW_RX_DESC_STATUS1_KEY_INDEX_VALID 0x00000100 +#define AR5K_NEW_RX_DESC_STATUS1_KEY_INDEX 0x0000fe00 +#define AR5K_NEW_RX_DESC_STATUS1_KEY_INDEX_S 9 +#define AR5K_NEW_RX_DESC_STATUS1_RECEIVE_TIMESTAMP 0x7fff0000 +#define AR5K_NEW_RX_DESC_STATUS1_RECEIVE_TIMESTAMP_S 16 +#define AR5K_NEW_RX_DESC_STATUS1_KEY_CACHE_MISS 0x80000000 +} __packed; + +struct ath5k_hw_rx_error { + u32 rx_error_0; /* RX error word 0 */ + +#define AR5K_RX_DESC_ERROR0 0x00000000 + + u32 rx_error_1; /* RX error word 1 */ + +#define AR5K_RX_DESC_ERROR1_PHY_ERROR_CODE 0x0000ff00 +#define AR5K_RX_DESC_ERROR1_PHY_ERROR_CODE_S 8 +} __packed; + +#define AR5K_DESC_RX_PHY_ERROR_NONE 0x00 +#define AR5K_DESC_RX_PHY_ERROR_TIMING 0x20 +#define AR5K_DESC_RX_PHY_ERROR_PARITY 0x40 +#define AR5K_DESC_RX_PHY_ERROR_RATE 0x60 +#define AR5K_DESC_RX_PHY_ERROR_LENGTH 0x80 +#define AR5K_DESC_RX_PHY_ERROR_64QAM 0xa0 +#define AR5K_DESC_RX_PHY_ERROR_SERVICE 0xc0 +#define AR5K_DESC_RX_PHY_ERROR_TRANSMITOVR 0xe0 + +struct ath5k_hw_2w_tx_desc { + u32 tx_control_0; /* TX control word 0 */ + +#define AR5K_2W_TX_DESC_CTL0_FRAME_LEN 0x00000fff +#define AR5K_2W_TX_DESC_CTL0_HEADER_LEN 0x0003f000 /*[5210 ?]*/ +#define AR5K_2W_TX_DESC_CTL0_HEADER_LEN_S 12 +#define AR5K_2W_TX_DESC_CTL0_XMIT_RATE 0x003c0000 +#define AR5K_2W_TX_DESC_CTL0_XMIT_RATE_S 18 +#define AR5K_2W_TX_DESC_CTL0_RTSENA 0x00400000 +#define AR5K_2W_TX_DESC_CTL0_CLRDMASK 0x01000000 +#define AR5K_2W_TX_DESC_CTL0_LONG_PACKET 0x00800000 /*[5210]*/ +#define AR5K_2W_TX_DESC_CTL0_VEOL 0x00800000 /*[5211]*/ +#define AR5K_2W_TX_DESC_CTL0_FRAME_TYPE 0x1c000000 /*[5210]*/ +#define AR5K_2W_TX_DESC_CTL0_FRAME_TYPE_S 26 +#define AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_5210 0x02000000 +#define AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_5211 0x1e000000 +#define AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT (hal->ah_version == AR5K_AR5210 ? \ + AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_5210 : \ + AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_5211) +#define AR5K_2W_TX_DESC_CTL0_ANT_MODE_XMIT_S 25 +#define AR5K_2W_TX_DESC_CTL0_INTREQ 0x20000000 +#define AR5K_2W_TX_DESC_CTL0_ENCRYPT_KEY_VALID 0x40000000 + + u32 tx_control_1; /* TX control word 1 */ + +#define AR5K_2W_TX_DESC_CTL1_BUF_LEN 0x00000fff +#define AR5K_2W_TX_DESC_CTL1_MORE 0x00001000 +#define AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX_5210 0x0007e000 +#define AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX_5211 0x000fe000 +#define AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX (hal->ah_version == AR5K_AR5210 ? \ + AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX_5210 : \ + AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX_5211) +#define AR5K_2W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX_S 13 +#define AR5K_2W_TX_DESC_CTL1_FRAME_TYPE 0x00700000 /*[5211]*/ +#define AR5K_2W_TX_DESC_CTL1_FRAME_TYPE_S 20 +#define AR5K_2W_TX_DESC_CTL1_NOACK 0x00800000 /*[5211]*/ +#define AR5K_2W_TX_DESC_CTL1_RTS_DURATION 0xfff80000 /*[5210 ?]*/ +} __packed; + +#define AR5K_AR5210_TX_DESC_FRAME_TYPE_NORMAL 0x00 +#define AR5K_AR5210_TX_DESC_FRAME_TYPE_ATIM 0x04 +#define AR5K_AR5210_TX_DESC_FRAME_TYPE_PSPOLL 0x08 +#define AR5K_AR5210_TX_DESC_FRAME_TYPE_NO_DELAY 0x0c +#define AR5K_AR5210_TX_DESC_FRAME_TYPE_PIFS 0x10 + +/* + * 5212 4-word tx control descriptor + */ +struct ath5k_hw_4w_tx_desc { + u32 tx_control_0; /* TX control word 0 */ + +#define AR5K_4W_TX_DESC_CTL0_FRAME_LEN 0x00000fff +#define AR5K_4W_TX_DESC_CTL0_XMIT_POWER 0x003f0000 +#define AR5K_4W_TX_DESC_CTL0_XMIT_POWER_S 16 +#define AR5K_4W_TX_DESC_CTL0_RTSENA 0x00400000 +#define AR5K_4W_TX_DESC_CTL0_VEOL 0x00800000 +#define AR5K_4W_TX_DESC_CTL0_CLRDMASK 0x01000000 +#define AR5K_4W_TX_DESC_CTL0_ANT_MODE_XMIT 0x1e000000 +#define AR5K_4W_TX_DESC_CTL0_ANT_MODE_XMIT_S 25 +#define AR5K_4W_TX_DESC_CTL0_INTREQ 0x20000000 +#define AR5K_4W_TX_DESC_CTL0_ENCRYPT_KEY_VALID 0x40000000 +#define AR5K_4W_TX_DESC_CTL0_CTSENA 0x80000000 + + u32 tx_control_1; /* TX control word 1 */ + +#define AR5K_4W_TX_DESC_CTL1_BUF_LEN 0x00000fff +#define AR5K_4W_TX_DESC_CTL1_MORE 0x00001000 +#define AR5K_4W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX 0x000fe000 +#define AR5K_4W_TX_DESC_CTL1_ENCRYPT_KEY_INDEX_S 13 +#define AR5K_4W_TX_DESC_CTL1_FRAME_TYPE 0x00f00000 +#define AR5K_4W_TX_DESC_CTL1_FRAME_TYPE_S 20 +#define AR5K_4W_TX_DESC_CTL1_NOACK 0x01000000 +#define AR5K_4W_TX_DESC_CTL1_COMP_PROC 0x06000000 +#define AR5K_4W_TX_DESC_CTL1_COMP_PROC_S 25 +#define AR5K_4W_TX_DESC_CTL1_COMP_IV_LEN 0x18000000 +#define AR5K_4W_TX_DESC_CTL1_COMP_IV_LEN_S 27 +#define AR5K_4W_TX_DESC_CTL1_COMP_ICV_LEN 0x60000000 +#define AR5K_4W_TX_DESC_CTL1_COMP_ICV_LEN_S 29 + + u32 tx_control_2; /* TX control word 2 */ + +#define AR5K_4W_TX_DESC_CTL2_RTS_DURATION 0x00007fff +#define AR5K_4W_TX_DESC_CTL2_DURATION_UPDATE_ENABLE 0x00008000 +#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES0 0x000f0000 +#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES0_S 16 +#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES1 0x00f00000 +#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES1_S 20 +#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES2 0x0f000000 +#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES2_S 24 +#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES3 0xf0000000 +#define AR5K_4W_TX_DESC_CTL2_XMIT_TRIES3_S 28 + + u32 tx_control_3; /* TX control word 3 */ + +#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE0 0x0000001f +#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE1 0x000003e0 +#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE1_S 5 +#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE2 0x00007c00 +#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE2_S 10 +#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE3 0x000f8000 +#define AR5K_4W_TX_DESC_CTL3_XMIT_RATE3_S 15 +#define AR5K_4W_TX_DESC_CTL3_RTS_CTS_RATE 0x01f00000 +#define AR5K_4W_TX_DESC_CTL3_RTS_CTS_RATE_S 20 +} __packed; + +/* + * Common tx status descriptor + */ +struct ath5k_hw_tx_status { + u32 tx_status_0; /* TX status word 0 */ + +#define AR5K_DESC_TX_STATUS0_FRAME_XMIT_OK 0x00000001 +#define AR5K_DESC_TX_STATUS0_EXCESSIVE_RETRIES 0x00000002 +#define AR5K_DESC_TX_STATUS0_FIFO_UNDERRUN 0x00000004 +#define AR5K_DESC_TX_STATUS0_FILTERED 0x00000008 +/*??? +#define AR5K_DESC_TX_STATUS0_RTS_FAIL_COUNT 0x000000f0 +#define AR5K_DESC_TX_STATUS0_RTS_FAIL_COUNT_S 4 +*/ +#define AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT 0x000000f0 +#define AR5K_DESC_TX_STATUS0_SHORT_RETRY_COUNT_S 4 +/*??? +#define AR5K_DESC_TX_STATUS0_DATA_FAIL_COUNT 0x00000f00 +#define AR5K_DESC_TX_STATUS0_DATA_FAIL_COUNT_S 8 +*/ +#define AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT 0x00000f00 +#define AR5K_DESC_TX_STATUS0_LONG_RETRY_COUNT_S 8 +#define AR5K_DESC_TX_STATUS0_VIRT_COLL_COUNT 0x0000f000 +#define AR5K_DESC_TX_STATUS0_VIRT_COLL_COUNT_S 12 +#define AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP 0xffff0000 +#define AR5K_DESC_TX_STATUS0_SEND_TIMESTAMP_S 16 + + u32 tx_status_1; /* TX status word 1 */ + +#define AR5K_DESC_TX_STATUS1_DONE 0x00000001 +#define AR5K_DESC_TX_STATUS1_SEQ_NUM 0x00001ffe +#define AR5K_DESC_TX_STATUS1_SEQ_NUM_S 1 +#define AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH 0x001fe000 +#define AR5K_DESC_TX_STATUS1_ACK_SIG_STRENGTH_S 13 +#define AR5K_DESC_TX_STATUS1_FINAL_TS_INDEX 0x00600000 +#define AR5K_DESC_TX_STATUS1_FINAL_TS_INDEX_S 21 +#define AR5K_DESC_TX_STATUS1_COMP_SUCCESS 0x00800000 +#define AR5K_DESC_TX_STATUS1_XMIT_ANTENNA 0x01000000 +} __packed; + + +/* + * AR5K REGISTER ACCESS + */ + +/*Swap RX/TX Descriptor for big endian archs*/ +#if defined(__BIG_ENDIAN) +#define AR5K_INIT_CFG ( \ + AR5K_CFG_SWTD | AR5K_CFG_SWRD \ +) +#else +#define AR5K_INIT_CFG 0x00000000 +#endif + +/*#define AR5K_REG_READ(_reg) ath5k_hw_reg_read(hal, _reg) + +#define AR5K_REG_WRITE(_reg, _val) ath5k_hw_reg_write(hal, _val, _reg)*/ + +#define AR5K_REG_SM(_val, _flags) \ + (((_val) << _flags##_S) & (_flags)) + +#define AR5K_REG_MS(_val, _flags) \ + (((_val) & (_flags)) >> _flags##_S) + +/* Some registers can hold multiple values of interest. For this + * reason when we want to write to these registers we must first + * retrieve the values which we do not want to clear (lets call this + * old_data) and then set the register with this and our new_value: + * ( old_data | new_value) */ +#define AR5K_REG_WRITE_BITS(hal, _reg, _flags, _val) \ + ath5k_hw_reg_write(hal, (ath5k_hw_reg_read(hal, _reg) & ~(_flags)) | \ + (((_val) << _flags##_S) & (_flags)), _reg) + +#define AR5K_REG_MASKED_BITS(hal, _reg, _flags, _mask) \ + ath5k_hw_reg_write(hal, (ath5k_hw_reg_read(hal, _reg) & \ + (_mask)) | (_flags), _reg) + +#define AR5K_REG_ENABLE_BITS(hal, _reg, _flags) \ + ath5k_hw_reg_write(hal, ath5k_hw_reg_read(hal, _reg) | (_flags), _reg) + +#define AR5K_REG_DISABLE_BITS(hal, _reg, _flags) \ + ath5k_hw_reg_write(hal, ath5k_hw_reg_read(hal, _reg) & ~(_flags), _reg) + +#define AR5K_PHY_WRITE(hal, _reg, _val) \ + ath5k_hw_reg_write(hal, _val, (hal)->ah_phy + ((_reg) << 2)) + +#define AR5K_PHY_READ(hal, _reg) \ + ath5k_hw_reg_read(hal, (hal)->ah_phy + ((_reg) << 2)) + +#define AR5K_REG_WAIT(_i) do { \ + if (_i % 64) \ + udelay(1); \ +} while (0) + +#define AR5K_EEPROM_READ(_o, _v) do { \ + if ((ret = ath5k_hw_eeprom_read(hal, (_o), &(_v))) != 0) \ + return (ret); \ +} while (0) + +#define AR5K_EEPROM_READ_HDR(_o, _v) \ + AR5K_EEPROM_READ(_o, hal->ah_capabilities.cap_eeprom._v); \ + +/* Read status of selected queue */ +#define AR5K_REG_READ_Q(hal, _reg, _queue) \ + (ath5k_hw_reg_read(hal, _reg) & (1 << _queue)) \ + +#define AR5K_REG_WRITE_Q(hal, _reg, _queue) \ + ath5k_hw_reg_write(hal, (1 << _queue), _reg) + +#define AR5K_Q_ENABLE_BITS(_reg, _queue) do { \ + _reg |= 1 << _queue; \ +} while (0) + +#define AR5K_Q_DISABLE_BITS(_reg, _queue) do { \ + _reg &= ~(1 << _queue); \ +} while (0) + +#define AR5K_LOW_ID(_a)( \ +(_a)[0] | (_a)[1] << 8 | (_a)[2] << 16 | (_a)[3] << 24 \ +) + +#define AR5K_HIGH_ID(_a) ((_a)[4] | (_a)[5] << 8) + +/* + * Initial register values + */ + +/* + * Common initial register values + */ +#define AR5K_INIT_MODE CHANNEL_B + +#define AR5K_INIT_TX_LATENCY 502 +#define AR5K_INIT_USEC 39 +#define AR5K_INIT_USEC_TURBO 79 +#define AR5K_INIT_USEC_32 31 +#define AR5K_INIT_CARR_SENSE_EN 1 +#define AR5K_INIT_PROG_IFS 920 +#define AR5K_INIT_PROG_IFS_TURBO 960 +#define AR5K_INIT_EIFS 3440 +#define AR5K_INIT_EIFS_TURBO 6880 +#define AR5K_INIT_SLOT_TIME 396 +#define AR5K_INIT_SLOT_TIME_TURBO 480 +#define AR5K_INIT_ACK_CTS_TIMEOUT 1024 +#define AR5K_INIT_ACK_CTS_TIMEOUT_TURBO 0x08000800 +#define AR5K_INIT_SIFS 560 +#define AR5K_INIT_SIFS_TURBO 480 +#define AR5K_INIT_SH_RETRY 10 +#define AR5K_INIT_LG_RETRY AR5K_INIT_SH_RETRY +#define AR5K_INIT_SSH_RETRY 32 +#define AR5K_INIT_SLG_RETRY AR5K_INIT_SSH_RETRY +#define AR5K_INIT_TX_RETRY 10 +#define AR5K_INIT_TOPS 8 +#define AR5K_INIT_RXNOFRM 8 +#define AR5K_INIT_RPGTO 0 +#define AR5K_INIT_TXNOFRM 0 +#define AR5K_INIT_BEACON_PERIOD 65535 +#define AR5K_INIT_TIM_OFFSET 0 +#define AR5K_INIT_BEACON_EN 0 +#define AR5K_INIT_RESET_TSF 0 + +#define AR5K_INIT_TRANSMIT_LATENCY ( \ + (AR5K_INIT_TX_LATENCY << 14) | (AR5K_INIT_USEC_32 << 7) | \ + (AR5K_INIT_USEC) \ +) +#define AR5K_INIT_TRANSMIT_LATENCY_TURBO ( \ + (AR5K_INIT_TX_LATENCY << 14) | (AR5K_INIT_USEC_32 << 7) | \ + (AR5K_INIT_USEC_TURBO) \ +) +#define AR5K_INIT_PROTO_TIME_CNTRL ( \ + (AR5K_INIT_CARR_SENSE_EN << 26) | (AR5K_INIT_EIFS << 12) | \ + (AR5K_INIT_PROG_IFS) \ +) +#define AR5K_INIT_PROTO_TIME_CNTRL_TURBO ( \ + (AR5K_INIT_CARR_SENSE_EN << 26) | (AR5K_INIT_EIFS_TURBO << 12) | \ + (AR5K_INIT_PROG_IFS_TURBO) \ +) +#define AR5K_INIT_BEACON_CONTROL ( \ + (AR5K_INIT_RESET_TSF << 24) | (AR5K_INIT_BEACON_EN << 23) | \ + (AR5K_INIT_TIM_OFFSET << 16) | (AR5K_INIT_BEACON_PERIOD) \ +) + +/* + * Non-common initial register values which have to be loaded into the + * card at boot time and after each reset. + */ + +/* Register dumps are done per operation mode */ +#define AR5K_INI_RFGAIN_5GHZ 0 +#define AR5K_INI_RFGAIN_2GHZ 1 + +#define AR5K_INI_VAL_11A 0 +#define AR5K_INI_VAL_11A_TURBO 1 +#define AR5K_INI_VAL_11B 2 +#define AR5K_INI_VAL_11G 3 +#define AR5K_INI_VAL_11G_TURBO 4 +#define AR5K_INI_VAL_XR 0 +#define AR5K_INI_VAL_MAX 5 + +#define AR5K_RF5111_INI_RF_MAX_BANKS AR5K_MAX_RF_BANKS +#define AR5K_RF5112_INI_RF_MAX_BANKS AR5K_MAX_RF_BANKS + +static inline u32 ath5k_hw_bitswap(u32 val, unsigned int bits) +{ + u32 retval = 0, bit, i; + + for (i = 0; i < bits; i++) { + bit = (val >> i) & 1; + retval = (retval << 1) | bit; + } + + return retval; +} diff -puN /dev/null drivers/net/wireless/ath5k/initvals.c --- /dev/null +++ a/drivers/net/wireless/ath5k/initvals.c @@ -0,0 +1,1102 @@ +/* + * Initial register settings functions + * + * Copyright (c) 2004, 2005, 2006, 2007 Reyk Floeter + * Copyright (c) 2006, 2007 Nick Kossifidis + * Copyright (c) 2007 Jiri Slaby + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "ath5k.h" +#include "reg.h" + +/* + * MAC/PHY REGISTERS + */ + + +/* + * Mode-independent initial register writes + */ + +struct ath5k_ini { + u16 ini_register; + u32 ini_value; + + enum { + AR5K_INI_WRITE = 0, /* Default */ + AR5K_INI_READ = 1, /* Cleared on read */ + } ini_mode; +}; + +/* + * Mode specific initial register values + */ + +struct ath5k_ini_mode { + u16 mode_register; + u32 mode_value[5]; +}; + +/* Initial register settings for AR5210 */ +static const struct ath5k_ini ar5210_ini[] = { + /* PCU and MAC registers */ + { AR5K_NOQCU_TXDP0, 0 }, + { AR5K_NOQCU_TXDP1, 0 }, + { AR5K_RXDP, 0 }, + { AR5K_CR, 0 }, + { AR5K_ISR, 0, AR5K_INI_READ }, + { AR5K_IMR, 0 }, + { AR5K_IER, AR5K_IER_DISABLE }, + { AR5K_BSR, 0, AR5K_INI_READ }, + { AR5K_TXCFG, AR5K_DMASIZE_128B }, + { AR5K_RXCFG, AR5K_DMASIZE_128B }, + { AR5K_CFG, AR5K_INIT_CFG }, + { AR5K_TOPS, AR5K_INIT_TOPS }, + { AR5K_RXNOFRM, AR5K_INIT_RXNOFRM }, + { AR5K_RPGTO, AR5K_INIT_RPGTO }, + { AR5K_TXNOFRM, AR5K_INIT_TXNOFRM }, + { AR5K_SFR, 0 }, + { AR5K_MIBC, 0 }, + { AR5K_MISC, 0 }, + { AR5K_RX_FILTER_5210, 0 }, + { AR5K_MCAST_FILTER0_5210, 0 }, + { AR5K_MCAST_FILTER1_5210, 0 }, + { AR5K_TX_MASK0, 0 }, + { AR5K_TX_MASK1, 0 }, + { AR5K_CLR_TMASK, 0 }, + { AR5K_TRIG_LVL, AR5K_TUNE_MIN_TX_FIFO_THRES }, + { AR5K_DIAG_SW_5210, 0 }, + { AR5K_RSSI_THR, AR5K_TUNE_RSSI_THRES }, + { AR5K_TSF_L32_5210, 0 }, + { AR5K_TIMER0_5210, 0 }, + { AR5K_TIMER1_5210, 0xffffffff }, + { AR5K_TIMER2_5210, 0xffffffff }, + { AR5K_TIMER3_5210, 1 }, + { AR5K_CFP_DUR_5210, 0 }, + { AR5K_CFP_PERIOD_5210, 0 }, + /* PHY registers */ + { AR5K_PHY(0), 0x00000047 }, + { AR5K_PHY_AGC, 0x00000000 }, + { AR5K_PHY(3), 0x09848ea6 }, + { AR5K_PHY(4), 0x3d32e000 }, + { AR5K_PHY(5), 0x0000076b }, + { AR5K_PHY_ACT, AR5K_PHY_ACT_DISABLE }, + { AR5K_PHY(8), 0x02020200 }, + { AR5K_PHY(9), 0x00000e0e }, + { AR5K_PHY(10), 0x0a020201 }, + { AR5K_PHY(11), 0x00036ffc }, + { AR5K_PHY(12), 0x00000000 }, + { AR5K_PHY(13), 0x00000e0e }, + { AR5K_PHY(14), 0x00000007 }, + { AR5K_PHY(15), 0x00020100 }, + { AR5K_PHY(16), 0x89630000 }, + { AR5K_PHY(17), 0x1372169c }, + { AR5K_PHY(18), 0x0018b633 }, + { AR5K_PHY(19), 0x1284613c }, + { AR5K_PHY(20), 0x0de8b8e0 }, + { AR5K_PHY(21), 0x00074859 }, + { AR5K_PHY(22), 0x7e80beba }, + { AR5K_PHY(23), 0x313a665e }, + { AR5K_PHY_AGCCTL, 0x00001d08 }, + { AR5K_PHY(25), 0x0001ce00 }, + { AR5K_PHY(26), 0x409a4190 }, + { AR5K_PHY(28), 0x0000000f }, + { AR5K_PHY(29), 0x00000080 }, + { AR5K_PHY(30), 0x00000004 }, + { AR5K_PHY(31), 0x00000018 }, /* 0x987c */ + { AR5K_PHY(64), 0x00000000 }, /* 0x9900 */ + { AR5K_PHY(65), 0x00000000 }, + { AR5K_PHY(66), 0x00000000 }, + { AR5K_PHY(67), 0x00800000 }, + { AR5K_PHY(68), 0x00000003 }, + /* BB gain table (64bytes) */ + { AR5K_BB_GAIN(0), 0x00000000 }, + { AR5K_BB_GAIN(1), 0x00000020 }, + { AR5K_BB_GAIN(2), 0x00000010 }, + { AR5K_BB_GAIN(3), 0x00000030 }, + { AR5K_BB_GAIN(4), 0x00000008 }, + { AR5K_BB_GAIN(5), 0x00000028 }, + { AR5K_BB_GAIN(6), 0x00000028 }, + { AR5K_BB_GAIN(7), 0x00000004 }, + { AR5K_BB_GAIN(8), 0x00000024 }, + { AR5K_BB_GAIN(9), 0x00000014 }, + { AR5K_BB_GAIN(10), 0x00000034 }, + { AR5K_BB_GAIN(11), 0x0000000c }, + { AR5K_BB_GAIN(12), 0x0000002c }, + { AR5K_BB_GAIN(13), 0x00000002 }, + { AR5K_BB_GAIN(14), 0x00000022 }, + { AR5K_BB_GAIN(15), 0x00000012 }, + { AR5K_BB_GAIN(16), 0x00000032 }, + { AR5K_BB_GAIN(17), 0x0000000a }, + { AR5K_BB_GAIN(18), 0x0000002a }, + { AR5K_BB_GAIN(19), 0x00000001 }, + { AR5K_BB_GAIN(20), 0x00000021 }, + { AR5K_BB_GAIN(21), 0x00000011 }, + { AR5K_BB_GAIN(22), 0x00000031 }, + { AR5K_BB_GAIN(23), 0x00000009 }, + { AR5K_BB_GAIN(24), 0x00000029 }, + { AR5K_BB_GAIN(25), 0x00000005 }, + { AR5K_BB_GAIN(26), 0x00000025 }, + { AR5K_BB_GAIN(27), 0x00000015 }, + { AR5K_BB_GAIN(28), 0x00000035 }, + { AR5K_BB_GAIN(29), 0x0000000d }, + { AR5K_BB_GAIN(30), 0x0000002d }, + { AR5K_BB_GAIN(31), 0x00000003 }, + { AR5K_BB_GAIN(32), 0x00000023 }, + { AR5K_BB_GAIN(33), 0x00000013 }, + { AR5K_BB_GAIN(34), 0x00000033 }, + { AR5K_BB_GAIN(35), 0x0000000b }, + { AR5K_BB_GAIN(36), 0x0000002b }, + { AR5K_BB_GAIN(37), 0x00000007 }, + { AR5K_BB_GAIN(38), 0x00000027 }, + { AR5K_BB_GAIN(39), 0x00000017 }, + { AR5K_BB_GAIN(40), 0x00000037 }, + { AR5K_BB_GAIN(41), 0x0000000f }, + { AR5K_BB_GAIN(42), 0x0000002f }, + { AR5K_BB_GAIN(43), 0x0000002f }, + { AR5K_BB_GAIN(44), 0x0000002f }, + { AR5K_BB_GAIN(45), 0x0000002f }, + { AR5K_BB_GAIN(46), 0x0000002f }, + { AR5K_BB_GAIN(47), 0x0000002f }, + { AR5K_BB_GAIN(48), 0x0000002f }, + { AR5K_BB_GAIN(49), 0x0000002f }, + { AR5K_BB_GAIN(50), 0x0000002f }, + { AR5K_BB_GAIN(51), 0x0000002f }, + { AR5K_BB_GAIN(52), 0x0000002f }, + { AR5K_BB_GAIN(53), 0x0000002f }, + { AR5K_BB_GAIN(54), 0x0000002f }, + { AR5K_BB_GAIN(55), 0x0000002f }, + { AR5K_BB_GAIN(56), 0x0000002f }, + { AR5K_BB_GAIN(57), 0x0000002f }, + { AR5K_BB_GAIN(58), 0x0000002f }, + { AR5K_BB_GAIN(59), 0x0000002f }, + { AR5K_BB_GAIN(60), 0x0000002f }, + { AR5K_BB_GAIN(61), 0x0000002f }, + { AR5K_BB_GAIN(62), 0x0000002f }, + { AR5K_BB_GAIN(63), 0x0000002f }, + /* 5110 RF gain table (64btes) */ + { AR5K_RF_GAIN(0), 0x0000001d }, + { AR5K_RF_GAIN(1), 0x0000005d }, + { AR5K_RF_GAIN(2), 0x0000009d }, + { AR5K_RF_GAIN(3), 0x000000dd }, + { AR5K_RF_GAIN(4), 0x0000011d }, + { AR5K_RF_GAIN(5), 0x00000021 }, + { AR5K_RF_GAIN(6), 0x00000061 }, + { AR5K_RF_GAIN(7), 0x000000a1 }, + { AR5K_RF_GAIN(8), 0x000000e1 }, + { AR5K_RF_GAIN(9), 0x00000031 }, + { AR5K_RF_GAIN(10), 0x00000071 }, + { AR5K_RF_GAIN(11), 0x000000b1 }, + { AR5K_RF_GAIN(12), 0x0000001c }, + { AR5K_RF_GAIN(13), 0x0000005c }, + { AR5K_RF_GAIN(14), 0x00000029 }, + { AR5K_RF_GAIN(15), 0x00000069 }, + { AR5K_RF_GAIN(16), 0x000000a9 }, + { AR5K_RF_GAIN(17), 0x00000020 }, + { AR5K_RF_GAIN(18), 0x00000019 }, + { AR5K_RF_GAIN(19), 0x00000059 }, + { AR5K_RF_GAIN(20), 0x00000099 }, + { AR5K_RF_GAIN(21), 0x00000030 }, + { AR5K_RF_GAIN(22), 0x00000005 }, + { AR5K_RF_GAIN(23), 0x00000025 }, + { AR5K_RF_GAIN(24), 0x00000065 }, + { AR5K_RF_GAIN(25), 0x000000a5 }, + { AR5K_RF_GAIN(26), 0x00000028 }, + { AR5K_RF_GAIN(27), 0x00000068 }, + { AR5K_RF_GAIN(28), 0x0000001f }, + { AR5K_RF_GAIN(29), 0x0000001e }, + { AR5K_RF_GAIN(30), 0x00000018 }, + { AR5K_RF_GAIN(31), 0x00000058 }, + { AR5K_RF_GAIN(32), 0x00000098 }, + { AR5K_RF_GAIN(33), 0x00000003 }, + { AR5K_RF_GAIN(34), 0x00000004 }, + { AR5K_RF_GAIN(35), 0x00000044 }, + { AR5K_RF_GAIN(36), 0x00000084 }, + { AR5K_RF_GAIN(37), 0x00000013 }, + { AR5K_RF_GAIN(38), 0x00000012 }, + { AR5K_RF_GAIN(39), 0x00000052 }, + { AR5K_RF_GAIN(40), 0x00000092 }, + { AR5K_RF_GAIN(41), 0x000000d2 }, + { AR5K_RF_GAIN(42), 0x0000002b }, + { AR5K_RF_GAIN(43), 0x0000002a }, + { AR5K_RF_GAIN(44), 0x0000006a }, + { AR5K_RF_GAIN(45), 0x000000aa }, + { AR5K_RF_GAIN(46), 0x0000001b }, + { AR5K_RF_GAIN(47), 0x0000001a }, + { AR5K_RF_GAIN(48), 0x0000005a }, + { AR5K_RF_GAIN(49), 0x0000009a }, + { AR5K_RF_GAIN(50), 0x000000da }, + { AR5K_RF_GAIN(51), 0x00000006 }, + { AR5K_RF_GAIN(52), 0x00000006 }, + { AR5K_RF_GAIN(53), 0x00000006 }, + { AR5K_RF_GAIN(54), 0x00000006 }, + { AR5K_RF_GAIN(55), 0x00000006 }, + { AR5K_RF_GAIN(56), 0x00000006 }, + { AR5K_RF_GAIN(57), 0x00000006 }, + { AR5K_RF_GAIN(58), 0x00000006 }, + { AR5K_RF_GAIN(59), 0x00000006 }, + { AR5K_RF_GAIN(60), 0x00000006 }, + { AR5K_RF_GAIN(61), 0x00000006 }, + { AR5K_RF_GAIN(62), 0x00000006 }, + { AR5K_RF_GAIN(63), 0x00000006 }, + /* PHY activation */ + { AR5K_PHY(53), 0x00000020 }, + { AR5K_PHY(51), 0x00000004 }, + { AR5K_PHY(50), 0x00060106 }, + { AR5K_PHY(39), 0x0000006d }, + { AR5K_PHY(48), 0x00000000 }, + { AR5K_PHY(52), 0x00000014 }, + { AR5K_PHY_ACT, AR5K_PHY_ACT_ENABLE }, +}; + +/* Initial register settings for AR5211 */ +static const struct ath5k_ini ar5211_ini[] = { + { AR5K_RXDP, 0x00000000 }, + { AR5K_RTSD0, 0x84849c9c }, + { AR5K_RTSD1, 0x7c7c7c7c }, + { AR5K_RXCFG, 0x00000005 }, + { AR5K_MIBC, 0x00000000 }, + { AR5K_TOPS, 0x00000008 }, + { AR5K_RXNOFRM, 0x00000008 }, + { AR5K_TXNOFRM, 0x00000010 }, + { AR5K_RPGTO, 0x00000000 }, + { AR5K_RFCNT, 0x0000001f }, + { AR5K_QUEUE_TXDP(0), 0x00000000 }, + { AR5K_QUEUE_TXDP(1), 0x00000000 }, + { AR5K_QUEUE_TXDP(2), 0x00000000 }, + { AR5K_QUEUE_TXDP(3), 0x00000000 }, + { AR5K_QUEUE_TXDP(4), 0x00000000 }, + { AR5K_QUEUE_TXDP(5), 0x00000000 }, + { AR5K_QUEUE_TXDP(6), 0x00000000 }, + { AR5K_QUEUE_TXDP(7), 0x00000000 }, + { AR5K_QUEUE_TXDP(8), 0x00000000 }, + { AR5K_QUEUE_TXDP(9), 0x00000000 }, + { AR5K_DCU_FP, 0x00000000 }, + { AR5K_STA_ID1, 0x00000000 }, + { AR5K_BSS_ID0, 0x00000000 }, + { AR5K_BSS_ID1, 0x00000000 }, + { AR5K_RSSI_THR, 0x00000000 }, + { AR5K_CFP_PERIOD_5211, 0x00000000 }, + { AR5K_TIMER0_5211, 0x00000030 }, + { AR5K_TIMER1_5211, 0x0007ffff }, + { AR5K_TIMER2_5211, 0x01ffffff }, + { AR5K_TIMER3_5211, 0x00000031 }, + { AR5K_CFP_DUR_5211, 0x00000000 }, + { AR5K_RX_FILTER_5211, 0x00000000 }, + { AR5K_MCAST_FILTER0_5211, 0x00000000 }, + { AR5K_MCAST_FILTER1_5211, 0x00000002 }, + { AR5K_DIAG_SW_5211, 0x00000000 }, + { AR5K_ADDAC_TEST, 0x00000000 }, + { AR5K_DEFAULT_ANTENNA, 0x00000000 }, + /* PHY registers */ + { AR5K_PHY_AGC, 0x00000000 }, + { AR5K_PHY(3), 0x2d849093 }, + { AR5K_PHY(4), 0x7d32e000 }, + { AR5K_PHY(5), 0x00000f6b }, + { AR5K_PHY_ACT, 0x00000000 }, + { AR5K_PHY(11), 0x00026ffe }, + { AR5K_PHY(12), 0x00000000 }, + { AR5K_PHY(15), 0x00020100 }, + { AR5K_PHY(16), 0x206a017a }, + { AR5K_PHY(19), 0x1284613c }, + { AR5K_PHY(21), 0x00000859 }, + { AR5K_PHY(26), 0x409a4190 }, /* 0x9868 */ + { AR5K_PHY(27), 0x050cb081 }, + { AR5K_PHY(28), 0x0000000f }, + { AR5K_PHY(29), 0x00000080 }, + { AR5K_PHY(30), 0x0000000c }, + { AR5K_PHY(64), 0x00000000 }, + { AR5K_PHY(65), 0x00000000 }, + { AR5K_PHY(66), 0x00000000 }, + { AR5K_PHY(67), 0x00800000 }, + { AR5K_PHY(68), 0x00000001 }, + { AR5K_PHY(71), 0x0000092a }, + { AR5K_PHY_IQ, 0x00000000 }, + { AR5K_PHY(73), 0x00058a05 }, + { AR5K_PHY(74), 0x00000001 }, + { AR5K_PHY(75), 0x00000000 }, + { AR5K_PHY_PAPD_PROBE, 0x00000000 }, + { AR5K_PHY(77), 0x00000000 }, /* 0x9934 */ + { AR5K_PHY(78), 0x00000000 }, /* 0x9938 */ + { AR5K_PHY(79), 0x0000003f }, /* 0x993c */ + { AR5K_PHY(80), 0x00000004 }, + { AR5K_PHY(82), 0x00000000 }, + { AR5K_PHY(83), 0x00000000 }, + { AR5K_PHY(84), 0x00000000 }, + { AR5K_PHY_RADAR, 0x5d50f14c }, + { AR5K_PHY(86), 0x00000018 }, + { AR5K_PHY(87), 0x004b6a8e }, + /* Power table (32bytes) */ + { AR5K_PHY_PCDAC_TXPOWER(1), 0x06ff05ff }, + { AR5K_PHY_PCDAC_TXPOWER(2), 0x07ff07ff }, + { AR5K_PHY_PCDAC_TXPOWER(3), 0x08ff08ff }, + { AR5K_PHY_PCDAC_TXPOWER(4), 0x09ff09ff }, + { AR5K_PHY_PCDAC_TXPOWER(5), 0x0aff0aff }, + { AR5K_PHY_PCDAC_TXPOWER(6), 0x0bff0bff }, + { AR5K_PHY_PCDAC_TXPOWER(7), 0x0cff0cff }, + { AR5K_PHY_PCDAC_TXPOWER(8), 0x0dff0dff }, + { AR5K_PHY_PCDAC_TXPOWER(9), 0x0fff0eff }, + { AR5K_PHY_PCDAC_TXPOWER(10), 0x12ff12ff }, + { AR5K_PHY_PCDAC_TXPOWER(11), 0x14ff13ff }, + { AR5K_PHY_PCDAC_TXPOWER(12), 0x16ff15ff }, + { AR5K_PHY_PCDAC_TXPOWER(13), 0x19ff17ff }, + { AR5K_PHY_PCDAC_TXPOWER(14), 0x1bff1aff }, + { AR5K_PHY_PCDAC_TXPOWER(15), 0x1eff1dff }, + { AR5K_PHY_PCDAC_TXPOWER(16), 0x23ff20ff }, + { AR5K_PHY_PCDAC_TXPOWER(17), 0x27ff25ff }, + { AR5K_PHY_PCDAC_TXPOWER(18), 0x2cff29ff }, + { AR5K_PHY_PCDAC_TXPOWER(19), 0x31ff2fff }, + { AR5K_PHY_PCDAC_TXPOWER(20), 0x37ff34ff }, + { AR5K_PHY_PCDAC_TXPOWER(21), 0x3aff3aff }, + { AR5K_PHY_PCDAC_TXPOWER(22), 0x3aff3aff }, + { AR5K_PHY_PCDAC_TXPOWER(23), 0x3aff3aff }, + { AR5K_PHY_PCDAC_TXPOWER(24), 0x3aff3aff }, + { AR5K_PHY_PCDAC_TXPOWER(25), 0x3aff3aff }, + { AR5K_PHY_PCDAC_TXPOWER(26), 0x3aff3aff }, + { AR5K_PHY_PCDAC_TXPOWER(27), 0x3aff3aff }, + { AR5K_PHY_PCDAC_TXPOWER(28), 0x3aff3aff }, + { AR5K_PHY_PCDAC_TXPOWER(29), 0x3aff3aff }, + { AR5K_PHY_PCDAC_TXPOWER(30), 0x3aff3aff }, + { AR5K_PHY_PCDAC_TXPOWER(31), 0x3aff3aff }, + { AR5K_PHY_CCKTXCTL, 0x00000000 }, + { AR5K_PHY(642), 0x503e4646 }, + { AR5K_PHY_GAIN_2GHZ, 0x6480416c }, + { AR5K_PHY(644), 0x0199a003 }, + { AR5K_PHY(645), 0x044cd610 }, + { AR5K_PHY(646), 0x13800040 }, + { AR5K_PHY(647), 0x1be00060 }, + { AR5K_PHY(648), 0x0c53800a }, + { AR5K_PHY(649), 0x0014df3b }, + { AR5K_PHY(650), 0x000001b5 }, + { AR5K_PHY(651), 0x00000020 }, +}; + +/* Initial mode-specific settings for AR5211 + * XXX: how about gTurbo ? RF5111 supports it, how about AR5211 ? + */ +static const struct ath5k_ini_mode ar5211_ini_mode[] = { + { AR5K_TXCFG, + /* a/XR aTurbo b g(OFDM?) gTurbo (N/A) */ + { 0x00000017, 0x00000017, 0x00000017, 0x00000017, 0x00000017 } }, + { AR5K_QUEUE_DFS_LOCAL_IFS(0), + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, + { AR5K_QUEUE_DFS_LOCAL_IFS(1), + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, + { AR5K_QUEUE_DFS_LOCAL_IFS(2), + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, + { AR5K_QUEUE_DFS_LOCAL_IFS(3), + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, + { AR5K_QUEUE_DFS_LOCAL_IFS(4), + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, + { AR5K_QUEUE_DFS_LOCAL_IFS(5), + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, + { AR5K_QUEUE_DFS_LOCAL_IFS(6), + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, + { AR5K_QUEUE_DFS_LOCAL_IFS(7), + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, + { AR5K_QUEUE_DFS_LOCAL_IFS(8), + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, + { AR5K_QUEUE_DFS_LOCAL_IFS(9), + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, + { AR5K_DCU_GBL_IFS_SLOT, + { 0x00000168, 0x000001e0, 0x000001b8, 0x00000168, 0x00000168 } }, + { AR5K_DCU_GBL_IFS_SIFS, + { 0x00000230, 0x000001e0, 0x000000b0, 0x00000230, 0x00000230 } }, + { AR5K_DCU_GBL_IFS_EIFS, + { 0x00000d98, 0x00001180, 0x00001f48, 0x00000d98, 0x00000d98 } }, + { AR5K_DCU_GBL_IFS_MISC, + { 0x0000a0e0, 0x00014068, 0x00005880, 0x0000a0e0, 0x0000a0e0 } }, + { AR5K_TIME_OUT, + { 0x04000400, 0x08000800, 0x20003000, 0x04000400, 0x04000400 } }, + { AR5K_USEC_5211, + { 0x0e8d8fa7, 0x0e8d8fcf, 0x01608f95, 0x0e8d8fa7, 0x0e8d8fa7 } }, + { AR5K_PHY_TURBO, + { 0x00000000, 0x00000003, 0x00000000, 0x00000000, 0x00000000 } }, + { 0x9820, + { 0x02020200, 0x02020200, 0x02010200, 0x02020200, 0x02020200 } }, + { 0x9824, + { 0x00000e0e, 0x00000e0e, 0x00000707, 0x00000e0e, 0x00000e0e } }, + { 0x9828, + { 0x0a020001, 0x0a020001, 0x05010000, 0x0a020001, 0x0a020001 } }, + { 0x9834, + { 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e } }, + { 0x9838, + { 0x00000007, 0x00000007, 0x0000000b, 0x0000000b, 0x0000000b } }, + { 0x9844, + { 0x1372169c, 0x137216a5, 0x137216a8, 0x1372169c, 0x1372169c } }, + { 0x9848, + { 0x0018ba67, 0x0018ba67, 0x0018ba69, 0x0018ba69, 0x0018ba69 } }, + { 0x9850, + { 0x0c28b4e0, 0x0c28b4e0, 0x0c28b4e0, 0x0c28b4e0, 0x0c28b4e0 } }, + { AR5K_PHY_SIG, + { 0x7e800d2e, 0x7e800d2e, 0x7ec00d2e, 0x7e800d2e, 0x7e800d2e } }, + { AR5K_PHY_AGCCOARSE, + { 0x31375d5e, 0x31375d5e, 0x313a5d5e, 0x31375d5e, 0x31375d5e } }, + { AR5K_PHY_AGCCTL, + { 0x0000bd10, 0x0000bd10, 0x0000bd38, 0x0000bd10, 0x0000bd10 } }, + { AR5K_PHY_NF, + { 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00 } }, + { AR5K_PHY_RX_DELAY, + { 0x00002710, 0x00002710, 0x0000157c, 0x00002710, 0x00002710 } }, + { 0x9918, + { 0x00000190, 0x00000190, 0x00000084, 0x00000190, 0x00000190 } }, + { AR5K_PHY_FRAME_CTL_5211, + { 0x6fe01020, 0x6fe01020, 0x6fe00920, 0x6fe01020, 0x6fe01020 } }, + { AR5K_PHY_PCDAC_TXPOWER(0), + { 0x05ff14ff, 0x05ff14ff, 0x05ff14ff, 0x05ff19ff, 0x05ff19ff } }, + { AR5K_RF_BUFFER_CONTROL_4, + { 0x00000010, 0x00000014, 0x00000010, 0x00000010, 0x00000010 } }, +}; + +/* Initial register settings for AR5212 */ +static const struct ath5k_ini ar5212_ini[] = { + { AR5K_RXDP, 0x00000000 }, + { AR5K_RXCFG, 0x00000005 }, + { AR5K_MIBC, 0x00000000 }, + { AR5K_TOPS, 0x00000008 }, + { AR5K_RXNOFRM, 0x00000008 }, + { AR5K_TXNOFRM, 0x00000010 }, + { AR5K_RPGTO, 0x00000000 }, + { AR5K_RFCNT, 0x0000001f }, + { AR5K_QUEUE_TXDP(0), 0x00000000 }, + { AR5K_QUEUE_TXDP(1), 0x00000000 }, + { AR5K_QUEUE_TXDP(2), 0x00000000 }, + { AR5K_QUEUE_TXDP(3), 0x00000000 }, + { AR5K_QUEUE_TXDP(4), 0x00000000 }, + { AR5K_QUEUE_TXDP(5), 0x00000000 }, + { AR5K_QUEUE_TXDP(6), 0x00000000 }, + { AR5K_QUEUE_TXDP(7), 0x00000000 }, + { AR5K_QUEUE_TXDP(8), 0x00000000 }, + { AR5K_QUEUE_TXDP(9), 0x00000000 }, + { AR5K_DCU_FP, 0x00000000 }, + { AR5K_DCU_TXP, 0x00000000 }, + { AR5K_DCU_TX_FILTER, 0x00000000 }, + /* Unknown table */ + { 0x1078, 0x00000000 }, + { 0x10b8, 0x00000000 }, + { 0x10f8, 0x00000000 }, + { 0x1138, 0x00000000 }, + { 0x1178, 0x00000000 }, + { 0x11b8, 0x00000000 }, + { 0x11f8, 0x00000000 }, + { 0x1238, 0x00000000 }, + { 0x1278, 0x00000000 }, + { 0x12b8, 0x00000000 }, + { 0x12f8, 0x00000000 }, + { 0x1338, 0x00000000 }, + { 0x1378, 0x00000000 }, + { 0x13b8, 0x00000000 }, + { 0x13f8, 0x00000000 }, + { 0x1438, 0x00000000 }, + { 0x1478, 0x00000000 }, + { 0x14b8, 0x00000000 }, + { 0x14f8, 0x00000000 }, + { 0x1538, 0x00000000 }, + { 0x1578, 0x00000000 }, + { 0x15b8, 0x00000000 }, + { 0x15f8, 0x00000000 }, + { 0x1638, 0x00000000 }, + { 0x1678, 0x00000000 }, + { 0x16b8, 0x00000000 }, + { 0x16f8, 0x00000000 }, + { 0x1738, 0x00000000 }, + { 0x1778, 0x00000000 }, + { 0x17b8, 0x00000000 }, + { 0x17f8, 0x00000000 }, + { 0x103c, 0x00000000 }, + { 0x107c, 0x00000000 }, + { 0x10bc, 0x00000000 }, + { 0x10fc, 0x00000000 }, + { 0x113c, 0x00000000 }, + { 0x117c, 0x00000000 }, + { 0x11bc, 0x00000000 }, + { 0x11fc, 0x00000000 }, + { 0x123c, 0x00000000 }, + { 0x127c, 0x00000000 }, + { 0x12bc, 0x00000000 }, + { 0x12fc, 0x00000000 }, + { 0x133c, 0x00000000 }, + { 0x137c, 0x00000000 }, + { 0x13bc, 0x00000000 }, + { 0x13fc, 0x00000000 }, + { 0x143c, 0x00000000 }, + { 0x147c, 0x00000000 }, + { AR5K_STA_ID1, 0x00000000 }, + { AR5K_BSS_ID0, 0x00000000 }, + { AR5K_BSS_ID1, 0x00000000 }, + { AR5K_RSSI_THR, 0x00000000 }, + { AR5K_BEACON_5211, 0x00000000 }, + { AR5K_CFP_PERIOD_5211, 0x00000000 }, + { AR5K_TIMER0_5211, 0x00000030 }, + { AR5K_TIMER1_5211, 0x0007ffff }, + { AR5K_TIMER2_5211, 0x01ffffff }, + { AR5K_TIMER3_5211, 0x00000031 }, + { AR5K_CFP_DUR_5211, 0x00000000 }, + { AR5K_RX_FILTER_5211, 0x00000000 }, + { AR5K_DIAG_SW_5211, 0x00000000 }, + { AR5K_ADDAC_TEST, 0x00000000 }, + { AR5K_DEFAULT_ANTENNA, 0x00000000 }, + { 0x805c, 0xffffc7ff }, + { 0x8080, 0x00000000 }, + { AR5K_NAV_5211, 0x00000000 }, + { AR5K_RTS_OK_5211, 0x00000000 }, + { AR5K_RTS_FAIL_5211, 0x00000000 }, + { AR5K_ACK_FAIL_5211, 0x00000000 }, + { AR5K_FCS_FAIL_5211, 0x00000000 }, + { AR5K_BEACON_CNT_5211, 0x00000000 }, + { AR5K_XRMODE, 0x2a82301a }, + { AR5K_XRDELAY, 0x05dc01e0 }, + { AR5K_XRTIMEOUT, 0x1f402710 }, + { AR5K_XRCHIRP, 0x01f40000 }, + { AR5K_XRSTOMP, 0x00001e1c }, + { AR5K_SLEEP0, 0x0002aaaa }, + { AR5K_SLEEP1, 0x02005555 }, + { AR5K_SLEEP2, 0x00000000 }, + { AR5K_BSS_IDM0, 0xffffffff }, + { AR5K_BSS_IDM1, 0x0000ffff }, + { AR5K_TXPC, 0x00000000 }, + { AR5K_PROFCNT_TX, 0x00000000 }, + { AR5K_PROFCNT_RX, 0x00000000 }, + { AR5K_PROFCNT_RXCLR, 0x00000000 }, + { AR5K_PROFCNT_CYCLE, 0x00000000 }, + { 0x80fc, 0x00000088 }, + { AR5K_RATE_DUR(0), 0x00000000 }, + { AR5K_RATE_DUR(1), 0x0000008c }, + { AR5K_RATE_DUR(2), 0x000000e4 }, + { AR5K_RATE_DUR(3), 0x000002d5 }, + { AR5K_RATE_DUR(4), 0x00000000 }, + { AR5K_RATE_DUR(5), 0x00000000 }, + { AR5K_RATE_DUR(6), 0x000000a0 }, + { AR5K_RATE_DUR(7), 0x000001c9 }, + { AR5K_RATE_DUR(8), 0x0000002c }, + { AR5K_RATE_DUR(9), 0x0000002c }, + { AR5K_RATE_DUR(10), 0x00000030 }, + { AR5K_RATE_DUR(11), 0x0000003c }, + { AR5K_RATE_DUR(12), 0x0000002c }, + { AR5K_RATE_DUR(13), 0x0000002c }, + { AR5K_RATE_DUR(14), 0x00000030 }, + { AR5K_RATE_DUR(15), 0x0000003c }, + { AR5K_RATE_DUR(16), 0x00000000 }, + { AR5K_RATE_DUR(17), 0x00000000 }, + { AR5K_RATE_DUR(18), 0x00000000 }, + { AR5K_RATE_DUR(19), 0x00000000 }, + { AR5K_RATE_DUR(20), 0x00000000 }, + { AR5K_RATE_DUR(21), 0x00000000 }, + { AR5K_RATE_DUR(22), 0x00000000 }, + { AR5K_RATE_DUR(23), 0x00000000 }, + { AR5K_RATE_DUR(24), 0x000000d5 }, + { AR5K_RATE_DUR(25), 0x000000df }, + { AR5K_RATE_DUR(26), 0x00000102 }, + { AR5K_RATE_DUR(27), 0x0000013a }, + { AR5K_RATE_DUR(28), 0x00000075 }, + { AR5K_RATE_DUR(29), 0x0000007f }, + { AR5K_RATE_DUR(30), 0x000000a2 }, + { AR5K_RATE_DUR(31), 0x00000000 }, + { 0x8100, 0x00010002}, + { AR5K_TSF_PARM, 0x00000001 }, + { 0x8108, 0x000000c0 }, + { AR5K_PHY_ERR_FIL, 0x00000000 }, + { 0x8110, 0x00000168 }, + { 0x8114, 0x00000000 }, + /* Some kind of table + * also notice ...03<-02<-01<-00) */ + { 0x87c0, 0x03020100 }, + { 0x87c4, 0x07060504 }, + { 0x87c8, 0x0b0a0908 }, + { 0x87cc, 0x0f0e0d0c }, + { 0x87d0, 0x13121110 }, + { 0x87d4, 0x17161514 }, + { 0x87d8, 0x1b1a1918 }, + { 0x87dc, 0x1f1e1d1c }, + /* loop ? */ + { 0x87e0, 0x03020100 }, + { 0x87e4, 0x07060504 }, + { 0x87e8, 0x0b0a0908 }, + { 0x87ec, 0x0f0e0d0c }, + { 0x87f0, 0x13121110 }, + { 0x87f4, 0x17161514 }, + { 0x87f8, 0x1b1a1918 }, + { 0x87fc, 0x1f1e1d1c }, + /* PHY registers */ + { AR5K_PHY_AGC, 0x00000000 }, + { AR5K_PHY(3), 0xad848e19 }, + { AR5K_PHY(4), 0x7d28e000 }, + { AR5K_PHY_TIMING_3, 0x9c0a9f6b }, + { AR5K_PHY_ACT, 0x00000000 }, + { AR5K_PHY(11), 0x00022ffe }, + { AR5K_PHY(15), 0x00020100 }, + { AR5K_PHY(16), 0x206a017a }, + { AR5K_PHY(19), 0x1284613c }, + { AR5K_PHY(21), 0x00000859 }, + { AR5K_PHY(64), 0x00000000 }, + { AR5K_PHY(65), 0x00000000 }, + { AR5K_PHY(66), 0x00000000 }, + { AR5K_PHY(67), 0x00800000 }, + { AR5K_PHY(68), 0x00000001 }, + { AR5K_PHY(71), 0x0000092a }, + { AR5K_PHY_IQ, 0x05100000 }, + { AR5K_PHY(74), 0x00000001 }, + { AR5K_PHY(75), 0x00000004 }, + { AR5K_PHY_TXPOWER_RATE1, 0x1e1f2022 }, + { AR5K_PHY_TXPOWER_RATE2, 0x0a0b0c0d }, + { AR5K_PHY_TXPOWER_RATE_MAX, 0x0000003f }, + { AR5K_PHY(80), 0x00000004 }, + { AR5K_PHY(82), 0x9280b212 }, + { AR5K_PHY_RADAR, 0x5d50e188 }, + { AR5K_PHY(86), 0x000000ff }, + { AR5K_PHY(87), 0x004b6a8e }, + { AR5K_PHY(90), 0x000003ce }, + { AR5K_PHY(92), 0x192fb515 }, + { AR5K_PHY(93), 0x00000000 }, + { AR5K_PHY(94), 0x00000001 }, + { AR5K_PHY(95), 0x00000000 }, + /* Power table (32bytes) */ + { AR5K_PHY_PCDAC_TXPOWER(1), 0x10ff10ff }, + { AR5K_PHY_PCDAC_TXPOWER(2), 0x10ff10ff }, + { AR5K_PHY_PCDAC_TXPOWER(3), 0x10ff10ff }, + { AR5K_PHY_PCDAC_TXPOWER(4), 0x10ff10ff }, + { AR5K_PHY_PCDAC_TXPOWER(5), 0x10ff10ff }, + { AR5K_PHY_PCDAC_TXPOWER(6), 0x10ff10ff }, + { AR5K_PHY_PCDAC_TXPOWER(7), 0x10ff10ff }, + { AR5K_PHY_PCDAC_TXPOWER(8), 0x10ff10ff }, + { AR5K_PHY_PCDAC_TXPOWER(9), 0x10ff10ff }, + { AR5K_PHY_PCDAC_TXPOWER(10), 0x10ff10ff }, + { AR5K_PHY_PCDAC_TXPOWER(11), 0x10ff10ff }, + { AR5K_PHY_PCDAC_TXPOWER(12), 0x10ff10ff }, + { AR5K_PHY_PCDAC_TXPOWER(13), 0x10ff10ff }, + { AR5K_PHY_PCDAC_TXPOWER(14), 0x10ff10ff }, + { AR5K_PHY_PCDAC_TXPOWER(15), 0x10ff10ff }, + { AR5K_PHY_PCDAC_TXPOWER(16), 0x10ff10ff }, + { AR5K_PHY_PCDAC_TXPOWER(17), 0x10ff10ff }, + { AR5K_PHY_PCDAC_TXPOWER(18), 0x10ff10ff }, + { AR5K_PHY_PCDAC_TXPOWER(19), 0x10ff10ff }, + { AR5K_PHY_PCDAC_TXPOWER(20), 0x10ff10ff }, + { AR5K_PHY_PCDAC_TXPOWER(21), 0x10ff10ff }, + { AR5K_PHY_PCDAC_TXPOWER(22), 0x10ff10ff }, + { AR5K_PHY_PCDAC_TXPOWER(23), 0x10ff10ff }, + { AR5K_PHY_PCDAC_TXPOWER(24), 0x10ff10ff }, + { AR5K_PHY_PCDAC_TXPOWER(25), 0x10ff10ff }, + { AR5K_PHY_PCDAC_TXPOWER(26), 0x10ff10ff }, + { AR5K_PHY_PCDAC_TXPOWER(27), 0x10ff10ff }, + { AR5K_PHY_PCDAC_TXPOWER(28), 0x10ff10ff }, + { AR5K_PHY_PCDAC_TXPOWER(29), 0x10ff10ff }, + { AR5K_PHY_PCDAC_TXPOWER(30), 0x10ff10ff }, + { AR5K_PHY_PCDAC_TXPOWER(31), 0x10ff10ff }, + { AR5K_PHY(644), 0x0080a333 }, + { AR5K_PHY(645), 0x00206c10 }, + { AR5K_PHY(646), 0x009c4060 }, + { AR5K_PHY(647), 0x1483800a }, + { AR5K_PHY(648), 0x01831061 }, + { AR5K_PHY(649), 0x00000400 }, + { AR5K_PHY(650), 0x000001b5 }, + { AR5K_PHY(651), 0x00000000 }, + { AR5K_PHY_TXPOWER_RATE3, 0x20202020 }, + { AR5K_PHY_TXPOWER_RATE2, 0x20202020 }, + { AR5K_PHY(655), 0x13c889af }, + { AR5K_PHY(656), 0x38490a20 }, + { AR5K_PHY(657), 0x00007bb6 }, + { AR5K_PHY(658), 0x0fff3ffc }, + { AR5K_PHY_CCKTXCTL, 0x00000000 }, +}; + +/* Initial mode-specific settings for AR5212 */ +static const struct ath5k_ini_mode ar5212_ini_mode[] = { + { AR5K_TXCFG, + /* a/XR aTurbo b g (DYN) gTurbo */ + { 0x00008107, 0x00008107, 0x00008107, 0x00008107, 0x00008107 } }, + { AR5K_QUEUE_DFS_LOCAL_IFS(0), + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, + { AR5K_QUEUE_DFS_LOCAL_IFS(1), + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, + { AR5K_QUEUE_DFS_LOCAL_IFS(2), + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, + { AR5K_QUEUE_DFS_LOCAL_IFS(3), + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, + { AR5K_QUEUE_DFS_LOCAL_IFS(4), + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, + { AR5K_QUEUE_DFS_LOCAL_IFS(5), + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, + { AR5K_QUEUE_DFS_LOCAL_IFS(6), + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, + { AR5K_QUEUE_DFS_LOCAL_IFS(7), + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, + { AR5K_QUEUE_DFS_LOCAL_IFS(8), + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, + { AR5K_QUEUE_DFS_LOCAL_IFS(9), + { 0x002ffc0f, 0x002ffc0f, 0x002ffc1f, 0x002ffc0f, 0x002ffc0f } }, + { AR5K_DCU_GBL_IFS_SIFS, + { 0x00000230, 0x000001e0, 0x000000b0, 0x00000160, 0x000001e0 } }, + { AR5K_DCU_GBL_IFS_SLOT, + { 0x00000168, 0x000001e0, 0x000001b8, 0x0000018c, 0x000001e0 } }, + { AR5K_DCU_GBL_IFS_EIFS, + { 0x00000e60, 0x00001180, 0x00001f1c, 0x00003e38, 0x00001180 } }, + { AR5K_DCU_GBL_IFS_MISC, + { 0x0000a0e0, 0x00014068, 0x00005880, 0x0000b0e0, 0x00014068 } }, + { AR5K_TIME_OUT, + { 0x03e803e8, 0x06e006e0, 0x04200420, 0x08400840, 0x06e006e0 } }, +}; + +/* Initial mode-specific settings for AR5212 + RF5111 */ +static const struct ath5k_ini_mode ar5212_rf5111_ini_mode[] = { + { AR5K_USEC_5211, + /* a/XR aTurbo b g gTurbo */ + { 0x128d8fa7, 0x09880fcf, 0x04e00f95, 0x128d8fab, 0x09880fcf } }, + { AR5K_PHY_TURBO, + { 0x00000000, 0x00000003, 0x00000000, 0x00000000, 0x00000003 } }, + { 0x9820, + { 0x02020200, 0x02020200, 0x02010200, 0x02020200, 0x02020200 } }, + { 0x9824, + { 0x00000e0e, 0x00000e0e, 0x00000707, 0x00000e0e, 0x00000e0e } }, + { 0x9828, + { 0x0a020001, 0x0a020001, 0x05010100, 0x0a020001, 0x0a020001 } }, + { 0x9834, + { 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e } }, + { 0x9838, + { 0x00000007, 0x00000007, 0x0000000b, 0x0000000b, 0x0000000b } }, + { 0x9844, + { 0x1372161c, 0x13721c25, 0x13721728, 0x137216a2, 0x13721c25 } }, + { 0x9848, + { 0x0018da5a, 0x0018da5a, 0x0018ca69, 0x0018ca69, 0x0018ca69 } }, + { 0x9850, + { 0x0de8b4e0, 0x0de8b4e0, 0x0de8b4e0, 0x0de8b4e0, 0x0de8b4e0 } }, + { AR5K_PHY_SIG, + { 0x7e800d2e, 0x7e800d2e, 0x7ee84d2e, 0x7ee84d2e, 0x7e800d2e } }, + { AR5K_PHY_AGCCOARSE, + { 0x3137665e, 0x3137665e, 0x3137665e, 0x3137665e, 0x3137615e } }, + { AR5K_PHY_AGCCTL, + { 0x00009d10, 0x00009d10, 0x00009d18, 0x00009d10, 0x00009d10 } }, + { AR5K_PHY_NF, + { 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00 } }, + { AR5K_PHY_ADCSAT, + { 0x409a4190, 0x409a4190, 0x409a4190, 0x409a4190, 0x409a4190 } }, + { 0x986c, + { 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb080, 0x050cb080 } }, + { AR5K_PHY_RX_DELAY, + { 0x00002710, 0x00002710, 0x0000157c, 0x00002af8, 0x00002710 } }, + { 0x9918, + { 0x000001b8, 0x000001b8, 0x00000084, 0x00000108, 0x000001b8 } }, + { 0x9924, + { 0x10058a05, 0x10058a05, 0x10058a05, 0x10058a05, 0x10058a05 } }, + { AR5K_PHY_FRAME_CTL_5211, + { 0xffb81020, 0xffb81020, 0xffb80d20, 0xffb81020, 0xffb81020 } }, + { AR5K_PHY_PCDAC_TXPOWER(0), + { 0x10ff14ff, 0x10ff14ff, 0x10ff10ff, 0x10ff19ff, 0x10ff19ff } }, + { 0xa230, + { 0x00000000, 0x00000000, 0x00000000, 0x00000108, 0x00000000 } }, + { 0xa208, + { 0xd03e6788, 0xd03e6788, 0xd03e6788, 0xd03e6788, 0xd03e6788 } }, +}; + +/* Initial mode-specific settings for AR5212 + RF5112 */ +static const struct ath5k_ini_mode ar5212_rf5112_ini_mode[] = { + { AR5K_USEC_5211, + /* a/XR aTurbo b g gTurbo */ + { 0x128d93a7, 0x098813cf, 0x04e01395, 0x128d93ab, 0x098813cf } }, + { AR5K_PHY_TURBO, + { 0x00000000, 0x00000003, 0x00000000, 0x00000000, 0x00000003 } }, + { 0x9820, + { 0x02020200, 0x02020200, 0x02010200, 0x02020200, 0x02020200 } }, + { 0x9824, + { 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e } }, + { 0x9828, + { 0x0a020001, 0x0a020001, 0x05020100, 0x0a020001, 0x0a020001 } }, + { 0x9834, + { 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e } }, + { 0x9838, + { 0x00000007, 0x00000007, 0x0000000b, 0x0000000b, 0x0000000b } }, + { 0x9844, + { 0x1372161c, 0x13721c25, 0x13721728, 0x137216a2, 0x13721c25 } }, + { 0x9848, + { 0x0018da6d, 0x0018da6d, 0x0018ca75, 0x0018ca75, 0x0018ca75 } }, + { 0x9850, + { 0x0de8b4e0, 0x0de8b4e0, 0x0de8b4e0, 0x0de8b4e0, 0x0de8b4e0 } }, + { AR5K_PHY_SIG, + { 0x7e800d2e, 0x7e800d2e, 0x7ee84d2e, 0x7ee84d2e, 0x7e800d2e } }, + { AR5K_PHY_AGCCOARSE, + { 0x3137665e, 0x3137665e, 0x3137665e, 0x3137665e, 0x3137665e } }, + { AR5K_PHY_AGCCTL, + { 0x00009d10, 0x00009d10, 0x00009d18, 0x00009d10, 0x00009d10 } }, + { AR5K_PHY_NF, + { 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00 } }, + { AR5K_PHY_ADCSAT, + { 0x409a4190, 0x409a4190, 0x409a4190, 0x409a4190, 0x409a4190 } }, + { 0x986c, + { 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081 } }, + { AR5K_PHY_RX_DELAY, + { 0x000007d0, 0x000007d0, 0x0000044c, 0x00000898, 0x000007d0 } }, + { 0x9918, + { 0x000001b8, 0x000001b8, 0x00000084, 0x00000108, 0x000001b8 } }, + { 0x9924, + { 0x10058a05, 0x10058a05, 0x10058a05, 0x10058a05, 0x10058a05 } }, + { AR5K_PHY_FRAME_CTL_5211, + { 0xffb81020, 0xffb81020, 0xffb80d10, 0xffb81010, 0xffb81010 } }, + { AR5K_PHY_PCDAC_TXPOWER(0), + { 0x10ff14ff, 0x10ff14ff, 0x10ff10ff, 0x10ff19ff, 0x10ff19ff } }, + { 0xa230, + { 0x00000000, 0x00000000, 0x00000000, 0x00000108, 0x00000000 } }, + { AR5K_PHY_CCKTXCTL, + { 0x00000000, 0x00000000, 0x00000004, 0x00000004, 0x00000004 } }, + { 0xa208, + { 0xd6be6788, 0xd6be6788, 0xd03e6788, 0xd03e6788, 0xd03e6788 } }, + { AR5K_PHY_GAIN_2GHZ, + { 0x642c0140, 0x642c0140, 0x6442c160, 0x6442c160, 0x6442c160 } }, +}; + +/* + * Initial BaseBand Gain settings for RF5111/5112 (only AR5210 comes with + * RF5110 so initial BB Gain settings are included in AR5K_AR5210_INI) + */ + +/* RF5111 Initial BaseBand Gain settings */ +static const struct ath5k_ini rf5111_ini_bbgain[] = { + { AR5K_BB_GAIN(0), 0x00000000 }, + { AR5K_BB_GAIN(1), 0x00000020 }, + { AR5K_BB_GAIN(2), 0x00000010 }, + { AR5K_BB_GAIN(3), 0x00000030 }, + { AR5K_BB_GAIN(4), 0x00000008 }, + { AR5K_BB_GAIN(5), 0x00000028 }, + { AR5K_BB_GAIN(6), 0x00000004 }, + { AR5K_BB_GAIN(7), 0x00000024 }, + { AR5K_BB_GAIN(8), 0x00000014 }, + { AR5K_BB_GAIN(9), 0x00000034 }, + { AR5K_BB_GAIN(10), 0x0000000c }, + { AR5K_BB_GAIN(11), 0x0000002c }, + { AR5K_BB_GAIN(12), 0x00000002 }, + { AR5K_BB_GAIN(13), 0x00000022 }, + { AR5K_BB_GAIN(14), 0x00000012 }, + { AR5K_BB_GAIN(15), 0x00000032 }, + { AR5K_BB_GAIN(16), 0x0000000a }, + { AR5K_BB_GAIN(17), 0x0000002a }, + { AR5K_BB_GAIN(18), 0x00000006 }, + { AR5K_BB_GAIN(19), 0x00000026 }, + { AR5K_BB_GAIN(20), 0x00000016 }, + { AR5K_BB_GAIN(21), 0x00000036 }, + { AR5K_BB_GAIN(22), 0x0000000e }, + { AR5K_BB_GAIN(23), 0x0000002e }, + { AR5K_BB_GAIN(24), 0x00000001 }, + { AR5K_BB_GAIN(25), 0x00000021 }, + { AR5K_BB_GAIN(26), 0x00000011 }, + { AR5K_BB_GAIN(27), 0x00000031 }, + { AR5K_BB_GAIN(28), 0x00000009 }, + { AR5K_BB_GAIN(29), 0x00000029 }, + { AR5K_BB_GAIN(30), 0x00000005 }, + { AR5K_BB_GAIN(31), 0x00000025 }, + { AR5K_BB_GAIN(32), 0x00000015 }, + { AR5K_BB_GAIN(33), 0x00000035 }, + { AR5K_BB_GAIN(34), 0x0000000d }, + { AR5K_BB_GAIN(35), 0x0000002d }, + { AR5K_BB_GAIN(36), 0x00000003 }, + { AR5K_BB_GAIN(37), 0x00000023 }, + { AR5K_BB_GAIN(38), 0x00000013 }, + { AR5K_BB_GAIN(39), 0x00000033 }, + { AR5K_BB_GAIN(40), 0x0000000b }, + { AR5K_BB_GAIN(41), 0x0000002b }, + { AR5K_BB_GAIN(42), 0x0000002b }, + { AR5K_BB_GAIN(43), 0x0000002b }, + { AR5K_BB_GAIN(44), 0x0000002b }, + { AR5K_BB_GAIN(45), 0x0000002b }, + { AR5K_BB_GAIN(46), 0x0000002b }, + { AR5K_BB_GAIN(47), 0x0000002b }, + { AR5K_BB_GAIN(48), 0x0000002b }, + { AR5K_BB_GAIN(49), 0x0000002b }, + { AR5K_BB_GAIN(50), 0x0000002b }, + { AR5K_BB_GAIN(51), 0x0000002b }, + { AR5K_BB_GAIN(52), 0x0000002b }, + { AR5K_BB_GAIN(53), 0x0000002b }, + { AR5K_BB_GAIN(54), 0x0000002b }, + { AR5K_BB_GAIN(55), 0x0000002b }, + { AR5K_BB_GAIN(56), 0x0000002b }, + { AR5K_BB_GAIN(57), 0x0000002b }, + { AR5K_BB_GAIN(58), 0x0000002b }, + { AR5K_BB_GAIN(59), 0x0000002b }, + { AR5K_BB_GAIN(60), 0x0000002b }, + { AR5K_BB_GAIN(61), 0x0000002b }, + { AR5K_BB_GAIN(62), 0x00000002 }, + { AR5K_BB_GAIN(63), 0x00000016 }, +}; + +/* RF 5112 Initial BaseBand Gain settings */ +static const struct ath5k_ini rf5112_ini_bbgain[] = { + { AR5K_BB_GAIN(0), 0x00000000 }, + { AR5K_BB_GAIN(1), 0x00000001 }, + { AR5K_BB_GAIN(2), 0x00000002 }, + { AR5K_BB_GAIN(3), 0x00000003 }, + { AR5K_BB_GAIN(4), 0x00000004 }, + { AR5K_BB_GAIN(5), 0x00000005 }, + { AR5K_BB_GAIN(6), 0x00000008 }, + { AR5K_BB_GAIN(7), 0x00000009 }, + { AR5K_BB_GAIN(8), 0x0000000a }, + { AR5K_BB_GAIN(9), 0x0000000b }, + { AR5K_BB_GAIN(10), 0x0000000c }, + { AR5K_BB_GAIN(11), 0x0000000d }, + { AR5K_BB_GAIN(12), 0x00000010 }, + { AR5K_BB_GAIN(13), 0x00000011 }, + { AR5K_BB_GAIN(14), 0x00000012 }, + { AR5K_BB_GAIN(15), 0x00000013 }, + { AR5K_BB_GAIN(16), 0x00000014 }, + { AR5K_BB_GAIN(17), 0x00000015 }, + { AR5K_BB_GAIN(18), 0x00000018 }, + { AR5K_BB_GAIN(19), 0x00000019 }, + { AR5K_BB_GAIN(20), 0x0000001a }, + { AR5K_BB_GAIN(21), 0x0000001b }, + { AR5K_BB_GAIN(22), 0x0000001c }, + { AR5K_BB_GAIN(23), 0x0000001d }, + { AR5K_BB_GAIN(24), 0x00000020 }, + { AR5K_BB_GAIN(25), 0x00000021 }, + { AR5K_BB_GAIN(26), 0x00000022 }, + { AR5K_BB_GAIN(27), 0x00000023 }, + { AR5K_BB_GAIN(28), 0x00000024 }, + { AR5K_BB_GAIN(29), 0x00000025 }, + { AR5K_BB_GAIN(30), 0x00000028 }, + { AR5K_BB_GAIN(31), 0x00000029 }, + { AR5K_BB_GAIN(32), 0x0000002a }, + { AR5K_BB_GAIN(33), 0x0000002b }, + { AR5K_BB_GAIN(34), 0x0000002c }, + { AR5K_BB_GAIN(35), 0x0000002d }, + { AR5K_BB_GAIN(36), 0x00000030 }, + { AR5K_BB_GAIN(37), 0x00000031 }, + { AR5K_BB_GAIN(38), 0x00000032 }, + { AR5K_BB_GAIN(39), 0x00000033 }, + { AR5K_BB_GAIN(40), 0x00000034 }, + { AR5K_BB_GAIN(41), 0x00000035 }, + { AR5K_BB_GAIN(42), 0x00000035 }, + { AR5K_BB_GAIN(43), 0x00000035 }, + { AR5K_BB_GAIN(44), 0x00000035 }, + { AR5K_BB_GAIN(45), 0x00000035 }, + { AR5K_BB_GAIN(46), 0x00000035 }, + { AR5K_BB_GAIN(47), 0x00000035 }, + { AR5K_BB_GAIN(48), 0x00000035 }, + { AR5K_BB_GAIN(49), 0x00000035 }, + { AR5K_BB_GAIN(50), 0x00000035 }, + { AR5K_BB_GAIN(51), 0x00000035 }, + { AR5K_BB_GAIN(52), 0x00000035 }, + { AR5K_BB_GAIN(53), 0x00000035 }, + { AR5K_BB_GAIN(54), 0x00000035 }, + { AR5K_BB_GAIN(55), 0x00000035 }, + { AR5K_BB_GAIN(56), 0x00000035 }, + { AR5K_BB_GAIN(57), 0x00000035 }, + { AR5K_BB_GAIN(58), 0x00000035 }, + { AR5K_BB_GAIN(59), 0x00000035 }, + { AR5K_BB_GAIN(60), 0x00000035 }, + { AR5K_BB_GAIN(61), 0x00000035 }, + { AR5K_BB_GAIN(62), 0x00000010 }, + { AR5K_BB_GAIN(63), 0x0000001a }, +}; + +/* + * Write initial register dump + */ +static void ath5k_hw_ini_registers(struct ath_hw *hal, unsigned int size, + const struct ath5k_ini *ini_regs, bool change_channel) +{ + unsigned int i; + + /* Write initial registers */ + for (i = 0; i < size; i++) { + /* On channel change there is + * no need to mess with PCU */ + if (change_channel && + ini_regs[i].ini_register >= AR5K_PCU_MIN && + ini_regs[i].ini_register <= AR5K_PCU_MAX) + continue; + + switch (ini_regs[i].ini_mode) { + case AR5K_INI_READ: + /* Cleared on read */ + ath5k_hw_reg_read(hal, ini_regs[i].ini_register); + break; + case AR5K_INI_WRITE: + default: + AR5K_REG_WAIT(i); + ath5k_hw_reg_write(hal, ini_regs[i].ini_value, + ini_regs[i].ini_register); + } + } +} + +static void ath5k_hw_ini_mode_registers(struct ath_hw *hal, + unsigned int size, const struct ath5k_ini_mode *ini_mode, + u8 mode) +{ + unsigned int i; + + for (i = 0; i < size; i++) { + AR5K_REG_WAIT(i); + ath5k_hw_reg_write(hal, ini_mode[i].mode_value[mode], + (u32)ini_mode[i].mode_register); + } + +} + +int ath5k_hw_write_initvals(struct ath_hw *hal, u8 mode, bool change_channel) +{ + /* + * Write initial mode-specific settings + */ + /*For 5212*/ + if (hal->ah_version == AR5K_AR5212) { + ath5k_hw_ini_mode_registers(hal, ARRAY_SIZE(ar5212_ini_mode), + ar5212_ini_mode, mode); + if (hal->ah_radio == AR5K_RF5111) + ath5k_hw_ini_mode_registers(hal, + ARRAY_SIZE(ar5212_rf5111_ini_mode), + ar5212_rf5111_ini_mode, mode); + else if (hal->ah_radio == AR5K_RF5112) + ath5k_hw_ini_mode_registers(hal, + ARRAY_SIZE(ar5212_rf5112_ini_mode), + ar5212_rf5112_ini_mode, mode); + } + /*For 5211*/ + if (hal->ah_version == AR5K_AR5211) + ath5k_hw_ini_mode_registers(hal, ARRAY_SIZE(ar5211_ini_mode), + ar5211_ini_mode, mode); + /* For 5210 mode settings check out ath5k_hw_reset_tx_queue */ + + /* + * Write initial settings common for all modes + */ + /*For 5212*/ + if (hal->ah_version == AR5K_AR5212) { + ath5k_hw_ini_registers(hal, ARRAY_SIZE(ar5212_ini), + ar5212_ini, change_channel); + if (hal->ah_radio == AR5K_RF5112) { + ath5k_hw_reg_write(hal, AR5K_PHY_PAPD_PROBE_INI_5112, + AR5K_PHY_PAPD_PROBE); + ath5k_hw_ini_registers(hal, + ARRAY_SIZE(rf5112_ini_bbgain), + rf5112_ini_bbgain, change_channel); + } else if (hal->ah_radio == AR5K_RF5111) { + ath5k_hw_reg_write(hal, AR5K_PHY_GAIN_2GHZ_INI_5111, + AR5K_PHY_GAIN_2GHZ); + ath5k_hw_reg_write(hal, AR5K_PHY_PAPD_PROBE_INI_5111, + AR5K_PHY_PAPD_PROBE); + ath5k_hw_ini_registers(hal, + ARRAY_SIZE(rf5111_ini_bbgain), + rf5111_ini_bbgain, change_channel); + } + } else if (hal->ah_version == AR5K_AR5211) { + ath5k_hw_ini_registers(hal, ARRAY_SIZE(ar5211_ini), + ar5211_ini, change_channel); + /* AR5211 only comes with 5111 */ + ath5k_hw_ini_registers(hal, ARRAY_SIZE(rf5111_ini_bbgain), + rf5111_ini_bbgain, change_channel); + } else if (hal->ah_version == AR5K_AR5210) { + ath5k_hw_ini_registers(hal, ARRAY_SIZE(ar5210_ini), + ar5210_ini, change_channel); + } + + return 0; +} diff -puN /dev/null drivers/net/wireless/ath5k/phy.c --- /dev/null +++ a/drivers/net/wireless/ath5k/phy.c @@ -0,0 +1,1686 @@ +/* + * PHY functions + * + * Copyright (c) 2004, 2005, 2006, 2007 Reyk Floeter + * Copyright (c) 2006, 2007 Nick Kossifidis + * Copyright (c) 2007 Jiri Slaby + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include + +#include "ath5k.h" +#include "reg.h" + +/* Struct to hold initial RF register values (RF Banks) */ +struct ath5k_ini_rf { + u8 rf_bank; /* check out ath5k_reg.h */ + u16 rf_register; /* register address */ + u32 rf_value[5]; /* register value for different modes (above) */ +}; + +/* + * Mode-specific RF Gain table (64bytes) for RF5111/5112 + * (RF5110 only comes with AR5210 and only supports a/turbo a mode so initial + * RF Gain values are included in AR5K_AR5210_INI) + */ +struct ath5k_ini_rfgain { + u16 rfg_register; /* RF Gain register address */ + u32 rfg_value[2]; /* [freq (see below)] */ +}; + +struct ath5k_gain_opt { + u32 go_default; + u32 go_steps_count; + const struct ath5k_gain_opt_step go_step[AR5K_GAIN_STEP_COUNT]; +}; + +/* RF5111 mode-specific init registers */ +static const struct ath5k_ini_rf rfregs_5111[] = { + { 0, 0x989c, + /* mode a/XR mode aTurbo mode b mode g mode gTurbo */ + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 0, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 0, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 0, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 0, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 0, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 0, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 0, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 0, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 0, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 0, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 0, 0x989c, + { 0x00380000, 0x00380000, 0x00380000, 0x00380000, 0x00380000 } }, + { 0, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 0, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 0, 0x989c, + { 0x00000000, 0x00000000, 0x000000c0, 0x00000080, 0x00000080 } }, + { 0, 0x989c, + { 0x000400f9, 0x000400f9, 0x000400ff, 0x000400fd, 0x000400fd } }, + { 0, 0x98d4, + { 0x00000000, 0x00000000, 0x00000004, 0x00000004, 0x00000004 } }, + { 1, 0x98d4, + { 0x00000020, 0x00000020, 0x00000020, 0x00000020, 0x00000020 } }, + { 2, 0x98d4, + { 0x00000010, 0x00000014, 0x00000010, 0x00000010, 0x00000014 } }, + { 3, 0x98d8, + { 0x00601068, 0x00601068, 0x00601068, 0x00601068, 0x00601068 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x10000000, 0x10000000, 0x10000000, 0x10000000, 0x10000000 } }, + { 6, 0x989c, + { 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x0a000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x003800c0, 0x00380080, 0x023800c0, 0x003800c0, 0x003800c0 } }, + { 6, 0x989c, + { 0x00020006, 0x00020006, 0x00000006, 0x00020006, 0x00020006 } }, + { 6, 0x989c, + { 0x00000089, 0x00000089, 0x00000089, 0x00000089, 0x00000089 } }, + { 6, 0x989c, + { 0x000000a0, 0x000000a0, 0x000000a0, 0x000000a0, 0x000000a0 } }, + { 6, 0x989c, + { 0x00040007, 0x00040007, 0x00040007, 0x00040007, 0x00040007 } }, + { 6, 0x98d4, + { 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a } }, + { 7, 0x989c, + { 0x00000040, 0x00000048, 0x00000040, 0x00000040, 0x00000040 } }, + { 7, 0x989c, + { 0x00000010, 0x00000010, 0x00000010, 0x00000010, 0x00000010 } }, + { 7, 0x989c, + { 0x00000008, 0x00000008, 0x00000008, 0x00000008, 0x00000008 } }, + { 7, 0x989c, + { 0x0000004f, 0x0000004f, 0x0000004f, 0x0000004f, 0x0000004f } }, + { 7, 0x989c, + { 0x000000f1, 0x000000f1, 0x00000061, 0x000000f1, 0x000000f1 } }, + { 7, 0x989c, + { 0x0000904f, 0x0000904f, 0x0000904c, 0x0000904f, 0x0000904f } }, + { 7, 0x989c, + { 0x0000125a, 0x0000125a, 0x0000129a, 0x0000125a, 0x0000125a } }, + { 7, 0x98cc, + { 0x0000000e, 0x0000000e, 0x0000000f, 0x0000000e, 0x0000000e } }, +}; + +/* Initial RF Gain settings for RF5111 */ +static const struct ath5k_ini_rfgain rfgain_5111[] = { + /* 5Ghz 2Ghz */ + { AR5K_RF_GAIN(0), { 0x000001a9, 0x00000000 } }, + { AR5K_RF_GAIN(1), { 0x000001e9, 0x00000040 } }, + { AR5K_RF_GAIN(2), { 0x00000029, 0x00000080 } }, + { AR5K_RF_GAIN(3), { 0x00000069, 0x00000150 } }, + { AR5K_RF_GAIN(4), { 0x00000199, 0x00000190 } }, + { AR5K_RF_GAIN(5), { 0x000001d9, 0x000001d0 } }, + { AR5K_RF_GAIN(6), { 0x00000019, 0x00000010 } }, + { AR5K_RF_GAIN(7), { 0x00000059, 0x00000044 } }, + { AR5K_RF_GAIN(8), { 0x00000099, 0x00000084 } }, + { AR5K_RF_GAIN(9), { 0x000001a5, 0x00000148 } }, + { AR5K_RF_GAIN(10), { 0x000001e5, 0x00000188 } }, + { AR5K_RF_GAIN(11), { 0x00000025, 0x000001c8 } }, + { AR5K_RF_GAIN(12), { 0x000001c8, 0x00000014 } }, + { AR5K_RF_GAIN(13), { 0x00000008, 0x00000042 } }, + { AR5K_RF_GAIN(14), { 0x00000048, 0x00000082 } }, + { AR5K_RF_GAIN(15), { 0x00000088, 0x00000178 } }, + { AR5K_RF_GAIN(16), { 0x00000198, 0x000001b8 } }, + { AR5K_RF_GAIN(17), { 0x000001d8, 0x000001f8 } }, + { AR5K_RF_GAIN(18), { 0x00000018, 0x00000012 } }, + { AR5K_RF_GAIN(19), { 0x00000058, 0x00000052 } }, + { AR5K_RF_GAIN(20), { 0x00000098, 0x00000092 } }, + { AR5K_RF_GAIN(21), { 0x000001a4, 0x0000017c } }, + { AR5K_RF_GAIN(22), { 0x000001e4, 0x000001bc } }, + { AR5K_RF_GAIN(23), { 0x00000024, 0x000001fc } }, + { AR5K_RF_GAIN(24), { 0x00000064, 0x0000000a } }, + { AR5K_RF_GAIN(25), { 0x000000a4, 0x0000004a } }, + { AR5K_RF_GAIN(26), { 0x000000e4, 0x0000008a } }, + { AR5K_RF_GAIN(27), { 0x0000010a, 0x0000015a } }, + { AR5K_RF_GAIN(28), { 0x0000014a, 0x0000019a } }, + { AR5K_RF_GAIN(29), { 0x0000018a, 0x000001da } }, + { AR5K_RF_GAIN(30), { 0x000001ca, 0x0000000e } }, + { AR5K_RF_GAIN(31), { 0x0000000a, 0x0000004e } }, + { AR5K_RF_GAIN(32), { 0x0000004a, 0x0000008e } }, + { AR5K_RF_GAIN(33), { 0x0000008a, 0x0000015e } }, + { AR5K_RF_GAIN(34), { 0x000001ba, 0x0000019e } }, + { AR5K_RF_GAIN(35), { 0x000001fa, 0x000001de } }, + { AR5K_RF_GAIN(36), { 0x0000003a, 0x00000009 } }, + { AR5K_RF_GAIN(37), { 0x0000007a, 0x00000049 } }, + { AR5K_RF_GAIN(38), { 0x00000186, 0x00000089 } }, + { AR5K_RF_GAIN(39), { 0x000001c6, 0x00000179 } }, + { AR5K_RF_GAIN(40), { 0x00000006, 0x000001b9 } }, + { AR5K_RF_GAIN(41), { 0x00000046, 0x000001f9 } }, + { AR5K_RF_GAIN(42), { 0x00000086, 0x00000039 } }, + { AR5K_RF_GAIN(43), { 0x000000c6, 0x00000079 } }, + { AR5K_RF_GAIN(44), { 0x000000c6, 0x000000b9 } }, + { AR5K_RF_GAIN(45), { 0x000000c6, 0x000001bd } }, + { AR5K_RF_GAIN(46), { 0x000000c6, 0x000001fd } }, + { AR5K_RF_GAIN(47), { 0x000000c6, 0x0000003d } }, + { AR5K_RF_GAIN(48), { 0x000000c6, 0x0000007d } }, + { AR5K_RF_GAIN(49), { 0x000000c6, 0x000000bd } }, + { AR5K_RF_GAIN(50), { 0x000000c6, 0x000000fd } }, + { AR5K_RF_GAIN(51), { 0x000000c6, 0x000000fd } }, + { AR5K_RF_GAIN(52), { 0x000000c6, 0x000000fd } }, + { AR5K_RF_GAIN(53), { 0x000000c6, 0x000000fd } }, + { AR5K_RF_GAIN(54), { 0x000000c6, 0x000000fd } }, + { AR5K_RF_GAIN(55), { 0x000000c6, 0x000000fd } }, + { AR5K_RF_GAIN(56), { 0x000000c6, 0x000000fd } }, + { AR5K_RF_GAIN(57), { 0x000000c6, 0x000000fd } }, + { AR5K_RF_GAIN(58), { 0x000000c6, 0x000000fd } }, + { AR5K_RF_GAIN(59), { 0x000000c6, 0x000000fd } }, + { AR5K_RF_GAIN(60), { 0x000000c6, 0x000000fd } }, + { AR5K_RF_GAIN(61), { 0x000000c6, 0x000000fd } }, + { AR5K_RF_GAIN(62), { 0x000000c6, 0x000000fd } }, + { AR5K_RF_GAIN(63), { 0x000000c6, 0x000000fd } }, +}; + +static const struct ath5k_gain_opt rfgain_opt_5111 = { + 4, + 9, + { + { { 4, 1, 1, 1 }, 6 }, + { { 4, 0, 1, 1 }, 4 }, + { { 3, 1, 1, 1 }, 3 }, + { { 4, 0, 0, 1 }, 1 }, + { { 4, 1, 1, 0 }, 0 }, + { { 4, 0, 1, 0 }, -2 }, + { { 3, 1, 1, 0 }, -3 }, + { { 4, 0, 0, 0 }, -4 }, + { { 2, 1, 1, 0 }, -6 } + } +}; + +/* RF5112 mode-specific init registers */ +static const struct ath5k_ini_rf rfregs_5112[] = { + { 1, 0x98d4, + /* mode a/XR mode aTurbo mode b mode g mode gTurbo */ + { 0x00000020, 0x00000020, 0x00000020, 0x00000020, 0x00000020 } }, + { 2, 0x98d0, + { 0x03060408, 0x03070408, 0x03060408, 0x03060408, 0x03070408 } }, + { 3, 0x98dc, + { 0x00a0c0c0, 0x00a0c0c0, 0x00e0c0c0, 0x00e0c0c0, 0x00e0c0c0 } }, + { 6, 0x989c, + { 0x00a00000, 0x00a00000, 0x00a00000, 0x00a00000, 0x00a00000 } }, + { 6, 0x989c, + { 0x000a0000, 0x000a0000, 0x000a0000, 0x000a0000, 0x000a0000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00660000, 0x00660000, 0x00660000, 0x00660000, 0x00660000 } }, + { 6, 0x989c, + { 0x00db0000, 0x00db0000, 0x00db0000, 0x00db0000, 0x00db0000 } }, + { 6, 0x989c, + { 0x00f10000, 0x00f10000, 0x00f10000, 0x00f10000, 0x00f10000 } }, + { 6, 0x989c, + { 0x00120000, 0x00120000, 0x00120000, 0x00120000, 0x00120000 } }, + { 6, 0x989c, + { 0x00120000, 0x00120000, 0x00120000, 0x00120000, 0x00120000 } }, + { 6, 0x989c, + { 0x00730000, 0x00730000, 0x00730000, 0x00730000, 0x00730000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x000c0000, 0x000c0000, 0x000c0000, 0x000c0000, 0x000c0000 } }, + { 6, 0x989c, + { 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000 } }, + { 6, 0x989c, + { 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000 } }, + { 6, 0x989c, + { 0x008b0000, 0x008b0000, 0x008b0000, 0x008b0000, 0x008b0000 } }, + { 6, 0x989c, + { 0x00600000, 0x00600000, 0x00600000, 0x00600000, 0x00600000 } }, + { 6, 0x989c, + { 0x000c0000, 0x000c0000, 0x000c0000, 0x000c0000, 0x000c0000 } }, + { 6, 0x989c, + { 0x00840000, 0x00840000, 0x00840000, 0x00840000, 0x00840000 } }, + { 6, 0x989c, + { 0x00640000, 0x00640000, 0x00640000, 0x00640000, 0x00640000 } }, + { 6, 0x989c, + { 0x00200000, 0x00200000, 0x00200000, 0x00200000, 0x00200000 } }, + { 6, 0x989c, + { 0x00240000, 0x00240000, 0x00240000, 0x00240000, 0x00240000 } }, + { 6, 0x989c, + { 0x00250000, 0x00250000, 0x00250000, 0x00250000, 0x00250000 } }, + { 6, 0x989c, + { 0x00110000, 0x00110000, 0x00110000, 0x00110000, 0x00110000 } }, + { 6, 0x989c, + { 0x00110000, 0x00110000, 0x00110000, 0x00110000, 0x00110000 } }, + { 6, 0x989c, + { 0x00510000, 0x00510000, 0x00510000, 0x00510000, 0x00510000 } }, + { 6, 0x989c, + { 0x1c040000, 0x1c040000, 0x1c040000, 0x1c040000, 0x1c040000 } }, + { 6, 0x989c, + { 0x000a0000, 0x000a0000, 0x000a0000, 0x000a0000, 0x000a0000 } }, + { 6, 0x989c, + { 0x00a10000, 0x00a10000, 0x00a10000, 0x00a10000, 0x00a10000 } }, + { 6, 0x989c, + { 0x00400000, 0x00400000, 0x00400000, 0x00400000, 0x00400000 } }, + { 6, 0x989c, + { 0x03090000, 0x03090000, 0x03090000, 0x03090000, 0x03090000 } }, + { 6, 0x989c, + { 0x06000000, 0x06000000, 0x06000000, 0x06000000, 0x06000000 } }, + { 6, 0x989c, + { 0x000000b0, 0x000000b0, 0x000000a8, 0x000000a8, 0x000000a8 } }, + { 6, 0x989c, + { 0x0000002e, 0x0000002e, 0x0000002e, 0x0000002e, 0x0000002e } }, + { 6, 0x989c, + { 0x006c4a41, 0x006c4a41, 0x006c4af1, 0x006c4a61, 0x006c4a61 } }, + { 6, 0x989c, + { 0x0050892a, 0x0050892a, 0x0050892b, 0x0050892b, 0x0050892b } }, + { 6, 0x989c, + { 0x00842400, 0x00842400, 0x00842400, 0x00842400, 0x00842400 } }, + { 6, 0x989c, + { 0x00c69200, 0x00c69200, 0x00c69200, 0x00c69200, 0x00c69200 } }, + { 6, 0x98d0, + { 0x0002000c, 0x0002000c, 0x0002000c, 0x0002000c, 0x0002000c } }, + { 7, 0x989c, + { 0x00000094, 0x00000094, 0x00000094, 0x00000094, 0x00000094 } }, + { 7, 0x989c, + { 0x00000091, 0x00000091, 0x00000091, 0x00000091, 0x00000091 } }, + { 7, 0x989c, + { 0x0000000a, 0x0000000a, 0x00000012, 0x00000012, 0x00000012 } }, + { 7, 0x989c, + { 0x00000080, 0x00000080, 0x00000080, 0x00000080, 0x00000080 } }, + { 7, 0x989c, + { 0x000000c1, 0x000000c1, 0x000000c1, 0x000000c1, 0x000000c1 } }, + { 7, 0x989c, + { 0x00000060, 0x00000060, 0x00000060, 0x00000060, 0x00000060 } }, + { 7, 0x989c, + { 0x000000f0, 0x000000f0, 0x000000f0, 0x000000f0, 0x000000f0 } }, + { 7, 0x989c, + { 0x00000022, 0x00000022, 0x00000022, 0x00000022, 0x00000022 } }, + { 7, 0x989c, + { 0x00000092, 0x00000092, 0x00000092, 0x00000092, 0x00000092 } }, + { 7, 0x989c, + { 0x000000d4, 0x000000d4, 0x000000d4, 0x000000d4, 0x000000d4 } }, + { 7, 0x989c, + { 0x000014cc, 0x000014cc, 0x000014cc, 0x000014cc, 0x000014cc } }, + { 7, 0x989c, + { 0x0000048c, 0x0000048c, 0x0000048c, 0x0000048c, 0x0000048c } }, + { 7, 0x98c4, + { 0x00000003, 0x00000003, 0x00000003, 0x00000003, 0x00000003 } }, +}; + +/* RF5112A mode-specific init registers */ +static const struct ath5k_ini_rf rfregs_5112a[] = { + { 1, 0x98d4, + /* mode a/XR mode aTurbo mode b mode g mode gTurbo */ + { 0x00000020, 0x00000020, 0x00000020, 0x00000020, 0x00000020 } }, + { 2, 0x98d0, + { 0x03060408, 0x03070408, 0x03060408, 0x03060408, 0x03070408 } }, + { 3, 0x98dc, + { 0x00a0c0c0, 0x00a0c0c0, 0x00e0c0c0, 0x00e0c0c0, 0x00e0c0c0 } }, + { 6, 0x989c, + { 0x0f000000, 0x0f000000, 0x0f000000, 0x0f000000, 0x0f000000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00800000, 0x00800000, 0x00800000, 0x00800000, 0x00800000 } }, + { 6, 0x989c, + { 0x002a0000, 0x002a0000, 0x002a0000, 0x002a0000, 0x002a0000 } }, + { 6, 0x989c, + { 0x00010000, 0x00010000, 0x00010000, 0x00010000, 0x00010000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00180000, 0x00180000, 0x00180000, 0x00180000, 0x00180000 } }, + { 6, 0x989c, + { 0x00600000, 0x00600000, 0x006e0000, 0x006e0000, 0x006e0000 } }, + { 6, 0x989c, + { 0x00c70000, 0x00c70000, 0x00c70000, 0x00c70000, 0x00c70000 } }, + { 6, 0x989c, + { 0x004b0000, 0x004b0000, 0x004b0000, 0x004b0000, 0x004b0000 } }, + { 6, 0x989c, + { 0x04480000, 0x04480000, 0x04480000, 0x04480000, 0x04480000 } }, + { 6, 0x989c, + { 0x00220000, 0x00220000, 0x00220000, 0x00220000, 0x00220000 } }, + { 6, 0x989c, + { 0x00e40000, 0x00e40000, 0x00e40000, 0x00e40000, 0x00e40000 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x00fc0000, 0x00fc0000, 0x00fc0000, 0x00fc0000, 0x00fc0000 } }, + { 6, 0x989c, + { 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000, 0x00ff0000 } }, + { 6, 0x989c, + { 0x043f0000, 0x043f0000, 0x043f0000, 0x043f0000, 0x043f0000 } }, + { 6, 0x989c, + { 0x000c0000, 0x000c0000, 0x000c0000, 0x000c0000, 0x000c0000 } }, + { 6, 0x989c, + { 0x00190000, 0x00190000, 0x00190000, 0x00190000, 0x00190000 } }, + { 6, 0x989c, + { 0x00240000, 0x00240000, 0x00240000, 0x00240000, 0x00240000 } }, + { 6, 0x989c, + { 0x00b40000, 0x00b40000, 0x00b40000, 0x00b40000, 0x00b40000 } }, + { 6, 0x989c, + { 0x00990000, 0x00990000, 0x00990000, 0x00990000, 0x00990000 } }, + { 6, 0x989c, + { 0x00500000, 0x00500000, 0x00500000, 0x00500000, 0x00500000 } }, + { 6, 0x989c, + { 0x002a0000, 0x002a0000, 0x002a0000, 0x002a0000, 0x002a0000 } }, + { 6, 0x989c, + { 0x00120000, 0x00120000, 0x00120000, 0x00120000, 0x00120000 } }, + { 6, 0x989c, + { 0xc0320000, 0xc0320000, 0xc0320000, 0xc0320000, 0xc0320000 } }, + { 6, 0x989c, + { 0x01740000, 0x01740000, 0x01740000, 0x01740000, 0x01740000 } }, + { 6, 0x989c, + { 0x00110000, 0x00110000, 0x00110000, 0x00110000, 0x00110000 } }, + { 6, 0x989c, + { 0x86280000, 0x86280000, 0x86280000, 0x86280000, 0x86280000 } }, + { 6, 0x989c, + { 0x31840000, 0x31840000, 0x31840000, 0x31840000, 0x31840000 } }, + { 6, 0x989c, + { 0x00020080, 0x00020080, 0x00020080, 0x00020080, 0x00020080 } }, + { 6, 0x989c, + { 0x00080009, 0x00080009, 0x00080009, 0x00080009, 0x00080009 } }, + { 6, 0x989c, + { 0x00000003, 0x00000003, 0x00000003, 0x00000003, 0x00000003 } }, + { 6, 0x989c, + { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, + { 6, 0x989c, + { 0x000000b2, 0x000000b2, 0x000000b2, 0x000000b2, 0x000000b2 } }, + { 6, 0x989c, + { 0x00b02084, 0x00b02084, 0x00b02084, 0x00b02084, 0x00b02084 } }, + { 6, 0x989c, + { 0x004125a4, 0x004125a4, 0x004125a4, 0x004125a4, 0x004125a4 } }, + { 6, 0x989c, + { 0x00119220, 0x00119220, 0x00119220, 0x00119220, 0x00119220 } }, + { 6, 0x989c, + { 0x001a4800, 0x001a4800, 0x001a4800, 0x001a4800, 0x001a4800 } }, + { 6, 0x98d8, + { 0x000b0230, 0x000b0230, 0x000b0230, 0x000b0230, 0x000b0230 } }, + { 7, 0x989c, + { 0x00000094, 0x00000094, 0x00000094, 0x00000094, 0x00000094 } }, + { 7, 0x989c, + { 0x00000091, 0x00000091, 0x00000091, 0x00000091, 0x00000091 } }, + { 7, 0x989c, + { 0x00000012, 0x00000012, 0x00000012, 0x00000012, 0x00000012 } }, + { 7, 0x989c, + { 0x00000080, 0x00000080, 0x00000080, 0x00000080, 0x00000080 } }, + { 7, 0x989c, + { 0x000000d9, 0x000000d9, 0x000000d9, 0x000000d9, 0x000000d9 } }, + { 7, 0x989c, + { 0x00000060, 0x00000060, 0x00000060, 0x00000060, 0x00000060 } }, + { 7, 0x989c, + { 0x000000f0, 0x000000f0, 0x000000f0, 0x000000f0, 0x000000f0 } }, + { 7, 0x989c, + { 0x000000a2, 0x000000a2, 0x000000a2, 0x000000a2, 0x000000a2 } }, + { 7, 0x989c, + { 0x00000052, 0x00000052, 0x00000052, 0x00000052, 0x00000052 } }, + { 7, 0x989c, + { 0x000000d4, 0x000000d4, 0x000000d4, 0x000000d4, 0x000000d4 } }, + { 7, 0x989c, + { 0x000014cc, 0x000014cc, 0x000014cc, 0x000014cc, 0x000014cc } }, + { 7, 0x989c, + { 0x0000048c, 0x0000048c, 0x0000048c, 0x0000048c, 0x0000048c } }, + { 7, 0x98c4, + { 0x00000003, 0x00000003, 0x00000003, 0x00000003, 0x00000003 } }, +}; + + +/* Initial RF Gain settings for RF5112 */ +static const struct ath5k_ini_rfgain rfgain_5112[] = { + /* 5Ghz 2Ghz */ + { AR5K_RF_GAIN(0), { 0x00000007, 0x00000007 } }, + { AR5K_RF_GAIN(1), { 0x00000047, 0x00000047 } }, + { AR5K_RF_GAIN(2), { 0x00000087, 0x00000087 } }, + { AR5K_RF_GAIN(3), { 0x000001a0, 0x000001a0 } }, + { AR5K_RF_GAIN(4), { 0x000001e0, 0x000001e0 } }, + { AR5K_RF_GAIN(5), { 0x00000020, 0x00000020 } }, + { AR5K_RF_GAIN(6), { 0x00000060, 0x00000060 } }, + { AR5K_RF_GAIN(7), { 0x000001a1, 0x000001a1 } }, + { AR5K_RF_GAIN(8), { 0x000001e1, 0x000001e1 } }, + { AR5K_RF_GAIN(9), { 0x00000021, 0x00000021 } }, + { AR5K_RF_GAIN(10), { 0x00000061, 0x00000061 } }, + { AR5K_RF_GAIN(11), { 0x00000162, 0x00000162 } }, + { AR5K_RF_GAIN(12), { 0x000001a2, 0x000001a2 } }, + { AR5K_RF_GAIN(13), { 0x000001e2, 0x000001e2 } }, + { AR5K_RF_GAIN(14), { 0x00000022, 0x00000022 } }, + { AR5K_RF_GAIN(15), { 0x00000062, 0x00000062 } }, + { AR5K_RF_GAIN(16), { 0x00000163, 0x00000163 } }, + { AR5K_RF_GAIN(17), { 0x000001a3, 0x000001a3 } }, + { AR5K_RF_GAIN(18), { 0x000001e3, 0x000001e3 } }, + { AR5K_RF_GAIN(19), { 0x00000023, 0x00000023 } }, + { AR5K_RF_GAIN(20), { 0x00000063, 0x00000063 } }, + { AR5K_RF_GAIN(21), { 0x00000184, 0x00000184 } }, + { AR5K_RF_GAIN(22), { 0x000001c4, 0x000001c4 } }, + { AR5K_RF_GAIN(23), { 0x00000004, 0x00000004 } }, + { AR5K_RF_GAIN(24), { 0x000001ea, 0x0000000b } }, + { AR5K_RF_GAIN(25), { 0x0000002a, 0x0000004b } }, + { AR5K_RF_GAIN(26), { 0x0000006a, 0x0000008b } }, + { AR5K_RF_GAIN(27), { 0x000000aa, 0x000001ac } }, + { AR5K_RF_GAIN(28), { 0x000001ab, 0x000001ec } }, + { AR5K_RF_GAIN(29), { 0x000001eb, 0x0000002c } }, + { AR5K_RF_GAIN(30), { 0x0000002b, 0x00000012 } }, + { AR5K_RF_GAIN(31), { 0x0000006b, 0x00000052 } }, + { AR5K_RF_GAIN(32), { 0x000000ab, 0x00000092 } }, + { AR5K_RF_GAIN(33), { 0x000001ac, 0x00000193 } }, + { AR5K_RF_GAIN(34), { 0x000001ec, 0x000001d3 } }, + { AR5K_RF_GAIN(35), { 0x0000002c, 0x00000013 } }, + { AR5K_RF_GAIN(36), { 0x0000003a, 0x00000053 } }, + { AR5K_RF_GAIN(37), { 0x0000007a, 0x00000093 } }, + { AR5K_RF_GAIN(38), { 0x000000ba, 0x00000194 } }, + { AR5K_RF_GAIN(39), { 0x000001bb, 0x000001d4 } }, + { AR5K_RF_GAIN(40), { 0x000001fb, 0x00000014 } }, + { AR5K_RF_GAIN(41), { 0x0000003b, 0x0000003a } }, + { AR5K_RF_GAIN(42), { 0x0000007b, 0x0000007a } }, + { AR5K_RF_GAIN(43), { 0x000000bb, 0x000000ba } }, + { AR5K_RF_GAIN(44), { 0x000001bc, 0x000001bb } }, + { AR5K_RF_GAIN(45), { 0x000001fc, 0x000001fb } }, + { AR5K_RF_GAIN(46), { 0x0000003c, 0x0000003b } }, + { AR5K_RF_GAIN(47), { 0x0000007c, 0x0000007b } }, + { AR5K_RF_GAIN(48), { 0x000000bc, 0x000000bb } }, + { AR5K_RF_GAIN(49), { 0x000000fc, 0x000001bc } }, + { AR5K_RF_GAIN(50), { 0x000000fc, 0x000001fc } }, + { AR5K_RF_GAIN(51), { 0x000000fc, 0x0000003c } }, + { AR5K_RF_GAIN(52), { 0x000000fc, 0x0000007c } }, + { AR5K_RF_GAIN(53), { 0x000000fc, 0x000000bc } }, + { AR5K_RF_GAIN(54), { 0x000000fc, 0x000000fc } }, + { AR5K_RF_GAIN(55), { 0x000000fc, 0x000000fc } }, + { AR5K_RF_GAIN(56), { 0x000000fc, 0x000000fc } }, + { AR5K_RF_GAIN(57), { 0x000000fc, 0x000000fc } }, + { AR5K_RF_GAIN(58), { 0x000000fc, 0x000000fc } }, + { AR5K_RF_GAIN(59), { 0x000000fc, 0x000000fc } }, + { AR5K_RF_GAIN(60), { 0x000000fc, 0x000000fc } }, + { AR5K_RF_GAIN(61), { 0x000000fc, 0x000000fc } }, + { AR5K_RF_GAIN(62), { 0x000000fc, 0x000000fc } }, + { AR5K_RF_GAIN(63), { 0x000000fc, 0x000000fc } }, +}; + +static const struct ath5k_gain_opt rfgain_opt_5112 = { + 1, + 8, + { + { { 3, 0, 0, 0, 0, 0, 0 }, 6 }, + { { 2, 0, 0, 0, 0, 0, 0 }, 0 }, + { { 1, 0, 0, 0, 0, 0, 0 }, -3 }, + { { 0, 0, 0, 0, 0, 0, 0 }, -6 }, + { { 0, 1, 1, 0, 0, 0, 0 }, -8 }, + { { 0, 1, 1, 0, 1, 1, 0 }, -10 }, + { { 0, 1, 0, 1, 1, 1, 0 }, -13 }, + { { 0, 1, 0, 1, 1, 0, 1 }, -16 }, + } +}; + +/* + * Used to modify RF Banks before writing them to AR5K_RF_BUFFER + */ +static unsigned int ath5k_hw_rfregs_op(u32 *rf, u32 offset, u32 reg, u32 bits, + u32 first, u32 col, bool set) +{ + u32 mask, entry, last, data, shift, position; + s32 left; + int i; + + data = 0; + + if (rf == NULL) + /* should not happen */ + return 0; + + if (!(col <= 3 && bits <= 32 && first + bits <= 319)) { + AR5K_PRINTF("invalid values at offset %u\n", offset); + return 0; + } + + entry = ((first - 1) / 8) + offset; + position = (first - 1) % 8; + + if (set == true) + data = ath5k_hw_bitswap(reg, bits); + + for (i = shift = 0, left = bits; left > 0; position = 0, entry++, i++) { + last = (position + left > 8) ? 8 : position + left; + mask = (((1 << last) - 1) ^ ((1 << position) - 1)) << (col * 8); + + if (set == true) { + rf[entry] &= ~mask; + rf[entry] |= ((data << position) << (col * 8)) & mask; + data >>= (8 - position); + } else { + data = (((rf[entry] & mask) >> (col * 8)) >> position) + << shift; + shift += last - position; + } + + left -= 8 - position; + } + + data = set == true ? 1 : ath5k_hw_bitswap(data, bits); + + return data; +} + +static u32 ath5k_hw_rfregs_gainf_corr(struct ath_hw *hal) +{ + u32 mix, step; + u32 *rf; + + if (hal->ah_rf_banks == NULL) + return 0; + + rf = hal->ah_rf_banks; + hal->ah_gain.g_f_corr = 0; + + if (ath5k_hw_rfregs_op(rf, hal->ah_offset[7], 0, 1, 36, 0, false) != 1) + return 0; + + step = ath5k_hw_rfregs_op(rf, hal->ah_offset[7], 0, 4, 32, 0, false); + mix = hal->ah_gain.g_step->gos_param[0]; + + switch (mix) { + case 3: + hal->ah_gain.g_f_corr = step * 2; + break; + case 2: + hal->ah_gain.g_f_corr = (step - 5) * 2; + break; + case 1: + hal->ah_gain.g_f_corr = step; + break; + default: + hal->ah_gain.g_f_corr = 0; + break; + } + + return hal->ah_gain.g_f_corr; +} + +static bool ath5k_hw_rfregs_gain_readback(struct ath_hw *hal) +{ + u32 step, mix, level[4]; + u32 *rf; + + if (hal->ah_rf_banks == NULL) + return false; + + rf = hal->ah_rf_banks; + + if (hal->ah_radio == AR5K_RF5111) { + step = ath5k_hw_rfregs_op(rf, hal->ah_offset[7], 0, 6, 37, 0, + false); + level[0] = 0; + level[1] = (step == 0x3f) ? 0x32 : step + 4; + level[2] = (step != 0x3f) ? 0x40 : level[0]; + level[3] = level[2] + 0x32; + + hal->ah_gain.g_high = level[3] - + (step == 0x3f ? AR5K_GAIN_DYN_ADJUST_HI_MARGIN : -5); + hal->ah_gain.g_low = level[0] + + (step == 0x3f ? AR5K_GAIN_DYN_ADJUST_LO_MARGIN : 0); + } else { + mix = ath5k_hw_rfregs_op(rf, hal->ah_offset[7], 0, 1, 36, 0, + false); + level[0] = level[2] = 0; + + if (mix == 1) { + level[1] = level[3] = 83; + } else { + level[1] = level[3] = 107; + hal->ah_gain.g_high = 55; + } + } + + return (hal->ah_gain.g_current >= level[0] && + hal->ah_gain.g_current <= level[1]) || + (hal->ah_gain.g_current >= level[2] && + hal->ah_gain.g_current <= level[3]); +} + +static s32 ath5k_hw_rfregs_gain_adjust(struct ath_hw *hal) +{ + const struct ath5k_gain_opt *go; + int ret = 0; + + switch (hal->ah_radio) { + case AR5K_RF5111: + go = &rfgain_opt_5111; + break; + case AR5K_RF5112: + go = &rfgain_opt_5112; + break; + default: + return 0; + } + + hal->ah_gain.g_step = &go->go_step[hal->ah_gain.g_step_idx]; + + if (hal->ah_gain.g_current >= hal->ah_gain.g_high) { + if (hal->ah_gain.g_step_idx == 0) + return -1; + for (hal->ah_gain.g_target = hal->ah_gain.g_current; + hal->ah_gain.g_target >= hal->ah_gain.g_high && + hal->ah_gain.g_step_idx > 0; + hal->ah_gain.g_step = + &go->go_step[hal->ah_gain.g_step_idx]) + hal->ah_gain.g_target -= 2 * + (go->go_step[--(hal->ah_gain.g_step_idx)].gos_gain - + hal->ah_gain.g_step->gos_gain); + + ret = 1; + goto done; + } + + if (hal->ah_gain.g_current <= hal->ah_gain.g_low) { + if (hal->ah_gain.g_step_idx == (go->go_steps_count - 1)) + return -2; + for (hal->ah_gain.g_target = hal->ah_gain.g_current; + hal->ah_gain.g_target <= hal->ah_gain.g_low && + hal->ah_gain.g_step_idx < go->go_steps_count-1; + hal->ah_gain.g_step = + &go->go_step[hal->ah_gain.g_step_idx]) + hal->ah_gain.g_target -= 2 * + (go->go_step[++hal->ah_gain.g_step_idx].gos_gain - + hal->ah_gain.g_step->gos_gain); + + ret = 2; + goto done; + } + +done: +#ifdef AR5K_DEBUG + AR5K_PRINTF("ret %d, gain step %u, current gain %u, target gain %u\n", + ret, hal->ah_gain.g_step_idx, hal->ah_gain.g_current, + hal->ah_gain.g_target); +#endif + + return ret; +} + +/* + * Read EEPROM Calibration data, modify RF Banks and Initialize RF5111 + */ +static int ath5k_hw_rf5111_rfregs(struct ath_hw *hal, + struct ieee80211_channel *channel, unsigned int mode) +{ + struct ath5k_eeprom_info *ee = &hal->ah_capabilities.cap_eeprom; + u32 *rf; + const unsigned int rf_size = ARRAY_SIZE(rfregs_5111); + unsigned int i; + int obdb = -1, bank = -1; + u32 ee_mode; + + AR5K_ASSERT_ENTRY(mode, AR5K_INI_VAL_MAX); + + rf = hal->ah_rf_banks; + + /* Copy values to modify them */ + for (i = 0; i < rf_size; i++) { + if (rfregs_5111[i].rf_bank >= AR5K_RF5111_INI_RF_MAX_BANKS) { + AR5K_PRINT("invalid bank\n"); + return -EINVAL; + } + + if (bank != rfregs_5111[i].rf_bank) { + bank = rfregs_5111[i].rf_bank; + hal->ah_offset[bank] = i; + } + + rf[i] = rfregs_5111[i].rf_value[mode]; + } + + /* Modify bank 0 */ + if (channel->val & CHANNEL_2GHZ) { + if (channel->val & CHANNEL_B) + ee_mode = AR5K_EEPROM_MODE_11B; + else + ee_mode = AR5K_EEPROM_MODE_11G; + obdb = 0; + + if (!ath5k_hw_rfregs_op(rf, hal->ah_offset[0], + ee->ee_ob[ee_mode][obdb], 3, 119, 0, true)) + return -EINVAL; + + if (!ath5k_hw_rfregs_op(rf, hal->ah_offset[0], + ee->ee_ob[ee_mode][obdb], 3, 122, 0, true)) + return -EINVAL; + + obdb = 1; + /* Modify bank 6 */ + } else { + /* For 11a, Turbo and XR */ + ee_mode = AR5K_EEPROM_MODE_11A; + obdb = channel->freq >= 5725 ? 3 : + (channel->freq >= 5500 ? 2 : + (channel->freq >= 5260 ? 1 : + (channel->freq > 4000 ? 0 : -1))); + + if (!ath5k_hw_rfregs_op(rf, hal->ah_offset[6], + ee->ee_pwd_84, 1, 51, 3, true)) + return -EINVAL; + + if (!ath5k_hw_rfregs_op(rf, hal->ah_offset[6], + ee->ee_pwd_90, 1, 45, 3, true)) + return -EINVAL; + } + + if (!ath5k_hw_rfregs_op(rf, hal->ah_offset[6], + !ee->ee_xpd[ee_mode], 1, 95, 0, true)) + return -EINVAL; + + if (!ath5k_hw_rfregs_op(rf, hal->ah_offset[6], + ee->ee_x_gain[ee_mode], 4, 96, 0, true)) + return -EINVAL; + + if (!ath5k_hw_rfregs_op(rf, hal->ah_offset[6], obdb >= 0 ? + ee->ee_ob[ee_mode][obdb] : 0, 3, 104, 0, true)) + return -EINVAL; + + if (!ath5k_hw_rfregs_op(rf, hal->ah_offset[6], obdb >= 0 ? + ee->ee_db[ee_mode][obdb] : 0, 3, 107, 0, true)) + return -EINVAL; + + /* Modify bank 7 */ + if (!ath5k_hw_rfregs_op(rf, hal->ah_offset[7], + ee->ee_i_gain[ee_mode], 6, 29, 0, true)) + return -EINVAL; + + if (!ath5k_hw_rfregs_op(rf, hal->ah_offset[7], + ee->ee_xpd[ee_mode], 1, 4, 0, true)) + return -EINVAL; + + /* Write RF values */ + for (i = 0; i < rf_size; i++) { + AR5K_REG_WAIT(i); + ath5k_hw_reg_write(hal, rf[i], rfregs_5111[i].rf_register); + } + + return 0; +} + +/* + * Read EEPROM Calibration data, modify RF Banks and Initialize RF5112 + */ +static int ath5k_hw_rf5112_rfregs(struct ath_hw *hal, + struct ieee80211_channel *channel, unsigned int mode) +{ + const struct ath5k_ini_rf *rf_ini; + struct ath5k_eeprom_info *ee = &hal->ah_capabilities.cap_eeprom; + u32 *rf; + unsigned int rf_size, i; + int obdb = -1, bank = -1; + u32 ee_mode; + + AR5K_ASSERT_ENTRY(mode, AR5K_INI_VAL_MAX); + + rf = hal->ah_rf_banks; + + if (hal->ah_radio_5ghz_revision >= AR5K_SREV_RAD_5112A) { + rf_ini = rfregs_5112a; + rf_size = ARRAY_SIZE(rfregs_5112a); + } else { + rf_ini = rfregs_5112; + rf_size = ARRAY_SIZE(rfregs_5112); + } + + /* Copy values to modify them */ + for (i = 0; i < rf_size; i++) { + if (rf_ini[i].rf_bank >= AR5K_RF5112_INI_RF_MAX_BANKS) { + AR5K_PRINT("invalid bank\n"); + return -EINVAL; + } + + if (bank != rf_ini[i].rf_bank) { + bank = rf_ini[i].rf_bank; + hal->ah_offset[bank] = i; + } + + rf[i] = rf_ini[i].rf_value[mode]; + } + + /* Modify bank 6 */ + if (channel->val & CHANNEL_2GHZ) { + if (channel->val & CHANNEL_B) + ee_mode = AR5K_EEPROM_MODE_11B; + else + ee_mode = AR5K_EEPROM_MODE_11G; + obdb = 0; + + if (!ath5k_hw_rfregs_op(rf, hal->ah_offset[6], + ee->ee_ob[ee_mode][obdb], 3, 287, 0, true)) + return -EINVAL; + + if (!ath5k_hw_rfregs_op(rf, hal->ah_offset[6], + ee->ee_ob[ee_mode][obdb], 3, 290, 0, true)) + return -EINVAL; + } else { + /* For 11a, Turbo and XR */ + ee_mode = AR5K_EEPROM_MODE_11A; + obdb = channel->freq >= 5725 ? 3 : + (channel->freq >= 5500 ? 2 : + (channel->freq >= 5260 ? 1 : + (channel->freq > 4000 ? 0 : -1))); + + if (!ath5k_hw_rfregs_op(rf, hal->ah_offset[6], + ee->ee_ob[ee_mode][obdb], 3, 279, 0, true)) + return -EINVAL; + + if (!ath5k_hw_rfregs_op(rf, hal->ah_offset[6], + ee->ee_ob[ee_mode][obdb], 3, 282, 0, true)) + return -EINVAL; + } + +#ifdef notyet + ath5k_hw_rfregs_op(rf, hal->ah_offset[6], + ee->ee_x_gain[ee_mode], 2, 270, 0, true); + ath5k_hw_rfregs_op(rf, hal->ah_offset[6], + ee->ee_x_gain[ee_mode], 2, 257, 0, true); +#endif + + if (!ath5k_hw_rfregs_op(rf, hal->ah_offset[6], + ee->ee_xpd[ee_mode], 1, 302, 0, true)) + return -EINVAL; + + /* Modify bank 7 */ + if (!ath5k_hw_rfregs_op(rf, hal->ah_offset[7], + ee->ee_i_gain[ee_mode], 6, 14, 0, true)) + return -EINVAL; + + /* Write RF values */ + for (i = 0; i < rf_size; i++) + ath5k_hw_reg_write(hal, rf[i], rf_ini[i].rf_register); + + return 0; +} + +/* + * Initialize RF + */ +int ath5k_hw_rfregs(struct ath_hw *hal, struct ieee80211_channel *channel, + unsigned int mode) +{ + int (*func)(struct ath_hw *, struct ieee80211_channel *, unsigned int); + int ret; + + switch (hal->ah_radio) { + case AR5K_RF5111: + hal->ah_rf_banks_size = sizeof(rfregs_5111); + func = ath5k_hw_rf5111_rfregs; + break; + case AR5K_RF5112: + if (hal->ah_radio_5ghz_revision >= AR5K_SREV_RAD_5112A) + hal->ah_rf_banks_size = sizeof(rfregs_5112a); + else + hal->ah_rf_banks_size = sizeof(rfregs_5112); + func = ath5k_hw_rf5112_rfregs; + break; + default: + return -EINVAL; + } + + if (hal->ah_rf_banks == NULL) { + /* XXX do extra checks? */ + hal->ah_rf_banks = kmalloc(hal->ah_rf_banks_size, GFP_KERNEL); + if (hal->ah_rf_banks == NULL) { + AR5K_PRINT("out of memory\n"); + return -ENOMEM; + } + } + + ret = func(hal, channel, mode); + if (!ret) + hal->ah_rf_gain = AR5K_RFGAIN_INACTIVE; + + return ret; +} + +int ath5k_hw_rfgain(struct ath_hw *hal, unsigned int freq) +{ + const struct ath5k_ini_rfgain *ath5k_rfg; + unsigned int i, size; + + switch (hal->ah_radio) { + case AR5K_RF5111: + ath5k_rfg = rfgain_5111; + size = ARRAY_SIZE(rfgain_5111); + break; + case AR5K_RF5112: + ath5k_rfg = rfgain_5112; + size = ARRAY_SIZE(rfgain_5112); + break; + default: + return -EINVAL; + } + + switch (freq) { + case AR5K_INI_RFGAIN_2GHZ: + case AR5K_INI_RFGAIN_5GHZ: + break; + default: + return -EINVAL; + } + + for (i = 0; i < size; i++) { + AR5K_REG_WAIT(i); + ath5k_hw_reg_write(hal, ath5k_rfg[i].rfg_value[freq], + (u32)ath5k_rfg[i].rfg_register); + } + + return 0; +} + +enum ath5k_rfgain ath5k_hw_get_rf_gain(struct ath_hw *hal) +{ + u32 data, type; + + AR5K_TRACE; + + if (hal->ah_rf_banks == NULL || !hal->ah_gain.g_active || + hal->ah_version <= AR5K_AR5211) + return AR5K_RFGAIN_INACTIVE; + + if (hal->ah_rf_gain != AR5K_RFGAIN_READ_REQUESTED) + goto done; + + data = ath5k_hw_reg_read(hal, AR5K_PHY_PAPD_PROBE); + + if (!(data & AR5K_PHY_PAPD_PROBE_TX_NEXT)) { + hal->ah_gain.g_current = data >> AR5K_PHY_PAPD_PROBE_GAINF_S; + type = AR5K_REG_MS(data, AR5K_PHY_PAPD_PROBE_TYPE); + + if (type == AR5K_PHY_PAPD_PROBE_TYPE_CCK) + hal->ah_gain.g_current += AR5K_GAIN_CCK_PROBE_CORR; + + if (hal->ah_radio == AR5K_RF5112) { + ath5k_hw_rfregs_gainf_corr(hal); + hal->ah_gain.g_current = + hal->ah_gain.g_current>=hal->ah_gain.g_f_corr ? + (hal->ah_gain.g_current-hal->ah_gain.g_f_corr) : + 0; + } + + if (ath5k_hw_rfregs_gain_readback(hal) && + AR5K_GAIN_CHECK_ADJUST(&hal->ah_gain) && + ath5k_hw_rfregs_gain_adjust(hal)) + hal->ah_rf_gain = AR5K_RFGAIN_NEED_CHANGE; + } + +done: + return hal->ah_rf_gain; +} + +int ath5k_hw_set_rfgain_opt(struct ath_hw *hal) +{ + /* Initialize the gain optimization values */ + switch (hal->ah_radio) { + case AR5K_RF5111: + hal->ah_gain.g_step_idx = rfgain_opt_5111.go_default; + hal->ah_gain.g_step = + &rfgain_opt_5111.go_step[hal->ah_gain.g_step_idx]; + hal->ah_gain.g_low = 20; + hal->ah_gain.g_high = 35; + hal->ah_gain.g_active = 1; + break; + case AR5K_RF5112: + hal->ah_gain.g_step_idx = rfgain_opt_5112.go_default; + hal->ah_gain.g_step = + &rfgain_opt_5112.go_step[hal->ah_gain.g_step_idx]; + hal->ah_gain.g_low = 20; + hal->ah_gain.g_high = 85; + hal->ah_gain.g_active = 1; + break; + default: + return -EINVAL; + } + + return 0; +} + +/**************************\ + PHY/RF channel functions +\**************************/ + +/* + * Check if a channel is supported + */ +bool ath5k_channel_ok(struct ath_hw *hal, u16 freq, unsigned int flags) +{ + /* Check if the channel is in our supported range */ + if (flags & CHANNEL_2GHZ) { + if ((freq >= hal->ah_capabilities.cap_range.range_2ghz_min) && + (freq <= hal->ah_capabilities.cap_range.range_2ghz_max)) + return true; + } else if (flags & CHANNEL_5GHZ) + if ((freq >= hal->ah_capabilities.cap_range.range_5ghz_min) && + (freq <= hal->ah_capabilities.cap_range.range_5ghz_max)) + return true; + + return false; +} + +/* + * Convertion needed for RF5110 + */ +static u32 ath5k_hw_rf5110_chan2athchan(struct ieee80211_channel *channel) +{ + u32 athchan; + + /* + * Convert IEEE channel/MHz to an internal channel value used + * by the AR5210 chipset. This has not been verified with + * newer chipsets like the AR5212A who have a completely + * different RF/PHY part. + */ + athchan = (ath5k_hw_bitswap((channel->chan - 24) / 2, 5) << 1) | + (1 << 6) | 0x1; + + return athchan; +} + +/* + * Set channel on RF5110 + */ +static int ath5k_hw_rf5110_channel(struct ath_hw *hal, + struct ieee80211_channel *channel) +{ + u32 data; + + /* + * Set the channel and wait + */ + data = ath5k_hw_rf5110_chan2athchan(channel); + ath5k_hw_reg_write(hal, data, AR5K_RF_BUFFER); + ath5k_hw_reg_write(hal, 0, AR5K_RF_BUFFER_CONTROL_0); + mdelay(1); + + return 0; +} + +/* + * Convertion needed for 5111 + */ +static int ath5k_hw_rf5111_chan2athchan(unsigned int ieee, + struct ath5k_athchan_2ghz *athchan) +{ + int channel; + + /* Cast this value to catch negative channel numbers (>= -19) */ + channel = (int)ieee; + + /* + * Map 2GHz IEEE channel to 5GHz Atheros channel + */ + if (channel <= 13) { + athchan->a2_athchan = 115 + channel; + athchan->a2_flags = 0x46; + } else if (channel == 14) { + athchan->a2_athchan = 124; + athchan->a2_flags = 0x44; + } else if (channel >= 15 && channel <= 26) { + athchan->a2_athchan = ((channel - 14) * 4) + 132; + athchan->a2_flags = 0x46; + } else + return -EINVAL; + + return 0; +} + +/* + * Set channel on 5111 + */ +static int ath5k_hw_rf5111_channel(struct ath_hw *hal, + struct ieee80211_channel *channel) +{ + struct ath5k_athchan_2ghz ath_channel_2ghz; + unsigned int ath_channel = channel->chan; + u32 data0, data1, clock; + int ret; + + /* + * Set the channel on the RF5111 radio + */ + data0 = data1 = 0; + + if (channel->val & CHANNEL_2GHZ) { + /* Map 2GHz channel to 5GHz Atheros channel ID */ + ret = ath5k_hw_rf5111_chan2athchan(channel->chan, + &ath_channel_2ghz); + if (ret) + return ret; + + ath_channel = ath_channel_2ghz.a2_athchan; + data0 = ((ath5k_hw_bitswap(ath_channel_2ghz.a2_flags, 8) & 0xff) + << 5) | (1 << 4); + } + + if (ath_channel < 145 || !(ath_channel & 1)) { + clock = 1; + data1 = ((ath5k_hw_bitswap(ath_channel - 24, 8) & 0xff) << 2) | + (clock << 1) | (1 << 10) | 1; + } else { + clock = 0; + data1 = ((ath5k_hw_bitswap((ath_channel - 24) / 2, 8) & 0xff) + << 2) | (clock << 1) | (1 << 10) | 1; + } + + ath5k_hw_reg_write(hal, (data1 & 0xff) | ((data0 & 0xff) << 8), + AR5K_RF_BUFFER); + ath5k_hw_reg_write(hal, ((data1 >> 8) & 0xff) | (data0 & 0xff00), + AR5K_RF_BUFFER_CONTROL_3); + + return 0; +} + +/* + * Set channel on 5112 + */ +static int ath5k_hw_rf5112_channel(struct ath_hw *hal, + struct ieee80211_channel *channel) +{ + u32 data, data0, data1, data2; + u16 c; + + data = data0 = data1 = data2 = 0; + c = channel->freq; + + /* + * Set the channel on the RF5112 or newer + */ + if (c < 4800) { + if (!((c - 2224) % 5)) { + data0 = ((2 * (c - 704)) - 3040) / 10; + data1 = 1; + } else if (!((c - 2192) % 5)) { + data0 = ((2 * (c - 672)) - 3040) / 10; + data1 = 0; + } else + return -EINVAL; + + data0 = ath5k_hw_bitswap((data0 << 2) & 0xff, 8); + } else { + if (!(c % 20) && c >= 5120) { + data0 = ath5k_hw_bitswap(((c - 4800) / 20 << 2), 8); + data2 = ath5k_hw_bitswap(3, 2); + } else if (!(c % 10)) { + data0 = ath5k_hw_bitswap(((c - 4800) / 10 << 1), 8); + data2 = ath5k_hw_bitswap(2, 2); + } else if (!(c % 5)) { + data0 = ath5k_hw_bitswap((c - 4800) / 5, 8); + data2 = ath5k_hw_bitswap(1, 2); + } else + return -EINVAL; + } + + data = (data0 << 4) | (data1 << 1) | (data2 << 2) | 0x1001; + + ath5k_hw_reg_write(hal, data & 0xff, AR5K_RF_BUFFER); + ath5k_hw_reg_write(hal, (data >> 8) & 0x7f, AR5K_RF_BUFFER_CONTROL_5); + + return 0; +} + +/* + * Set a channel on the radio chip + */ +int ath5k_hw_channel(struct ath_hw *hal, struct ieee80211_channel *channel) +{ + int ret; + + /* + * Check bounds supported by the PHY + * (don't care about regulation restrictions at this point) + */ + if ((channel->freq < hal->ah_capabilities.cap_range.range_2ghz_min || + channel->freq > hal->ah_capabilities.cap_range.range_2ghz_max) && + (channel->freq < hal->ah_capabilities.cap_range.range_5ghz_min || + channel->freq > hal->ah_capabilities.cap_range.range_5ghz_max)) { + AR5K_PRINTF("channel out of supported range (%u MHz)\n", + channel->freq); + return -EINVAL; + } + + /* + * Set the channel and wait + */ + switch (hal->ah_radio) { + case AR5K_RF5110: + ret = ath5k_hw_rf5110_channel(hal, channel); + break; + case AR5K_RF5111: + ret = ath5k_hw_rf5111_channel(hal, channel); + break; + default: + ret = ath5k_hw_rf5112_channel(hal, channel); + break; + } + + if (ret) + return ret; + + hal->ah_current_channel.freq = channel->freq; + hal->ah_current_channel.val = channel->val; + hal->ah_turbo = channel->val == CHANNEL_T ? true : false; + + return 0; +} + +/*****************\ + PHY calibration +\*****************/ + +/* + * Perform a PHY calibration on RF5110 + * -Fix BPSK/QAM Constellation (I/Q correction) + * -Calculate Noise Floor + */ +static int ath5k_hw_rf5110_calibrate(struct ath_hw *hal, + struct ieee80211_channel *channel) +{ + u32 phy_sig, phy_agc, phy_sat, beacon, noise_floor; + unsigned int i; + int ret; + + /* + * Disable beacons and RX/TX queues, wait + */ + AR5K_REG_ENABLE_BITS(hal, AR5K_DIAG_SW_5210, + AR5K_DIAG_SW_DIS_TX | AR5K_DIAG_SW_DIS_RX_5210); + beacon = ath5k_hw_reg_read(hal, AR5K_BEACON_5210); + ath5k_hw_reg_write(hal, beacon & ~AR5K_BEACON_ENABLE, AR5K_BEACON_5210); + + udelay(2300); + + /* + * Set the channel (with AGC turned off) + */ + AR5K_REG_ENABLE_BITS(hal, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE); + udelay(10); + ret = ath5k_hw_channel(hal, channel); + + /* + * Activate PHY and wait + */ + ath5k_hw_reg_write(hal, AR5K_PHY_ACT_ENABLE, AR5K_PHY_ACT); + mdelay(1); + + AR5K_REG_DISABLE_BITS(hal, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE); + + if (ret) + return ret; + + /* + * Calibrate the radio chip + */ + + /* Remember normal state */ + phy_sig = ath5k_hw_reg_read(hal, AR5K_PHY_SIG); + phy_agc = ath5k_hw_reg_read(hal, AR5K_PHY_AGCCOARSE); + phy_sat = ath5k_hw_reg_read(hal, AR5K_PHY_ADCSAT); + + /* Update radio registers */ + ath5k_hw_reg_write(hal, (phy_sig & ~(AR5K_PHY_SIG_FIRPWR)) | + AR5K_REG_SM(-1, AR5K_PHY_SIG_FIRPWR), AR5K_PHY_SIG); + + ath5k_hw_reg_write(hal, (phy_agc & ~(AR5K_PHY_AGCCOARSE_HI | + AR5K_PHY_AGCCOARSE_LO)) | + AR5K_REG_SM(-1, AR5K_PHY_AGCCOARSE_HI) | + AR5K_REG_SM(-127, AR5K_PHY_AGCCOARSE_LO), AR5K_PHY_AGCCOARSE); + + ath5k_hw_reg_write(hal, (phy_sat & ~(AR5K_PHY_ADCSAT_ICNT | + AR5K_PHY_ADCSAT_THR)) | + AR5K_REG_SM(2, AR5K_PHY_ADCSAT_ICNT) | + AR5K_REG_SM(12, AR5K_PHY_ADCSAT_THR), AR5K_PHY_ADCSAT); + + udelay(20); + + AR5K_REG_ENABLE_BITS(hal, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE); + udelay(10); + ath5k_hw_reg_write(hal, AR5K_PHY_RFSTG_DISABLE, AR5K_PHY_RFSTG); + AR5K_REG_DISABLE_BITS(hal, AR5K_PHY_AGC, AR5K_PHY_AGC_DISABLE); + + mdelay(1); + + /* + * Enable calibration and wait until completion + */ + AR5K_REG_ENABLE_BITS(hal, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_CAL); + + ret = ath5k_hw_register_timeout(hal, AR5K_PHY_AGCCTL, + AR5K_PHY_AGCCTL_CAL, 0, false); + + /* Reset to normal state */ + ath5k_hw_reg_write(hal, phy_sig, AR5K_PHY_SIG); + ath5k_hw_reg_write(hal, phy_agc, AR5K_PHY_AGCCOARSE); + ath5k_hw_reg_write(hal, phy_sat, AR5K_PHY_ADCSAT); + + if (ret) { + AR5K_PRINTF("calibration timeout (%uMHz)\n", channel->freq); + return ret; + } + + /* + * Enable noise floor calibration and wait until completion + */ + AR5K_REG_ENABLE_BITS(hal, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_NF); + + ret = ath5k_hw_register_timeout(hal, AR5K_PHY_AGCCTL, + AR5K_PHY_AGCCTL_NF, 0, false); + if (ret) { + AR5K_PRINTF("noise floor calibration timeout (%uMHz)\n", + channel->freq); + return ret; + } + + /* Wait until the noise floor is calibrated */ + for (i = 20; i > 0; i--) { + mdelay(1); + noise_floor = ath5k_hw_reg_read(hal, AR5K_PHY_NF); + + if (AR5K_PHY_NF_RVAL(noise_floor) & AR5K_PHY_NF_ACTIVE) + noise_floor = AR5K_PHY_NF_AVAL(noise_floor); + + if (noise_floor <= AR5K_TUNE_NOISE_FLOOR) + break; + } + + if (noise_floor > AR5K_TUNE_NOISE_FLOOR) { + AR5K_PRINTF("noise floor calibration failed (%uMHz)\n", + channel->freq); + return -EIO; + } + + /* + * Re-enable RX/TX and beacons + */ + AR5K_REG_DISABLE_BITS(hal, AR5K_DIAG_SW_5210, + AR5K_DIAG_SW_DIS_TX | AR5K_DIAG_SW_DIS_RX_5210); + ath5k_hw_reg_write(hal, beacon, AR5K_BEACON_5210); + + return 0; +} + +/* + * Perform a PHY calibration on RF5111/5112 + */ +static int ath5k_hw_rf511x_calibrate(struct ath_hw *hal, + struct ieee80211_channel *channel) +{ + u32 i_pwr, q_pwr; + s32 iq_corr, i_coff, i_coffd, q_coff, q_coffd; + AR5K_TRACE; + + if (hal->ah_calibration == false || + ath5k_hw_reg_read(hal, AR5K_PHY_IQ) & AR5K_PHY_IQ_RUN) + goto done; + + hal->ah_calibration = false; + + iq_corr = ath5k_hw_reg_read(hal, AR5K_PHY_IQRES_CAL_CORR); + i_pwr = ath5k_hw_reg_read(hal, AR5K_PHY_IQRES_CAL_PWR_I); + q_pwr = ath5k_hw_reg_read(hal, AR5K_PHY_IQRES_CAL_PWR_Q); + i_coffd = ((i_pwr >> 1) + (q_pwr >> 1)) >> 7; + q_coffd = q_pwr >> 6; + + if (i_coffd == 0 || q_coffd == 0) + goto done; + + i_coff = ((-iq_corr) / i_coffd) & 0x3f; + q_coff = (((s32)i_pwr / q_coffd) - 64) & 0x1f; + + /* Commit new IQ value */ + AR5K_REG_ENABLE_BITS(hal, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_ENABLE | + ((u32)q_coff) | ((u32)i_coff << AR5K_PHY_IQ_CORR_Q_I_COFF_S)); + +done: + /* Start noise floor calibration */ + AR5K_REG_ENABLE_BITS(hal, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_NF); + + /* Request RF gain */ + if (channel->val & CHANNEL_5GHZ) { + ath5k_hw_reg_write(hal, AR5K_REG_SM(hal->ah_txpower.txp_max, + AR5K_PHY_PAPD_PROBE_TXPOWER) | + AR5K_PHY_PAPD_PROBE_TX_NEXT, AR5K_PHY_PAPD_PROBE); + hal->ah_rf_gain = AR5K_RFGAIN_READ_REQUESTED; + } + + return 0; +} + +/* + * Perform a PHY calibration + */ +int ath5k_hw_phy_calibrate(struct ath_hw *hal, + struct ieee80211_channel *channel) +{ + int ret; + + if (hal->ah_radio == AR5K_RF5110) + ret = ath5k_hw_rf5110_calibrate(hal, channel); + else + ret = ath5k_hw_rf511x_calibrate(hal, channel); + + return ret; +} + +int ath5k_hw_phy_disable(struct ath_hw *hal) +{ + AR5K_TRACE; + /*Just a try M.F.*/ + ath5k_hw_reg_write(hal, AR5K_PHY_ACT_DISABLE, AR5K_PHY_ACT); + + return 0; +} + +/********************\ + Misc PHY functions +\********************/ + +/* + * Get the PHY Chip revision + */ +u16 ath5k_hw_radio_revision(struct ath_hw *hal, unsigned int chan) +{ + unsigned int i; + u32 srev; + u16 ret; + + AR5K_TRACE; + + /* + * Set the radio chip access register + */ + switch (chan) { + case CHANNEL_2GHZ: + ath5k_hw_reg_write(hal, AR5K_PHY_SHIFT_2GHZ, AR5K_PHY(0)); + break; + case CHANNEL_5GHZ: + ath5k_hw_reg_write(hal, AR5K_PHY_SHIFT_5GHZ, AR5K_PHY(0)); + break; + default: + return 0; + } + + mdelay(2); + + /* ...wait until PHY is ready and read the selected radio revision */ + ath5k_hw_reg_write(hal, 0x00001c16, AR5K_PHY(0x34)); + + for (i = 0; i < 8; i++) + ath5k_hw_reg_write(hal, 0x00010000, AR5K_PHY(0x20)); + + if (hal->ah_version == AR5K_AR5210) { + srev = ath5k_hw_reg_read(hal, AR5K_PHY(256) >> 28) & 0xf; + ret = (u16)ath5k_hw_bitswap(srev, 4) + 1; + } else { + srev = (ath5k_hw_reg_read(hal, AR5K_PHY(0x100)) >> 24) & 0xff; + ret = (u16)ath5k_hw_bitswap(((srev & 0xf0) >> 4) | + ((srev & 0x0f) << 4), 8); + } + + /* Reset to the 5GHz mode */ + ath5k_hw_reg_write(hal, AR5K_PHY_SHIFT_5GHZ, AR5K_PHY(0)); + + return ret; +} + +void /*TODO:Boundary check*/ +ath5k_hw_set_def_antenna(struct ath_hw *hal, unsigned int ant) +{ + AR5K_TRACE; + /*Just a try M.F.*/ + if (hal->ah_version != AR5K_AR5210) + ath5k_hw_reg_write(hal, ant, AR5K_DEFAULT_ANTENNA); +} + +unsigned int ath5k_hw_get_def_antenna(struct ath_hw *hal) +{ + AR5K_TRACE; + /*Just a try M.F.*/ + if (hal->ah_version != AR5K_AR5210) + return ath5k_hw_reg_read(hal, AR5K_DEFAULT_ANTENNA); + + return false; /*XXX: What do we return for 5210 ?*/ +} + +/* + * TX power setup + */ + +/* + * Initialize the tx power table (not fully implemented) + */ +static void ath5k_txpower_table(struct ath_hw *hal, + struct ieee80211_channel *channel, s16 max_power) +{ + unsigned int i, min, max, n; + u16 txpower, *rates; + + rates = hal->ah_txpower.txp_rates; + + txpower = AR5K_TUNE_DEFAULT_TXPOWER * 2; + if (max_power > txpower) + txpower = max_power > AR5K_TUNE_MAX_TXPOWER ? + AR5K_TUNE_MAX_TXPOWER : max_power; + + for (i = 0; i < AR5K_MAX_RATES; i++) + rates[i] = txpower; + + /* XXX setup target powers by rate */ + + hal->ah_txpower.txp_min = rates[7]; + hal->ah_txpower.txp_max = rates[0]; + hal->ah_txpower.txp_ofdm = rates[0]; + + /* Calculate the power table */ + n = ARRAY_SIZE(hal->ah_txpower.txp_pcdac); + min = AR5K_EEPROM_PCDAC_START; + max = AR5K_EEPROM_PCDAC_STOP; + for (i = 0; i < n; i += AR5K_EEPROM_PCDAC_STEP) + hal->ah_txpower.txp_pcdac[i] = +#ifdef notyet + min + ((i * (max - min)) / n); +#else + min; +#endif +} + +/* + * Set transmition power + */ +int /*O.K. - txpower_table is unimplemented so this doesn't work*/ +ath5k_hw_txpower(struct ath_hw *hal, struct ieee80211_channel *channel, + unsigned int txpower) +{ + bool tpc = hal->ah_txpower.txp_tpc; + unsigned int i; + + AR5K_TRACE; + if (txpower > AR5K_TUNE_MAX_TXPOWER) { + AR5K_PRINTF("invalid tx power: %u\n", txpower); + return -EINVAL; + } + + /* Reset TX power values */ + memset(&hal->ah_txpower, 0, sizeof(hal->ah_txpower)); + hal->ah_txpower.txp_tpc = tpc; + + /* Initialize TX power table */ + ath5k_txpower_table(hal, channel, txpower); + + /* + * Write TX power values + */ + for (i = 0; i < (AR5K_EEPROM_POWER_TABLE_SIZE / 2); i++) { + ath5k_hw_reg_write(hal, + ((((hal->ah_txpower.txp_pcdac[(i << 1) + 1] << 8) | 0xff) & 0xffff) << 16) | + (((hal->ah_txpower.txp_pcdac[(i << 1) ] << 8) | 0xff) & 0xffff), + AR5K_PHY_PCDAC_TXPOWER(i)); + } + + ath5k_hw_reg_write(hal, AR5K_TXPOWER_OFDM(3, 24) | + AR5K_TXPOWER_OFDM(2, 16) | AR5K_TXPOWER_OFDM(1, 8) | + AR5K_TXPOWER_OFDM(0, 0), AR5K_PHY_TXPOWER_RATE1); + + ath5k_hw_reg_write(hal, AR5K_TXPOWER_OFDM(7, 24) | + AR5K_TXPOWER_OFDM(6, 16) | AR5K_TXPOWER_OFDM(5, 8) | + AR5K_TXPOWER_OFDM(4, 0), AR5K_PHY_TXPOWER_RATE2); + + ath5k_hw_reg_write(hal, AR5K_TXPOWER_CCK(10, 24) | + AR5K_TXPOWER_CCK(9, 16) | AR5K_TXPOWER_CCK(15, 8) | + AR5K_TXPOWER_CCK(8, 0), AR5K_PHY_TXPOWER_RATE3); + + ath5k_hw_reg_write(hal, AR5K_TXPOWER_CCK(14, 24) | + AR5K_TXPOWER_CCK(13, 16) | AR5K_TXPOWER_CCK(12, 8) | + AR5K_TXPOWER_CCK(11, 0), AR5K_PHY_TXPOWER_RATE4); + + if (hal->ah_txpower.txp_tpc == true) + ath5k_hw_reg_write(hal, AR5K_PHY_TXPOWER_RATE_MAX_TPC_ENABLE | + AR5K_TUNE_MAX_TXPOWER, AR5K_PHY_TXPOWER_RATE_MAX); + else + ath5k_hw_reg_write(hal, AR5K_PHY_TXPOWER_RATE_MAX | + AR5K_TUNE_MAX_TXPOWER, AR5K_PHY_TXPOWER_RATE_MAX); + + return 0; +} + +int ath5k_hw_set_txpower_limit(struct ath_hw *hal, unsigned int power) +{ + /*Just a try M.F.*/ + struct ieee80211_channel *channel = &hal->ah_current_channel; + + AR5K_TRACE; +#ifdef AR5K_DEBUG + AR5K_PRINTF("changing txpower to %d\n", power); +#endif + return ath5k_hw_txpower(hal, channel, power); +} diff -puN /dev/null drivers/net/wireless/ath5k/reg.h --- /dev/null +++ a/drivers/net/wireless/ath5k/reg.h @@ -0,0 +1,1978 @@ +/* + * Copyright (c) 2007 Nick Kossifidis + * Copyright (c) 2004, 2005, 2006, 2007 Reyk Floeter + * Copyright (c) 2007 Michael Taylor + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +/* + * Register values for Atheros 5210/5211/5212 cards from OpenBSD's ar5k + * maintained by Reyk Floeter + * + * I tried to document those registers by looking at ar5k code, some + * 802.11 (802.11e mostly) papers and by reading various public available + * Atheros presentations and papers like these: + * + * 5210 - http://nova.stanford.edu/~bbaas/ps/isscc2002_slides.pdf + * http://www.it.iitb.ac.in/~janak/wifire/01222734.pdf + * + * 5211 - http://www.hotchips.org/archives/hc14/3_Tue/16_mcfarland.pdf + */ + + + +/*====MAC DMA REGISTERS====*/ + +/* + * AR5210-Specific TXDP registers + * 5210 has only 2 transmit queues so no DCU/QCU, just + * 2 transmit descriptor pointers... + */ +#define AR5K_NOQCU_TXDP0 0x0000 /* Queue 0 - data */ +#define AR5K_NOQCU_TXDP1 0x0004 /* Queue 1 - beacons */ + +/* + * Mac Control Register + */ +#define AR5K_CR 0x0008 /* Register Address */ +#define AR5K_CR_TXE0 0x00000001 /* TX Enable for queue 0 on 5210 */ +#define AR5K_CR_TXE1 0x00000002 /* TX Enable for queue 1 on 5210 */ +#define AR5K_CR_RXE 0x00000004 /* RX Enable */ +#define AR5K_CR_TXD0 0x00000008 /* TX Disable for queue 0 on 5210 */ +#define AR5K_CR_TXD1 0x00000010 /* TX Disable for queue 1 on 5210 */ +#define AR5K_CR_RXD 0x00000020 /* RX Disable */ +#define AR5K_CR_SWI 0x00000040 + +/* + * RX Descriptor Pointer register + */ +#define AR5K_RXDP 0x000c + +/* + * Configuration and status register + */ +#define AR5K_CFG 0x0014 /* Register Address */ +#define AR5K_CFG_SWTD 0x00000001 /* Byte-swap TX descriptor (for big endian archs) */ +#define AR5K_CFG_SWTB 0x00000002 /* Byte-swap TX buffer (?) */ +#define AR5K_CFG_SWRD 0x00000004 /* Byte-swap RX descriptor */ +#define AR5K_CFG_SWRB 0x00000008 /* Byte-swap RX buffer (?) */ +#define AR5K_CFG_SWRG 0x00000010 /* Byte-swap Register values (?) */ +#define AR5K_CFG_ADHOC 0x00000020 /* [5211+] */ +#define AR5K_CFG_PHY_OK 0x00000100 /* [5211+] */ +#define AR5K_CFG_EEBS 0x00000200 /* EEPROM is busy */ +#define AR5K_CFG_CLKGD 0x00000400 /* Clock gated (?) */ +#define AR5K_CFG_TXCNT 0x00007800 /* Tx frame count (?) [5210] */ +#define AR5K_CFG_TXCNT_S 11 +#define AR5K_CFG_TXFSTAT 0x00008000 /* Tx frame status (?) [5210] */ +#define AR5K_CFG_TXFSTRT 0x00010000 /* [5210] */ +#define AR5K_CFG_PCI_THRES 0x00060000 /* [5211+] */ +#define AR5K_CFG_PCI_THRES_S 17 + +/* + * Interrupt enable register + */ +#define AR5K_IER 0x0024 /* Register Address */ +#define AR5K_IER_DISABLE 0x00000000 /* Disable card interrupts */ +#define AR5K_IER_ENABLE 0x00000001 /* Enable card interrupts */ + + +/* + * 0x0028 is Beacon Control Register on 5210 + * and first RTS duration register on 5211 + */ + +/* + * Beacon control register [5210] + */ +#define AR5K_BCR 0x0028 /* Register Address */ +#define AR5K_BCR_AP 0x00000000 /* AP mode */ +#define AR5K_BCR_ADHOC 0x00000001 /* Ad-Hoc mode */ +#define AR5K_BCR_BDMAE 0x00000002 /* DMA enable */ +#define AR5K_BCR_TQ1FV 0x00000004 /* Use Queue1 for CAB traffic */ +#define AR5K_BCR_TQ1V 0x00000008 /* Use Queue1 for Beacon traffic */ +#define AR5K_BCR_BCGET 0x00000010 + +/* + * First RTS duration register [5211] + */ +#define AR5K_RTSD0 0x0028 /* Register Address */ +#define AR5K_RTSD0_6 0x000000ff /* 6Mb RTS duration mask (?) */ +#define AR5K_RTSD0_6_S 0 /* 6Mb RTS duration shift (?) */ +#define AR5K_RTSD0_9 0x0000ff00 /* 9Mb*/ +#define AR5K_RTSD0_9_S 8 +#define AR5K_RTSD0_12 0x00ff0000 /* 12Mb*/ +#define AR5K_RTSD0_12_S 16 +#define AR5K_RTSD0_18 0xff000000 /* 16Mb*/ +#define AR5K_RTSD0_18_S 24 + + +/* + * 0x002c is Beacon Status Register on 5210 + * and second RTS duration register on 5211 + */ + +/* + * Beacon status register [5210] + * + * As i can see in ar5k_ar5210_tx_start Reyk uses some of the values of BCR + * for this register, so i guess TQ1V,TQ1FV and BDMAE have the same meaning + * here and SNP/SNAP means "snapshot" (so this register gets synced with BCR). + * So SNAPPEDBCRVALID sould also stand for "snapped BCR -values- valid", so i + * renamed it to SNAPSHOTSVALID to make more sense. I realy have no idea what + * else can it be. I also renamed SNPBCMD to SNPADHOC to match BCR. + */ +#define AR5K_BSR 0x002c /* Register Address */ +#define AR5K_BSR_BDLYSW 0x00000001 /* SW Beacon delay (?) */ +#define AR5K_BSR_BDLYDMA 0x00000002 /* DMA Beacon delay (?) */ +#define AR5K_BSR_TXQ1F 0x00000004 /* Beacon queue (1) finished */ +#define AR5K_BSR_ATIMDLY 0x00000008 /* ATIM delay (?) */ +#define AR5K_BSR_SNPADHOC 0x00000100 /* Ad-hoc mode set (?) */ +#define AR5K_BSR_SNPBDMAE 0x00000200 /* Beacon DMA enabled (?) */ +#define AR5K_BSR_SNPTQ1FV 0x00000400 /* Queue1 is used for CAB traffic (?) */ +#define AR5K_BSR_SNPTQ1V 0x00000800 /* Queue1 is used for Beacon traffic (?) */ +#define AR5K_BSR_SNAPSHOTSVALID 0x00001000 /* BCR snapshots are valid (?) */ +#define AR5K_BSR_SWBA_CNT 0x00ff0000 + +/* + * Second RTS duration register [5211] + */ +#define AR5K_RTSD1 0x002c /* Register Address */ +#define AR5K_RTSD1_24 0x000000ff /* 24Mb */ +#define AR5K_RTSD1_24_S 0 +#define AR5K_RTSD1_36 0x0000ff00 /* 36Mb */ +#define AR5K_RTSD1_36_S 8 +#define AR5K_RTSD1_48 0x00ff0000 /* 48Mb */ +#define AR5K_RTSD1_48_S 16 +#define AR5K_RTSD1_54 0xff000000 /* 54Mb */ +#define AR5K_RTSD1_54_S 24 + + +/* + * Transmit configuration register + */ +#define AR5K_TXCFG 0x0030 /* Register Address */ +#define AR5K_TXCFG_SDMAMR 0x00000007 /* DMA size */ +#define AR5K_TXCFG_SDMAMR_S 0 +#define AR5K_TXCFG_B_MODE 0x00000008 /* Set b mode for 5111 (enable 2111) */ +#define AR5K_TXCFG_TXFSTP 0x00000008 /* TX DMA full Stop [5210] */ +#define AR5K_TXCFG_TXFULL 0x000003f0 /* TX Triger level mask */ +#define AR5K_TXCFG_TXFULL_S 4 +#define AR5K_TXCFG_TXFULL_0B 0x00000000 +#define AR5K_TXCFG_TXFULL_64B 0x00000010 +#define AR5K_TXCFG_TXFULL_128B 0x00000020 +#define AR5K_TXCFG_TXFULL_192B 0x00000030 +#define AR5K_TXCFG_TXFULL_256B 0x00000040 +#define AR5K_TXCFG_TXCONT_EN 0x00000080 +#define AR5K_TXCFG_DMASIZE 0x00000100 /* Flag for passing DMA size [5210] */ +#define AR5K_TXCFG_JUMBO_TXE 0x00000400 /* Enable jumbo frames transmition (?) [5211+] */ +#define AR5K_TXCFG_RTSRND 0x00001000 /* [5211+] */ +#define AR5K_TXCFG_FRMPAD_DIS 0x00002000 /* [5211+] */ +#define AR5K_TXCFG_RDY_DIS 0x00004000 /* [5211+] */ + +/* + * Receive configuration register + */ +#define AR5K_RXCFG 0x0034 /* Register Address */ +#define AR5K_RXCFG_SDMAMW 0x00000007 /* DMA size */ +#define AR5K_RXCFG_SDMAMW_S 0 +#define AR5K_RXCFG_DEF_ANTENNA 0x00000008 /* Default antenna */ +#define AR5K_RXCFG_ZLFDMA 0x00000010 /* Zero-length DMA */ +#define AR5K_RXCFG_JUMBO_RXE 0x00000020 /* Enable jumbo frames reception (?) [5211+] */ +#define AR5K_RXCFG_JUMBO_WRAP 0x00000040 /* Wrap jumbo frames (?) [5211+] */ + +/* + * Receive jumbo descriptor last address register + * Only found in 5211 (?) + */ +#define AR5K_RXJLA 0x0038 + +/* + * MIB control register + */ +#define AR5K_MIBC 0x0040 /* Register Address */ +#define AR5K_MIBC_COW 0x00000001 +#define AR5K_MIBC_FMC 0x00000002 /* Freeze Mib Counters (?) */ +#define AR5K_MIBC_CMC 0x00000004 /* Clean Mib Counters (?) */ +#define AR5K_MIBC_MCS 0x00000008 + +/* + * Timeout prescale register + */ +#define AR5K_TOPS 0x0044 +#define AR5K_TOPS_M 0x0000ffff /* [5211+] (?) */ + +/* + * Receive timeout register (no frame received) + */ +#define AR5K_RXNOFRM 0x0048 +#define AR5K_RXNOFRM_M 0x000003ff /* [5211+] (?) */ + +/* + * Transmit timeout register (no frame sent) + */ +#define AR5K_TXNOFRM 0x004c +#define AR5K_TXNOFRM_M 0x000003ff /* [5211+] (?) */ +#define AR5K_TXNOFRM_QCU 0x000ffc00 /* [5211+] (?) */ + +/* + * Receive frame gap timeout register + */ +#define AR5K_RPGTO 0x0050 +#define AR5K_RPGTO_M 0x000003ff /* [5211+] (?) */ + +/* + * Receive frame count limit register + */ +#define AR5K_RFCNT 0x0054 +#define AR5K_RFCNT_M 0x0000001f /* [5211+] (?) */ +#define AR5K_RFCNT_RFCL 0x0000000f /* [5210] */ + +/* + * Misc settings register + */ +#define AR5K_MISC 0x0058 /* Register Address */ +#define AR5K_MISC_DMA_OBS_M 0x000001e0 +#define AR5K_MISC_DMA_OBS_S 5 +#define AR5K_MISC_MISC_OBS_M 0x00000e00 +#define AR5K_MISC_MISC_OBS_S 9 +#define AR5K_MISC_MAC_OBS_LSB_M 0x00007000 +#define AR5K_MISC_MAC_OBS_LSB_S 12 +#define AR5K_MISC_MAC_OBS_MSB_M 0x00038000 +#define AR5K_MISC_MAC_OBS_MSB_S 15 +#define AR5K_MISC_LED_DECAY 0x001c0000 /* [5210] */ +#define AR5K_MISC_LED_BLINK 0x00e00000 /* [5210] */ + +/* + * QCU/DCU clock gating register (5311) + */ +#define AR5K_QCUDCU_CLKGT 0x005c /* Register Address (?) */ +#define AR5K_QCUDCU_CLKGT_QCU 0x0000ffff /* Mask for QCU clock */ +#define AR5K_QCUDCU_CLKGT_DCU 0x07ff0000 /* Mask for DCU clock */ + +/* + * Interrupt Status Registers + * + * For 5210 there is only one status register but for + * 5211/5212 we have one primary and 4 secondary registers. + * So we have AR5K_ISR for 5210 and AR5K_PISR /SISRx for 5211/5212. + * Most of these bits are common for all chipsets. + */ +#define AR5K_ISR 0x001c /* Register Address [5210] */ +#define AR5K_PISR 0x0080 /* Register Address [5211+] */ +#define AR5K_ISR_RXOK 0x00000001 /* Frame successfuly recieved */ +#define AR5K_ISR_RXDESC 0x00000002 /* RX descriptor request */ +#define AR5K_ISR_RXERR 0x00000004 /* Receive error */ +#define AR5K_ISR_RXNOFRM 0x00000008 /* No frame received (receive timeout) */ +#define AR5K_ISR_RXEOL 0x00000010 /* Empty RX descriptor */ +#define AR5K_ISR_RXORN 0x00000020 /* Receive FIFO overrun */ +#define AR5K_ISR_TXOK 0x00000040 /* Frame successfuly transmited */ +#define AR5K_ISR_TXDESC 0x00000080 /* TX descriptor request */ +#define AR5K_ISR_TXERR 0x00000100 /* Transmit error */ +#define AR5K_ISR_TXNOFRM 0x00000200 /* No frame transmited (transmit timeout) */ +#define AR5K_ISR_TXEOL 0x00000400 /* Empty TX descriptor */ +#define AR5K_ISR_TXURN 0x00000800 /* Transmit FIFO underrun */ +#define AR5K_ISR_MIB 0x00001000 /* Update MIB counters */ +#define AR5K_ISR_SWI 0x00002000 /* Software interrupt (?) */ +#define AR5K_ISR_RXPHY 0x00004000 /* PHY error */ +#define AR5K_ISR_RXKCM 0x00008000 +#define AR5K_ISR_SWBA 0x00010000 /* Software beacon alert */ +#define AR5K_ISR_BRSSI 0x00020000 +#define AR5K_ISR_BMISS 0x00040000 /* Beacon missed */ +#define AR5K_ISR_HIUERR 0x00080000 /* Host Interface Unit error [5211+] */ +#define AR5K_ISR_BNR 0x00100000 /* Beacon not ready [5211+] */ +#define AR5K_ISR_MCABT 0x00100000 /* [5210] */ +#define AR5K_ISR_RXCHIRP 0x00200000 /* [5212+] */ +#define AR5K_ISR_SSERR 0x00200000 /* [5210] */ +#define AR5K_ISR_DPERR 0x00400000 /* [5210] */ +#define AR5K_ISR_TIM 0x00800000 /* [5210] */ +#define AR5K_ISR_BCNMISC 0x00800000 /* [5212+] */ +#define AR5K_ISR_GPIO 0x01000000 /* GPIO (rf kill)*/ +#define AR5K_ISR_QCBRORN 0x02000000 /* CBR overrun (?) [5211+] */ +#define AR5K_ISR_QCBRURN 0x04000000 /* CBR underrun (?) [5211+] */ +#define AR5K_ISR_QTRIG 0x08000000 /* [5211+] */ + +/* + * Secondary status registers [5211+] (0 - 4) + * + * I guess from the names that these give the status for each + * queue, that's why only masks are defined here, haven't got + * any info about them (couldn't find them anywhere in ar5k code). + */ +#define AR5K_SISR0 0x0084 /* Register Address [5211+] */ +#define AR5K_SISR0_QCU_TXOK 0x000003ff /* Mask for QCU_TXOK */ +#define AR5K_SISR0_QCU_TXDESC 0x03ff0000 /* Mask for QCU_TXDESC */ + +#define AR5K_SISR1 0x0088 /* Register Address [5211+] */ +#define AR5K_SISR1_QCU_TXERR 0x000003ff /* Mask for QCU_TXERR */ +#define AR5K_SISR1_QCU_TXEOL 0x03ff0000 /* Mask for QCU_TXEOL */ + +#define AR5K_SISR2 0x008c /* Register Address [5211+] */ +#define AR5K_SISR2_QCU_TXURN 0x000003ff /* Mask for QCU_TXURN */ +#define AR5K_SISR2_MCABT 0x00100000 +#define AR5K_SISR2_SSERR 0x00200000 +#define AR5K_SISR2_DPERR 0x00400000 +#define AR5K_SISR2_TIM 0x01000000 /* [5212+] */ +#define AR5K_SISR2_CAB_END 0x02000000 /* [5212+] */ +#define AR5K_SISR2_DTIM_SYNC 0x04000000 /* [5212+] */ +#define AR5K_SISR2_BCN_TIMEOUT 0x08000000 /* [5212+] */ +#define AR5K_SISR2_CAB_TIMEOUT 0x10000000 /* [5212+] */ +#define AR5K_SISR2_DTIM 0x20000000 /* [5212+] */ + +#define AR5K_SISR3 0x0090 /* Register Address [5211+] */ +#define AR5K_SISR3_QCBRORN 0x000003ff /* Mask for QCBRORN */ +#define AR5K_SISR3_QCBRURN 0x03ff0000 /* Mask for QCBRURN */ + +#define AR5K_SISR4 0x0094 /* Register Address [5211+] */ +#define AR5K_SISR4_QTRIG 0x000003ff /* Mask for QTRIG */ + +/* + * Shadow read-and-clear interrupt status registers [5211+] + */ +#define AR5K_RAC_PISR 0x00c0 /* Read and clear PISR */ +#define AR5K_RAC_SISR0 0x00c4 /* Read and clear SISR0 */ +#define AR5K_RAC_SISR1 0x00c8 /* Read and clear SISR1 */ +#define AR5K_RAC_SISR2 0x00cc /* Read and clear SISR2 */ +#define AR5K_RAC_SISR3 0x00d0 /* Read and clear SISR3 */ +#define AR5K_RAC_SISR4 0x00d4 /* Read and clear SISR4 */ + +/* + * Interrupt Mask Registers + * + * As whith ISRs 5210 has one IMR (AR5K_IMR) and 5211/5212 has one primary + * (AR5K_PIMR) and 4 secondary IMRs (AR5K_SIMRx). Note that ISR/IMR flags match. + */ +#define AR5K_IMR 0x0020 /* Register Address [5210] */ +#define AR5K_PIMR 0x00a0 /* Register Address [5211+] */ +#define AR5K_IMR_RXOK 0x00000001 /* Frame successfuly recieved*/ +#define AR5K_IMR_RXDESC 0x00000002 /* RX descriptor request*/ +#define AR5K_IMR_RXERR 0x00000004 /* Receive error*/ +#define AR5K_IMR_RXNOFRM 0x00000008 /* No frame received (receive timeout)*/ +#define AR5K_IMR_RXEOL 0x00000010 /* Empty RX descriptor*/ +#define AR5K_IMR_RXORN 0x00000020 /* Receive FIFO overrun*/ +#define AR5K_IMR_TXOK 0x00000040 /* Frame successfuly transmited*/ +#define AR5K_IMR_TXDESC 0x00000080 /* TX descriptor request*/ +#define AR5K_IMR_TXERR 0x00000100 /* Transmit error*/ +#define AR5K_IMR_TXNOFRM 0x00000200 /* No frame transmited (transmit timeout)*/ +#define AR5K_IMR_TXEOL 0x00000400 /* Empty TX descriptor*/ +#define AR5K_IMR_TXURN 0x00000800 /* Transmit FIFO underrun*/ +#define AR5K_IMR_MIB 0x00001000 /* Update MIB counters*/ +#define AR5K_IMR_SWI 0x00002000 +#define AR5K_IMR_RXPHY 0x00004000 /* PHY error*/ +#define AR5K_IMR_RXKCM 0x00008000 +#define AR5K_IMR_SWBA 0x00010000 /* Software beacon alert*/ +#define AR5K_IMR_BRSSI 0x00020000 +#define AR5K_IMR_BMISS 0x00040000 /* Beacon missed*/ +#define AR5K_IMR_HIUERR 0x00080000 /* Host Interface Unit error [5211+] */ +#define AR5K_IMR_BNR 0x00100000 /* Beacon not ready [5211+] */ +#define AR5K_IMR_MCABT 0x00100000 /* [5210] */ +#define AR5K_IMR_RXCHIRP 0x00200000 /* [5212+]*/ +#define AR5K_IMR_SSERR 0x00200000 /* [5210] */ +#define AR5K_IMR_DPERR 0x00400000 /* [5210] */ +#define AR5K_IMR_TIM 0x00800000 /* [5211+] */ +#define AR5K_IMR_BCNMISC 0x00800000 /* [5212+] */ +#define AR5K_IMR_GPIO 0x01000000 /* GPIO (rf kill)*/ +#define AR5K_IMR_QCBRORN 0x02000000 /* CBR overrun (?) [5211+] */ +#define AR5K_IMR_QCBRURN 0x04000000 /* CBR underrun (?) [5211+] */ +#define AR5K_IMR_QTRIG 0x08000000 /* [5211+] */ + +/* + * Secondary interrupt mask registers [5211+] (0 - 4) + */ +#define AR5K_SIMR0 0x00a4 /* Register Address [5211+] */ +#define AR5K_SIMR0_QCU_TXOK 0x000003ff /* Mask for QCU_TXOK */ +#define AR5K_SIMR0_QCU_TXOK_S 0 +#define AR5K_SIMR0_QCU_TXDESC 0x03ff0000 /* Mask for QCU_TXDESC */ +#define AR5K_SIMR0_QCU_TXDESC_S 16 + +#define AR5K_SIMR1 0x00a8 /* Register Address [5211+] */ +#define AR5K_SIMR1_QCU_TXERR 0x000003ff /* Mask for QCU_TXERR */ +#define AR5K_SIMR1_QCU_TXERR_S 0 +#define AR5K_SIMR1_QCU_TXEOL 0x03ff0000 /* Mask for QCU_TXEOL */ +#define AR5K_SIMR1_QCU_TXEOL_S 16 + +#define AR5K_SIMR2 0x00ac /* Register Address [5211+] */ +#define AR5K_SIMR2_QCU_TXURN 0x000003ff /* Mask for QCU_TXURN */ +#define AR5K_SIMR2_QCU_TXURN_S 0 +#define AR5K_SIMR2_MCABT 0x00100000 +#define AR5K_SIMR2_SSERR 0x00200000 +#define AR5K_SIMR2_DPERR 0x00400000 +#define AR5K_SIMR2_TIM 0x01000000 /* [5212+] */ +#define AR5K_SIMR2_CAB_END 0x02000000 /* [5212+] */ +#define AR5K_SIMR2_DTIM_SYNC 0x04000000 /* [5212+] */ +#define AR5K_SIMR2_BCN_TIMEOUT 0x08000000 /* [5212+] */ +#define AR5K_SIMR2_CAB_TIMEOUT 0x10000000 /* [5212+] */ +#define AR5K_SIMR2_DTIM 0x20000000 /* [5212+] */ + +#define AR5K_SIMR3 0x00b0 /* Register Address [5211+] */ +#define AR5K_SIMR3_QCBRORN 0x000003ff /* Mask for QCBRORN */ +#define AR5K_SIMR3_QCBRORN_S 0 +#define AR5K_SIMR3_QCBRURN 0x03ff0000 /* Mask for QCBRURN */ +#define AR5K_SIMR3_QCBRURN_S 16 + +#define AR5K_SIMR4 0x00b4 /* Register Address [5211+] */ +#define AR5K_SIMR4_QTRIG 0x000003ff /* Mask for QTRIG */ +#define AR5K_SIMR4_QTRIG_S 0 + + +/* + * Decompression mask registers [5212+] + */ +#define AR5K_DCM_ADDR 0x0400 /*Decompression mask address (?)*/ +#define AR5K_DCM_DATA 0x0404 /*Decompression mask data (?)*/ + +/* + * Decompression configuration registers [5212+] + */ +#define AR5K_DCCFG 0x0420 + +/* + * Compression configuration registers [5212+] + */ +#define AR5K_CCFG 0x0600 +#define AR5K_CCFG_CUP 0x0604 + +/* + * Compression performance counter registers [5212+] + */ +#define AR5K_CPC0 0x0610 /* Compression performance counter 0 */ +#define AR5K_CPC1 0x0614 /* Compression performance counter 1*/ +#define AR5K_CPC2 0x0618 /* Compression performance counter 2 */ +#define AR5K_CPC3 0x061c /* Compression performance counter 3 */ +#define AR5K_CPCORN 0x0620 /* Compression performance overrun (?) */ + + +/* + * Queue control unit (QCU) registers [5211+] + * + * Card has 12 TX Queues but i see that only 0-9 are used (?) + * both in binary HAL (see ah.h) and ar5k. Each queue has it's own + * TXDP at addresses 0x0800 - 0x082c, a CBR (Constant Bit Rate) + * configuration register (0x08c0 - 0x08ec), a ready time configuration + * register (0x0900 - 0x092c), a misc configuration register (0x09c0 - + * 0x09ec) and a status register (0x0a00 - 0x0a2c). We also have some + * global registers, QCU transmit enable/disable and "one shot arm (?)" + * set/clear, which contain status for all queues (we shift by 1 for each + * queue). To access these registers easily we define some macros here + * that are used inside HAL. For more infos check out *_tx_queue functs. + * + * TODO: Boundary checking on macros (here?) + */ + +/* + * Generic QCU Register access macros + */ +#define AR5K_QUEUE_REG(_r, _q) (((_q) << 2) + _r) +#define AR5K_QCU_GLOBAL_READ(_r, _q) (AR5K_REG_READ(_r) & (1 << _q)) +#define AR5K_QCU_GLOBAL_WRITE(_r, _q) AR5K_REG_WRITE(_r, (1 << _q)) + +/* + * QCU Transmit descriptor pointer registers + */ +#define AR5K_QCU_TXDP_BASE 0x0800 /* Register Address - Queue0 TXDP */ +#define AR5K_QUEUE_TXDP(_q) AR5K_QUEUE_REG(AR5K_QCU_TXDP_BASE, _q) + +/* + * QCU Transmit enable register + */ +#define AR5K_QCU_TXE 0x0840 +#define AR5K_ENABLE_QUEUE(_q) AR5K_QCU_GLOBAL_WRITE(AR5K_QCU_TXE, _q) +#define AR5K_QUEUE_ENABLED(_q) AR5K_QCU_GLOBAL_READ(AR5K_QCU_TXE, _q) + +/* + * QCU Transmit disable register + */ +#define AR5K_QCU_TXD 0x0880 +#define AR5K_DISABLE_QUEUE(_q) AR5K_QCU_GLOBAL_WRITE(AR5K_QCU_TXD, _q) +#define AR5K_QUEUE_DISABLED(_q) AR5K_QCU_GLOBAL_READ(AR5K_QCU_TXD, _q) + +/* + * QCU Constant Bit Rate configuration registers + */ +#define AR5K_QCU_CBRCFG_BASE 0x08c0 /* Register Address - Queue0 CBRCFG */ +#define AR5K_QCU_CBRCFG_INTVAL 0x00ffffff /* CBR Interval mask */ +#define AR5K_QCU_CBRCFG_INTVAL_S 0 +#define AR5K_QCU_CBRCFG_ORN_THRES 0xff000000 /* CBR overrun threshold mask */ +#define AR5K_QCU_CBRCFG_ORN_THRES_S 24 +#define AR5K_QUEUE_CBRCFG(_q) AR5K_QUEUE_REG(AR5K_QCU_CBRCFG_BASE, _q) + +/* + * QCU Ready time configuration registers + */ +#define AR5K_QCU_RDYTIMECFG_BASE 0x0900 /* Register Address - Queue0 RDYTIMECFG */ +#define AR5K_QCU_RDYTIMECFG_INTVAL 0x00ffffff /* Ready time interval mask */ +#define AR5K_QCU_RDYTIMECFG_INTVAL_S 0 +#define AR5K_QCU_RDYTIMECFG_DURATION 0x00ffffff /* Ready time duration mask */ +#define AR5K_QCU_RDYTIMECFG_ENABLE 0x01000000 /* Ready time enable mask */ +#define AR5K_QUEUE_RDYTIMECFG(_q) AR5K_QUEUE_REG(AR5K_QCU_RDYTIMECFG_BASE, _q) + +/* + * QCU one shot arm set registers + */ +#define AR5K_QCU_ONESHOTARM_SET 0x0940 /* Register Address -QCU "one shot arm set (?)" */ +#define AR5K_QCU_ONESHOTARM_SET_M 0x0000ffff + +/* + * QCU one shot arm clear registers + */ +#define AR5K_QCU_ONESHOTARM_CLEAR 0x0980 /* Register Address -QCU "one shot arm clear (?)" */ +#define AR5K_QCU_ONESHOTARM_CLEAR_M 0x0000ffff + +/* + * QCU misc registers + */ +#define AR5K_QCU_MISC_BASE 0x09c0 /* Register Address -Queue0 MISC */ +#define AR5K_QCU_MISC_FRSHED_M 0x0000000f /* Frame sheduling mask */ +#define AR5K_QCU_MISC_FRSHED_ASAP 0 /* ASAP */ +#define AR5K_QCU_MISC_FRSHED_CBR 1 /* Constant Bit Rate */ +#define AR5K_QCU_MISC_FRSHED_DBA_GT 2 /* DMA Beacon alert gated (?) */ +#define AR5K_QCU_MISC_FRSHED_TIM_GT 3 /* Time gated (?) */ +#define AR5K_QCU_MISC_FRSHED_BCN_SENT_GT 4 /* Beacon sent gated (?) */ +#define AR5K_QCU_MISC_ONESHOT_ENABLE 0x00000010 /* Oneshot enable */ +#define AR5K_QCU_MISC_CBREXP 0x00000020 /* CBR expired (normal queue) */ +#define AR5K_QCU_MISC_CBREXP_BCN 0x00000040 /* CBR expired (beacon queue) */ +#define AR5K_QCU_MISC_BCN_ENABLE 0x00000080 /* Beacons enabled */ +#define AR5K_QCU_MISC_CBR_THRES_ENABLE 0x00000100 /* CBR threshold enabled (?) */ +#define AR5K_QCU_MISC_TXE 0x00000200 /* TXE reset when RDYTIME enalbed (?) */ +#define AR5K_QCU_MISC_CBR 0x00000400 /* CBR threshold reset (?) */ +#define AR5K_QCU_MISC_DCU_EARLY 0x00000800 /* DCU reset (?) */ +#define AR5K_QUEUE_MISC(_q) AR5K_QUEUE_REG(AR5K_QCU_MISC_BASE, _q) + + +/* + * QCU status registers + */ +#define AR5K_QCU_STS_BASE 0x0a00 /* Register Address - Queue0 STS */ +#define AR5K_QCU_STS_FRMPENDCNT 0x00000003 /* Frames pending counter */ +#define AR5K_QCU_STS_CBREXPCNT 0x0000ff00 /* CBR expired counter (?) */ +#define AR5K_QUEUE_STATUS(_q) AR5K_QUEUE_REG(AR5K_QCU_STS_BASE, _q) + +/* + * QCU ready time shutdown register + */ +#define AR5K_QCU_RDYTIMESHDN 0x0a40 +#define AR5K_QCU_RDYTIMESHDN_M 0x000003ff + +/* + * QCU compression buffer base registers [5212+] + */ +#define AR5K_QCU_CBB_SELECT 0x0b00 +#define AR5K_QCU_CBB_ADDR 0x0b04 + +/* + * QCU compression buffer configuration register [5212+] + */ +#define AR5K_QCU_CBCFG 0x0b08 + + + +/* + * Distributed Coordination Function (DCF) control unit (DCU) + * registers [5211+] + * + * These registers control the various characteristics of each queue + * for 802.11e (WME) combatibility so they go together with + * QCU registers in pairs. For each queue we have a QCU mask register, + * (0x1000 - 0x102c), a local-IFS settings register (0x1040 - 0x106c), + * a retry limit register (0x1080 - 0x10ac), a channel time register + * (0x10c0 - 0x10ec), a misc-settings register (0x1100 - 0x112c) and + * a sequence number register (0x1140 - 0x116c). It seems that "global" + * registers here afect all queues (see use of DCU_GBL_IFS_SLOT in ar5k). + * We use the same macros here for easier register access. + * + */ + +/* + * DCU QCU mask registers + */ +#define AR5K_DCU_QCUMASK_BASE 0x1000 /* Register Address -Queue0 DCU_QCUMASK */ +#define AR5K_DCU_QCUMASK_M 0x000003ff +#define AR5K_QUEUE_QCUMASK(_q) AR5K_QUEUE_REG(AR5K_DCU_QCUMASK_BASE, _q) + +/* + * DCU local Inter Frame Space settings register + */ +#define AR5K_DCU_LCL_IFS_BASE 0x1040 /* Register Address -Queue0 DCU_LCL_IFS */ +#define AR5K_DCU_LCL_IFS_CW_MIN 0x000003ff /* Minimum Contention Window */ +#define AR5K_DCU_LCL_IFS_CW_MIN_S 0 +#define AR5K_DCU_LCL_IFS_CW_MAX 0x000ffc00 /* Maximum Contention Window */ +#define AR5K_DCU_LCL_IFS_CW_MAX_S 10 +#define AR5K_DCU_LCL_IFS_AIFS 0x0ff00000 /* Arbitrated Interframe Space */ +#define AR5K_DCU_LCL_IFS_AIFS_S 20 +#define AR5K_QUEUE_DFS_LOCAL_IFS(_q) AR5K_QUEUE_REG(AR5K_DCU_LCL_IFS_BASE, _q) + +/* + * DCU retry limit registers + */ +#define AR5K_DCU_RETRY_LMT_BASE 0x1080 /* Register Address -Queue0 DCU_RETRY_LMT */ +#define AR5K_DCU_RETRY_LMT_SH_RETRY 0x0000000f /* Short retry limit mask */ +#define AR5K_DCU_RETRY_LMT_SH_RETRY_S 0 +#define AR5K_DCU_RETRY_LMT_LG_RETRY 0x000000f0 /* Long retry limit mask */ +#define AR5K_DCU_RETRY_LMT_LG_RETRY_S 4 +#define AR5K_DCU_RETRY_LMT_SSH_RETRY 0x00003f00 /* Station short retry limit mask (?) */ +#define AR5K_DCU_RETRY_LMT_SSH_RETRY_S 8 +#define AR5K_DCU_RETRY_LMT_SLG_RETRY 0x000fc000 /* Station long retry limit mask (?) */ +#define AR5K_DCU_RETRY_LMT_SLG_RETRY_S 14 +#define AR5K_QUEUE_DFS_RETRY_LIMIT(_q) AR5K_QUEUE_REG(AR5K_DCU_RETRY_LMT_BASE, _q) + +/* + * DCU channel time registers + */ +#define AR5K_DCU_CHAN_TIME_BASE 0x10c0 /* Register Address -Queue0 DCU_CHAN_TIME */ +#define AR5K_DCU_CHAN_TIME_DUR 0x000fffff /* Channel time duration */ +#define AR5K_DCU_CHAN_TIME_DUR_S 0 +#define AR5K_DCU_CHAN_TIME_ENABLE 0x00100000 /* Enable channel time */ +#define AR5K_QUEUE_DFS_CHANNEL_TIME(_q) AR5K_QUEUE_REG(AR5K_DCU_CHAN_TIME_BASE, _q) + +/* + * DCU misc registers [5211+] + * + * For some of the registers i couldn't find in the code + * (only backoff stuff is there realy) i tried to match the + * names with 802.11e parameters etc, so i guess VIRTCOL here + * means Virtual Collision and HCFPOLL means Hybrid Coordination + * factor Poll (CF- Poll). Arbiter lockout control controls the + * behaviour on low priority queues when we have multiple queues + * with pending frames. Intra-frame lockout means we wait until + * the queue's current frame transmits (with post frame backoff and bursting) + * before we transmit anything else and global lockout means we + * wait for the whole queue to finish before higher priority queues + * can transmit (this is used on beacon and CAB queues). + * No lockout means there is no special handling. + */ +#define AR5K_DCU_MISC_BASE 0x1100 /* Register Address -Queue0 DCU_MISC */ +#define AR5K_DCU_MISC_BACKOFF 0x000007ff /* Mask for backoff setting (?) */ +#define AR5K_DCU_MISC_BACKOFF_FRAG 0x00000200 /* Enable backoff while bursting */ +#define AR5K_DCU_MISC_HCFPOLL_ENABLE 0x00000800 /* CF - Poll (?) */ +#define AR5K_DCU_MISC_BACKOFF_PERSIST 0x00001000 /* Persistent backoff (?) */ +#define AR5K_DCU_MISC_FRMPRFTCH_ENABLE 0x00002000 /* Enable frame pre-fetch (?) */ +#define AR5K_DCU_MISC_VIRTCOL 0x0000c000 /* Mask for Virtual Collision (?) */ +#define AR5K_DCU_MISC_VIRTCOL_NORMAL 0 +#define AR5K_DCU_MISC_VIRTCOL_MODIFIED 1 +#define AR5K_DCU_MISC_VIRTCOL_IGNORE 2 +#define AR5K_DCU_MISC_BCN_ENABLE 0x00010000 /* Beacon enable (?) */ +#define AR5K_DCU_MISC_ARBLOCK_CTL 0x00060000 /* Arbiter lockout control mask */ +#define AR5K_DCU_MISC_ARBLOCK_CTL_S 17 +#define AR5K_DCU_MISC_ARBLOCK_CTL_NONE 0 /* No arbiter lockout */ +#define AR5K_DCU_MISC_ARBLOCK_CTL_INTFRM 1 /* Intra-frame lockout */ +#define AR5K_DCU_MISC_ARBLOCK_CTL_GLOBAL 2 /* Global lockout */ +#define AR5K_DCU_MISC_ARBLOCK_IGNORE 0x00080000 +#define AR5K_DCU_MISC_SEQ_NUM_INCR_DIS 0x00100000 /* Disable sequence number increment (?) */ +#define AR5K_DCU_MISC_POST_FR_BKOFF_DIS 0x00200000 /* Disable post-frame backoff (?) */ +#define AR5K_DCU_MISC_VIRT_COLL_POLICY 0x00400000 /* Virtual Collision policy (?) */ +#define AR5K_DCU_MISC_BLOWN_IFS_POLICY 0x00800000 +#define AR5K_DCU_MISC_SEQNUM_CTL 0x01000000 /* Sequence number control (?) */ +#define AR5K_QUEUE_DFS_MISC(_q) AR5K_QUEUE_REG(AR5K_DCU_MISC_BASE, _q) + +/* + * DCU frame sequence number registers + */ +#define AR5K_DCU_SEQNUM_BASE 0x1140 +#define AR5K_DCU_SEQNUM_M 0x00000fff +#define AR5K_QUEUE_DFS_SEQNUM(_q) AR5K_QUEUE_REG(AR5K_DCU_SEQNUM_BASE, _q) + +/* + * DCU global IFS SIFS registers + */ +#define AR5K_DCU_GBL_IFS_SIFS 0x1030 +#define AR5K_DCU_GBL_IFS_SIFS_M 0x0000ffff + +/* + * DCU global IFS slot interval registers + */ +#define AR5K_DCU_GBL_IFS_SLOT 0x1070 +#define AR5K_DCU_GBL_IFS_SLOT_M 0x0000ffff + +/* + * DCU global IFS EIFS registers + */ +#define AR5K_DCU_GBL_IFS_EIFS 0x10b0 +#define AR5K_DCU_GBL_IFS_EIFS_M 0x0000ffff + +/* + * DCU global IFS misc registers + */ +#define AR5K_DCU_GBL_IFS_MISC 0x10f0 /* Register Address */ +#define AR5K_DCU_GBL_IFS_MISC_LFSR_SLICE 0x00000007 +#define AR5K_DCU_GBL_IFS_MISC_TURBO_MODE 0x00000008 /* Turbo mode (?) */ +#define AR5K_DCU_GBL_IFS_MISC_SIFS_DUR_USEC 0x000003f0 /* SIFS Duration mask (?) */ +#define AR5K_DCU_GBL_IFS_MISC_USEC_DUR 0x000ffc00 +#define AR5K_DCU_GBL_IFS_MISC_DCU_ARB_DELAY 0x00300000 + +/* + * DCU frame prefetch control register + */ +#define AR5K_DCU_FP 0x1230 + +/* + * DCU transmit pause control/status register + */ +#define AR5K_DCU_TXP 0x1270 /* Register Address */ +#define AR5K_DCU_TXP_M 0x000003ff /* Tx pause mask (?) */ +#define AR5K_DCU_TXP_STATUS 0x00010000 /* Tx pause status (?) */ + +/* + * DCU transmit filter register + */ +#define AR5K_DCU_TX_FILTER 0x1038 + +/* + * DCU clear transmit filter register + */ +#define AR5K_DCU_TX_FILTER_CLR 0x143c + +/* + * DCU set transmit filter register + */ +#define AR5K_DCU_TX_FILTER_SET 0x147c + +/* + * Reset control register + * + * 4 and 8 are not used in 5211/5212 and + * 2 means "baseband reset" on 5211/5212. + */ +#define AR5K_RESET_CTL 0x4000 /* Register Address */ +#define AR5K_RESET_CTL_PCU 0x00000001 /* Protocol Control Unit reset */ +#define AR5K_RESET_CTL_DMA 0x00000002 /* DMA (Rx/Tx) reset [5210] */ +#define AR5K_RESET_CTL_BASEBAND 0x00000002 /* Baseband reset [5211+] */ +#define AR5K_RESET_CTL_MAC 0x00000004 /* MAC reset (PCU+Baseband ?) [5210] */ +#define AR5K_RESET_CTL_PHY 0x00000008 /* PHY reset [5210] */ +#define AR5K_RESET_CTL_PCI 0x00000010 /* PCI Core reset (interrupts etc) */ +#define AR5K_RESET_CTL_CHIP (AR5K_RESET_CTL_PCU | AR5K_RESET_CTL_DMA | \ + AR5K_RESET_CTL_MAC | AR5K_RESET_CTL_PHY) + +/* + * Sleep control register + */ +#define AR5K_SLEEP_CTL 0x4004 /* Register Address */ +#define AR5K_SLEEP_CTL_SLDUR 0x0000ffff /* Sleep duration mask */ +#define AR5K_SLEEP_CTL_SLDUR_S 0 +#define AR5K_SLEEP_CTL_SLE 0x00030000 /* Sleep enable mask */ +#define AR5K_SLEEP_CTL_SLE_S 16 +#define AR5K_SLEEP_CTL_SLE_WAKE 0x00000000 /* Force chip awake */ +#define AR5K_SLEEP_CTL_SLE_SLP 0x00010000 /* Force chip sleep */ +#define AR5K_SLEEP_CTL_SLE_ALLOW 0x00020000 +#define AR5K_SLEEP_CTL_SLE_UNITS 0x00000008 /* [5211+] */ + +/* + * Interrupt pending register + */ +#define AR5K_INTPEND 0x4008 +#define AR5K_INTPEND_M 0x00000001 + +/* + * Sleep force register + */ +#define AR5K_SFR 0x400c +#define AR5K_SFR_M 0x00000001 + +/* + * PCI configuration register + */ +#define AR5K_PCICFG 0x4010 /* Register Address */ +#define AR5K_PCICFG_EEAE 0x00000001 /* Eeprom access enable [5210] */ +#define AR5K_PCICFG_CLKRUNEN 0x00000004 /* CLKRUN enable [5211+] */ +#define AR5K_PCICFG_EESIZE 0x00000018 /* Mask for EEPROM size [5211+] */ +#define AR5K_PCICFG_EESIZE_S 3 +#define AR5K_PCICFG_EESIZE_4K 0 /* 4K */ +#define AR5K_PCICFG_EESIZE_8K 1 /* 8K */ +#define AR5K_PCICFG_EESIZE_16K 2 /* 16K */ +#define AR5K_PCICFG_EESIZE_FAIL 3 /* Failed to get size (?) [5211+] */ +#define AR5K_PCICFG_LED 0x00000060 /* Led status [5211+] */ +#define AR5K_PCICFG_LED_NONE 0x00000000 /* Default [5211+] */ +#define AR5K_PCICFG_LED_PEND 0x00000020 /* Scan / Auth pending */ +#define AR5K_PCICFG_LED_ASSOC 0x00000040 /* Associated */ +#define AR5K_PCICFG_BUS_SEL 0x00000380 /* Mask for "bus select" [5211+] (?) */ +#define AR5K_PCICFG_CBEFIX_DIS 0x00000400 /* Disable CBE fix (?) */ +#define AR5K_PCICFG_SL_INTEN 0x00000800 /* Enable interrupts when asleep (?) */ +#define AR5K_PCICFG_LED_BCTL 0x00001000 /* Led blink (?) [5210] */ +#define AR5K_PCICFG_SL_INPEN 0x00002800 /* Sleep even whith pending interrupts (?) */ +#define AR5K_PCICFG_SPWR_DN 0x00010000 /* Mask for power status */ +#define AR5K_PCICFG_LEDMODE 0x000e0000 /* Ledmode [5211+] */ +#define AR5K_PCICFG_LEDMODE_PROP 0x00000000 /* Blink on standard traffic [5211+] */ +#define AR5K_PCICFG_LEDMODE_PROM 0x00020000 /* Default mode (blink on any traffic) [5211+] */ +#define AR5K_PCICFG_LEDMODE_PWR 0x00040000 /* Some other blinking mode (?) [5211+] */ +#define AR5K_PCICFG_LEDMODE_RAND 0x00060000 /* Random blinking (?) [5211+] */ +#define AR5K_PCICFG_LEDBLINK 0x00700000 +#define AR5K_PCICFG_LEDBLINK_S 20 +#define AR5K_PCICFG_LEDSLOW 0x00800000 /* Slow led blink rate (?) [5211+] */ +#define AR5K_PCICFG_LEDSTATE \ + (AR5K_PCICFG_LED | AR5K_PCICFG_LEDMODE | \ + AR5K_PCICFG_LEDBLINK | AR5K_PCICFG_LEDSLOW) + +/* + * "General Purpose Input/Output" (GPIO) control register + * + * I'm not sure about this but after looking at the code + * for all chipsets here is what i got. + * + * We have 6 GPIOs (pins), each GPIO has 4 modes (2 bits) + * Mode 0 -> always input + * Mode 1 -> output when GPIODO for this GPIO is set to 0 + * Mode 2 -> output when GPIODO for this GPIO is set to 1 + * Mode 3 -> always output + * + * For more infos check out get_gpio/set_gpio and + * set_gpio_input/set_gpio_output functs. + * For more infos on gpio interrupt check out set_gpio_intr. + */ +#define AR5K_NUM_GPIO 6 + +#define AR5K_GPIOCR 0x4014 /* Register Address */ +#define AR5K_GPIOCR_INT_ENA 0x00008000 /* Enable GPIO interrupt */ +#define AR5K_GPIOCR_INT_SELL 0x00000000 /* Generate interrupt when pin is off (?) */ +#define AR5K_GPIOCR_INT_SELH 0x00010000 /* Generate interrupt when pin is on */ +#define AR5K_GPIOCR_IN(n) (0 << ((n) * 2)) /* Mode 0 for pin n */ +#define AR5K_GPIOCR_OUT0(n) (1 << ((n) * 2)) /* Mode 1 for pin n */ +#define AR5K_GPIOCR_OUT1(n) (2 << ((n) * 2)) /* Mode 2 for pin n */ +#define AR5K_GPIOCR_OUT(n) (3 << ((n) * 2)) /* Mode 3 for pin n */ +#define AR5K_GPIOCR_INT_SEL(n) ((n) << 12) /* Interrupt for GPIO pin n */ + +/* + * "General Purpose Input/Output" (GPIO) data output register + */ +#define AR5K_GPIODO 0x4018 + +/* + * "General Purpose Input/Output" (GPIO) data input register + */ +#define AR5K_GPIODI 0x401c +#define AR5K_GPIODI_M 0x0000002f + + +/* + * Silicon revision register + */ +#define AR5K_SREV 0x4020 /* Register Address */ +#define AR5K_SREV_REV 0x0000000f /* Mask for revision */ +#define AR5K_SREV_REV_S 0 +#define AR5K_SREV_VER 0x000000ff /* Mask for version */ +#define AR5K_SREV_VER_S 4 + + + +/*====EEPROM REGISTERS====*/ + +/* + * EEPROM access registers + * + * Here we got a difference between 5210/5211-12 + * read data register for 5210 is at 0x6800 and + * status register is at 0x6c00. There is also + * no eeprom command register on 5210 and the + * offsets are different. + * + * To read eeprom data for a specific offset: + * 5210 - enable eeprom access (AR5K_PCICFG_EEAE) + * read AR5K_EEPROM_BASE +(4 * offset) + * check the eeprom status register + * and read eeprom data register. + * + * 5211 - write offset to AR5K_EEPROM_BASE + * 5212 write AR5K_EEPROM_CMD_READ on AR5K_EEPROM_CMD + * check the eeprom status register + * and read eeprom data register. + * + * To write eeprom data for a specific offset: + * 5210 - enable eeprom access (AR5K_PCICFG_EEAE) + * write data to AR5K_EEPROM_BASE +(4 * offset) + * check the eeprom status register + * 5211 - write AR5K_EEPROM_CMD_RESET on AR5K_EEPROM_CMD + * 5212 write offset to AR5K_EEPROM_BASE + * write data to data register + * write AR5K_EEPROM_CMD_WRITE on AR5K_EEPROM_CMD + * check the eeprom status register + * + * For more infos check eeprom_* functs and the ar5k.c + * file posted in madwifi-devel mailing list. + * http://sourceforge.net/mailarchive/message.php?msg_id=8966525 + * + */ +#define AR5K_EEPROM_BASE 0x6000 + +/* + * Common ar5xxx EEPROM data offsets (set these on AR5K_EEPROM_BASE) + */ +#define AR5K_EEPROM_MAGIC 0x003d /* EEPROM Magic number */ +#define AR5K_EEPROM_MAGIC_VALUE 0x5aa5 /* Default - found on EEPROM */ +#define AR5K_EEPROM_MAGIC_5212 0x0000145c /* 5212 */ +#define AR5K_EEPROM_MAGIC_5211 0x0000145b /* 5211 */ +#define AR5K_EEPROM_MAGIC_5210 0x0000145a /* 5210 */ + +#define AR5K_EEPROM_PROTECT 0x003f /* EEPROM protect status */ +#define AR5K_EEPROM_PROTECT_RD_0_31 0x0001 /* Read protection bit for offsets 0x0 - 0x1f */ +#define AR5K_EEPROM_PROTECT_WR_0_31 0x0002 /* Write protection bit for offsets 0x0 - 0x1f */ +#define AR5K_EEPROM_PROTECT_RD_32_63 0x0004 /* 0x20 - 0x3f */ +#define AR5K_EEPROM_PROTECT_WR_32_63 0x0008 +#define AR5K_EEPROM_PROTECT_RD_64_127 0x0010 /* 0x40 - 0x7f */ +#define AR5K_EEPROM_PROTECT_WR_64_127 0x0020 +#define AR5K_EEPROM_PROTECT_RD_128_191 0x0040 /* 0x80 - 0xbf (regdom) */ +#define AR5K_EEPROM_PROTECT_WR_128_191 0x0080 +#define AR5K_EEPROM_PROTECT_RD_192_207 0x0100 /* 0xc0 - 0xcf */ +#define AR5K_EEPROM_PROTECT_WR_192_207 0x0200 +#define AR5K_EEPROM_PROTECT_RD_208_223 0x0400 /* 0xd0 - 0xdf */ +#define AR5K_EEPROM_PROTECT_WR_208_223 0x0800 +#define AR5K_EEPROM_PROTECT_RD_224_239 0x1000 /* 0xe0 - 0xef */ +#define AR5K_EEPROM_PROTECT_WR_224_239 0x2000 +#define AR5K_EEPROM_PROTECT_RD_240_255 0x4000 /* 0xf0 - 0xff */ +#define AR5K_EEPROM_PROTECT_WR_240_255 0x8000 +#define AR5K_EEPROM_REG_DOMAIN 0x00bf /* EEPROM regdom */ +#define AR5K_EEPROM_INFO_BASE 0x00c0 /* EEPROM header */ +#define AR5K_EEPROM_INFO_MAX (0x400 - AR5K_EEPROM_INFO_BASE) +#define AR5K_EEPROM_INFO_CKSUM 0xffff +#define AR5K_EEPROM_INFO(_n) (AR5K_EEPROM_INFO_BASE + (_n)) + +#define AR5K_EEPROM_VERSION AR5K_EEPROM_INFO(1) /* EEPROM Version */ +#define AR5K_EEPROM_VERSION_3_0 0x3000 /* No idea what's going on before this version */ +#define AR5K_EEPROM_VERSION_3_1 0x3001 /* ob/db values for 2Ghz (ar5211_rfregs) */ +#define AR5K_EEPROM_VERSION_3_2 0x3002 /* different frequency representation (eeprom_bin2freq) */ +#define AR5K_EEPROM_VERSION_3_3 0x3003 /* offsets changed, has 32 CTLs (see below) and ee_false_detect (eeprom_read_modes) */ +#define AR5K_EEPROM_VERSION_3_4 0x3004 /* has ee_i_gain ee_cck_ofdm_power_delta (eeprom_read_modes) */ +#define AR5K_EEPROM_VERSION_4_0 0x4000 /* has ee_misc*, ee_cal_pier, ee_turbo_max_power and ee_xr_power (eeprom_init) */ +#define AR5K_EEPROM_VERSION_4_1 0x4001 /* has ee_margin_tx_rx (eeprom_init) */ +#define AR5K_EEPROM_VERSION_4_2 0x4002 /* has ee_cck_ofdm_gain_delta (eeprom_init) */ +#define AR5K_EEPROM_VERSION_4_3 0x4003 +#define AR5K_EEPROM_VERSION_4_4 0x4004 +#define AR5K_EEPROM_VERSION_4_5 0x4005 +#define AR5K_EEPROM_VERSION_4_6 0x4006 /* has ee_scaled_cck_delta */ +#define AR5K_EEPROM_VERSION_4_7 0x3007 + +#define AR5K_EEPROM_MODE_11A 0 +#define AR5K_EEPROM_MODE_11B 1 +#define AR5K_EEPROM_MODE_11G 2 + +#define AR5K_EEPROM_HDR AR5K_EEPROM_INFO(2) /* Header that contains the device caps */ +#define AR5K_EEPROM_HDR_11A(_v) (((_v) >> AR5K_EEPROM_MODE_11A) & 0x1) +#define AR5K_EEPROM_HDR_11B(_v) (((_v) >> AR5K_EEPROM_MODE_11B) & 0x1) +#define AR5K_EEPROM_HDR_11G(_v) (((_v) >> AR5K_EEPROM_MODE_11G) & 0x1) +#define AR5K_EEPROM_HDR_T_2GHZ_DIS(_v) (((_v) >> 3) & 0x1) /* Disable turbo for 2Ghz (?) */ +#define AR5K_EEPROM_HDR_T_5GHZ_DBM(_v) (((_v) >> 4) & 0x7f) /* Max turbo power for a/XR mode (eeprom_init) */ +#define AR5K_EEPROM_HDR_DEVICE(_v) (((_v) >> 11) & 0x7) +#define AR5K_EEPROM_HDR_T_5GHZ_DIS(_v) (((_v) >> 15) & 0x1) /* Disable turbo for 5Ghz (?) */ +#define AR5K_EEPROM_HDR_RFKILL(_v) (((_v) >> 14) & 0x1) /* Device has RFKill support */ + +#define AR5K_EEPROM_RFKILL_GPIO_SEL 0x0000001c +#define AR5K_EEPROM_RFKILL_GPIO_SEL_S 2 +#define AR5K_EEPROM_RFKILL_POLARITY 0x00000002 +#define AR5K_EEPROM_RFKILL_POLARITY_S 1 + +/* Newer EEPROMs are using a different offset */ +#define AR5K_EEPROM_OFF(_v, _v3_0, _v3_3) \ + (((_v) >= AR5K_EEPROM_VERSION_3_3) ? _v3_3 : _v3_0) + +#define AR5K_EEPROM_ANT_GAIN(_v) AR5K_EEPROM_OFF(_v, 0x00c4, 0x00c3) +#define AR5K_EEPROM_ANT_GAIN_5GHZ(_v) ((int8_t)(((_v) >> 8) & 0xff)) +#define AR5K_EEPROM_ANT_GAIN_2GHZ(_v) ((int8_t)((_v) & 0xff)) + +/* calibration settings */ +#define AR5K_EEPROM_MODES_11A(_v) AR5K_EEPROM_OFF(_v, 0x00c5, 0x00d4) +#define AR5K_EEPROM_MODES_11B(_v) AR5K_EEPROM_OFF(_v, 0x00d0, 0x00f2) +#define AR5K_EEPROM_MODES_11G(_v) AR5K_EEPROM_OFF(_v, 0x00da, 0x010d) +#define AR5K_EEPROM_CTL(_v) AR5K_EEPROM_OFF(_v, 0x00e4, 0x0128) /* Conformance test limits */ + +/* [3.1 - 3.3] */ +#define AR5K_EEPROM_OBDB0_2GHZ 0x00ec +#define AR5K_EEPROM_OBDB1_2GHZ 0x00ed + +/* Misc values available since EEPROM 4.0 */ +#define AR5K_EEPROM_MISC0 0x00c4 +#define AR5K_EEPROM_EARSTART(_v) ((_v) & 0xfff) +#define AR5K_EEPROM_EEMAP(_v) (((_v) >> 14) & 0x3) +#define AR5K_EEPROM_MISC1 0x00c5 +#define AR5K_EEPROM_TARGET_PWRSTART(_v) ((_v) & 0xfff) +#define AR5K_EEPROM_HAS32KHZCRYSTAL(_v) (((_v) >> 14) & 0x1) + +/* + * EEPROM data register + */ +#define AR5K_EEPROM_DATA_5211 0x6004 +#define AR5K_EEPROM_DATA_5210 0x6800 +#define AR5K_EEPROM_DATA (hal->ah_version == AR5K_AR5210 ? \ + AR5K_EEPROM_DATA_5210 : AR5K_EEPROM_DATA_5211) + +/* + * EEPROM command register + */ +#define AR5K_EEPROM_CMD 0x6008 /* Register Addres */ +#define AR5K_EEPROM_CMD_READ 0x00000001 /* EEPROM read */ +#define AR5K_EEPROM_CMD_WRITE 0x00000002 /* EEPROM write */ +#define AR5K_EEPROM_CMD_RESET 0x00000004 /* EEPROM reset */ + +/* + * EEPROM status register + */ +#define AR5K_EEPROM_STAT_5210 0x6c00 /* Register Address [5210] */ +#define AR5K_EEPROM_STAT_5211 0x600c /* Register Address [5211+] */ +#define AR5K_EEPROM_STATUS (hal->ah_version == AR5K_AR5210 ? \ + AR5K_EEPROM_STAT_5210 : AR5K_EEPROM_STAT_5211) +#define AR5K_EEPROM_STAT_RDERR 0x00000001 /* EEPROM read failed */ +#define AR5K_EEPROM_STAT_RDDONE 0x00000002 /* EEPROM read successful */ +#define AR5K_EEPROM_STAT_WRERR 0x00000004 /* EEPROM write failed */ +#define AR5K_EEPROM_STAT_WRDONE 0x00000008 /* EEPROM write successful */ + +/* + * EEPROM config register (?) + */ +#define AR5K_EEPROM_CFG 0x6010 + + + +/* + * Protocol Control Unit (PCU) registers + */ +/* + * Used for checking initial register writes + * during channel reset (see reset func) + */ +#define AR5K_PCU_MIN 0x8000 +#define AR5K_PCU_MAX 0x8fff + +/* + * First station id register (MAC address in lower 32 bits) + */ +#define AR5K_STA_ID0 0x8000 + +/* + * Second station id register (MAC address in upper 16 bits) + */ +#define AR5K_STA_ID1 0x8004 /* Register Address */ +#define AR5K_STA_ID1_AP 0x00010000 /* Set AP mode */ +#define AR5K_STA_ID1_ADHOC 0x00020000 /* Set Ad-Hoc mode */ +#define AR5K_STA_ID1_PWR_SV 0x00040000 /* Power save reporting (?) */ +#define AR5K_STA_ID1_NO_KEYSRCH 0x00080000 /* No key search */ +#define AR5K_STA_ID1_NO_PSPOLL 0x00100000 /* No power save polling [5210] */ +#define AR5K_STA_ID1_PCF_5211 0x00100000 /* Enable PCF on [5211+] */ +#define AR5K_STA_ID1_PCF_5210 0x00200000 /* Enable PCF on [5210]*/ +#define AR5K_STA_ID1_PCF (hal->ah_version == AR5K_AR5210 ? \ + AR5K_STA_ID1_PCF_5210 : AR5K_STA_ID1_PCF_5211) +#define AR5K_STA_ID1_DEFAULT_ANTENNA 0x00200000 /* Use default antenna */ +#define AR5K_STA_ID1_DESC_ANTENNA 0x00400000 /* Update antenna from descriptor */ +#define AR5K_STA_ID1_RTS_DEF_ANTENNA 0x00800000 /* Use default antenna for RTS (?) */ +#define AR5K_STA_ID1_ACKCTS_6MB 0x01000000 /* Use 6Mbit/s for ACK/CTS (?) */ +#define AR5K_STA_ID1_BASE_RATE_11B 0x02000000 /* Use 11b base rate (for ACK/CTS ?) [5211+] */ + +/* + * First BSSID register (MAC address, lower 32bits) + */ +#define AR5K_BSS_ID0 0x8008 + +/* + * Second BSSID register (MAC address in upper 16 bits) + * + * AID: Association ID + */ +#define AR5K_BSS_ID1 0x800c +#define AR5K_BSS_ID1_AID 0xffff0000 +#define AR5K_BSS_ID1_AID_S 16 + +/* + * Backoff slot time register + */ +#define AR5K_SLOT_TIME 0x8010 + +/* + * ACK/CTS timeout register + */ +#define AR5K_TIME_OUT 0x8014 /* Register Address */ +#define AR5K_TIME_OUT_ACK 0x00001fff /* ACK timeout mask */ +#define AR5K_TIME_OUT_ACK_S 0 +#define AR5K_TIME_OUT_CTS 0x1fff0000 /* CTS timeout mask */ +#define AR5K_TIME_OUT_CTS_S 16 + +/* + * RSSI threshold register + */ +#define AR5K_RSSI_THR 0x8018 /* Register Address */ +#define AR5K_RSSI_THR_M 0x000000ff /* Mask for RSSI threshold [5211+] */ +#define AR5K_RSSI_THR_BMISS_5210 0x00000700 /* Mask for Beacon Missed threshold [5210] */ +#define AR5K_RSSI_THR_BMISS_5210_S 8 +#define AR5K_RSSI_THR_BMISS_5211 0x0000ff00 /* Mask for Beacon Missed threshold [5211+] */ +#define AR5K_RSSI_THR_BMISS_5211_S 8 +#define AR5K_RSSI_THR_BMISS (hal->ah_version == AR5K_AR5210 ? \ + AR5K_RSSI_THR_BMISS_5210 : AR5K_RSSI_THR_BMISS_5211) +#define AR5K_RSSI_THR_BMISS_S 8 + +/* + * 5210 has more PCU registers because there is no QCU/DCU + * so queue parameters are set here, this way a lot common + * registers have different address for 5210. To make things + * easier we define a macro based on hal->ah_version for common + * registers with different addresses and common flags. + */ + +/* + * Retry limit register + * + * Retry limit register for 5210 (no QCU/DCU so it's done in PCU) + */ +#define AR5K_NODCU_RETRY_LMT 0x801c /*Register Address */ +#define AR5K_NODCU_RETRY_LMT_SH_RETRY 0x0000000f /* Short retry limit mask */ +#define AR5K_NODCU_RETRY_LMT_SH_RETRY_S 0 +#define AR5K_NODCU_RETRY_LMT_LG_RETRY 0x000000f0 /* Long retry mask */ +#define AR5K_NODCU_RETRY_LMT_LG_RETRY_S 4 +#define AR5K_NODCU_RETRY_LMT_SSH_RETRY 0x00003f00 /* Station short retry limit mask */ +#define AR5K_NODCU_RETRY_LMT_SSH_RETRY_S 8 +#define AR5K_NODCU_RETRY_LMT_SLG_RETRY 0x000fc000 /* Station long retry limit mask */ +#define AR5K_NODCU_RETRY_LMT_SLG_RETRY_S 14 +#define AR5K_NODCU_RETRY_LMT_CW_MIN 0x3ff00000 /* Minimum contention window mask */ +#define AR5K_NODCU_RETRY_LMT_CW_MIN_S 20 + +/* + * Transmit latency register + */ +#define AR5K_USEC_5210 0x8020 /* Register Address [5210] */ +#define AR5K_USEC_5211 0x801c /* Register Address [5211+] */ +#define AR5K_USEC (hal->ah_version == AR5K_AR5210 ? \ + AR5K_USEC_5210 : AR5K_USEC_5211) +#define AR5K_USEC_1 0x0000007f +#define AR5K_USEC_1_S 0 +#define AR5K_USEC_32 0x00003f80 +#define AR5K_USEC_32_S 7 +#define AR5K_USEC_TX_LATENCY_5211 0x007fc000 +#define AR5K_USEC_TX_LATENCY_5211_S 14 +#define AR5K_USEC_RX_LATENCY_5211 0x1f800000 +#define AR5K_USEC_RX_LATENCY_5211_S 23 +#define AR5K_USEC_TX_LATENCY_5210 0x000fc000 /* also for 5311 */ +#define AR5K_USEC_TX_LATENCY_5210_S 14 +#define AR5K_USEC_RX_LATENCY_5210 0x03f00000 /* also for 5311 */ +#define AR5K_USEC_RX_LATENCY_5210_S 20 + +/* + * PCU beacon control register + */ +#define AR5K_BEACON_5210 0x8024 +#define AR5K_BEACON_5211 0x8020 +#define AR5K_BEACON (hal->ah_version == AR5K_AR5210 ? \ + AR5K_BEACON_5210 : AR5K_BEACON_5211) +#define AR5K_BEACON_PERIOD 0x0000ffff +#define AR5K_BEACON_PERIOD_S 0 +#define AR5K_BEACON_TIM 0x007f0000 +#define AR5K_BEACON_TIM_S 16 +#define AR5K_BEACON_ENABLE 0x00800000 +#define AR5K_BEACON_RESET_TSF 0x01000000 + +/* + * CFP period register + */ +#define AR5K_CFP_PERIOD_5210 0x8028 +#define AR5K_CFP_PERIOD_5211 0x8024 +#define AR5K_CFP_PERIOD (hal->ah_version == AR5K_AR5210 ? \ + AR5K_CFP_PERIOD_5210 : AR5K_CFP_PERIOD_5211) + +/* + * Next beacon time register + */ +#define AR5K_TIMER0_5210 0x802c +#define AR5K_TIMER0_5211 0x8028 +#define AR5K_TIMER0 (hal->ah_version == AR5K_AR5210 ? \ + AR5K_TIMER0_5210 : AR5K_TIMER0_5211) + +/* + * Next DMA beacon alert register + */ +#define AR5K_TIMER1_5210 0x8030 +#define AR5K_TIMER1_5211 0x802c +#define AR5K_TIMER1 (hal->ah_version == AR5K_AR5210 ? \ + AR5K_TIMER1_5210 : AR5K_TIMER1_5211) + +/* + * Next software beacon alert register + */ +#define AR5K_TIMER2_5210 0x8034 +#define AR5K_TIMER2_5211 0x8030 +#define AR5K_TIMER2 (hal->ah_version == AR5K_AR5210 ? \ + AR5K_TIMER2_5210 : AR5K_TIMER2_5211) + +/* + * Next ATIM window time register + */ +#define AR5K_TIMER3_5210 0x8038 +#define AR5K_TIMER3_5211 0x8034 +#define AR5K_TIMER3 (hal->ah_version == AR5K_AR5210 ? \ + AR5K_TIMER3_5210 : AR5K_TIMER3_5211) + + +/* + * 5210 First inter frame spacing register (IFS) + */ +#define AR5K_IFS0 0x8040 +#define AR5K_IFS0_SIFS 0x000007ff +#define AR5K_IFS0_SIFS_S 0 +#define AR5K_IFS0_DIFS 0x007ff800 +#define AR5K_IFS0_DIFS_S 11 + +/* + * 5210 Second inter frame spacing register (IFS) + */ +#define AR5K_IFS1 0x8044 +#define AR5K_IFS1_PIFS 0x00000fff +#define AR5K_IFS1_PIFS_S 0 +#define AR5K_IFS1_EIFS 0x03fff000 +#define AR5K_IFS1_EIFS_S 12 +#define AR5K_IFS1_CS_EN 0x04000000 + + +/* + * CFP duration register + */ +#define AR5K_CFP_DUR_5210 0x8048 +#define AR5K_CFP_DUR_5211 0x8038 +#define AR5K_CFP_DUR (hal->ah_version == AR5K_AR5210 ? \ + AR5K_CFP_DUR_5210 : AR5K_CFP_DUR_5211) + +/* + * Receive filter register + * TODO: Get these out of ar5xxx.h on ath5k + */ +#define AR5K_RX_FILTER_5210 0x804c /* Register Address [5210] */ +#define AR5K_RX_FILTER_5211 0x803c /* Register Address [5211+] */ +#define AR5K_RX_FILTER (ah->ah_version == AR5K_AR5210 ? \ + AR5K_RX_FILTER_5210 : AR5K_RX_FILTER_5211) +#define AR5K_RX_FILTER_UCAST 0x00000001 /* Don't filter unicast frames */ +#define AR5K_RX_FILTER_MCAST 0x00000002 /* Don't filter multicast frames */ +#define AR5K_RX_FILTER_BCAST 0x00000004 /* Don't filter broadcast frames */ +#define AR5K_RX_FILTER_CONTROL 0x00000008 /* Don't filter control frames */ +#define AR5K_RX_FILTER_BEACON 0x00000010 /* Don't filter beacon frames */ +#define AR5K_RX_FILTER_PROM 0x00000020 /* Set promiscuous mode */ +#define AR5K_RX_FILTER_XRPOLL 0x00000040 /* Don't filter XR poll frame [5212+] */ +#define AR5K_RX_FILTER_PROBEREQ 0x00000080 /* Don't filter probe requests [5212+] */ +#define AR5K_RX_FILTER_PHYERR_5212 0x00000100 /* Don't filter phy errors [5212+] */ +#define AR5K_RX_FILTER_RADARERR_5212 0x00000200 /* Don't filter phy radar errors [5212+] */ +#define AR5K_RX_FILTER_PHYERR_5211 0x00000040 /* [5211] */ +#define AR5K_RX_FILTER_RADARERR_5211 0x00000080 /* [5211] */ +#define AR5K_RX_FILTER_PHYERR (ah->ah_version == AR5K_AR5211 ? \ + AR5K_RX_FILTER_PHYERR_5211 : AR5K_RX_FILTER_PHYERR_5212) +#define AR5K_RX_FILTER_RADARERR (ah->ah_version == AR5K_AR5211 ? \ + AR5K_RX_FILTER_RADARERR_5211 : AR5K_RX_FILTER_RADARERR_5212) +/* + * Multicast filter register (lower 32 bits) + */ +#define AR5K_MCAST_FILTER0_5210 0x8050 +#define AR5K_MCAST_FILTER0_5211 0x8040 +#define AR5K_MCAST_FILTER0 (hal->ah_version == AR5K_AR5210 ? \ + AR5K_MCAST_FILTER0_5210 : AR5K_MCAST_FILTER0_5211) + +/* + * Multicast filter register (higher 16 bits) + */ +#define AR5K_MCAST_FILTER1_5210 0x8054 +#define AR5K_MCAST_FILTER1_5211 0x8044 +#define AR5K_MCAST_FILTER1 (hal->ah_version == AR5K_AR5210 ? \ + AR5K_MCAST_FILTER1_5210 : AR5K_MCAST_FILTER1_5211) + + +/* + * Transmit mask register (lower 32 bits) [5210] + */ +#define AR5K_TX_MASK0 0x8058 + +/* + * Transmit mask register (higher 16 bits) [5210] + */ +#define AR5K_TX_MASK1 0x805c + +/* + * Clear transmit mask [5210] + */ +#define AR5K_CLR_TMASK 0x8060 + +/* + * Trigger level register (before transmission) [5210] + */ +#define AR5K_TRIG_LVL 0x8064 + + +/* + * PCU control register + * + * Only DIS_RX is used in the code, the rest i guess are + * for tweaking/diagnostics. + */ +#define AR5K_DIAG_SW_5210 0x8068 /* Register Address [5210] */ +#define AR5K_DIAG_SW_5211 0x8048 /* Register Address [5211+] */ +#define AR5K_DIAG_SW (hal->ah_version == AR5K_AR5210 ? \ + AR5K_DIAG_SW_5210 : AR5K_DIAG_SW_5211) +#define AR5K_DIAG_SW_DIS_WEP_ACK 0x00000001 +#define AR5K_DIAG_SW_DIS_ACK 0x00000002 /* Disable ACKs (?) */ +#define AR5K_DIAG_SW_DIS_CTS 0x00000004 /* Disable CTSs (?) */ +#define AR5K_DIAG_SW_DIS_ENC 0x00000008 /* Disable encryption (?) */ +#define AR5K_DIAG_SW_DIS_DEC 0x00000010 /* Disable decryption (?) */ +#define AR5K_DIAG_SW_DIS_TX 0x00000020 /* Disable transmit [5210] */ +#define AR5K_DIAG_SW_DIS_RX_5210 0x00000040 /* Disable recieve */ +#define AR5K_DIAG_SW_DIS_RX_5211 0x00000020 +#define AR5K_DIAG_SW_DIS_RX (hal->ah_version == AR5K_AR5210 ? \ + AR5K_DIAG_SW_DIS_RX_5210 : AR5K_DIAG_SW_DIS_RX_5211) +#define AR5K_DIAG_SW_LOOP_BACK_5210 0x00000080 /* Loopback (i guess it goes with DIS_TX) [5210] */ +#define AR5K_DIAG_SW_LOOP_BACK_5211 0x00000040 +#define AR5K_DIAG_SW_LOOP_BACK (hal->ah_version == AR5K_AR5210 ? \ + AR5K_DIAG_SW_LOOP_BACK_5210 : AR5K_DIAG_SW_LOOP_BACK_5211) +#define AR5K_DIAG_SW_CORR_FCS_5210 0x00000100 +#define AR5K_DIAG_SW_CORR_FCS_5211 0x00000080 +#define AR5K_DIAG_SW_CORR_FCS (hal->ah_version == AR5K_AR5210 ? \ + AR5K_DIAG_SW_CORR_FCS_5210 : AR5K_DIAG_SW_CORR_FCS_5211) +#define AR5K_DIAG_SW_CHAN_INFO_5210 0x00000200 +#define AR5K_DIAG_SW_CHAN_INFO_5211 0x00000100 +#define AR5K_DIAG_SW_CHAN_INFO (hal->ah_version == AR5K_AR5210 ? \ + AR5K_DIAG_SW_CHAN_INFO_5210 : AR5K_DIAG_SW_CHAN_INFO_5211) +#define AR5K_DIAG_SW_EN_SCRAM_SEED_5211 0x00000200 /* Scrambler seed (?) */ +#define AR5K_DIAG_SW_EN_SCRAM_SEED_5210 0x00000400 +#define AR5K_DIAG_SW_EN_SCRAM_SEED (hal->ah_version == AR5K_AR5210 ? \ + AR5K_DIAG_SW_EN_SCRAM_SEED_5210 : AR5K_DIAG_SW_EN_SCRAM_SEED_5211) +#define AR5K_DIAG_SW_ECO_ENABLE 0x00000400 /* [5211+] */ +#define AR5K_DIAG_SW_SCVRAM_SEED 0x0003f800 /* [5210] */ +#define AR5K_DIAG_SW_SCRAM_SEED_M 0x0001fc00 /* Scrambler seed mask (?) */ +#define AR5K_DIAG_SW_SCRAM_SEED_S 10 +#define AR5K_DIAG_SW_DIS_SEQ_INC 0x00040000 /* Disable seqnum increment (?)[5210] */ +#define AR5K_DIAG_SW_FRAME_NV0_5210 0x00080000 +#define AR5K_DIAG_SW_FRAME_NV0_5211 0x00020000 +#define AR5K_DIAG_SW_FRAME_NV0 (hal->ah_version == AR5K_AR5210 ? \ + AR5K_DIAG_SW_FRAME_NV0_5210 : AR5K_DIAG_SW_FRAME_NV0_5211) +#define AR5K_DIAG_SW_OBSPT_M 0x000c0000 +#define AR5K_DIAG_SW_OBSPT_S 18 + +/* + * TSF (clock) register (lower 32 bits) + */ +#define AR5K_TSF_L32_5210 0x806c +#define AR5K_TSF_L32_5211 0x804c +#define AR5K_TSF_L32 (hal->ah_version == AR5K_AR5210 ? \ + AR5K_TSF_L32_5210 : AR5K_TSF_L32_5211) + +/* + * TSF (clock) register (higher 32 bits) + */ +#define AR5K_TSF_U32_5210 0x8070 +#define AR5K_TSF_U32_5211 0x8050 +#define AR5K_TSF_U32 (hal->ah_version == AR5K_AR5210 ? \ + AR5K_TSF_U32_5210 : AR5K_TSF_U32_5211) + +/* + * Last beacon timestamp register + */ +#define AR5K_LAST_TSTP 0x8080 + +/* + * ADDAC test register [5211+] + */ +#define AR5K_ADDAC_TEST 0x8054 +#define AR5K_ADDAC_TEST_TXCONT 0x00000001 + +/* + * Default antenna register [5211+] + */ +#define AR5K_DEFAULT_ANTENNA 0x8058 + + + +/* + * Retry count register [5210] + */ +#define AR5K_RETRY_CNT 0x8084 /* Register Address [5210] */ +#define AR5K_RETRY_CNT_SSH 0x0000003f /* Station short retry count (?) */ +#define AR5K_RETRY_CNT_SLG 0x00000fc0 /* Station long retry count (?) */ + +/* + * Back-off status register [5210] + */ +#define AR5K_BACKOFF 0x8088 /* Register Address [5210] */ +#define AR5K_BACKOFF_CW 0x000003ff /* Backoff Contention Window (?) */ +#define AR5K_BACKOFF_CNT 0x03ff0000 /* Backoff count (?) */ + + + +/* + * NAV register (current) + */ +#define AR5K_NAV_5210 0x808c +#define AR5K_NAV_5211 0x8084 +#define AR5K_NAV (hal->ah_version == AR5K_AR5210 ? \ + AR5K_NAV_5210 : AR5K_NAV_5211) + +/* + * RTS success register + */ +#define AR5K_RTS_OK_5210 0x8090 +#define AR5K_RTS_OK_5211 0x8088 +#define AR5K_RTS_OK (hal->ah_version == AR5K_AR5210 ? \ + AR5K_RTS_OK_5210 : AR5K_RTS_OK_5211) + +/* + * RTS failure register + */ +#define AR5K_RTS_FAIL_5210 0x8094 +#define AR5K_RTS_FAIL_5211 0x808c +#define AR5K_RTS_FAIL (hal->ah_version == AR5K_AR5210 ? \ + AR5K_RTS_FAIL_5210 : AR5K_RTS_FAIL_5211) + +/* + * ACK failure register + */ +#define AR5K_ACK_FAIL_5210 0x8098 +#define AR5K_ACK_FAIL_5211 0x8090 +#define AR5K_ACK_FAIL (hal->ah_version == AR5K_AR5210 ? \ + AR5K_ACK_FAIL_5210 : AR5K_ACK_FAIL_5211) + +/* + * FCS failure register + */ +#define AR5K_FCS_FAIL_5210 0x809c +#define AR5K_FCS_FAIL_5211 0x8094 +#define AR5K_FCS_FAIL (hal->ah_version == AR5K_AR5210 ? \ + AR5K_FCS_FAIL_5210 : AR5K_FCS_FAIL_5211) + +/* + * Beacon count register + */ +#define AR5K_BEACON_CNT_5210 0x80a0 +#define AR5K_BEACON_CNT_5211 0x8098 +#define AR5K_BEACON_CNT (hal->ah_version == AR5K_AR5210 ? \ + AR5K_BEACON_CNT_5210 : AR5K_BEACON_CNT_5211) + + +/*===5212 Specific PCU registers===*/ + +/* + * XR (eXtended Range) mode register + */ +#define AR5K_XRMODE 0x80c0 +#define AR5K_XRMODE_POLL_TYPE_M 0x0000003f +#define AR5K_XRMODE_POLL_TYPE_S 0 +#define AR5K_XRMODE_POLL_SUBTYPE_M 0x0000003c +#define AR5K_XRMODE_POLL_SUBTYPE_S 2 +#define AR5K_XRMODE_POLL_WAIT_ALL 0x00000080 +#define AR5K_XRMODE_SIFS_DELAY 0x000fff00 +#define AR5K_XRMODE_FRAME_HOLD_M 0xfff00000 +#define AR5K_XRMODE_FRAME_HOLD_S 20 + +/* + * XR delay register + */ +#define AR5K_XRDELAY 0x80c4 +#define AR5K_XRDELAY_SLOT_DELAY_M 0x0000ffff +#define AR5K_XRDELAY_SLOT_DELAY_S 0 +#define AR5K_XRDELAY_CHIRP_DELAY_M 0xffff0000 +#define AR5K_XRDELAY_CHIRP_DELAY_S 16 + +/* + * XR timeout register + */ +#define AR5K_XRTIMEOUT 0x80c8 +#define AR5K_XRTIMEOUT_CHIRP_M 0x0000ffff +#define AR5K_XRTIMEOUT_CHIRP_S 0 +#define AR5K_XRTIMEOUT_POLL_M 0xffff0000 +#define AR5K_XRTIMEOUT_POLL_S 16 + +/* + * XR chirp register + */ +#define AR5K_XRCHIRP 0x80cc +#define AR5K_XRCHIRP_SEND 0x00000001 +#define AR5K_XRCHIRP_GAP 0xffff0000 + +/* + * XR stomp register + */ +#define AR5K_XRSTOMP 0x80d0 +#define AR5K_XRSTOMP_TX 0x00000001 +#define AR5K_XRSTOMP_RX_ABORT 0x00000002 +#define AR5K_XRSTOMP_RSSI_THRES 0x0000ff00 + +/* + * First enhanced sleep register + */ +#define AR5K_SLEEP0 0x80d4 +#define AR5K_SLEEP0_NEXT_DTIM 0x0007ffff +#define AR5K_SLEEP0_NEXT_DTIM_S 0 +#define AR5K_SLEEP0_ASSUME_DTIM 0x00080000 +#define AR5K_SLEEP0_ENH_SLEEP_EN 0x00100000 +#define AR5K_SLEEP0_CABTO 0xff000000 +#define AR5K_SLEEP0_CABTO_S 24 + +/* + * Second enhanced sleep register + */ +#define AR5K_SLEEP1 0x80d8 +#define AR5K_SLEEP1_NEXT_TIM 0x0007ffff +#define AR5K_SLEEP1_NEXT_TIM_S 0 +#define AR5K_SLEEP1_BEACON_TO 0xff000000 +#define AR5K_SLEEP1_BEACON_TO_S 24 + +/* + * Third enhanced sleep register + */ +#define AR5K_SLEEP2 0x80dc +#define AR5K_SLEEP2_TIM_PER 0x0000ffff +#define AR5K_SLEEP2_TIM_PER_S 0 +#define AR5K_SLEEP2_DTIM_PER 0xffff0000 +#define AR5K_SLEEP2_DTIM_PER_S 16 + +/* + * BSSID mask registers + */ +#define AR5K_BSS_IDM0 0x80e0 +#define AR5K_BSS_IDM1 0x80e4 + +/* + * TX power control (TPC) register + */ +#define AR5K_TXPC 0x80e8 +#define AR5K_TXPC_ACK_M 0x0000003f +#define AR5K_TXPC_ACK_S 0 +#define AR5K_TXPC_CTS_M 0x00003f00 +#define AR5K_TXPC_CTS_S 8 +#define AR5K_TXPC_CHIRP_M 0x003f0000 +#define AR5K_TXPC_CHIRP_S 22 + +/* + * Profile count registers + */ +#define AR5K_PROFCNT_TX 0x80ec +#define AR5K_PROFCNT_RX 0x80f0 +#define AR5K_PROFCNT_RXCLR 0x80f4 +#define AR5K_PROFCNT_CYCLE 0x80f8 + +/* + * TSF parameter register + */ +#define AR5K_TSF_PARM 0x8104 +#define AR5K_TSF_PARM_INC_M 0x000000ff +#define AR5K_TSF_PARM_INC_S 0 + +/* + * PHY error filter register + */ +#define AR5K_PHY_ERR_FIL 0x810c +#define AR5K_PHY_ERR_FIL_RADAR 0x00000020 +#define AR5K_PHY_ERR_FIL_OFDM 0x00020000 +#define AR5K_PHY_ERR_FIL_CCK 0x02000000 + +/* + * Rate duration register + */ +#define AR5K_RATE_DUR_BASE 0x8700 +#define AR5K_RATE_DUR(_n) (AR5K_RATE_DUR_BASE + ((_n) << 2)) + +/*===5212 end===*/ + +/* + * Key table (WEP) register + */ +#define AR5K_KEYTABLE_0_5210 0x9000 +#define AR5K_KEYTABLE_0_5211 0x8800 +#define AR5K_KEYTABLE_5210(_n) (AR5K_KEYTABLE_0_5210 + ((_n) << 5)) +#define AR5K_KEYTABLE_5211(_n) (AR5K_KEYTABLE_0_5211 + ((_n) << 5)) +#define AR5K_KEYTABLE(_n) (hal->ah_version == AR5K_AR5210 ? \ + AR5K_KEYTABLE_5210(_n) : AR5K_KEYTABLE_5211(_n)) +#define AR5K_KEYTABLE_OFF(_n, x) (AR5K_KEYTABLE(_n) + (x << 2)) +#define AR5K_KEYTABLE_TYPE(_n) AR5K_KEYTABLE_OFF(_n, 5) +#define AR5K_KEYTABLE_TYPE_40 0x00000000 +#define AR5K_KEYTABLE_TYPE_104 0x00000001 +#define AR5K_KEYTABLE_TYPE_128 0x00000003 +#define AR5K_KEYTABLE_TYPE_TKIP 0x00000004 /* [5212+] */ +#define AR5K_KEYTABLE_TYPE_AES 0x00000005 /* [5211+] */ +#define AR5K_KEYTABLE_TYPE_CCM 0x00000006 /* [5212+] */ +#define AR5K_KEYTABLE_TYPE_NULL 0x00000007 /* [5211+] */ +#define AR5K_KEYTABLE_ANTENNA 0x00000008 /* [5212+] */ +#define AR5K_KEYTABLE_MAC0(_n) AR5K_KEYTABLE_OFF(_n, 6) +#define AR5K_KEYTABLE_MAC1(_n) AR5K_KEYTABLE_OFF(_n, 7) +#define AR5K_KEYTABLE_VALID 0x00008000 + +/* WEP 40-bit = 40-bit entered key + 24 bit IV = 64-bit + * WEP 104-bit = 104-bit entered key + 24-bit IV = 128-bit + * WEP 128-bit = 128-bit entered key + 24 bit IV = 152-bit + * + * Some vendors have introduced bigger WEP keys to address + * security vulnerabilities in WEP. This includes: + * + * WEP 232-bit = 232-bit entered key + 24 bit IV = 256-bit + * + * We can expand this if we find ar5k Atheros cards with a larger + * key table size. + */ +#define AR5K_KEYTABLE_SIZE_5210 64 +#define AR5K_KEYTABLE_SIZE_5211 128 +#define AR5K_KEYTABLE_SIZE (hal->ah_version == AR5K_AR5210 ? \ + AR5K_KEYTABLE_SIZE_5210 : AR5K_KEYTABLE_SIZE_5211) + + +/*===PHY REGISTERS===*/ + +/* + * PHY register + */ +#define AR5K_PHY_BASE 0x9800 +#define AR5K_PHY(_n) (AR5K_PHY_BASE + ((_n) << 2)) +#define AR5K_PHY_SHIFT_2GHZ 0x00004007 +#define AR5K_PHY_SHIFT_5GHZ 0x00000007 + +/* + * PHY frame control register [5110] /turbo mode register [5111+] + * + * There is another frame control register for [5111+] + * at address 0x9944 (see below) but the 2 first flags + * are common here between 5110 frame control register + * and [5111+] turbo mode register, so this also works as + * a "turbo mode register" for 5110. We treat this one as + * a frame control register for 5110 below. + */ +#define AR5K_PHY_TURBO 0x9804 +#define AR5K_PHY_TURBO_MODE 0x00000001 +#define AR5K_PHY_TURBO_SHORT 0x00000002 + +/* + * PHY agility command register + */ +#define AR5K_PHY_AGC 0x9808 +#define AR5K_PHY_AGC_DISABLE 0x08000000 + +/* + * PHY timing register [5112+] + */ +#define AR5K_PHY_TIMING_3 0x9814 +#define AR5K_PHY_TIMING_3_DSC_MAN 0xfffe0000 +#define AR5K_PHY_TIMING_3_DSC_MAN_S 17 +#define AR5K_PHY_TIMING_3_DSC_EXP 0x0001e000 +#define AR5K_PHY_TIMING_3_DSC_EXP_S 13 + +/* + * PHY chip revision register + */ +#define AR5K_PHY_CHIP_ID 0x9818 + +/* + * PHY activation register + */ +#define AR5K_PHY_ACT 0x981c +#define AR5K_PHY_ACT_ENABLE 0x00000001 +#define AR5K_PHY_ACT_DISABLE 0x00000002 + +/* + * PHY signal register + */ +#define AR5K_PHY_SIG 0x9858 +#define AR5K_PHY_SIG_FIRSTEP 0x0003f000 +#define AR5K_PHY_SIG_FIRSTEP_S 12 +#define AR5K_PHY_SIG_FIRPWR 0x03fc0000 +#define AR5K_PHY_SIG_FIRPWR_S 18 + +/* + * PHY coarse agility control register + */ +#define AR5K_PHY_AGCCOARSE 0x985c +#define AR5K_PHY_AGCCOARSE_LO 0x00007f80 +#define AR5K_PHY_AGCCOARSE_LO_S 7 +#define AR5K_PHY_AGCCOARSE_HI 0x003f8000 +#define AR5K_PHY_AGCCOARSE_HI_S 15 + +/* + * PHY agility control register + */ +#define AR5K_PHY_AGCCTL 0x9860 /* Register address */ +#define AR5K_PHY_AGCCTL_CAL 0x00000001 /* Enable PHY calibration */ +#define AR5K_PHY_AGCCTL_NF 0x00000002 /* Enable Noise Floor calibration */ + +/* + * PHY noise floor status register + */ +#define AR5K_PHY_NF 0x9864 +#define AR5K_PHY_NF_M 0x000001ff +#define AR5K_PHY_NF_ACTIVE 0x00000100 +#define AR5K_PHY_NF_RVAL(_n) (((_n) >> 19) & AR5K_PHY_NF_M) +#define AR5K_PHY_NF_AVAL(_n) (-((_n) ^ AR5K_PHY_NF_M) + 1) +#define AR5K_PHY_NF_SVAL(_n) (((_n) & AR5K_PHY_NF_M) | (1 << 9)) + +/* + * PHY ADC saturation register [5110] + */ +#define AR5K_PHY_ADCSAT 0x9868 +#define AR5K_PHY_ADCSAT_ICNT 0x0001f800 +#define AR5K_PHY_ADCSAT_ICNT_S 11 +#define AR5K_PHY_ADCSAT_THR 0x000007e0 +#define AR5K_PHY_ADCSAT_THR_S 5 + +/* + * PHY sleep registers [5112+] + */ +#define AR5K_PHY_SCR 0x9870 +#define AR5K_PHY_SCR_32MHZ 0x0000001f +#define AR5K_PHY_SLMT 0x9874 +#define AR5K_PHY_SLMT_32MHZ 0x0000007f +#define AR5K_PHY_SCAL 0x9878 +#define AR5K_PHY_SCAL_32MHZ 0x0000000e + +/* + * PHY PLL (Phase Locked Loop) control register + */ +#define AR5K_PHY_PLL 0x987c +#define AR5K_PHY_PLL_20MHZ 0x13 /* For half rate (?) [5111+] */ +#define AR5K_PHY_PLL_40MHZ_5211 0x18 /* For 802.11a */ +#define AR5K_PHY_PLL_40MHZ_5212 0x000000aa +#define AR5K_PHY_PLL_40MHZ (hal->ah_version == AR5K_AR5211 ? \ + AR5K_PHY_PLL_40MHZ_5211 : AR5K_PHY_PLL_40MHZ_5212) +#define AR5K_PHY_PLL_44MHZ_5211 0x19 /* For 802.11b/g */ +#define AR5K_PHY_PLL_44MHZ_5212 0x000000ab +#define AR5K_PHY_PLL_44MHZ (hal->ah_version == AR5K_AR5211 ? \ + AR5K_PHY_PLL_44MHZ_5211 : AR5K_PHY_PLL_44MHZ_5212) +#define AR5K_PHY_PLL_RF5111 0x00000000 +#define AR5K_PHY_PLL_RF5112 0x00000040 + +/* + * RF Buffer register + * + * There are some special control registers on the RF chip + * that hold various operation settings related mostly to + * the analog parts (channel, gain adjustment etc). + * + * We don't write on those registers directly but + * we send a data packet on the buffer register and + * then write on another special register to notify hw + * to apply the settings. This is done so that control registers + * can be dynamicaly programmed during operation and the settings + * are applied faster on the hw. + * + * We sent such data packets during rf initialization and channel change + * through ath5k_hw_rf*_rfregs and ath5k_hw_rf*_channel functions. + * + * The data packets we send during initializadion are inside ath5k_ini_rf + * struct (see ath5k_hw.h) and each one is related to an "rf register bank". + * We use *rfregs functions to modify them acording to current operation + * mode and eeprom values and pass them all together to the chip. + * + * It's obvious from the code that 0x989c is the buffer register but + * for the other special registers that we write to after sending each + * packet, i have no idea. So i'll name them BUFFER_CONTROL_X registers + * for now. It's interesting that they are also used for some other operations. + * + * Also check out ath5k_hw.h and U.S. Patent 6677779 B1 (about buffer + * registers and control registers) + */ + +#define AR5K_RF_BUFFER 0x989c +#define AR5K_RF_BUFFER_CONTROL_0 0x98c0 /* Channel on 5110 */ +#define AR5K_RF_BUFFER_CONTROL_1 0x98c4 /* Bank 7 on 5112 */ +#define AR5K_RF_BUFFER_CONTROL_2 0x98cc /* Bank 7 on 5111 */ + +#define AR5K_RF_BUFFER_CONTROL_3 0x98d0 /* Bank 2 on 5112 */ + /* Channel set on 5111 */ + /* Used to read radio revision*/ + +#define AR5K_RF_BUFFER_CONTROL_4 0x98d4 /* RF Stage register on 5110 */ + /* Bank 0,1,2,6 on 5111 */ + /* Bank 1 on 5112 */ + /* Used during activation on 5111 */ + +#define AR5K_RF_BUFFER_CONTROL_5 0x98d8 /* Bank 3 on 5111 */ + /* Used during activation on 5111 */ + /* Channel on 5112 */ + /* Bank 6 on 5112 */ + +#define AR5K_RF_BUFFER_CONTROL_6 0x98dc /* Bank 3 on 5112 */ + +/* + * PHY RF stage register [5210] + */ +#define AR5K_PHY_RFSTG 0x98d4 +#define AR5K_PHY_RFSTG_DISABLE 0x00000021 + +/* + * PHY receiver delay register [5111+] + */ +#define AR5K_PHY_RX_DELAY 0x9914 +#define AR5K_PHY_RX_DELAY_M 0x00003fff + +/* + * PHY timing I(nphase) Q(adrature) control register [5111+] + */ +#define AR5K_PHY_IQ 0x9920 /* Register address */ +#define AR5K_PHY_IQ_CORR_Q_Q_COFF 0x0000001f /* Mask for q correction info */ +#define AR5K_PHY_IQ_CORR_Q_I_COFF 0x000007e0 /* Mask for i correction info */ +#define AR5K_PHY_IQ_CORR_Q_I_COFF_S 5 +#define AR5K_PHY_IQ_CORR_ENABLE 0x00000800 /* Enable i/q correction */ +#define AR5K_PHY_IQ_CAL_NUM_LOG_MAX 0x0000f000 +#define AR5K_PHY_IQ_CAL_NUM_LOG_MAX_S 12 +#define AR5K_PHY_IQ_RUN 0x00010000 /* Run i/q calibration */ + + +/* + * PHY PAPD probe register [5111+ (?)] + * Is this only present in 5212 ? + * Because it's always 0 in 5211 initialization code + */ +#define AR5K_PHY_PAPD_PROBE 0x9930 +#define AR5K_PHY_PAPD_PROBE_TXPOWER 0x00007e00 +#define AR5K_PHY_PAPD_PROBE_TXPOWER_S 9 +#define AR5K_PHY_PAPD_PROBE_TX_NEXT 0x00008000 +#define AR5K_PHY_PAPD_PROBE_TYPE 0x01800000 /* [5112+] */ +#define AR5K_PHY_PAPD_PROBE_TYPE_S 23 +#define AR5K_PHY_PAPD_PROBE_TYPE_OFDM 0 +#define AR5K_PHY_PAPD_PROBE_TYPE_XR 1 +#define AR5K_PHY_PAPD_PROBE_TYPE_CCK 2 +#define AR5K_PHY_PAPD_PROBE_GAINF 0xfe000000 +#define AR5K_PHY_PAPD_PROBE_GAINF_S 25 +#define AR5K_PHY_PAPD_PROBE_INI_5111 0x00004883 /* [5212+] */ +#define AR5K_PHY_PAPD_PROBE_INI_5112 0x00004882 /* [5212+] */ + + +/* + * PHY TX rate power registers [5112+] + */ +#define AR5K_PHY_TXPOWER_RATE1 0x9934 +#define AR5K_PHY_TXPOWER_RATE2 0x9938 +#define AR5K_PHY_TXPOWER_RATE_MAX 0x993c +#define AR5K_PHY_TXPOWER_RATE_MAX_TPC_ENABLE 0x00000040 +#define AR5K_PHY_TXPOWER_RATE3 0xa234 +#define AR5K_PHY_TXPOWER_RATE4 0xa238 + +/* + * PHY frame control register [5111+] + */ +#define AR5K_PHY_FRAME_CTL_5210 0x9804 +#define AR5K_PHY_FRAME_CTL_5211 0x9944 +#define AR5K_PHY_FRAME_CTL (hal->ah_version == AR5K_AR5210 ? \ + AR5K_PHY_FRAME_CTL_5210 : AR5K_PHY_FRAME_CTL_5211) +/*---[5111+]---*/ +#define AR5K_PHY_FRAME_CTL_TX_CLIP 0x00000038 +#define AR5K_PHY_FRAME_CTL_TX_CLIP_S 3 +/*---[5110/5111]---*/ +#define AR5K_PHY_FRAME_CTL_TIMING_ERR 0x01000000 +#define AR5K_PHY_FRAME_CTL_PARITY_ERR 0x02000000 +#define AR5K_PHY_FRAME_CTL_ILLRATE_ERR 0x04000000 /* illegal rate */ +#define AR5K_PHY_FRAME_CTL_ILLLEN_ERR 0x08000000 /* illegal length */ +#define AR5K_PHY_FRAME_CTL_SERVICE_ERR 0x20000000 +#define AR5K_PHY_FRAME_CTL_TXURN_ERR 0x40000000 /* tx underrun */ +#define AR5K_PHY_FRAME_CTL_INI AR5K_PHY_FRAME_CTL_SERVICE_ERR | \ + AR5K_PHY_FRAME_CTL_TXURN_ERR | \ + AR5K_PHY_FRAME_CTL_ILLLEN_ERR | \ + AR5K_PHY_FRAME_CTL_ILLRATE_ERR | \ + AR5K_PHY_FRAME_CTL_PARITY_ERR | \ + AR5K_PHY_FRAME_CTL_TIMING_ERR + +/* + * PHY radar detection register [5111+] + */ +#define AR5K_PHY_RADAR 0x9954 + +/* Radar enable ........ ........ ........ .......1 */ +#define AR5K_PHY_RADAR_ENABLE 0x00000001 +#define AR5K_PHY_RADAR_DISABLE 0x00000000 +#define AR5K_PHY_RADAR_ENABLE_S 0 + +/* This is the value found on the card .1.111.1 .1.1.... 111....1 1...1... +at power on. */ +#define AR5K_PHY_RADAR_PWONDEF_AR5213 0x5d50e188 + +/* This is the value found on the card .1.1.111 ..11...1 .1...1.1 1...11.1 +after DFS is enabled */ +#define AR5K_PHY_RADAR_ENABLED_AR5213 0x5731458d + +/* Finite Impulse Response (FIR) filter .1111111 ........ ........ ........ + * power out threshold. + * 7-bits, standard power range {0..127} in 1/2 dBm units. */ +#define AR5K_PHY_RADAR_FIRPWROUTTHR 0x7f000000 +#define AR5K_PHY_RADAR_FIRPWROUTTHR_S 24 + +/* Radar RSSI/SNR threshold. ........ 111111.. ........ ........ + * 6-bits, dBm range {0..63} in dBm units. */ +#define AR5K_PHY_RADAR_RADARRSSITHR 0x00fc0000 +#define AR5K_PHY_RADAR_RADARRSSITHR_S 18 + +/* Pulse height threshold ........ ......11 1111.... ........ + * 6-bits, dBm range {0..63} in dBm units. */ +#define AR5K_PHY_RADAR_PULSEHEIGHTTHR 0x0003f000 +#define AR5K_PHY_RADAR_PULSEHEIGHTTHR_S 12 + +/* Pulse RSSI/SNR threshold ........ ........ ....1111 11...... + * 6-bits, dBm range {0..63} in dBm units. */ +#define AR5K_PHY_RADAR_PULSERSSITHR 0x00000fc0 +#define AR5K_PHY_RADAR_PULSERSSITHR_S 6 + +/* Inband threshold ........ ........ ........ ..11111. + * 5-bits, units unknown {0..31} (? MHz ?) */ +#define AR5K_PHY_RADAR_INBANDTHR 0x0000003e +#define AR5K_PHY_RADAR_INBANDTHR_S 1 + +/* + * PHY antenna switch table registers [5110] + */ +#define AR5K_PHY_ANT_SWITCH_TABLE_0 0x9960 +#define AR5K_PHY_ANT_SWITCH_TABLE_1 0x9964 + +/* + * PHY clock sleep registers [5112+] + */ +#define AR5K_PHY_SCLOCK 0x99f0 +#define AR5K_PHY_SCLOCK_32MHZ 0x0000000c +#define AR5K_PHY_SDELAY 0x99f4 +#define AR5K_PHY_SDELAY_32MHZ 0x000000ff +#define AR5K_PHY_SPENDING 0x99f8 +#define AR5K_PHY_SPENDING_RF5111 0x00000018 +#define AR5K_PHY_SPENDING_RF5112 0x00000014 + +/* + * Misc PHY/radio registers [5110 - 5111] + */ +#define AR5K_BB_GAIN_BASE 0x9b00 /* BaseBand Amplifier Gain table base address */ +#define AR5K_BB_GAIN(_n) (AR5K_BB_GAIN_BASE + ((_n) << 2)) +#define AR5K_RF_GAIN_BASE 0x9a00 /* RF Amplrifier Gain table base address */ +#define AR5K_RF_GAIN(_n) (AR5K_RF_GAIN_BASE + ((_n) << 2)) + +/* + * PHY timing IQ calibration result register [5111+] + */ +#define AR5K_PHY_IQRES_CAL_PWR_I 0x9c10 /* I (Inphase) power value */ +#define AR5K_PHY_IQRES_CAL_PWR_Q 0x9c14 /* Q (Quadrature) power value */ +#define AR5K_PHY_IQRES_CAL_CORR 0x9c18 /* I/Q Correlation */ + +/* + * PHY current RSSI register [5111+] + */ +#define AR5K_PHY_CURRENT_RSSI 0x9c1c + +/* + * PHY PCDAC TX power table + */ +#define AR5K_PHY_PCDAC_TXPOWER_BASE 0xa180 +#define AR5K_PHY_PCDAC_TXPOWER(_n) (AR5K_PHY_PCDAC_TXPOWER_BASE + ((_n) << 2)) + +/* + * PHY mode register [5111+] + */ +#define AR5K_PHY_MODE 0x0a200 /* Register address */ +#define AR5K_PHY_MODE_MOD 0x00000001 /* PHY Modulation mask*/ +#define AR5K_PHY_MODE_MOD_OFDM 0 +#define AR5K_PHY_MODE_MOD_CCK 1 +#define AR5K_PHY_MODE_FREQ 0x00000002 /* Freq mode mask */ +#define AR5K_PHY_MODE_FREQ_5GHZ 0 +#define AR5K_PHY_MODE_FREQ_2GHZ 2 +#define AR5K_PHY_MODE_MOD_DYN 0x00000004 /* Dynamic OFDM/CCK mode mask [5112+] */ +#define AR5K_PHY_MODE_RAD 0x00000008 /* [5212+] */ +#define AR5K_PHY_MODE_RAD_RF5111 0 +#define AR5K_PHY_MODE_RAD_RF5112 8 +#define AR5K_PHY_MODE_XR 0x00000010 /* [5112+] */ + +/* + * PHY CCK transmit control register [5111+ (?)] + */ +#define AR5K_PHY_CCKTXCTL 0xa204 +#define AR5K_PHY_CCKTXCTL_WORLD 0x00000000 +#define AR5K_PHY_CCKTXCTL_JAPAN 0x00000010 + +/* + * PHY 2GHz gain register [5111+] + */ +#define AR5K_PHY_GAIN_2GHZ 0xa20c +#define AR5K_PHY_GAIN_2GHZ_MARGIN_TXRX 0x00fc0000 +#define AR5K_PHY_GAIN_2GHZ_MARGIN_TXRX_S 18 +#define AR5K_PHY_GAIN_2GHZ_INI_5111 0x6480416c diff -puN /dev/null drivers/net/wireless/ath5k/regdom.c --- /dev/null +++ a/drivers/net/wireless/ath5k/regdom.c @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2004, 2005 Reyk Floeter + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Basic regulation domain extensions for the IEEE 802.11 stack + */ + +#include +#include + +#include "regdom.h" + +static const struct ath5k_regdommap { + enum ath5k_regdom dmn; + enum ath5k_regdom dmn5; + enum ath5k_regdom dmn2; +} r_map[] = { + { DMN_DEFAULT, DMN_DEBUG, DMN_DEBUG }, + { DMN_NULL_WORLD, DMN_NULL, DMN_WORLD }, + { DMN_NULL_ETSIB, DMN_NULL, DMN_ETSIB }, + { DMN_NULL_ETSIC, DMN_NULL, DMN_ETSIC }, + { DMN_FCC1_FCCA, DMN_FCC1, DMN_FCCA }, + { DMN_FCC1_WORLD, DMN_FCC1, DMN_WORLD }, + { DMN_FCC2_FCCA, DMN_FCC2, DMN_FCCA }, + { DMN_FCC2_WORLD, DMN_FCC2, DMN_WORLD }, + { DMN_FCC2_ETSIC, DMN_FCC2, DMN_ETSIC }, + { DMN_FRANCE_NULL, DMN_ETSI3, DMN_ETSI3 }, + { DMN_FCC3_FCCA, DMN_FCC3, DMN_WORLD }, + { DMN_ETSI1_WORLD, DMN_ETSI1, DMN_WORLD }, + { DMN_ETSI3_ETSIA, DMN_ETSI3, DMN_WORLD }, + { DMN_ETSI2_WORLD, DMN_ETSI2, DMN_WORLD }, + { DMN_ETSI3_WORLD, DMN_ETSI3, DMN_WORLD }, + { DMN_ETSI4_WORLD, DMN_ETSI4, DMN_WORLD }, + { DMN_ETSI4_ETSIC, DMN_ETSI4, DMN_ETSIC }, + { DMN_ETSI5_WORLD, DMN_ETSI5, DMN_WORLD }, + { DMN_ETSI6_WORLD, DMN_ETSI6, DMN_WORLD }, + { DMN_ETSI_NULL, DMN_ETSI1, DMN_ETSI1 }, + { DMN_MKK1_MKKA, DMN_MKK1, DMN_MKKA }, + { DMN_MKK1_MKKB, DMN_MKK1, DMN_MKKA }, + { DMN_APL4_WORLD, DMN_APL4, DMN_WORLD }, + { DMN_MKK2_MKKA, DMN_MKK2, DMN_MKKA }, + { DMN_APL_NULL, DMN_APL1, DMN_NULL }, + { DMN_APL2_WORLD, DMN_APL2, DMN_WORLD }, + { DMN_APL2_APLC, DMN_APL2, DMN_WORLD }, + { DMN_APL3_WORLD, DMN_APL3, DMN_WORLD }, + { DMN_MKK1_FCCA, DMN_MKK1, DMN_FCCA }, + { DMN_APL2_APLD, DMN_APL2, DMN_APLD }, + { DMN_MKK1_MKKA1, DMN_MKK1, DMN_MKKA }, + { DMN_MKK1_MKKA2, DMN_MKK1, DMN_MKKA }, + { DMN_APL1_WORLD, DMN_APL1, DMN_WORLD }, + { DMN_APL1_FCCA, DMN_APL1, DMN_FCCA }, + { DMN_APL1_APLA, DMN_APL1, DMN_WORLD }, + { DMN_APL1_ETSIC, DMN_APL1, DMN_ETSIC }, + { DMN_APL2_ETSIC, DMN_APL2, DMN_ETSIC }, + { DMN_APL5_WORLD, DMN_APL5, DMN_WORLD }, + { DMN_WOR0_WORLD, DMN_WORLD, DMN_WORLD }, + { DMN_WOR1_WORLD, DMN_WORLD, DMN_WORLD }, + { DMN_WOR2_WORLD, DMN_WORLD, DMN_WORLD }, + { DMN_WOR3_WORLD, DMN_WORLD, DMN_WORLD }, + { DMN_WOR4_WORLD, DMN_WORLD, DMN_WORLD }, + { DMN_WOR5_ETSIC, DMN_WORLD, DMN_WORLD }, + { DMN_WOR01_WORLD, DMN_WORLD, DMN_WORLD }, + { DMN_WOR02_WORLD, DMN_WORLD, DMN_WORLD }, + { DMN_EU1_WORLD, DMN_ETSI1, DMN_WORLD }, + { DMN_WOR9_WORLD, DMN_WORLD, DMN_WORLD }, + { DMN_WORA_WORLD, DMN_WORLD, DMN_WORLD }, +}; + +enum ath5k_regdom ath5k_regdom2flag(enum ath5k_regdom dmn, u16 mhz) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(r_map); i++) { + if (r_map[i].dmn == dmn) { + if (mhz >= 2000 && mhz <= 3000) + return r_map[i].dmn2; + if (mhz >= IEEE80211_CHANNELS_5GHZ_MIN && + mhz <= IEEE80211_CHANNELS_5GHZ_MAX) + return r_map[i].dmn5; + } + } + + return DMN_DEBUG; +} + +u16 ath5k_regdom_from_ieee(enum ath5k_regdom ieee) +{ + u32 regdomain = (u32)ieee; + + /* + * Use the default regulation domain if the value is empty + * or not supported by the net80211 regulation code. + */ + if (ath5k_regdom2flag(regdomain, IEEE80211_CHANNELS_5GHZ_MIN) == + DMN_DEBUG) + return (u16)AR5K_TUNE_REGDOMAIN; + + /* It is supported, just return the value */ + return regdomain; +} + +enum ath5k_regdom ath5k_regdom_to_ieee(u16 regdomain) +{ + enum ath5k_regdom ieee = (enum ath5k_regdom)regdomain; + + return ieee; +} + diff -puN /dev/null drivers/net/wireless/ath5k/regdom.h --- /dev/null +++ a/drivers/net/wireless/ath5k/regdom.h @@ -0,0 +1,500 @@ +/* + * Copyright (c) 2004, 2005 Reyk Floeter + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _IEEE80211_REGDOMAIN_H_ +#define _IEEE80211_REGDOMAIN_H_ + +#include + +/* Default regulation domain if stored value EEPROM value is invalid */ +#define AR5K_TUNE_REGDOMAIN DMN_FCC2_FCCA /* Canada */ +#define AR5K_TUNE_CTRY CTRY_DEFAULT + + +enum ath5k_regdom { + DMN_DEFAULT = 0x00, + DMN_NULL_WORLD = 0x03, + DMN_NULL_ETSIB = 0x07, + DMN_NULL_ETSIC = 0x08, + DMN_FCC1_FCCA = 0x10, + DMN_FCC1_WORLD = 0x11, + DMN_FCC2_FCCA = 0x20, + DMN_FCC2_WORLD = 0x21, + DMN_FCC2_ETSIC = 0x22, + DMN_FRANCE_NULL = 0x31, + DMN_FCC3_FCCA = 0x3A, + DMN_ETSI1_WORLD = 0x37, + DMN_ETSI3_ETSIA = 0x32, + DMN_ETSI2_WORLD = 0x35, + DMN_ETSI3_WORLD = 0x36, + DMN_ETSI4_WORLD = 0x30, + DMN_ETSI4_ETSIC = 0x38, + DMN_ETSI5_WORLD = 0x39, + DMN_ETSI6_WORLD = 0x34, + DMN_ETSI_NULL = 0x33, + DMN_MKK1_MKKA = 0x40, + DMN_MKK1_MKKB = 0x41, + DMN_APL4_WORLD = 0x42, + DMN_MKK2_MKKA = 0x43, + DMN_APL_NULL = 0x44, + DMN_APL2_WORLD = 0x45, + DMN_APL2_APLC = 0x46, + DMN_APL3_WORLD = 0x47, + DMN_MKK1_FCCA = 0x48, + DMN_APL2_APLD = 0x49, + DMN_MKK1_MKKA1 = 0x4A, + DMN_MKK1_MKKA2 = 0x4B, + DMN_APL1_WORLD = 0x52, + DMN_APL1_FCCA = 0x53, + DMN_APL1_APLA = 0x54, + DMN_APL1_ETSIC = 0x55, + DMN_APL2_ETSIC = 0x56, + DMN_APL5_WORLD = 0x58, + DMN_WOR0_WORLD = 0x60, + DMN_WOR1_WORLD = 0x61, + DMN_WOR2_WORLD = 0x62, + DMN_WOR3_WORLD = 0x63, + DMN_WOR4_WORLD = 0x64, + DMN_WOR5_ETSIC = 0x65, + DMN_WOR01_WORLD = 0x66, + DMN_WOR02_WORLD = 0x67, + DMN_EU1_WORLD = 0x68, + DMN_WOR9_WORLD = 0x69, + DMN_WORA_WORLD = 0x6A, + + DMN_APL1 = 0xf0000001, + DMN_APL2 = 0xf0000002, + DMN_APL3 = 0xf0000004, + DMN_APL4 = 0xf0000008, + DMN_APL5 = 0xf0000010, + DMN_ETSI1 = 0xf0000020, + DMN_ETSI2 = 0xf0000040, + DMN_ETSI3 = 0xf0000080, + DMN_ETSI4 = 0xf0000100, + DMN_ETSI5 = 0xf0000200, + DMN_ETSI6 = 0xf0000400, + DMN_ETSIA = 0xf0000800, + DMN_ETSIB = 0xf0001000, + DMN_ETSIC = 0xf0002000, + DMN_FCC1 = 0xf0004000, + DMN_FCC2 = 0xf0008000, + DMN_FCC3 = 0xf0010000, + DMN_FCCA = 0xf0020000, + DMN_APLD = 0xf0040000, + DMN_MKK1 = 0xf0080000, + DMN_MKK2 = 0xf0100000, + DMN_MKKA = 0xf0200000, + DMN_NULL = 0xf0400000, + DMN_WORLD = 0xf0800000, + DMN_DEBUG = 0xf1000000 /* used for debugging */ +}; + +#define IEEE80211_DMN(_d) ((_d) & ~0xf0000000) + +enum ath5k_countrycode { + CTRY_DEFAULT = 0, /* Default domain (NA) */ + CTRY_ALBANIA = 8, /* Albania */ + CTRY_ALGERIA = 12, /* Algeria */ + CTRY_ARGENTINA = 32, /* Argentina */ + CTRY_ARMENIA = 51, /* Armenia */ + CTRY_AUSTRALIA = 36, /* Australia */ + CTRY_AUSTRIA = 40, /* Austria */ + CTRY_AZERBAIJAN = 31, /* Azerbaijan */ + CTRY_BAHRAIN = 48, /* Bahrain */ + CTRY_BELARUS = 112, /* Belarus */ + CTRY_BELGIUM = 56, /* Belgium */ + CTRY_BELIZE = 84, /* Belize */ + CTRY_BOLIVIA = 68, /* Bolivia */ + CTRY_BRAZIL = 76, /* Brazil */ + CTRY_BRUNEI_DARUSSALAM = 96, /* Brunei Darussalam */ + CTRY_BULGARIA = 100, /* Bulgaria */ + CTRY_CANADA = 124, /* Canada */ + CTRY_CHILE = 152, /* Chile */ + CTRY_CHINA = 156, /* People's Republic of China */ + CTRY_COLOMBIA = 170, /* Colombia */ + CTRY_COSTA_RICA = 188, /* Costa Rica */ + CTRY_CROATIA = 191, /* Croatia */ + CTRY_CYPRUS = 196, /* Cyprus */ + CTRY_CZECH = 203, /* Czech Republic */ + CTRY_DENMARK = 208, /* Denmark */ + CTRY_DOMINICAN_REPUBLIC = 214, /* Dominican Republic */ + CTRY_ECUADOR = 218, /* Ecuador */ + CTRY_EGYPT = 818, /* Egypt */ + CTRY_EL_SALVADOR = 222, /* El Salvador */ + CTRY_ESTONIA = 233, /* Estonia */ + CTRY_FAEROE_ISLANDS = 234, /* Faeroe Islands */ + CTRY_FINLAND = 246, /* Finland */ + CTRY_FRANCE = 250, /* France */ + CTRY_FRANCE2 = 255, /* France2 */ + CTRY_GEORGIA = 268, /* Georgia */ + CTRY_GERMANY = 276, /* Germany */ + CTRY_GREECE = 300, /* Greece */ + CTRY_GUATEMALA = 320, /* Guatemala */ + CTRY_HONDURAS = 340, /* Honduras */ + CTRY_HONG_KONG = 344, /* Hong Kong S.A.R., P.R.C. */ + CTRY_HUNGARY = 348, /* Hungary */ + CTRY_ICELAND = 352, /* Iceland */ + CTRY_INDIA = 356, /* India */ + CTRY_INDONESIA = 360, /* Indonesia */ + CTRY_IRAN = 364, /* Iran */ + CTRY_IRAQ = 368, /* Iraq */ + CTRY_IRELAND = 372, /* Ireland */ + CTRY_ISRAEL = 376, /* Israel */ + CTRY_ITALY = 380, /* Italy */ + CTRY_JAMAICA = 388, /* Jamaica */ + CTRY_JAPAN = 392, /* Japan */ + CTRY_JAPAN1 = 393, /* Japan (JP1) */ + CTRY_JAPAN2 = 394, /* Japan (JP0) */ + CTRY_JAPAN3 = 395, /* Japan (JP1-1) */ + CTRY_JAPAN4 = 396, /* Japan (JE1) */ + CTRY_JAPAN5 = 397, /* Japan (JE2) */ + CTRY_JORDAN = 400, /* Jordan */ + CTRY_KAZAKHSTAN = 398, /* Kazakhstan */ + CTRY_KENYA = 404, /* Kenya */ + CTRY_KOREA_NORTH = 408, /* North Korea */ + CTRY_KOREA_ROC = 410, /* South Korea */ + CTRY_KOREA_ROC2 = 411, /* South Korea */ + CTRY_KUWAIT = 414, /* Kuwait */ + CTRY_LATVIA = 428, /* Latvia */ + CTRY_LEBANON = 422, /* Lebanon */ + CTRY_LIBYA = 434, /* Libya */ + CTRY_LIECHTENSTEIN = 438, /* Liechtenstein */ + CTRY_LITHUANIA = 440, /* Lithuania */ + CTRY_LUXEMBOURG = 442, /* Luxembourg */ + CTRY_MACAU = 446, /* Macau */ + CTRY_MACEDONIA = 807, /* Republic of Macedonia */ + CTRY_MALAYSIA = 458, /* Malaysia */ + CTRY_MEXICO = 484, /* Mexico */ + CTRY_MONACO = 492, /* Principality of Monaco */ + CTRY_MOROCCO = 504, /* Morocco */ + CTRY_NETHERLANDS = 528, /* Netherlands */ + CTRY_NEW_ZEALAND = 554, /* New Zealand */ + CTRY_NICARAGUA = 558, /* Nicaragua */ + CTRY_NORWAY = 578, /* Norway */ + CTRY_OMAN = 512, /* Oman */ + CTRY_PAKISTAN = 586, /* Islamic Republic of Pakistan */ + CTRY_PANAMA = 591, /* Panama */ + CTRY_PARAGUAY = 600, /* Paraguay */ + CTRY_PERU = 604, /* Peru */ + CTRY_PHILIPPINES = 608, /* Republic of the Philippines */ + CTRY_POLAND = 616, /* Poland */ + CTRY_PORTUGAL = 620, /* Portugal */ + CTRY_PUERTO_RICO = 630, /* Puerto Rico */ + CTRY_QATAR = 634, /* Qatar */ + CTRY_ROMANIA = 642, /* Romania */ + CTRY_RUSSIA = 643, /* Russia */ + CTRY_SAUDI_ARABIA = 682, /* Saudi Arabia */ + CTRY_SINGAPORE = 702, /* Singapore */ + CTRY_SLOVAKIA = 703, /* Slovak Republic */ + CTRY_SLOVENIA = 705, /* Slovenia */ + CTRY_SOUTH_AFRICA = 710, /* South Africa */ + CTRY_SPAIN = 724, /* Spain */ + CTRY_SRI_LANKA = 728, /* Sri Lanka */ + CTRY_SWEDEN = 752, /* Sweden */ + CTRY_SWITZERLAND = 756, /* Switzerland */ + CTRY_SYRIA = 760, /* Syria */ + CTRY_TAIWAN = 158, /* Taiwan */ + CTRY_THAILAND = 764, /* Thailand */ + CTRY_TRINIDAD_Y_TOBAGO = 780, /* Trinidad y Tobago */ + CTRY_TUNISIA = 788, /* Tunisia */ + CTRY_TURKEY = 792, /* Turkey */ + CTRY_UAE = 784, /* U.A.E. */ + CTRY_UKRAINE = 804, /* Ukraine */ + CTRY_UNITED_KINGDOM = 826, /* United Kingdom */ + CTRY_UNITED_STATES = 840, /* United States */ + CTRY_URUGUAY = 858, /* Uruguay */ + CTRY_UZBEKISTAN = 860, /* Uzbekistan */ + CTRY_VENEZUELA = 862, /* Venezuela */ + CTRY_VIET_NAM = 704, /* Viet Nam */ + CTRY_YEMEN = 887, /* Yemen */ + CTRY_ZIMBABWE = 716, /* Zimbabwe */ +}; + +#define IEEE80211_CHANNELS_2GHZ_MIN 2412 /* 2GHz channel 1 */ +#define IEEE80211_CHANNELS_2GHZ_MAX 2732 /* 2GHz channel 26 */ +#define IEEE80211_CHANNELS_5GHZ_MIN 5005 /* 5GHz channel 1 */ +#define IEEE80211_CHANNELS_5GHZ_MAX 6100 /* 5GHz channel 220 */ + +struct ath5k_regchannel { + u16 chan; + enum ath5k_regdom domain; + u32 mode; +}; + +#define IEEE80211_CHANNELS_2GHZ { \ +/*2412*/ { 1, DMN_APLD, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2417*/ { 2, DMN_APLD, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2422*/ { 3, DMN_APLD, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2427*/ { 4, DMN_APLD, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2432*/ { 5, DMN_APLD, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2437*/ { 6, DMN_APLD, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2442*/ { 7, DMN_APLD, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2447*/ { 8, DMN_APLD, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2452*/ { 9, DMN_APLD, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2457*/ { 10, DMN_APLD, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2462*/ { 11, DMN_APLD, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2467*/ { 12, DMN_APLD, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2472*/ { 13, DMN_APLD, CHANNEL_CCK|CHANNEL_OFDM }, \ + \ +/*2432*/ { 5, DMN_ETSIB, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2437*/ { 6, DMN_ETSIB, CHANNEL_CCK|CHANNEL_OFDM|CHANNEL_TURBO }, \ +/*2442*/ { 7, DMN_ETSIB, CHANNEL_CCK|CHANNEL_OFDM }, \ + \ +/*2412*/ { 1, DMN_ETSIC, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2417*/ { 2, DMN_ETSIC, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2422*/ { 3, DMN_ETSIC, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2427*/ { 4, DMN_ETSIC, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2432*/ { 5, DMN_ETSIC, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2437*/ { 6, DMN_ETSIC, CHANNEL_CCK|CHANNEL_OFDM|CHANNEL_TURBO }, \ +/*2442*/ { 7, DMN_ETSIC, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2447*/ { 8, DMN_ETSIC, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2452*/ { 9, DMN_ETSIC, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2457*/ { 10, DMN_ETSIC, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2462*/ { 11, DMN_ETSIC, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2467*/ { 12, DMN_ETSIC, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2472*/ { 13, DMN_ETSIC, CHANNEL_CCK|CHANNEL_OFDM }, \ + \ +/*2412*/ { 1, DMN_FCCA, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2417*/ { 2, DMN_FCCA, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2422*/ { 3, DMN_FCCA, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2427*/ { 4, DMN_FCCA, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2432*/ { 5, DMN_FCCA, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2437*/ { 6, DMN_FCCA, CHANNEL_CCK|CHANNEL_OFDM|CHANNEL_TURBO }, \ +/*2442*/ { 7, DMN_FCCA, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2447*/ { 8, DMN_FCCA, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2452*/ { 9, DMN_FCCA, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2457*/ { 10, DMN_FCCA, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2462*/ { 11, DMN_FCCA, CHANNEL_CCK|CHANNEL_OFDM }, \ + \ +/*2412*/ { 1, DMN_MKKA, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2417*/ { 2, DMN_MKKA, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2422*/ { 3, DMN_MKKA, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2427*/ { 4, DMN_MKKA, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2432*/ { 5, DMN_MKKA, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2437*/ { 6, DMN_MKKA, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2442*/ { 7, DMN_MKKA, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2447*/ { 8, DMN_MKKA, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2452*/ { 9, DMN_MKKA, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2457*/ { 10, DMN_MKKA, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2462*/ { 11, DMN_MKKA, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2467*/ { 12, DMN_MKKA, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2472*/ { 13, DMN_MKKA, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2484*/ { 14, DMN_MKKA, CHANNEL_CCK }, \ + \ +/*2412*/ { 1, DMN_WORLD, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2417*/ { 2, DMN_WORLD, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2422*/ { 3, DMN_WORLD, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2427*/ { 4, DMN_WORLD, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2432*/ { 5, DMN_WORLD, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2437*/ { 6, DMN_WORLD, CHANNEL_CCK|CHANNEL_OFDM|CHANNEL_TURBO }, \ +/*2442*/ { 7, DMN_WORLD, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2447*/ { 8, DMN_WORLD, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2452*/ { 9, DMN_WORLD, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2457*/ { 10, DMN_WORLD, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2462*/ { 11, DMN_WORLD, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2467*/ { 12, DMN_WORLD, CHANNEL_CCK|CHANNEL_OFDM }, \ +/*2472*/ { 13, DMN_WORLD, CHANNEL_CCK|CHANNEL_OFDM }, \ +} + +#define IEEE80211_CHANNELS_5GHZ { \ +/*5745*/ { 149, DMN_APL1, CHANNEL_OFDM }, \ +/*5765*/ { 153, DMN_APL1, CHANNEL_OFDM }, \ +/*5785*/ { 157, DMN_APL1, CHANNEL_OFDM }, \ +/*5805*/ { 161, DMN_APL1, CHANNEL_OFDM }, \ +/*5825*/ { 165, DMN_APL1, CHANNEL_OFDM }, \ + \ +/*5745*/ { 149, DMN_APL2, CHANNEL_OFDM }, \ +/*5765*/ { 153, DMN_APL2, CHANNEL_OFDM }, \ +/*5785*/ { 157, DMN_APL2, CHANNEL_OFDM }, \ +/*5805*/ { 161, DMN_APL2, CHANNEL_OFDM }, \ + \ +/*5280*/ { 56, DMN_APL3, CHANNEL_OFDM }, \ +/*5300*/ { 60, DMN_APL3, CHANNEL_OFDM }, \ +/*5320*/ { 64, DMN_APL3, CHANNEL_OFDM }, \ +/*5745*/ { 149, DMN_APL3, CHANNEL_OFDM }, \ +/*5765*/ { 153, DMN_APL3, CHANNEL_OFDM }, \ +/*5785*/ { 157, DMN_APL3, CHANNEL_OFDM }, \ +/*5805*/ { 161, DMN_APL3, CHANNEL_OFDM }, \ + \ +/*5180*/ { 36, DMN_APL4, CHANNEL_OFDM }, \ +/*5200*/ { 40, DMN_APL4, CHANNEL_OFDM }, \ +/*5220*/ { 44, DMN_APL4, CHANNEL_OFDM }, \ +/*5240*/ { 48, DMN_APL4, CHANNEL_OFDM }, \ +/*5745*/ { 149, DMN_APL4, CHANNEL_OFDM }, \ +/*5765*/ { 153, DMN_APL4, CHANNEL_OFDM }, \ +/*5785*/ { 157, DMN_APL4, CHANNEL_OFDM }, \ +/*5805*/ { 161, DMN_APL4, CHANNEL_OFDM }, \ +/*5825*/ { 165, DMN_APL4, CHANNEL_OFDM }, \ + \ +/*5745*/ { 149, DMN_APL5, CHANNEL_OFDM }, \ +/*5765*/ { 153, DMN_APL5, CHANNEL_OFDM }, \ +/*5785*/ { 157, DMN_APL5, CHANNEL_OFDM }, \ +/*5805*/ { 161, DMN_APL5, CHANNEL_OFDM }, \ +/*5825*/ { 165, DMN_APL5, CHANNEL_OFDM }, \ + \ +/*5180*/ { 36, DMN_ETSI1, CHANNEL_OFDM }, \ +/*5200*/ { 40, DMN_ETSI1, CHANNEL_OFDM }, \ +/*5220*/ { 44, DMN_ETSI1, CHANNEL_OFDM }, \ +/*5240*/ { 48, DMN_ETSI1, CHANNEL_OFDM }, \ +/*5260*/ { 52, DMN_ETSI1, CHANNEL_OFDM }, \ +/*5280*/ { 56, DMN_ETSI1, CHANNEL_OFDM }, \ +/*5300*/ { 60, DMN_ETSI1, CHANNEL_OFDM }, \ +/*5320*/ { 64, DMN_ETSI1, CHANNEL_OFDM }, \ +/*5500*/ { 100, DMN_ETSI1, CHANNEL_OFDM }, \ +/*5520*/ { 104, DMN_ETSI1, CHANNEL_OFDM }, \ +/*5540*/ { 108, DMN_ETSI1, CHANNEL_OFDM }, \ +/*5560*/ { 112, DMN_ETSI1, CHANNEL_OFDM }, \ +/*5580*/ { 116, DMN_ETSI1, CHANNEL_OFDM }, \ +/*5600*/ { 120, DMN_ETSI1, CHANNEL_OFDM }, \ +/*5620*/ { 124, DMN_ETSI1, CHANNEL_OFDM }, \ +/*5640*/ { 128, DMN_ETSI1, CHANNEL_OFDM }, \ +/*5660*/ { 132, DMN_ETSI1, CHANNEL_OFDM }, \ +/*5680*/ { 136, DMN_ETSI1, CHANNEL_OFDM }, \ +/*5700*/ { 140, DMN_ETSI1, CHANNEL_OFDM }, \ + \ +/*5180*/ { 36, DMN_ETSI2, CHANNEL_OFDM }, \ +/*5200*/ { 40, DMN_ETSI2, CHANNEL_OFDM }, \ +/*5220*/ { 44, DMN_ETSI2, CHANNEL_OFDM }, \ +/*5240*/ { 48, DMN_ETSI2, CHANNEL_OFDM }, \ + \ +/*5180*/ { 36, DMN_ETSI3, CHANNEL_OFDM }, \ +/*5200*/ { 40, DMN_ETSI3, CHANNEL_OFDM }, \ +/*5220*/ { 44, DMN_ETSI3, CHANNEL_OFDM }, \ +/*5240*/ { 48, DMN_ETSI3, CHANNEL_OFDM }, \ +/*5260*/ { 52, DMN_ETSI3, CHANNEL_OFDM }, \ +/*5280*/ { 56, DMN_ETSI3, CHANNEL_OFDM }, \ +/*5300*/ { 60, DMN_ETSI3, CHANNEL_OFDM }, \ +/*5320*/ { 64, DMN_ETSI3, CHANNEL_OFDM }, \ + \ +/*5180*/ { 36, DMN_ETSI4, CHANNEL_OFDM }, \ +/*5200*/ { 40, DMN_ETSI4, CHANNEL_OFDM }, \ +/*5220*/ { 44, DMN_ETSI4, CHANNEL_OFDM }, \ +/*5240*/ { 48, DMN_ETSI4, CHANNEL_OFDM }, \ +/*5260*/ { 52, DMN_ETSI4, CHANNEL_OFDM }, \ +/*5280*/ { 56, DMN_ETSI4, CHANNEL_OFDM }, \ +/*5300*/ { 60, DMN_ETSI4, CHANNEL_OFDM }, \ +/*5320*/ { 64, DMN_ETSI4, CHANNEL_OFDM }, \ + \ +/*5180*/ { 36, DMN_ETSI5, CHANNEL_OFDM }, \ +/*5200*/ { 40, DMN_ETSI5, CHANNEL_OFDM }, \ +/*5220*/ { 44, DMN_ETSI5, CHANNEL_OFDM }, \ +/*5240*/ { 48, DMN_ETSI5, CHANNEL_OFDM }, \ + \ +/*5180*/ { 36, DMN_ETSI6, CHANNEL_OFDM }, \ +/*5200*/ { 40, DMN_ETSI6, CHANNEL_OFDM }, \ +/*5220*/ { 44, DMN_ETSI6, CHANNEL_OFDM }, \ +/*5240*/ { 48, DMN_ETSI6, CHANNEL_OFDM }, \ +/*5260*/ { 52, DMN_ETSI6, CHANNEL_OFDM }, \ +/*5280*/ { 56, DMN_ETSI6, CHANNEL_OFDM }, \ +/*5500*/ { 100, DMN_ETSI6, CHANNEL_OFDM }, \ +/*5520*/ { 104, DMN_ETSI6, CHANNEL_OFDM }, \ +/*5540*/ { 108, DMN_ETSI6, CHANNEL_OFDM }, \ +/*5560*/ { 112, DMN_ETSI6, CHANNEL_OFDM }, \ +/*5580*/ { 116, DMN_ETSI6, CHANNEL_OFDM }, \ +/*5600*/ { 120, DMN_ETSI6, CHANNEL_OFDM }, \ +/*5620*/ { 124, DMN_ETSI6, CHANNEL_OFDM }, \ +/*5640*/ { 128, DMN_ETSI6, CHANNEL_OFDM }, \ +/*5660*/ { 132, DMN_ETSI6, CHANNEL_OFDM }, \ +/*5680*/ { 136, DMN_ETSI6, CHANNEL_OFDM }, \ +/*5700*/ { 140, DMN_ETSI6, CHANNEL_OFDM }, \ + \ +/*5180*/ { 36, DMN_FCC1, CHANNEL_OFDM }, \ +/*5200*/ { 40, DMN_FCC1, CHANNEL_OFDM }, \ +/*5210*/ { 42, DMN_FCC1, CHANNEL_OFDM|CHANNEL_TURBO }, \ +/*5220*/ { 44, DMN_FCC1, CHANNEL_OFDM }, \ +/*5240*/ { 48, DMN_FCC1, CHANNEL_OFDM }, \ +/*5250*/ { 50, DMN_FCC1, CHANNEL_OFDM|CHANNEL_TURBO }, \ +/*5260*/ { 52, DMN_FCC1, CHANNEL_OFDM }, \ +/*5280*/ { 56, DMN_FCC1, CHANNEL_OFDM }, \ +/*5290*/ { 58, DMN_FCC1, CHANNEL_OFDM|CHANNEL_TURBO }, \ +/*5300*/ { 60, DMN_FCC1, CHANNEL_OFDM }, \ +/*5320*/ { 64, DMN_FCC1, CHANNEL_OFDM }, \ +/*5745*/ { 149, DMN_FCC1, CHANNEL_OFDM }, \ +/*5760*/ { 152, DMN_FCC1, CHANNEL_OFDM|CHANNEL_TURBO }, \ +/*5765*/ { 153, DMN_FCC1, CHANNEL_OFDM }, \ +/*5785*/ { 157, DMN_FCC1, CHANNEL_OFDM }, \ +/*5800*/ { 160, DMN_FCC1, CHANNEL_OFDM|CHANNEL_TURBO }, \ +/*5805*/ { 161, DMN_FCC1, CHANNEL_OFDM }, \ +/*5825*/ { 165, DMN_FCC1, CHANNEL_OFDM }, \ + \ +/*5180*/ { 36, DMN_FCC2, CHANNEL_OFDM }, \ +/*5200*/ { 40, DMN_FCC2, CHANNEL_OFDM }, \ +/*5220*/ { 44, DMN_FCC2, CHANNEL_OFDM }, \ +/*5240*/ { 48, DMN_FCC2, CHANNEL_OFDM }, \ +/*5260*/ { 52, DMN_FCC2, CHANNEL_OFDM }, \ +/*5280*/ { 56, DMN_FCC2, CHANNEL_OFDM }, \ +/*5300*/ { 60, DMN_FCC2, CHANNEL_OFDM }, \ +/*5320*/ { 64, DMN_FCC2, CHANNEL_OFDM }, \ +/*5745*/ { 149, DMN_FCC2, CHANNEL_OFDM }, \ +/*5765*/ { 153, DMN_FCC2, CHANNEL_OFDM }, \ +/*5785*/ { 157, DMN_FCC2, CHANNEL_OFDM }, \ +/*5805*/ { 161, DMN_FCC2, CHANNEL_OFDM }, \ +/*5825*/ { 165, DMN_FCC2, CHANNEL_OFDM }, \ + \ +/*5180*/ { 36, DMN_FCC3, CHANNEL_OFDM }, \ +/*5200*/ { 40, DMN_FCC3, CHANNEL_OFDM }, \ +/*5210*/ { 42, DMN_FCC3, CHANNEL_OFDM|CHANNEL_TURBO }, \ +/*5220*/ { 44, DMN_FCC3, CHANNEL_OFDM }, \ +/*5240*/ { 48, DMN_FCC3, CHANNEL_OFDM }, \ +/*5250*/ { 50, DMN_FCC3, CHANNEL_OFDM|CHANNEL_TURBO }, \ +/*5260*/ { 52, DMN_FCC3, CHANNEL_OFDM }, \ +/*5280*/ { 56, DMN_FCC3, CHANNEL_OFDM }, \ +/*5290*/ { 58, DMN_FCC3, CHANNEL_OFDM|CHANNEL_TURBO }, \ +/*5300*/ { 60, DMN_FCC3, CHANNEL_OFDM }, \ +/*5320*/ { 64, DMN_FCC3, CHANNEL_OFDM }, \ +/*5500*/ { 100, DMN_FCC3, CHANNEL_OFDM }, \ +/*5520*/ { 104, DMN_FCC3, CHANNEL_OFDM }, \ +/*5540*/ { 108, DMN_FCC3, CHANNEL_OFDM }, \ +/*5560*/ { 112, DMN_FCC3, CHANNEL_OFDM }, \ +/*5580*/ { 116, DMN_FCC3, CHANNEL_OFDM }, \ +/*5600*/ { 120, DMN_FCC3, CHANNEL_OFDM }, \ +/*5620*/ { 124, DMN_FCC3, CHANNEL_OFDM }, \ +/*5640*/ { 128, DMN_FCC3, CHANNEL_OFDM }, \ +/*5660*/ { 132, DMN_FCC3, CHANNEL_OFDM }, \ +/*5680*/ { 136, DMN_FCC3, CHANNEL_OFDM }, \ +/*5700*/ { 140, DMN_FCC3, CHANNEL_OFDM }, \ +/*5745*/ { 149, DMN_FCC3, CHANNEL_OFDM }, \ +/*5760*/ { 152, DMN_FCC3, CHANNEL_OFDM|CHANNEL_TURBO }, \ +/*5765*/ { 153, DMN_FCC3, CHANNEL_OFDM }, \ +/*5785*/ { 157, DMN_FCC3, CHANNEL_OFDM }, \ +/*5800*/ { 160, DMN_FCC3, CHANNEL_OFDM|CHANNEL_TURBO }, \ +/*5805*/ { 161, DMN_FCC3, CHANNEL_OFDM }, \ +/*5825*/ { 165, DMN_FCC3, CHANNEL_OFDM }, \ + \ +/*5170*/ { 34, DMN_MKK1, CHANNEL_OFDM }, \ +/*5190*/ { 38, DMN_MKK1, CHANNEL_OFDM }, \ +/*5210*/ { 42, DMN_MKK1, CHANNEL_OFDM }, \ +/*5230*/ { 46, DMN_MKK1, CHANNEL_OFDM }, \ + \ +/*5040*/ { 8, DMN_MKK2, CHANNEL_OFDM }, \ +/*5060*/ { 12, DMN_MKK2, CHANNEL_OFDM }, \ +/*5080*/ { 16, DMN_MKK2, CHANNEL_OFDM }, \ +/*5170*/ { 34, DMN_MKK2, CHANNEL_OFDM }, \ +/*5190*/ { 38, DMN_MKK2, CHANNEL_OFDM }, \ +/*5210*/ { 42, DMN_MKK2, CHANNEL_OFDM }, \ +/*5230*/ { 46, DMN_MKK2, CHANNEL_OFDM }, \ + \ +/*5180*/ { 36, DMN_WORLD, CHANNEL_OFDM }, \ +/*5200*/ { 40, DMN_WORLD, CHANNEL_OFDM }, \ +/*5220*/ { 44, DMN_WORLD, CHANNEL_OFDM }, \ +/*5240*/ { 48, DMN_WORLD, CHANNEL_OFDM }, \ +} + +enum ath5k_regdom ath5k_regdom2flag(enum ath5k_regdom, u16); +u16 ath5k_regdom_from_ieee(enum ath5k_regdom ieee); +enum ath5k_regdom ath5k_regdom_to_ieee(u16 regdomain); + +#endif diff -puN /dev/null drivers/net/wireless/zd1211rw-mac80211/Kconfig --- /dev/null +++ a/drivers/net/wireless/zd1211rw-mac80211/Kconfig @@ -0,0 +1,18 @@ +config ZD1211RW_MAC80211 + tristate "ZyDAS ZD1211/ZD1211B USB support (mac80211 stack)" + depends on USB && MAC80211 && WLAN_80211 && EXPERIMENTAL + depends on m || !ZD1211RW + select FW_LOADER + ---help--- + This is an experimental driver for the ZyDAS ZD1211/ZD1211B wireless + chip, present in many USB-wireless adapters. + + Device firmware is required alongside this driver. You can download + the firmware distribution from http://zd1211.ath.cx/get-firmware + +config ZD1211RW_MAC80211_DEBUG + bool "ZyDAS ZD1211 debugging" + depends on ZD1211RW_MAC80211 + ---help--- + ZD1211 debugging messages. Choosing Y will result in additional debug + messages being saved to your kernel logs, which may help debug any diff -puN /dev/null drivers/net/wireless/zd1211rw-mac80211/Makefile --- /dev/null +++ a/drivers/net/wireless/zd1211rw-mac80211/Makefile @@ -0,0 +1,10 @@ +obj-$(CONFIG_ZD1211RW_MAC80211) += zd1211rw-mac80211.o + +zd1211rw-mac80211-objs := zd_chip.o zd_mac.o \ + zd_rf_al2230.o zd_rf_rf2959.o \ + zd_rf_al7230b.o zd_rf_uw2453.o \ + zd_ieee80211.o zd_rf.o zd_usb.o + +ifeq ($(CONFIG_ZD1211RW_MAC80211_DEBUG),y) +EXTRA_CFLAGS += -DDEBUG +endif diff -puN /dev/null drivers/net/wireless/zd1211rw-mac80211/zd_chip.c --- /dev/null +++ a/drivers/net/wireless/zd1211rw-mac80211/zd_chip.c @@ -0,0 +1,1622 @@ +/* zd_chip.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* This file implements all the hardware specific functions for the ZD1211 + * and ZD1211B chips. Support for the ZD1211B was possible after Timothy + * Legge sent me a ZD1211B device. Thank you Tim. -- Uli + */ + +#include +#include + +#include "zd_def.h" +#include "zd_chip.h" +#include "zd_ieee80211.h" +#include "zd_mac.h" +#include "zd_rf.h" + +void zd_chip_init(struct zd_chip *chip, + struct ieee80211_hw *hw, + struct usb_interface *intf) +{ + memset(chip, 0, sizeof(*chip)); + mutex_init(&chip->mutex); + zd_usb_init(&chip->usb, hw, intf); + zd_rf_init(&chip->rf); +} + +void zd_chip_clear(struct zd_chip *chip) +{ + ZD_ASSERT(!mutex_is_locked(&chip->mutex)); + zd_usb_clear(&chip->usb); + zd_rf_clear(&chip->rf); + mutex_destroy(&chip->mutex); + ZD_MEMCLEAR(chip, sizeof(*chip)); +} + +static int scnprint_mac_oui(struct zd_chip *chip, char *buffer, size_t size) +{ + u8 *addr = zd_mac_get_perm_addr(zd_chip_to_mac(chip)); + return scnprintf(buffer, size, "%02x-%02x-%02x", + addr[0], addr[1], addr[2]); +} + +/* Prints an identifier line, which will support debugging. */ +static int scnprint_id(struct zd_chip *chip, char *buffer, size_t size) +{ + int i = 0; + + i = scnprintf(buffer, size, "zd1211%s chip ", + zd_chip_is_zd1211b(chip) ? "b" : ""); + i += zd_usb_scnprint_id(&chip->usb, buffer+i, size-i); + i += scnprintf(buffer+i, size-i, " "); + i += scnprint_mac_oui(chip, buffer+i, size-i); + i += scnprintf(buffer+i, size-i, " "); + i += zd_rf_scnprint_id(&chip->rf, buffer+i, size-i); + i += scnprintf(buffer+i, size-i, " pa%1x %c%c%c%c%c", chip->pa_type, + chip->patch_cck_gain ? 'g' : '-', + chip->patch_cr157 ? '7' : '-', + chip->patch_6m_band_edge ? '6' : '-', + chip->new_phy_layout ? 'N' : '-', + chip->al2230s_bit ? 'S' : '-'); + return i; +} + +static void print_id(struct zd_chip *chip) +{ + char buffer[80]; + + scnprint_id(chip, buffer, sizeof(buffer)); + buffer[sizeof(buffer)-1] = 0; + dev_info(zd_chip_dev(chip), "%s\n", buffer); +} + +static zd_addr_t inc_addr(zd_addr_t addr) +{ + u16 a = (u16)addr; + /* Control registers use byte addressing, but everything else uses word + * addressing. */ + if ((a & 0xf000) == CR_START) + a += 2; + else + a += 1; + return (zd_addr_t)a; +} + +/* Read a variable number of 32-bit values. Parameter count is not allowed to + * exceed USB_MAX_IOREAD32_COUNT. + */ +int zd_ioread32v_locked(struct zd_chip *chip, u32 *values, const zd_addr_t *addr, + unsigned int count) +{ + int r; + int i; + zd_addr_t *a16 = (zd_addr_t *)NULL; + u16 *v16; + unsigned int count16; + + if (count > USB_MAX_IOREAD32_COUNT) + return -EINVAL; + + /* Allocate a single memory block for values and addresses. */ + count16 = 2*count; + a16 = (zd_addr_t *)kmalloc(count16 * (sizeof(zd_addr_t) + sizeof(u16)), + GFP_KERNEL); + if (!a16) { + dev_dbg_f(zd_chip_dev(chip), + "error ENOMEM in allocation of a16\n"); + r = -ENOMEM; + goto out; + } + v16 = (u16 *)(a16 + count16); + + for (i = 0; i < count; i++) { + int j = 2*i; + /* We read the high word always first. */ + a16[j] = inc_addr(addr[i]); + a16[j+1] = addr[i]; + } + + r = zd_ioread16v_locked(chip, v16, a16, count16); + if (r) { + dev_dbg_f(zd_chip_dev(chip), + "error: zd_ioread16v_locked. Error number %d\n", r); + goto out; + } + + for (i = 0; i < count; i++) { + int j = 2*i; + values[i] = (v16[j] << 16) | v16[j+1]; + } + +out: + kfree((void *)a16); + return r; +} + +int _zd_iowrite32v_locked(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs, + unsigned int count) +{ + int i, j, r; + struct zd_ioreq16 *ioreqs16; + unsigned int count16; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + + if (count == 0) + return 0; + if (count > USB_MAX_IOWRITE32_COUNT) + return -EINVAL; + + /* Allocate a single memory block for values and addresses. */ + count16 = 2*count; + ioreqs16 = kmalloc(count16 * sizeof(struct zd_ioreq16), GFP_KERNEL); + if (!ioreqs16) { + r = -ENOMEM; + dev_dbg_f(zd_chip_dev(chip), + "error %d in ioreqs16 allocation\n", r); + goto out; + } + + for (i = 0; i < count; i++) { + j = 2*i; + /* We write the high word always first. */ + ioreqs16[j].value = ioreqs[i].value >> 16; + ioreqs16[j].addr = inc_addr(ioreqs[i].addr); + ioreqs16[j+1].value = ioreqs[i].value; + ioreqs16[j+1].addr = ioreqs[i].addr; + } + + r = zd_usb_iowrite16v(&chip->usb, ioreqs16, count16); +#ifdef DEBUG + if (r) { + dev_dbg_f(zd_chip_dev(chip), + "error %d in zd_usb_write16v\n", r); + } +#endif /* DEBUG */ +out: + kfree(ioreqs16); + return r; +} + +int zd_iowrite16a_locked(struct zd_chip *chip, + const struct zd_ioreq16 *ioreqs, unsigned int count) +{ + int r; + unsigned int i, j, t, max; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + for (i = 0; i < count; i += j + t) { + t = 0; + max = count-i; + if (max > USB_MAX_IOWRITE16_COUNT) + max = USB_MAX_IOWRITE16_COUNT; + for (j = 0; j < max; j++) { + if (!ioreqs[i+j].addr) { + t = 1; + break; + } + } + + r = zd_usb_iowrite16v(&chip->usb, &ioreqs[i], j); + if (r) { + dev_dbg_f(zd_chip_dev(chip), + "error zd_usb_iowrite16v. Error number %d\n", + r); + return r; + } + } + + return 0; +} + +/* Writes a variable number of 32 bit registers. The functions will split + * that in several USB requests. A split can be forced by inserting an IO + * request with an zero address field. + */ +int zd_iowrite32a_locked(struct zd_chip *chip, + const struct zd_ioreq32 *ioreqs, unsigned int count) +{ + int r; + unsigned int i, j, t, max; + + for (i = 0; i < count; i += j + t) { + t = 0; + max = count-i; + if (max > USB_MAX_IOWRITE32_COUNT) + max = USB_MAX_IOWRITE32_COUNT; + for (j = 0; j < max; j++) { + if (!ioreqs[i+j].addr) { + t = 1; + break; + } + } + + r = _zd_iowrite32v_locked(chip, &ioreqs[i], j); + if (r) { + dev_dbg_f(zd_chip_dev(chip), + "error _zd_iowrite32v_locked." + " Error number %d\n", r); + return r; + } + } + + return 0; +} + +int zd_ioread16(struct zd_chip *chip, zd_addr_t addr, u16 *value) +{ + int r; + + mutex_lock(&chip->mutex); + r = zd_ioread16_locked(chip, value, addr); + mutex_unlock(&chip->mutex); + return r; +} + +int zd_ioread32(struct zd_chip *chip, zd_addr_t addr, u32 *value) +{ + int r; + + mutex_lock(&chip->mutex); + r = zd_ioread32_locked(chip, value, addr); + mutex_unlock(&chip->mutex); + return r; +} + +int zd_iowrite16(struct zd_chip *chip, zd_addr_t addr, u16 value) +{ + int r; + + mutex_lock(&chip->mutex); + r = zd_iowrite16_locked(chip, value, addr); + mutex_unlock(&chip->mutex); + return r; +} + +int zd_iowrite32(struct zd_chip *chip, zd_addr_t addr, u32 value) +{ + int r; + + mutex_lock(&chip->mutex); + r = zd_iowrite32_locked(chip, value, addr); + mutex_unlock(&chip->mutex); + return r; +} + +int zd_ioread32v(struct zd_chip *chip, const zd_addr_t *addresses, + u32 *values, unsigned int count) +{ + int r; + + mutex_lock(&chip->mutex); + r = zd_ioread32v_locked(chip, values, addresses, count); + mutex_unlock(&chip->mutex); + return r; +} + +int zd_iowrite32a(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs, + unsigned int count) +{ + int r; + + mutex_lock(&chip->mutex); + r = zd_iowrite32a_locked(chip, ioreqs, count); + mutex_unlock(&chip->mutex); + return r; +} + +static int read_pod(struct zd_chip *chip, u8 *rf_type) +{ + int r; + u32 value; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = zd_ioread32_locked(chip, &value, E2P_POD); + if (r) + goto error; + dev_dbg_f(zd_chip_dev(chip), "E2P_POD %#010x\n", value); + + /* FIXME: AL2230 handling (Bit 7 in POD) */ + *rf_type = value & 0x0f; + chip->pa_type = (value >> 16) & 0x0f; + chip->patch_cck_gain = (value >> 8) & 0x1; + chip->patch_cr157 = (value >> 13) & 0x1; + chip->patch_6m_band_edge = (value >> 21) & 0x1; + chip->new_phy_layout = (value >> 31) & 0x1; + chip->al2230s_bit = (value >> 7) & 0x1; + chip->link_led = ((value >> 4) & 1) ? LED1 : LED2; + chip->supports_tx_led = 1; + if (value & (1 << 24)) { /* LED scenario */ + if (value & (1 << 29)) + chip->supports_tx_led = 0; + } + + dev_dbg_f(zd_chip_dev(chip), + "RF %s %#01x PA type %#01x patch CCK %d patch CR157 %d " + "patch 6M %d new PHY %d link LED%d tx led %d\n", + zd_rf_name(*rf_type), *rf_type, + chip->pa_type, chip->patch_cck_gain, + chip->patch_cr157, chip->patch_6m_band_edge, + chip->new_phy_layout, + chip->link_led == LED1 ? 1 : 2, + chip->supports_tx_led); + return 0; +error: + *rf_type = 0; + chip->pa_type = 0; + chip->patch_cck_gain = 0; + chip->patch_cr157 = 0; + chip->patch_6m_band_edge = 0; + chip->new_phy_layout = 0; + return r; +} + +/* MAC address: if custom mac addresses are to to be used CR_MAC_ADDR_P1 and + * CR_MAC_ADDR_P2 must be overwritten + */ +int zd_write_mac_addr(struct zd_chip *chip, const u8 *mac_addr) +{ + int r; + struct zd_ioreq32 reqs[2] = { + [0] = { .addr = CR_MAC_ADDR_P1 }, + [1] = { .addr = CR_MAC_ADDR_P2 }, + }; + DECLARE_MAC_BUF(mac); + + if (mac_addr) { + reqs[0].value = (mac_addr[3] << 24) + | (mac_addr[2] << 16) + | (mac_addr[1] << 8) + | mac_addr[0]; + reqs[1].value = (mac_addr[5] << 8) + | mac_addr[4]; + dev_dbg_f(zd_chip_dev(chip), + "mac addr %s\n", print_mac(mac, mac_addr)); + } else { + dev_dbg_f(zd_chip_dev(chip), "set NULL mac\n"); + } + + mutex_lock(&chip->mutex); + r = zd_iowrite32a_locked(chip, reqs, ARRAY_SIZE(reqs)); + mutex_unlock(&chip->mutex); + return r; +} + +int zd_read_regdomain(struct zd_chip *chip, u8 *regdomain) +{ + int r; + u32 value; + + mutex_lock(&chip->mutex); + r = zd_ioread32_locked(chip, &value, E2P_SUBID); + mutex_unlock(&chip->mutex); + if (r) + return r; + + *regdomain = value >> 16; + dev_dbg_f(zd_chip_dev(chip), "regdomain: %#04x\n", *regdomain); + + return 0; +} + +static int read_values(struct zd_chip *chip, u8 *values, size_t count, + zd_addr_t e2p_addr, u32 guard) +{ + int r; + int i; + u32 v; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + for (i = 0;;) { + r = zd_ioread32_locked(chip, &v, + (zd_addr_t)((u16)e2p_addr+i/2)); + if (r) + return r; + v -= guard; + if (i+4 < count) { + values[i++] = v; + values[i++] = v >> 8; + values[i++] = v >> 16; + values[i++] = v >> 24; + continue; + } + for (;i < count; i++) + values[i] = v >> (8*(i%3)); + return 0; + } +} + +static int read_pwr_cal_values(struct zd_chip *chip) +{ + return read_values(chip, chip->pwr_cal_values, + E2P_CHANNEL_COUNT, E2P_PWR_CAL_VALUE1, + 0); +} + +static int read_pwr_int_values(struct zd_chip *chip) +{ + return read_values(chip, chip->pwr_int_values, + E2P_CHANNEL_COUNT, E2P_PWR_INT_VALUE1, + E2P_PWR_INT_GUARD); +} + +static int read_ofdm_cal_values(struct zd_chip *chip) +{ + int r; + int i; + static const zd_addr_t addresses[] = { + E2P_36M_CAL_VALUE1, + E2P_48M_CAL_VALUE1, + E2P_54M_CAL_VALUE1, + }; + + for (i = 0; i < 3; i++) { + r = read_values(chip, chip->ofdm_cal_values[i], + E2P_CHANNEL_COUNT, addresses[i], 0); + if (r) + return r; + } + return 0; +} + +static int read_cal_int_tables(struct zd_chip *chip) +{ + int r; + + r = read_pwr_cal_values(chip); + if (r) + return r; + r = read_pwr_int_values(chip); + if (r) + return r; + r = read_ofdm_cal_values(chip); + if (r) + return r; + return 0; +} + +/* phy means physical registers */ +int zd_chip_lock_phy_regs(struct zd_chip *chip) +{ + int r; + u32 tmp; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = zd_ioread32_locked(chip, &tmp, CR_REG1); + if (r) { + dev_err(zd_chip_dev(chip), "error ioread32(CR_REG1): %d\n", r); + return r; + } + + tmp &= ~UNLOCK_PHY_REGS; + + r = zd_iowrite32_locked(chip, tmp, CR_REG1); + if (r) + dev_err(zd_chip_dev(chip), "error iowrite32(CR_REG1): %d\n", r); + return r; +} + +int zd_chip_unlock_phy_regs(struct zd_chip *chip) +{ + int r; + u32 tmp; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = zd_ioread32_locked(chip, &tmp, CR_REG1); + if (r) { + dev_err(zd_chip_dev(chip), + "error ioread32(CR_REG1): %d\n", r); + return r; + } + + tmp |= UNLOCK_PHY_REGS; + + r = zd_iowrite32_locked(chip, tmp, CR_REG1); + if (r) + dev_err(zd_chip_dev(chip), "error iowrite32(CR_REG1): %d\n", r); + return r; +} + +/* CR157 can be optionally patched by the EEPROM for original ZD1211 */ +static int patch_cr157(struct zd_chip *chip) +{ + int r; + u16 value; + + if (!chip->patch_cr157) + return 0; + + r = zd_ioread16_locked(chip, &value, E2P_PHY_REG); + if (r) + return r; + + dev_dbg_f(zd_chip_dev(chip), "patching value %x\n", value >> 8); + return zd_iowrite32_locked(chip, value >> 8, CR157); +} + +/* + * 6M band edge can be optionally overwritten for certain RF's + * Vendor driver says: for FCC regulation, enabled per HWFeature 6M band edge + * bit (for AL2230, AL2230S) + */ +static int patch_6m_band_edge(struct zd_chip *chip, u8 channel) +{ + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + if (!chip->patch_6m_band_edge) + return 0; + + return zd_rf_patch_6m_band_edge(&chip->rf, channel); +} + +/* Generic implementation of 6M band edge patching, used by most RFs via + * zd_rf_generic_patch_6m() */ +int zd_chip_generic_patch_6m_band(struct zd_chip *chip, int channel) +{ + struct zd_ioreq16 ioreqs[] = { + { CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 }, + { CR47, 0x1e }, + }; + + /* FIXME: Channel 11 is not the edge for all regulatory domains. */ + if (channel == 1 || channel == 11) + ioreqs[0].value = 0x12; + + dev_dbg_f(zd_chip_dev(chip), "patching for channel %d\n", channel); + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static int zd1211_hw_reset_phy(struct zd_chip *chip) +{ + static const struct zd_ioreq16 ioreqs[] = { + { CR0, 0x0a }, { CR1, 0x06 }, { CR2, 0x26 }, + { CR3, 0x38 }, { CR4, 0x80 }, { CR9, 0xa0 }, + { CR10, 0x81 }, { CR11, 0x00 }, { CR12, 0x7f }, + { CR13, 0x8c }, { CR14, 0x80 }, { CR15, 0x3d }, + { CR16, 0x20 }, { CR17, 0x1e }, { CR18, 0x0a }, + { CR19, 0x48 }, { CR20, 0x0c }, { CR21, 0x0c }, + { CR22, 0x23 }, { CR23, 0x90 }, { CR24, 0x14 }, + { CR25, 0x40 }, { CR26, 0x10 }, { CR27, 0x19 }, + { CR28, 0x7f }, { CR29, 0x80 }, { CR30, 0x4b }, + { CR31, 0x60 }, { CR32, 0x43 }, { CR33, 0x08 }, + { CR34, 0x06 }, { CR35, 0x0a }, { CR36, 0x00 }, + { CR37, 0x00 }, { CR38, 0x38 }, { CR39, 0x0c }, + { CR40, 0x84 }, { CR41, 0x2a }, { CR42, 0x80 }, + { CR43, 0x10 }, { CR44, 0x12 }, { CR46, 0xff }, + { CR47, 0x1E }, { CR48, 0x26 }, { CR49, 0x5b }, + { CR64, 0xd0 }, { CR65, 0x04 }, { CR66, 0x58 }, + { CR67, 0xc9 }, { CR68, 0x88 }, { CR69, 0x41 }, + { CR70, 0x23 }, { CR71, 0x10 }, { CR72, 0xff }, + { CR73, 0x32 }, { CR74, 0x30 }, { CR75, 0x65 }, + { CR76, 0x41 }, { CR77, 0x1b }, { CR78, 0x30 }, + { CR79, 0x68 }, { CR80, 0x64 }, { CR81, 0x64 }, + { CR82, 0x00 }, { CR83, 0x00 }, { CR84, 0x00 }, + { CR85, 0x02 }, { CR86, 0x00 }, { CR87, 0x00 }, + { CR88, 0xff }, { CR89, 0xfc }, { CR90, 0x00 }, + { CR91, 0x00 }, { CR92, 0x00 }, { CR93, 0x08 }, + { CR94, 0x00 }, { CR95, 0x00 }, { CR96, 0xff }, + { CR97, 0xe7 }, { CR98, 0x00 }, { CR99, 0x00 }, + { CR100, 0x00 }, { CR101, 0xae }, { CR102, 0x02 }, + { CR103, 0x00 }, { CR104, 0x03 }, { CR105, 0x65 }, + { CR106, 0x04 }, { CR107, 0x00 }, { CR108, 0x0a }, + { CR109, 0xaa }, { CR110, 0xaa }, { CR111, 0x25 }, + { CR112, 0x25 }, { CR113, 0x00 }, { CR119, 0x1e }, + { CR125, 0x90 }, { CR126, 0x00 }, { CR127, 0x00 }, + { }, + { CR5, 0x00 }, { CR6, 0x00 }, { CR7, 0x00 }, + { CR8, 0x00 }, { CR9, 0x20 }, { CR12, 0xf0 }, + { CR20, 0x0e }, { CR21, 0x0e }, { CR27, 0x10 }, + { CR44, 0x33 }, { CR47, 0x1E }, { CR83, 0x24 }, + { CR84, 0x04 }, { CR85, 0x00 }, { CR86, 0x0C }, + { CR87, 0x12 }, { CR88, 0x0C }, { CR89, 0x00 }, + { CR90, 0x10 }, { CR91, 0x08 }, { CR93, 0x00 }, + { CR94, 0x01 }, { CR95, 0x00 }, { CR96, 0x50 }, + { CR97, 0x37 }, { CR98, 0x35 }, { CR101, 0x13 }, + { CR102, 0x27 }, { CR103, 0x27 }, { CR104, 0x18 }, + { CR105, 0x12 }, { CR109, 0x27 }, { CR110, 0x27 }, + { CR111, 0x27 }, { CR112, 0x27 }, { CR113, 0x27 }, + { CR114, 0x27 }, { CR115, 0x26 }, { CR116, 0x24 }, + { CR117, 0xfc }, { CR118, 0xfa }, { CR120, 0x4f }, + { CR125, 0xaa }, { CR127, 0x03 }, { CR128, 0x14 }, + { CR129, 0x12 }, { CR130, 0x10 }, { CR131, 0x0C }, + { CR136, 0xdf }, { CR137, 0x40 }, { CR138, 0xa0 }, + { CR139, 0xb0 }, { CR140, 0x99 }, { CR141, 0x82 }, + { CR142, 0x54 }, { CR143, 0x1c }, { CR144, 0x6c }, + { CR147, 0x07 }, { CR148, 0x4c }, { CR149, 0x50 }, + { CR150, 0x0e }, { CR151, 0x18 }, { CR160, 0xfe }, + { CR161, 0xee }, { CR162, 0xaa }, { CR163, 0xfa }, + { CR164, 0xfa }, { CR165, 0xea }, { CR166, 0xbe }, + { CR167, 0xbe }, { CR168, 0x6a }, { CR169, 0xba }, + { CR170, 0xba }, { CR171, 0xba }, + /* Note: CR204 must lead the CR203 */ + { CR204, 0x7d }, + { }, + { CR203, 0x30 }, + }; + + int r, t; + + dev_dbg_f(zd_chip_dev(chip), "\n"); + + r = zd_chip_lock_phy_regs(chip); + if (r) + goto out; + + r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); + if (r) + goto unlock; + + r = patch_cr157(chip); +unlock: + t = zd_chip_unlock_phy_regs(chip); + if (t && !r) + r = t; +out: + return r; +} + +static int zd1211b_hw_reset_phy(struct zd_chip *chip) +{ + static const struct zd_ioreq16 ioreqs[] = { + { CR0, 0x14 }, { CR1, 0x06 }, { CR2, 0x26 }, + { CR3, 0x38 }, { CR4, 0x80 }, { CR9, 0xe0 }, + { CR10, 0x81 }, + /* power control { { CR11, 1 << 6 }, */ + { CR11, 0x00 }, + { CR12, 0xf0 }, { CR13, 0x8c }, { CR14, 0x80 }, + { CR15, 0x3d }, { CR16, 0x20 }, { CR17, 0x1e }, + { CR18, 0x0a }, { CR19, 0x48 }, + { CR20, 0x10 }, /* Org:0x0E, ComTrend:RalLink AP */ + { CR21, 0x0e }, { CR22, 0x23 }, { CR23, 0x90 }, + { CR24, 0x14 }, { CR25, 0x40 }, { CR26, 0x10 }, + { CR27, 0x10 }, { CR28, 0x7f }, { CR29, 0x80 }, + { CR30, 0x4b }, /* ASIC/FWT, no jointly decoder */ + { CR31, 0x60 }, { CR32, 0x43 }, { CR33, 0x08 }, + { CR34, 0x06 }, { CR35, 0x0a }, { CR36, 0x00 }, + { CR37, 0x00 }, { CR38, 0x38 }, { CR39, 0x0c }, + { CR40, 0x84 }, { CR41, 0x2a }, { CR42, 0x80 }, + { CR43, 0x10 }, { CR44, 0x33 }, { CR46, 0xff }, + { CR47, 0x1E }, { CR48, 0x26 }, { CR49, 0x5b }, + { CR64, 0xd0 }, { CR65, 0x04 }, { CR66, 0x58 }, + { CR67, 0xc9 }, { CR68, 0x88 }, { CR69, 0x41 }, + { CR70, 0x23 }, { CR71, 0x10 }, { CR72, 0xff }, + { CR73, 0x32 }, { CR74, 0x30 }, { CR75, 0x65 }, + { CR76, 0x41 }, { CR77, 0x1b }, { CR78, 0x30 }, + { CR79, 0xf0 }, { CR80, 0x64 }, { CR81, 0x64 }, + { CR82, 0x00 }, { CR83, 0x24 }, { CR84, 0x04 }, + { CR85, 0x00 }, { CR86, 0x0c }, { CR87, 0x12 }, + { CR88, 0x0c }, { CR89, 0x00 }, { CR90, 0x58 }, + { CR91, 0x04 }, { CR92, 0x00 }, { CR93, 0x00 }, + { CR94, 0x01 }, + { CR95, 0x20 }, /* ZD1211B */ + { CR96, 0x50 }, { CR97, 0x37 }, { CR98, 0x35 }, + { CR99, 0x00 }, { CR100, 0x01 }, { CR101, 0x13 }, + { CR102, 0x27 }, { CR103, 0x27 }, { CR104, 0x18 }, + { CR105, 0x12 }, { CR106, 0x04 }, { CR107, 0x00 }, + { CR108, 0x0a }, { CR109, 0x27 }, { CR110, 0x27 }, + { CR111, 0x27 }, { CR112, 0x27 }, { CR113, 0x27 }, + { CR114, 0x27 }, { CR115, 0x26 }, { CR116, 0x24 }, + { CR117, 0xfc }, { CR118, 0xfa }, { CR119, 0x1e }, + { CR125, 0x90 }, { CR126, 0x00 }, { CR127, 0x00 }, + { CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 }, + { CR131, 0x0c }, { CR136, 0xdf }, { CR137, 0xa0 }, + { CR138, 0xa8 }, { CR139, 0xb4 }, { CR140, 0x98 }, + { CR141, 0x82 }, { CR142, 0x53 }, { CR143, 0x1c }, + { CR144, 0x6c }, { CR147, 0x07 }, { CR148, 0x40 }, + { CR149, 0x40 }, /* Org:0x50 ComTrend:RalLink AP */ + { CR150, 0x14 }, /* Org:0x0E ComTrend:RalLink AP */ + { CR151, 0x18 }, { CR159, 0x70 }, { CR160, 0xfe }, + { CR161, 0xee }, { CR162, 0xaa }, { CR163, 0xfa }, + { CR164, 0xfa }, { CR165, 0xea }, { CR166, 0xbe }, + { CR167, 0xbe }, { CR168, 0x6a }, { CR169, 0xba }, + { CR170, 0xba }, { CR171, 0xba }, + /* Note: CR204 must lead the CR203 */ + { CR204, 0x7d }, + {}, + { CR203, 0x30 }, + }; + + int r, t; + + dev_dbg_f(zd_chip_dev(chip), "\n"); + + r = zd_chip_lock_phy_regs(chip); + if (r) + goto out; + + r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); + t = zd_chip_unlock_phy_regs(chip); + if (t && !r) + r = t; +out: + return r; +} + +static int hw_reset_phy(struct zd_chip *chip) +{ + return zd_chip_is_zd1211b(chip) ? zd1211b_hw_reset_phy(chip) : + zd1211_hw_reset_phy(chip); +} + +static int zd1211_hw_init_hmac(struct zd_chip *chip) +{ + static const struct zd_ioreq32 ioreqs[] = { + { CR_ZD1211_RETRY_MAX, 0x2 }, + { CR_RX_THRESHOLD, 0x000c0640 }, + }; + + dev_dbg_f(zd_chip_dev(chip), "\n"); + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + return zd_iowrite32a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static int zd1211b_hw_init_hmac(struct zd_chip *chip) +{ + static const struct zd_ioreq32 ioreqs[] = { + { CR_ZD1211B_RETRY_MAX, 0x02020202 }, + { CR_ZD1211B_TX_PWR_CTL4, 0x007f003f }, + { CR_ZD1211B_TX_PWR_CTL3, 0x007f003f }, + { CR_ZD1211B_TX_PWR_CTL2, 0x003f001f }, + { CR_ZD1211B_TX_PWR_CTL1, 0x001f000f }, + { CR_ZD1211B_AIFS_CTL1, 0x00280028 }, + { CR_ZD1211B_AIFS_CTL2, 0x008C003C }, + { CR_ZD1211B_TXOP, 0x01800824 }, + { CR_RX_THRESHOLD, 0x000c0eff, }, + }; + + dev_dbg_f(zd_chip_dev(chip), "\n"); + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + return zd_iowrite32a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static int hw_init_hmac(struct zd_chip *chip) +{ + int r; + static const struct zd_ioreq32 ioreqs[] = { + { CR_ACK_TIMEOUT_EXT, 0x20 }, + { CR_ADDA_MBIAS_WARMTIME, 0x30000808 }, + { CR_SNIFFER_ON, 0 }, + { CR_RX_FILTER, STA_RX_FILTER }, + { CR_GROUP_HASH_P1, 0x00 }, + { CR_GROUP_HASH_P2, 0x80000000 }, + { CR_REG1, 0xa4 }, + { CR_ADDA_PWR_DWN, 0x7f }, + { CR_BCN_PLCP_CFG, 0x00f00401 }, + { CR_PHY_DELAY, 0x00 }, + { CR_ACK_TIMEOUT_EXT, 0x80 }, + { CR_ADDA_PWR_DWN, 0x00 }, + { CR_ACK_TIME_80211, 0x100 }, + { CR_RX_PE_DELAY, 0x70 }, + { CR_PS_CTRL, 0x10000000 }, + { CR_RTS_CTS_RATE, 0x02030203 }, + { CR_AFTER_PNP, 0x1 }, + { CR_WEP_PROTECT, 0x114 }, + { CR_IFS_VALUE, IFS_VALUE_DEFAULT }, + }; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = zd_iowrite32a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); + if (r) + return r; + + return zd_chip_is_zd1211b(chip) ? + zd1211b_hw_init_hmac(chip) : zd1211_hw_init_hmac(chip); +} + +struct aw_pt_bi { + u32 atim_wnd_period; + u32 pre_tbtt; + u32 beacon_interval; +}; + +static int get_aw_pt_bi(struct zd_chip *chip, struct aw_pt_bi *s) +{ + int r; + static const zd_addr_t aw_pt_bi_addr[] = + { CR_ATIM_WND_PERIOD, CR_PRE_TBTT, CR_BCN_INTERVAL }; + u32 values[3]; + + r = zd_ioread32v_locked(chip, values, (const zd_addr_t *)aw_pt_bi_addr, + ARRAY_SIZE(aw_pt_bi_addr)); + if (r) { + memset(s, 0, sizeof(*s)); + return r; + } + + s->atim_wnd_period = values[0]; + s->pre_tbtt = values[1]; + s->beacon_interval = values[2]; + return 0; +} + +static int set_aw_pt_bi(struct zd_chip *chip, struct aw_pt_bi *s) +{ + struct zd_ioreq32 reqs[3]; + + if (s->beacon_interval <= 5) + s->beacon_interval = 5; + if (s->pre_tbtt < 4 || s->pre_tbtt >= s->beacon_interval) + s->pre_tbtt = s->beacon_interval - 1; + if (s->atim_wnd_period >= s->pre_tbtt) + s->atim_wnd_period = s->pre_tbtt - 1; + + reqs[0].addr = CR_ATIM_WND_PERIOD; + reqs[0].value = s->atim_wnd_period; + reqs[1].addr = CR_PRE_TBTT; + reqs[1].value = s->pre_tbtt; + reqs[2].addr = CR_BCN_INTERVAL; + reqs[2].value = s->beacon_interval; + + return zd_iowrite32a_locked(chip, reqs, ARRAY_SIZE(reqs)); +} + + +static int set_beacon_interval(struct zd_chip *chip, u32 interval) +{ + int r; + struct aw_pt_bi s; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = get_aw_pt_bi(chip, &s); + if (r) + return r; + s.beacon_interval = interval; + return set_aw_pt_bi(chip, &s); +} + +int zd_set_beacon_interval(struct zd_chip *chip, u32 interval) +{ + int r; + + mutex_lock(&chip->mutex); + r = set_beacon_interval(chip, interval); + mutex_unlock(&chip->mutex); + return r; +} + +static int hw_init(struct zd_chip *chip) +{ + int r; + + dev_dbg_f(zd_chip_dev(chip), "\n"); + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = hw_reset_phy(chip); + if (r) + return r; + + r = hw_init_hmac(chip); + if (r) + return r; + + return set_beacon_interval(chip, 100); +} + +static zd_addr_t fw_reg_addr(struct zd_chip *chip, u16 offset) +{ + return (zd_addr_t)((u16)chip->fw_regs_base + offset); +} + +#ifdef DEBUG +static int dump_cr(struct zd_chip *chip, const zd_addr_t addr, + const char *addr_string) +{ + int r; + u32 value; + + r = zd_ioread32_locked(chip, &value, addr); + if (r) { + dev_dbg_f(zd_chip_dev(chip), + "error reading %s. Error number %d\n", addr_string, r); + return r; + } + + dev_dbg_f(zd_chip_dev(chip), "%s %#010x\n", + addr_string, (unsigned int)value); + return 0; +} + +static int test_init(struct zd_chip *chip) +{ + int r; + + r = dump_cr(chip, CR_AFTER_PNP, "CR_AFTER_PNP"); + if (r) + return r; + r = dump_cr(chip, CR_GPI_EN, "CR_GPI_EN"); + if (r) + return r; + return dump_cr(chip, CR_INTERRUPT, "CR_INTERRUPT"); +} + +static void dump_fw_registers(struct zd_chip *chip) +{ + const zd_addr_t addr[4] = { + fw_reg_addr(chip, FW_REG_FIRMWARE_VER), + fw_reg_addr(chip, FW_REG_USB_SPEED), + fw_reg_addr(chip, FW_REG_FIX_TX_RATE), + fw_reg_addr(chip, FW_REG_LED_LINK_STATUS), + }; + + int r; + u16 values[4]; + + r = zd_ioread16v_locked(chip, values, (const zd_addr_t*)addr, + ARRAY_SIZE(addr)); + if (r) { + dev_dbg_f(zd_chip_dev(chip), "error %d zd_ioread16v_locked\n", + r); + return; + } + + dev_dbg_f(zd_chip_dev(chip), "FW_FIRMWARE_VER %#06hx\n", values[0]); + dev_dbg_f(zd_chip_dev(chip), "FW_USB_SPEED %#06hx\n", values[1]); + dev_dbg_f(zd_chip_dev(chip), "FW_FIX_TX_RATE %#06hx\n", values[2]); + dev_dbg_f(zd_chip_dev(chip), "FW_LINK_STATUS %#06hx\n", values[3]); +} +#endif /* DEBUG */ + +static int print_fw_version(struct zd_chip *chip) +{ + int r; + u16 version; + + r = zd_ioread16_locked(chip, &version, + fw_reg_addr(chip, FW_REG_FIRMWARE_VER)); + if (r) + return r; + + dev_info(zd_chip_dev(chip),"firmware version %04hx\n", version); + return 0; +} + +static int set_mandatory_rates(struct zd_chip *chip, int mode) +{ + u32 rates; + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + /* This sets the mandatory rates, which only depend from the standard + * that the device is supporting. Until further notice we should try + * to support 802.11g also for full speed USB. + */ + switch (mode) { + case MODE_IEEE80211B: + rates = CR_RATE_1M|CR_RATE_2M|CR_RATE_5_5M|CR_RATE_11M; + break; + case MODE_IEEE80211G: + rates = CR_RATE_1M|CR_RATE_2M|CR_RATE_5_5M|CR_RATE_11M| + CR_RATE_6M|CR_RATE_12M|CR_RATE_24M; + break; + default: + return -EINVAL; + } + return zd_iowrite32_locked(chip, rates, CR_MANDATORY_RATE_TBL); +} + +int zd_chip_set_rts_cts_rate_locked(struct zd_chip *chip, + int preamble) +{ + u32 value = 0; + + dev_dbg_f(zd_chip_dev(chip), "preamble=%x\n", preamble); + value |= preamble << RTSCTS_SH_RTS_PMB_TYPE; + value |= preamble << RTSCTS_SH_CTS_PMB_TYPE; + + /* We always send 11M RTS/self-CTS messages, like the vendor driver. */ + value |= ZD_PURE_RATE(ZD_CCK_RATE_11M) << RTSCTS_SH_RTS_RATE; + value |= ZD_RX_CCK << RTSCTS_SH_RTS_MOD_TYPE; + value |= ZD_PURE_RATE(ZD_CCK_RATE_11M) << RTSCTS_SH_CTS_RATE; + value |= ZD_RX_CCK << RTSCTS_SH_CTS_MOD_TYPE; + + return zd_iowrite32_locked(chip, value, CR_RTS_CTS_RATE); +} + +int zd_chip_enable_hwint(struct zd_chip *chip) +{ + int r; + + mutex_lock(&chip->mutex); + r = zd_iowrite32_locked(chip, HWINT_ENABLED, CR_INTERRUPT); + mutex_unlock(&chip->mutex); + return r; +} + +static int disable_hwint(struct zd_chip *chip) +{ + return zd_iowrite32_locked(chip, HWINT_DISABLED, CR_INTERRUPT); +} + +int zd_chip_disable_hwint(struct zd_chip *chip) +{ + int r; + + mutex_lock(&chip->mutex); + r = disable_hwint(chip); + mutex_unlock(&chip->mutex); + return r; +} + +static int read_fw_regs_offset(struct zd_chip *chip) +{ + int r; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = zd_ioread16_locked(chip, (u16*)&chip->fw_regs_base, + FWRAW_REGS_ADDR); + if (r) + return r; + dev_dbg_f(zd_chip_dev(chip), "fw_regs_base: %#06hx\n", + (u16)chip->fw_regs_base); + + return 0; +} + +/* Read mac address using pre-firmware interface */ +int zd_chip_read_mac_addr_fw(struct zd_chip *chip, u8 *addr) +{ + dev_dbg_f(zd_chip_dev(chip), "\n"); + return zd_usb_read_fw(&chip->usb, E2P_MAC_ADDR_P1, addr, + ETH_ALEN); +} + +int zd_chip_init_hw(struct zd_chip *chip) +{ + int r; + u8 rf_type; + + dev_dbg_f(zd_chip_dev(chip), "\n"); + + mutex_lock(&chip->mutex); + +#ifdef DEBUG + r = test_init(chip); + if (r) + goto out; +#endif + r = zd_iowrite32_locked(chip, 1, CR_AFTER_PNP); + if (r) + goto out; + + r = read_fw_regs_offset(chip); + if (r) + goto out; + + /* GPI is always disabled, also in the other driver. + */ + r = zd_iowrite32_locked(chip, 0, CR_GPI_EN); + if (r) + goto out; + r = zd_iowrite32_locked(chip, CWIN_SIZE, CR_CWMIN_CWMAX); + if (r) + goto out; + /* Currently we support IEEE 802.11g for full and high speed USB. + * It might be discussed, whether we should suppport pure b mode for + * full speed USB. + */ + r = set_mandatory_rates(chip, MODE_IEEE80211G); + if (r) + goto out; + /* Disabling interrupts is certainly a smart thing here. + */ + r = disable_hwint(chip); + if (r) + goto out; + r = read_pod(chip, &rf_type); + if (r) + goto out; + r = hw_init(chip); + if (r) + goto out; + r = zd_rf_init_hw(&chip->rf, rf_type); + if (r) + goto out; + + r = print_fw_version(chip); + if (r) + goto out; + +#ifdef DEBUG + dump_fw_registers(chip); + r = test_init(chip); + if (r) + goto out; +#endif /* DEBUG */ + + r = read_cal_int_tables(chip); + if (r) + goto out; + + print_id(chip); +out: + mutex_unlock(&chip->mutex); + return r; +} + +static int update_pwr_int(struct zd_chip *chip, u8 channel) +{ + u8 value = chip->pwr_int_values[channel - 1]; + return zd_iowrite16_locked(chip, value, CR31); +} + +static int update_pwr_cal(struct zd_chip *chip, u8 channel) +{ + u8 value = chip->pwr_cal_values[channel-1]; + return zd_iowrite16_locked(chip, value, CR68); +} + +static int update_ofdm_cal(struct zd_chip *chip, u8 channel) +{ + struct zd_ioreq16 ioreqs[3]; + + ioreqs[0].addr = CR67; + ioreqs[0].value = chip->ofdm_cal_values[OFDM_36M_INDEX][channel-1]; + ioreqs[1].addr = CR66; + ioreqs[1].value = chip->ofdm_cal_values[OFDM_48M_INDEX][channel-1]; + ioreqs[2].addr = CR65; + ioreqs[2].value = chip->ofdm_cal_values[OFDM_54M_INDEX][channel-1]; + + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static int update_channel_integration_and_calibration(struct zd_chip *chip, + u8 channel) +{ + int r; + + if (!zd_rf_should_update_pwr_int(&chip->rf)) + return 0; + + r = update_pwr_int(chip, channel); + if (r) + return r; + if (zd_chip_is_zd1211b(chip)) { + static const struct zd_ioreq16 ioreqs[] = { + { CR69, 0x28 }, + {}, + { CR69, 0x2a }, + }; + + r = update_ofdm_cal(chip, channel); + if (r) + return r; + r = update_pwr_cal(chip, channel); + if (r) + return r; + r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); + if (r) + return r; + } + + return 0; +} + +/* The CCK baseband gain can be optionally patched by the EEPROM */ +static int patch_cck_gain(struct zd_chip *chip) +{ + int r; + u32 value; + + if (!chip->patch_cck_gain || !zd_rf_should_patch_cck_gain(&chip->rf)) + return 0; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = zd_ioread32_locked(chip, &value, E2P_PHY_REG); + if (r) + return r; + dev_dbg_f(zd_chip_dev(chip), "patching value %x\n", value & 0xff); + return zd_iowrite16_locked(chip, value & 0xff, CR47); +} + +int zd_chip_set_channel(struct zd_chip *chip, u8 channel) +{ + int r, t; + + mutex_lock(&chip->mutex); + r = zd_chip_lock_phy_regs(chip); + if (r) + goto out; + r = zd_rf_set_channel(&chip->rf, channel); + if (r) + goto unlock; + r = update_channel_integration_and_calibration(chip, channel); + if (r) + goto unlock; + r = patch_cck_gain(chip); + if (r) + goto unlock; + r = patch_6m_band_edge(chip, channel); + if (r) + goto unlock; + r = zd_iowrite32_locked(chip, 0, CR_CONFIG_PHILIPS); +unlock: + t = zd_chip_unlock_phy_regs(chip); + if (t && !r) + r = t; +out: + mutex_unlock(&chip->mutex); + return r; +} + +u8 zd_chip_get_channel(struct zd_chip *chip) +{ + u8 channel; + + mutex_lock(&chip->mutex); + channel = chip->rf.channel; + mutex_unlock(&chip->mutex); + return channel; +} + +int zd_chip_control_leds(struct zd_chip *chip, enum led_status status) +{ + const zd_addr_t a[] = { + fw_reg_addr(chip, FW_REG_LED_LINK_STATUS), + CR_LED, + }; + + int r; + u16 v[ARRAY_SIZE(a)]; + struct zd_ioreq16 ioreqs[ARRAY_SIZE(a)] = { + [0] = { fw_reg_addr(chip, FW_REG_LED_LINK_STATUS) }, + [1] = { CR_LED }, + }; + u16 other_led; + + mutex_lock(&chip->mutex); + r = zd_ioread16v_locked(chip, v, (const zd_addr_t *)a, ARRAY_SIZE(a)); + if (r) + goto out; + + other_led = chip->link_led == LED1 ? LED2 : LED1; + + switch (status) { + case LED_OFF: + ioreqs[0].value = FW_LINK_OFF; + ioreqs[1].value = v[1] & ~(LED1|LED2); + break; + case LED_SCANNING: + ioreqs[0].value = FW_LINK_OFF; + ioreqs[1].value = v[1] & ~other_led; + if (get_seconds() % 3 == 0) { + ioreqs[1].value &= ~chip->link_led; + } else { + ioreqs[1].value |= chip->link_led; + } + break; + case LED_ASSOCIATED: + ioreqs[0].value = FW_LINK_TX; + ioreqs[1].value = v[1] & ~other_led; + ioreqs[1].value |= chip->link_led; + break; + default: + r = -EINVAL; + goto out; + } + + if (v[0] != ioreqs[0].value || v[1] != ioreqs[1].value) { + r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); + if (r) + goto out; + } + r = 0; +out: + mutex_unlock(&chip->mutex); + return r; +} + +int zd_chip_set_basic_rates(struct zd_chip *chip, u16 cr_rates) +{ + int r; + + if (cr_rates & ~(CR_RATES_80211B|CR_RATES_80211G)) + return -EINVAL; + + mutex_lock(&chip->mutex); + r = zd_iowrite32_locked(chip, cr_rates, CR_BASIC_RATE_TBL); + mutex_unlock(&chip->mutex); + return r; +} + +static int ofdm_qual_db(u8 status_quality, u8 zd_rate, unsigned int size) +{ + static const u16 constants[] = { + 715, 655, 585, 540, 470, 410, 360, 315, + 270, 235, 205, 175, 150, 125, 105, 85, + 65, 50, 40, 25, 15 + }; + + int i; + u32 x; + + /* It seems that their quality parameter is somehow per signal + * and is now transferred per bit. + */ + switch (zd_rate) { + case ZD_OFDM_RATE_6M: + case ZD_OFDM_RATE_12M: + case ZD_OFDM_RATE_24M: + size *= 2; + break; + case ZD_OFDM_RATE_9M: + case ZD_OFDM_RATE_18M: + case ZD_OFDM_RATE_36M: + case ZD_OFDM_RATE_54M: + size *= 4; + size /= 3; + break; + case ZD_OFDM_RATE_48M: + size *= 3; + size /= 2; + break; + default: + return -EINVAL; + } + + x = (10000 * status_quality)/size; + for (i = 0; i < ARRAY_SIZE(constants); i++) { + if (x > constants[i]) + break; + } + + switch (zd_rate) { + case ZD_OFDM_RATE_6M: + case ZD_OFDM_RATE_9M: + i += 3; + break; + case ZD_OFDM_RATE_12M: + case ZD_OFDM_RATE_18M: + i += 5; + break; + case ZD_OFDM_RATE_24M: + case ZD_OFDM_RATE_36M: + i += 9; + break; + case ZD_OFDM_RATE_48M: + case ZD_OFDM_RATE_54M: + i += 15; + break; + default: + return -EINVAL; + } + + return i; +} + +static int ofdm_qual_percent(u8 status_quality, u8 zd_rate, unsigned int size) +{ + int r; + + r = ofdm_qual_db(status_quality, zd_rate, size); + ZD_ASSERT(r >= 0); + if (r < 0) + r = 0; + + r = (r * 100)/29; + return r <= 100 ? r : 100; +} + +static unsigned int log10times100(unsigned int x) +{ + static const u8 log10[] = { + 0, + 0, 30, 47, 60, 69, 77, 84, 90, 95, 100, + 104, 107, 111, 114, 117, 120, 123, 125, 127, 130, + 132, 134, 136, 138, 139, 141, 143, 144, 146, 147, + 149, 150, 151, 153, 154, 155, 156, 157, 159, 160, + 161, 162, 163, 164, 165, 166, 167, 168, 169, 169, + 170, 171, 172, 173, 174, 174, 175, 176, 177, 177, + 178, 179, 179, 180, 181, 181, 182, 183, 183, 184, + 185, 185, 186, 186, 187, 188, 188, 189, 189, 190, + 190, 191, 191, 192, 192, 193, 193, 194, 194, 195, + 195, 196, 196, 197, 197, 198, 198, 199, 199, 200, + 200, 200, 201, 201, 202, 202, 202, 203, 203, 204, + 204, 204, 205, 205, 206, 206, 206, 207, 207, 207, + 208, 208, 208, 209, 209, 210, 210, 210, 211, 211, + 211, 212, 212, 212, 213, 213, 213, 213, 214, 214, + 214, 215, 215, 215, 216, 216, 216, 217, 217, 217, + 217, 218, 218, 218, 219, 219, 219, 219, 220, 220, + 220, 220, 221, 221, 221, 222, 222, 222, 222, 223, + 223, 223, 223, 224, 224, 224, 224, + }; + + return x < ARRAY_SIZE(log10) ? log10[x] : 225; +} + +enum { + MAX_CCK_EVM_DB = 45, +}; + +static int cck_evm_db(u8 status_quality) +{ + return (20 * log10times100(status_quality)) / 100; +} + +static int cck_snr_db(u8 status_quality) +{ + int r = MAX_CCK_EVM_DB - cck_evm_db(status_quality); + ZD_ASSERT(r >= 0); + return r; +} + +static int cck_qual_percent(u8 status_quality) +{ + int r; + + r = cck_snr_db(status_quality); + r = (100*r)/17; + return r <= 100 ? r : 100; +} + +static inline u8 zd_rate_from_ofdm_plcp_header(const void *rx_frame) +{ + return ZD_OFDM | zd_ofdm_plcp_header_rate(rx_frame); +} + +u8 zd_rx_qual_percent(const void *rx_frame, unsigned int size, + const struct rx_status *status) +{ + return (status->frame_status&ZD_RX_OFDM) ? + ofdm_qual_percent(status->signal_quality_ofdm, + zd_rate_from_ofdm_plcp_header(rx_frame), + size) : + cck_qual_percent(status->signal_quality_cck); +} + +/** + * zd_rx_rate - report zd-rate + * @rx_frame - received frame + * @rx_status - rx_status as given by the device + * + * This function converts the rate as encoded in the received packet to the + * zd-rate, we are using on other places in the driver. + */ +u8 zd_rx_rate(const void *rx_frame, const struct rx_status *status) +{ + u8 zd_rate; + if (status->frame_status & ZD_RX_OFDM) { + zd_rate = zd_rate_from_ofdm_plcp_header(rx_frame); + } else { + switch (zd_cck_plcp_header_signal(rx_frame)) { + case ZD_CCK_PLCP_SIGNAL_1M: + zd_rate = ZD_CCK_RATE_1M; + break; + case ZD_CCK_PLCP_SIGNAL_2M: + zd_rate = ZD_CCK_RATE_2M; + break; + case ZD_CCK_PLCP_SIGNAL_5M5: + zd_rate = ZD_CCK_RATE_5_5M; + break; + case ZD_CCK_PLCP_SIGNAL_11M: + zd_rate = ZD_CCK_RATE_11M; + break; + default: + zd_rate = 0; + } + } + + return zd_rate; +} + +int zd_chip_switch_radio_on(struct zd_chip *chip) +{ + int r; + + mutex_lock(&chip->mutex); + r = zd_switch_radio_on(&chip->rf); + mutex_unlock(&chip->mutex); + return r; +} + +int zd_chip_switch_radio_off(struct zd_chip *chip) +{ + int r; + + mutex_lock(&chip->mutex); + r = zd_switch_radio_off(&chip->rf); + mutex_unlock(&chip->mutex); + return r; +} + +int zd_chip_enable_int(struct zd_chip *chip) +{ + int r; + + mutex_lock(&chip->mutex); + r = zd_usb_enable_int(&chip->usb); + mutex_unlock(&chip->mutex); + return r; +} + +void zd_chip_disable_int(struct zd_chip *chip) +{ + mutex_lock(&chip->mutex); + zd_usb_disable_int(&chip->usb); + mutex_unlock(&chip->mutex); +} + +int zd_chip_enable_rxtx(struct zd_chip *chip) +{ + int r; + + mutex_lock(&chip->mutex); + zd_usb_enable_tx(&chip->usb); + r = zd_usb_enable_rx(&chip->usb); + mutex_unlock(&chip->mutex); + return r; +} + +void zd_chip_disable_rxtx(struct zd_chip *chip) +{ + mutex_lock(&chip->mutex); + zd_usb_disable_rx(&chip->usb); + zd_usb_disable_tx(&chip->usb); + mutex_unlock(&chip->mutex); +} + +int zd_rfwritev_locked(struct zd_chip *chip, + const u32* values, unsigned int count, u8 bits) +{ + int r; + unsigned int i; + + for (i = 0; i < count; i++) { + r = zd_rfwrite_locked(chip, values[i], bits); + if (r) + return r; + } + + return 0; +} + +/* + * We can optionally program the RF directly through CR regs, if supported by + * the hardware. This is much faster than the older method. + */ +int zd_rfwrite_cr_locked(struct zd_chip *chip, u32 value) +{ + struct zd_ioreq16 ioreqs[] = { + { CR244, (value >> 16) & 0xff }, + { CR243, (value >> 8) & 0xff }, + { CR242, value & 0xff }, + }; + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +int zd_rfwritev_cr_locked(struct zd_chip *chip, + const u32 *values, unsigned int count) +{ + int r; + unsigned int i; + + for (i = 0; i < count; i++) { + r = zd_rfwrite_cr_locked(chip, values[i]); + if (r) + return r; + } + + return 0; +} + +int zd_chip_set_multicast_hash(struct zd_chip *chip, + struct zd_mc_hash *hash) +{ + struct zd_ioreq32 ioreqs[] = { + { CR_GROUP_HASH_P1, hash->low }, + { CR_GROUP_HASH_P2, hash->high }, + }; + + return zd_iowrite32a(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} diff -puN /dev/null drivers/net/wireless/zd1211rw-mac80211/zd_chip.h --- /dev/null +++ a/drivers/net/wireless/zd1211rw-mac80211/zd_chip.h @@ -0,0 +1,943 @@ +/* zd_chip.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _ZD_CHIP_H +#define _ZD_CHIP_H + +#include "zd_rf.h" +#include "zd_usb.h" + +/* Header for the Media Access Controller (MAC) and the Baseband Processor + * (BBP). It appears that the ZD1211 wraps the old ZD1205 with USB glue and + * adds a processor for handling the USB protocol. + */ + +/* Address space */ +enum { + /* CONTROL REGISTERS */ + CR_START = 0x9000, + + + /* FIRMWARE */ + FW_START = 0xee00, + + + /* EEPROM */ + E2P_START = 0xf800, + E2P_LEN = 0x800, + + /* EEPROM layout */ + E2P_LOAD_CODE_LEN = 0xe, /* base 0xf800 */ + E2P_LOAD_VECT_LEN = 0x9, /* base 0xf80e */ + /* E2P_DATA indexes into this */ + E2P_DATA_LEN = 0x7e, /* base 0xf817 */ + E2P_BOOT_CODE_LEN = 0x760, /* base 0xf895 */ + E2P_INTR_VECT_LEN = 0xb, /* base 0xfff5 */ + + /* Some precomputed offsets into the EEPROM */ + E2P_DATA_OFFSET = E2P_LOAD_CODE_LEN + E2P_LOAD_VECT_LEN, + E2P_BOOT_CODE_OFFSET = E2P_DATA_OFFSET + E2P_DATA_LEN, +}; + +#define CTL_REG(offset) ((zd_addr_t)(CR_START + (offset))) +#define E2P_DATA(offset) ((zd_addr_t)(E2P_START + E2P_DATA_OFFSET + (offset))) +#define FWRAW_DATA(offset) ((zd_addr_t)(FW_START + (offset))) + +/* 8-bit hardware registers */ +#define CR0 CTL_REG(0x0000) +#define CR1 CTL_REG(0x0004) +#define CR2 CTL_REG(0x0008) +#define CR3 CTL_REG(0x000C) + +#define CR5 CTL_REG(0x0010) +/* bit 5: if set short preamble used + * bit 6: filter band - Japan channel 14 on, else off + */ +#define CR6 CTL_REG(0x0014) +#define CR7 CTL_REG(0x0018) +#define CR8 CTL_REG(0x001C) + +#define CR4 CTL_REG(0x0020) + +#define CR9 CTL_REG(0x0024) +/* bit 2: antenna switch (together with CR10) */ +#define CR10 CTL_REG(0x0028) +/* bit 1: antenna switch (together with CR9) + * RF2959 controls with CR11 radion on and off + */ +#define CR11 CTL_REG(0x002C) +/* bit 6: TX power control for OFDM + * RF2959 controls with CR10 radio on and off + */ +#define CR12 CTL_REG(0x0030) +#define CR13 CTL_REG(0x0034) +#define CR14 CTL_REG(0x0038) +#define CR15 CTL_REG(0x003C) +#define CR16 CTL_REG(0x0040) +#define CR17 CTL_REG(0x0044) +#define CR18 CTL_REG(0x0048) +#define CR19 CTL_REG(0x004C) +#define CR20 CTL_REG(0x0050) +#define CR21 CTL_REG(0x0054) +#define CR22 CTL_REG(0x0058) +#define CR23 CTL_REG(0x005C) +#define CR24 CTL_REG(0x0060) /* CCA threshold */ +#define CR25 CTL_REG(0x0064) +#define CR26 CTL_REG(0x0068) +#define CR27 CTL_REG(0x006C) +#define CR28 CTL_REG(0x0070) +#define CR29 CTL_REG(0x0074) +#define CR30 CTL_REG(0x0078) +#define CR31 CTL_REG(0x007C) /* TX power control for RF in CCK mode */ +#define CR32 CTL_REG(0x0080) +#define CR33 CTL_REG(0x0084) +#define CR34 CTL_REG(0x0088) +#define CR35 CTL_REG(0x008C) +#define CR36 CTL_REG(0x0090) +#define CR37 CTL_REG(0x0094) +#define CR38 CTL_REG(0x0098) +#define CR39 CTL_REG(0x009C) +#define CR40 CTL_REG(0x00A0) +#define CR41 CTL_REG(0x00A4) +#define CR42 CTL_REG(0x00A8) +#define CR43 CTL_REG(0x00AC) +#define CR44 CTL_REG(0x00B0) +#define CR45 CTL_REG(0x00B4) +#define CR46 CTL_REG(0x00B8) +#define CR47 CTL_REG(0x00BC) /* CCK baseband gain + * (patch value might be in EEPROM) + */ +#define CR48 CTL_REG(0x00C0) +#define CR49 CTL_REG(0x00C4) +#define CR50 CTL_REG(0x00C8) +#define CR51 CTL_REG(0x00CC) /* TX power control for RF in 6-36M modes */ +#define CR52 CTL_REG(0x00D0) /* TX power control for RF in 48M mode */ +#define CR53 CTL_REG(0x00D4) /* TX power control for RF in 54M mode */ +#define CR54 CTL_REG(0x00D8) +#define CR55 CTL_REG(0x00DC) +#define CR56 CTL_REG(0x00E0) +#define CR57 CTL_REG(0x00E4) +#define CR58 CTL_REG(0x00E8) +#define CR59 CTL_REG(0x00EC) +#define CR60 CTL_REG(0x00F0) +#define CR61 CTL_REG(0x00F4) +#define CR62 CTL_REG(0x00F8) +#define CR63 CTL_REG(0x00FC) +#define CR64 CTL_REG(0x0100) +#define CR65 CTL_REG(0x0104) /* OFDM 54M calibration */ +#define CR66 CTL_REG(0x0108) /* OFDM 48M calibration */ +#define CR67 CTL_REG(0x010C) /* OFDM 36M calibration */ +#define CR68 CTL_REG(0x0110) /* CCK calibration */ +#define CR69 CTL_REG(0x0114) +#define CR70 CTL_REG(0x0118) +#define CR71 CTL_REG(0x011C) +#define CR72 CTL_REG(0x0120) +#define CR73 CTL_REG(0x0124) +#define CR74 CTL_REG(0x0128) +#define CR75 CTL_REG(0x012C) +#define CR76 CTL_REG(0x0130) +#define CR77 CTL_REG(0x0134) +#define CR78 CTL_REG(0x0138) +#define CR79 CTL_REG(0x013C) +#define CR80 CTL_REG(0x0140) +#define CR81 CTL_REG(0x0144) +#define CR82 CTL_REG(0x0148) +#define CR83 CTL_REG(0x014C) +#define CR84 CTL_REG(0x0150) +#define CR85 CTL_REG(0x0154) +#define CR86 CTL_REG(0x0158) +#define CR87 CTL_REG(0x015C) +#define CR88 CTL_REG(0x0160) +#define CR89 CTL_REG(0x0164) +#define CR90 CTL_REG(0x0168) +#define CR91 CTL_REG(0x016C) +#define CR92 CTL_REG(0x0170) +#define CR93 CTL_REG(0x0174) +#define CR94 CTL_REG(0x0178) +#define CR95 CTL_REG(0x017C) +#define CR96 CTL_REG(0x0180) +#define CR97 CTL_REG(0x0184) +#define CR98 CTL_REG(0x0188) +#define CR99 CTL_REG(0x018C) +#define CR100 CTL_REG(0x0190) +#define CR101 CTL_REG(0x0194) +#define CR102 CTL_REG(0x0198) +#define CR103 CTL_REG(0x019C) +#define CR104 CTL_REG(0x01A0) +#define CR105 CTL_REG(0x01A4) +#define CR106 CTL_REG(0x01A8) +#define CR107 CTL_REG(0x01AC) +#define CR108 CTL_REG(0x01B0) +#define CR109 CTL_REG(0x01B4) +#define CR110 CTL_REG(0x01B8) +#define CR111 CTL_REG(0x01BC) +#define CR112 CTL_REG(0x01C0) +#define CR113 CTL_REG(0x01C4) +#define CR114 CTL_REG(0x01C8) +#define CR115 CTL_REG(0x01CC) +#define CR116 CTL_REG(0x01D0) +#define CR117 CTL_REG(0x01D4) +#define CR118 CTL_REG(0x01D8) +#define CR119 CTL_REG(0x01DC) +#define CR120 CTL_REG(0x01E0) +#define CR121 CTL_REG(0x01E4) +#define CR122 CTL_REG(0x01E8) +#define CR123 CTL_REG(0x01EC) +#define CR124 CTL_REG(0x01F0) +#define CR125 CTL_REG(0x01F4) +#define CR126 CTL_REG(0x01F8) +#define CR127 CTL_REG(0x01FC) +#define CR128 CTL_REG(0x0200) +#define CR129 CTL_REG(0x0204) +#define CR130 CTL_REG(0x0208) +#define CR131 CTL_REG(0x020C) +#define CR132 CTL_REG(0x0210) +#define CR133 CTL_REG(0x0214) +#define CR134 CTL_REG(0x0218) +#define CR135 CTL_REG(0x021C) +#define CR136 CTL_REG(0x0220) +#define CR137 CTL_REG(0x0224) +#define CR138 CTL_REG(0x0228) +#define CR139 CTL_REG(0x022C) +#define CR140 CTL_REG(0x0230) +#define CR141 CTL_REG(0x0234) +#define CR142 CTL_REG(0x0238) +#define CR143 CTL_REG(0x023C) +#define CR144 CTL_REG(0x0240) +#define CR145 CTL_REG(0x0244) +#define CR146 CTL_REG(0x0248) +#define CR147 CTL_REG(0x024C) +#define CR148 CTL_REG(0x0250) +#define CR149 CTL_REG(0x0254) +#define CR150 CTL_REG(0x0258) +#define CR151 CTL_REG(0x025C) +#define CR152 CTL_REG(0x0260) +#define CR153 CTL_REG(0x0264) +#define CR154 CTL_REG(0x0268) +#define CR155 CTL_REG(0x026C) +#define CR156 CTL_REG(0x0270) +#define CR157 CTL_REG(0x0274) +#define CR158 CTL_REG(0x0278) +#define CR159 CTL_REG(0x027C) +#define CR160 CTL_REG(0x0280) +#define CR161 CTL_REG(0x0284) +#define CR162 CTL_REG(0x0288) +#define CR163 CTL_REG(0x028C) +#define CR164 CTL_REG(0x0290) +#define CR165 CTL_REG(0x0294) +#define CR166 CTL_REG(0x0298) +#define CR167 CTL_REG(0x029C) +#define CR168 CTL_REG(0x02A0) +#define CR169 CTL_REG(0x02A4) +#define CR170 CTL_REG(0x02A8) +#define CR171 CTL_REG(0x02AC) +#define CR172 CTL_REG(0x02B0) +#define CR173 CTL_REG(0x02B4) +#define CR174 CTL_REG(0x02B8) +#define CR175 CTL_REG(0x02BC) +#define CR176 CTL_REG(0x02C0) +#define CR177 CTL_REG(0x02C4) +#define CR178 CTL_REG(0x02C8) +#define CR179 CTL_REG(0x02CC) +#define CR180 CTL_REG(0x02D0) +#define CR181 CTL_REG(0x02D4) +#define CR182 CTL_REG(0x02D8) +#define CR183 CTL_REG(0x02DC) +#define CR184 CTL_REG(0x02E0) +#define CR185 CTL_REG(0x02E4) +#define CR186 CTL_REG(0x02E8) +#define CR187 CTL_REG(0x02EC) +#define CR188 CTL_REG(0x02F0) +#define CR189 CTL_REG(0x02F4) +#define CR190 CTL_REG(0x02F8) +#define CR191 CTL_REG(0x02FC) +#define CR192 CTL_REG(0x0300) +#define CR193 CTL_REG(0x0304) +#define CR194 CTL_REG(0x0308) +#define CR195 CTL_REG(0x030C) +#define CR196 CTL_REG(0x0310) +#define CR197 CTL_REG(0x0314) +#define CR198 CTL_REG(0x0318) +#define CR199 CTL_REG(0x031C) +#define CR200 CTL_REG(0x0320) +#define CR201 CTL_REG(0x0324) +#define CR202 CTL_REG(0x0328) +#define CR203 CTL_REG(0x032C) /* I2C bus template value & flash control */ +#define CR204 CTL_REG(0x0330) +#define CR205 CTL_REG(0x0334) +#define CR206 CTL_REG(0x0338) +#define CR207 CTL_REG(0x033C) +#define CR208 CTL_REG(0x0340) +#define CR209 CTL_REG(0x0344) +#define CR210 CTL_REG(0x0348) +#define CR211 CTL_REG(0x034C) +#define CR212 CTL_REG(0x0350) +#define CR213 CTL_REG(0x0354) +#define CR214 CTL_REG(0x0358) +#define CR215 CTL_REG(0x035C) +#define CR216 CTL_REG(0x0360) +#define CR217 CTL_REG(0x0364) +#define CR218 CTL_REG(0x0368) +#define CR219 CTL_REG(0x036C) +#define CR220 CTL_REG(0x0370) +#define CR221 CTL_REG(0x0374) +#define CR222 CTL_REG(0x0378) +#define CR223 CTL_REG(0x037C) +#define CR224 CTL_REG(0x0380) +#define CR225 CTL_REG(0x0384) +#define CR226 CTL_REG(0x0388) +#define CR227 CTL_REG(0x038C) +#define CR228 CTL_REG(0x0390) +#define CR229 CTL_REG(0x0394) +#define CR230 CTL_REG(0x0398) +#define CR231 CTL_REG(0x039C) +#define CR232 CTL_REG(0x03A0) +#define CR233 CTL_REG(0x03A4) +#define CR234 CTL_REG(0x03A8) +#define CR235 CTL_REG(0x03AC) +#define CR236 CTL_REG(0x03B0) + +#define CR240 CTL_REG(0x03C0) +/* bit 7: host-controlled RF register writes + * CR241-CR245: for hardware controlled writing of RF bits, not needed for + * USB + */ +#define CR241 CTL_REG(0x03C4) +#define CR242 CTL_REG(0x03C8) +#define CR243 CTL_REG(0x03CC) +#define CR244 CTL_REG(0x03D0) +#define CR245 CTL_REG(0x03D4) + +#define CR251 CTL_REG(0x03EC) /* only used for activation and deactivation of + * Airoha RFs AL2230 and AL7230B + */ +#define CR252 CTL_REG(0x03F0) +#define CR253 CTL_REG(0x03F4) +#define CR254 CTL_REG(0x03F8) +#define CR255 CTL_REG(0x03FC) + +#define CR_MAX_PHY_REG 255 + +/* Taken from the ZYDAS driver, not all of them are relevant for the ZD1211 + * driver. + */ + +#define CR_RF_IF_CLK CTL_REG(0x0400) +#define CR_RF_IF_DATA CTL_REG(0x0404) +#define CR_PE1_PE2 CTL_REG(0x0408) +#define CR_PE2_DLY CTL_REG(0x040C) +#define CR_LE1 CTL_REG(0x0410) +#define CR_LE2 CTL_REG(0x0414) +/* Seems to enable/disable GPI (General Purpose IO?) */ +#define CR_GPI_EN CTL_REG(0x0418) +#define CR_RADIO_PD CTL_REG(0x042C) +#define CR_RF2948_PD CTL_REG(0x042C) +#define CR_ENABLE_PS_MANUAL_AGC CTL_REG(0x043C) +#define CR_CONFIG_PHILIPS CTL_REG(0x0440) +#define CR_SA2400_SER_AP CTL_REG(0x0444) +#define CR_I2C_WRITE CTL_REG(0x0444) +#define CR_SA2400_SER_RP CTL_REG(0x0448) +#define CR_RADIO_PE CTL_REG(0x0458) +#define CR_RST_BUS_MASTER CTL_REG(0x045C) +#define CR_RFCFG CTL_REG(0x0464) +#define CR_HSTSCHG CTL_REG(0x046C) +#define CR_PHY_ON CTL_REG(0x0474) +#define CR_RX_DELAY CTL_REG(0x0478) +#define CR_RX_PE_DELAY CTL_REG(0x047C) +#define CR_GPIO_1 CTL_REG(0x0490) +#define CR_GPIO_2 CTL_REG(0x0494) +#define CR_EncryBufMux CTL_REG(0x04A8) +#define CR_PS_CTRL CTL_REG(0x0500) +#define CR_ADDA_PWR_DWN CTL_REG(0x0504) +#define CR_ADDA_MBIAS_WARMTIME CTL_REG(0x0508) +#define CR_MAC_PS_STATE CTL_REG(0x050C) + +#define CR_INTERRUPT CTL_REG(0x0510) +#define INT_TX_COMPLETE (1 << 0) +#define INT_RX_COMPLETE (1 << 1) +#define INT_RETRY_FAIL (1 << 2) +#define INT_WAKEUP (1 << 3) +#define INT_DTIM_NOTIFY (1 << 5) +#define INT_CFG_NEXT_BCN (1 << 6) +#define INT_BUS_ABORT (1 << 7) +#define INT_TX_FIFO_READY (1 << 8) +#define INT_UART (1 << 9) +#define INT_TX_COMPLETE_EN (1 << 16) +#define INT_RX_COMPLETE_EN (1 << 17) +#define INT_RETRY_FAIL_EN (1 << 18) +#define INT_WAKEUP_EN (1 << 19) +#define INT_DTIM_NOTIFY_EN (1 << 21) +#define INT_CFG_NEXT_BCN_EN (1 << 22) +#define INT_BUS_ABORT_EN (1 << 23) +#define INT_TX_FIFO_READY_EN (1 << 24) +#define INT_UART_EN (1 << 25) + +#define CR_TSF_LOW_PART CTL_REG(0x0514) +#define CR_TSF_HIGH_PART CTL_REG(0x0518) + +/* Following three values are in time units (1024us) + * Following condition must be met: + * atim < tbtt < bcn + */ +#define CR_ATIM_WND_PERIOD CTL_REG(0x051C) +#define CR_BCN_INTERVAL CTL_REG(0x0520) +#define CR_PRE_TBTT CTL_REG(0x0524) +/* in units of TU(1024us) */ + +/* for UART support */ +#define CR_UART_RBR_THR_DLL CTL_REG(0x0540) +#define CR_UART_DLM_IER CTL_REG(0x0544) +#define CR_UART_IIR_FCR CTL_REG(0x0548) +#define CR_UART_LCR CTL_REG(0x054c) +#define CR_UART_MCR CTL_REG(0x0550) +#define CR_UART_LSR CTL_REG(0x0554) +#define CR_UART_MSR CTL_REG(0x0558) +#define CR_UART_ECR CTL_REG(0x055c) +#define CR_UART_STATUS CTL_REG(0x0560) + +#define CR_PCI_TX_ADDR_P1 CTL_REG(0x0600) +#define CR_PCI_TX_AddR_P2 CTL_REG(0x0604) +#define CR_PCI_RX_AddR_P1 CTL_REG(0x0608) +#define CR_PCI_RX_AddR_P2 CTL_REG(0x060C) + +/* must be overwritten if custom MAC address will be used */ +#define CR_MAC_ADDR_P1 CTL_REG(0x0610) +#define CR_MAC_ADDR_P2 CTL_REG(0x0614) +#define CR_BSSID_P1 CTL_REG(0x0618) +#define CR_BSSID_P2 CTL_REG(0x061C) +#define CR_BCN_PLCP_CFG CTL_REG(0x0620) + +/* Group hash table for filtering incoming packets. + * + * The group hash table is 64 bit large and split over two parts. The first + * part is the lower part. The upper 6 bits of the last byte of the target + * address are used as index. Packets are received if the hash table bit is + * set. This is used for multicast handling, but for broadcasts (address + * ff:ff:ff:ff:ff:ff) the highest bit in the second table must also be set. + */ +#define CR_GROUP_HASH_P1 CTL_REG(0x0624) +#define CR_GROUP_HASH_P2 CTL_REG(0x0628) + +#define CR_RX_TIMEOUT CTL_REG(0x062C) + +/* Basic rates supported by the BSS. When producing ACK or CTS messages, the + * device will use a rate in this table that is less than or equal to the rate + * of the incoming frame which prompted the response. */ +#define CR_BASIC_RATE_TBL CTL_REG(0x0630) +#define CR_RATE_1M (1 << 0) /* 802.11b */ +#define CR_RATE_2M (1 << 1) /* 802.11b */ +#define CR_RATE_5_5M (1 << 2) /* 802.11b */ +#define CR_RATE_11M (1 << 3) /* 802.11b */ +#define CR_RATE_6M (1 << 8) /* 802.11g */ +#define CR_RATE_9M (1 << 9) /* 802.11g */ +#define CR_RATE_12M (1 << 10) /* 802.11g */ +#define CR_RATE_18M (1 << 11) /* 802.11g */ +#define CR_RATE_24M (1 << 12) /* 802.11g */ +#define CR_RATE_36M (1 << 13) /* 802.11g */ +#define CR_RATE_48M (1 << 14) /* 802.11g */ +#define CR_RATE_54M (1 << 15) /* 802.11g */ +#define CR_RATES_80211G 0xff00 +#define CR_RATES_80211B 0x000f + +/* Mandatory rates required in the BSS. When producing ACK or CTS messages, if + * the device could not find an appropriate rate in CR_BASIC_RATE_TBL, it will + * look for a rate in this table that is less than or equal to the rate of + * the incoming frame. */ +#define CR_MANDATORY_RATE_TBL CTL_REG(0x0634) +#define CR_RTS_CTS_RATE CTL_REG(0x0638) + +/* These are all bit indexes in CR_RTS_CTS_RATE, so remember to shift. */ +#define RTSCTS_SH_RTS_RATE 0 +#define RTSCTS_SH_EXP_CTS_RATE 4 +#define RTSCTS_SH_RTS_MOD_TYPE 8 +#define RTSCTS_SH_RTS_PMB_TYPE 9 +#define RTSCTS_SH_CTS_RATE 16 +#define RTSCTS_SH_CTS_MOD_TYPE 24 +#define RTSCTS_SH_CTS_PMB_TYPE 25 + +#define CR_WEP_PROTECT CTL_REG(0x063C) +#define CR_RX_THRESHOLD CTL_REG(0x0640) + +/* register for controlling the LEDS */ +#define CR_LED CTL_REG(0x0644) +/* masks for controlling LEDs */ +#define LED1 (1 << 8) +#define LED2 (1 << 9) +#define LED_SW (1 << 10) + +/* Seems to indicate that the configuration is over. + */ +#define CR_AFTER_PNP CTL_REG(0x0648) +#define CR_ACK_TIME_80211 CTL_REG(0x0658) + +#define CR_RX_OFFSET CTL_REG(0x065c) + +#define CR_PHY_DELAY CTL_REG(0x066C) +#define CR_BCN_FIFO CTL_REG(0x0670) +#define CR_SNIFFER_ON CTL_REG(0x0674) + +#define CR_ENCRYPTION_TYPE CTL_REG(0x0678) +#define NO_WEP 0 +#define WEP64 1 +#define WEP128 5 +#define WEP256 6 +#define ENC_SNIFFER 8 + +#define CR_ZD1211_RETRY_MAX CTL_REG(0x067C) + +#define CR_REG1 CTL_REG(0x0680) +/* Setting the bit UNLOCK_PHY_REGS disallows the write access to physical + * registers, so one could argue it is a LOCK bit. But calling it + * LOCK_PHY_REGS makes it confusing. + */ +#define UNLOCK_PHY_REGS (1 << 7) + +#define CR_DEVICE_STATE CTL_REG(0x0684) +#define CR_UNDERRUN_CNT CTL_REG(0x0688) + +#define CR_RX_FILTER CTL_REG(0x068c) +#define RX_FILTER_ASSOC_REQUEST (1 << 0) +#define RX_FILTER_ASSOC_RESPONSE (1 << 1) +#define RX_FILTER_REASSOC_REQUEST (1 << 2) +#define RX_FILTER_REASSOC_RESPONSE (1 << 3) +#define RX_FILTER_PROBE_REQUEST (1 << 4) +#define RX_FILTER_PROBE_RESPONSE (1 << 5) +/* bits 6 and 7 reserved */ +#define RX_FILTER_BEACON (1 << 8) +#define RX_FILTER_ATIM (1 << 9) +#define RX_FILTER_DISASSOC (1 << 10) +#define RX_FILTER_AUTH (1 << 11) +#define RX_FILTER_DEAUTH (1 << 12) +#define RX_FILTER_PSPOLL (1 << 26) +#define RX_FILTER_RTS (1 << 27) +#define RX_FILTER_CTS (1 << 28) +#define RX_FILTER_ACK (1 << 29) +#define RX_FILTER_CFEND (1 << 30) +#define RX_FILTER_CFACK (1 << 31) + +/* Enable bits for all frames you are interested in. */ +#define STA_RX_FILTER (RX_FILTER_ASSOC_REQUEST | RX_FILTER_ASSOC_RESPONSE | \ + RX_FILTER_REASSOC_REQUEST | RX_FILTER_REASSOC_RESPONSE | \ + RX_FILTER_PROBE_REQUEST | RX_FILTER_PROBE_RESPONSE | \ + (0x3 << 6) /* vendor driver sets these reserved bits */ | \ + RX_FILTER_BEACON | RX_FILTER_ATIM | RX_FILTER_DISASSOC | \ + RX_FILTER_AUTH | RX_FILTER_DEAUTH | \ + (0x7 << 13) /* vendor driver sets these reserved bits */ | \ + RX_FILTER_PSPOLL | RX_FILTER_ACK) /* 0x2400ffff */ + +#define RX_FILTER_CTRL (RX_FILTER_RTS | RX_FILTER_CTS | \ + RX_FILTER_CFEND | RX_FILTER_CFACK) + +/* Monitor mode sets filter to 0xfffff */ + +#define CR_ACK_TIMEOUT_EXT CTL_REG(0x0690) +#define CR_BCN_FIFO_SEMAPHORE CTL_REG(0x0694) + +#define CR_IFS_VALUE CTL_REG(0x0698) +#define IFS_VALUE_DIFS_SH 0 +#define IFS_VALUE_EIFS_SH 12 +#define IFS_VALUE_SIFS_SH 24 +#define IFS_VALUE_DEFAULT (( 50 << IFS_VALUE_DIFS_SH) | \ + (1148 << IFS_VALUE_EIFS_SH) | \ + ( 10 << IFS_VALUE_SIFS_SH)) + +#define CR_RX_TIME_OUT CTL_REG(0x069C) +#define CR_TOTAL_RX_FRM CTL_REG(0x06A0) +#define CR_CRC32_CNT CTL_REG(0x06A4) +#define CR_CRC16_CNT CTL_REG(0x06A8) +#define CR_DECRYPTION_ERR_UNI CTL_REG(0x06AC) +#define CR_RX_FIFO_OVERRUN CTL_REG(0x06B0) + +#define CR_DECRYPTION_ERR_MUL CTL_REG(0x06BC) + +#define CR_NAV_CNT CTL_REG(0x06C4) +#define CR_NAV_CCA CTL_REG(0x06C8) +#define CR_RETRY_CNT CTL_REG(0x06CC) + +#define CR_READ_TCB_ADDR CTL_REG(0x06E8) +#define CR_READ_RFD_ADDR CTL_REG(0x06EC) +#define CR_CWMIN_CWMAX CTL_REG(0x06F0) +#define CR_TOTAL_TX_FRM CTL_REG(0x06F4) + +/* CAM: Continuous Access Mode (power management) */ +#define CR_CAM_MODE CTL_REG(0x0700) +#define CR_CAM_ROLL_TB_LOW CTL_REG(0x0704) +#define CR_CAM_ROLL_TB_HIGH CTL_REG(0x0708) +#define CR_CAM_ADDRESS CTL_REG(0x070C) +#define CR_CAM_DATA CTL_REG(0x0710) + +#define CR_ROMDIR CTL_REG(0x0714) + +#define CR_DECRY_ERR_FLG_LOW CTL_REG(0x0714) +#define CR_DECRY_ERR_FLG_HIGH CTL_REG(0x0718) + +#define CR_WEPKEY0 CTL_REG(0x0720) +#define CR_WEPKEY1 CTL_REG(0x0724) +#define CR_WEPKEY2 CTL_REG(0x0728) +#define CR_WEPKEY3 CTL_REG(0x072C) +#define CR_WEPKEY4 CTL_REG(0x0730) +#define CR_WEPKEY5 CTL_REG(0x0734) +#define CR_WEPKEY6 CTL_REG(0x0738) +#define CR_WEPKEY7 CTL_REG(0x073C) +#define CR_WEPKEY8 CTL_REG(0x0740) +#define CR_WEPKEY9 CTL_REG(0x0744) +#define CR_WEPKEY10 CTL_REG(0x0748) +#define CR_WEPKEY11 CTL_REG(0x074C) +#define CR_WEPKEY12 CTL_REG(0x0750) +#define CR_WEPKEY13 CTL_REG(0x0754) +#define CR_WEPKEY14 CTL_REG(0x0758) +#define CR_WEPKEY15 CTL_REG(0x075c) +#define CR_TKIP_MODE CTL_REG(0x0760) + +#define CR_EEPROM_PROTECT0 CTL_REG(0x0758) +#define CR_EEPROM_PROTECT1 CTL_REG(0x075C) + +#define CR_DBG_FIFO_RD CTL_REG(0x0800) +#define CR_DBG_SELECT CTL_REG(0x0804) +#define CR_FIFO_Length CTL_REG(0x0808) + + +#define CR_RSSI_MGC CTL_REG(0x0810) + +#define CR_PON CTL_REG(0x0818) +#define CR_RX_ON CTL_REG(0x081C) +#define CR_TX_ON CTL_REG(0x0820) +#define CR_CHIP_EN CTL_REG(0x0824) +#define CR_LO_SW CTL_REG(0x0828) +#define CR_TXRX_SW CTL_REG(0x082C) +#define CR_S_MD CTL_REG(0x0830) + +#define CR_USB_DEBUG_PORT CTL_REG(0x0888) + +#define CR_ZD1211B_TX_PWR_CTL1 CTL_REG(0x0b00) +#define CR_ZD1211B_TX_PWR_CTL2 CTL_REG(0x0b04) +#define CR_ZD1211B_TX_PWR_CTL3 CTL_REG(0x0b08) +#define CR_ZD1211B_TX_PWR_CTL4 CTL_REG(0x0b0c) +#define CR_ZD1211B_AIFS_CTL1 CTL_REG(0x0b10) +#define CR_ZD1211B_AIFS_CTL2 CTL_REG(0x0b14) +#define CR_ZD1211B_TXOP CTL_REG(0x0b20) +#define CR_ZD1211B_RETRY_MAX CTL_REG(0x0b28) + +/* Used to detect PLL lock */ +#define UW2453_INTR_REG ((zd_addr_t)0x85c1) + +#define CWIN_SIZE 0x007f043f + + +#define HWINT_ENABLED 0x004f0000 +#define HWINT_DISABLED 0 + +#define E2P_PWR_INT_GUARD 8 +#define E2P_CHANNEL_COUNT 14 + +/* If you compare this addresses with the ZYDAS orignal driver, please notify + * that we use word mapping for the EEPROM. + */ + +/* + * Upper 16 bit contains the regulatory domain. + */ +#define E2P_SUBID E2P_DATA(0x00) +#define E2P_POD E2P_DATA(0x02) +#define E2P_MAC_ADDR_P1 E2P_DATA(0x04) +#define E2P_MAC_ADDR_P2 E2P_DATA(0x06) +#define E2P_PWR_CAL_VALUE1 E2P_DATA(0x08) +#define E2P_PWR_CAL_VALUE2 E2P_DATA(0x0a) +#define E2P_PWR_CAL_VALUE3 E2P_DATA(0x0c) +#define E2P_PWR_CAL_VALUE4 E2P_DATA(0x0e) +#define E2P_PWR_INT_VALUE1 E2P_DATA(0x10) +#define E2P_PWR_INT_VALUE2 E2P_DATA(0x12) +#define E2P_PWR_INT_VALUE3 E2P_DATA(0x14) +#define E2P_PWR_INT_VALUE4 E2P_DATA(0x16) + +/* Contains a bit for each allowed channel. It gives for Europe (ETSI 0x30) + * also only 11 channels. */ +#define E2P_ALLOWED_CHANNEL E2P_DATA(0x18) + +#define E2P_DEVICE_VER E2P_DATA(0x20) +#define E2P_PHY_REG E2P_DATA(0x25) +#define E2P_36M_CAL_VALUE1 E2P_DATA(0x28) +#define E2P_36M_CAL_VALUE2 E2P_DATA(0x2a) +#define E2P_36M_CAL_VALUE3 E2P_DATA(0x2c) +#define E2P_36M_CAL_VALUE4 E2P_DATA(0x2e) +#define E2P_11A_INT_VALUE1 E2P_DATA(0x30) +#define E2P_11A_INT_VALUE2 E2P_DATA(0x32) +#define E2P_11A_INT_VALUE3 E2P_DATA(0x34) +#define E2P_11A_INT_VALUE4 E2P_DATA(0x36) +#define E2P_48M_CAL_VALUE1 E2P_DATA(0x38) +#define E2P_48M_CAL_VALUE2 E2P_DATA(0x3a) +#define E2P_48M_CAL_VALUE3 E2P_DATA(0x3c) +#define E2P_48M_CAL_VALUE4 E2P_DATA(0x3e) +#define E2P_48M_INT_VALUE1 E2P_DATA(0x40) +#define E2P_48M_INT_VALUE2 E2P_DATA(0x42) +#define E2P_48M_INT_VALUE3 E2P_DATA(0x44) +#define E2P_48M_INT_VALUE4 E2P_DATA(0x46) +#define E2P_54M_CAL_VALUE1 E2P_DATA(0x48) /* ??? */ +#define E2P_54M_CAL_VALUE2 E2P_DATA(0x4a) +#define E2P_54M_CAL_VALUE3 E2P_DATA(0x4c) +#define E2P_54M_CAL_VALUE4 E2P_DATA(0x4e) +#define E2P_54M_INT_VALUE1 E2P_DATA(0x50) +#define E2P_54M_INT_VALUE2 E2P_DATA(0x52) +#define E2P_54M_INT_VALUE3 E2P_DATA(0x54) +#define E2P_54M_INT_VALUE4 E2P_DATA(0x56) + +/* This word contains the base address of the FW_REG_ registers below */ +#define FWRAW_REGS_ADDR FWRAW_DATA(0x1d) + +/* All 16 bit values, offset from the address in FWRAW_REGS_ADDR */ +enum { + FW_REG_FIRMWARE_VER = 0, + /* non-zero if USB high speed connection */ + FW_REG_USB_SPEED = 1, + FW_REG_FIX_TX_RATE = 2, + /* Seems to be able to control LEDs over the firmware */ + FW_REG_LED_LINK_STATUS = 3, + FW_REG_SOFT_RESET = 4, + FW_REG_FLASH_CHK = 5, +}; + +/* Values for FW_LINK_STATUS */ +#define FW_LINK_OFF 0x0 +#define FW_LINK_TX 0x1 +/* 0x2 - link led on? */ + +enum { + /* indices for ofdm_cal_values */ + OFDM_36M_INDEX = 0, + OFDM_48M_INDEX = 1, + OFDM_54M_INDEX = 2, +}; + +struct zd_chip { + struct zd_usb usb; + struct zd_rf rf; + struct mutex mutex; + /* Base address of FW_REG_ registers */ + zd_addr_t fw_regs_base; + /* EepSetPoint in the vendor driver */ + u8 pwr_cal_values[E2P_CHANNEL_COUNT]; + /* integration values in the vendor driver */ + u8 pwr_int_values[E2P_CHANNEL_COUNT]; + /* SetPointOFDM in the vendor driver */ + u8 ofdm_cal_values[3][E2P_CHANNEL_COUNT]; + u16 link_led; + unsigned int pa_type:4, + patch_cck_gain:1, patch_cr157:1, patch_6m_band_edge:1, + new_phy_layout:1, al2230s_bit:1, + supports_tx_led:1; +}; + +static inline struct zd_chip *zd_usb_to_chip(struct zd_usb *usb) +{ + return container_of(usb, struct zd_chip, usb); +} + +static inline struct zd_chip *zd_rf_to_chip(struct zd_rf *rf) +{ + return container_of(rf, struct zd_chip, rf); +} + +#define zd_chip_dev(chip) (&(chip)->usb.intf->dev) + +void zd_chip_init(struct zd_chip *chip, + struct ieee80211_hw *hw, + struct usb_interface *intf); +void zd_chip_clear(struct zd_chip *chip); +int zd_chip_read_mac_addr_fw(struct zd_chip *chip, u8 *addr); +int zd_chip_init_hw(struct zd_chip *chip); +int zd_chip_reset(struct zd_chip *chip); + +static inline int zd_chip_is_zd1211b(struct zd_chip *chip) +{ + return chip->usb.is_zd1211b; +} + +static inline int zd_ioread16v_locked(struct zd_chip *chip, u16 *values, + const zd_addr_t *addresses, + unsigned int count) +{ + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + return zd_usb_ioread16v(&chip->usb, values, addresses, count); +} + +static inline int zd_ioread16_locked(struct zd_chip *chip, u16 *value, + const zd_addr_t addr) +{ + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + return zd_usb_ioread16(&chip->usb, value, addr); +} + +int zd_ioread32v_locked(struct zd_chip *chip, u32 *values, + const zd_addr_t *addresses, unsigned int count); + +static inline int zd_ioread32_locked(struct zd_chip *chip, u32 *value, + const zd_addr_t addr) +{ + return zd_ioread32v_locked(chip, value, (const zd_addr_t *)&addr, 1); +} + +static inline int zd_iowrite16_locked(struct zd_chip *chip, u16 value, + zd_addr_t addr) +{ + struct zd_ioreq16 ioreq; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + ioreq.addr = addr; + ioreq.value = value; + + return zd_usb_iowrite16v(&chip->usb, &ioreq, 1); +} + +int zd_iowrite16a_locked(struct zd_chip *chip, + const struct zd_ioreq16 *ioreqs, unsigned int count); + +int _zd_iowrite32v_locked(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs, + unsigned int count); + +static inline int zd_iowrite32_locked(struct zd_chip *chip, u32 value, + zd_addr_t addr) +{ + struct zd_ioreq32 ioreq; + + ioreq.addr = addr; + ioreq.value = value; + + return _zd_iowrite32v_locked(chip, &ioreq, 1); +} + +int zd_iowrite32a_locked(struct zd_chip *chip, + const struct zd_ioreq32 *ioreqs, unsigned int count); + +static inline int zd_rfwrite_locked(struct zd_chip *chip, u32 value, u8 bits) +{ + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + return zd_usb_rfwrite(&chip->usb, value, bits); +} + +int zd_rfwrite_cr_locked(struct zd_chip *chip, u32 value); + +int zd_rfwritev_locked(struct zd_chip *chip, + const u32* values, unsigned int count, u8 bits); +int zd_rfwritev_cr_locked(struct zd_chip *chip, + const u32* values, unsigned int count); + +/* Locking functions for reading and writing registers. + * The different parameters are intentional. + */ +int zd_ioread16(struct zd_chip *chip, zd_addr_t addr, u16 *value); +int zd_iowrite16(struct zd_chip *chip, zd_addr_t addr, u16 value); +int zd_ioread32(struct zd_chip *chip, zd_addr_t addr, u32 *value); +int zd_iowrite32(struct zd_chip *chip, zd_addr_t addr, u32 value); +int zd_ioread32v(struct zd_chip *chip, const zd_addr_t *addresses, + u32 *values, unsigned int count); +int zd_iowrite32a(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs, + unsigned int count); + +int zd_chip_set_channel(struct zd_chip *chip, u8 channel); +static inline u8 _zd_chip_get_channel(struct zd_chip *chip) +{ + return chip->rf.channel; +} +u8 zd_chip_get_channel(struct zd_chip *chip); +int zd_read_regdomain(struct zd_chip *chip, u8 *regdomain); +int zd_write_mac_addr(struct zd_chip *chip, const u8 *mac_addr); +int zd_chip_switch_radio_on(struct zd_chip *chip); +int zd_chip_switch_radio_off(struct zd_chip *chip); +int zd_chip_enable_int(struct zd_chip *chip); +void zd_chip_disable_int(struct zd_chip *chip); +int zd_chip_enable_rxtx(struct zd_chip *chip); +void zd_chip_disable_rxtx(struct zd_chip *chip); +int zd_chip_enable_hwint(struct zd_chip *chip); +int zd_chip_disable_hwint(struct zd_chip *chip); +int zd_chip_generic_patch_6m_band(struct zd_chip *chip, int channel); +int zd_chip_set_rts_cts_rate_locked(struct zd_chip *chip, int preamble); + +static inline int zd_get_encryption_type(struct zd_chip *chip, u32 *type) +{ + return zd_ioread32(chip, CR_ENCRYPTION_TYPE, type); +} + +static inline int zd_set_encryption_type(struct zd_chip *chip, u32 type) +{ + return zd_iowrite32(chip, CR_ENCRYPTION_TYPE, type); +} + +static inline int zd_chip_get_basic_rates(struct zd_chip *chip, u16 *cr_rates) +{ + return zd_ioread16(chip, CR_BASIC_RATE_TBL, cr_rates); +} + +int zd_chip_set_basic_rates(struct zd_chip *chip, u16 cr_rates); + +int zd_chip_lock_phy_regs(struct zd_chip *chip); +int zd_chip_unlock_phy_regs(struct zd_chip *chip); + +enum led_status { + LED_OFF = 0, + LED_SCANNING = 1, + LED_ASSOCIATED = 2, +}; + +int zd_chip_control_leds(struct zd_chip *chip, enum led_status status); + +int zd_set_beacon_interval(struct zd_chip *chip, u32 interval); + +static inline int zd_get_beacon_interval(struct zd_chip *chip, u32 *interval) +{ + return zd_ioread32(chip, CR_BCN_INTERVAL, interval); +} + +struct rx_status; + +u8 zd_rx_qual_percent(const void *rx_frame, unsigned int size, + const struct rx_status *status); + +u8 zd_rx_rate(const void *rx_frame, const struct rx_status *status); + +struct zd_mc_hash { + u32 low; + u32 high; +}; + +static inline void zd_mc_clear(struct zd_mc_hash *hash) +{ + hash->low = 0; + /* The interfaces must always received broadcasts. + * The hash of the broadcast address ff:ff:ff:ff:ff:ff is 63. + */ + hash->high = 0x80000000; +} + +static inline void zd_mc_add_all(struct zd_mc_hash *hash) +{ + hash->low = hash->high = 0xffffffff; +} + +static inline void zd_mc_add_addr(struct zd_mc_hash *hash, u8 *addr) +{ + unsigned int i = addr[5] >> 2; + if (i < 32) { + hash->low |= 1 << i; + } else { + hash->high |= 1 << (i-32); + } +} + +int zd_chip_set_multicast_hash(struct zd_chip *chip, + struct zd_mc_hash *hash); + +#endif /* _ZD_CHIP_H */ diff -puN /dev/null drivers/net/wireless/zd1211rw-mac80211/zd_def.h --- /dev/null +++ a/drivers/net/wireless/zd1211rw-mac80211/zd_def.h @@ -0,0 +1,57 @@ +/* zd_def.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _ZD_DEF_H +#define _ZD_DEF_H + +#include +#include +#include +#include + +typedef u16 __nocast zd_addr_t; + +#define dev_printk_f(level, dev, fmt, args...) \ + dev_printk(level, dev, "%s() " fmt, __func__, ##args) + +#ifdef DEBUG +# define dev_dbg_f(dev, fmt, args...) \ + dev_printk_f(KERN_DEBUG, dev, fmt, ## args) +#else +# define dev_dbg_f(dev, fmt, args...) do { (void)(dev); } while (0) +#endif /* DEBUG */ + +#ifdef DEBUG +# define ZD_ASSERT(x) \ +do { \ + if (!(x)) { \ + pr_debug("%s:%d ASSERT %s VIOLATED!\n", \ + __FILE__, __LINE__, __stringify(x)); \ + dump_stack(); \ + } \ +} while (0) +#else +# define ZD_ASSERT(x) do { } while (0) +#endif + +#ifdef DEBUG +# define ZD_MEMCLEAR(pointer, size) memset((pointer), 0xff, (size)) +#else +# define ZD_MEMCLEAR(pointer, size) do { } while (0) +#endif + +#endif /* _ZD_DEF_H */ diff -puN /dev/null drivers/net/wireless/zd1211rw-mac80211/zd_ieee80211.c --- /dev/null +++ a/drivers/net/wireless/zd1211rw-mac80211/zd_ieee80211.c @@ -0,0 +1,100 @@ +/* zd_ieee80211.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * In the long term, we'll probably find a better way of handling regulatory + * requirements outside of the driver. + */ + +#include +#include + +#include "zd_ieee80211.h" +#include "zd_mac.h" + +struct channel_range { + u8 regdomain; + u8 start; + u8 end; /* exclusive (channel must be less than end) */ +}; + +static const struct channel_range channel_ranges[] = { + { ZD_REGDOMAIN_FCC, 1, 12 }, + { ZD_REGDOMAIN_IC, 1, 12 }, + { ZD_REGDOMAIN_ETSI, 1, 14 }, + { ZD_REGDOMAIN_JAPAN, 1, 14 }, + { ZD_REGDOMAIN_SPAIN, 1, 14 }, + { ZD_REGDOMAIN_FRANCE, 1, 14 }, + + /* Japan originally only had channel 14 available (see CHNL_ID 0x40 in + * 802.11). However, in 2001 the range was extended to include channels + * 1-13. The ZyDAS devices still use the old region code but are + * designed to allow the extra channel access in Japan. */ + { ZD_REGDOMAIN_JAPAN_ADD, 1, 15 }, +}; + +static const struct channel_range *zd_channel_range(u8 regdomain) +{ + int i; + for (i = 0; i < ARRAY_SIZE(channel_ranges); i++) { + const struct channel_range *range = &channel_ranges[i]; + if (range->regdomain == regdomain) + return range; + } + return NULL; +} + +#define CHAN_TO_IDX(chan) ((chan) - 1) + +static void unmask_bg_channels(struct ieee80211_hw *hw, + const struct channel_range *range, + struct ieee80211_hw_mode *mode) +{ + u8 channel; + + for (channel = range->start; channel < range->end; channel++) { + struct ieee80211_channel *chan = + &mode->channels[CHAN_TO_IDX(channel)]; + chan->flag |= IEEE80211_CHAN_W_SCAN | + IEEE80211_CHAN_W_ACTIVE_SCAN | + IEEE80211_CHAN_W_IBSS; + } +} + +void zd_geo_init(struct ieee80211_hw *hw, u8 regdomain) +{ + struct zd_mac *mac = zd_hw_mac(hw); + const struct channel_range *range; + + dev_dbg(zd_mac_dev(mac), "regdomain %#02x\n", regdomain); + + range = zd_channel_range(regdomain); + if (!range) { + /* The vendor driver overrides the regulatory domain and + * allowed channel registers and unconditionally restricts + * available channels to 1-11 everywhere. Match their + * questionable behaviour only for regdomains which we don't + * recognise. */ + dev_warn(zd_mac_dev(mac), "Unrecognised regulatory domain: " + "%#02x. Defaulting to FCC.\n", regdomain); + range = zd_channel_range(ZD_REGDOMAIN_FCC); + } + + unmask_bg_channels(hw, range, &mac->modes[0]); + unmask_bg_channels(hw, range, &mac->modes[1]); +} + diff -puN /dev/null drivers/net/wireless/zd1211rw-mac80211/zd_ieee80211.h --- /dev/null +++ a/drivers/net/wireless/zd1211rw-mac80211/zd_ieee80211.h @@ -0,0 +1,75 @@ +#ifndef _ZD_IEEE80211_H +#define _ZD_IEEE80211_H + +#include + +/* Additional definitions from the standards. + */ + +#define ZD_REGDOMAIN_FCC 0x10 +#define ZD_REGDOMAIN_IC 0x20 +#define ZD_REGDOMAIN_ETSI 0x30 +#define ZD_REGDOMAIN_SPAIN 0x31 +#define ZD_REGDOMAIN_FRANCE 0x32 +#define ZD_REGDOMAIN_JAPAN_ADD 0x40 +#define ZD_REGDOMAIN_JAPAN 0x41 + +enum { + MIN_CHANNEL24 = 1, + MAX_CHANNEL24 = 14, +}; + +void zd_geo_init(struct ieee80211_hw *hw, u8 regdomain); + +#define ZD_PLCP_SERVICE_LENGTH_EXTENSION 0x80 + +struct ofdm_plcp_header { + u8 prefix[3]; + __le16 service; +} __attribute__((packed)); + +static inline u8 zd_ofdm_plcp_header_rate(const struct ofdm_plcp_header *header) +{ + return header->prefix[0] & 0xf; +} + +/* The following defines give the encoding of the 4-bit rate field in the + * OFDM (802.11a/802.11g) PLCP header. Notify that these values are used to + * define the zd-rate values for OFDM. + * + * See the struct zd_ctrlset definition in zd_mac.h. + */ +#define ZD_OFDM_PLCP_RATE_6M 0xb +#define ZD_OFDM_PLCP_RATE_9M 0xf +#define ZD_OFDM_PLCP_RATE_12M 0xa +#define ZD_OFDM_PLCP_RATE_18M 0xe +#define ZD_OFDM_PLCP_RATE_24M 0x9 +#define ZD_OFDM_PLCP_RATE_36M 0xd +#define ZD_OFDM_PLCP_RATE_48M 0x8 +#define ZD_OFDM_PLCP_RATE_54M 0xc + +struct cck_plcp_header { + u8 signal; + u8 service; + __le16 length; + __le16 crc16; +} __attribute__((packed)); + +static inline u8 zd_cck_plcp_header_signal(const struct cck_plcp_header *header) +{ + return header->signal; +} + +/* These defines give the encodings of the signal field in the 802.11b PLCP + * header. The signal field gives the bit rate of the following packet. Even + * if technically wrong we use CCK here also for the 1 MBit/s and 2 MBit/s + * rate to stay consistent with Zydas and our use of the term. + * + * Notify that these values are *not* used in the zd-rates. + */ +#define ZD_CCK_PLCP_SIGNAL_1M 0x0a +#define ZD_CCK_PLCP_SIGNAL_2M 0x14 +#define ZD_CCK_PLCP_SIGNAL_5M5 0x37 +#define ZD_CCK_PLCP_SIGNAL_11M 0x6e + +#endif /* _ZD_IEEE80211_H */ diff -puN /dev/null drivers/net/wireless/zd1211rw-mac80211/zd_mac.c --- /dev/null +++ a/drivers/net/wireless/zd1211rw-mac80211/zd_mac.c @@ -0,0 +1,978 @@ +/* zd_mac.c + * + * Copyright (c) 2007 Luis R. Rodriguez + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +#include "zd_def.h" +#include "zd_chip.h" +#include "zd_mac.h" +#include "zd_ieee80211.h" +#include "zd_rf.h" + +/* This table contains the hardware specific values for the modulation rates. */ +static const struct ieee80211_rate zd_rates[] = { + { .rate = 10, + .val = ZD_CCK_RATE_1M, + .flags = IEEE80211_RATE_CCK }, + { .rate = 20, + .val = ZD_CCK_RATE_2M, + .val2 = ZD_CCK_RATE_2M | ZD_CCK_PREA_SHORT, + .flags = IEEE80211_RATE_CCK_2 }, + { .rate = 55, + .val = ZD_CCK_RATE_5_5M, + .val2 = ZD_CCK_RATE_5_5M | ZD_CCK_PREA_SHORT, + .flags = IEEE80211_RATE_CCK_2 }, + { .rate = 110, + .val = ZD_CCK_RATE_11M, + .val2 = ZD_CCK_RATE_11M | ZD_CCK_PREA_SHORT, + .flags = IEEE80211_RATE_CCK_2 }, + { .rate = 60, + .val = ZD_OFDM_RATE_6M, + .flags = IEEE80211_RATE_OFDM }, + { .rate = 90, + .val = ZD_OFDM_RATE_9M, + .flags = IEEE80211_RATE_OFDM }, + { .rate = 120, + .val = ZD_OFDM_RATE_12M, + .flags = IEEE80211_RATE_OFDM }, + { .rate = 180, + .val = ZD_OFDM_RATE_18M, + .flags = IEEE80211_RATE_OFDM }, + { .rate = 240, + .val = ZD_OFDM_RATE_24M, + .flags = IEEE80211_RATE_OFDM }, + { .rate = 360, + .val = ZD_OFDM_RATE_36M, + .flags = IEEE80211_RATE_OFDM }, + { .rate = 480, + .val = ZD_OFDM_RATE_48M, + .flags = IEEE80211_RATE_OFDM }, + { .rate = 540, + .val = ZD_OFDM_RATE_54M, + .flags = IEEE80211_RATE_OFDM }, +}; + +static const struct ieee80211_channel zd_channels[] = { + { .chan = 1, + .freq = 2412}, + { .chan = 2, + .freq = 2417}, + { .chan = 3, + .freq = 2422}, + { .chan = 4, + .freq = 2427}, + { .chan = 5, + .freq = 2432}, + { .chan = 6, + .freq = 2437}, + { .chan = 7, + .freq = 2442}, + { .chan = 8, + .freq = 2447}, + { .chan = 9, + .freq = 2452}, + { .chan = 10, + .freq = 2457}, + { .chan = 11, + .freq = 2462}, + { .chan = 12, + .freq = 2467}, + { .chan = 13, + .freq = 2472}, + { .chan = 14, + .freq = 2484} +}; + +static void housekeeping_init(struct zd_mac *mac); +static void housekeeping_enable(struct zd_mac *mac); +static void housekeeping_disable(struct zd_mac *mac); + +int zd_mac_preinit_hw(struct ieee80211_hw *hw) +{ + int r; + u8 addr[ETH_ALEN]; + struct zd_mac *mac = zd_hw_mac(hw); + + r = zd_chip_read_mac_addr_fw(&mac->chip, addr); + if (r) + return r; + + SET_IEEE80211_PERM_ADDR(hw, addr); + + return 0; +} + +int zd_mac_init_hw(struct ieee80211_hw *hw) +{ + int r; + struct zd_mac *mac = zd_hw_mac(hw); + struct zd_chip *chip = &mac->chip; + u8 default_regdomain; + + r = zd_chip_enable_int(chip); + if (r) + goto out; + r = zd_chip_init_hw(chip); + if (r) + goto disable_int; + + ZD_ASSERT(!irqs_disabled()); + + r = zd_read_regdomain(chip, &default_regdomain); + if (r) + goto disable_int; + spin_lock_irq(&mac->lock); + mac->regdomain = mac->default_regdomain = default_regdomain; + spin_unlock_irq(&mac->lock); + + /* We must inform the device that we are doing encryption/decryption in + * software at the moment. */ + r = zd_set_encryption_type(chip, ENC_SNIFFER); + if (r) + goto disable_int; + + zd_geo_init(hw, mac->regdomain); + + r = 0; +disable_int: + zd_chip_disable_int(chip); +out: + return r; +} + +void zd_mac_clear(struct zd_mac *mac) +{ + flush_workqueue(zd_workqueue); + zd_chip_clear(&mac->chip); + ZD_ASSERT(!spin_is_locked(&mac->lock)); + ZD_MEMCLEAR(mac, sizeof(struct zd_mac)); +} + +static int set_rx_filter(struct zd_mac *mac) +{ + unsigned long flags; + u32 filter = STA_RX_FILTER; + + spin_lock_irqsave(&mac->lock, flags); + if (mac->pass_ctrl) + filter |= RX_FILTER_CTRL; + spin_unlock_irqrestore(&mac->lock, flags); + + return zd_iowrite32(&mac->chip, CR_RX_FILTER, filter); +} + +static int set_mc_hash(struct zd_mac *mac) +{ + struct zd_mc_hash hash; + zd_mc_clear(&hash); + return zd_chip_set_multicast_hash(&mac->chip, &hash); +} + +static int zd_op_start(struct ieee80211_hw *hw) +{ + struct zd_mac *mac = zd_hw_mac(hw); + struct zd_chip *chip = &mac->chip; + struct zd_usb *usb = &chip->usb; + int r; + + if (!usb->initialized) { + r = zd_usb_init_hw(usb); + if (r) + goto out; + } + + r = zd_chip_enable_int(chip); + if (r < 0) + goto out; + + r = zd_chip_set_basic_rates(chip, CR_RATES_80211B | CR_RATES_80211G); + if (r < 0) + goto disable_int; + r = set_rx_filter(mac); + if (r) + goto disable_int; + r = set_mc_hash(mac); + if (r) + goto disable_int; + r = zd_chip_switch_radio_on(chip); + if (r < 0) + goto disable_int; + r = zd_chip_enable_rxtx(chip); + if (r < 0) + goto disable_radio; + r = zd_chip_enable_hwint(chip); + if (r < 0) + goto disable_rxtx; + + housekeeping_enable(mac); + return 0; +disable_rxtx: + zd_chip_disable_rxtx(chip); +disable_radio: + zd_chip_switch_radio_off(chip); +disable_int: + zd_chip_disable_int(chip); +out: + return r; +} + +/** + * clear_tx_skb_control_block - clears the control block of tx skbuffs + * @skb: a &struct sk_buff pointer + * + * This clears the control block of skbuff buffers, which were transmitted to + * the device. Notify that the function is not thread-safe, so prevent + * multiple calls. + */ +static void clear_tx_skb_control_block(struct sk_buff *skb) +{ + struct zd_tx_skb_control_block *cb = + (struct zd_tx_skb_control_block *)skb->cb; + + kfree(cb->control); + cb->control = NULL; +} + +/** + * kfree_tx_skb - frees a tx skbuff + * @skb: a &struct sk_buff pointer + * + * Frees the tx skbuff. Frees also the allocated control structure in the + * control block if necessary. + */ +static void kfree_tx_skb(struct sk_buff *skb) +{ + clear_tx_skb_control_block(skb); + dev_kfree_skb_any(skb); +} + +static void zd_op_stop(struct ieee80211_hw *hw) +{ + struct zd_mac *mac = zd_hw_mac(hw); + struct zd_chip *chip = &mac->chip; + struct sk_buff *skb; + struct sk_buff_head *ack_wait_queue = &mac->ack_wait_queue; + + /* The order here deliberately is a little different from the open() + * method, since we need to make sure there is no opportunity for RX + * frames to be processed by mac80211 after we have stopped it. + */ + + zd_chip_disable_rxtx(chip); + housekeeping_disable(mac); + flush_workqueue(zd_workqueue); + + zd_chip_disable_hwint(chip); + zd_chip_switch_radio_off(chip); + zd_chip_disable_int(chip); + + + while ((skb = skb_dequeue(ack_wait_queue))) + kfree_tx_skb(skb); +} + +/** + * init_tx_skb_control_block - initializes skb control block + * @skb: a &sk_buff pointer + * @dev: pointer to the mac80221 device + * @control: mac80211 tx control applying for the frame in @skb + * + * Initializes the control block of the skbuff to be transmitted. + */ +static int init_tx_skb_control_block(struct sk_buff *skb, + struct ieee80211_hw *hw, + struct ieee80211_tx_control *control) +{ + struct zd_tx_skb_control_block *cb = + (struct zd_tx_skb_control_block *)skb->cb; + + ZD_ASSERT(sizeof(*cb) <= sizeof(skb->cb)); + memset(cb, 0, sizeof(*cb)); + cb->hw= hw; + cb->control = kmalloc(sizeof(*control), GFP_ATOMIC); + if (cb->control == NULL) + return -ENOMEM; + memcpy(cb->control, control, sizeof(*control)); + + return 0; +} + +/** + * tx_status - reports tx status of a packet if required + * @hw - a &struct ieee80211_hw pointer + * @skb - a sk-buffer + * @status - the tx status of the packet without control information + * @success - True for successfull transmission of the frame + * + * This information calls ieee80211_tx_status_irqsafe() if required by the + * control information. It copies the control information into the status + * information. + * + * If no status information has been requested, the skb is freed. + */ +static void tx_status(struct ieee80211_hw *hw, struct sk_buff *skb, + struct ieee80211_tx_status *status, + bool success) +{ + struct zd_tx_skb_control_block *cb = (struct zd_tx_skb_control_block *) + skb->cb; + + ZD_ASSERT(cb->control != NULL); + memcpy(&status->control, cb->control, sizeof(status->control)); + if (!success) + status->excessive_retries = 1; + clear_tx_skb_control_block(skb); + ieee80211_tx_status_irqsafe(hw, skb, status); +} + +/** + * zd_mac_tx_failed - callback for failed frames + * @dev: the mac80211 wireless device + * + * This function is called if a frame couldn't be succesfully be + * transferred. The first frame from the tx queue, will be selected and + * reported as error to the upper layers. + */ +void zd_mac_tx_failed(struct ieee80211_hw *hw) +{ + struct sk_buff_head *q = &zd_hw_mac(hw)->ack_wait_queue; + struct sk_buff *skb; + struct ieee80211_tx_status status = {{0}}; + + skb = skb_dequeue(q); + if (skb == NULL) + return; + tx_status(hw, skb, &status, 0); +} + +/** + * zd_mac_tx_to_dev - callback for USB layer + * @skb: a &sk_buff pointer + * @error: error value, 0 if transmission successful + * + * Informs the MAC layer that the frame has successfully transferred to the + * device. If an ACK is required and the transfer to the device has been + * successful, the packets are put on the @ack_wait_queue with + * the control set removed. + */ +void zd_mac_tx_to_dev(struct sk_buff *skb, int error) +{ + struct zd_tx_skb_control_block *cb = + (struct zd_tx_skb_control_block *)skb->cb; + struct ieee80211_hw *hw = cb->hw; + + if (likely(cb->control)) { + skb_pull(skb, sizeof(struct zd_ctrlset)); + if (unlikely(error || + (cb->control->flags & IEEE80211_TXCTL_NO_ACK))) + { + struct ieee80211_tx_status status = {{0}}; + tx_status(hw, skb, &status, !error); + } else { + struct sk_buff_head *q = + &zd_hw_mac(hw)->ack_wait_queue; + + skb_queue_tail(q, skb); + while (skb_queue_len(q) > ZD_MAC_MAX_ACK_WAITERS) + zd_mac_tx_failed(hw); + } + } else { + kfree_tx_skb(skb); + } +} + +static int zd_calc_tx_length_us(u8 *service, u8 zd_rate, u16 tx_length) +{ + /* ZD_PURE_RATE() must be used to remove the modulation type flag of + * the zd-rate values. + */ + static const u8 rate_divisor[] = { + [ZD_PURE_RATE(ZD_CCK_RATE_1M)] = 1, + [ZD_PURE_RATE(ZD_CCK_RATE_2M)] = 2, + /* Bits must be doubled. */ + [ZD_PURE_RATE(ZD_CCK_RATE_5_5M)] = 11, + [ZD_PURE_RATE(ZD_CCK_RATE_11M)] = 11, + [ZD_PURE_RATE(ZD_OFDM_RATE_6M)] = 6, + [ZD_PURE_RATE(ZD_OFDM_RATE_9M)] = 9, + [ZD_PURE_RATE(ZD_OFDM_RATE_12M)] = 12, + [ZD_PURE_RATE(ZD_OFDM_RATE_18M)] = 18, + [ZD_PURE_RATE(ZD_OFDM_RATE_24M)] = 24, + [ZD_PURE_RATE(ZD_OFDM_RATE_36M)] = 36, + [ZD_PURE_RATE(ZD_OFDM_RATE_48M)] = 48, + [ZD_PURE_RATE(ZD_OFDM_RATE_54M)] = 54, + }; + + u32 bits = (u32)tx_length * 8; + u32 divisor; + + divisor = rate_divisor[ZD_PURE_RATE(zd_rate)]; + if (divisor == 0) + return -EINVAL; + + switch (zd_rate) { + case ZD_CCK_RATE_5_5M: + bits = (2*bits) + 10; /* round up to the next integer */ + break; + case ZD_CCK_RATE_11M: + if (service) { + u32 t = bits % 11; + *service &= ~ZD_PLCP_SERVICE_LENGTH_EXTENSION; + if (0 < t && t <= 3) { + *service |= ZD_PLCP_SERVICE_LENGTH_EXTENSION; + } + } + bits += 10; /* round up to the next integer */ + break; + } + + return bits/divisor; +} + +static void cs_set_control(struct zd_mac *mac, struct zd_ctrlset *cs, + struct ieee80211_hdr *header, u32 flags) +{ + u16 fctl = le16_to_cpu(header->frame_control); + + /* + * CONTROL: + * - start at 0x00 + * - if fragment 0, enable bit 0 + * - if backoff needed, enable bit 0 + * - if burst (backoff not needed) disable bit 0 + * - if multicast, enable bit 1 + * - if PS-POLL frame, enable bit 2 + * - if in INDEPENDENT_BSS mode and zd1205_DestPowerSave, then enable + * bit 4 (FIXME: wtf) + * - if frag_len > RTS threshold, set bit 5 as long if it isnt + * multicast or mgt + * - if bit 5 is set, and we are in OFDM mode, unset bit 5 and set bit + * 7 + */ + + cs->control = 0; + + /* First fragment */ + if (flags & IEEE80211_TXCTL_FIRST_FRAGMENT) + cs->control |= ZD_CS_NEED_RANDOM_BACKOFF; + + /* Multicast */ + if (is_multicast_ether_addr(header->addr1)) + cs->control |= ZD_CS_MULTICAST; + + /* PS-POLL */ + if ((fctl & (IEEE80211_FCTL_FTYPE|IEEE80211_FCTL_STYPE)) == + (IEEE80211_FTYPE_CTL|IEEE80211_STYPE_PSPOLL)) + cs->control |= ZD_CS_PS_POLL_FRAME; + + if (flags & IEEE80211_TXCTL_USE_RTS_CTS) + cs->control |= ZD_CS_RTS; + + if (flags & IEEE80211_TXCTL_USE_CTS_PROTECT) + cs->control |= ZD_CS_SELF_CTS; + + /* FIXME: Management frame? */ +} + +static int fill_ctrlset(struct zd_mac *mac, + struct sk_buff *skb, + struct ieee80211_tx_control *control) +{ + int r; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + unsigned int frag_len = skb->len + FCS_LEN; + unsigned int packet_length; + struct zd_ctrlset *cs = (struct zd_ctrlset *) + skb_push(skb, sizeof(struct zd_ctrlset)); + + ZD_ASSERT(frag_len <= 0xffff); + + cs->modulation = control->tx_rate; + + cs->tx_length = cpu_to_le16(frag_len); + + cs_set_control(mac, cs, hdr, control->flags); + + packet_length = frag_len + sizeof(struct zd_ctrlset) + 10; + ZD_ASSERT(packet_length <= 0xffff); + /* ZD1211B: Computing the length difference this way, gives us + * flexibility to compute the packet length. + */ + cs->packet_length = cpu_to_le16(zd_chip_is_zd1211b(&mac->chip) ? + packet_length - frag_len : packet_length); + + /* + * CURRENT LENGTH: + * - transmit frame length in microseconds + * - seems to be derived from frame length + * - see Cal_Us_Service() in zdinlinef.h + * - if macp->bTxBurstEnable is enabled, then multiply by 4 + * - bTxBurstEnable is never set in the vendor driver + * + * SERVICE: + * - "for PLCP configuration" + * - always 0 except in some situations at 802.11b 11M + * - see line 53 of zdinlinef.h + */ + cs->service = 0; + r = zd_calc_tx_length_us(&cs->service, ZD_RATE(cs->modulation), + le16_to_cpu(cs->tx_length)); + if (r < 0) + return r; + cs->current_length = cpu_to_le16(r); + cs->next_frame_length = 0; + + return 0; +} + +/** + * zd_op_tx - transmits a network frame to the device + * + * @dev: mac80211 hardware device + * @skb: socket buffer + * @control: the control structure + * + * This function transmit an IEEE 802.11 network frame to the device. The + * control block of the skbuff will be initialized. If necessary the incoming + * mac80211 queues will be stopped. + */ +static int zd_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb, + struct ieee80211_tx_control *control) +{ + struct zd_mac *mac = zd_hw_mac(hw); + int r; + + r = fill_ctrlset(mac, skb, control); + if (r) + return r; + + r = init_tx_skb_control_block(skb, hw, control); + if (r) + return r; + r = zd_usb_tx(&mac->chip.usb, skb); + if (r) { + clear_tx_skb_control_block(skb); + return r; + } + return 0; +} + +/** + * filter_ack - filters incoming packets for acknowledgements + * @dev: the mac80211 device + * @rx_hdr: received header + * @stats: the status for the received packet + * + * This functions looks for ACK packets and tries to match them with the + * frames in the tx queue. If a match is found the frame will be dequeued and + * the upper layers is informed about the successful transmission. If + * mac80211 queues have been stopped and the number of frames still to be + * transmitted is low the queues will be opened again. + * + * Returns 1 if the frame was an ACK, 0 if it was ignored. + */ +static int filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr, + struct ieee80211_rx_status *stats) +{ + u16 fc = le16_to_cpu(rx_hdr->frame_control); + struct sk_buff *skb; + struct sk_buff_head *q; + unsigned long flags; + + if ((fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) != + (IEEE80211_FTYPE_CTL | IEEE80211_STYPE_ACK)) + return 0; + + q = &zd_hw_mac(hw)->ack_wait_queue; + spin_lock_irqsave(&q->lock, flags); + for (skb = q->next; skb != (struct sk_buff *)q; skb = skb->next) { + struct ieee80211_hdr *tx_hdr; + + tx_hdr = (struct ieee80211_hdr *)skb->data; + if (likely(!compare_ether_addr(tx_hdr->addr2, rx_hdr->addr1))) + { + struct ieee80211_tx_status status = {{0}}; + status.flags = IEEE80211_TX_STATUS_ACK; + status.ack_signal = stats->ssi; + __skb_unlink(skb, q); + tx_status(hw, skb, &status, 1); + goto out; + } + } +out: + spin_unlock_irqrestore(&q->lock, flags); + return 1; +} + +int zd_mac_rx(struct ieee80211_hw *hw, const u8 *buffer, unsigned int length) +{ + struct zd_mac *mac = zd_hw_mac(hw); + struct ieee80211_rx_status stats; + const struct rx_status *status; + struct sk_buff *skb; + int bad_frame = 0; + + if (length < ZD_PLCP_HEADER_SIZE + 10 /* IEEE80211_1ADDR_LEN */ + + FCS_LEN + sizeof(struct rx_status)) + return -EINVAL; + + memset(&stats, 0, sizeof(stats)); + + /* Note about pass_failed_fcs and pass_ctrl access below: + * mac locking intentionally omitted here, as this is the only unlocked + * reader and the only writer is configure_filter. Plus, if there were + * any races accessing these variables, it wouldn't really matter. + * If mac80211 ever provides a way for us to access filter flags + * from outside configure_filter, we could improve on this. Also, this + * situation may change once we implement some kind of DMA-into-skb + * RX path. */ + + /* Caller has to ensure that length >= sizeof(struct rx_status). */ + status = (struct rx_status *) + (buffer + (length - sizeof(struct rx_status))); + if (status->frame_status & ZD_RX_ERROR) { + if (mac->pass_failed_fcs && + (status->frame_status & ZD_RX_CRC32_ERROR)) { + stats.flag |= RX_FLAG_FAILED_FCS_CRC; + bad_frame = 1; + } else { + return -EINVAL; + } + } + + stats.channel = _zd_chip_get_channel(&mac->chip); + stats.freq = zd_channels[stats.channel - 1].freq; + stats.phymode = MODE_IEEE80211G; + stats.ssi = status->signal_strength; + stats.signal = zd_rx_qual_percent(buffer, + length - sizeof(struct rx_status), + status); + stats.rate = zd_rx_rate(buffer, status); + + length -= ZD_PLCP_HEADER_SIZE + sizeof(struct rx_status); + buffer += ZD_PLCP_HEADER_SIZE; + + /* Except for bad frames, filter each frame to see if it is an ACK, in + * which case our internal TX tracking is updated. Normally we then + * bail here as there's no need to pass ACKs on up to the stack, but + * there is also the case where the stack has requested us to pass + * control frames on up (pass_ctrl) which we must consider. */ + if (!bad_frame && + filter_ack(hw, (struct ieee80211_hdr *)buffer, &stats) + && !mac->pass_ctrl) + return 0; + + skb = dev_alloc_skb(length); + if (skb == NULL) + return -ENOMEM; + memcpy(skb_put(skb, length), buffer, length); + + ieee80211_rx_irqsafe(hw, skb, &stats); + return 0; +} + +static int zd_op_add_interface(struct ieee80211_hw *hw, + struct ieee80211_if_init_conf *conf) +{ + struct zd_mac *mac = zd_hw_mac(hw); + + /* using IEEE80211_IF_TYPE_INVALID to indicate no mode selected */ + if (mac->type != IEEE80211_IF_TYPE_INVALID) + return -EOPNOTSUPP; + + switch (conf->type) { + case IEEE80211_IF_TYPE_MNTR: + case IEEE80211_IF_TYPE_STA: + mac->type = conf->type; + break; + default: + return -EOPNOTSUPP; + } + + return zd_write_mac_addr(&mac->chip, conf->mac_addr); +} + +static void zd_op_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_if_init_conf *conf) +{ + struct zd_mac *mac = zd_hw_mac(hw); + mac->type = IEEE80211_IF_TYPE_INVALID; + zd_write_mac_addr(&mac->chip, NULL); +} + +static int zd_op_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf) +{ + struct zd_mac *mac = zd_hw_mac(hw); + return zd_chip_set_channel(&mac->chip, conf->channel); +} + +static int zd_op_config_interface(struct ieee80211_hw *hw, int if_id, + struct ieee80211_if_conf *conf) +{ + struct zd_mac *mac = zd_hw_mac(hw); + + spin_lock_irq(&mac->lock); + mac->associated = is_valid_ether_addr(conf->bssid); + spin_unlock_irq(&mac->lock); + + /* TODO: do hardware bssid filtering */ + return 0; +} + +static void set_multicast_hash_handler(struct work_struct *work) +{ + struct zd_mac *mac = + container_of(work, struct zd_mac, set_multicast_hash_work); + struct zd_mc_hash hash; + + spin_lock_irq(&mac->lock); + hash = mac->multicast_hash; + spin_unlock_irq(&mac->lock); + + zd_chip_set_multicast_hash(&mac->chip, &hash); +} + +static void set_rx_filter_handler(struct work_struct *work) +{ + struct zd_mac *mac = + container_of(work, struct zd_mac, set_rx_filter_work); + int r; + + dev_dbg_f(zd_mac_dev(mac), "\n"); + r = set_rx_filter(mac); + if (r) + dev_err(zd_mac_dev(mac), "set_rx_filter_handler error %d\n", r); +} + +#define SUPPORTED_FIF_FLAGS \ + (FIF_PROMISC_IN_BSS | FIF_ALLMULTI | FIF_FCSFAIL | FIF_CONTROL | \ + FIF_OTHER_BSS) +static void zd_op_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *new_flags, + int mc_count, struct dev_mc_list *mclist) +{ + struct zd_mc_hash hash; + struct zd_mac *mac = zd_hw_mac(hw); + unsigned long flags; + int i; + + /* Only deal with supported flags */ + changed_flags &= SUPPORTED_FIF_FLAGS; + *new_flags &= SUPPORTED_FIF_FLAGS; + + /* changed_flags is always populated but this driver + * doesn't support all FIF flags so its possible we don't + * need to do anything */ + if (!changed_flags) + return; + + if (*new_flags & (FIF_PROMISC_IN_BSS | FIF_ALLMULTI)) { + zd_mc_add_all(&hash); + } else { + DECLARE_MAC_BUF(macbuf); + + zd_mc_clear(&hash); + for (i = 0; i < mc_count; i++) { + if (!mclist) + break; + dev_dbg_f(zd_mac_dev(mac), "mc addr %s\n", + print_mac(macbuf, mclist->dmi_addr)); + zd_mc_add_addr(&hash, mclist->dmi_addr); + mclist = mclist->next; + } + } + + spin_lock_irqsave(&mac->lock, flags); + mac->pass_failed_fcs = !!(*new_flags & FIF_FCSFAIL); + mac->pass_ctrl = !!(*new_flags & FIF_CONTROL); + mac->multicast_hash = hash; + spin_unlock_irqrestore(&mac->lock, flags); + queue_work(zd_workqueue, &mac->set_multicast_hash_work); + + if (changed_flags & FIF_CONTROL) + queue_work(zd_workqueue, &mac->set_rx_filter_work); + + /* no handling required for FIF_OTHER_BSS as we don't currently + * do BSSID filtering */ + /* FIXME: in future it would be nice to enable the probe response + * filter (so that the driver doesn't see them) until + * FIF_BCN_PRBRESP_PROMISC is set. however due to atomicity here, we'd + * have to schedule work to enable prbresp reception, which might + * happen too late. For now we'll just listen and forward them all the + * time. */ +} + +static void set_rts_cts_work(struct work_struct *work) +{ + struct zd_mac *mac = + container_of(work, struct zd_mac, set_rts_cts_work); + unsigned long flags; + unsigned int short_preamble; + + mutex_lock(&mac->chip.mutex); + + spin_lock_irqsave(&mac->lock, flags); + mac->updating_rts_rate = 0; + short_preamble = mac->short_preamble; + spin_unlock_irqrestore(&mac->lock, flags); + + zd_chip_set_rts_cts_rate_locked(&mac->chip, short_preamble); + mutex_unlock(&mac->chip.mutex); +} + +static void zd_op_erp_ie_changed(struct ieee80211_hw *hw, u8 changes, + int cts_protection, int preamble) +{ + struct zd_mac *mac = zd_hw_mac(hw); + unsigned long flags; + + dev_dbg_f(zd_mac_dev(mac), "changes: %x\n", changes); + + if (changes & IEEE80211_ERP_CHANGE_PREAMBLE) { + spin_lock_irqsave(&mac->lock, flags); + mac->short_preamble = !preamble; + if (!mac->updating_rts_rate) { + mac->updating_rts_rate = 1; + /* FIXME: should disable TX here, until work has + * completed and RTS_CTS reg is updated */ + queue_work(zd_workqueue, &mac->set_rts_cts_work); + } + spin_unlock_irqrestore(&mac->lock, flags); + } +} + +static const struct ieee80211_ops zd_ops = { + .tx = zd_op_tx, + .start = zd_op_start, + .stop = zd_op_stop, + .add_interface = zd_op_add_interface, + .remove_interface = zd_op_remove_interface, + .config = zd_op_config, + .config_interface = zd_op_config_interface, + .configure_filter = zd_op_configure_filter, + .erp_ie_changed = zd_op_erp_ie_changed, +}; + +struct ieee80211_hw *zd_mac_alloc_hw(struct usb_interface *intf) +{ + struct zd_mac *mac; + struct ieee80211_hw *hw; + int i; + + hw = ieee80211_alloc_hw(sizeof(struct zd_mac), &zd_ops); + if (!hw) { + dev_dbg_f(&intf->dev, "out of memory\n"); + return NULL; + } + + mac = zd_hw_mac(hw); + + memset(mac, 0, sizeof(*mac)); + spin_lock_init(&mac->lock); + mac->hw = hw; + + mac->type = IEEE80211_IF_TYPE_INVALID; + + memcpy(mac->channels, zd_channels, sizeof(zd_channels)); + memcpy(mac->rates, zd_rates, sizeof(zd_rates)); + mac->modes[0].mode = MODE_IEEE80211G; + mac->modes[0].num_rates = ARRAY_SIZE(zd_rates); + mac->modes[0].rates = mac->rates; + mac->modes[0].num_channels = ARRAY_SIZE(zd_channels); + mac->modes[0].channels = mac->channels; + mac->modes[1].mode = MODE_IEEE80211B; + mac->modes[1].num_rates = 4; + mac->modes[1].rates = mac->rates; + mac->modes[1].num_channels = ARRAY_SIZE(zd_channels); + mac->modes[1].channels = mac->channels; + + hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | + IEEE80211_HW_DEFAULT_REG_DOMAIN_CONFIGURED; + hw->max_rssi = 100; + hw->max_signal = 100; + + hw->queues = 1; + hw->extra_tx_headroom = sizeof(struct zd_ctrlset); + + skb_queue_head_init(&mac->ack_wait_queue); + + for (i = 0; i < 2; i++) { + if (ieee80211_register_hwmode(hw, &mac->modes[i])) { + dev_dbg_f(&intf->dev, "cannot register hwmode\n"); + ieee80211_free_hw(hw); + return NULL; + } + } + + zd_chip_init(&mac->chip, hw, intf); + housekeeping_init(mac); + INIT_WORK(&mac->set_multicast_hash_work, set_multicast_hash_handler); + INIT_WORK(&mac->set_rts_cts_work, set_rts_cts_work); + INIT_WORK(&mac->set_rx_filter_work, set_rx_filter_handler); + + SET_IEEE80211_DEV(hw, &intf->dev); + return hw; +} + +#define LINK_LED_WORK_DELAY HZ + +static void link_led_handler(struct work_struct *work) +{ + struct zd_mac *mac = + container_of(work, struct zd_mac, housekeeping.link_led_work.work); + struct zd_chip *chip = &mac->chip; + int is_associated; + int r; + + spin_lock_irq(&mac->lock); + is_associated = mac->associated; + spin_unlock_irq(&mac->lock); + + r = zd_chip_control_leds(chip, + is_associated ? LED_ASSOCIATED : LED_SCANNING); + if (r) + dev_dbg_f(zd_mac_dev(mac), "zd_chip_control_leds error %d\n", r); + + queue_delayed_work(zd_workqueue, &mac->housekeeping.link_led_work, + LINK_LED_WORK_DELAY); +} + +static void housekeeping_init(struct zd_mac *mac) +{ + INIT_DELAYED_WORK(&mac->housekeeping.link_led_work, link_led_handler); +} + +static void housekeeping_enable(struct zd_mac *mac) +{ + dev_dbg_f(zd_mac_dev(mac), "\n"); + queue_delayed_work(zd_workqueue, &mac->housekeeping.link_led_work, + 0); +} + +static void housekeeping_disable(struct zd_mac *mac) +{ + dev_dbg_f(zd_mac_dev(mac), "\n"); + cancel_rearming_delayed_workqueue(zd_workqueue, + &mac->housekeeping.link_led_work); + zd_chip_control_leds(&mac->chip, LED_OFF); +} diff -puN /dev/null drivers/net/wireless/zd1211rw-mac80211/zd_mac.h --- /dev/null +++ a/drivers/net/wireless/zd1211rw-mac80211/zd_mac.h @@ -0,0 +1,238 @@ +/* zd_mac.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _ZD_MAC_H +#define _ZD_MAC_H + +#include +#include + +#include "zd_chip.h" +#include "zd_ieee80211.h" + +struct zd_ctrlset { + u8 modulation; + __le16 tx_length; + u8 control; + /* stores only the difference to tx_length on ZD1211B */ + __le16 packet_length; + __le16 current_length; + u8 service; + __le16 next_frame_length; +} __attribute__((packed)); + +#define ZD_CS_RESERVED_SIZE 25 + +/* The field modulation of struct zd_ctrlset controls the bit rate, the use + * of short or long preambles in 802.11b (CCK mode) or the use of 802.11a or + * 802.11g in OFDM mode. + * + * The term zd-rate is used for the combination of the modulation type flag + * and the "pure" rate value. + */ +#define ZD_PURE_RATE_MASK 0x0f +#define ZD_MODULATION_TYPE_MASK 0x10 +#define ZD_RATE_MASK (ZD_PURE_RATE_MASK|ZD_MODULATION_TYPE_MASK) +#define ZD_PURE_RATE(modulation) ((modulation) & ZD_PURE_RATE_MASK) +#define ZD_MODULATION_TYPE(modulation) ((modulation) & ZD_MODULATION_TYPE_MASK) +#define ZD_RATE(modulation) ((modulation) & ZD_RATE_MASK) + +/* The two possible modulation types. Notify that 802.11b doesn't use the CCK + * codeing for the 1 and 2 MBit/s rate. We stay with the term here to remain + * consistent with uses the term at other places. + */ +#define ZD_CCK 0x00 +#define ZD_OFDM 0x10 + +/* The ZD1211 firmware uses proprietary encodings of the 802.11b (CCK) rates. + * For OFDM the PLCP rate encodings are used. We combine these "pure" rates + * with the modulation type flag and call the resulting values zd-rates. + */ +#define ZD_CCK_RATE_1M (ZD_CCK|0x00) +#define ZD_CCK_RATE_2M (ZD_CCK|0x01) +#define ZD_CCK_RATE_5_5M (ZD_CCK|0x02) +#define ZD_CCK_RATE_11M (ZD_CCK|0x03) +#define ZD_OFDM_RATE_6M (ZD_OFDM|ZD_OFDM_PLCP_RATE_6M) +#define ZD_OFDM_RATE_9M (ZD_OFDM|ZD_OFDM_PLCP_RATE_9M) +#define ZD_OFDM_RATE_12M (ZD_OFDM|ZD_OFDM_PLCP_RATE_12M) +#define ZD_OFDM_RATE_18M (ZD_OFDM|ZD_OFDM_PLCP_RATE_18M) +#define ZD_OFDM_RATE_24M (ZD_OFDM|ZD_OFDM_PLCP_RATE_24M) +#define ZD_OFDM_RATE_36M (ZD_OFDM|ZD_OFDM_PLCP_RATE_36M) +#define ZD_OFDM_RATE_48M (ZD_OFDM|ZD_OFDM_PLCP_RATE_48M) +#define ZD_OFDM_RATE_54M (ZD_OFDM|ZD_OFDM_PLCP_RATE_54M) + +/* The bit 5 of the zd_ctrlset modulation field controls the preamble in CCK + * mode or the 802.11a/802.11g selection in OFDM mode. + */ +#define ZD_CCK_PREA_LONG 0x00 +#define ZD_CCK_PREA_SHORT 0x20 +#define ZD_OFDM_MODE_11G 0x00 +#define ZD_OFDM_MODE_11A 0x20 + +/* zd_ctrlset control field */ +#define ZD_CS_NEED_RANDOM_BACKOFF 0x01 +#define ZD_CS_MULTICAST 0x02 + +#define ZD_CS_FRAME_TYPE_MASK 0x0c +#define ZD_CS_DATA_FRAME 0x00 +#define ZD_CS_PS_POLL_FRAME 0x04 +#define ZD_CS_MANAGEMENT_FRAME 0x08 +#define ZD_CS_NO_SEQUENCE_CTL_FRAME 0x0c + +#define ZD_CS_WAKE_DESTINATION 0x10 +#define ZD_CS_RTS 0x20 +#define ZD_CS_ENCRYPT 0x40 +#define ZD_CS_SELF_CTS 0x80 + +/* Incoming frames are prepended by a PLCP header */ +#define ZD_PLCP_HEADER_SIZE 5 + +struct rx_length_info { + __le16 length[3]; + __le16 tag; +} __attribute__((packed)); + +#define RX_LENGTH_INFO_TAG 0x697e + +struct rx_status { + u8 signal_quality_cck; + /* rssi */ + u8 signal_strength; + u8 signal_quality_ofdm; + u8 decryption_type; + u8 frame_status; +} __attribute__((packed)); + +/* rx_status field decryption_type */ +#define ZD_RX_NO_WEP 0 +#define ZD_RX_WEP64 1 +#define ZD_RX_TKIP 2 +#define ZD_RX_AES 4 +#define ZD_RX_WEP128 5 +#define ZD_RX_WEP256 6 + +/* rx_status field frame_status */ +#define ZD_RX_FRAME_MODULATION_MASK 0x01 +#define ZD_RX_CCK 0x00 +#define ZD_RX_OFDM 0x01 + +#define ZD_RX_TIMEOUT_ERROR 0x02 +#define ZD_RX_FIFO_OVERRUN_ERROR 0x04 +#define ZD_RX_DECRYPTION_ERROR 0x08 +#define ZD_RX_CRC32_ERROR 0x10 +#define ZD_RX_NO_ADDR1_MATCH_ERROR 0x20 +#define ZD_RX_CRC16_ERROR 0x40 +#define ZD_RX_ERROR 0x80 + +enum mac_flags { + MAC_FIXED_CHANNEL = 0x01, +}; + +struct housekeeping { + struct delayed_work link_led_work; +}; + +/** + * struct zd_tx_skb_control_block - control block for tx skbuffs + * @control: &struct ieee80211_tx_control pointer + * @context: context pointer + * + * This structure is used to fill the cb field in an &sk_buff to transmit. + * The control field is NULL, if there is no requirement from the mac80211 + * stack to report about the packet ACK. This is the case if the flag + * IEEE80211_TXCTL_NO_ACK is not set in &struct ieee80211_tx_control. + */ +struct zd_tx_skb_control_block { + struct ieee80211_tx_control *control; + struct ieee80211_hw *hw; + void *context; +}; + +#define ZD_MAC_STATS_BUFFER_SIZE 16 + +#define ZD_MAC_MAX_ACK_WAITERS 10 + +struct zd_mac { + struct zd_chip chip; + spinlock_t lock; + struct ieee80211_hw *hw; + struct housekeeping housekeeping; + struct work_struct set_multicast_hash_work; + struct work_struct set_rts_cts_work; + struct work_struct set_rx_filter_work; + struct zd_mc_hash multicast_hash; + u8 regdomain; + u8 default_regdomain; + int type; + int associated; + struct sk_buff_head ack_wait_queue; + struct ieee80211_channel channels[14]; + struct ieee80211_rate rates[12]; + struct ieee80211_hw_mode modes[2]; + + /* Short preamble (used for RTS/CTS) */ + unsigned int short_preamble:1; + + /* flags to indicate update in progress */ + unsigned int updating_rts_rate:1; + + /* whether to pass frames with CRC errors to stack */ + unsigned int pass_failed_fcs:1; + + /* whether to pass control frames to stack */ + unsigned int pass_ctrl:1; +}; + +static inline struct zd_mac *zd_hw_mac(struct ieee80211_hw *hw) +{ + return hw->priv; +} + +static inline struct zd_mac *zd_chip_to_mac(struct zd_chip *chip) +{ + return container_of(chip, struct zd_mac, chip); +} + +static inline struct zd_mac *zd_usb_to_mac(struct zd_usb *usb) +{ + return zd_chip_to_mac(zd_usb_to_chip(usb)); +} + +static inline u8 *zd_mac_get_perm_addr(struct zd_mac *mac) +{ + return mac->hw->wiphy->perm_addr; +} + +#define zd_mac_dev(mac) (zd_chip_dev(&(mac)->chip)) + +struct ieee80211_hw *zd_mac_alloc_hw(struct usb_interface *intf); +void zd_mac_clear(struct zd_mac *mac); + +int zd_mac_preinit_hw(struct ieee80211_hw *hw); +int zd_mac_init_hw(struct ieee80211_hw *hw); + +int zd_mac_rx(struct ieee80211_hw *hw, const u8 *buffer, unsigned int length); +void zd_mac_tx_failed(struct ieee80211_hw *hw); +void zd_mac_tx_to_dev(struct sk_buff *skb, int error); + +#ifdef DEBUG +void zd_dump_rx_status(const struct rx_status *status); +#else +#define zd_dump_rx_status(status) +#endif /* DEBUG */ + +#endif /* _ZD_MAC_H */ diff -puN /dev/null drivers/net/wireless/zd1211rw-mac80211/zd_rf.c --- /dev/null +++ a/drivers/net/wireless/zd1211rw-mac80211/zd_rf.c @@ -0,0 +1,178 @@ +/* zd_rf.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#include "zd_def.h" +#include "zd_rf.h" +#include "zd_ieee80211.h" +#include "zd_chip.h" + +static const char * const rfs[] = { + [0] = "unknown RF0", + [1] = "unknown RF1", + [UW2451_RF] = "UW2451_RF", + [UCHIP_RF] = "UCHIP_RF", + [AL2230_RF] = "AL2230_RF", + [AL7230B_RF] = "AL7230B_RF", + [THETA_RF] = "THETA_RF", + [AL2210_RF] = "AL2210_RF", + [MAXIM_NEW_RF] = "MAXIM_NEW_RF", + [UW2453_RF] = "UW2453_RF", + [AL2230S_RF] = "AL2230S_RF", + [RALINK_RF] = "RALINK_RF", + [INTERSIL_RF] = "INTERSIL_RF", + [RF2959_RF] = "RF2959_RF", + [MAXIM_NEW2_RF] = "MAXIM_NEW2_RF", + [PHILIPS_RF] = "PHILIPS_RF", +}; + +const char *zd_rf_name(u8 type) +{ + if (type & 0xf0) + type = 0; + return rfs[type]; +} + +void zd_rf_init(struct zd_rf *rf) +{ + memset(rf, 0, sizeof(*rf)); + + /* default to update channel integration, as almost all RF's do want + * this */ + rf->update_channel_int = 1; +} + +void zd_rf_clear(struct zd_rf *rf) +{ + if (rf->clear) + rf->clear(rf); + ZD_MEMCLEAR(rf, sizeof(*rf)); +} + +int zd_rf_init_hw(struct zd_rf *rf, u8 type) +{ + int r = 0; + int t; + struct zd_chip *chip = zd_rf_to_chip(rf); + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + switch (type) { + case RF2959_RF: + r = zd_rf_init_rf2959(rf); + break; + case AL2230_RF: + case AL2230S_RF: + r = zd_rf_init_al2230(rf); + break; + case AL7230B_RF: + r = zd_rf_init_al7230b(rf); + break; + case UW2453_RF: + r = zd_rf_init_uw2453(rf); + break; + default: + dev_err(zd_chip_dev(chip), + "RF %s %#x is not supported\n", zd_rf_name(type), type); + rf->type = 0; + return -ENODEV; + } + + if (r) + return r; + + rf->type = type; + + r = zd_chip_lock_phy_regs(chip); + if (r) + return r; + t = rf->init_hw(rf); + r = zd_chip_unlock_phy_regs(chip); + if (t) + r = t; + return r; +} + +int zd_rf_scnprint_id(struct zd_rf *rf, char *buffer, size_t size) +{ + return scnprintf(buffer, size, "%s", zd_rf_name(rf->type)); +} + +int zd_rf_set_channel(struct zd_rf *rf, u8 channel) +{ + int r; + + ZD_ASSERT(mutex_is_locked(&zd_rf_to_chip(rf)->mutex)); + if (channel < MIN_CHANNEL24) + return -EINVAL; + if (channel > MAX_CHANNEL24) + return -EINVAL; + dev_dbg_f(zd_chip_dev(zd_rf_to_chip(rf)), "channel: %d\n", channel); + + r = rf->set_channel(rf, channel); + if (r >= 0) + rf->channel = channel; + return r; +} + +int zd_switch_radio_on(struct zd_rf *rf) +{ + int r, t; + struct zd_chip *chip = zd_rf_to_chip(rf); + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = zd_chip_lock_phy_regs(chip); + if (r) + return r; + t = rf->switch_radio_on(rf); + r = zd_chip_unlock_phy_regs(chip); + if (t) + r = t; + return r; +} + +int zd_switch_radio_off(struct zd_rf *rf) +{ + int r, t; + struct zd_chip *chip = zd_rf_to_chip(rf); + + /* TODO: move phy regs handling to zd_chip */ + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = zd_chip_lock_phy_regs(chip); + if (r) + return r; + t = rf->switch_radio_off(rf); + r = zd_chip_unlock_phy_regs(chip); + if (t) + r = t; + return r; +} + +int zd_rf_patch_6m_band_edge(struct zd_rf *rf, u8 channel) +{ + if (!rf->patch_6m_band_edge) + return 0; + + return rf->patch_6m_band_edge(rf, channel); +} + +int zd_rf_generic_patch_6m(struct zd_rf *rf, u8 channel) +{ + return zd_chip_generic_patch_6m_band(zd_rf_to_chip(rf), channel); +} + diff -puN /dev/null drivers/net/wireless/zd1211rw-mac80211/zd_rf.h --- /dev/null +++ a/drivers/net/wireless/zd1211rw-mac80211/zd_rf.h @@ -0,0 +1,108 @@ +/* zd_rf.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _ZD_RF_H +#define _ZD_RF_H + +#define UW2451_RF 0x2 +#define UCHIP_RF 0x3 +#define AL2230_RF 0x4 +#define AL7230B_RF 0x5 /* a,b,g */ +#define THETA_RF 0x6 +#define AL2210_RF 0x7 +#define MAXIM_NEW_RF 0x8 +#define UW2453_RF 0x9 +#define AL2230S_RF 0xa +#define RALINK_RF 0xb +#define INTERSIL_RF 0xc +#define RF2959_RF 0xd +#define MAXIM_NEW2_RF 0xe +#define PHILIPS_RF 0xf + +#define RF_CHANNEL(ch) [(ch)-1] + +/* Provides functions of the RF transceiver. */ + +enum { + RF_REG_BITS = 6, + RF_VALUE_BITS = 18, + RF_RV_BITS = RF_REG_BITS + RF_VALUE_BITS, +}; + +struct zd_rf { + u8 type; + + u8 channel; + + /* whether channel integration and calibration should be updated + * defaults to 1 (yes) */ + u8 update_channel_int:1; + + /* whether CR47 should be patched from the EEPROM, if the appropriate + * flag is set in the POD. The vendor driver suggests that this should + * be done for all RF's, but a bug in their code prevents but their + * HW_OverWritePhyRegFromE2P() routine from ever taking effect. */ + u8 patch_cck_gain:1; + + /* private RF driver data */ + void *priv; + + /* RF-specific functions */ + int (*init_hw)(struct zd_rf *rf); + int (*set_channel)(struct zd_rf *rf, u8 channel); + int (*switch_radio_on)(struct zd_rf *rf); + int (*switch_radio_off)(struct zd_rf *rf); + int (*patch_6m_band_edge)(struct zd_rf *rf, u8 channel); + void (*clear)(struct zd_rf *rf); +}; + +const char *zd_rf_name(u8 type); +void zd_rf_init(struct zd_rf *rf); +void zd_rf_clear(struct zd_rf *rf); +int zd_rf_init_hw(struct zd_rf *rf, u8 type); + +int zd_rf_scnprint_id(struct zd_rf *rf, char *buffer, size_t size); + +int zd_rf_set_channel(struct zd_rf *rf, u8 channel); + +int zd_switch_radio_on(struct zd_rf *rf); +int zd_switch_radio_off(struct zd_rf *rf); + +int zd_rf_patch_6m_band_edge(struct zd_rf *rf, u8 channel); +int zd_rf_generic_patch_6m(struct zd_rf *rf, u8 channel); + +static inline int zd_rf_should_update_pwr_int(struct zd_rf *rf) +{ + return rf->update_channel_int; +} + +static inline int zd_rf_should_patch_cck_gain(struct zd_rf *rf) +{ + return rf->patch_cck_gain; +} + +int zd_rf_patch_6m_band_edge(struct zd_rf *rf, u8 channel); +int zd_rf_generic_patch_6m(struct zd_rf *rf, u8 channel); + +/* Functions for individual RF chips */ + +int zd_rf_init_rf2959(struct zd_rf *rf); +int zd_rf_init_al2230(struct zd_rf *rf); +int zd_rf_init_al7230b(struct zd_rf *rf); +int zd_rf_init_uw2453(struct zd_rf *rf); + +#endif /* _ZD_RF_H */ diff -puN /dev/null drivers/net/wireless/zd1211rw-mac80211/zd_rf_al2230.c --- /dev/null +++ a/drivers/net/wireless/zd1211rw-mac80211/zd_rf_al2230.c @@ -0,0 +1,439 @@ +/* zd_rf_al2230.c: Functions for the AL2230 RF controller + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "zd_rf.h" +#include "zd_usb.h" +#include "zd_chip.h" + +#define IS_AL2230S(chip) ((chip)->al2230s_bit || (chip)->rf.type == AL2230S_RF) + +static const u32 zd1211_al2230_table[][3] = { + RF_CHANNEL( 1) = { 0x03f790, 0x033331, 0x00000d, }, + RF_CHANNEL( 2) = { 0x03f790, 0x0b3331, 0x00000d, }, + RF_CHANNEL( 3) = { 0x03e790, 0x033331, 0x00000d, }, + RF_CHANNEL( 4) = { 0x03e790, 0x0b3331, 0x00000d, }, + RF_CHANNEL( 5) = { 0x03f7a0, 0x033331, 0x00000d, }, + RF_CHANNEL( 6) = { 0x03f7a0, 0x0b3331, 0x00000d, }, + RF_CHANNEL( 7) = { 0x03e7a0, 0x033331, 0x00000d, }, + RF_CHANNEL( 8) = { 0x03e7a0, 0x0b3331, 0x00000d, }, + RF_CHANNEL( 9) = { 0x03f7b0, 0x033331, 0x00000d, }, + RF_CHANNEL(10) = { 0x03f7b0, 0x0b3331, 0x00000d, }, + RF_CHANNEL(11) = { 0x03e7b0, 0x033331, 0x00000d, }, + RF_CHANNEL(12) = { 0x03e7b0, 0x0b3331, 0x00000d, }, + RF_CHANNEL(13) = { 0x03f7c0, 0x033331, 0x00000d, }, + RF_CHANNEL(14) = { 0x03e7c0, 0x066661, 0x00000d, }, +}; + +static const u32 zd1211b_al2230_table[][3] = { + RF_CHANNEL( 1) = { 0x09efc0, 0x8cccc0, 0xb00000, }, + RF_CHANNEL( 2) = { 0x09efc0, 0x8cccd0, 0xb00000, }, + RF_CHANNEL( 3) = { 0x09e7c0, 0x8cccc0, 0xb00000, }, + RF_CHANNEL( 4) = { 0x09e7c0, 0x8cccd0, 0xb00000, }, + RF_CHANNEL( 5) = { 0x05efc0, 0x8cccc0, 0xb00000, }, + RF_CHANNEL( 6) = { 0x05efc0, 0x8cccd0, 0xb00000, }, + RF_CHANNEL( 7) = { 0x05e7c0, 0x8cccc0, 0xb00000, }, + RF_CHANNEL( 8) = { 0x05e7c0, 0x8cccd0, 0xb00000, }, + RF_CHANNEL( 9) = { 0x0defc0, 0x8cccc0, 0xb00000, }, + RF_CHANNEL(10) = { 0x0defc0, 0x8cccd0, 0xb00000, }, + RF_CHANNEL(11) = { 0x0de7c0, 0x8cccc0, 0xb00000, }, + RF_CHANNEL(12) = { 0x0de7c0, 0x8cccd0, 0xb00000, }, + RF_CHANNEL(13) = { 0x03efc0, 0x8cccc0, 0xb00000, }, + RF_CHANNEL(14) = { 0x03e7c0, 0x866660, 0xb00000, }, +}; + +static const struct zd_ioreq16 zd1211b_ioreqs_shared_1[] = { + { CR240, 0x57 }, { CR9, 0xe0 }, +}; + +static const struct zd_ioreq16 ioreqs_init_al2230s[] = { + { CR47, 0x1e }, /* MARK_002 */ + { CR106, 0x22 }, + { CR107, 0x2a }, /* MARK_002 */ + { CR109, 0x13 }, /* MARK_002 */ + { CR118, 0xf8 }, /* MARK_002 */ + { CR119, 0x12 }, { CR122, 0xe0 }, + { CR128, 0x10 }, /* MARK_001 from 0xe->0x10 */ + { CR129, 0x0e }, /* MARK_001 from 0xd->0x0e */ + { CR130, 0x10 }, /* MARK_001 from 0xb->0x0d */ +}; + +static int zd1211b_al2230_finalize_rf(struct zd_chip *chip) +{ + int r; + static const struct zd_ioreq16 ioreqs[] = { + { CR80, 0x30 }, { CR81, 0x30 }, { CR79, 0x58 }, + { CR12, 0xf0 }, { CR77, 0x1b }, { CR78, 0x58 }, + { CR203, 0x06 }, + { }, + + { CR240, 0x80 }, + }; + + r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); + if (r) + return r; + + /* related to antenna selection? */ + if (chip->new_phy_layout) { + r = zd_iowrite16_locked(chip, 0xe1, CR9); + if (r) + return r; + } + + return zd_iowrite16_locked(chip, 0x06, CR203); +} + +static int zd1211_al2230_init_hw(struct zd_rf *rf) +{ + int r; + struct zd_chip *chip = zd_rf_to_chip(rf); + + static const struct zd_ioreq16 ioreqs_init[] = { + { CR15, 0x20 }, { CR23, 0x40 }, { CR24, 0x20 }, + { CR26, 0x11 }, { CR28, 0x3e }, { CR29, 0x00 }, + { CR44, 0x33 }, { CR106, 0x2a }, { CR107, 0x1a }, + { CR109, 0x09 }, { CR110, 0x27 }, { CR111, 0x2b }, + { CR112, 0x2b }, { CR119, 0x0a }, { CR10, 0x89 }, + /* for newest (3rd cut) AL2300 */ + { CR17, 0x28 }, + { CR26, 0x93 }, { CR34, 0x30 }, + /* for newest (3rd cut) AL2300 */ + { CR35, 0x3e }, + { CR41, 0x24 }, { CR44, 0x32 }, + /* for newest (3rd cut) AL2300 */ + { CR46, 0x96 }, + { CR47, 0x1e }, { CR79, 0x58 }, { CR80, 0x30 }, + { CR81, 0x30 }, { CR87, 0x0a }, { CR89, 0x04 }, + { CR92, 0x0a }, { CR99, 0x28 }, { CR100, 0x00 }, + { CR101, 0x13 }, { CR102, 0x27 }, { CR106, 0x24 }, + { CR107, 0x2a }, { CR109, 0x09 }, { CR110, 0x13 }, + { CR111, 0x1f }, { CR112, 0x1f }, { CR113, 0x27 }, + { CR114, 0x27 }, + /* for newest (3rd cut) AL2300 */ + { CR115, 0x24 }, + { CR116, 0x24 }, { CR117, 0xf4 }, { CR118, 0xfc }, + { CR119, 0x10 }, { CR120, 0x4f }, { CR121, 0x77 }, + { CR122, 0xe0 }, { CR137, 0x88 }, { CR252, 0xff }, + { CR253, 0xff }, + }; + + static const struct zd_ioreq16 ioreqs_pll[] = { + /* shdnb(PLL_ON)=0 */ + { CR251, 0x2f }, + /* shdnb(PLL_ON)=1 */ + { CR251, 0x3f }, + { CR138, 0x28 }, { CR203, 0x06 }, + }; + + static const u32 rv1[] = { + /* Channel 1 */ + 0x03f790, + 0x033331, + 0x00000d, + + 0x0b3331, + 0x03b812, + 0x00fff3, + }; + + static const u32 rv2[] = { + 0x000da4, + 0x0f4dc5, /* fix freq shift, 0x04edc5 */ + 0x0805b6, + 0x011687, + 0x000688, + 0x0403b9, /* external control TX power (CR31) */ + 0x00dbba, + 0x00099b, + 0x0bdffc, + 0x00000d, + 0x00500f, + }; + + static const u32 rv3[] = { + 0x00d00f, + 0x004c0f, + 0x00540f, + 0x00700f, + 0x00500f, + }; + + r = zd_iowrite16a_locked(chip, ioreqs_init, ARRAY_SIZE(ioreqs_init)); + if (r) + return r; + + if (IS_AL2230S(chip)) { + r = zd_iowrite16a_locked(chip, ioreqs_init_al2230s, + ARRAY_SIZE(ioreqs_init_al2230s)); + if (r) + return r; + } + + r = zd_rfwritev_locked(chip, rv1, ARRAY_SIZE(rv1), RF_RV_BITS); + if (r) + return r; + + /* improve band edge for AL2230S */ + if (IS_AL2230S(chip)) + r = zd_rfwrite_locked(chip, 0x000824, RF_RV_BITS); + else + r = zd_rfwrite_locked(chip, 0x0005a4, RF_RV_BITS); + if (r) + return r; + + r = zd_rfwritev_locked(chip, rv2, ARRAY_SIZE(rv2), RF_RV_BITS); + if (r) + return r; + + r = zd_iowrite16a_locked(chip, ioreqs_pll, ARRAY_SIZE(ioreqs_pll)); + if (r) + return r; + + r = zd_rfwritev_locked(chip, rv3, ARRAY_SIZE(rv3), RF_RV_BITS); + if (r) + return r; + + return 0; +} + +static int zd1211b_al2230_init_hw(struct zd_rf *rf) +{ + int r; + struct zd_chip *chip = zd_rf_to_chip(rf); + + static const struct zd_ioreq16 ioreqs1[] = { + { CR10, 0x89 }, { CR15, 0x20 }, + { CR17, 0x2B }, /* for newest(3rd cut) AL2230 */ + { CR23, 0x40 }, { CR24, 0x20 }, { CR26, 0x93 }, + { CR28, 0x3e }, { CR29, 0x00 }, + { CR33, 0x28 }, /* 5621 */ + { CR34, 0x30 }, + { CR35, 0x3e }, /* for newest(3rd cut) AL2230 */ + { CR41, 0x24 }, { CR44, 0x32 }, + { CR46, 0x99 }, /* for newest(3rd cut) AL2230 */ + { CR47, 0x1e }, + + /* ZD1211B 05.06.10 */ + { CR48, 0x06 }, { CR49, 0xf9 }, { CR51, 0x01 }, + { CR52, 0x80 }, { CR53, 0x7e }, { CR65, 0x00 }, + { CR66, 0x00 }, { CR67, 0x00 }, { CR68, 0x00 }, + { CR69, 0x28 }, + + { CR79, 0x58 }, { CR80, 0x30 }, { CR81, 0x30 }, + { CR87, 0x0a }, { CR89, 0x04 }, + { CR91, 0x00 }, /* 5621 */ + { CR92, 0x0a }, + { CR98, 0x8d }, /* 4804, for 1212 new algorithm */ + { CR99, 0x00 }, /* 5621 */ + { CR101, 0x13 }, { CR102, 0x27 }, + { CR106, 0x24 }, /* for newest(3rd cut) AL2230 */ + { CR107, 0x2a }, + { CR109, 0x13 }, /* 4804, for 1212 new algorithm */ + { CR110, 0x1f }, /* 4804, for 1212 new algorithm */ + { CR111, 0x1f }, { CR112, 0x1f }, { CR113, 0x27 }, + { CR114, 0x27 }, + { CR115, 0x26 }, /* 24->26 at 4902 for newest(3rd cut) AL2230 */ + { CR116, 0x24 }, + { CR117, 0xfa }, /* for 1211b */ + { CR118, 0xfa }, /* for 1211b */ + { CR119, 0x10 }, + { CR120, 0x4f }, + { CR121, 0x6c }, /* for 1211b */ + { CR122, 0xfc }, /* E0->FC at 4902 */ + { CR123, 0x57 }, /* 5623 */ + { CR125, 0xad }, /* 4804, for 1212 new algorithm */ + { CR126, 0x6c }, /* 5614 */ + { CR127, 0x03 }, /* 4804, for 1212 new algorithm */ + { CR137, 0x50 }, /* 5614 */ + { CR138, 0xa8 }, + { CR144, 0xac }, /* 5621 */ + { CR150, 0x0d }, { CR252, 0x34 }, { CR253, 0x34 }, + }; + + static const u32 rv1[] = { + 0x8cccd0, + 0x481dc0, + 0xcfff00, + 0x25a000, + }; + + static const u32 rv2[] = { + /* To improve AL2230 yield, improve phase noise, 4713 */ + 0x25a000, + 0xa3b2f0, + + 0x6da010, /* Reg6 update for MP versio */ + 0xe36280, /* Modified by jxiao for Bor-Chin on 2004/08/02 */ + 0x116000, + 0x9dc020, /* External control TX power (CR31) */ + 0x5ddb00, /* RegA update for MP version */ + 0xd99000, /* RegB update for MP version */ + 0x3ffbd0, /* RegC update for MP version */ + 0xb00000, /* RegD update for MP version */ + + /* improve phase noise and remove phase calibration,4713 */ + 0xf01a00, + }; + + static const struct zd_ioreq16 ioreqs2[] = { + { CR251, 0x2f }, /* shdnb(PLL_ON)=0 */ + { CR251, 0x7f }, /* shdnb(PLL_ON)=1 */ + }; + + static const u32 rv3[] = { + /* To improve AL2230 yield, 4713 */ + 0xf01b00, + 0xf01e00, + 0xf01a00, + }; + + static const struct zd_ioreq16 ioreqs3[] = { + /* related to 6M band edge patching, happens unconditionally */ + { CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 }, + }; + + r = zd_iowrite16a_locked(chip, zd1211b_ioreqs_shared_1, + ARRAY_SIZE(zd1211b_ioreqs_shared_1)); + if (r) + return r; + r = zd_iowrite16a_locked(chip, ioreqs1, ARRAY_SIZE(ioreqs1)); + if (r) + return r; + + if (IS_AL2230S(chip)) { + r = zd_iowrite16a_locked(chip, ioreqs_init_al2230s, + ARRAY_SIZE(ioreqs_init_al2230s)); + if (r) + return r; + } + + r = zd_rfwritev_cr_locked(chip, zd1211b_al2230_table[0], 3); + if (r) + return r; + r = zd_rfwritev_cr_locked(chip, rv1, ARRAY_SIZE(rv1)); + if (r) + return r; + + if (IS_AL2230S(chip)) + r = zd_rfwrite_locked(chip, 0x241000, RF_RV_BITS); + else + r = zd_rfwrite_locked(chip, 0x25a000, RF_RV_BITS); + if (r) + return r; + + r = zd_rfwritev_cr_locked(chip, rv2, ARRAY_SIZE(rv2)); + if (r) + return r; + r = zd_iowrite16a_locked(chip, ioreqs2, ARRAY_SIZE(ioreqs2)); + if (r) + return r; + r = zd_rfwritev_cr_locked(chip, rv3, ARRAY_SIZE(rv3)); + if (r) + return r; + r = zd_iowrite16a_locked(chip, ioreqs3, ARRAY_SIZE(ioreqs3)); + if (r) + return r; + return zd1211b_al2230_finalize_rf(chip); +} + +static int zd1211_al2230_set_channel(struct zd_rf *rf, u8 channel) +{ + int r; + const u32 *rv = zd1211_al2230_table[channel-1]; + struct zd_chip *chip = zd_rf_to_chip(rf); + static const struct zd_ioreq16 ioreqs[] = { + { CR138, 0x28 }, + { CR203, 0x06 }, + }; + + r = zd_rfwritev_locked(chip, rv, 3, RF_RV_BITS); + if (r) + return r; + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static int zd1211b_al2230_set_channel(struct zd_rf *rf, u8 channel) +{ + int r; + const u32 *rv = zd1211b_al2230_table[channel-1]; + struct zd_chip *chip = zd_rf_to_chip(rf); + + r = zd_iowrite16a_locked(chip, zd1211b_ioreqs_shared_1, + ARRAY_SIZE(zd1211b_ioreqs_shared_1)); + if (r) + return r; + + r = zd_rfwritev_cr_locked(chip, rv, 3); + if (r) + return r; + + return zd1211b_al2230_finalize_rf(chip); +} + +static int zd1211_al2230_switch_radio_on(struct zd_rf *rf) +{ + struct zd_chip *chip = zd_rf_to_chip(rf); + static const struct zd_ioreq16 ioreqs[] = { + { CR11, 0x00 }, + { CR251, 0x3f }, + }; + + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static int zd1211b_al2230_switch_radio_on(struct zd_rf *rf) +{ + struct zd_chip *chip = zd_rf_to_chip(rf); + static const struct zd_ioreq16 ioreqs[] = { + { CR11, 0x00 }, + { CR251, 0x7f }, + }; + + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static int al2230_switch_radio_off(struct zd_rf *rf) +{ + struct zd_chip *chip = zd_rf_to_chip(rf); + static const struct zd_ioreq16 ioreqs[] = { + { CR11, 0x04 }, + { CR251, 0x2f }, + }; + + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +int zd_rf_init_al2230(struct zd_rf *rf) +{ + struct zd_chip *chip = zd_rf_to_chip(rf); + + rf->switch_radio_off = al2230_switch_radio_off; + if (zd_chip_is_zd1211b(chip)) { + rf->init_hw = zd1211b_al2230_init_hw; + rf->set_channel = zd1211b_al2230_set_channel; + rf->switch_radio_on = zd1211b_al2230_switch_radio_on; + } else { + rf->init_hw = zd1211_al2230_init_hw; + rf->set_channel = zd1211_al2230_set_channel; + rf->switch_radio_on = zd1211_al2230_switch_radio_on; + } + rf->patch_6m_band_edge = zd_rf_generic_patch_6m; + rf->patch_cck_gain = 1; + return 0; +} diff -puN /dev/null drivers/net/wireless/zd1211rw-mac80211/zd_rf_al7230b.c --- /dev/null +++ a/drivers/net/wireless/zd1211rw-mac80211/zd_rf_al7230b.c @@ -0,0 +1,492 @@ +/* zd_rf_al7230b.c: Functions for the AL7230B RF controller + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "zd_rf.h" +#include "zd_usb.h" +#include "zd_chip.h" + +static const u32 chan_rv[][2] = { + RF_CHANNEL( 1) = { 0x09ec00, 0x8cccc8 }, + RF_CHANNEL( 2) = { 0x09ec00, 0x8cccd8 }, + RF_CHANNEL( 3) = { 0x09ec00, 0x8cccc0 }, + RF_CHANNEL( 4) = { 0x09ec00, 0x8cccd0 }, + RF_CHANNEL( 5) = { 0x05ec00, 0x8cccc8 }, + RF_CHANNEL( 6) = { 0x05ec00, 0x8cccd8 }, + RF_CHANNEL( 7) = { 0x05ec00, 0x8cccc0 }, + RF_CHANNEL( 8) = { 0x05ec00, 0x8cccd0 }, + RF_CHANNEL( 9) = { 0x0dec00, 0x8cccc8 }, + RF_CHANNEL(10) = { 0x0dec00, 0x8cccd8 }, + RF_CHANNEL(11) = { 0x0dec00, 0x8cccc0 }, + RF_CHANNEL(12) = { 0x0dec00, 0x8cccd0 }, + RF_CHANNEL(13) = { 0x03ec00, 0x8cccc8 }, + RF_CHANNEL(14) = { 0x03ec00, 0x866660 }, +}; + +static const u32 std_rv[] = { + 0x4ff821, + 0xc5fbfc, + 0x21ebfe, + 0xafd401, /* freq shift 0xaad401 */ + 0x6cf56a, + 0xe04073, + 0x193d76, + 0x9dd844, + 0x500007, + 0xd8c010, +}; + +static const u32 rv_init1[] = { + 0x3c9000, + 0xbfffff, + 0x700000, + 0xf15d58, +}; + +static const u32 rv_init2[] = { + 0xf15d59, + 0xf15d5c, + 0xf15d58, +}; + +static const struct zd_ioreq16 ioreqs_sw[] = { + { CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 }, + { CR38, 0x38 }, { CR136, 0xdf }, +}; + +static int zd1211b_al7230b_finalize(struct zd_chip *chip) +{ + int r; + static const struct zd_ioreq16 ioreqs[] = { + { CR80, 0x30 }, { CR81, 0x30 }, { CR79, 0x58 }, + { CR12, 0xf0 }, { CR77, 0x1b }, { CR78, 0x58 }, + { CR203, 0x04 }, + { }, + { CR240, 0x80 }, + }; + + r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); + if (r) + return r; + + if (chip->new_phy_layout) { + /* antenna selection? */ + r = zd_iowrite16_locked(chip, 0xe5, CR9); + if (r) + return r; + } + + return zd_iowrite16_locked(chip, 0x04, CR203); +} + +static int zd1211_al7230b_init_hw(struct zd_rf *rf) +{ + int r; + struct zd_chip *chip = zd_rf_to_chip(rf); + + /* All of these writes are identical to AL2230 unless otherwise + * specified */ + static const struct zd_ioreq16 ioreqs_1[] = { + /* This one is 7230-specific, and happens before the rest */ + { CR240, 0x57 }, + { }, + + { CR15, 0x20 }, { CR23, 0x40 }, { CR24, 0x20 }, + { CR26, 0x11 }, { CR28, 0x3e }, { CR29, 0x00 }, + { CR44, 0x33 }, + /* This value is different for 7230 (was: 0x2a) */ + { CR106, 0x22 }, + { CR107, 0x1a }, { CR109, 0x09 }, { CR110, 0x27 }, + { CR111, 0x2b }, { CR112, 0x2b }, { CR119, 0x0a }, + /* This happened further down in AL2230, + * and the value changed (was: 0xe0) */ + { CR122, 0xfc }, + { CR10, 0x89 }, + /* for newest (3rd cut) AL2300 */ + { CR17, 0x28 }, + { CR26, 0x93 }, { CR34, 0x30 }, + /* for newest (3rd cut) AL2300 */ + { CR35, 0x3e }, + { CR41, 0x24 }, { CR44, 0x32 }, + /* for newest (3rd cut) AL2300 */ + { CR46, 0x96 }, + { CR47, 0x1e }, { CR79, 0x58 }, { CR80, 0x30 }, + { CR81, 0x30 }, { CR87, 0x0a }, { CR89, 0x04 }, + { CR92, 0x0a }, { CR99, 0x28 }, + /* This value is different for 7230 (was: 0x00) */ + { CR100, 0x02 }, + { CR101, 0x13 }, { CR102, 0x27 }, + /* This value is different for 7230 (was: 0x24) */ + { CR106, 0x22 }, + /* This value is different for 7230 (was: 0x2a) */ + { CR107, 0x3f }, + { CR109, 0x09 }, + /* This value is different for 7230 (was: 0x13) */ + { CR110, 0x1f }, + { CR111, 0x1f }, { CR112, 0x1f }, { CR113, 0x27 }, + { CR114, 0x27 }, + /* for newest (3rd cut) AL2300 */ + { CR115, 0x24 }, + /* This value is different for 7230 (was: 0x24) */ + { CR116, 0x3f }, + /* This value is different for 7230 (was: 0xf4) */ + { CR117, 0xfa }, + { CR118, 0xfc }, { CR119, 0x10 }, { CR120, 0x4f }, + { CR121, 0x77 }, { CR137, 0x88 }, + /* This one is 7230-specific */ + { CR138, 0xa8 }, + /* This value is different for 7230 (was: 0xff) */ + { CR252, 0x34 }, + /* This value is different for 7230 (was: 0xff) */ + { CR253, 0x34 }, + + /* PLL_OFF */ + { CR251, 0x2f }, + }; + + static const struct zd_ioreq16 ioreqs_2[] = { + { CR251, 0x3f }, /* PLL_ON */ + { CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 }, + { CR38, 0x38 }, { CR136, 0xdf }, + }; + + r = zd_iowrite16a_locked(chip, ioreqs_1, ARRAY_SIZE(ioreqs_1)); + if (r) + return r; + + r = zd_rfwritev_cr_locked(chip, chan_rv[0], ARRAY_SIZE(chan_rv[0])); + if (r) + return r; + + r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv)); + if (r) + return r; + + r = zd_rfwritev_cr_locked(chip, rv_init1, ARRAY_SIZE(rv_init1)); + if (r) + return r; + + r = zd_iowrite16a_locked(chip, ioreqs_2, ARRAY_SIZE(ioreqs_2)); + if (r) + return r; + + r = zd_rfwritev_cr_locked(chip, rv_init2, ARRAY_SIZE(rv_init2)); + if (r) + return r; + + r = zd_iowrite16_locked(chip, 0x06, CR203); + if (r) + return r; + r = zd_iowrite16_locked(chip, 0x80, CR240); + if (r) + return r; + + return 0; +} + +static int zd1211b_al7230b_init_hw(struct zd_rf *rf) +{ + int r; + struct zd_chip *chip = zd_rf_to_chip(rf); + + static const struct zd_ioreq16 ioreqs_1[] = { + { CR240, 0x57 }, { CR9, 0x9 }, + { }, + { CR10, 0x8b }, { CR15, 0x20 }, + { CR17, 0x2B }, /* for newest (3rd cut) AL2230 */ + { CR20, 0x10 }, /* 4N25->Stone Request */ + { CR23, 0x40 }, { CR24, 0x20 }, { CR26, 0x93 }, + { CR28, 0x3e }, { CR29, 0x00 }, + { CR33, 0x28 }, /* 5613 */ + { CR34, 0x30 }, + { CR35, 0x3e }, /* for newest (3rd cut) AL2230 */ + { CR41, 0x24 }, { CR44, 0x32 }, + { CR46, 0x99 }, /* for newest (3rd cut) AL2230 */ + { CR47, 0x1e }, + + /* ZD1215 5610 */ + { CR48, 0x00 }, { CR49, 0x00 }, { CR51, 0x01 }, + { CR52, 0x80 }, { CR53, 0x7e }, { CR65, 0x00 }, + { CR66, 0x00 }, { CR67, 0x00 }, { CR68, 0x00 }, + { CR69, 0x28 }, + + { CR79, 0x58 }, { CR80, 0x30 }, { CR81, 0x30 }, + { CR87, 0x0A }, { CR89, 0x04 }, + { CR90, 0x58 }, /* 5112 */ + { CR91, 0x00 }, /* 5613 */ + { CR92, 0x0a }, + { CR98, 0x8d }, /* 4804, for 1212 new algorithm */ + { CR99, 0x00 }, { CR100, 0x02 }, { CR101, 0x13 }, + { CR102, 0x27 }, + { CR106, 0x20 }, /* change to 0x24 for AL7230B */ + { CR109, 0x13 }, /* 4804, for 1212 new algorithm */ + { CR112, 0x1f }, + }; + + static const struct zd_ioreq16 ioreqs_new_phy[] = { + { CR107, 0x28 }, + { CR110, 0x1f }, /* 5127, 0x13->0x1f */ + { CR111, 0x1f }, /* 0x13 to 0x1f for AL7230B */ + { CR116, 0x2a }, { CR118, 0xfa }, { CR119, 0x12 }, + { CR121, 0x6c }, /* 5613 */ + }; + + static const struct zd_ioreq16 ioreqs_old_phy[] = { + { CR107, 0x24 }, + { CR110, 0x13 }, /* 5127, 0x13->0x1f */ + { CR111, 0x13 }, /* 0x13 to 0x1f for AL7230B */ + { CR116, 0x24 }, { CR118, 0xfc }, { CR119, 0x11 }, + { CR121, 0x6a }, /* 5613 */ + }; + + static const struct zd_ioreq16 ioreqs_2[] = { + { CR113, 0x27 }, { CR114, 0x27 }, { CR115, 0x24 }, + { CR117, 0xfa }, { CR120, 0x4f }, + { CR122, 0xfc }, /* E0->FCh at 4901 */ + { CR123, 0x57 }, /* 5613 */ + { CR125, 0xad }, /* 4804, for 1212 new algorithm */ + { CR126, 0x6c }, /* 5613 */ + { CR127, 0x03 }, /* 4804, for 1212 new algorithm */ + { CR130, 0x10 }, + { CR131, 0x00 }, /* 5112 */ + { CR137, 0x50 }, /* 5613 */ + { CR138, 0xa8 }, /* 5112 */ + { CR144, 0xac }, /* 5613 */ + { CR148, 0x40 }, /* 5112 */ + { CR149, 0x40 }, /* 4O07, 50->40 */ + { CR150, 0x1a }, /* 5112, 0C->1A */ + { CR252, 0x34 }, { CR253, 0x34 }, + { CR251, 0x2f }, /* PLL_OFF */ + }; + + static const struct zd_ioreq16 ioreqs_3[] = { + { CR251, 0x7f }, /* PLL_ON */ + { CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 }, + { CR38, 0x38 }, { CR136, 0xdf }, + }; + + r = zd_iowrite16a_locked(chip, ioreqs_1, ARRAY_SIZE(ioreqs_1)); + if (r) + return r; + + if (chip->new_phy_layout) + r = zd_iowrite16a_locked(chip, ioreqs_new_phy, + ARRAY_SIZE(ioreqs_new_phy)); + else + r = zd_iowrite16a_locked(chip, ioreqs_old_phy, + ARRAY_SIZE(ioreqs_old_phy)); + if (r) + return r; + + r = zd_iowrite16a_locked(chip, ioreqs_2, ARRAY_SIZE(ioreqs_2)); + if (r) + return r; + + r = zd_rfwritev_cr_locked(chip, chan_rv[0], ARRAY_SIZE(chan_rv[0])); + if (r) + return r; + + r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv)); + if (r) + return r; + + r = zd_rfwritev_cr_locked(chip, rv_init1, ARRAY_SIZE(rv_init1)); + if (r) + return r; + + r = zd_iowrite16a_locked(chip, ioreqs_3, ARRAY_SIZE(ioreqs_3)); + if (r) + return r; + + r = zd_rfwritev_cr_locked(chip, rv_init2, ARRAY_SIZE(rv_init2)); + if (r) + return r; + + return zd1211b_al7230b_finalize(chip); +} + +static int zd1211_al7230b_set_channel(struct zd_rf *rf, u8 channel) +{ + int r; + const u32 *rv = chan_rv[channel-1]; + struct zd_chip *chip = zd_rf_to_chip(rf); + + static const struct zd_ioreq16 ioreqs[] = { + /* PLL_ON */ + { CR251, 0x3f }, + { CR203, 0x06 }, { CR240, 0x08 }, + }; + + r = zd_iowrite16_locked(chip, 0x57, CR240); + if (r) + return r; + + /* PLL_OFF */ + r = zd_iowrite16_locked(chip, 0x2f, CR251); + if (r) + return r; + + r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv)); + if (r) + return r; + + r = zd_rfwrite_cr_locked(chip, 0x3c9000); + if (r) + return r; + r = zd_rfwrite_cr_locked(chip, 0xf15d58); + if (r) + return r; + + r = zd_iowrite16a_locked(chip, ioreqs_sw, ARRAY_SIZE(ioreqs_sw)); + if (r) + return r; + + r = zd_rfwritev_cr_locked(chip, rv, 2); + if (r) + return r; + + r = zd_rfwrite_cr_locked(chip, 0x3c9000); + if (r) + return r; + + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static int zd1211b_al7230b_set_channel(struct zd_rf *rf, u8 channel) +{ + int r; + const u32 *rv = chan_rv[channel-1]; + struct zd_chip *chip = zd_rf_to_chip(rf); + + r = zd_iowrite16_locked(chip, 0x57, CR240); + if (r) + return r; + r = zd_iowrite16_locked(chip, 0xe4, CR9); + if (r) + return r; + + /* PLL_OFF */ + r = zd_iowrite16_locked(chip, 0x2f, CR251); + if (r) + return r; + r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv)); + if (r) + return r; + + r = zd_rfwrite_cr_locked(chip, 0x3c9000); + if (r) + return r; + r = zd_rfwrite_cr_locked(chip, 0xf15d58); + if (r) + return r; + + r = zd_iowrite16a_locked(chip, ioreqs_sw, ARRAY_SIZE(ioreqs_sw)); + if (r) + return r; + + r = zd_rfwritev_cr_locked(chip, rv, 2); + if (r) + return r; + + r = zd_rfwrite_cr_locked(chip, 0x3c9000); + if (r) + return r; + + r = zd_iowrite16_locked(chip, 0x7f, CR251); + if (r) + return r; + + return zd1211b_al7230b_finalize(chip); +} + +static int zd1211_al7230b_switch_radio_on(struct zd_rf *rf) +{ + struct zd_chip *chip = zd_rf_to_chip(rf); + static const struct zd_ioreq16 ioreqs[] = { + { CR11, 0x00 }, + { CR251, 0x3f }, + }; + + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static int zd1211b_al7230b_switch_radio_on(struct zd_rf *rf) +{ + struct zd_chip *chip = zd_rf_to_chip(rf); + static const struct zd_ioreq16 ioreqs[] = { + { CR11, 0x00 }, + { CR251, 0x7f }, + }; + + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static int al7230b_switch_radio_off(struct zd_rf *rf) +{ + struct zd_chip *chip = zd_rf_to_chip(rf); + static const struct zd_ioreq16 ioreqs[] = { + { CR11, 0x04 }, + { CR251, 0x2f }, + }; + + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +/* ZD1211B+AL7230B 6m band edge patching differs slightly from other + * configurations */ +static int zd1211b_al7230b_patch_6m(struct zd_rf *rf, u8 channel) +{ + struct zd_chip *chip = zd_rf_to_chip(rf); + struct zd_ioreq16 ioreqs[] = { + { CR128, 0x14 }, { CR129, 0x12 }, + }; + + /* FIXME: Channel 11 is not the edge for all regulatory domains. */ + if (channel == 1) { + ioreqs[0].value = 0x0e; + ioreqs[1].value = 0x10; + } else if (channel == 11) { + ioreqs[0].value = 0x10; + ioreqs[1].value = 0x10; + } + + dev_dbg_f(zd_chip_dev(chip), "patching for channel %d\n", channel); + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +int zd_rf_init_al7230b(struct zd_rf *rf) +{ + struct zd_chip *chip = zd_rf_to_chip(rf); + + if (zd_chip_is_zd1211b(chip)) { + rf->init_hw = zd1211b_al7230b_init_hw; + rf->switch_radio_on = zd1211b_al7230b_switch_radio_on; + rf->set_channel = zd1211b_al7230b_set_channel; + rf->patch_6m_band_edge = zd1211b_al7230b_patch_6m; + } else { + rf->init_hw = zd1211_al7230b_init_hw; + rf->switch_radio_on = zd1211_al7230b_switch_radio_on; + rf->set_channel = zd1211_al7230b_set_channel; + rf->patch_6m_band_edge = zd_rf_generic_patch_6m; + rf->patch_cck_gain = 1; + } + + rf->switch_radio_off = al7230b_switch_radio_off; + + return 0; +} diff -puN /dev/null drivers/net/wireless/zd1211rw-mac80211/zd_rf_rf2959.c --- /dev/null +++ a/drivers/net/wireless/zd1211rw-mac80211/zd_rf_rf2959.c @@ -0,0 +1,279 @@ +/* zd_rf_rfmd.c: Functions for the RFMD RF controller + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "zd_rf.h" +#include "zd_usb.h" +#include "zd_chip.h" + +static const u32 rf2959_table[][2] = { + RF_CHANNEL( 1) = { 0x181979, 0x1e6666 }, + RF_CHANNEL( 2) = { 0x181989, 0x1e6666 }, + RF_CHANNEL( 3) = { 0x181999, 0x1e6666 }, + RF_CHANNEL( 4) = { 0x1819a9, 0x1e6666 }, + RF_CHANNEL( 5) = { 0x1819b9, 0x1e6666 }, + RF_CHANNEL( 6) = { 0x1819c9, 0x1e6666 }, + RF_CHANNEL( 7) = { 0x1819d9, 0x1e6666 }, + RF_CHANNEL( 8) = { 0x1819e9, 0x1e6666 }, + RF_CHANNEL( 9) = { 0x1819f9, 0x1e6666 }, + RF_CHANNEL(10) = { 0x181a09, 0x1e6666 }, + RF_CHANNEL(11) = { 0x181a19, 0x1e6666 }, + RF_CHANNEL(12) = { 0x181a29, 0x1e6666 }, + RF_CHANNEL(13) = { 0x181a39, 0x1e6666 }, + RF_CHANNEL(14) = { 0x181a60, 0x1c0000 }, +}; + +#if 0 +static int bits(u32 rw, int from, int to) +{ + rw &= ~(0xffffffffU << (to+1)); + rw >>= from; + return rw; +} + +static int bit(u32 rw, int bit) +{ + return bits(rw, bit, bit); +} + +static void dump_regwrite(u32 rw) +{ + int reg = bits(rw, 18, 22); + int rw_flag = bits(rw, 23, 23); + PDEBUG("rf2959 %#010x reg %d rw %d", rw, reg, rw_flag); + + switch (reg) { + case 0: + PDEBUG("reg0 CFG1 ref_sel %d hybernate %d rf_vco_reg_en %d" + " if_vco_reg_en %d if_vga_en %d", + bits(rw, 14, 15), bit(rw, 3), bit(rw, 2), bit(rw, 1), + bit(rw, 0)); + break; + case 1: + PDEBUG("reg1 IFPLL1 pll_en1 %d kv_en1 %d vtc_en1 %d lpf1 %d" + " cpl1 %d pdp1 %d autocal_en1 %d ld_en1 %d ifloopr %d" + " ifloopc %d dac1 %d", + bit(rw, 17), bit(rw, 16), bit(rw, 15), bit(rw, 14), + bit(rw, 13), bit(rw, 12), bit(rw, 11), bit(rw, 10), + bits(rw, 7, 9), bits(rw, 4, 6), bits(rw, 0, 3)); + break; + case 2: + PDEBUG("reg2 IFPLL2 n1 %d num1 %d", + bits(rw, 6, 17), bits(rw, 0, 5)); + break; + case 3: + PDEBUG("reg3 IFPLL3 num %d", bits(rw, 0, 17)); + break; + case 4: + PDEBUG("reg4 IFPLL4 dn1 %#04x ct_def1 %d kv_def1 %d", + bits(rw, 8, 16), bits(rw, 4, 7), bits(rw, 0, 3)); + break; + case 5: + PDEBUG("reg5 RFPLL1 pll_en %d kv_en %d vtc_en %d lpf %d cpl %d" + " pdp %d autocal_en %d ld_en %d rfloopr %d rfloopc %d" + " dac %d", + bit(rw, 17), bit(rw, 16), bit(rw, 15), bit(rw, 14), + bit(rw, 13), bit(rw, 12), bit(rw, 11), bit(rw, 10), + bits(rw, 7, 9), bits(rw, 4, 6), bits(rw, 0,3)); + break; + case 6: + PDEBUG("reg6 RFPLL2 n %d num %d", + bits(rw, 6, 17), bits(rw, 0, 5)); + break; + case 7: + PDEBUG("reg7 RFPLL3 num2 %d", bits(rw, 0, 17)); + break; + case 8: + PDEBUG("reg8 RFPLL4 dn %#06x ct_def %d kv_def %d", + bits(rw, 8, 16), bits(rw, 4, 7), bits(rw, 0, 3)); + break; + case 9: + PDEBUG("reg9 CAL1 tvco %d tlock %d m_ct_value %d ld_window %d", + bits(rw, 13, 17), bits(rw, 8, 12), bits(rw, 3, 7), + bits(rw, 0, 2)); + break; + case 10: + PDEBUG("reg10 TXRX1 rxdcfbbyps %d pcontrol %d txvgc %d" + " rxlpfbw %d txlpfbw %d txdiffmode %d txenmode %d" + " intbiasen %d tybypass %d", + bit(rw, 17), bits(rw, 15, 16), bits(rw, 10, 14), + bits(rw, 7, 9), bits(rw, 4, 6), bit(rw, 3), bit(rw, 2), + bit(rw, 1), bit(rw, 0)); + break; + case 11: + PDEBUG("reg11 PCNT1 mid_bias %d p_desired %d pc_offset %d" + " tx_delay %d", + bits(rw, 15, 17), bits(rw, 9, 14), bits(rw, 3, 8), + bits(rw, 0, 2)); + break; + case 12: + PDEBUG("reg12 PCNT2 max_power %d mid_power %d min_power %d", + bits(rw, 12, 17), bits(rw, 6, 11), bits(rw, 0, 5)); + break; + case 13: + PDEBUG("reg13 VCOT1 rfpll vco comp %d ifpll vco comp %d" + " lobias %d if_biasbuf %d if_biasvco %d rf_biasbuf %d" + " rf_biasvco %d", + bit(rw, 17), bit(rw, 16), bit(rw, 15), + bits(rw, 8, 9), bits(rw, 5, 7), bits(rw, 3, 4), + bits(rw, 0, 2)); + break; + case 14: + PDEBUG("reg14 IQCAL rx_acal %d rx_pcal %d" + " tx_acal %d tx_pcal %d", + bits(rw, 13, 17), bits(rw, 9, 12), bits(rw, 4, 8), + bits(rw, 0, 3)); + break; + } +} +#endif /* 0 */ + +static int rf2959_init_hw(struct zd_rf *rf) +{ + int r; + struct zd_chip *chip = zd_rf_to_chip(rf); + + static const struct zd_ioreq16 ioreqs[] = { + { CR2, 0x1E }, { CR9, 0x20 }, { CR10, 0x89 }, + { CR11, 0x00 }, { CR15, 0xD0 }, { CR17, 0x68 }, + { CR19, 0x4a }, { CR20, 0x0c }, { CR21, 0x0E }, + { CR23, 0x48 }, + /* normal size for cca threshold */ + { CR24, 0x14 }, + /* { CR24, 0x20 }, */ + { CR26, 0x90 }, { CR27, 0x30 }, { CR29, 0x20 }, + { CR31, 0xb2 }, { CR32, 0x43 }, { CR33, 0x28 }, + { CR38, 0x30 }, { CR34, 0x0f }, { CR35, 0xF0 }, + { CR41, 0x2a }, { CR46, 0x7F }, { CR47, 0x1E }, + { CR51, 0xc5 }, { CR52, 0xc5 }, { CR53, 0xc5 }, + { CR79, 0x58 }, { CR80, 0x30 }, { CR81, 0x30 }, + { CR82, 0x00 }, { CR83, 0x24 }, { CR84, 0x04 }, + { CR85, 0x00 }, { CR86, 0x10 }, { CR87, 0x2A }, + { CR88, 0x10 }, { CR89, 0x24 }, { CR90, 0x18 }, + /* { CR91, 0x18 }, */ + /* should solve continous CTS frame problems */ + { CR91, 0x00 }, + { CR92, 0x0a }, { CR93, 0x00 }, { CR94, 0x01 }, + { CR95, 0x00 }, { CR96, 0x40 }, { CR97, 0x37 }, + { CR98, 0x05 }, { CR99, 0x28 }, { CR100, 0x00 }, + { CR101, 0x13 }, { CR102, 0x27 }, { CR103, 0x27 }, + { CR104, 0x18 }, { CR105, 0x12 }, + /* normal size */ + { CR106, 0x1a }, + /* { CR106, 0x22 }, */ + { CR107, 0x24 }, { CR108, 0x0a }, { CR109, 0x13 }, + { CR110, 0x2F }, { CR111, 0x27 }, { CR112, 0x27 }, + { CR113, 0x27 }, { CR114, 0x27 }, { CR115, 0x40 }, + { CR116, 0x40 }, { CR117, 0xF0 }, { CR118, 0xF0 }, + { CR119, 0x16 }, + /* no TX continuation */ + { CR122, 0x00 }, + /* { CR122, 0xff }, */ + { CR127, 0x03 }, { CR131, 0x08 }, { CR138, 0x28 }, + { CR148, 0x44 }, { CR150, 0x10 }, { CR169, 0xBB }, + { CR170, 0xBB }, + }; + + static const u32 rv[] = { + 0x000007, /* REG0(CFG1) */ + 0x07dd43, /* REG1(IFPLL1) */ + 0x080959, /* REG2(IFPLL2) */ + 0x0e6666, + 0x116a57, /* REG4 */ + 0x17dd43, /* REG5 */ + 0x1819f9, /* REG6 */ + 0x1e6666, + 0x214554, + 0x25e7fa, + 0x27fffa, + /* The Zydas driver somehow forgets to set this value. It's + * only set for Japan. We are using internal power control + * for now. + */ + 0x294128, /* internal power */ + /* 0x28252c, */ /* External control TX power */ + /* CR31_CCK, CR51_6-36M, CR52_48M, CR53_54M */ + 0x2c0000, + 0x300000, + 0x340000, /* REG13(0xD) */ + 0x381e0f, /* REG14(0xE) */ + /* Bogus, RF2959's data sheet doesn't know register 27, which is + * actually referenced here. The commented 0x11 is 17. + */ + 0x6c180f, /* REG27(0x11) */ + }; + + r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); + if (r) + return r; + + return zd_rfwritev_locked(chip, rv, ARRAY_SIZE(rv), RF_RV_BITS); +} + +static int rf2959_set_channel(struct zd_rf *rf, u8 channel) +{ + int i, r; + const u32 *rv = rf2959_table[channel-1]; + struct zd_chip *chip = zd_rf_to_chip(rf); + + for (i = 0; i < 2; i++) { + r = zd_rfwrite_locked(chip, rv[i], RF_RV_BITS); + if (r) + return r; + } + return 0; +} + +static int rf2959_switch_radio_on(struct zd_rf *rf) +{ + static const struct zd_ioreq16 ioreqs[] = { + { CR10, 0x89 }, + { CR11, 0x00 }, + }; + struct zd_chip *chip = zd_rf_to_chip(rf); + + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static int rf2959_switch_radio_off(struct zd_rf *rf) +{ + static const struct zd_ioreq16 ioreqs[] = { + { CR10, 0x15 }, + { CR11, 0x81 }, + }; + struct zd_chip *chip = zd_rf_to_chip(rf); + + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +int zd_rf_init_rf2959(struct zd_rf *rf) +{ + struct zd_chip *chip = zd_rf_to_chip(rf); + + if (zd_chip_is_zd1211b(chip)) { + dev_err(zd_chip_dev(chip), + "RF2959 is currently not supported for ZD1211B" + " devices\n"); + return -ENODEV; + } + rf->init_hw = rf2959_init_hw; + rf->set_channel = rf2959_set_channel; + rf->switch_radio_on = rf2959_switch_radio_on; + rf->switch_radio_off = rf2959_switch_radio_off; + return 0; +} diff -puN /dev/null drivers/net/wireless/zd1211rw-mac80211/zd_rf_uw2453.c --- /dev/null +++ a/drivers/net/wireless/zd1211rw-mac80211/zd_rf_uw2453.c @@ -0,0 +1,534 @@ +/* zd_rf_uw2453.c: Functions for the UW2453 RF controller + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "zd_rf.h" +#include "zd_usb.h" +#include "zd_chip.h" + +/* This RF programming code is based upon the code found in v2.16.0.0 of the + * ZyDAS vendor driver. Unlike other RF's, Ubec publish full technical specs + * for this RF on their website, so we're able to understand more than + * usual as to what is going on. Thumbs up for Ubec for doing that. */ + +/* The 3-wire serial interface provides access to 8 write-only registers. + * The data format is a 4 bit register address followed by a 20 bit value. */ +#define UW2453_REGWRITE(reg, val) ((((reg) & 0xf) << 20) | ((val) & 0xfffff)) + +/* For channel tuning, we have to configure registers 1 (synthesizer), 2 (synth + * fractional divide ratio) and 3 (VCO config). + * + * We configure the RF to produce an interrupt when the PLL is locked onto + * the configured frequency. During initialization, we run through a variety + * of different VCO configurations on channel 1 until we detect a PLL lock. + * When this happens, we remember which VCO configuration produced the lock + * and use it later. Actually, we use the configuration *after* the one that + * produced the lock, which seems odd, but it works. + * + * If we do not see a PLL lock on any standard VCO config, we fall back on an + * autocal configuration, which has a fixed (as opposed to per-channel) VCO + * config and different synth values from the standard set (divide ratio + * is still shared with the standard set). */ + +/* The per-channel synth values for all standard VCO configurations. These get + * written to register 1. */ +static const u8 uw2453_std_synth[] = { + RF_CHANNEL( 1) = 0x47, + RF_CHANNEL( 2) = 0x47, + RF_CHANNEL( 3) = 0x67, + RF_CHANNEL( 4) = 0x67, + RF_CHANNEL( 5) = 0x67, + RF_CHANNEL( 6) = 0x67, + RF_CHANNEL( 7) = 0x57, + RF_CHANNEL( 8) = 0x57, + RF_CHANNEL( 9) = 0x57, + RF_CHANNEL(10) = 0x57, + RF_CHANNEL(11) = 0x77, + RF_CHANNEL(12) = 0x77, + RF_CHANNEL(13) = 0x77, + RF_CHANNEL(14) = 0x4f, +}; + +/* This table stores the synthesizer fractional divide ratio for *all* VCO + * configurations (both standard and autocal). These get written to register 2. + */ +static const u16 uw2453_synth_divide[] = { + RF_CHANNEL( 1) = 0x999, + RF_CHANNEL( 2) = 0x99b, + RF_CHANNEL( 3) = 0x998, + RF_CHANNEL( 4) = 0x99a, + RF_CHANNEL( 5) = 0x999, + RF_CHANNEL( 6) = 0x99b, + RF_CHANNEL( 7) = 0x998, + RF_CHANNEL( 8) = 0x99a, + RF_CHANNEL( 9) = 0x999, + RF_CHANNEL(10) = 0x99b, + RF_CHANNEL(11) = 0x998, + RF_CHANNEL(12) = 0x99a, + RF_CHANNEL(13) = 0x999, + RF_CHANNEL(14) = 0xccc, +}; + +/* Here is the data for all the standard VCO configurations. We shrink our + * table a little by observing that both channels in a consecutive pair share + * the same value. We also observe that the high 4 bits ([0:3] in the specs) + * are all 'Reserved' and are always set to 0x4 - we chop them off in the data + * below. */ +#define CHAN_TO_PAIRIDX(a) ((a - 1) / 2) +#define RF_CHANPAIR(a,b) [CHAN_TO_PAIRIDX(a)] +static const u16 uw2453_std_vco_cfg[][7] = { + { /* table 1 */ + RF_CHANPAIR( 1, 2) = 0x664d, + RF_CHANPAIR( 3, 4) = 0x604d, + RF_CHANPAIR( 5, 6) = 0x6675, + RF_CHANPAIR( 7, 8) = 0x6475, + RF_CHANPAIR( 9, 10) = 0x6655, + RF_CHANPAIR(11, 12) = 0x6455, + RF_CHANPAIR(13, 14) = 0x6665, + }, + { /* table 2 */ + RF_CHANPAIR( 1, 2) = 0x666d, + RF_CHANPAIR( 3, 4) = 0x606d, + RF_CHANPAIR( 5, 6) = 0x664d, + RF_CHANPAIR( 7, 8) = 0x644d, + RF_CHANPAIR( 9, 10) = 0x6675, + RF_CHANPAIR(11, 12) = 0x6475, + RF_CHANPAIR(13, 14) = 0x6655, + }, + { /* table 3 */ + RF_CHANPAIR( 1, 2) = 0x665d, + RF_CHANPAIR( 3, 4) = 0x605d, + RF_CHANPAIR( 5, 6) = 0x666d, + RF_CHANPAIR( 7, 8) = 0x646d, + RF_CHANPAIR( 9, 10) = 0x664d, + RF_CHANPAIR(11, 12) = 0x644d, + RF_CHANPAIR(13, 14) = 0x6675, + }, + { /* table 4 */ + RF_CHANPAIR( 1, 2) = 0x667d, + RF_CHANPAIR( 3, 4) = 0x607d, + RF_CHANPAIR( 5, 6) = 0x665d, + RF_CHANPAIR( 7, 8) = 0x645d, + RF_CHANPAIR( 9, 10) = 0x666d, + RF_CHANPAIR(11, 12) = 0x646d, + RF_CHANPAIR(13, 14) = 0x664d, + }, + { /* table 5 */ + RF_CHANPAIR( 1, 2) = 0x6643, + RF_CHANPAIR( 3, 4) = 0x6043, + RF_CHANPAIR( 5, 6) = 0x667d, + RF_CHANPAIR( 7, 8) = 0x647d, + RF_CHANPAIR( 9, 10) = 0x665d, + RF_CHANPAIR(11, 12) = 0x645d, + RF_CHANPAIR(13, 14) = 0x666d, + }, + { /* table 6 */ + RF_CHANPAIR( 1, 2) = 0x6663, + RF_CHANPAIR( 3, 4) = 0x6063, + RF_CHANPAIR( 5, 6) = 0x6643, + RF_CHANPAIR( 7, 8) = 0x6443, + RF_CHANPAIR( 9, 10) = 0x667d, + RF_CHANPAIR(11, 12) = 0x647d, + RF_CHANPAIR(13, 14) = 0x665d, + }, + { /* table 7 */ + RF_CHANPAIR( 1, 2) = 0x6653, + RF_CHANPAIR( 3, 4) = 0x6053, + RF_CHANPAIR( 5, 6) = 0x6663, + RF_CHANPAIR( 7, 8) = 0x6463, + RF_CHANPAIR( 9, 10) = 0x6643, + RF_CHANPAIR(11, 12) = 0x6443, + RF_CHANPAIR(13, 14) = 0x667d, + }, + { /* table 8 */ + RF_CHANPAIR( 1, 2) = 0x6673, + RF_CHANPAIR( 3, 4) = 0x6073, + RF_CHANPAIR( 5, 6) = 0x6653, + RF_CHANPAIR( 7, 8) = 0x6453, + RF_CHANPAIR( 9, 10) = 0x6663, + RF_CHANPAIR(11, 12) = 0x6463, + RF_CHANPAIR(13, 14) = 0x6643, + }, + { /* table 9 */ + RF_CHANPAIR( 1, 2) = 0x664b, + RF_CHANPAIR( 3, 4) = 0x604b, + RF_CHANPAIR( 5, 6) = 0x6673, + RF_CHANPAIR( 7, 8) = 0x6473, + RF_CHANPAIR( 9, 10) = 0x6653, + RF_CHANPAIR(11, 12) = 0x6453, + RF_CHANPAIR(13, 14) = 0x6663, + }, + { /* table 10 */ + RF_CHANPAIR( 1, 2) = 0x666b, + RF_CHANPAIR( 3, 4) = 0x606b, + RF_CHANPAIR( 5, 6) = 0x664b, + RF_CHANPAIR( 7, 8) = 0x644b, + RF_CHANPAIR( 9, 10) = 0x6673, + RF_CHANPAIR(11, 12) = 0x6473, + RF_CHANPAIR(13, 14) = 0x6653, + }, + { /* table 11 */ + RF_CHANPAIR( 1, 2) = 0x665b, + RF_CHANPAIR( 3, 4) = 0x605b, + RF_CHANPAIR( 5, 6) = 0x666b, + RF_CHANPAIR( 7, 8) = 0x646b, + RF_CHANPAIR( 9, 10) = 0x664b, + RF_CHANPAIR(11, 12) = 0x644b, + RF_CHANPAIR(13, 14) = 0x6673, + }, + +}; + +/* The per-channel synth values for autocal. These get written to register 1. */ +static const u16 uw2453_autocal_synth[] = { + RF_CHANNEL( 1) = 0x6847, + RF_CHANNEL( 2) = 0x6847, + RF_CHANNEL( 3) = 0x6867, + RF_CHANNEL( 4) = 0x6867, + RF_CHANNEL( 5) = 0x6867, + RF_CHANNEL( 6) = 0x6867, + RF_CHANNEL( 7) = 0x6857, + RF_CHANNEL( 8) = 0x6857, + RF_CHANNEL( 9) = 0x6857, + RF_CHANNEL(10) = 0x6857, + RF_CHANNEL(11) = 0x6877, + RF_CHANNEL(12) = 0x6877, + RF_CHANNEL(13) = 0x6877, + RF_CHANNEL(14) = 0x684f, +}; + +/* The VCO configuration for autocal (all channels) */ +static const u16 UW2453_AUTOCAL_VCO_CFG = 0x6662; + +/* TX gain settings. The array index corresponds to the TX power integration + * values found in the EEPROM. The values get written to register 7. */ +static u32 uw2453_txgain[] = { + [0x00] = 0x0e313, + [0x01] = 0x0fb13, + [0x02] = 0x0e093, + [0x03] = 0x0f893, + [0x04] = 0x0ea93, + [0x05] = 0x1f093, + [0x06] = 0x1f493, + [0x07] = 0x1f693, + [0x08] = 0x1f393, + [0x09] = 0x1f35b, + [0x0a] = 0x1e6db, + [0x0b] = 0x1ff3f, + [0x0c] = 0x1ffff, + [0x0d] = 0x361d7, + [0x0e] = 0x37fbf, + [0x0f] = 0x3ff8b, + [0x10] = 0x3ff33, + [0x11] = 0x3fb3f, + [0x12] = 0x3ffff, +}; + +/* RF-specific structure */ +struct uw2453_priv { + /* index into synth/VCO config tables where PLL lock was found + * -1 means autocal */ + int config; +}; + +#define UW2453_PRIV(rf) ((struct uw2453_priv *) (rf)->priv) + +static int uw2453_synth_set_channel(struct zd_chip *chip, int channel, + bool autocal) +{ + int r; + int idx = channel - 1; + u32 val; + + if (autocal) + val = UW2453_REGWRITE(1, uw2453_autocal_synth[idx]); + else + val = UW2453_REGWRITE(1, uw2453_std_synth[idx]); + + r = zd_rfwrite_locked(chip, val, RF_RV_BITS); + if (r) + return r; + + return zd_rfwrite_locked(chip, + UW2453_REGWRITE(2, uw2453_synth_divide[idx]), RF_RV_BITS); +} + +static int uw2453_write_vco_cfg(struct zd_chip *chip, u16 value) +{ + /* vendor driver always sets these upper bits even though the specs say + * they are reserved */ + u32 val = 0x40000 | value; + return zd_rfwrite_locked(chip, UW2453_REGWRITE(3, val), RF_RV_BITS); +} + +static int uw2453_init_mode(struct zd_chip *chip) +{ + static const u32 rv[] = { + UW2453_REGWRITE(0, 0x25f98), /* enter IDLE mode */ + UW2453_REGWRITE(0, 0x25f9a), /* enter CAL_VCO mode */ + UW2453_REGWRITE(0, 0x25f94), /* enter RX/TX mode */ + UW2453_REGWRITE(0, 0x27fd4), /* power down RSSI circuit */ + }; + + return zd_rfwritev_locked(chip, rv, ARRAY_SIZE(rv), RF_RV_BITS); +} + +static int uw2453_set_tx_gain_level(struct zd_chip *chip, int channel) +{ + u8 int_value = chip->pwr_int_values[channel - 1]; + + if (int_value >= ARRAY_SIZE(uw2453_txgain)) { + dev_dbg_f(zd_chip_dev(chip), "can't configure TX gain for " + "int value %x on channel %d\n", int_value, channel); + return 0; + } + + return zd_rfwrite_locked(chip, + UW2453_REGWRITE(7, uw2453_txgain[int_value]), RF_RV_BITS); +} + +static int uw2453_init_hw(struct zd_rf *rf) +{ + int i, r; + int found_config = -1; + u16 intr_status; + struct zd_chip *chip = zd_rf_to_chip(rf); + + static const struct zd_ioreq16 ioreqs[] = { + { CR10, 0x89 }, { CR15, 0x20 }, + { CR17, 0x28 }, /* 6112 no change */ + { CR23, 0x38 }, { CR24, 0x20 }, { CR26, 0x93 }, + { CR27, 0x15 }, { CR28, 0x3e }, { CR29, 0x00 }, + { CR33, 0x28 }, { CR34, 0x30 }, + { CR35, 0x43 }, /* 6112 3e->43 */ + { CR41, 0x24 }, { CR44, 0x32 }, + { CR46, 0x92 }, /* 6112 96->92 */ + { CR47, 0x1e }, + { CR48, 0x04 }, /* 5602 Roger */ + { CR49, 0xfa }, { CR79, 0x58 }, { CR80, 0x30 }, + { CR81, 0x30 }, { CR87, 0x0a }, { CR89, 0x04 }, + { CR91, 0x00 }, { CR92, 0x0a }, { CR98, 0x8d }, + { CR99, 0x28 }, { CR100, 0x02 }, + { CR101, 0x09 }, /* 6112 13->1f 6220 1f->13 6407 13->9 */ + { CR102, 0x27 }, + { CR106, 0x1c }, /* 5d07 5112 1f->1c 6220 1c->1f 6221 1f->1c */ + { CR107, 0x1c }, /* 6220 1c->1a 5221 1a->1c */ + { CR109, 0x13 }, + { CR110, 0x1f }, /* 6112 13->1f 6221 1f->13 6407 13->0x09 */ + { CR111, 0x13 }, { CR112, 0x1f }, { CR113, 0x27 }, + { CR114, 0x23 }, /* 6221 27->23 */ + { CR115, 0x24 }, /* 6112 24->1c 6220 1c->24 */ + { CR116, 0x24 }, /* 6220 1c->24 */ + { CR117, 0xfa }, /* 6112 fa->f8 6220 f8->f4 6220 f4->fa */ + { CR118, 0xf0 }, /* 5d07 6112 f0->f2 6220 f2->f0 */ + { CR119, 0x1a }, /* 6112 1a->10 6220 10->14 6220 14->1a */ + { CR120, 0x4f }, + { CR121, 0x1f }, /* 6220 4f->1f */ + { CR122, 0xf0 }, { CR123, 0x57 }, { CR125, 0xad }, + { CR126, 0x6c }, { CR127, 0x03 }, + { CR128, 0x14 }, /* 6302 12->11 */ + { CR129, 0x12 }, /* 6301 10->0f */ + { CR130, 0x10 }, { CR137, 0x50 }, { CR138, 0xa8 }, + { CR144, 0xac }, { CR146, 0x20 }, { CR252, 0xff }, + { CR253, 0xff }, + }; + + static const u32 rv[] = { + UW2453_REGWRITE(4, 0x2b), /* configure reciever gain */ + UW2453_REGWRITE(5, 0x19e4f), /* configure transmitter gain */ + UW2453_REGWRITE(6, 0xf81ad), /* enable RX/TX filter tuning */ + UW2453_REGWRITE(7, 0x3fffe), /* disable TX gain in test mode */ + + /* enter CAL_FIL mode, TX gain set by registers, RX gain set by pins, + * RSSI circuit powered down, reduced RSSI range */ + UW2453_REGWRITE(0, 0x25f9c), /* 5d01 cal_fil */ + + /* synthesizer configuration for channel 1 */ + UW2453_REGWRITE(1, 0x47), + UW2453_REGWRITE(2, 0x999), + + /* disable manual VCO band selection */ + UW2453_REGWRITE(3, 0x7602), + + /* enable manual VCO band selection, configure current level */ + UW2453_REGWRITE(3, 0x46063), + }; + + r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); + if (r) + return r; + + r = zd_rfwritev_locked(chip, rv, ARRAY_SIZE(rv), RF_RV_BITS); + if (r) + return r; + + r = uw2453_init_mode(chip); + if (r) + return r; + + /* Try all standard VCO configuration settings on channel 1 */ + for (i = 0; i < ARRAY_SIZE(uw2453_std_vco_cfg) - 1; i++) { + /* Configure synthesizer for channel 1 */ + r = uw2453_synth_set_channel(chip, 1, false); + if (r) + return r; + + /* Write VCO config */ + r = uw2453_write_vco_cfg(chip, uw2453_std_vco_cfg[i][0]); + if (r) + return r; + + /* ack interrupt event */ + r = zd_iowrite16_locked(chip, 0x0f, UW2453_INTR_REG); + if (r) + return r; + + /* check interrupt status */ + r = zd_ioread16_locked(chip, &intr_status, UW2453_INTR_REG); + if (r) + return r; + + if (!intr_status & 0xf) { + dev_dbg_f(zd_chip_dev(chip), + "PLL locked on configuration %d\n", i); + found_config = i; + break; + } + } + + if (found_config == -1) { + /* autocal */ + dev_dbg_f(zd_chip_dev(chip), + "PLL did not lock, using autocal\n"); + + r = uw2453_synth_set_channel(chip, 1, true); + if (r) + return r; + + r = uw2453_write_vco_cfg(chip, UW2453_AUTOCAL_VCO_CFG); + if (r) + return r; + } + + /* To match the vendor driver behaviour, we use the configuration after + * the one that produced a lock. */ + UW2453_PRIV(rf)->config = found_config + 1; + + return zd_iowrite16_locked(chip, 0x06, CR203); +} + +static int uw2453_set_channel(struct zd_rf *rf, u8 channel) +{ + int r; + u16 vco_cfg; + int config = UW2453_PRIV(rf)->config; + bool autocal = (config == -1); + struct zd_chip *chip = zd_rf_to_chip(rf); + + static const struct zd_ioreq16 ioreqs[] = { + { CR80, 0x30 }, { CR81, 0x30 }, { CR79, 0x58 }, + { CR12, 0xf0 }, { CR77, 0x1b }, { CR78, 0x58 }, + }; + + r = uw2453_synth_set_channel(chip, channel, autocal); + if (r) + return r; + + if (autocal) + vco_cfg = UW2453_AUTOCAL_VCO_CFG; + else + vco_cfg = uw2453_std_vco_cfg[config][CHAN_TO_PAIRIDX(channel)]; + + r = uw2453_write_vco_cfg(chip, vco_cfg); + if (r) + return r; + + r = uw2453_init_mode(chip); + if (r) + return r; + + r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); + if (r) + return r; + + r = uw2453_set_tx_gain_level(chip, channel); + if (r) + return r; + + return zd_iowrite16_locked(chip, 0x06, CR203); +} + +static int uw2453_switch_radio_on(struct zd_rf *rf) +{ + int r; + struct zd_chip *chip = zd_rf_to_chip(rf); + struct zd_ioreq16 ioreqs[] = { + { CR11, 0x00 }, { CR251, 0x3f }, + }; + + /* enter RXTX mode */ + r = zd_rfwrite_locked(chip, UW2453_REGWRITE(0, 0x25f94), RF_RV_BITS); + if (r) + return r; + + if (zd_chip_is_zd1211b(chip)) + ioreqs[1].value = 0x7f; + + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static int uw2453_switch_radio_off(struct zd_rf *rf) +{ + int r; + struct zd_chip *chip = zd_rf_to_chip(rf); + static const struct zd_ioreq16 ioreqs[] = { + { CR11, 0x04 }, { CR251, 0x2f }, + }; + + /* enter IDLE mode */ + /* FIXME: shouldn't we go to SLEEP? sent email to zydas */ + r = zd_rfwrite_locked(chip, UW2453_REGWRITE(0, 0x25f90), RF_RV_BITS); + if (r) + return r; + + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static void uw2453_clear(struct zd_rf *rf) +{ + kfree(rf->priv); +} + +int zd_rf_init_uw2453(struct zd_rf *rf) +{ + rf->init_hw = uw2453_init_hw; + rf->set_channel = uw2453_set_channel; + rf->switch_radio_on = uw2453_switch_radio_on; + rf->switch_radio_off = uw2453_switch_radio_off; + rf->patch_6m_band_edge = zd_rf_generic_patch_6m; + rf->clear = uw2453_clear; + /* we have our own TX integration code */ + rf->update_channel_int = 0; + + rf->priv = kmalloc(sizeof(struct uw2453_priv), GFP_KERNEL); + if (rf->priv == NULL) + return -ENOMEM; + + return 0; +} + diff -puN /dev/null drivers/net/wireless/zd1211rw-mac80211/zd_usb.c --- /dev/null +++ a/drivers/net/wireless/zd1211rw-mac80211/zd_usb.c @@ -0,0 +1,1528 @@ +/* zd_usb.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "zd_def.h" +#include "zd_mac.h" +#include "zd_usb.h" + +static struct usb_device_id usb_ids[] = { + /* ZD1211 */ + { USB_DEVICE(0x0ace, 0x1211), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x07b8, 0x6001), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x126f, 0xa006), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x6891, 0xa727), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x0df6, 0x9071), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x0df6, 0x9075), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x157e, 0x300b), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x079b, 0x004a), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x1740, 0x2000), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x157e, 0x3204), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x0586, 0x3402), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x0b3b, 0x5630), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x0b05, 0x170c), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x1435, 0x0711), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x0586, 0x3409), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x0b3b, 0x1630), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x0586, 0x3401), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x14ea, 0xab13), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x13b1, 0x001e), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x0586, 0x3407), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x129b, 0x1666), .driver_info = DEVICE_ZD1211 }, + /* ZD1211B */ + { USB_DEVICE(0x0ace, 0x1215), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x157e, 0x300d), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x079b, 0x0062), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x1582, 0x6003), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x050d, 0x705c), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x083a, 0x4505), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0471, 0x1236), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x13b1, 0x0024), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0586, 0x340f), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0b05, 0x171b), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0586, 0x3410), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0baf, 0x0121), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0586, 0x3412), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0586, 0x3413), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0053, 0x5301), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0411, 0x00da), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x2019, 0x5303), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x129b, 0x1667), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0cde, 0x001a), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0586, 0x340a), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0471, 0x1237), .driver_info = DEVICE_ZD1211B }, + /* "Driverless" devices that need ejecting */ + { USB_DEVICE(0x0ace, 0x2011), .driver_info = DEVICE_INSTALLER }, + { USB_DEVICE(0x0ace, 0x20ff), .driver_info = DEVICE_INSTALLER }, + {} +}; + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("USB driver for devices with the ZD1211 chip."); +MODULE_AUTHOR("Ulrich Kunitz"); +MODULE_AUTHOR("Daniel Drake"); +MODULE_VERSION("1.0"); +MODULE_DEVICE_TABLE(usb, usb_ids); + +#define FW_ZD1211_PREFIX "zd1211/zd1211_" +#define FW_ZD1211B_PREFIX "zd1211/zd1211b_" + +/* USB device initialization */ + +static int request_fw_file( + const struct firmware **fw, const char *name, struct device *device) +{ + int r; + + dev_dbg_f(device, "fw name %s\n", name); + + r = request_firmware(fw, name, device); + if (r) + dev_err(device, + "Could not load firmware file %s. Error number %d\n", + name, r); + return r; +} + +static inline u16 get_bcdDevice(const struct usb_device *udev) +{ + return le16_to_cpu(udev->descriptor.bcdDevice); +} + +enum upload_code_flags { + REBOOT = 1, +}; + +/* Ensures that MAX_TRANSFER_SIZE is even. */ +#define MAX_TRANSFER_SIZE (USB_MAX_TRANSFER_SIZE & ~1) + +static int upload_code(struct usb_device *udev, + const u8 *data, size_t size, u16 code_offset, int flags) +{ + u8 *p; + int r; + + /* USB request blocks need "kmalloced" buffers. + */ + p = kmalloc(MAX_TRANSFER_SIZE, GFP_KERNEL); + if (!p) { + dev_err(&udev->dev, "out of memory\n"); + r = -ENOMEM; + goto error; + } + + size &= ~1; + while (size > 0) { + size_t transfer_size = size <= MAX_TRANSFER_SIZE ? + size : MAX_TRANSFER_SIZE; + + dev_dbg_f(&udev->dev, "transfer size %zu\n", transfer_size); + + memcpy(p, data, transfer_size); + r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_FIRMWARE_DOWNLOAD, + USB_DIR_OUT | USB_TYPE_VENDOR, + code_offset, 0, p, transfer_size, 1000 /* ms */); + if (r < 0) { + dev_err(&udev->dev, + "USB control request for firmware upload" + " failed. Error number %d\n", r); + goto error; + } + transfer_size = r & ~1; + + size -= transfer_size; + data += transfer_size; + code_offset += transfer_size/sizeof(u16); + } + + if (flags & REBOOT) { + u8 ret; + + r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + USB_REQ_FIRMWARE_CONFIRM, + USB_DIR_IN | USB_TYPE_VENDOR, + 0, 0, &ret, sizeof(ret), 5000 /* ms */); + if (r != sizeof(ret)) { + dev_err(&udev->dev, + "control request firmeware confirmation failed." + " Return value %d\n", r); + if (r >= 0) + r = -ENODEV; + goto error; + } + if (ret & 0x80) { + dev_err(&udev->dev, + "Internal error while downloading." + " Firmware confirm return value %#04x\n", + (unsigned int)ret); + r = -ENODEV; + goto error; + } + dev_dbg_f(&udev->dev, "firmware confirm return value %#04x\n", + (unsigned int)ret); + } + + r = 0; +error: + kfree(p); + return r; +} + +static u16 get_word(const void *data, u16 offset) +{ + const __le16 *p = data; + return le16_to_cpu(p[offset]); +} + +static char *get_fw_name(struct zd_usb *usb, char *buffer, size_t size, + const char* postfix) +{ + scnprintf(buffer, size, "%s%s", + usb->is_zd1211b ? + FW_ZD1211B_PREFIX : FW_ZD1211_PREFIX, + postfix); + return buffer; +} + +static int handle_version_mismatch(struct zd_usb *usb, + const struct firmware *ub_fw) +{ + struct usb_device *udev = zd_usb_to_usbdev(usb); + const struct firmware *ur_fw = NULL; + int offset; + int r = 0; + char fw_name[128]; + + r = request_fw_file(&ur_fw, + get_fw_name(usb, fw_name, sizeof(fw_name), "ur"), + &udev->dev); + if (r) + goto error; + + r = upload_code(udev, ur_fw->data, ur_fw->size, FW_START, REBOOT); + if (r) + goto error; + + offset = (E2P_BOOT_CODE_OFFSET * sizeof(u16)); + r = upload_code(udev, ub_fw->data + offset, ub_fw->size - offset, + E2P_START + E2P_BOOT_CODE_OFFSET, REBOOT); + + /* At this point, the vendor driver downloads the whole firmware + * image, hacks around with version IDs, and uploads it again, + * completely overwriting the boot code. We do not do this here as + * it is not required on any tested devices, and it is suspected to + * cause problems. */ +error: + release_firmware(ur_fw); + return r; +} + +static int upload_firmware(struct zd_usb *usb) +{ + int r; + u16 fw_bcdDevice; + u16 bcdDevice; + struct usb_device *udev = zd_usb_to_usbdev(usb); + const struct firmware *ub_fw = NULL; + const struct firmware *uph_fw = NULL; + char fw_name[128]; + + bcdDevice = get_bcdDevice(udev); + + r = request_fw_file(&ub_fw, + get_fw_name(usb, fw_name, sizeof(fw_name), "ub"), + &udev->dev); + if (r) + goto error; + + fw_bcdDevice = get_word(ub_fw->data, E2P_DATA_OFFSET); + + if (fw_bcdDevice != bcdDevice) { + dev_info(&udev->dev, + "firmware version %#06x and device bootcode version " + "%#06x differ\n", fw_bcdDevice, bcdDevice); + if (bcdDevice <= 0x4313) + dev_warn(&udev->dev, "device has old bootcode, please " + "report success or failure\n"); + + r = handle_version_mismatch(usb, ub_fw); + if (r) + goto error; + } else { + dev_dbg_f(&udev->dev, + "firmware device id %#06x is equal to the " + "actual device id\n", fw_bcdDevice); + } + + + r = request_fw_file(&uph_fw, + get_fw_name(usb, fw_name, sizeof(fw_name), "uphr"), + &udev->dev); + if (r) + goto error; + + r = upload_code(udev, uph_fw->data, uph_fw->size, FW_START, REBOOT); + if (r) { + dev_err(&udev->dev, + "Could not upload firmware code uph. Error number %d\n", + r); + } + + /* FALL-THROUGH */ +error: + release_firmware(ub_fw); + release_firmware(uph_fw); + return r; +} + +/* Read data from device address space using "firmware interface" which does + * not require firmware to be loaded. */ +int zd_usb_read_fw(struct zd_usb *usb, zd_addr_t addr, u8 *data, u16 len) +{ + int r; + struct usb_device *udev = zd_usb_to_usbdev(usb); + + r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + USB_REQ_FIRMWARE_READ_DATA, USB_DIR_IN | 0x40, addr, 0, + data, len, 5000); + if (r < 0) { + dev_err(&udev->dev, + "read over firmware interface failed: %d\n", r); + return r; + } else if (r != len) { + dev_err(&udev->dev, + "incomplete read over firmware interface: %d/%d\n", + r, len); + return -EIO; + } + + return 0; +} + +#define urb_dev(urb) (&(urb)->dev->dev) + +static inline void handle_regs_int(struct urb *urb) +{ + struct zd_usb *usb = urb->context; + struct zd_usb_interrupt *intr = &usb->intr; + int len; + + ZD_ASSERT(in_interrupt()); + spin_lock(&intr->lock); + + if (intr->read_regs_enabled) { + intr->read_regs.length = len = urb->actual_length; + + if (len > sizeof(intr->read_regs.buffer)) + len = sizeof(intr->read_regs.buffer); + memcpy(intr->read_regs.buffer, urb->transfer_buffer, len); + intr->read_regs_enabled = 0; + complete(&intr->read_regs.completion); + goto out; + } + + dev_dbg_f(urb_dev(urb), "regs interrupt ignored\n"); +out: + spin_unlock(&intr->lock); +} + +static void int_urb_complete(struct urb *urb) +{ + int r; + struct usb_int_header *hdr; + + switch (urb->status) { + case 0: + break; + case -ESHUTDOWN: + case -EINVAL: + case -ENODEV: + case -ENOENT: + case -ECONNRESET: + case -EPIPE: + goto kfree; + default: + goto resubmit; + } + + if (urb->actual_length < sizeof(hdr)) { + dev_dbg_f(urb_dev(urb), "error: urb %p to small\n", urb); + goto resubmit; + } + + hdr = urb->transfer_buffer; + if (hdr->type != USB_INT_TYPE) { + dev_dbg_f(urb_dev(urb), "error: urb %p wrong type\n", urb); + goto resubmit; + } + + switch (hdr->id) { + case USB_INT_ID_REGS: + handle_regs_int(urb); + break; + case USB_INT_ID_RETRY_FAILED: + zd_mac_tx_failed(zd_usb_to_hw(urb->context)); + break; + default: + dev_dbg_f(urb_dev(urb), "error: urb %p unknown id %x\n", urb, + (unsigned int)hdr->id); + goto resubmit; + } + +resubmit: + r = usb_submit_urb(urb, GFP_ATOMIC); + if (r) { + dev_dbg_f(urb_dev(urb), "resubmit urb %p\n", urb); + goto kfree; + } + return; +kfree: + kfree(urb->transfer_buffer); +} + +static inline int int_urb_interval(struct usb_device *udev) +{ + switch (udev->speed) { + case USB_SPEED_HIGH: + return 4; + case USB_SPEED_LOW: + return 10; + case USB_SPEED_FULL: + default: + return 1; + } +} + +static inline int usb_int_enabled(struct zd_usb *usb) +{ + unsigned long flags; + struct zd_usb_interrupt *intr = &usb->intr; + struct urb *urb; + + spin_lock_irqsave(&intr->lock, flags); + urb = intr->urb; + spin_unlock_irqrestore(&intr->lock, flags); + return urb != NULL; +} + +int zd_usb_enable_int(struct zd_usb *usb) +{ + int r; + struct usb_device *udev; + struct zd_usb_interrupt *intr = &usb->intr; + void *transfer_buffer = NULL; + struct urb *urb; + + dev_dbg_f(zd_usb_dev(usb), "\n"); + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + r = -ENOMEM; + goto out; + } + + ZD_ASSERT(!irqs_disabled()); + spin_lock_irq(&intr->lock); + if (intr->urb) { + spin_unlock_irq(&intr->lock); + r = 0; + goto error_free_urb; + } + intr->urb = urb; + spin_unlock_irq(&intr->lock); + + /* TODO: make it a DMA buffer */ + r = -ENOMEM; + transfer_buffer = kmalloc(USB_MAX_EP_INT_BUFFER, GFP_KERNEL); + if (!transfer_buffer) { + dev_dbg_f(zd_usb_dev(usb), + "couldn't allocate transfer_buffer\n"); + goto error_set_urb_null; + } + + udev = zd_usb_to_usbdev(usb); + usb_fill_int_urb(urb, udev, usb_rcvintpipe(udev, EP_INT_IN), + transfer_buffer, USB_MAX_EP_INT_BUFFER, + int_urb_complete, usb, + intr->interval); + + dev_dbg_f(zd_usb_dev(usb), "submit urb %p\n", intr->urb); + r = usb_submit_urb(urb, GFP_KERNEL); + if (r) { + dev_dbg_f(zd_usb_dev(usb), + "Couldn't submit urb. Error number %d\n", r); + goto error; + } + + return 0; +error: + kfree(transfer_buffer); +error_set_urb_null: + spin_lock_irq(&intr->lock); + intr->urb = NULL; + spin_unlock_irq(&intr->lock); +error_free_urb: + usb_free_urb(urb); +out: + return r; +} + +void zd_usb_disable_int(struct zd_usb *usb) +{ + unsigned long flags; + struct zd_usb_interrupt *intr = &usb->intr; + struct urb *urb; + + spin_lock_irqsave(&intr->lock, flags); + urb = intr->urb; + if (!urb) { + spin_unlock_irqrestore(&intr->lock, flags); + return; + } + intr->urb = NULL; + spin_unlock_irqrestore(&intr->lock, flags); + + usb_kill_urb(urb); + dev_dbg_f(zd_usb_dev(usb), "urb %p killed\n", urb); + usb_free_urb(urb); +} + +static void handle_rx_packet(struct zd_usb *usb, const u8 *buffer, + unsigned int length) +{ + int i; + const struct rx_length_info *length_info; + + if (length < sizeof(struct rx_length_info)) { + /* It's not a complete packet anyhow. */ + return; + } + length_info = (struct rx_length_info *) + (buffer + length - sizeof(struct rx_length_info)); + + /* It might be that three frames are merged into a single URB + * transaction. We have to check for the length info tag. + * + * While testing we discovered that length_info might be unaligned, + * because if USB transactions are merged, the last packet will not + * be padded. Unaligned access might also happen if the length_info + * structure is not present. + */ + if (get_unaligned(&length_info->tag) == cpu_to_le16(RX_LENGTH_INFO_TAG)) + { + unsigned int l, k, n; + for (i = 0, l = 0;; i++) { + k = le16_to_cpu(get_unaligned(&length_info->length[i])); + if (k == 0) + return; + n = l+k; + if (n > length) + return; + zd_mac_rx(zd_usb_to_hw(usb), buffer+l, k); + if (i >= 2) + return; + l = (n+3) & ~3; + } + } else { + zd_mac_rx(zd_usb_to_hw(usb), buffer, length); + } +} + +static void rx_urb_complete(struct urb *urb) +{ + struct zd_usb *usb; + struct zd_usb_rx *rx; + const u8 *buffer; + unsigned int length; + + switch (urb->status) { + case 0: + break; + case -ESHUTDOWN: + case -EINVAL: + case -ENODEV: + case -ENOENT: + case -ECONNRESET: + case -EPIPE: + return; + default: + dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status); + goto resubmit; + } + + buffer = urb->transfer_buffer; + length = urb->actual_length; + usb = urb->context; + rx = &usb->rx; + + if (length%rx->usb_packet_size > rx->usb_packet_size-4) { + /* If there is an old first fragment, we don't care. */ + dev_dbg_f(urb_dev(urb), "*** first fragment ***\n"); + ZD_ASSERT(length <= ARRAY_SIZE(rx->fragment)); + spin_lock(&rx->lock); + memcpy(rx->fragment, buffer, length); + rx->fragment_length = length; + spin_unlock(&rx->lock); + goto resubmit; + } + + spin_lock(&rx->lock); + if (rx->fragment_length > 0) { + /* We are on a second fragment, we believe */ + ZD_ASSERT(length + rx->fragment_length <= + ARRAY_SIZE(rx->fragment)); + dev_dbg_f(urb_dev(urb), "*** second fragment ***\n"); + memcpy(rx->fragment+rx->fragment_length, buffer, length); + handle_rx_packet(usb, rx->fragment, + rx->fragment_length + length); + rx->fragment_length = 0; + spin_unlock(&rx->lock); + } else { + spin_unlock(&rx->lock); + handle_rx_packet(usb, buffer, length); + } + +resubmit: + usb_submit_urb(urb, GFP_ATOMIC); +} + +static struct urb *alloc_rx_urb(struct zd_usb *usb) +{ + struct usb_device *udev = zd_usb_to_usbdev(usb); + struct urb *urb; + void *buffer; + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + return NULL; + buffer = usb_buffer_alloc(udev, USB_MAX_RX_SIZE, GFP_KERNEL, + &urb->transfer_dma); + if (!buffer) { + usb_free_urb(urb); + return NULL; + } + + usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, EP_DATA_IN), + buffer, USB_MAX_RX_SIZE, + rx_urb_complete, usb); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + return urb; +} + +static void free_rx_urb(struct urb *urb) +{ + if (!urb) + return; + usb_buffer_free(urb->dev, urb->transfer_buffer_length, + urb->transfer_buffer, urb->transfer_dma); + usb_free_urb(urb); +} + +int zd_usb_enable_rx(struct zd_usb *usb) +{ + int i, r; + struct zd_usb_rx *rx = &usb->rx; + struct urb **urbs; + + dev_dbg_f(zd_usb_dev(usb), "\n"); + + r = -ENOMEM; + urbs = kcalloc(RX_URBS_COUNT, sizeof(struct urb *), GFP_KERNEL); + if (!urbs) + goto error; + for (i = 0; i < RX_URBS_COUNT; i++) { + urbs[i] = alloc_rx_urb(usb); + if (!urbs[i]) + goto error; + } + + ZD_ASSERT(!irqs_disabled()); + spin_lock_irq(&rx->lock); + if (rx->urbs) { + spin_unlock_irq(&rx->lock); + r = 0; + goto error; + } + rx->urbs = urbs; + rx->urbs_count = RX_URBS_COUNT; + spin_unlock_irq(&rx->lock); + + for (i = 0; i < RX_URBS_COUNT; i++) { + r = usb_submit_urb(urbs[i], GFP_KERNEL); + if (r) + goto error_submit; + } + + return 0; +error_submit: + for (i = 0; i < RX_URBS_COUNT; i++) { + usb_kill_urb(urbs[i]); + } + spin_lock_irq(&rx->lock); + rx->urbs = NULL; + rx->urbs_count = 0; + spin_unlock_irq(&rx->lock); +error: + if (urbs) { + for (i = 0; i < RX_URBS_COUNT; i++) + free_rx_urb(urbs[i]); + } + return r; +} + +void zd_usb_disable_rx(struct zd_usb *usb) +{ + int i; + unsigned long flags; + struct urb **urbs; + unsigned int count; + struct zd_usb_rx *rx = &usb->rx; + + spin_lock_irqsave(&rx->lock, flags); + urbs = rx->urbs; + count = rx->urbs_count; + spin_unlock_irqrestore(&rx->lock, flags); + if (!urbs) + return; + + for (i = 0; i < count; i++) { + usb_kill_urb(urbs[i]); + free_rx_urb(urbs[i]); + } + kfree(urbs); + + spin_lock_irqsave(&rx->lock, flags); + rx->urbs = NULL; + rx->urbs_count = 0; + spin_unlock_irqrestore(&rx->lock, flags); +} + +/** + * zd_usb_disable_tx - disable transmission + * @usb: the zd1211rw-private USB structure + * + * Frees all URBs in the free list and marks the transmission as disabled. + */ +void zd_usb_disable_tx(struct zd_usb *usb) +{ + struct zd_usb_tx *tx = &usb->tx; + unsigned long flags; + struct list_head *pos, *n; + + spin_lock_irqsave(&tx->lock, flags); + list_for_each_safe(pos, n, &tx->free_urb_list) { + list_del(pos); + usb_free_urb(list_entry(pos, struct urb, urb_list)); + } + tx->enabled = 0; + tx->submitted_urbs = 0; + /* The stopped state is ignored, relying on ieee80211_wake_queues() + * in a potentionally following zd_usb_enable_tx(). + */ + spin_unlock_irqrestore(&tx->lock, flags); +} + +/** + * zd_usb_enable_tx - enables transmission + * @usb: a &struct zd_usb pointer + * + * This function enables transmission and prepares the &zd_usb_tx data + * structure. + */ +void zd_usb_enable_tx(struct zd_usb *usb) +{ + unsigned long flags; + struct zd_usb_tx *tx = &usb->tx; + + spin_lock_irqsave(&tx->lock, flags); + tx->enabled = 1; + tx->submitted_urbs = 0; + ieee80211_wake_queues(zd_usb_to_hw(usb)); + tx->stopped = 0; + spin_unlock_irqrestore(&tx->lock, flags); +} + +/** + * alloc_tx_urb - provides an tx URB + * @usb: a &struct zd_usb pointer + * + * Allocates a new URB. If possible takes the urb from the free list in + * usb->tx. + */ +static struct urb *alloc_tx_urb(struct zd_usb *usb) +{ + struct zd_usb_tx *tx = &usb->tx; + unsigned long flags; + struct list_head *entry; + struct urb *urb; + + spin_lock_irqsave(&tx->lock, flags); + if (list_empty(&tx->free_urb_list)) { + urb = usb_alloc_urb(0, GFP_ATOMIC); + goto out; + } + entry = tx->free_urb_list.next; + list_del(entry); + urb = list_entry(entry, struct urb, urb_list); +out: + spin_unlock_irqrestore(&tx->lock, flags); + return urb; +} + +/** + * free_tx_urb - frees a used tx URB + * @usb: a &struct zd_usb pointer + * @urb: URB to be freed + * + * Frees the the transmission URB, which means to put it on the free URB + * list. + */ +static void free_tx_urb(struct zd_usb *usb, struct urb *urb) +{ + struct zd_usb_tx *tx = &usb->tx; + unsigned long flags; + + spin_lock_irqsave(&tx->lock, flags); + if (!tx->enabled) { + usb_free_urb(urb); + goto out; + } + list_add(&urb->urb_list, &tx->free_urb_list); +out: + spin_unlock_irqrestore(&tx->lock, flags); +} + +static void tx_dec_submitted_urbs(struct zd_usb *usb) +{ + struct zd_usb_tx *tx = &usb->tx; + unsigned long flags; + + spin_lock_irqsave(&tx->lock, flags); + --tx->submitted_urbs; + if (tx->stopped && tx->submitted_urbs <= ZD_USB_TX_LOW) { + ieee80211_wake_queues(zd_usb_to_hw(usb)); + tx->stopped = 0; + } + spin_unlock_irqrestore(&tx->lock, flags); +} + +static void tx_inc_submitted_urbs(struct zd_usb *usb) +{ + struct zd_usb_tx *tx = &usb->tx; + unsigned long flags; + + spin_lock_irqsave(&tx->lock, flags); + ++tx->submitted_urbs; + if (!tx->stopped && tx->submitted_urbs > ZD_USB_TX_HIGH) { + ieee80211_stop_queues(zd_usb_to_hw(usb)); + tx->stopped = 1; + } + spin_unlock_irqrestore(&tx->lock, flags); +} + +/** + * tx_urb_complete - completes the execution of an URB + * @urb: a URB + * + * This function is called if the URB has been transferred to a device or an + * error has happened. + */ +static void tx_urb_complete(struct urb *urb) +{ + int r; + struct sk_buff *skb; + struct zd_tx_skb_control_block *cb; + struct zd_usb *usb; + + switch (urb->status) { + case 0: + break; + case -ESHUTDOWN: + case -EINVAL: + case -ENODEV: + case -ENOENT: + case -ECONNRESET: + case -EPIPE: + dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status); + break; + default: + dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status); + goto resubmit; + } +free_urb: + skb = (struct sk_buff *)urb->context; + zd_mac_tx_to_dev(skb, urb->status); + cb = (struct zd_tx_skb_control_block *)skb->cb; + usb = &zd_hw_mac(cb->hw)->chip.usb; + free_tx_urb(usb, urb); + tx_dec_submitted_urbs(usb); + return; +resubmit: + r = usb_submit_urb(urb, GFP_ATOMIC); + if (r) { + dev_dbg_f(urb_dev(urb), "error resubmit urb %p %d\n", urb, r); + goto free_urb; + } +} + +/** + * zd_usb_tx: initiates transfer of a frame of the device + * + * @usb: the zd1211rw-private USB structure + * @skb: a &struct sk_buff pointer + * + * This function tranmits a frame to the device. It doesn't wait for + * completion. The frame must contain the control set and have all the + * control set information available. + * + * The function returns 0 if the transfer has been successfully initiated. + */ +int zd_usb_tx(struct zd_usb *usb, struct sk_buff *skb) +{ + int r; + struct usb_device *udev = zd_usb_to_usbdev(usb); + struct urb *urb; + + urb = alloc_tx_urb(usb); + if (!urb) { + r = -ENOMEM; + goto out; + } + + usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT), + skb->data, skb->len, tx_urb_complete, skb); + + r = usb_submit_urb(urb, GFP_ATOMIC); + if (r) + goto error; + tx_inc_submitted_urbs(usb); + return 0; +error: + free_tx_urb(usb, urb); +out: + return r; +} + +static inline void init_usb_interrupt(struct zd_usb *usb) +{ + struct zd_usb_interrupt *intr = &usb->intr; + + spin_lock_init(&intr->lock); + intr->interval = int_urb_interval(zd_usb_to_usbdev(usb)); + init_completion(&intr->read_regs.completion); + intr->read_regs.cr_int_addr = cpu_to_le16((u16)CR_INTERRUPT); +} + +static inline void init_usb_rx(struct zd_usb *usb) +{ + struct zd_usb_rx *rx = &usb->rx; + spin_lock_init(&rx->lock); + if (interface_to_usbdev(usb->intf)->speed == USB_SPEED_HIGH) { + rx->usb_packet_size = 512; + } else { + rx->usb_packet_size = 64; + } + ZD_ASSERT(rx->fragment_length == 0); +} + +static inline void init_usb_tx(struct zd_usb *usb) +{ + struct zd_usb_tx *tx = &usb->tx; + spin_lock_init(&tx->lock); + tx->enabled = 0; + tx->stopped = 0; + INIT_LIST_HEAD(&tx->free_urb_list); + tx->submitted_urbs = 0; +} + +void zd_usb_init(struct zd_usb *usb, struct ieee80211_hw *hw, + struct usb_interface *intf) +{ + memset(usb, 0, sizeof(*usb)); + usb->intf = usb_get_intf(intf); + usb_set_intfdata(usb->intf, hw); + init_usb_interrupt(usb); + init_usb_tx(usb); + init_usb_rx(usb); +} + +void zd_usb_clear(struct zd_usb *usb) +{ + usb_set_intfdata(usb->intf, NULL); + usb_put_intf(usb->intf); + ZD_MEMCLEAR(usb, sizeof(*usb)); + /* FIXME: usb_interrupt, usb_tx, usb_rx? */ +} + +static const char *speed(enum usb_device_speed speed) +{ + switch (speed) { + case USB_SPEED_LOW: + return "low"; + case USB_SPEED_FULL: + return "full"; + case USB_SPEED_HIGH: + return "high"; + default: + return "unknown speed"; + } +} + +static int scnprint_id(struct usb_device *udev, char *buffer, size_t size) +{ + return scnprintf(buffer, size, "%04hx:%04hx v%04hx %s", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct), + get_bcdDevice(udev), + speed(udev->speed)); +} + +int zd_usb_scnprint_id(struct zd_usb *usb, char *buffer, size_t size) +{ + struct usb_device *udev = interface_to_usbdev(usb->intf); + return scnprint_id(udev, buffer, size); +} + +#ifdef DEBUG +static void print_id(struct usb_device *udev) +{ + char buffer[40]; + + scnprint_id(udev, buffer, sizeof(buffer)); + buffer[sizeof(buffer)-1] = 0; + dev_dbg_f(&udev->dev, "%s\n", buffer); +} +#else +#define print_id(udev) do { } while (0) +#endif + +static int eject_installer(struct usb_interface *intf) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct usb_host_interface *iface_desc = &intf->altsetting[0]; + struct usb_endpoint_descriptor *endpoint; + unsigned char *cmd; + u8 bulk_out_ep; + int r; + + /* Find bulk out endpoint */ + endpoint = &iface_desc->endpoint[1].desc; + if ((endpoint->bEndpointAddress & USB_TYPE_MASK) == USB_DIR_OUT && + (endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_BULK) { + bulk_out_ep = endpoint->bEndpointAddress; + } else { + dev_err(&udev->dev, + "zd1211rw: Could not find bulk out endpoint\n"); + return -ENODEV; + } + + cmd = kzalloc(31, GFP_KERNEL); + if (cmd == NULL) + return -ENODEV; + + /* USB bulk command block */ + cmd[0] = 0x55; /* bulk command signature */ + cmd[1] = 0x53; /* bulk command signature */ + cmd[2] = 0x42; /* bulk command signature */ + cmd[3] = 0x43; /* bulk command signature */ + cmd[14] = 6; /* command length */ + + cmd[15] = 0x1b; /* SCSI command: START STOP UNIT */ + cmd[19] = 0x2; /* eject disc */ + + dev_info(&udev->dev, "Ejecting virtual installer media...\n"); + r = usb_bulk_msg(udev, usb_sndbulkpipe(udev, bulk_out_ep), + cmd, 31, NULL, 2000); + kfree(cmd); + if (r) + return r; + + /* At this point, the device disconnects and reconnects with the real + * ID numbers. */ + + usb_set_intfdata(intf, NULL); + return 0; +} + +int zd_usb_init_hw(struct zd_usb *usb) +{ + int r; + struct zd_mac *mac = zd_usb_to_mac(usb); + + dev_dbg_f(zd_usb_dev(usb), "\n"); + + r = upload_firmware(usb); + if (r) { + dev_err(zd_usb_dev(usb), + "couldn't load firmware. Error number %d\n", r); + return r; + } + + r = usb_reset_configuration(zd_usb_to_usbdev(usb)); + if (r) { + dev_dbg_f(zd_usb_dev(usb), + "couldn't reset configuration. Error number %d\n", r); + return r; + } + + r = zd_mac_init_hw(mac->hw); + if (r) { + dev_dbg_f(zd_usb_dev(usb), + "couldn't initialize mac. Error number %d\n", r); + return r; + } + + usb->initialized = 1; + return 0; +} + +static int probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + int r; + struct usb_device *udev = interface_to_usbdev(intf); + struct zd_usb *usb; + struct ieee80211_hw *hw = NULL; + + print_id(udev); + + if (id->driver_info & DEVICE_INSTALLER) + return eject_installer(intf); + + switch (udev->speed) { + case USB_SPEED_LOW: + case USB_SPEED_FULL: + case USB_SPEED_HIGH: + break; + default: + dev_dbg_f(&intf->dev, "Unknown USB speed\n"); + r = -ENODEV; + goto error; + } + + r = usb_reset_device(udev); + if (r) { + dev_err(&intf->dev, + "couldn't reset usb device. Error number %d\n", r); + goto error; + } + + hw = zd_mac_alloc_hw(intf); + if (hw == NULL) { + r = -ENOMEM; + goto error; + } + + usb = &zd_hw_mac(hw)->chip.usb; + usb->is_zd1211b = (id->driver_info == DEVICE_ZD1211B) != 0; + + r = zd_mac_preinit_hw(hw); + if (r) { + dev_dbg_f(&intf->dev, + "couldn't initialize mac. Error number %d\n", r); + goto error; + } + + r = ieee80211_register_hw(hw); + if (r) { + dev_dbg_f(&intf->dev, + "couldn't register device. Error number %d\n", r); + goto error; + } + + dev_dbg_f(&intf->dev, "successful\n"); + dev_info(&intf->dev, "%s\n", wiphy_name(hw->wiphy)); + return 0; +error: + usb_reset_device(interface_to_usbdev(intf)); + if (hw) { + zd_mac_clear(zd_hw_mac(hw)); + ieee80211_free_hw(hw); + } + return r; +} + +static void disconnect(struct usb_interface *intf) +{ + struct ieee80211_hw *hw = zd_intf_to_hw(intf); + struct zd_mac *mac; + struct zd_usb *usb; + + /* Either something really bad happened, or we're just dealing with + * a DEVICE_INSTALLER. */ + if (hw == NULL) + return; + + mac = zd_hw_mac(hw); + usb = &mac->chip.usb; + + dev_dbg_f(zd_usb_dev(usb), "\n"); + + ieee80211_unregister_hw(hw); + + /* Just in case something has gone wrong! */ + zd_usb_disable_rx(usb); + zd_usb_disable_int(usb); + + /* If the disconnect has been caused by a removal of the + * driver module, the reset allows reloading of the driver. If the + * reset will not be executed here, the upload of the firmware in the + * probe function caused by the reloading of the driver will fail. + */ + usb_reset_device(interface_to_usbdev(intf)); + + zd_mac_clear(mac); + ieee80211_free_hw(hw); + dev_dbg(&intf->dev, "disconnected\n"); +} + +static struct usb_driver driver = { + .name = KBUILD_MODNAME, + .id_table = usb_ids, + .probe = probe, + .disconnect = disconnect, +}; + +struct workqueue_struct *zd_workqueue; + +static int __init usb_init(void) +{ + int r; + + pr_debug("%s usb_init()\n", driver.name); + + zd_workqueue = create_singlethread_workqueue(driver.name); + if (zd_workqueue == NULL) { + printk(KERN_ERR "%s couldn't create workqueue\n", driver.name); + return -ENOMEM; + } + + r = usb_register(&driver); + if (r) { + destroy_workqueue(zd_workqueue); + printk(KERN_ERR "%s usb_register() failed. Error number %d\n", + driver.name, r); + return r; + } + + pr_debug("%s initialized\n", driver.name); + return 0; +} + +static void __exit usb_exit(void) +{ + pr_debug("%s usb_exit()\n", driver.name); + usb_deregister(&driver); + destroy_workqueue(zd_workqueue); +} + +module_init(usb_init); +module_exit(usb_exit); + +static int usb_int_regs_length(unsigned int count) +{ + return sizeof(struct usb_int_regs) + count * sizeof(struct reg_data); +} + +static void prepare_read_regs_int(struct zd_usb *usb) +{ + struct zd_usb_interrupt *intr = &usb->intr; + + spin_lock_irq(&intr->lock); + intr->read_regs_enabled = 1; + INIT_COMPLETION(intr->read_regs.completion); + spin_unlock_irq(&intr->lock); +} + +static void disable_read_regs_int(struct zd_usb *usb) +{ + struct zd_usb_interrupt *intr = &usb->intr; + + spin_lock_irq(&intr->lock); + intr->read_regs_enabled = 0; + spin_unlock_irq(&intr->lock); +} + +static int get_results(struct zd_usb *usb, u16 *values, + struct usb_req_read_regs *req, unsigned int count) +{ + int r; + int i; + struct zd_usb_interrupt *intr = &usb->intr; + struct read_regs_int *rr = &intr->read_regs; + struct usb_int_regs *regs = (struct usb_int_regs *)rr->buffer; + + spin_lock_irq(&intr->lock); + + r = -EIO; + /* The created block size seems to be larger than expected. + * However results appear to be correct. + */ + if (rr->length < usb_int_regs_length(count)) { + dev_dbg_f(zd_usb_dev(usb), + "error: actual length %d less than expected %d\n", + rr->length, usb_int_regs_length(count)); + goto error_unlock; + } + if (rr->length > sizeof(rr->buffer)) { + dev_dbg_f(zd_usb_dev(usb), + "error: actual length %d exceeds buffer size %zu\n", + rr->length, sizeof(rr->buffer)); + goto error_unlock; + } + + for (i = 0; i < count; i++) { + struct reg_data *rd = ®s->regs[i]; + if (rd->addr != req->addr[i]) { + dev_dbg_f(zd_usb_dev(usb), + "rd[%d] addr %#06hx expected %#06hx\n", i, + le16_to_cpu(rd->addr), + le16_to_cpu(req->addr[i])); + goto error_unlock; + } + values[i] = le16_to_cpu(rd->value); + } + + r = 0; +error_unlock: + spin_unlock_irq(&intr->lock); + return r; +} + +int zd_usb_ioread16v(struct zd_usb *usb, u16 *values, + const zd_addr_t *addresses, unsigned int count) +{ + int r; + int i, req_len, actual_req_len; + struct usb_device *udev; + struct usb_req_read_regs *req = NULL; + unsigned long timeout; + + if (count < 1) { + dev_dbg_f(zd_usb_dev(usb), "error: count is zero\n"); + return -EINVAL; + } + if (count > USB_MAX_IOREAD16_COUNT) { + dev_dbg_f(zd_usb_dev(usb), + "error: count %u exceeds possible max %u\n", + count, USB_MAX_IOREAD16_COUNT); + return -EINVAL; + } + if (in_atomic()) { + dev_dbg_f(zd_usb_dev(usb), + "error: io in atomic context not supported\n"); + return -EWOULDBLOCK; + } + if (!usb_int_enabled(usb)) { + dev_dbg_f(zd_usb_dev(usb), + "error: usb interrupt not enabled\n"); + return -EWOULDBLOCK; + } + + req_len = sizeof(struct usb_req_read_regs) + count * sizeof(__le16); + req = kmalloc(req_len, GFP_KERNEL); + if (!req) + return -ENOMEM; + req->id = cpu_to_le16(USB_REQ_READ_REGS); + for (i = 0; i < count; i++) + req->addr[i] = cpu_to_le16((u16)addresses[i]); + + udev = zd_usb_to_usbdev(usb); + prepare_read_regs_int(usb); + r = usb_bulk_msg(udev, usb_sndbulkpipe(udev, EP_REGS_OUT), + req, req_len, &actual_req_len, 1000 /* ms */); + if (r) { + dev_dbg_f(zd_usb_dev(usb), + "error in usb_bulk_msg(). Error number %d\n", r); + goto error; + } + if (req_len != actual_req_len) { + dev_dbg_f(zd_usb_dev(usb), "error in usb_bulk_msg()\n" + " req_len %d != actual_req_len %d\n", + req_len, actual_req_len); + r = -EIO; + goto error; + } + + timeout = wait_for_completion_timeout(&usb->intr.read_regs.completion, + msecs_to_jiffies(1000)); + if (!timeout) { + disable_read_regs_int(usb); + dev_dbg_f(zd_usb_dev(usb), "read timed out\n"); + r = -ETIMEDOUT; + goto error; + } + + r = get_results(usb, values, req, count); +error: + kfree(req); + return r; +} + +int zd_usb_iowrite16v(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs, + unsigned int count) +{ + int r; + struct usb_device *udev; + struct usb_req_write_regs *req = NULL; + int i, req_len, actual_req_len; + + if (count == 0) + return 0; + if (count > USB_MAX_IOWRITE16_COUNT) { + dev_dbg_f(zd_usb_dev(usb), + "error: count %u exceeds possible max %u\n", + count, USB_MAX_IOWRITE16_COUNT); + return -EINVAL; + } + if (in_atomic()) { + dev_dbg_f(zd_usb_dev(usb), + "error: io in atomic context not supported\n"); + return -EWOULDBLOCK; + } + + req_len = sizeof(struct usb_req_write_regs) + + count * sizeof(struct reg_data); + req = kmalloc(req_len, GFP_KERNEL); + if (!req) + return -ENOMEM; + + req->id = cpu_to_le16(USB_REQ_WRITE_REGS); + for (i = 0; i < count; i++) { + struct reg_data *rw = &req->reg_writes[i]; + rw->addr = cpu_to_le16((u16)ioreqs[i].addr); + rw->value = cpu_to_le16(ioreqs[i].value); + } + + udev = zd_usb_to_usbdev(usb); + r = usb_bulk_msg(udev, usb_sndbulkpipe(udev, EP_REGS_OUT), + req, req_len, &actual_req_len, 1000 /* ms */); + if (r) { + dev_dbg_f(zd_usb_dev(usb), + "error in usb_bulk_msg(). Error number %d\n", r); + goto error; + } + if (req_len != actual_req_len) { + dev_dbg_f(zd_usb_dev(usb), + "error in usb_bulk_msg()" + " req_len %d != actual_req_len %d\n", + req_len, actual_req_len); + r = -EIO; + goto error; + } + + /* FALL-THROUGH with r == 0 */ +error: + kfree(req); + return r; +} + +int zd_usb_rfwrite(struct zd_usb *usb, u32 value, u8 bits) +{ + int r; + struct usb_device *udev; + struct usb_req_rfwrite *req = NULL; + int i, req_len, actual_req_len; + u16 bit_value_template; + + if (in_atomic()) { + dev_dbg_f(zd_usb_dev(usb), + "error: io in atomic context not supported\n"); + return -EWOULDBLOCK; + } + if (bits < USB_MIN_RFWRITE_BIT_COUNT) { + dev_dbg_f(zd_usb_dev(usb), + "error: bits %d are smaller than" + " USB_MIN_RFWRITE_BIT_COUNT %d\n", + bits, USB_MIN_RFWRITE_BIT_COUNT); + return -EINVAL; + } + if (bits > USB_MAX_RFWRITE_BIT_COUNT) { + dev_dbg_f(zd_usb_dev(usb), + "error: bits %d exceed USB_MAX_RFWRITE_BIT_COUNT %d\n", + bits, USB_MAX_RFWRITE_BIT_COUNT); + return -EINVAL; + } +#ifdef DEBUG + if (value & (~0UL << bits)) { + dev_dbg_f(zd_usb_dev(usb), + "error: value %#09x has bits >= %d set\n", + value, bits); + return -EINVAL; + } +#endif /* DEBUG */ + + dev_dbg_f(zd_usb_dev(usb), "value %#09x bits %d\n", value, bits); + + r = zd_usb_ioread16(usb, &bit_value_template, CR203); + if (r) { + dev_dbg_f(zd_usb_dev(usb), + "error %d: Couldn't read CR203\n", r); + goto out; + } + bit_value_template &= ~(RF_IF_LE|RF_CLK|RF_DATA); + + req_len = sizeof(struct usb_req_rfwrite) + bits * sizeof(__le16); + req = kmalloc(req_len, GFP_KERNEL); + if (!req) + return -ENOMEM; + + req->id = cpu_to_le16(USB_REQ_WRITE_RF); + /* 1: 3683a, but not used in ZYDAS driver */ + req->value = cpu_to_le16(2); + req->bits = cpu_to_le16(bits); + + for (i = 0; i < bits; i++) { + u16 bv = bit_value_template; + if (value & (1 << (bits-1-i))) + bv |= RF_DATA; + req->bit_values[i] = cpu_to_le16(bv); + } + + udev = zd_usb_to_usbdev(usb); + r = usb_bulk_msg(udev, usb_sndbulkpipe(udev, EP_REGS_OUT), + req, req_len, &actual_req_len, 1000 /* ms */); + if (r) { + dev_dbg_f(zd_usb_dev(usb), + "error in usb_bulk_msg(). Error number %d\n", r); + goto out; + } + if (req_len != actual_req_len) { + dev_dbg_f(zd_usb_dev(usb), "error in usb_bulk_msg()" + " req_len %d != actual_req_len %d\n", + req_len, actual_req_len); + r = -EIO; + goto out; + } + + /* FALL-THROUGH with r == 0 */ +out: + kfree(req); + return r; +} diff -puN /dev/null drivers/net/wireless/zd1211rw-mac80211/zd_usb.h --- /dev/null +++ a/drivers/net/wireless/zd1211rw-mac80211/zd_usb.h @@ -0,0 +1,263 @@ +/* zd_usb.h: Header for USB interface implemented by ZD1211 chip + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _ZD_USB_H +#define _ZD_USB_H + +#include +#include +#include +#include +#include + +#include "zd_def.h" + +#define ZD_USB_TX_HIGH 5 +#define ZD_USB_TX_LOW 2 + +enum devicetype { + DEVICE_ZD1211 = 0, + DEVICE_ZD1211B = 1, + DEVICE_INSTALLER = 2, +}; + +enum endpoints { + EP_CTRL = 0, + EP_DATA_OUT = 1, + EP_DATA_IN = 2, + EP_INT_IN = 3, + EP_REGS_OUT = 4, +}; + +enum { + USB_MAX_TRANSFER_SIZE = 4096, /* bytes */ + /* FIXME: The original driver uses this value. We have to check, + * whether the MAX_TRANSFER_SIZE is sufficient and this needs only be + * used if one combined frame is split over two USB transactions. + */ + USB_MAX_RX_SIZE = 4800, /* bytes */ + USB_MAX_IOWRITE16_COUNT = 15, + USB_MAX_IOWRITE32_COUNT = USB_MAX_IOWRITE16_COUNT/2, + USB_MAX_IOREAD16_COUNT = 15, + USB_MAX_IOREAD32_COUNT = USB_MAX_IOREAD16_COUNT/2, + USB_MIN_RFWRITE_BIT_COUNT = 16, + USB_MAX_RFWRITE_BIT_COUNT = 28, + USB_MAX_EP_INT_BUFFER = 64, + USB_ZD1211B_BCD_DEVICE = 0x4810, +}; + +enum control_requests { + USB_REQ_WRITE_REGS = 0x21, + USB_REQ_READ_REGS = 0x22, + USB_REQ_WRITE_RF = 0x23, + USB_REQ_PROG_FLASH = 0x24, + USB_REQ_EEPROM_START = 0x0128, /* ? request is a byte */ + USB_REQ_EEPROM_MID = 0x28, + USB_REQ_EEPROM_END = 0x0228, /* ? request is a byte */ + USB_REQ_FIRMWARE_DOWNLOAD = 0x30, + USB_REQ_FIRMWARE_CONFIRM = 0x31, + USB_REQ_FIRMWARE_READ_DATA = 0x32, +}; + +struct usb_req_read_regs { + __le16 id; + __le16 addr[0]; +} __attribute__((packed)); + +struct reg_data { + __le16 addr; + __le16 value; +} __attribute__((packed)); + +struct usb_req_write_regs { + __le16 id; + struct reg_data reg_writes[0]; +} __attribute__((packed)); + +enum { + RF_IF_LE = 0x02, + RF_CLK = 0x04, + RF_DATA = 0x08, +}; + +struct usb_req_rfwrite { + __le16 id; + __le16 value; + /* 1: 3683a */ + /* 2: other (default) */ + __le16 bits; + /* RF2595: 24 */ + __le16 bit_values[0]; + /* (CR203 & ~(RF_IF_LE | RF_CLK | RF_DATA)) | (bit ? RF_DATA : 0) */ +} __attribute__((packed)); + +/* USB interrupt */ + +enum usb_int_id { + USB_INT_TYPE = 0x01, + USB_INT_ID_REGS = 0x90, + USB_INT_ID_RETRY_FAILED = 0xa0, +}; + +enum usb_int_flags { + USB_INT_READ_REGS_EN = 0x01, +}; + +struct usb_int_header { + u8 type; /* must always be 1 */ + u8 id; +} __attribute__((packed)); + +struct usb_int_regs { + struct usb_int_header hdr; + struct reg_data regs[0]; +} __attribute__((packed)); + +struct usb_int_retry_fail { + struct usb_int_header hdr; + u8 new_rate; + u8 _dummy; + u8 addr[ETH_ALEN]; + u8 ibss_wakeup_dest; +} __attribute__((packed)); + +struct read_regs_int { + struct completion completion; + /* Stores the USB int structure and contains the USB address of the + * first requested register before request. + */ + u8 buffer[USB_MAX_EP_INT_BUFFER]; + int length; + __le16 cr_int_addr; +}; + +struct zd_ioreq16 { + zd_addr_t addr; + u16 value; +}; + +struct zd_ioreq32 { + zd_addr_t addr; + u32 value; +}; + +struct zd_usb_interrupt { + struct read_regs_int read_regs; + spinlock_t lock; + struct urb *urb; + int interval; + u8 read_regs_enabled:1; +}; + +static inline struct usb_int_regs *get_read_regs(struct zd_usb_interrupt *intr) +{ + return (struct usb_int_regs *)intr->read_regs.buffer; +} + +#define RX_URBS_COUNT 5 + +struct zd_usb_rx { + spinlock_t lock; + u8 fragment[2*USB_MAX_RX_SIZE]; + unsigned int fragment_length; + unsigned int usb_packet_size; + struct urb **urbs; + int urbs_count; +}; + +/** + * struct zd_usb_tx - structure used for transmitting frames + * @lock: lock for transmission + * @free_urb_list: list of free URBs, contains all the URBs, which can be used + * @submitted_urbs: atomic integer that counts the URBs having sent to the + * device, which haven't been completed + * @enabled: enabled flag, indicates whether tx is enabled + * @stopped: indicates whether higher level tx queues are stopped + */ +struct zd_usb_tx { + spinlock_t lock; + struct list_head free_urb_list; + int submitted_urbs; + int enabled; + int stopped; +}; + +/* Contains the usb parts. The structure doesn't require a lock because intf + * will not be changed after initialization. + */ +struct zd_usb { + struct zd_usb_interrupt intr; + struct zd_usb_rx rx; + struct zd_usb_tx tx; + struct usb_interface *intf; + u8 is_zd1211b:1, initialized:1; +}; + +#define zd_usb_dev(usb) (&usb->intf->dev) + +static inline struct usb_device *zd_usb_to_usbdev(struct zd_usb *usb) +{ + return interface_to_usbdev(usb->intf); +} + +static inline struct ieee80211_hw *zd_intf_to_hw(struct usb_interface *intf) +{ + return usb_get_intfdata(intf); +} + +static inline struct ieee80211_hw *zd_usb_to_hw(struct zd_usb *usb) +{ + return zd_intf_to_hw(usb->intf); +} + +void zd_usb_init(struct zd_usb *usb, struct ieee80211_hw *hw, + struct usb_interface *intf); +int zd_usb_init_hw(struct zd_usb *usb); +void zd_usb_clear(struct zd_usb *usb); + +int zd_usb_scnprint_id(struct zd_usb *usb, char *buffer, size_t size); + +int zd_usb_enable_int(struct zd_usb *usb); +void zd_usb_disable_int(struct zd_usb *usb); + +int zd_usb_enable_rx(struct zd_usb *usb); +void zd_usb_disable_rx(struct zd_usb *usb); + +void zd_usb_enable_tx(struct zd_usb *usb); +void zd_usb_disable_tx(struct zd_usb *usb); + +int zd_usb_tx(struct zd_usb *usb, struct sk_buff *skb); + +int zd_usb_ioread16v(struct zd_usb *usb, u16 *values, + const zd_addr_t *addresses, unsigned int count); + +static inline int zd_usb_ioread16(struct zd_usb *usb, u16 *value, + const zd_addr_t addr) +{ + return zd_usb_ioread16v(usb, value, (const zd_addr_t *)&addr, 1); +} + +int zd_usb_iowrite16v(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs, + unsigned int count); + +int zd_usb_rfwrite(struct zd_usb *usb, u32 value, u8 bits); + +int zd_usb_read_fw(struct zd_usb *usb, zd_addr_t addr, u8 *data, u16 len); + +extern struct workqueue_struct *zd_workqueue; + +#endif /* _ZD_USB_H */ diff -puN drivers/usb/host/Kconfig~git-wireless drivers/usb/host/Kconfig --- a/drivers/usb/host/Kconfig~git-wireless +++ a/drivers/usb/host/Kconfig @@ -154,6 +154,19 @@ config USB_OHCI_HCD_PCI Enables support for PCI-bus plug-in USB controller cards. If unsure, say Y. +config USB_OHCI_HCD_SSB + bool "OHCI support for the Broadcom SSB OHCI core (embedded systems only)" + depends on USB_OHCI_HCD && ((USB_OHCI_HCD=m && SSB) || (USB_OHCI_HCD=y && SSB=y)) && EXPERIMENTAL + default n + ---help--- + Support for the Sonics Silicon Backplane (SSB) attached + Broadcom USB OHCI core. + + This device is only present in some embedded devices with + Broadcom based SSB bus. + + If unsure, say N. + config USB_OHCI_BIG_ENDIAN_DESC bool depends on USB_OHCI_HCD diff -puN drivers/usb/host/ohci-hcd.c~git-wireless drivers/usb/host/ohci-hcd.c --- a/drivers/usb/host/ohci-hcd.c~git-wireless +++ a/drivers/usb/host/ohci-hcd.c @@ -1033,11 +1033,17 @@ MODULE_LICENSE ("GPL"); #define PS3_SYSTEM_BUS_DRIVER ps3_ohci_driver #endif +#ifdef CONFIG_USB_OHCI_HCD_SSB +#include "ohci-ssb.c" +#define SSB_OHCI_DRIVER ssb_ohci_driver +#endif + #if !defined(PCI_DRIVER) && \ !defined(PLATFORM_DRIVER) && \ !defined(OF_PLATFORM_DRIVER) && \ !defined(SA1111_DRIVER) && \ - !defined(PS3_SYSTEM_BUS_DRIVER) + !defined(PS3_SYSTEM_BUS_DRIVER) && \ + !defined(SSB_OHCI_DRIVER) #error "missing bus glue for ohci-hcd" #endif @@ -1082,10 +1088,20 @@ static int __init ohci_hcd_mod_init(void goto error_pci; #endif +#ifdef SSB_OHCI_DRIVER + retval = ssb_driver_register(&SSB_OHCI_DRIVER); + if (retval) + goto error_ssb; +#endif + return retval; /* Error path */ +#ifdef SSB_OHCI_DRIVER + error_ssb: +#endif #ifdef PCI_DRIVER + pci_unregister_driver(&PCI_DRIVER); error_pci: #endif #ifdef SA1111_DRIVER @@ -1110,6 +1126,9 @@ module_init(ohci_hcd_mod_init); static void __exit ohci_hcd_mod_exit(void) { +#ifdef SSB_OHCI_DRIVER + ssb_driver_unregister(&SSB_OHCI_DRIVER); +#endif #ifdef PCI_DRIVER pci_unregister_driver(&PCI_DRIVER); #endif diff -puN /dev/null drivers/usb/host/ohci-ssb.c --- /dev/null +++ a/drivers/usb/host/ohci-ssb.c @@ -0,0 +1,254 @@ +/* + * Sonics Silicon Backplane + * Broadcom USB-core OHCI driver + * + * Copyright 2007 Michael Buesch + * + * Derived from the OHCI-PCI driver + * Copyright 1999 Roman Weissgaerber + * Copyright 2000-2002 David Brownell + * Copyright 1999 Linus Torvalds + * Copyright 1999 Gregory P. Smith + * + * Derived from the USBcore related parts of Broadcom-SB + * Copyright 2005 Broadcom Corporation + * + * Licensed under the GNU/GPL. See COPYING for details. + */ + +#include + + +#define SSB_OHCI_TMSLOW_HOSTMODE (1 << 29) + +struct ssb_ohci_device { + struct ohci_hcd ohci; /* _must_ be at the beginning. */ + + u32 enable_flags; +}; + + +static inline +struct ssb_ohci_device * hcd_to_ssb_ohci(struct usb_hcd *hcd) +{ + return (struct ssb_ohci_device *)(hcd->hcd_priv); +} + + +static const struct ssb_device_id ssb_ohci_table[] = { + SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOSTDEV, SSB_ANY_REV), + SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOST, SSB_ANY_REV), + SSB_DEVTABLE_END +}; +MODULE_DEVICE_TABLE(ssb, ssb_ohci_table); + + +static int ssb_ohci_reset(struct usb_hcd *hcd) +{ + struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd); + struct ohci_hcd *ohci = &ohcidev->ohci; + int err; + + ohci_hcd_init(ohci); + err = ohci_init(ohci); + + return err; +} + +static int ssb_ohci_start(struct usb_hcd *hcd) +{ + struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd); + struct ohci_hcd *ohci = &ohcidev->ohci; + int err; + + err = ohci_run(ohci); + if (err < 0) { + ohci_err(ohci, "can't start\n"); + ohci_stop(hcd); + } + + return err; +} + +#ifdef CONFIG_PM +static int ssb_ohci_hcd_suspend(struct usb_hcd *hcd, pm_message_t message) +{ + struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd); + struct ohci_hcd *ohci = &ohcidev->ohci; + unsigned long flags; + + spin_lock_irqsave(&ohci->lock, flags); + + ohci_writel(ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable); + ohci_readl(ohci, &ohci->regs->intrdisable); /* commit write */ + + /* make sure snapshot being resumed re-enumerates everything */ + if (message.event == PM_EVENT_PRETHAW) + ohci_usb_reset(ohci); + + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + + spin_unlock_irqrestore(&ohci->lock, flags); + + return 0; +} + +static int ssb_ohci_hcd_resume(struct usb_hcd *hcd) +{ + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + usb_hcd_resume_root_hub(hcd); + return 0; +} +#endif /* CONFIG_PM */ + +static const struct hc_driver ssb_ohci_hc_driver = { + .description = "ssb-usb-ohci", + .product_desc = "SSB OHCI Controller", + .hcd_priv_size = sizeof(struct ssb_ohci_device), + + .irq = ohci_irq, + .flags = HCD_MEMORY | HCD_USB11, + + .reset = ssb_ohci_reset, + .start = ssb_ohci_start, + .stop = ohci_stop, + .shutdown = ohci_shutdown, + +#ifdef CONFIG_PM + .suspend = ssb_ohci_hcd_suspend, + .resume = ssb_ohci_hcd_resume, +#endif + + .urb_enqueue = ohci_urb_enqueue, + .urb_dequeue = ohci_urb_dequeue, + .endpoint_disable = ohci_endpoint_disable, + + .get_frame_number = ohci_get_frame, + + .hub_status_data = ohci_hub_status_data, + .hub_control = ohci_hub_control, + .hub_irq_enable = ohci_rhsc_enable, + +#ifdef CONFIG_PM + .bus_suspend = ohci_bus_suspend, + .bus_resume = ohci_bus_resume, +#endif + + .start_port_reset = ohci_start_port_reset, +}; + + +static void ssb_ohci_detach(struct ssb_device *dev) +{ + struct usb_hcd *hcd = ssb_get_drvdata(dev); + + usb_remove_hcd(hcd); + iounmap(hcd->regs); + usb_put_hcd(hcd); + ssb_device_disable(dev, 0); +} + +static int ssb_ohci_attach(struct ssb_device *dev) +{ + struct ssb_ohci_device *ohcidev; + struct usb_hcd *hcd; + int err = -ENOMEM; + u32 tmp, flags = 0; + + if (dev->id.coreid == SSB_DEV_USB11_HOSTDEV) + flags |= SSB_OHCI_TMSLOW_HOSTMODE; + + ssb_device_enable(dev, flags); + + hcd = usb_create_hcd(&ssb_ohci_hc_driver, dev->dev, + dev->dev->bus_id); + if (!hcd) + goto err_dev_disable; + ohcidev = hcd_to_ssb_ohci(hcd); + ohcidev->enable_flags = flags; + + tmp = ssb_read32(dev, SSB_ADMATCH0); + hcd->rsrc_start = ssb_admatch_base(tmp); + hcd->rsrc_len = ssb_admatch_size(tmp); + hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len); + if (!hcd->regs) + goto err_put_hcd; + err = usb_add_hcd(hcd, dev->irq, IRQF_SHARED); + if (err) + goto err_iounmap; + + ssb_set_drvdata(dev, hcd); + + return err; + +err_iounmap: + iounmap(hcd->regs); +err_put_hcd: + usb_put_hcd(hcd); +err_dev_disable: + ssb_device_disable(dev, flags); + return err; +} + +static int ssb_ohci_probe(struct ssb_device *dev, + const struct ssb_device_id *id) +{ + int err; + u16 chipid_top; + + chipid_top = (dev->bus->chip_id & 0xFF00); + if (chipid_top != 0x4700 && + chipid_top != 0x5300) { + /* USBcores are only connected on embedded devices. */ + return -ENODEV; + } + /* TODO: Probably need more checks here whether the core is connected. */ + + if (usb_disabled()) + return -ENODEV; + + /* We currently always attach SSB_DEV_USB11_HOSTDEV + * as HOST OHCI. If we want to attach it as Client device, + * we must branch here and call into the (yet to + * be written) Client mode driver. Same for remove(). */ + + err = ssb_ohci_attach(dev); + + return err; +} + +static void ssb_ohci_remove(struct ssb_device *dev) +{ + ssb_ohci_detach(dev); +} + +#ifdef CONFIG_PM +static int ssb_ohci_suspend(struct ssb_device *dev, pm_message_t state) +{ + ssb_device_disable(dev, 0); + + return 0; +} + +static int ssb_ohci_resume(struct ssb_device *dev) +{ + struct usb_hcd *hcd = ssb_get_drvdata(dev); + struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd); + + ssb_device_enable(dev, ohcidev->enable_flags); + + return 0; +} +#else /* CONFIG_PM */ +# define ssb_ohci_suspend NULL +# define ssb_ohci_resume NULL +#endif /* CONFIG_PM */ + +static struct ssb_driver ssb_ohci_driver = { + .name = KBUILD_MODNAME, + .id_table = ssb_ohci_table, + .probe = ssb_ohci_probe, + .remove = ssb_ohci_remove, + .suspend = ssb_ohci_suspend, + .resume = ssb_ohci_resume, +}; _