GIT 580a2521f318461a9bf07004e77c34d750cb3285 git+ssh://master.kernel.org/pub/scm/linux/kernel/git/linville/wireless-dev.git#master commit Author: Jiri Benc Date: Wed May 2 23:18:18 2007 +0200 [PATCH] mac80211: fix false lockdep warning This makes lockdep aware of xmit_lock hierarchy between master and virtual interfaces. Signed-off-by: Jiri Benc commit 324ad94f04d7f20f77578333d166487e88e872d4 Author: Michael Wu Date: Wed May 2 22:44:13 2007 +0200 [PATCH] mac80211: don't compile ieee80211 qdisc code without CONFIG_NET_SCHED This patch prevents all the qdisc code from compiling without CONFIG_NET_SCHED. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit 65c44b4013faf5eac9f4bde147ee4c50856d448a Author: Johannes Berg Date: Thu Mar 29 00:20:08 2007 +0200 [PATCH] use NLA_BINARY in nl80211 Unfortunately I hadn't noticed when starting nl80211 that NLA_STRING validation removes a trailing \0. This means that we have a problem right now with SSIDs for example in nl80211 when they end with \0, or, more importantly maybe, with key data as well. This patch changes this to use the new NLA_BINARY attribute type I created but which isn't in wireless-dev yet, it is in davem's .22 branch though. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit d2f859f56c9f00f093f943cf810a863a5aa2e10f Author: Ulrich Kunitz Date: Tue May 1 04:01:00 2007 +0100 [PATCH] zd1211rw-mac80211: limit URB buffering in tx path The old code allowed unlimited buffing of tx frames in URBs submitted for transfer to the device. This patch stops the ieee80211_hw queue(s) if to many URBs are ready for submit to the device. Actually the ZD1211 device supports currently only one queue. Signed-off-by: Ulrich Kunitz Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit 3650d768e765a3a54267d202cc3b3d5f5f9842e3 Author: Jiri Benc Date: Tue May 1 17:40:44 2007 +0200 [PATCH] wireless: remove duplicate inclusion of wireless/ in Makefile The wireless/ directory is included two times in net/Makefile. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit d3031ffd6a780968b2a7e0b4a14500378233128c Author: Daniel Drake Date: Tue May 1 04:04:27 2007 +0100 [PATCH] zd1211rw-mac80211: Add ID for ZyXEL AG-225H v2 Tested by davo on IRC zd1211b chip 0586:3413 v4810 full 00-13-49 AL7230B_RF pa0 ----- Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit 4884190227bf5aed53c4748c7dc509a5757ec4ca Author: Matthew Davidson Date: Tue May 1 04:03:40 2007 +0100 [PATCH] zd1211rw-mac80211: Add ID for Sitecom WL-117 This is another "driverless" device which first presents itself as a USB CDROM drive. A separate patch has been submitted to make usb-storage ignore that device, so that zd1211rw can eject it. zd1211 chip 0df6:9075 v4916 full 00-0c-f6 AL2230_RF pa0 ---- Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit 6dd19320e1b54a8e06bcc05f781236f7c17e7a5f Author: Ulrich Kunitz Date: Tue May 1 04:02:59 2007 +0100 [PATCH] zd1211rw-mac80211: Added new USB id for Planex GW-US54ZGL Alan Tam asked for inclusion of this device into the tree. Signed-off-by: Ulrich Kunitz Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit 4eb6c670b52b9e5beaacde83a2ba5666c4724591 Author: Ulrich Kunitz Date: Tue May 1 04:02:18 2007 +0100 [PATCH] zd1211rw-mac80211: remove static table from zd_mac.h The header zd_mac.h did contain static declaration of tables, which are only used in zd_mac.c. These tables have been moved into the C file itself. Signed-off-by: Ulrich Kunitz Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit e4a5f54057f1059cfc82be5e943621789065e8aa Author: Ulrich Kunitz Date: Tue May 1 03:44:47 2007 +0100 [PATCH] zd1211rw-mac80211: fixed freeing skbs in interrupt context Some of the kfree_skb() calls could happen in irq context. Changed all calls to dev_kfree_skb() in non-irq context and to dev_kfree_skb_any() where an irq context might happen. Signed-off-by: Ulrich Kunitz Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit 07ff9e267663ae390de408b66564afe2c7c04b5c Author: Johannes Berg Date: Tue May 1 16:35:33 2007 +0200 [PATCH] fix mac80211 compile w/o VERBOSE_DEBUG but w/ DEBUG This fixes mac80211 compilation when CONFIG_MAC80211_DEBUG is set but CONFIG_MAC80211_VERBOSE_DEBUG is not. Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc commit 3a1bc483eba27848fb0c03171cfcc1115379dd04 Author: Jiri Benc Date: Mon Apr 30 20:17:55 2007 +0200 [PATCH] mac80211: mark as experimental Make the stack depend on EXPERIMENTAL. Also, change the name from "dscape" to "mac80211". Signed-off-by: Jiri Benc commit 46b0e3a4e5bda7b670754e99c92888d911efa6a7 Author: Jiri Benc Date: Mon Apr 30 19:35:39 2007 +0200 [PATCH] mac80211: rename remaining mentions of sysfs and procfs Remove or rename references to sysfs and procfs. #ifdef some more code that is not needed when the debugfs support is not built. Signed-off-by: Jiri Benc commit fc5902b2012465ae702934f98e531e2e380c29d0 Author: Jiri Benc Date: Mon Apr 30 18:13:09 2007 +0200 [PATCH] mac80211: destroy workqueue in error path Created workqueue is not destroyed when ieee80211_register_hw fails. Signed-off-by: Jiri Benc commit 1fd8ffe72853ed41a0d3b489a2d94959aef6af80 Author: Jiri Benc Date: Sun Apr 29 02:36:25 2007 +0200 [PATCH] add maintainers entry for mac80211 Add MAINTAINERS entry for mac80211. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit 8815cd603516a1fb0cdb1964bec169fb9274f47a Author: Michael Wu Date: Fri Apr 27 15:54:55 2007 -0400 [PATCH] mac80211: don't export linux/ieee80211.h The definitions in linux/ieee80211.h don't need to be exported. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit ded370913e630afe4ded7fe328c93fc1eb413846 Author: Michael Wu Date: Sun Apr 29 17:59:01 2007 +0200 [PATCH] mac80211: remove ieee80211_netif_oper Without ieee80211_scan.c, nothing uses ieee80211_netif_oper now. This patch removes it. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit 9febae241f3b2331add0552dec1904fa020430ed Author: Jiri Benc Date: Sun Apr 29 15:55:54 2007 +0200 [PATCH] mac80211: remove hostapd scanning This removes passive_scan callback with associated code. The whole thing is not a job for a driver and can be done in a much cleaner way using user space MLME. Besides, nobody (not even hostapd) uses that call. Signed-off-by: Jiri Benc commit ed29d901151db2a2883e993c32535600258d9d01 Author: Jiri Benc Date: Sun Apr 29 13:04:36 2007 +0200 [PATCH] mac80211: add copyrights I have been lazy with adding my copyright to some files. Signed-off-by: Jiri Benc commit bf871ace240e04bf3903d81ec58ef74cafac0afb Author: Jiri Benc Date: Sun Apr 29 13:04:36 2007 +0200 [PATCH] mac80211: document requirement for atomicity of callbacks Some callbacks must be atomic. This is not documented anywhere. Signed-off-by: Jiri Benc commit 0b2985c5e818a02211746052df0d0386b810d193 Author: Jiri Benc Date: Sun Apr 29 13:04:35 2007 +0200 [PATCH] mac80211: remove test_mode Testing of a radio is a driver-specific debug feature and doesn't belong to the stack. Drivers should implement it using debugfs. Signed-off-by: Jiri Benc commit 2269b85993fb1fffa427bfb4977d943c954d5cdc Author: Jiri Benc Date: Sun Apr 29 13:04:34 2007 +0200 [PATCH] mac80211: remove unused code Remove code that is commented out and doesn't even compile when uncommented. Signed-off-by: Jiri Benc commit ed2eba820169cb3822d532c36492cd73229524d0 Author: Michael Wu Date: Sun Apr 29 00:13:33 2007 +0200 [PATCH] mac80211: Allow drivers to configure default regulatory domain This patch allows drivers to configure the default set of channels if the device reports its default regulatory domain. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit eb6251ab981055e4e8fe7960c8f507cecb6b5cdb Author: Michael Wu Date: Sun Apr 29 00:13:32 2007 +0200 [PATCH] mac80211: suppress warnings This eliminates warnings from gcc and sparse. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit b62fd5033a3878521d9bb25a1d3df7844757d5a5 Author: Michael Wu Date: Sun Apr 29 00:13:32 2007 +0200 [PATCH] mac80211: set event_capa in iw_range This fills in the event capability bitfield in struct iw_range. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit 964055a79c7bb0a32e27dd878023b1ebe9046d2d Author: Michael Wu Date: Sun Apr 29 00:13:32 2007 +0200 [PATCH] mac80211: eliminate forward declarations in ieee80211.c ieee80211_get_bssid can be moved up in ieee80211 to avoid declaring it at the top of the file. ieee80211_mgmt_start_xmit is used after it's defined so it doesn't need to be declared at the top. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit f87568974c84acb59e2106daef4a3d12d73dfead Author: Michael Wu Date: Sun Apr 29 00:13:31 2007 +0200 [PATCH] mac80211: kill ceiling_div kernel.h provides a macro which does the same thing as ceiling_div. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit 7b7f555ae33332046eaab15fb9bd620791977e16 Author: Michael Wu Date: Sun Apr 29 00:13:31 2007 +0200 [PATCH] mac80211: kill ieee80211_set_mac_address ieee80211_set_mac_address does exactly the same thing that eth_mac_addr does. This patch removes it. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit dd3d9e83f333c9d2c81a4c67382a34b755daaf23 Author: Johannes Berg Date: Sat Apr 28 16:33:51 2007 +0200 [PATCH] mac80211: optimise ieee80211_get_hdrlen This patch optimises the ieee80211_get_hdrlen function by exploiting the bit masks directly. On powerpc, this decreases the function by 4 instructions, but more importantly it kills 3 of the 5 branches it contained. Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc commit bc3a08ae4799ea12afc1d112d92b52625465855d Author: James Ketrenos Date: Sat Apr 28 15:25:40 2007 +0200 [PATCH] mac80211: Fix ieee80211_sta_config_auth to select networks if rssi < 0 The rssi check was against the top_rssi being less than the network rssi. However, 0 is higher than all negative rssi, so if an adapter returned negative rssi values, auto AP selection would never work. This patch changes behavior such if no network is currently selected it ignores the rssi comparison and picks the network that matches all other criteria. Signed-off-by: James Ketrenos Signed-off-by: Jiri Benc commit 5d51c7e19f3007f82aef389f2e6dc32fc1c91a32 Author: Larry Finger Date: Sat Apr 28 14:50:22 2007 +0200 [PATCH] mac80211: Add channel to scan results The scan results from mac80211 list only the frequency for any access point found. This patch adds the channel to the output as well. Signed-off-by: Larry Finger Signed-off-by: Jiri Benc commit 103130bfa9de13b65b89bc2df89ebdd05a6d1117 Author: Michael Wu Date: Sat Apr 28 14:50:22 2007 +0200 [PATCH] mac80211: stop all virtual interfaces when master device goes down The master device cannot actually veto taking a device down. This patch makes all virtual devices go down if the master device goes down. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit 00a908826e778b39a802013729f7826c2d575360 Author: Michael Wu Date: Sat Apr 28 14:50:21 2007 +0200 [PATCH] mac80211: prevent master device from going up without ieee80211 qdisc This patch ensures that the master device cannot be opened without the ieee80211 qdisc installed. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit b70dcc6f2167f59ed1a4daf437a06dc2e65d8f00 Author: Michael Wu Date: Sat Apr 28 14:50:21 2007 +0200 [PATCH] mac80211: fix issues in ieee80211 qdisc This patch fixes two issues found by Patrick McHardy: 1. wme_qdiscop_enqueue doesn't increment q.qlen for packets queued to q->requeued[], which might cause upper layer code to stop dequeueing if q.qlen reaches zero. 2. wme_discop_destroy leaks classifier module references and memory when destroying classifiers, it should use tcf_destroy_chain() It also removes some dead code. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit 83caf2835bf57d8e1058761e958e84fb355fab2a Author: Michael Wu Date: Sat Apr 28 14:50:20 2007 +0200 [PATCH] mac80211: set bssid to broadcast before scan This patch sets the BSSID to broadcast before scanning to ensure that the hardware filter does not filter probe responses. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit f7322f46e6fdbb439ce61d5fad2ce50eb32fb347 Author: Michael Wu Date: Sat Apr 28 14:50:20 2007 +0200 [PATCH] mac80211: misc cleanups in ieee80211_sta.c This eliminates some unnecessary code in ieee80211_sta.c. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit ebaa3d418e4ffad102b7f8aabbb07fb7920c7d17 Author: Michael Wu Date: Sat Apr 28 14:50:20 2007 +0200 [PATCH] mac80211: fix configuration concurrency issues in ieee80211_sta.c This prevents userspace and the in-kernel MLME from configuring channel/BSSID/SSID at the same time when the in-kernel MLME is in the midst of automatic AP selection. This is done by holding the RTNL lock. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit 43c0cd468b9181c4f061927a1f9632ddd513e683 Author: Michael Wu Date: Sat Apr 28 14:50:19 2007 +0200 [PATCH] mac80211: avoid flush_scheduled_work flush_scheduled_work in ieee80211_if_shutdown is called with rtnl held, which can lead to deadlocks. This patch eliminates that by adding a new single thread workqueue which can be safely flushed. It is also available for driver use. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit d6dcd414479312d7ca1e341acc965466a65b8adb Author: Michael Wu Date: Sat Apr 28 14:50:19 2007 +0200 [PATCH] mac80211: remove statistics callback for master device Statistics were never updated properly for the master device, so don't bother reporting them. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit 3bfdfeb1d2d1de4e7ce0e83cfa57d712900a7f45 Author: Michael Wu Date: Sat Apr 28 14:50:18 2007 +0200 [PATCH] mac80211: disable tasklets on close This prevents all tasklets from running when the device is down. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit 739bc51ab3828446d9de6dcf0ccb803249e4f11f Author: Michael Wu Date: Sat Apr 28 14:50:18 2007 +0200 [PATCH] mac80211: fix virtual interface related locking This converts sub_if_lock to a rw lock and makes all code touching sub_if_list use it, grabs mdev's tx lock in set_multicast_list to synchronize multicast configuration, and simplifies some related code. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit e2acbb042af08dce240f371ba156cada24d32110 Author: Michael Wu Date: Sat Apr 28 14:50:18 2007 +0200 [PATCH] mac80211: Add radiotap support This patch makes mac80211 monitor interfaces use radiotap headers. It also provides a bit to let a driver specify a frame has a radiotap header and another bit to let the driver know if adding a radiotap header would be helpful. Thanks to Andy Green for testing earlier versions of this patch. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit 0f6551a918f0d6e6edc8a863461d7f549c911508 Author: Johannes Berg Date: Fri Apr 27 12:14:56 2007 +0200 [PATCH] wireless-dev wext updates This patch brings the wext in wireless-dev in line with the wext in net-2.6.22 (after the patches I just submitted) and removes the cfg80211/wext compat stuff. As long as cfg80211 holds the rtnl we'll be able to just hook into dev->do_ioctl for the compat code instead. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit 625301431222ba06adffe4d8f5c953b0e20344cd Author: Ivo van Doorn Date: Fri Apr 27 19:25:19 2007 +0200 rt2x00: rt61 should call ieee80211_wake_queue only for correct queues ieee80211_wake_queue should only be called for queues that have just been processed by the txdone handler. Signed-off-by: Ivo van Doorn commit bf272652983c61e7ca342855bd209f0f4d48d5c6 Author: Ivo van Doorn Date: Fri Apr 27 13:51:42 2007 +0200 rt2x00: Move USB txdone handler into rt2x00usb The 2 txdone handlers for rt2500usb and rt73usb are identical, there is one place for such methods: rt2x00usb module. Signed-off-by: Ivo van Doorn commit 843d22b6525779d99793d1f38dafb9325dd7336f Author: Ivo van Doorn Date: Fri Apr 27 13:44:51 2007 +0200 rt2x00: Set channel_change_time to 0 With passive scanning removed the channel_change_time no longer has any meaning. When config() ends, the channel will be correctly set, and mac80211 does not need to wait for it. Signed-off-by: Ivo van Doorn commit 3cb0ca51cda254839950645b0c1dac72c4219e08 Author: Ivo van Doorn Date: Fri Apr 27 13:42:43 2007 +0200 rt2x00: Remove passive scanning support RT2x00 was only faking to support passive scanning, it was a solution purely based on a software implementation. If that is not a problem, it should be part of mac80211 as a library call or something similar. If it was a problem, then it still doesn't belong in rt2x00. In both cases the software implementation for passive scanning should be removed. Signed-off-by: Ivo van Doorn commit 9e4fd17309d0acddd1748c76f6bd35a0c85446b6 Author: Johannes Berg Date: Tue Apr 24 20:39:09 2007 +0200 [PATCH] clarify locking comment in cfg80211 This patch clarifies the comment about locking in wiphy_unregister. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit 88fa496ee9a7b101cf7bb9dfeb073bfd62275b2c Author: Johannes Berg Date: Thu Apr 26 15:14:22 2007 +0200 [PATCH] cfg80211: fix locking in wiphy_new This patch fixes the locking in wiphy new. Ingo Oeser noticed that locking in the error case was wrong and also suggested this fix. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit 5a7a7f346d48e3afe32e5be653e5e10f2efb273d Author: Ivo van Doorn Date: Thu Apr 26 20:42:35 2007 +0200 rt2x00: Fix check for Acked frames The TXD ACK field is only set when the ACK was requested, when it is set during txdone it does not imply the ACK was received. We can only check if the ACK succeeded when the ACK was requested, but the txdone failed with retries. Signed-off-by: Ivo van Doorn commit a7c5c0a481b7e4c6b6c196b35a13d629bd9cfebe Author: Ivo van Doorn Date: Thu Apr 26 20:32:38 2007 +0200 rt2x00: Remove ENTRY_RTS_CTS_FRAME flag The flag ENTRY_RTS_CTS_FRAME is not required, it should be safe to pass the by a library call created RTS/CTS frames into the mac80211 stack after it has been send. Signed-off-by: Ivo van Doorn commit 45a5ba6cf7973e5a9a22422a78f3d8495aa118c1 Author: Ivo van Doorn Date: Thu Apr 26 20:29:09 2007 +0200 rt2x00: Move write_tx_desc initialization into rt2x00lib Large parts of write_tx_desc are generic for all rt2x00 modules, create a rt2x00lib functions that does most detection of the required fields that should be set the drivers. Signed-off-by: Ivo van Doorn commit 8482aaab046f4d04dacf19a9cef1ccbff529967a Author: Ivo van Doorn Date: Thu Apr 26 17:42:44 2007 +0200 rt2x00: Create dummy rt2x00lib_firmware_load_wait() Create a dummy rt2x00lib_firmware_load_wait() function that will be statically inlined when firmware loading is disabled. Signed-off-by: Ivo van Doorn commit c60fd6156d183ffc377f4a0ef923dda6a78459eb Author: Ivo van Doorn Date: Thu Apr 26 16:40:09 2007 +0200 rt2x00: Add return statement to rt2x00lib_alloc_dev rt2x00lib_alloc_dev was missing its return statement, which caused the driver to free all allocated memory directly after it was allocated. Signed-off-by: Ivo van Doorn commit 32e106da770774f015f7763ae3b17af74233cc2a Author: Ivo van Doorn Date: Thu Apr 26 15:36:43 2007 +0200 rt2x00: Fix compilation error when not selecting rt61pci and rt73usb The rt2x00_load_firmware() function was always called regardless if firmware loading was enabled by rt61 or rt73. This will add the rt2x00_load_firmware() function when firmware loading was disabled. When it is called it will print and return an error because it should never be called. Signed-off-by: Ivo van Doorn commit 11509da1b90ec2a4fe6c852bd43623e6f2ac46c4 Author: Tim Gardner Date: Mon Apr 23 15:33:22 2007 -0600 [PATCH] mac80211: remove duplicate rtnl_unlock() in ieee80211_register_hw Remove nested rtnl_unlock(). modified: net/mac80211/ieee80211.c Signed-off-by: Tim Gardner Signed-off-by: John W. Linville commit c397f70fe5d7273029149d492a019f51f503ba62 Author: Johannes Berg Date: Sat Apr 21 11:21:32 2007 +0200 [PATCH] mac80211: update for cfg80211 rtnl locking cfg80211 was changed to hold the rtnl lock over calls to stack specific operations. This patch changes mac80211 to account for that. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit 969f01377db16e423d3a0e18336e677186466a66 Author: Johannes Berg Date: Sat Apr 21 11:20:35 2007 +0200 [PATCH] cfg80211/nl80211: use rtnl Because the wext/no-rtnl patch was controversial at this time, this patch adds rtnl locking to cfg80211 so all calls to driver/stack operations are made with rtnl held. Once wext compat support in cfg80211 is mature enough and cfg80211 can fully replace wext, this will be revisited. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit d9bba7c6546b79d5236e2ff9582e2b86b8e91855 Author: Ivo van Doorn Date: Tue Apr 24 00:02:07 2007 +0200 rt2x00: Move write_tx_data functions into rt2x00pci/usb write_tx_data is common enough to be moved to the rt2x00pci/usb modules. This requires the tx_done handler for USB devices to be registered in the rt2x00_ops structure, and PCI devices need to have a generic ring entry available define which combines the owner and valid bits of the descriptor. Signed-off-by: Ivo van Doorn commit 4e81c6e01abdf82ad7f4b00bff508a9cc4abe5a4 Author: Michael Buesch Date: Mon Apr 23 17:32:47 2007 +0200 bcm43xx-mac80211: Fix typo in agcsetup Signed-off-by: Michael Buesch commit 65550f1f8de71307e79346603a7c56b90a0b3e94 Author: Ivo van Doorn Date: Sun Apr 22 19:57:34 2007 +0200 rt2x00: Allocate enough rings Allocate enough rings, there was a miscalculation which meant that the beacon ring wan't allocated. Signed-off-by: Ivo van Doorn commit 64fafdb666fbf0b2bc3375998ab1990625ad016b Author: Ivo van Doorn Date: Sat Apr 21 23:46:25 2007 +0200 rt2x00: Move rf values outside functions We need to pass the pointer to the rf values arrays to rt2x00lib. Best thing is to move the arrays outside of the functions to make those pointers valid. Signed-off-by: Ivo van Doorn commit 0cbd665a60a31146e99db4b0f8827307bb75041c Author: Ivo van Doorn Date: Sat Apr 21 16:02:06 2007 +0200 rt2x00: Fix ERROR strings All debug message strings should end with '\n' Signed-off-by: Ivo van Doorn commit 331e73017cbfda572e723827580b5860c13c85c4 Author: Ivo van Doorn Date: Sat Apr 21 13:43:25 2007 +0200 rt2x00: Fix sparse warnings sparse does not like it when __ffs() is called within a BUILD_BUG_ON() statement. To be able to check the correct length of the given mask we can cast it to the type we want (u16 or u32) and compare it to the uncast mask. If those 2 are not equal then the incorrect length has been passed. Signed-off-by: Ivo van Doorn commit 94dad8f8ba25fe7f8991cc1ec667317226a75209 Author: Michael Buesch Date: Sat Apr 21 12:38:28 2007 +0200 bcm43xx-mac80211: Workaround: Disable gmode in early PHY init for PHY rev == 1 Signed-off-by: Michael Buesch commit f33a35d2d89351293468d54b481566863ec8ca5c Author: Ivo van Doorn Date: Sat Apr 21 10:24:23 2007 +0200 rt2x00: Fix NULL pointer exeption during init_hw rt2x00dev->hw pointer needs to be initialized before calling init_hw(). Signed-off-by: Ivo van Doorn commit c1cda8dc47fef37240cab4cdf0dd892abe82eb1e Author: Michael Buesch Date: Sat Apr 21 02:56:21 2007 +0200 bcm43xx-mac80211: Add HW-RNG support. Signed-off-by: Michael Buesch commit efb5265e22e719da0c6843fde942324f9fb8f3d1 Author: Michael Buesch Date: Sat Apr 21 02:02:57 2007 +0200 bcm43xx-mac80211: Add parenthesis around bitwise ops. Signed-off-by: Michael Buesch commit ace99c1dd5562db3e9319fea715b4ea633f4fd9c Author: Michael Buesch Date: Sat Apr 21 02:00:19 2007 +0200 bcm43xx-mac80211: Setting phy->gmode in wireless_core_reset is redundant. This removes it. Signed-off-by: Michael Buesch commit 8897f3f1846d4f0a16c1f90e943d8fd0280cef4b Author: Larry Finger Date: Thu Apr 19 08:58:42 2007 -0500 bcm43xx-mac80211: Fix machine check on PPC for phy->rev == 1 Patch 3/3: References to the Analog Override and Analog Override Value Registers in the Extended G PHY Registers cause a machine check on PPC architecture and a phy->rev == 1 chip. These patches skip over the troublesome accesses. These changes do not conform with the specs, but were found necessary for bcm43xx-softmac as well. Signed-off-by: Larry Finger Signed-off-by: Michael Buesch commit bd32ea61e75b9fdfaa3f7194b5eccbe041d01cc4 Author: Larry Finger Date: Thu Apr 19 08:58:30 2007 -0500 bcm43xx-mac80211: Fix machine check on PPC for phy->rev == 1 Patch 2/3: There are recent changes to the specs that eliminate a number of machine check errors on PPC architecture when used with phy->rev == 1 chips. Signed-off-by: Larry Finger Signed-off-by: Michael Buesch commit fde7ae2e52f4ca1298777a90f2383b1bc890a3da Author: Larry Finger Date: Thu Apr 19 08:58:20 2007 -0500 bcm43xx-mac80211: Fix machine check on PPC for phy->rev == 1 Patch 1/3: Fix some typos and a logic error involving phy->gmode that caused machine checks on PPC architecture with phy->rev == 1 chips. Signed-off-by: Larry Finger Signed-off-by: Michael Buesch commit 4466d6baa6ec1cbd95a9db0d544f9405ac4ac021 Author: Ivo van Doorn Date: Fri Apr 20 23:23:30 2007 +0200 rt2x00: Fix hw mode registration Don't register 80211B mode up to 3 times, but actually register up to 3 different modes. Signed-off-by: Ivo van Doorn commit bf4bbf351980febb26b6749131399c5016751773 Author: Ivo van Doorn Date: Fri Apr 20 17:57:01 2007 +0200 rt2x00: Move PCI/USB handlers into rt2x00pci/usb modules Set the driver_info and driver_data fields for the structures usb_driver and pci_driver and initialize it to the addresss of the rt2x00_ops structure. This allows rt2x00pci and rt2x00usb to take control of the early driver initialization as well as suspend and resume. Signed-off-by: Ivo van Doorn commit 669de0e41beae3771a8c866280fbcea77b447f6f Author: Ivo van Doorn Date: Fri Apr 20 14:56:41 2007 +0200 rt2x00: Rename rt(25)73 identifications The rt73 driver is actually intended for chipsets rt2571W & rt2671 (Makes sense right? ;)), fix comments and defines to reflect this. Signed-off-by: Ivo van Doorn commit 45dd8a7fad4145243dcc4391e80c73c21623ed91 Author: Michael Wu Date: Sun Apr 15 20:14:24 2007 -0400 [PATCH] mac80211: remove ieee80211_netif_oper from mac80211.h rt2x00 was the last user of ieee80211_netif_oper outside of mac80211. ieee80211_netif_oper can now be removed from mac80211.h Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit 333e993e2fb7363318c497de2cb99cefb9f12d0c Author: Ulrich Kunitz Date: Sun Apr 15 11:25:28 2007 +0200 [PATCH] mac80211: add ieee80211_wake_queues() For zd1211rw I need the possibility to wake the tx queues in the mac80211 stack. Though the mac80211 stack provides the function ieee80211_stop_queues() there is no function ieee80211_wake_queues(). This patch adds ieee80211_wake_queues(). The patch includes also a tiny simplification of ieee80211_stop_queues(). Signed-off-by: Ulrich Kunitz Signed-off-by: John W. Linville commit eb345b164e27151631dc943a903f5f83809587d3 Author: Michael Wu Date: Fri Apr 13 21:39:07 2007 -0400 [PATCH] mac80211: Always report supported rates in scan results This makes mac80211 always report supported rates in the scan results. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit 246c0ff6a3b9e86fe1ef15b9ea5e5db7ae31f555 Author: Michael Wu Date: Fri Apr 13 20:16:54 2007 -0400 [PATCH] mac80211: remove redundant rssi information in scan results rssi is already reported in IWEVQUAL. Remove the custom event that also reported rssi. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit ac7b7b3a3b805b5b5fe597212bef0ff1677f1371 Author: Michael Wu Date: Fri Apr 13 10:17:17 2007 -0400 [PATCH] eeprom_93cx6: do not assume zeroed buffer eeprom_93cx6_read_bits assumes u16 *data is already zeroed. This removes that assumption. Thanks to Andrea Merello for discovering this issue. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit a015c85cb813b2d45f31c0bc385cf9f1b4d58b4e Author: Ivo van Doorn Date: Thu Apr 19 20:06:24 2007 +0200 rt2x00: rt2x00pci shouldn't use DRV_NAME for irq registration rt2x00pci shouldn't use the DRV_NAME define for the registration of the interrupt. Inside rt2x00pci this define is set to rt2x00pci which is not specific about which driver this interrupt line belongs to. Instead the name from the pci_driver can be used to provide a more appropriate name. Signed-off-by: Ivo van Doorn commit 711974a5a13a7e981f7cfeed1d5938a3acaf6f82 Author: Ivo van Doorn Date: Thu Apr 19 19:52:20 2007 +0200 rt2x00: Create rt2x00_ops structure Gather all _ops structures into a single rt2x00_ops structure, this makes passing the ops around easier. Signed-off-by: Ivo van Doorn commit 5de3b4ef18668c295c34132d5618fb5132e13a99 Author: Ivo van Doorn Date: Wed Apr 18 22:22:43 2007 +0200 rt2x00: Minor fixes (comment & headers) rt2x00lib.h is already included by rt2x00.h Fix comment in rt2x00mac.c that refers to rt2500, while it should be talking about all drivers. Signed-off-by: Ivo van Doorn commit 4c61af371989b2b0c294bebcaa4ec68e247a5473 Author: Ivo van Doorn Date: Wed Apr 18 19:53:55 2007 +0200 rt2x00: Make rt2x00debug const Allow the rt2x00debug structure to be declared const, for this all function arguments need to be fixed. Signed-off-by: Ivo van Doorn commit bd69c630dac539448994c5bbf66121463bc0351f Author: Ivo van Doorn Date: Tue Apr 17 20:42:05 2007 +0200 rt2x00: Move device allocation into rt2x00lib Move device allocation handling into rt2x00lib module, this means that several rt2x00lib functions no longer need to be exported. Signed-of-by: Ivo van Doorn commit 59bb64a9b78573dc7b83c91b52a853e32b17871f Author: Ivo van Doorn Date: Tue Apr 17 17:17:39 2007 +0200 rt2x00: Move alloc_rings into rt2x00lib Move the alloc_rings function into the rt2x00lib module. Signed-off-by: Ivo van Doorn commit 50ee4b6eedd1d4b99ced5e6a41a69aba15a53fd7 Author: Ivo van Doorn Date: Mon Apr 16 18:54:51 2007 +0200 rt2x00: Fix sparse errors Make *_kick_beacon_gen static, and fix the argument passed to __ffs (the result of __ffs and not the argument should be checked if it exceeds the bit limit). Signed-off-by: Ivo van Doorn commit 517c0f4e284b14014e1f1cb2688d4c3d988fbcf3 Author: Ivo van Doorn Date: Mon Apr 16 17:20:51 2007 +0200 rt2x00: Explicit return type for rt2x00debug_register Be more explicit about the return type of rt2x00debug_register, instead of returning void* it returns a debugsfs_intf pointer. Signed-off-by: Ivo van Doorn commit a9524e774ae80555205fad9e34af298e4eb167ae Author: Ivo van Doorn Date: Mon Apr 16 17:15:55 2007 +0200 rt2x00: Fix debugfs related compile problem This will move the rt2x00debugfs header into rt2x00.h (which means it is included before the first usage of rt2x00debug structures). Also move the debugfs pointer assignment in rt2x00dev between an ifdef statement. Signed-off-by: Ivo van Doorn commit b3c64caab7d87d9e50d4b4b2de1e710d7c8588c7 Author: Ivo van Doorn Date: Mon Apr 16 17:07:09 2007 +0200 rt2x00: Add struct debugsfs pointer to rt2x00dev This will add a pointer to the debugfs structure inside rt2x00dev. This will make the debugfs registration and deregistration more generic. Signed-off-by: Ivo van Doorn commit 200aac9cf719e37f1b5619907f3ab3cce93a9d76 Author: Ivo van Doorn Date: Mon Apr 16 11:21:55 2007 +0200 rt2x00: Fix hardware mode registration order The order in which the hardware modes are registered is important. The first registered mode will be used by default even when a better mode is availble. Make sure rt2x00 registers 80211G (if available) before 80211B. Signed-off-by: Ivo van Doorn commit f4d79025dbbcfe3aaa3931475d25cf3942d3ca95 Author: Ivo van Doorn Date: Sun Apr 15 21:59:25 2007 +0200 rt2x00: Move ieee80211_hw init & registration into rt2x00lib Move ieee80211 structure initialization and registration into rt2x00lib module. This move most of the duplicated code for initialization and uninitialization into the rt2x00lib module. Signed-off-by: Ivo van Doorn commit aa970575d9777b6cdb205cc476bf44edb4775e22 Author: Michael Buesch Date: Sun Apr 15 20:39:45 2007 +0200 ssb: Code cleanup: Move printk to the specific register funcs. Signed-off-by: Michael Buesch commit 5723491c4059e0f1c55800d251f1baf2d1e0dabe Author: Michael Buesch Date: Sun Apr 15 20:33:44 2007 +0200 ssb: Fix some error path cleanup bugs. Signed-off-by: Michael Buesch commit 8050b413d6aa681730f0fbb94fea18b2674da045 Author: Ivo van Doorn Date: Sun Apr 15 20:00:12 2007 +0200 rt2x00: Fix spellin error Fix small spelling error in register comment Signed-off-by: Ivo van Doorn commit f90e8ebe340d820b7acd11592f9eacea8c93c789 Author: Michael Buesch Date: Sun Apr 15 17:51:17 2007 +0200 bcm43xx-mac80211: Use fallback rate from mac80211 to not confuse rate management. Also correctly calculate duration. Signed-off-by: Michael Buesch commit f757d7b53562e8cdc06bda88a59da5d158f0c3a5 Author: Ivo van Doorn Date: Sun Apr 15 12:52:58 2007 +0200 rt2x00: Move initialize() and uninitialize() into rt2x00pci/rt2x00usb The initialize() and uninitialize() functions share a lot of code between the individual PCI and USB drivers. Add the required interrupt callback to lib_ops structure and move the entire initialize() and uninitialize handling into rt2x00pci and rt2x00usb modules. Signed-off-by: Ivo van Doorn commit 2f1d3d6ddaddd287aab906a0a645f800b180b321 Author: Ivo van Doorn Date: Sun Apr 15 12:02:07 2007 +0200 rt2x00: Set rf_base during channel configuration Currently rf_base is set during init_hw, this should be moved into config_channel to simplify init_hw (and allows us later to move more into rt2x00lib). Change the name of the argument of config_channel to reflect this change. Signed-off-by: Ivo van Doorn commit 7e86ea648931fcb4aaef70f41cb0289d0713238d Author: Ivo van Doorn Date: Sun Apr 15 11:25:17 2007 +0200 rt2x00: Move common rxdone and txdone code into rt2x00lib Both PCI and USB drivers have a lot of the rxdone and txdone code in common. This will move that code into rxdone and txdone functions inside rt2x00lib. This will also make the PCI drivers handle the interrupt during interrupt context instead of rescheduling it for later. Signed-off-by: Ivo van Doorn commit e37e4547c3276e05238f52ebbc557c6dd683aaa5 Author: Ivo van Doorn Date: Sat Apr 14 15:18:55 2007 +0200 rt2x00: Fix rt2x00pci, rt2x00usb, rt2x00mac module/debug enviroment All modules (or objects for a module) should have the DRV_NAME defined for the debug handling. For rt2x00pci and rt2x00usb the module init and exit methods should be defined. Signed-off-by: Ivo van Doorn commit bb1cb51315e99f940dc9e493f22a7b84d262ba85 Author: Ivo van Doorn Date: Sat Apr 14 14:56:12 2007 +0200 rt2x00: Move beaconhandling into rt2x00{usb,pci} modules Cleanup common beaconhandling code, there are only differences in beacon handling between pci and usb devices. So create a rt2x00usb and rt2x00pci module and put the code in there. Signed-off-by: Ivo van Doorn commit eb7ca7025cfad0b37f13615723f7cbaf3909a293 Author: Johannes Berg Date: Thu Apr 12 12:00:54 2007 +0200 [PATCH] cfg80211/nl80211: remove reassociate command This doesn't seem to be useful. If we need it later we can add it back then. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit c83ea1a040755a4b42cf29a2ac4b13f2c880fde2 Author: Johannes Berg Date: Thu Apr 12 02:56:39 2007 +0200 [PATCH] mac80211: kill useless scan printks This patch removes the "starting scan" and "scan completed" messages that mac80211 prints all the time. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit 4e5bb7f5d61ef91e51935041900716e2cbff54cb Author: Michael Wu Date: Wed Apr 11 19:01:06 2007 -0400 [PATCH] bcm43xx: Hide if bcm43xx-mac80211 is builtin This prevents users from compiling bcm43xx (softmac) and bcm43xx-mac80211 into the kernel at the same time. In the case that it is attempted, make bcm43xx (softmac) not build. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit 74f15cc707a8d0dd685a9d918a1479a097e68efd Author: Ivo van Doorn Date: Thu Apr 12 19:38:59 2007 +0200 rt2x00: Fix crash during rmmod with debugfs enabled rt2x00debug_deregister should be called with a valid reference to the rt2x00debug_intf structure. Instead it was called with a pointer to a uninitialized pointer. Second bug was that rt2x00debug_register could fail and send a reference to struct rt2x00debug instead of rt2x00debug_intf. Signed-off-by: Ivo van Doorn commit 02587795e94ac2ab8d5cb9780576fd9bccb6a750 Author: Ivo van Doorn Date: Thu Apr 12 19:16:26 2007 +0200 rt2x00: Fix debugfs register length handling Register length in rt2x00debug is handled through the word count and not bytes. Rename the variable to make this clear, and fix the lengths that are being passed. This will fix the bug where the register values beyong the allowed length are being read. Signed-off-by: Ivo van Doorn commit 465f7ff5bb275b9a7c353dfeeabe92d3eb37e4e8 Author: Ivo van Doorn Date: Thu Apr 12 18:08:55 2007 +0200 rt2x00: rt2x00debug should be compiled into rt2x00lib rt2x00debug shouldn't be a seperate module, instead it should be compiled into the rt2x00lib module. Signed-off-by: Ivo van Doorn commit ccc47c8696e2a0c62bb5684d26eff2525a6cd5cc Author: Ivo van Doorn Date: Thu Apr 12 17:05:04 2007 +0200 rt2x00: Create generic rt2x00lib_{enable,disable}_radio() function Move generic radio control handling into rt2x00lib and make the driver versions of that function only perform the register calls to enable or disable the radio. Signed-off-by: Ivo van Doorn commit c523e1a8f6e3cdb4be538accfeb1dc825e623dde Author: Michael Wu Date: Sat Mar 31 17:00:47 2007 -0400 [PATCH] mac80211: set enc_capa Update we_version_source and set enc_capa in iw_range. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit e7d29764355539d2ce58519d243fbcec8a267669 Author: Michael Wu Date: Sat Mar 31 15:03:45 2007 -0400 [PATCH] mac80211: use IWEVGENIE Use IWEVGENIE which was defined in WE-18. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit aa426aa9d45f92c038a6efcb01c846274ee82d21 Author: Christian Lamparter Date: Wed Apr 11 14:22:17 2007 -0400 [PATCH] adm8211 remove some unnecessary IEEE80211_RATE_PREAMBLE2. IEEE80211_RATE_CCK_2 includes the flag for the short preambles. Signed-off-by: Christian Lamparter Signed-off-by: Michael Wu commit 22a61f3832f397c9cb2f02202341ff6a82db974e Author: Michael Wu Date: Wed Apr 11 14:19:33 2007 -0400 adm8211: remove passive scan handler The adm8211 passive scan handler does nothing that mac80211 can't. Signed-off-by: Michael Wu commit e3279bece6c4d0b4e61dfd265f2eb24a2a44a64c Author: Michael Wu Date: Wed Apr 11 14:18:03 2007 -0400 zd1211rw: Hide if zd1211rw-mac80211 is builtin This prevents users from compiling zd1211rw (softmac) and zd1211rw-mac80211 into the kernel at the same time. In the case that it is attempted, make zd1211rw (softmac) not build. Signed-off-by: Michael Wu commit 15260f3823315996ee3d78a6862b51833595469d Author: Michael Wu Date: Wed Apr 11 14:12:51 2007 -0400 zd1211rw-mac80211: Enable monitor mode This patch allows monitor mode to be directly used in zd1211rw-mac80211. Signed-off-by: Michael Wu commit cdfd0c5a045540fdda7f40e6d1a9ffb4493a01eb Author: Ivo van Doorn Date: Wed Apr 11 20:03:30 2007 +0200 rt2x00: Don't unregister unregistered interfaces When ieee80211_hw structure is not yet registered, we should not attempt to unregister such structure since that will result into a kernel panic. Signed-off-by: Ivo van Doorn commit 601426f9623ec8b3ffe573cb95cd1f63ec806ee1 Author: Michael Wu Date: Wed Apr 11 13:25:09 2007 -0400 rtl8187: fix rtl818x_ioread* return types I screwed up the return types on the rtl818x_ioread* functions and made them all return u8. Somehow, the driver managed to work despite this. Signed-off-by: Michael Wu commit e25588a08a1b7e0d500c30dd0ccee76d318fe72d Author: Larry Finger Date: Wed Apr 11 11:08:53 2007 -0500 bcm43xx-mac80211: Fix machine checks on PPC with rev 1 PHYs On PPC architecture with phy->rev == 1, machine checks occur during initialization of the "Extended G PHY registers". This problem was also seen on bcm43xx-softmac, and was fixed by conditionally skipping over certain reads/writes of these registers. The same solution has been applied here with testing by David Woodhouse. Note: These modifications are not found in the specifications, but are needed for PPC. Signed-off-by: Larry Finger Signed-off-by: Michael Buesch commit 96a7b5c2f11ffe8e0ca852292d6d56f3d6afcbc9 Author: Christian Lamparter Date: Wed Apr 11 13:17:28 2007 -0400 [PATCH] p54: more on retry count, rates and outputpower. This patch adds more meaningful names for unknown magic values and structures for the upcoming 802.11e patch. Thanks to "Jean-Baptiste Note" for notes about the bits & bytes in p54_frame_sent_hdr. Signed-off-by: Christian Lamparter Signed-off-by: Michael Wu commit 07cefb185c0d6cd6c92cd57293d648d506ae05ed Author: Michael Wu Date: Wed Apr 11 13:07:38 2007 -0400 adm8211, p54, zd1211rw-mac80211, rtl8187: return -EOPNOTSUPP in add_interface The add_interface callback's return code ends up going to userspace so it should return something meaningful like -EOPNOTSUPP. Thanks to Johannes Berg for pointing this out. Signed-off-by: Michael Wu commit aff5669d0e9ea13e31489d37dc93dc6c4522fae2 Author: Michael Wu Date: Mon Apr 9 22:45:45 2007 -0400 [PATCH] mac80211: make mode change to same mode always successful This patch makes ieee80211_ioctl_siwmode always return success if the new mode is the same as the current mode. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit fac99a7e47a3d0c3bde0726935c27d13877a364c Author: Michael Buesch Date: Sun Apr 8 12:51:12 2007 +0200 [PATCH] mac80211: Export duration calculation function This exports a frame duration calculation library function to allow drivers easy calculation of the duration field for some generic frame. This is needed for drivers like bcm43xx, where we must precalculate possible duration fields in software (for fallback rates, etc..). Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 2a65f7119f77460d51ce86b8445e3a5cc025cf38 Author: Daniel Drake Date: Sat Apr 7 15:45:39 2007 +0100 [PATCH] zd1211rw-mac80211: Add ID for ZyXEL AG-220 Tested by Christoph Sager and Tomas Klas zd1211b chip 0586:3412 v4810 high 00-13-49 AL7230B_RF pa0 g---- Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit aac634bb334292dee6c843a630517ac90b959e2a Author: Daniel Drake Date: Sat Apr 7 15:45:24 2007 +0100 [PATCH] zd1211rw-mac80211: Add AL7230B RF support for ZD1211B This patch adds support for some new ZD1211B devices which ship with the AL7230B RF. Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit fe6b0fa2d07c505fa9455308c6957835499a350a Author: Daniel Drake Date: Sat Apr 7 15:44:55 2007 +0100 [PATCH] zd1211rw-mac80211: rework band edge patching This change allows RF drivers to provide their own 6M band edge patching implementation, while providing a generic implementation shared by most currently supported RF's. The upcoming ZD1211B/AL7230B code will use this to define its own patching function, which is different from the other RF configurations. Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit 0a04fbcffcb56d8d231f00935ac1bccf51d4cefc Author: Daniel Drake Date: Tue Apr 3 23:08:47 2007 +0100 [PATCH] zd1211rw-mac80211: Fix E2P_PHY_REG patching Due to conflicting/confusing defines in the vendor driver, we were reading E2P_PHY_REG from the wrong location. CR157 patching was slightly incorrect in that the vendor driver only patches in an 8-bit value, whereas we were patching 24 bits. Additionally, CR157 patching was happening on both zd1211 and zd1211b, but this should only happen on zd1211. Signed-off-by: Daniel Drake Signed-off-by: Ulrich Kunitz Signed-off-by: John W. Linville commit 3dbae1d7ed6ff443553bef605c2e50fd993e02c1 Author: Daniel Drake Date: Tue Apr 3 23:08:24 2007 +0100 [PATCH] zd1211rw-mac80211: Remove invalid CR write during ZD1211 phy reset The vendor driver only does the CR123 write for non-USB devices (which don't exist on the consumer market) Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit 516088259f1ef37e6d74504d1626cc1125893ffd Author: Ivo van Doorn Date: Wed Apr 11 17:14:45 2007 +0200 rt2x00: Add new "invisible" config options for PCI and USB Add RT2X00_LIB_PCI and RT2X00_LIB_USB config options, these will be required for putting bringing shared code together. Signed-off-by: Ivo van Doorn commit f5efd860e17fba64e1466b91ed91fa5eae34b136 Author: Johannes Berg Date: Thu Mar 29 00:10:39 2007 +0200 [PATCH] remove another wext/netlink comment Looks like I missed this when removing wext/nl. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit 842c2e7c3a92ff9e9f9e875b14d4947dbdeab4dd Author: Johannes Berg Date: Tue Mar 27 23:52:39 2007 +0200 [PATCH] mac80211: use is_broadcast_ether_addr Change a whole bunch of places to use is_broadcast_ether_addr. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit d80bb18ca3444520b465f3006bbb78837a5e28d9 Author: Johannes Berg Date: Tue Mar 27 18:01:17 2007 +0200 [PATCH] mac80211: add sta item when changing WDS address This patch adds a sta item for the new address when you change a WDS link's address. If the sta item cannot be created return an error. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit 98c24cfb86b59aaadc9e045ed4da777e90c89321 Author: Johannes Berg Date: Tue Mar 27 18:00:50 2007 +0200 [PATCH] mac80211: delete pfifo qdisc Since the regular sch_fifo can now be selected even without NET_SCHED we can use that one instead of shipping our own for the case where the kernel was built without NET_SCHED. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit e4c4acae5bc8eae8d061397b5616b25ea05022bc Author: Johannes Berg Date: Tue Mar 27 18:00:26 2007 +0200 [PATCH] nl80211: update interface types "secondary" type doesn't seem useful, no driver implements it and the semantics aren't clear either. Hence, don't support that in nl80211 but do support AP_VLAN type devices. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit dd29ce16a2f0930fa8368341c414b13d859c4cd1 Author: Johannes Berg Date: Tue Mar 27 17:59:54 2007 +0200 [PATCH] mac80211: make 802.1X setting consistent with low-level driver If the low-level driver fails to reconfigure 802.1X we currently still set the variable. Don't do that since it brings things out of sync. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit b707f8f6b7595f561f61be58bb08fd7425aa7979 Author: Johannes Berg Date: Tue Mar 27 17:59:26 2007 +0200 [PATCH] mac80211: use ARRAY_SIZE This patch converts all applicable instances of sizeof(array)/sizeof(member) that I found to ARRAY_SIZE. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit a4055a8688c92f4edf1d50cc32f4f68e5751cc73 Author: mabbas Date: Wed Apr 4 03:37:53 2007 -0700 [PATCH] mac80211: 802.11n mgmt action frame handling Add draft IEEE 802.11n management action frame handling Several Action frame format are defined to support IEEE 802.11n features. This patch adds support to parse Block Ack action frame, then call low level driver with the frame's body. Signed-off-by: Mohamed Abbas Signed-off-by: John W. Linville commit 300c5c4eaeb0ae9dc52381402be2df1b1b787240 Author: mabbas Date: Wed Apr 4 03:37:46 2007 -0700 [PATCH] mac80211: 802.11n mgmt action frame data structures Add draft IEEE 802.11n management action frame data structures and constants Several Action frame format are defined to support IEEE 802.11n features. This patch add the data structure for Block Ack management action frame. Signed-off-by: Mohamed Abbas Signed-off-by: John W. Linville commit f9a1c4eceb8bd55187ce983dc3b6929c866b0c1a Author: mabbas Date: Wed Apr 4 03:37:38 2007 -0700 [PATCH] mac80211: A-MSDU Rx aggregation support Add A-MSDU Rx aggregation support. To support IEEE 802.11n, we need to be able to process A-MSDU frames. The present of the HT control field indicates it is A-MSDU frame. This patch adds support to discover and process A-MSDU frames. Signed-off-by: Mohamed Abbas Signed-off-by: John W. Linville commit 0a52c306308d838b1180566149779ca16f8eb23b Author: mabbas Date: Wed Apr 4 03:37:29 2007 -0700 [PATCH] mac80211: Add 802.11n discovery and association support Add basic support for IEEE 802.11n discovery and association. This patch adds support to discover IEEE 802.11n AP and enable association to 802.11n Network. It parses beacon to discover 802.11n IE and include HT capability information element in Association Request Frame. It also call low level driver with the HT capability available during association. Signed-off-by: Mohamed Abbas Signed-off-by: John W. Linville commit 6caf5f31dae2ea66173ef197ff365e3d387845da Author: mabbas Date: Wed Apr 4 03:37:18 2007 -0700 [PATCH] mac80211: Add 802.11n data structures and constants Add draft IEEE 802.11n data structures and constants to ieee80211.h Several information element were added to support 80211n capabilities. This patch adds all data structures related to these information element. Signed-off-by: Mohamed Abbas Signed-off-by: John W. Linville commit e88140add8d2304cb531af01fed5a98534656df7 Author: Ivo van Doorn Date: Wed Mar 21 19:57:39 2007 +0100 [PATCH] Move eeprom_93cx6 from lib/ to drivers/misc/ As requested, this will move the eeprom_93cx6 from its location in lib/ to drivers/misc/ This second version uses correct identation for the --help-- line. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 62173a8a0d5d8abc924bbcd2fcf5059bddd83a76 Author: Hong Liu Date: Tue Mar 27 10:07:20 2007 +0800 [PATCH] mac80211: fix iwlist wlanX retry behavior iwconfig relies on what mac80211 returned to display the retry info when it sets retry->flag = 0. So we can't just return -EINVAL when retry->flag == 0. Signed-off-by: Hong Liu Signed-off-by: John W. Linville commit 6dc1dab7e163e5456856171fba5142f8977daf17 Author: Hong Liu Date: Tue Mar 27 10:07:10 2007 +0800 [PATCH] mac80211: fix iwlist wlanX key behavior return correct key info in SIOCGIWRANGE, iwconfig will use this info to further query the key info by SIOCGIWENCODE. Signed-off-by: Hong Liu Signed-off-by: John W. Linville commit aaf881104a6e6d19945828001c6579abede683d5 Author: Ivo van Doorn Date: Wed Apr 11 14:57:49 2007 +0200 rt2x00: Add new rt73 USB ID This will add a new USB ID for rt73usb Signed-off-by: Ivo van Doorn commit 0a24fc00133198fd82b61df2f034f0a45153dd9b Author: Ivo van Doorn Date: Tue Apr 10 22:44:20 2007 +0200 [PATCH] rt2x00: Correctly enable the radio The DEVICE_ENABLED_RADIO_HW flag was accidently added, this flag is only used (and set correctly) with the rfkill patch applied to it. Without the rfkill patch this will only prevent the radio to be enabled for PCI devices. Signed-off-by: Ivo van Doorn commit 9159f411976bc3cf0843f9bc006c115d0b8f8bc7 Author: Ivo van Doorn Date: Tue Apr 10 22:43:44 2007 +0200 [PATCH] rt2x00: new USB ID for rt73usb This will add a new USB ID for the rt73usb driver Signed-off-by: Ivo van Doorn commit ffdcbbc6c961c025288ec595daff9ad31f702cea Author: Ivo van Doorn Date: Tue Apr 10 22:43:14 2007 +0200 [PATCH] rt2x00: Use correct length in descriptor This fixes an important issue where the incorrect length is being passed to the descriptor initializor. This incorrect initialization will cause frames to be send out incorrectly. Signed-off-by: Ivo van Doorn commit f16ed052f53a09fa78c8713365209a8689382400 Author: Will Dyson Date: Mon Apr 9 00:36:23 2007 -0400 bcm43xx-mac80211: Work around 30bit DMA limitation When DMA mapping for RX fails because of the limitation, retry the allocation in ZONE_DMA. When the network stack passes us TX buffers that cannot be mapped because of the limitation, allocate a bounce buffer in ZONE_DMA and copy the packet there. Signed-off-by: Will Dyson Signed-off-by: Michael Buesch commit 12234d08ae94054c38f1136313a1463b291114ff Author: Will Dyson Date: Mon Apr 9 00:36:22 2007 -0400 bcm43xx-mac80211: Fix error path memory leak When doing setup for the dma ring, the txhdr_cache must be freed if there is an error after it is allocated. Signed-off-by: Will Dyson Signed-off-by: Michael Buesch commit 969d0de14522c3e6608c6650dea231bc9b474b7f Author: Will Dyson Date: Mon Apr 9 00:36:21 2007 -0400 bcm43xx-mac80211: Catch dma mapping failures. Check result of dma_map_single(), print warnings and propagate errors up the call stack. Signed-off-by: Will Dyson Signed-off-by: Michael Buesch commit f4e2dd4a8b9ff1d14c3fffc36d578d12f8cb8da3 Author: John W. Linville Date: Mon Apr 9 14:10:01 2007 -0400 [PATCH] wext-old: fixup copyright info Signed-off-by: John W. Linville commit f85a84e323b98fb8ca0fc6cdefe9289b1da9a151 Author: Larry Finger Date: Sat Apr 7 13:00:05 2007 -0500 bcm43xx-mac80211: Fix error in initiallizing max RSSI and max signal In bcm43xx-mac80211, the max_rssi and max_signal values sent to mac80211 are reversed. Signed-off-by: Larry Finger Signed-off-by: Michael Buesch commit 7f6f458d73b95074a5fa9e24c940ec4589be4837 Author: Ivo van Doorn Date: Sat Apr 7 19:49:27 2007 +0200 rt2x00: rt2x00_register_multiwrite should use correct length rt2x00_register_multiwrite only works correctly when the length that is written is a multiple of the register wordsize (u16 for rt2500usb, u32 for the other drivers). This means we have to remove the optimization from mac and bssid writing. Signed-off-by: Ivo van Doorn commit 952c2f8585b5f8a23bdd7abe945f1d601457942e Author: Michael Buesch Date: Sat Apr 7 17:14:31 2007 +0200 bcm43xx-mac80211: Remove the "vstack" and its remaining uses. It's overengineered crap. Signed-off-by: Michael Buesch commit c5268b1b623538903a7b1cae5a4bc4b062b459ec Author: Michael Buesch Date: Sat Apr 7 16:33:14 2007 +0200 bcm43xx-mac80211: Replace vstack in LO setup by a data structure. This fixes a bug where a value is saved twice, so incorrectly restored by the vstack. Signed-off-by: Michael Buesch commit 32bfb29ad13f7451c3a09f092430b30133fc6739 Author: Johannes Berg Date: Wed Mar 28 23:09:22 2007 +0200 [PATCH] fix debugfs compile w/o debug counters Ouch. So I made a rather stupid mistake when doing the debugfs stuff in that some things aren't properly ifdef'ed with debug counter support. This patch fixes it. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit e978548f4f13c610b63f0fb21c587e2a898c65cc Author: Luis R. Rodriguez Date: Wed Apr 4 16:24:31 2007 -0400 [PATCH] White space patch to make mm-master and master identical FWIW, resolves two space conflicts between master and mm-master. Not including list as not sure if you wanted this anyway. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville commit fff1820d736a297b3d705e4e0d578e5890388642 Author: Ivo van Doorn Date: Fri Apr 6 18:44:04 2007 +0200 Make rt2x00 debugfs support depend on mac80211 support. This is valid because rt2x00 uses the debugfs entry created by mac80211 as a starting location for the rt2x00 debugfs files. Signed-off-by: Ivo van Doorn commit 5b04fc147ab7152409f0fedd76d1fa2230e54c17 Author: Ivo van Doorn Date: Fri Apr 6 18:29:17 2007 +0200 Don't use magical values for the length with rt2x00_vendor_request, instead use sizeof() with the correct type. Signed-off-by: Ivo van Doorn commit 356f78c6de1ee9d887794e6bc3db41da575ba1fa Author: Daniel Drake Date: Mon Mar 26 00:20:25 2007 +0100 [PATCH] zd1211rw-mac80211: Add another ID for Linksys WUSBF54G Tested by TiCPU on irc zd1211 chip 13b1:001e v4802 high 00-14-bf AL2230_RF pa0 g--- Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit 1934b1c487dd797daacc8b27d68852c37dd52f6f Author: Daniel Drake Date: Mon Mar 26 00:18:39 2007 +0100 [PATCH] zd1211rw-mac80211: Fix NULL dereference on installer exit path When dealing with a DEVICE_INSTALLER, dev will be NULL when disconnect() is called. This fixes a NULL dereference oops with such devices. Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit ee7934d565808069048fa8b6d842c3290ee9b16d Author: Ulrich Kunitz Date: Mon Mar 26 00:18:17 2007 +0100 [PATCH] zd1211rw-mac80211: Fix for monitor mode bug The ZD1211 supports the delivery of packets with a wrong CRC value to the host. We switched that feature on in monitor mode, so that incomplete packets were delivered. This problem has been reported to bugzilla.kernel.org as bug 8152. This patch fixes it for the mac80211 stack. Signed-off-by: Ulrich Kunitz Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit d88487618c869655fde3e6f88c1c83891eda3d56 Author: Ulrich Kunitz Date: Mon Mar 26 00:17:54 2007 +0100 [PATCH] zd1211rw-mac80211: tweak reset-on-probe behaviour This is an update to the earlier commit 8b4ebaabf12. The argument is directly used and an error condition will be handled. Here are some sentences about the reason for this commit. There have been a larger number of reports of problems after rebooting the machine. According to reports the resetting of the stick during probe makes this problem disappear. Notify that it doesn't help while reloading the module. Signed-off-by: Ulrich Kunitz Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit fbc5a553c5c5b18a2c367dacb209d2c832914899 Author: Johannes Berg Date: Sat Mar 24 14:14:36 2007 +0100 [PATCH] add myself to various files Now that Jiri merged those broken patches that showed only my email address instead of my name I guess I should add myself to .mailmap. Add myself to MAINTAINERS for cfg80211/nl80211 too and CREDITS while I'm at it. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit 2ee2548e9e8856c6aa9c2cb08d0f65eb7da1f4ae Author: Johannes Berg Date: Fri Mar 23 15:06:52 2007 +0100 [PATCH] cfg80211/nl80211 updates This patch adds a whole bunch of new nl80211 commands and removes the configure call since I have decided that simply passing all required parameters along with the associate call is much better since it doesn't require a transaction layer in the kernel. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit 788b58157afc973088273856cdca051f871d0761 Author: Johannes Berg Date: Wed Mar 7 18:23:13 2007 +0100 [PATCH] remove wext/nl This removes wireless extensions over netlink as per feature removal schedule. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit 1abe018c9df94c9e65723b4bb5a29188fd841b0f Author: Johannes Berg Date: Mon Mar 26 12:47:13 2007 +0200 [PATCH] cfg80211: allow wiphy renaming This patch enables userspace to rename 802.11 PHYs via nl80211. It adds the first multicast group and notifies it of these renames as well. Also, it changes the default wiphy name to just "phy%d in anticipation of Greg KH's sysfs change that will add the class name in front of the directory name. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit 30254f24ffefffd341b81650ae0ebe222fb15582 Author: Michael Buesch Date: Sun Mar 25 00:05:42 2007 +0100 bcm43xx-mac80211: Fix hwencryption breakage for latest firmware. The firmware API changed slightly. This fixes the driver to work on both, new and old v4 firmware. Signed-off-by: Michael Buesch commit 3c2057947fc00137788ce2e259cf07cd8b3d8a42 Author: Michael Buesch Date: Sat Mar 24 22:52:22 2007 +0100 ssb: kfree devwrap (not dev) in dev register error path. nonfatal bug, as the two pointers point actually the same place. Signed-off-by: Michael Buesch commit 35262af661f8561e197b093581889e85ef6f5d74 Author: Ivo van Doorn Date: Fri Mar 23 21:29:44 2007 +0100 [PATCH] mac80211: Add software sequence support Most hardware can keep track of sequence numbers themselves, unfortunately *most* doesn't cover all devices. ;) This patch will keep track of the (per-bss) sequence number. Signed-off-by: Ivo van Doorn Signed-off-by: Jiri Benc commit a6d266e59dd7d2f2c4bc439d47f21cf92d6df949 Author: Ivo van Doorn Date: Fri Mar 23 21:29:44 2007 +0100 [PATCH] mac80211: Prevent unregistering of unregistered hw At the moment it is possible to call ieee80211_unregister_hw() for an unregistered hw structure. This will cause a big panic. This patch will add a BUG_ON() line to warn about this situation. Signed-off-by: Ivo van Doorn Signed-off-by: Jiri Benc commit baf56723a69d16311ad09db5ecee1f3e4c4d7ca1 Author: Michael Buesch Date: Fri Mar 23 21:29:43 2007 +0100 [PATCH] mac80211: Properly kill tasklets before shutdown We need to do tasklet_kill() on any tasklet on unregister to make sure the tasklet is not running _and_ scheduled anymore. (tasklet_disable() only ensures it's not running anymore). This fixes the tasklet related crash that was reported some time ago. Signed-off-by: Michael Buesch Signed-off-by: Jiri Benc commit c8c6581672445578ec8a7a2b7e48a7591174d24b Author: Michael Buesch Date: Fri Mar 23 21:14:00 2007 +0100 ssb: use container_of for the dev-wrapper. Much less confusing. Signed-off-by: Michael Buesch commit 0ddfc778426ba75c2ad71d23e9e2e48121264c70 Author: Hong Liu Date: Fri Mar 23 19:34:53 2007 +0100 [PATCH] mac80211: fix passing wrong pointer to ieee80211_dump_frame Signed-off-by: Hong Liu Signed-off-by: Jiri Benc commit 1485998df4d688be3e0907423a774dadbf90a631 Author: Hong Liu Date: Fri Mar 23 19:34:53 2007 +0100 [PATCH] mac80211: fix wrong keyidx will change default key Signed-off-by: Hong Liu Signed-off-by: Jiri Benc commit 202d728b6d56a44e1be0970ee3e3f0971fbd7c66 Author: Michael Wu Date: Fri Mar 23 19:34:52 2007 +0100 [PATCH] mac80211: switch STA interfaces to PS mode during scan This makes scans switch STA interfaces into PS mode so the AP queues frames destined for us while we are scanning. This is achieved by sending a nullfunc data frame with the PS mode bit set before scanning commences, and a nullfunc data frame without the PS mode bit set after scanning is completed. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit a290e1fe4bd5779b96e07cc9deff9c75b7887a54 Author: Johannes Berg Date: Fri Mar 23 19:03:51 2007 +0100 [PATCH] mac80211: fix remaining sparse warnings This patch fixes the sparse warnings that remain after Michael Wu's patches as well as those I might have introduced myself during the debugfs restructuring... Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc commit 7eb09815b60aba14a6e3e4bafb5841f13ae75c0f Author: johannes@sipsolutions.net Date: Fri Mar 23 19:03:51 2007 +0100 [PATCH] mac80211 debugfs Kconfig This patch adds a configuration option to mac80211 that lets you enable or disable the debugfs internal state export. I intentionally made this option not depend on MAC80211_DEBUG because that option always generates debug messages in the kernel log; this way you can see the internal state without seeing any debugging message. Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc commit a31d8e7a6e4789185ad35851a1f554b7ed29c439 Author: johannes@sipsolutions.net Date: Fri Mar 23 19:03:50 2007 +0100 [PATCH] mac80211: move per-netdev and key stuff to debugfs This patch moves the remaining sysfs stuff from mac80211 into debugfs. Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc commit 06c54cc07ddcf8a2fcaa247f8c72c6a3c183d2bd Author: johannes@sipsolutions.net Date: Fri Mar 23 19:03:50 2007 +0100 [PATCH] mac80211: move sta dir to debugfs This patch moves the "sta" dir to a new debugfs dir "stations". Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc commit fc21c442d9236386b5c87ffee08cbc61a7f294dd Author: johannes@sipsolutions.net Date: Fri Mar 23 19:03:49 2007 +0100 [PATCH] mac80211: move PHY things to debugfs This patch moves everything except the "sta" and "key" directories from the phy sysfs to phy debugfs. Since the current rate control algorithm isn't using any global sysfs entries it also removes the ability for a rate control algorithm to do so. That can be re-added to debugfs when necessary. Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc commit 4eb6baed5808950d168cb935c426148ba8256366 Author: Michael Buesch Date: Fri Mar 23 19:03:49 2007 +0100 [PATCH] mac80211: Fix pkt_type annotations There is no need for an instance of the enum in ieee80211_local. Remove it. Also make the constants uppercase to respect kernel coding style. Signed-off-by: Michael Buesch Signed-off-by: Jiri Benc commit a24e7bbf06b53845f311f596203bc00e0564991d Author: Michael Wu Date: Fri Mar 23 17:21:02 2007 +0100 [PATCH] mac80211: Stop virtual interfaces during scan This prevents data frames from being queued on the master device if it is in the midst of a scan. It also makes both master and virtual interfaces properly set trans_start when frames are sent. tx_queue_len is left as the default on virtual interfaces to allow frames to be queued while the device is scanning. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit 22abe7f30c0e34ac94eda86e6aad66f74e582317 Author: Michael Wu Date: Fri Mar 23 17:21:02 2007 +0100 [PATCH] mac80211: Set carrier status for STA interfaces This makes STA interfaces set the carrier status based on the current association status. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit 3dcfdf95493a7317b1373d6212f4e7b3461b465d Author: Michael Wu Date: Fri Mar 23 17:21:02 2007 +0100 [PATCH] mac80211: Remove tx_timeout callback This never worked in the first place and we can't use the network watchdog anyway since that checks if the queue is stopped, but wireless devices can have many queues. TX timeouts on virtual interfaces don't really make sense either since they can't stall. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit 6daea49d5cb784819a81946260823c12129f1537 Author: Michael Wu Date: Fri Mar 23 17:21:02 2007 +0100 [PATCH] mac80211: Remove curr_rates and fix related concurrency issues This switches the code from curr_rates and num_curr_rates to directly using the mode and converts portions of the code to use pointers to rates instead of indices to refer to rates. Two new fields are introduced in struct ieee80211_conf which may replace the channel/frequency/channel_val/phymode fields in the future. The rate control is now cleared only when the operating channel is changed. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit 9d0a7f4ed6daf144111954e2ecd70df78e7449d9 Author: Jiri Benc Date: Fri Mar 23 17:21:02 2007 +0100 [PATCH] mac80211: fix rtnl locking in ieee80211_register_hw rtnl locking in ieee80211_register_hw was racy. This patch fixes that. Signed-off-by: Jiri Benc commit 9e00ff1fabe362774319e9d24fe866a0140b0f34 Author: Jiri Benc Date: Fri Mar 23 17:21:01 2007 +0100 [PATCH] mac80211: remove useless callbacks from wmaster Wireless handlers (and other callbacks) on master interface only confuse users. Let's get rid of them. Signed-off-by: Jiri Benc commit c5ba64d9fc39c42ebc1ed38993dd6d3281708b8a Author: Jiri Benc Date: Fri Mar 23 17:21:01 2007 +0100 [PATCH] mac80211: drop packets from nonexisting interfaces in PS mode In a power saving mode, packets queued by devices that meanwhile disappeared has to be discarded. Signed-off-by: Jiri Benc commit 4f8b54c257d19d23cc243bbf89e1dd7bfac6c079 Author: Michael Wu Date: Thu Mar 22 21:12:35 2007 -0400 rtl8187: Set MSR This sets the MSR accordingly when a link has be established/lost. Signed-off-by: Michael Wu commit ebc20eedea012c9ca805a1453a0f4eb6f52c8f00 Author: Michael Wu Date: Thu Mar 22 21:12:13 2007 -0400 rtl8187: Enable contention window Random backoff shouldn't be disabled. This reenables it. Signed-off-by: Michael Wu commit d63e74b065c80c5de9d5915ec2743dc30dbd02eb Author: Michael Wu Date: Thu Mar 22 19:59:16 2007 -0400 p54: Add RTS/CTS support Thanks to Christian Lamparter for discovering these flags. Signed-off-by: Michael Wu commit 62ee473d67b7ae353d210b186abaadc37a642237 Author: Christian Lamparter Date: Thu Mar 22 19:50:25 2007 -0400 [PATCH] p54: fix rates and add short preambles support for cck http://islsm.org/pipermail/developers/2007-March/000148.html Signed-off-by: Christian Lamparter Signed-off-by: Michael Wu commit 37e4f51da24d712046ff971e45df9fa26a5bb387 Author: Michael Wu Date: Thu Mar 22 19:47:42 2007 -0400 p54pci: Fix error path when eeprom read fails The p54 PCI driver leaks memory when eeprom read fails. This fixes it. Signed-off-by: Michael Wu commit 5edbf5a5a49e396a02094c9834db3a5ceb88bdf5 Author: Michael Wu Date: Thu Mar 22 19:47:11 2007 -0400 p54usb: Move USB ID entry to v2 section The DLink DWL-G122 rev A2 is a v2 usb (GW3887) device. Signed-off-by: Michael Wu commit fbb7380ecbd5f61d201ef9b11c458f932b5dc8fb Author: Michael Buesch Date: Thu Mar 22 18:09:29 2007 +0100 ssb: Do pci core init on attach stage. bus register stage is too early on embedded systems. Signed-off-by: Michael Buesch commit c9fe9967146f1529d730e08cf80201797303d30a Author: Michael Buesch Date: Thu Mar 22 17:17:51 2007 +0100 bcm43xx-mac80211: Fix extlna flag for 2050 init Original patch by Larry Finger. Signed-off-by: Michael Buesch commit 242004ba4362919f4ef59fc0546d3facefd9c0af Author: Michael Buesch Date: Wed Mar 21 17:58:24 2007 +0100 bcm43xx-mac80211: LO setup: Registers are ORed, not overwritten. Signed-off-by: Michael Buesch commit c199a37667e9e37babe1bdd070052ccd3db05fef Author: Michael Buesch Date: Wed Mar 21 17:40:48 2007 +0100 bcm43xx-mac80211: Update my email address. But the old one is still valid. Signed-off-by: Michael Buesch commit a88d079c727b9c029c164398687c0a746264d72d Author: Michael Buesch Date: Wed Mar 21 17:31:45 2007 +0100 bcm43xx-mac80211: Remove old unsupported devs from the PCI list. Signed-off-by: Michael Buesch commit d5c6d2c64b2656e41c28a7be65f01ecbf3ece840 Author: Michael Buesch Date: Wed Mar 21 17:29:21 2007 +0100 bcm43xx-mac80211: Add more VStack debugging. Signed-off-by: Michael Buesch commit 5f8e7b060d9973d0a1e81d1faacd2fbb69ac7417 Author: Michael Buesch Date: Wed Mar 21 17:04:32 2007 +0100 bcm43xx-mac80211: vstack: Fix slight bug and add debugging Bug: Queue overrun by one in restore. Really nonfatal bug. Signed-off-by: Michael Buesch commit 2ffdfb0b716aeb4d6174394a411f26094d1bb2fe Author: Michael Buesch Date: Wed Mar 21 16:24:51 2007 +0100 bcm43xx-mac80211: Fix 2050 radio init value restore for lb gain devices. Original patch from Larry Finger. Signed-off-by: Michael Buesch commit 72bc18d52c57c56c4f6a9b50e42b9e6f2400c38f Author: Ivo van Doorn Date: Sun Mar 11 15:05:48 2007 +0100 [PATCH] rt2x00: fix firmware dependency There is no need to always build rt2x00lib.ko including the firmware handlers, only rt61pci and rt73usb will require the firmware handling. So lets add a new invisible config option selected by rt61pci and rt73usb to enable the firmware handling inside rt2x00lib. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit ca166bc4b6ee3deb139df6e9e116f585ce7b1835 Author: Ivo van Doorn Date: Fri Mar 9 16:11:33 2007 +0100 [PATCH] rt2x00: Fix power of 2 check Cleanup code, make gcc happy, make sparse happy. What a happy patch ;) This cleans up the code used to check the validity of the register fields, this will also reduce the required memory usage while working with sparse. This patch was inspired on the codesuggestions from Linus. This update also includes a check if the first found bit does not exceed the 16bit or 32 bit limit. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 16dbfa582fad18c9fc2009145527e3bc66fad72d Author: Ivo van Doorn Date: Thu Mar 8 22:14:51 2007 +0100 [PATCH] rt2x00: Remove ieee80211_netif_oper usage Remove the ieee80211_netif_oper usage from rt2x00. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 2f9b6d2550470c62156d173413c1f8c7fd76d02d Author: Ivo van Doorn Date: Thu Mar 8 22:14:50 2007 +0100 [PATCH] rt2x00: Split rt2x00dev.c This splits the rt2x00dev file into rt2x00dev.c and rt2x00mac.c The latter of those 2 files is intended for the mac80211 callback functions. This makes the contents of both files clearer and makes moving new functions into rt2x00dev.c easier. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 5968b79b5976e7bb875666445d32cf726ff48492 Author: Ivo van Doorn Date: Thu Mar 8 22:14:46 2007 +0100 [PATCH] rt2x00: multiread/multiwrite should accept void* This patch will make rt2x00_register_multiread/multiwrite accept void* pointers. This could prevent possible byte ordering issues during unneeded casts. Since we are at it anyway also cleanup those casts. ;) Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit aba4f739c27ee6ee7c2860504ed088ca61936156 Author: Johannes Berg Date: Thu Mar 8 02:19:02 2007 +0100 [PATCH] make netdev unregister notifier idempotent Due to the fact that other things may hold a netdev open and the kernel retries to call all notifiers again and again until the netdev's refcount reaches 0, the notifier must be idempotent. This patch achieves that by re-initialising the list and checking whether it is empty. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit 87680a9df15f7c13798b2d526985ddf7527b95a9 Author: Daniel Drake Date: Sun Mar 11 19:26:51 2007 +0000 [PATCH] zd1211rw-mac80211: More device IDs ASUS A9Rp Tested by Serge zd1211b chip 0b05:171b v4802 high 00-17-31 AL2230_RF pa0 g-- ZyXEL G-202 Tested by Marcus D. Hanwell zd1211b chip 0586:3410 v4810 high 00-13-49 AL2230_RF pa0 g--- US Robotics USR805423 Tested by Pascal S. de Kloe FCC ID: RAXWN4501H zd1211b chip 0baf:0121 v4810 high 00-14-c1 AL2230_RF pa0 g--N Julien Pinon reports this also comes in AL2230S form Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit 7f83973b981ab34f15d444e17aa5aaeadcc51d1a Author: Daniel Drake Date: Sun Mar 11 19:20:33 2007 +0000 [PATCH] zd1211rw-mac80211: Add AL2230S RF support ZD1211 appears to be back in production: a number of new devices have been appearing! Some of them are using new radios. This patch adds support for the next generation AL2230 RF chip which has been spotted in a few new devices. Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit e02c6cad09449bb876b08046d1b09e85410afd58 Author: Pavel Roskin Date: Sun Mar 11 19:19:48 2007 +0000 [PATCH] zd1211rw-mac80211: sparse-annotate radiotap header All fields in radiotap header must be little endian. The driver does it correctly, but the structure definition must be annotated. Signed-off-by: Pavel Roskin Signed-off-by: Ulrich Kunitz Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit 71e2a35a9d79914e6218e451395324253892aa31 Author: Ulrich Kunitz Date: Sun Mar 11 19:19:31 2007 +0000 [PATCH] zd1211rw-mac80211, bcm43xx: changed Kconfig Changed the reference to Devicescape stack to mac80211. Signed-off-by: Ulrich Kunitz Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit 10d56a1ca4f4cacd372314ea7ef037dc09b55059 Author: Daniel Drake Date: Sun Mar 11 19:18:57 2007 +0000 [PATCH] zd1211rw-mac80211: Use compare_ether_addr() Suggested by Maxime Austruy, based on mac80211 changes from Stephen Hemminger Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit f075f80c304d5f3d10b5815be5800f2da62bbf2e Author: Michael Buesch Date: Thu Mar 15 16:05:42 2007 +0100 bcm43xx-mac80211: Select SSB_DEBUG if bcm43xx debugging is enabled. Signed-off-by: Michael Buesch commit 0b928d1359f7a64b90c8141870bb498e842ea0ff Author: Michael Buesch Date: Thu Mar 8 01:11:29 2007 +0100 bcm43xx-mac80211: More LO fixes. Signed-off-by: Michael Buesch commit 87918132ca054ae89c9d2e24196a520e2f06dbdb Author: Michael Buesch Date: Thu Mar 8 00:33:26 2007 +0100 bcm43xx-mac80211: Rewrite of the LO feedthrough measuring statemachine. There are still some issues left. All marked with FIXME. Signed-off-by: Michael Buesch commit 08531ff130bcc8181d9294a66e25010f48eefb97 Author: Michael Buesch Date: Wed Mar 7 23:01:08 2007 +0100 ssb: Don't freeze unregistered devices. Signed-off-by: Michael Buesch commit 267e8b1c890379016c70f3c81d3b99c8c617d193 Author: Michael Buesch Date: Wed Mar 7 22:45:07 2007 +0100 ssb: Move and rename some files. Directory structure should be _much_ more sane and maintainable now. Signed-off-by: Michael Buesch commit b87f743f3643ea162892dce0bbc91e6f026a49bf Author: Michael Buesch Date: Wed Mar 7 22:32:45 2007 +0100 ssb-ohci: Completely disable the driver on non-embedded systems Signed-off-by: Michael Buesch commit 9c5cc254335289ac4e353f9b7faaf6e5ba9697c1 Author: Michael Wu Date: Wed Mar 7 13:05:16 2007 -0500 p54: Suppress warning in prism54common.c This eliminates a warning in p54_assign_address in prism54common.c. Signed-off-by: Michael Wu commit 1ece15cf80f3ba680319846911bf1f85a81b932b Author: Michael Wu Date: Wed Mar 7 13:01:36 2007 -0500 p54usb: fix warnings on 64bit arch This eliminates the warnings in the p54 USB driver when compiling for a 64 bit arch. Signed-off-by: Michael Wu commit d473bbc2a992ac34d24e5c31b553aeb6ff690872 Author: Michael Wu Date: Wed Mar 7 12:57:04 2007 -0500 rtl8187: Select EEPROM_93CX6 rtl8187 uses the eeprom_93cx6 library, so select it. Signed-off-by: Michael Wu commit 7b0e1790c433293c772efcc33d304b2d5da87dbf Author: Andrea Merello Date: Wed Mar 7 12:56:09 2007 -0500 rtl8187: check IEEE80211_CONF_SHORT_SLOT_TIME This makes rtl8187 check IEEE80211_CONF_SHORT_SLOT_TIME when setting slot time instead of MODE_IEEE80211G. Signed-off-by: Michael Wu commit 74a5248c2b70f39a9d0a36b20f8b003cf808e9b5 Author: Andrea Merello Date: Wed Mar 7 12:55:37 2007 -0500 rtl8187: Minimize power consumption when down This turns off various parts of the card when it is down. Signed-off-by: Michael Wu commit 3eabe34ece7e4a9ccfa162c0ef6570cb3b3ef7db Author: Michael Wu Date: Wed Mar 7 12:50:55 2007 -0500 adm8211, p54: select CRC32 adm8211 and p54 use crc32, so select it. Signed-off-by: Michael Wu commit 481941f40c49819eaf60939fc9356426b36c5116 Author: Helge Deller Date: Mon Mar 5 17:06:36 2007 +0100 [PATCH] p54/rtl818x/zd1211rw - various fixes - use PCI_DEVICE macro - add some const / __read_mostly annotations - mark some arrays 'static' Signed-off-by: Helge Deller Signed-off-by: Michael Wu commit 4533da881f2d8c3e0dbb5b3dbc7a919e12438a8a Author: Andrew Morton Date: Wed Mar 7 01:21:35 2007 -0800 [PATCH] b44 warning fixes drivers/net/b44.c: In function 'b44_tx': drivers/net/b44.c:622: warning: passing argument 1 of 'dma_unmap_single' from incompatible pointer type drivers/net/b44.c: In function 'b44_alloc_rx_skb': drivers/net/b44.c:669: warning: passing argument 1 of 'dma_unmap_single' from incompatible pointer type drivers/net/b44.c: In function 'b44_start_xmit': drivers/net/b44.c:991: warning: passing argument 1 of 'dma_unmap_single' from incompatible pointer type drivers/net/b44.c:1003: warning: passing argument 1 of 'dma_unmap_single' from incompatible pointer type drivers/net/b44.c: In function 'b44_free_rings': drivers/net/b44.c:1104: warning: passing argument 1 of 'dma_unmap_single' from incompatible pointer type drivers/net/b44.c:1118: warning: passing argument 1 of 'dma_unmap_single' from incompatible pointer type That's going to crash hard if anyone uses that first arg. Signed-off-by: Andrew Morton Signed-off-by: John W. Linville commit c817635776b7a21d64f26e7f30fa2a32594dd09e Author: Ivo van Doorn Date: Mon Mar 5 19:53:18 2007 +0100 [PATCH] rt2x00: Move rt2x00debug include file Now that we are working on rt2x00debug anyway, lets remove the #if statements about RT2X00_DEBUGFS from rt2x00.h and completely handle it in the drivers. This will remove the ugly define CONFIG_RT2X00_DEBUGFS hack And the rt2x00_dev structure is only increased slightly because of the void* pointer. The rt2x00_debugfs.h needs to be included inside the drivers. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 17435202e87ef83de9d6f47042c12f3f36247e74 Author: Ivo van Doorn Date: Mon Mar 5 19:20:24 2007 +0100 [PATCH] rt2x00: Make rt2x00debug structure interface independent The rt2x00debug structure was 90% interface independent, this patch will remove the last interface dependent fields which allows us to make the rt2x00debug structure a static field inside each rt2x00 driver. This patch also moves the rt2x00 debugfs files into a seperate folder _within_ the debugfs dentry provided by the wiphy structure. This will prevent rt2x00 dentry file names to accidently interfere with dentry files from others (i.e. mac80211). This patch will make moving rt2x00debug into the rt2x00lib structure easier, but that will be handled in a different patch later. ;) Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 9b7b55239641fc95b0f9bfd1abeceb299a3e40b5 Author: Johannes Berg Date: Mon Mar 5 17:41:58 2007 +0100 [PATCH] make ieee80211_get_hdr_info static This patch makes the ieee80211_get_hdr_info function which is only used in wpa.c static. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit 75ff40964e1ed1fb034d550b9c48d85ca7386f05 Author: Johannes Berg Date: Mon Mar 5 17:37:53 2007 +0100 [PATCH] mac80211: remove some dead header junk This patch removes a bunch of things from various header files that aren't used within the stack (any more) Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit 7a5086ca1b87eea19bb2c162e008da16ceadb882 Author: Luis Correia Date: Sat Mar 3 21:11:41 2007 +0000 [PATCH] CREDITS: remove my full address Remove address from CREDITS. Signed-off-by: Luis Correia Signed-off-by: John W. Linville commit db8f8a1f091aa78b99c401426482d946813ad3bb Author: Ivo van Doorn Date: Sat Mar 3 18:55:06 2007 +0100 [PATCH] rt2x00: rename rt2x00lib.c It is best to rename rt2x00lib.c now, because we want to link rt2x00debug.c and several other objects to it soon. Patches for creating those new objects, which will link to rt2x00lib as well will arrive soon. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 1a9e0dd0bd60474465e0b0f1bca774d8c042d879 Author: Johannes Berg Date: Sat Mar 3 13:06:15 2007 +0100 [PATCH] rework wireless Kconfig This patch * kills NET_RADIO * adds a new "Wireless LAN" menu * adds two new options WLAN_PRE80211 and WLAN_80211 that drivers depend on * makes WIRELESS_EXT visible (to avoid the arguments we had in commit c1783454a31e05b94774951b0b5d1eb9075ebfb4) * changes everything that depended on NET_RADIO to select WIRELESS_EXT and to depend on WLAN_PRE80211 or WLAN_80211 By removing NET_RADIO, these changes pave the way to making wireless extensions optional when cfg80211 can fully take over for some drivers and you don't have any older drivers that still require wext. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit 21c45101a7a6ca6af7dce1497aacfd04ab72d9ec Author: Johannes Berg Date: Fri Mar 2 14:29:10 2007 +0100 [PATCH] add privid field to wiphy While working on more sysfs removal (moving all the per-netdev stuff as well) I noticed that we have no easy way to tell, given a netdev, whether it belongs to a wiphy mac80211 controls or maybe somebody else. This patch adds a void *privid field to the wiphy that you can assign to something global in the driver/stack module to find out whether the wiphy belongs to you or not when given a netdev with ieee80211_ptr, a wireless dev or a wiphy by some other code. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit e54b33f976d9f486a6b7ad5fef3cc4cc3f7105c9 Author: Johannes Berg Date: Fri Mar 2 11:38:28 2007 +0100 [PATCH] mac80211: fix s390 allmodconfig breakage Yeah, it is. s390 is funny, it doesn't include drivers/Kconfig, I don't think anybody of us would have suspected that. ... I can offer below patch to fix the LED trigger problem, it's probably cleaner to depend on LEDS_TRIGGERS rather than selecting it and NEW_LEDS. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit d5126682a2a4f0703a48d739331119331b60db28 Author: Michael Buesch Date: Tue Mar 6 17:27:52 2007 +0100 bcm43xx-mac80211: b5init: fix misinterpretation from the rev-eng group. Signed-off-by: Michael Buesch commit a0e5d0815d710ed70a6f55b549f3de1c2a9684c2 Author: Michael Buesch Date: Tue Mar 6 15:42:18 2007 +0100 bcm43xx-mac80211: Use tasklet_kill() instead of tasklet_disable() for syncing, as tasklet_kill() also ensures the the tasklet is not scheduled anymore. Signed-off-by: Michael Buesch commit 034cca791d8e1389804117d627d6f6e971856e86 Author: Michael Buesch Date: Mon Mar 5 22:27:28 2007 +0100 ssb-ohci: Proper start/reset and suspend/resume routines. Signed-off-by: Michael Buesch commit 77de1ae54d0d9d4b72fc650a6534e11da008a8b9 Author: Michael Buesch Date: Mon Mar 5 21:54:44 2007 +0100 ssb: Don't poke with dev->drvdata in SSB to avoid a conflict with USB OHCI. Signed-off-by: Michael Buesch commit 2026940b150d66dd3ebce2ef3e052e94cea08f81 Author: Michael Buesch Date: Mon Mar 5 20:56:26 2007 +0100 ssb: Fix compile of OHCI core driver. Signed-off-by: Michael Buesch commit cd21e3dc9490d1e6525cab4da29697fd9d6e5ef2 Author: Michael Buesch Date: Mon Mar 5 14:44:04 2007 +0100 Subject: [PATCH] bcm43xx-mac80211: Put the PCI stub into main.c It's not worth to have its own file for a few lines of code anymore, as almost all of the stub was moved to ssb. Signed-off-by: Michael Buesch commit 14488910d0c37b9fc386f87feddb5f3ccf9cd7ff Author: Michael Buesch Date: Mon Mar 5 14:42:14 2007 +0100 bcm43xx-mac80211: bcm43xx-pcmcia must depend on PCMCIA Signed-off-by: Michael Buesch commit 15dff851aa928159722dd9ea8e71f85ee4199993 Author: Michael Buesch Date: Mon Mar 5 14:36:10 2007 +0100 bcm43xx-mac80211: Don't free beacon_control, as it's not allocated dynamically. Signed-off-by: Michael Buesch commit 44247de93b39483229c82f93b92e666be60cadfd Author: Michael Buesch Date: Mon Mar 5 14:34:50 2007 +0100 bcm43xx-mac80211: Flip radio_ver conditional in b5 init. Signed-off-by: Michael Buesch commit 87b2b149c09326334e103dbf3f079199e8a3d6d6 Author: Michael Buesch Date: Mon Mar 5 14:33:17 2007 +0100 bcm43xx-mac80211: Ignore any IRQ that's not for us. Signed-off-by: Michael Buesch commit 83a57668820eaad5361bf3c6bcb77eb67a45ee1a Author: Michael Buesch Date: Mon Mar 5 14:28:40 2007 +0100 ssb: Save lots of memory by shrinking struct ssb_device. shrink struct ssb_device by a huge amount and don't register ssb-system-devices to the kernel. Signed-off-by: Michael Buesch commit 8a80554868289869bc421fc7a5788abbc98cecec Author: Michael Buesch Date: Mon Mar 5 14:25:24 2007 +0100 bcm43xx-mac80211: fix a sparse warning This fixes a missing prototype sparse complains about. Signed-off-by: Johannes Berg Signed-off-by: Michael Buesch commit 96f93b6fbd8b2cd454ede3a9b75bbd98415164d8 Author: Ivo van Doorn Date: Wed Feb 28 23:08:36 2007 +0100 [PATCH] rt2x00: fix NULL pointer exception This will fix a NULL pointer exception when working in master mode. When a master mode interface is added, the bssid is initialized, when config_interface() is called the bssid is invalid (only with master mode) so we cannot use that, instead the bssid inside the interface structure should be used. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit d80fe3157e05b61d6555f82241169bd4ba6bcc23 Author: Ivo van Doorn Date: Wed Feb 28 19:26:38 2007 +0100 [PATCH] rt2x00: split RT2X00 option This patch will split the RT2X00 config option and adds the (for the user) invisible RT2X00_LIB config option that will be selected by the drivers. Make the debug(fs) config options depend on this new config option. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit b0b497da0ab0dab2c77a112e67c51e6a468ca87b Author: Ivo van Doorn Date: Wed Feb 28 15:07:05 2007 +0100 [PATCH] rt2x00: Misc fixes Misc fixes: - sparse fixes - register initialization - device uninitialization - txpower updating Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 93afa59694872674c2c50eb98f8973fc0db99474 Author: Ivo van Doorn Date: Wed Feb 28 15:07:06 2007 +0100 [PATCH] rt2x00: Include cleanup Cleanup includes a bit. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit a9b6cf931bf8ff772b5d75b9a950ec3b10bd9763 Author: Ivo van Doorn Date: Wed Feb 28 15:07:06 2007 +0100 [PATCH] rt2x00: Fix rt2500usb linktuning Use EEPROM information to correctly work with the link tuning for rt2500usb. This improves link stability/quality. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 4ac180139758424a280c0e9f9b28527e27606a86 Author: Ivo van Doorn Date: Wed Feb 28 15:07:07 2007 +0100 [PATCH] rt2x00: rt2x00.h cleanup Cleanup some old code that was replaced by rt2x00lib, or isn't required anymore. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 1a213e95c772e2b28a2ed21c29cf33a7770d1fe7 Author: Ivo van Doorn Date: Wed Feb 28 15:07:07 2007 +0100 [PATCH] rt2x00: Spellcheck for some comments. Spellcheck for some comments. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit d35a4bf6c95086b852e43bc36bd81e08684dae39 Author: Ivo van Doorn Date: Wed Feb 28 15:07:08 2007 +0100 [PATCH] rt2x00: Cleanup ring entry handlers Cleanup access to the data and descriptor fields inside a ring entry. PCI devices can use the direct pointers instead of the inlined functions. Also move the generic functions for eeprom/descriptor access to rt2x00.h Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 56312248579340749eb57aee85ca55dd62bae165 Author: Ivo van Doorn Date: Wed Feb 28 15:07:08 2007 +0100 [PATCH] rt2x00: Fix big endian problems for the pci drivers Fix big endian problems for the pci drivers. Register access does not need to be byteordered. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit b008ad943e834e2ace51eb3a88e83f66f5364919 Author: Ivo van Doorn Date: Wed Feb 28 15:07:08 2007 +0100 [PATCH] rt2x00: Fix TSF_SYNC selection Rename TSF_SYNC_MODE to TSF_SYNC. Also set the value correctly to allow master mode functionality. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 013864d16bd42209466a1773e5baec3b019c2443 Author: Ivo van Doorn Date: Wed Feb 28 15:07:09 2007 +0100 [PATCH] rt2x00: Move channel time detection to rt2x00lib Use the rt2x00lib provided function for detecting channel time. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit c6b87dcf876ca63b5d443a8dea7c4805cb61d3da Author: Ivo van Doorn Date: Wed Feb 28 15:07:09 2007 +0100 [PATCH] rt2x00: Code cleanup after rt2x00lib Start using rt2x00lib, this removes quite a lot of code. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 51207d809542dabf2bbe24641a59b76a9b143c59 Author: Ivo van Doorn Date: Wed Feb 28 15:07:09 2007 +0100 [PATCH] rt2x00: Move firmware handling to rt2x00lib Move firmware handling to rt2x00lib, andmake sure that device initialization (debugfs etc) waits until the firmware is loaded. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 6684ef6a4b574d12f4ed11527c21fc3712f7db78 Author: Ivo van Doorn Date: Wed Feb 28 15:07:10 2007 +0100 [PATCH] rt2x00: Make USB work in interrupt context USB devices will no longer schedule their interrupt tasks to a later time. Instead the interrupt handlers will be called immediately when an interrupt is being raised. For PCI devices this is only done for beacondone handling, (although the other interrupts are also under consideration...). Also the interrupt handlers should make use of rt2x00lib for the statistics updating. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit b8783dcd77e8bec7450b479839528ace10b3e45d Author: Ivo van Doorn Date: Wed Feb 28 15:07:10 2007 +0100 [PATCH] rt2x00: Fix hwmode selection Store the current selected mode, and use that whenever for selecting the modes during later configuration steps. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit b90e4adfd0b3f4a27e3919af6ecc6328fb3e729a Author: Ivo van Doorn Date: Wed Feb 28 15:07:10 2007 +0100 [PATCH] rt2x00: Initialize the rt2x00lib_ops structure Initialize the rt2x00lib_ops structure, and change functions that don't follow the definition in that structure. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 19c823a6c7df480624b010ed58182471e3d260b5 Author: Ivo van Doorn Date: Wed Feb 28 15:07:12 2007 +0100 [PATCH] rt2x00: Create rt2x00lib module Create rt2x00lib module, this module contains all generic code that is shared between the individual drivers. The following patches will start using the code provided by rt2x00lib. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 0129f05f4dea6a393e48af1270fba912b0742fa7 Author: Ivo van Doorn Date: Wed Feb 28 15:07:13 2007 +0100 [PATCH] rt2x00: Fix txdone flood in rt61pci Prevent rt61pci txdone register to become flooded during under stress, immediately read the register and mark the reported entries as done. This way the txdone handler only has to search for these particular entries. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 25627583efb299748576ecd42a43ef240b1cbfb9 Author: Ivo van Doorn Date: Wed Feb 28 15:07:15 2007 +0100 [PATCH] rt2x00: USB don't need preallocated DMA Instead of using a preallocated DMA buffer for USB devices, it is sufficient to provide sk_buffs to it and let the USB layer handle the rest. This makes the USB transfers a bit simpler for us. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 8c8d034d54177cfa8c184b8b1a997ae5a1425c17 Author: Ivo van Doorn Date: Wed Feb 28 15:07:16 2007 +0100 [PATCH] rt2x00: Add guardian byte for beacons USB devices need a second entry in the beacon ring, the second entry is intended for the beacon data, but the first is a guardian byte that needs to be send first. Without that, there will be no beacon generation. And after the beacon generation has started, there is no need to update the beacon anymore the device will handle everything. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 87d6cc12dfa285ad90e950c63cf45b68516aa1ac Author: Ivo van Doorn Date: Wed Feb 28 15:07:16 2007 +0100 [PATCH] rt2x00: Create write_tx_data and kick_tx_queue functions Create write_tx_data and kick_tx_queue functions. This will make the tx() function itself more generic which is something we need when we are going to add the rt2x00lib module. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 3d12cfb7a13301dae2c217309ac99ea8765835ad Author: Ivo van Doorn Date: Wed Feb 28 15:07:11 2007 +0100 [PATCH] rt2x00: Create toggle_rx function Create toggle_rx function to simplify enabling/disabling the RX of the device. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 78f6ed831df22d92d4e51413be9c60bd9cd9ce3f Author: Ivo van Doorn Date: Wed Feb 28 15:07:11 2007 +0100 [PATCH] rt2x00: optimize mac/bssid writing Handling the mac and bssid configuration can be done much easier by writing the passed data directly into the register instead of moving it to a local variable first. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 5ac370dd0557ce967eb5428e9b98ae7b0f0267f1 Author: Ivo van Doorn Date: Wed Feb 28 15:07:17 2007 +0100 [PATCH] rt2x00: Don't pass a skb directly with write_tx_desc Don't pass a skb directly with write_tx_desc. Instead send a pointer to the ieee80211hdr and the frame length. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit cafa7c0575223e6948ed85c3049275f7f6c88096 Author: Ivo van Doorn Date: Wed Feb 28 15:07:14 2007 +0100 [PATCH] rt2x00: define MAX_RX_NOISE Move max_noise value into a MAX_NOISE define. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 23242d282e5087eedeccf5b01d46a6cace8b15c1 Author: Ivo van Doorn Date: Wed Feb 28 15:07:15 2007 +0100 [PATCH] rt2x00: New USB ID's Add new USB ID's for rt73usb Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit a03161ed474f17c1dab76c4193d0c0fe14309ab4 Author: Ivo van Doorn Date: Wed Feb 28 15:07:11 2007 +0100 [PATCH] rt2x00: Rename rx_params to rx_status Rename rx_params to rx_status to reflect the actual purpose of this field. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 059e6ff7bf01275115d3a68a81d1adf0d1bd541d Author: Ivo van Doorn Date: Wed Feb 28 15:07:12 2007 +0100 [PATCH] rt2x00: Make debug option rt2x00-global Remove per-driver debug option, and replace it with a rt2x00 global debug option. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 785affe5db720d2f582d846b979dd4b005143c4a Author: Ivo van Doorn Date: Wed Feb 28 18:16:52 2007 +0100 [PATCH] rt2x00: Add debugfs support This patch will add debugfs support to rt2x00. It will add these files into the debugfs directory that has been created by the wiphy handler. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit e75ca7d9f591297190a991859d570fdb0838739a Author: Ivo van Doorn Date: Wed Feb 28 15:07:14 2007 +0100 [PATCH] rt2x00: Update copyright Update year inside the copyright notice. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 2c6cea456725655a2d94fbca97a2dacbc00839e4 Author: Johannes Berg Date: Wed Feb 28 15:10:01 2007 +0100 [PATCH] fix cfg80211 deadlock My patch titled [PATCH] make cfg80211 manage wiphy netdev list introduced a deadlock in cfg80211 when adding or removing interfaces. The deadlock happens because I used the same mutex for making sure that no two calls are in progress as for managing the list. This patch fixes it by using a separate mutex for the list. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit 8c3fac0dca9d6e996d39360a74818ed31775f1d7 Author: Johannes Berg Date: Wed Feb 28 00:18:17 2007 +0100 [PATCH] make cfg80211 manage wiphy netdev list This patch makes cfg80211 handle the list of netdevices associated with a wiphy. It does this by watching NETDEV_REGISTER/UNREGISTER events. One effect of this patch is that cfg80211 users no longer need to provide a list_devices hook. Another effect is that all cfg80211 users will get a "phy80211" symlink in all their netdevices pointing to the 802.11 phy this netdev is associated to, thereby standardising this. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit c26a45d627dbe17ddc3f66005054ad5086a9b8d6 Author: John W. Linville Date: Tue Feb 27 09:37:56 2007 -0500 [PATCH] rename 80211.ko to mac80211.ko More naming cleanup, as suggested by Jiri Benc . Signed-off-by: John W. Linville commit e0bf9f16a7991869809e09a76fa802de6d7b8474 Author: Johannes Berg Date: Tue Feb 27 12:08:01 2007 +0100 [PATCH] mac80211: fix more whitespace damage This replaces all occurrences of 8 spaces with a tab. Without doubt we'll find more problems, but this at least lets us edit the files sanely again. Signed-off-by: Johannes Berg Acked-by: Jiri Benc Signed-off-by: John W. Linville commit 3abd6195d3363a28cc51cfba79d84f30b2aed29c Author: Johannes Berg Date: Tue Feb 27 00:12:34 2007 +0100 [PATCH] bcm43xx-mac80211: fix a sparse warning This fixes a missing prototype sparse complains about. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit 8f485020c96b908abc5cef7f3d779ffff4df0f2c Author: Johannes Berg Date: Mon Feb 26 23:59:19 2007 +0100 [PATCH] mac80211: fix sparse warnings This fixes some sparse warnings in mac80211. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit b6fd6b633feeef1dcd59c30ca065543c4894dbf5 Author: Johannes Berg Date: Mon Feb 26 23:46:56 2007 +0100 [PATCH] net/wireless: fix sparse warnings This fixes a few things sparse pointed out when I finally got it working. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit 7b80a550299de2744b4fd08bf622f8b4bf19b6f3 Author: John W. Linville Date: Mon Feb 26 20:54:15 2007 -0500 [PATCH] wireless: more whitespace cleanups Signed-off-by: John W. Linville commit cedc850b61d1540c3b5241c7e2646646737c8b9a Author: John W. Linville Date: Mon Feb 26 16:42:56 2007 -0500 [PATCH] wireless: rename d80211 -> mac80211 The name mac80211 better reflects the component's role. Also, fix a bunch of whitespace errors in the process... Signed-off-by: John W. Linville commit 6614b5b4986cc4fe4b745d123f87db826ba9b6f3 Author: Michael Wu Date: Sun Feb 25 23:33:36 2007 -0500 rtl818x: Add RTL8187 driver This is a d80211 based driver for the RTL8187 wireless adapter. It still needs work - proper TX status reporting hasn't been implemented yet, it has issues with rate control on some routers, and LEDs don't blink, but it works. Thanks to Andrea Merello for his help with this driver, and another thanks to Realtek for supporting Linux! Signed-off-by: Michael Wu commit 4df38370643dd355c6209caf708356763093eef7 Author: Michael Wu Date: Sun Feb 25 22:47:56 2007 -0500 zd1211rw-d80211: remove requested_channel requested_channel in struct zd_mac is not needed. d80211 now immediately configures the channel after the device goes up so the channel setting is not required at open. Signed-off-by: Michael Wu commit 9e87186b4c95e8cb15b166a6381b25dc786749eb Author: Michael Wu Date: Sun Feb 25 22:41:06 2007 -0500 zd1211rw-d80211: fix zd_mac_open error path zd_mac_open should not immediately return if zd_write_mac_addr fails. Signed-off-by: Michael Wu commit 2f1c85975cdf202f04c4b298eb74fb1940a5d39a Author: Christian Lamparter Date: Sun Feb 25 22:32:26 2007 -0500 p54: fix reloading issues with (mini-)pci adapters This patch fixes the long-standing reloading issue of softmac prism54 pci adapters. Signed-off-by: Christian Lamparter Signed-off-by: Michael Wu commit 8b090b2c788b0c83cd257fe0238a6e1c0de2ee20 Author: John W. Linville Date: Thu Feb 22 19:33:58 2007 -0500 [PATCH] b44: remove references to pci_unmap_addr API The port to the SSB bus changed b44 from using the PCI DMA API to using the low-level DMA API. So, use of pci_unmap_addr no longer seems appropriate. Signed-off-by: John W. Linville commit 47bd977bbd5fae89f33d64aa41dfe0cce7c97d8f Author: Pavel Roskin Date: Fri Feb 23 20:40:26 2007 -0500 [PATCH] d80211: make keyidx signed to suppress sparse message sparse reports: error: dubious bitfield without explicit `signed' or `unsigned' keyidx should be signed for consistency with definitions in d80211.h, and it doesn't need to be a bitfield - s8 would do the right thing. Reorder the fields to keep the one-bit flags together. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit b42e6c05a44fb9e3d00c4b2e1776d5c108d7ae3c Author: James Ketrenos Date: Fri Feb 23 14:52:30 2007 -0800 [PATCH] wireless: make building nl80211 optional Move the wireless Kconfig options into their own wireless/Kconfig and add a CONFIG_NL80211 configuration option to allow nl80211 support to be optionally included (default =y) This also implements stub functions for nl80211_init and nl80211_exit when CONFIG_NL80211 is not defined. Acked-by: Johannes Berg Signed-off-by: James Ketrenos Signed-off-by: John W. Linville commit c77f2b91a9512ee974a3cdf69f1870f45574e944 Author: Michael Wu Date: Sun Feb 25 21:24:15 2007 -0500 adm8211, p54, zd1211rw-d80211: remove skb_headroom check d80211 now reserves extra_tx_headroom for all frames passed to drivers so these checks can be removed. Signed-off-by: Michael Wu commit 43839d60fbef458f6aa5abe256a1576b42d7f20f Author: Johannes Berg Date: Fri Feb 23 15:59:48 2007 +0100 [PATCH] wireless: convert wiphy to struct device Ok so I did the wiphy stuff w/o knowing that class devs are apparently deprecated. Below is a patch to convert it. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit eb55c46c050dca83af06b9f7b91f2a517c76354d Author: Ivo van Doorn Date: Wed Feb 21 17:02:47 2007 +0100 [PATCH] rt2x00: Split antenna selection into TX and RX antenna With the split antenna selection rt2x00 can now read the default value for both selections from the EEPROM and can now configure the card correctly based on the individual RX and TX antenna selections. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 84c5ec1e2e7d7c5cee320c5adf60f58691108bea Author: Ivo van Doorn Date: Wed Feb 21 17:02:47 2007 +0100 [PATCH] bcm43xx-d80211: Split antenna selection into TX and RX antenna Let broadcom use the antenna_tx field for hardware configuration. I did not check if the device is also capable of RX antenna selection, but current implementation only suggested support for the TX antenna. Signed-off-by: Ivo van Doorn Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 06301e47e138810c0f3ccf6cb34277671654260d Author: Michael Buesch Date: Sun Feb 25 19:29:01 2007 +0100 bcm43xx-d80211: Fix incorrect has_loopback_gain condition. Signed-off-by: Michael Buesch commit 930ddde84cefb949fb837f2261cacce4b525e330 Author: Michael Buesch Date: Sun Feb 25 19:20:10 2007 +0100 bcm43xx-d80211: Fix 2050 radio init. Signed-off-by: Michael Buesch commit 1fed73172c25ba6987056070df255f7673309c82 Author: Michael Buesch Date: Fri Feb 23 23:37:31 2007 +0100 ssb: Memory usage and speed optimizations. Signed-off-by: Michael Buesch commit 6a59dd2cfe8d38d6622ce70ea0d00d82c20be50a Author: Michael Buesch Date: Fri Feb 23 23:10:24 2007 +0100 bcm43xx-d80211: Use generic dev_get_drvdata. Saves memory (one pointer). Signed-off-by: Michael Buesch commit 8d83ff9bda4ffa6c8a8653c4493958b58b8b8495 Author: Stephen Hemminger Date: Fri Feb 23 17:35:13 2007 +0100 [PATCH] d80211: optimise if_alloc() name creation. The code to do guess and check for creating a sub-device name is unnecessary. dev_alloc_name() is optimized to do this already. Signed-off-by: Stephen Hemminger Signed-off-by: Jiri Benc commit 44da2d9d2ca269d17bf62593ea189c7b456a0eb9 Author: Stephen Hemminger Date: Fri Feb 23 17:35:13 2007 +0100 [PATCH] d80211: convert to use compare_ether_addr compare_ether_addr is faster than memcmp() Signed-off-by: Stephen Hemminger Signed-off-by: Jiri Benc commit 8c840e7902611f0eea61a9c0c3217c13a53a0787 Author: Stephen Hemminger Date: Fri Feb 23 17:35:12 2007 +0100 [PATCH] d80211: use const Use const to indicate constant arguments and encapsulation headers. Signed-off-by: Stephen Hemminger Signed-off-by: Jiri Benc commit befa8ba120579fb1dd6643e84d33fbc6ab2dfc84 Author: Ivo van Doorn Date: Fri Feb 23 17:35:12 2007 +0100 [PATCH] d80211: Split antenna selection into TX and RX antenna Currently d80211 only uses a single variable for antenna selection, some devices (rt2x00) can configure the TX and RX antenna seperately from eachother. Assuming that antenna_sel is only for tx, and rx is "the other antenna" is flawed and does hinder possible switching of RX antenna based on RSSI results. And configuring the wrong antenna also impacts transfer rates and link quality. This patch will remove the usage of the IOCTL call: PRISM2_PARAM_ANTENNA_SEL but will restore the usage of (the already excisting) IOCTL calls PRISM2_PARAM_ANTSEL_TX and PRISM2_PARAM_ANTSEL_RX. Signed-off-by: Ivo van Doorn Signed-off-by: Jiri Benc commit 58a02760422d8fd65e5a426a073fd5c1bc9fd4d7 Author: Michael Buesch Date: Thu Feb 22 17:45:46 2007 +0100 bcm43xx-d80211: Remove UCODEFLAGS stuff. Signed-off-by: Michael Buesch commit 51d99b129de7c0251f0c06021dd54be09a9e030c Author: Michael Buesch Date: Thu Feb 22 17:17:17 2007 +0100 bcm43xx-d80211: Fix a few PHY register writes to match recent specs. Signed-off-by: Michael Buesch commit db1eee24ed04ea33587f899b00dde9be8cd2e7e1 Author: Johannes Berg Date: Thu Feb 22 13:36:17 2007 +0100 [PATCH] hook up nl80211 again Apparently when doing all the cfg80211 changes I accidentally removed the nl80211 registration. This patch adds it back. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit 06fb408a0193c1d7d067de5a05d57bdec4e1ce43 Author: John W. Linville Date: Tue Feb 20 19:58:11 2007 -0500 [PATCH] d80211: fix build break from netdev class_device -> device changes Fix-up the build breakages in d80211 caused by commit 43cb76d91ee85f579a69d42bc8efc08bac560278. Signed-off-by: John W. Linville commit 17e0264113fcf465d0712ce492c6a546d207d786 Author: Michael Wu Date: Tue Feb 20 18:13:54 2007 -0500 [PATCH] d80211: Fix ieee80211_ptr check d80211: Fix ieee80211_ptr check dev->ieee80211_ptr can no longer be directly compared to check if a virtual interface belongs to a master device. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit 8e6a51df7a9e82cb2928c8aa8a592a2f68451bea Author: Johannes Berg Date: Tue Feb 20 22:14:16 2007 +0100 [PATCH] add add_iface/remove_iface back to sysfs This patch adds the wiphy add_iface/remove_iface sysfs attributes back into sysfs. However, they are implemented on top of cfg80211 now and nl80211 userspace should work as well. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit c3cb470085c476ef711ce4e20849606a7047ecb1 Author: Michael Buesch Date: Tue Feb 20 21:02:14 2007 +0100 [PATCH] rt2x00: Fix max_rssi values signedness bug char is not signed on all architectures. This fixes a compiletime warning and bug on PPC. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 21856c5c3f630e3335dfef76e5a1efeca880fb82 Author: Michael Buesch Date: Tue Feb 20 20:45:01 2007 +0100 [PATCH] d80211: Fix max_signal values signedness bug char is not signed on all architectures. This fixes a compiletime warning and bug on PPC. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit dd7b21ff04ea2e53d4eb05f39b4d1578435193f1 Author: Pavel Roskin Date: Sun Feb 18 21:46:54 2007 -0500 [PATCH] rt2x00: fix memory corruption caused by eeprom buffer overflow eeprom_93cx6_multiread() expects the last argument to be the buffer length in words, but kzalloc() expects the length in bytes. This results in dangerous kernel memory corruption. Since there are already occurrences of "EEPROM_SIZE * sizeof(u16)" in the driver, I'm assuming that EEPROM_SIZE is in words, so the driver needs to allocate more memory. Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville commit 5de81e1b29a1498b183152dfb028b4b2aa7e6741 Author: Daniel Drake Date: Sun Feb 18 01:40:41 2007 +0000 [PATCH] zd1211rw-d80211: Add ID for ZyXEL ZyAIR G-220 v2 Tested by Marijn Schouten zd1211b chip 0586:340f v4810 high 00-13-49 AL2230_RF pa0 g--- FCC ID: I88G220V2 Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit c6424b1e12179cf45aec21c8349293156f18e98a Author: Ulrich Kunitz Date: Sun Feb 18 01:34:43 2007 +0000 [PATCH] zd1211rw-d80211: changed GFP_NOFS to GFP_KERNEL Michael Buesch commented that GFP_NOFS should not be used in a network driver. This patch implements it for zd1211rw-d80211. Signed-off-by: Ulrich Kunitz Signed-off-by: Daniel Drake Signed-off-by: John W. Linville commit 0076cca703b4277f8bcd01d96b198ce96d9c9fd6 Author: Johannes Berg Date: Sat Feb 17 01:47:32 2007 +0100 [PATCH] fix cfg80211 modular compile w/o wext-compat The static inlines that are supposed to be used w/o wext-compat to init wext-compat were done with the wrong #ifdef. This fixes it. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit a4ecf9d95722f65af15e493525ea5925a35d3cc3 Author: Michael Buesch Date: Tue Feb 20 16:01:10 2007 +0100 bcm43xx-d80211: Fix register write for !GPHY. Signed-off-by: Michael Buesch commit 4bc7a9b6efd1190e859878b521db74a2aa6b98a8 Author: John W. Linville Date: Tue Feb 20 09:58:18 2007 -0500 [PATCH] rt2x00: avoid warning introduced by maxssi -> max_rssi change The change from int maxssi to char max_rssi introduced a warning in a comparsion to the value 0xff. This value is read from the hardware and seems to indicate an error condition relating to reading an ssi value. The warning is side-stepped by a simple cast: (char)0xff. Signed-off-by: John W. Linville commit d61e122b6f5db0c240016dc772d7c38c1205e787 Author: Michael Buesch Date: Tue Feb 20 15:50:38 2007 +0100 bcm43xx-d80211: Set GMODE-MACCTL, if we are operating in GMODE. Signed-off-by: Michael Buesch commit e7699c44745f693b0c13bbdbe97cb574f3b2f1a6 Author: Michael Buesch Date: Tue Feb 20 15:46:15 2007 +0100 bcm43xx-d80211: Turn the Radio and Analog off on core-exit and attach-finish. Signed-off-by: Michael Buesch commit c369d2a696e67e2e9fbc3756443d06e160e909e0 Author: Michael Wu Date: Thu Feb 15 17:27:50 2007 -0500 [PATCH] rt2x00: update to new statistics reporting API rt2x00: update to new statistics reporting API This patch updates rt2x00 to the new statistics reporting API. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit 7cca78cdf8a16a9e47a5f5a9a088d18ea611871e Author: Michael Wu Date: Sun Feb 11 22:13:29 2007 -0500 [PATCH] bcm43xx-d80211: update to new statistics reporting API bcm43xx-d80211: update to new statistics reporting API This patch updates bcm43xx-d80211 to the new statistics reporting API. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit dcb64f974ab96edcc586df2f783384df13a15a75 Author: Michael Wu Date: Sun Feb 11 22:12:27 2007 -0500 [PATCH] adm8211, p54, zd1211rw-d80211: Update to new statistics reporting API adm8211, p54, zd1211rw-d80211: Update to new statistics reporting API This patch updates adm8211, p54, and zd1211rw-d80211 to the new statistics reporting API in d80211. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit 4293c37042da42cea187bb3f4a7846cde5c05767 Author: Johannes Berg Date: Sat Feb 17 02:03:57 2007 +0100 [PATCH] fix wext_ioctl() exit path This fixes the wext_ioctl() exit path, it was copying things from "&ifr" instead of "ifr", caused by now using a pointer to ifr in that new function. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit ae498e0b044d611d9d748c546e13ac07924c390e Author: Johannes Berg Date: Fri Feb 16 22:09:59 2007 +0100 [PATCH] fix wiphy_create/wiphy_free sequence Big bad oops when you did wiphy_create/wiphy_free without registering it inbetween. This should fix it. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit a28a60667bae9cea83ddd264c3862b19e40c6ecb Author: Michael Buesch Date: Tue Feb 20 12:33:26 2007 +0100 ssb: Take care of changed REJECT bit in new backplane revisions. Signed-off-by: Michael Buesch commit 36d7bfa99c55b8b59fa49d5d6a030a470cecaaa0 Author: Pavel Roskin Date: Mon Feb 19 23:34:13 2007 +0100 [PATCH] d80211: fix incorrect hw.priv setting in ieee80211_alloc_hw() hw.priv is set twice, and the second time it's set incorrectly to an area relative to the master device, which wasn't allocated for private data. Signed-off-by: Pavel Roskin Acked-by: Johannes Berg Signed-off-by: Jiri Benc commit cec21de5adc61353fe43e6b931674a5f3f85206c Author: Jiri Benc Date: Mon Feb 19 23:34:13 2007 +0100 [PATCH] d80211: more wiphy API fixes After resolving the conflict with John's tree, several things were still broken. This patch fixes that, together with fixing one related GCC warning. Signed-off-by: Jiri Benc commit 42b2c331d68dd667282fda5f8f3dcdfe190ead44 Author: Michael Buesch Date: Mon Feb 19 21:27:33 2007 +0100 bcm43xx-d80211: Add modparam to keep bad frames in monitor mode. Signed-off-by: Michael Buesch commit 27cfdcaec0ff523ce5a09915f76e36348753c9e2 Author: Ivo van Doorn Date: Mon Feb 19 21:00:57 2007 +0100 [PATCH] d80211: Fix skb panic during passive scan Only add the extra_tx_headroom to the len when allocating the sk_buff. This will prevent using an invalid length for skb_put which would cause a skb panic inside the driver. Signed-off-by: Ivo van Doorn Signed-off-by: Jiri Benc commit 2746f09e163ca7d80bbb1ec0419bd47c6b31ec28 Author: Johannes Berg Date: Mon Feb 19 21:00:57 2007 +0100 [PATCH] d80211: remove IEEE80211_HW_FRAGLIST flag This patch removes the IEEE80211_HW_FRAGLIST flag as it is neither used nor makes sense (since we never submit fragmented frames to the master device.) Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc commit 91b48d9630e6319741206bb36314ecf1750d8f65 Author: Michael Wu Date: Mon Feb 19 21:00:56 2007 +0100 [PATCH] d80211: Fix wireless statistics reporting This fixes statistics reporting. It allows drivers to specify what type of values they support, makes scan results return correct statistics, and generally fixes the brain damaged statistics reporting code. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit fa1b0d85c68b852b52ab39426bec4c845a66c23a Author: Michael Wu Date: Mon Feb 19 21:00:56 2007 +0100 [PATCH] d80211: Support automatic channel/BSSID/SSID configuration This patch implements auto channel/BSSID/SSID selection for backwards compatibility with anyone not using wpa_supplicant to associate. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit c1b52e560e42179237fe4c4e70d51f933f312f19 Author: Michael Buesch Date: Sat Feb 17 19:48:48 2007 +0100 ssb: Also check for PCI vendor ID, not just PCI device ID. Signed-off-by: Michael Buesch commit 64689c7d77e9ccd25d9d24bfb7555a8074e8f35b Author: Michael Buesch Date: Sat Feb 17 19:35:24 2007 +0100 bcm43xx-d80211: Move firmware pointers to bcm43xx_wldev, where they belong. Signed-off-by: Michael Buesch commit 99acebd7ed008951278284f3c39c30ca53a0ea69 Author: Michael Buesch Date: Sat Feb 17 19:10:45 2007 +0100 bcm43xx-d80211: Hold firmware in memory until we rmmod the module. This helps us avoid re-requesting the fw all over the time from userspace. Signed-off-by: Michael Buesch commit 2bf1b552f59909c0381e511b4d66d512560ddd52 Author: Michael Buesch Date: Sat Feb 17 18:53:40 2007 +0100 bcm43xx-d80211: Remove unneeded SHUTTINGDOWN and RESTARTING flags. Signed-off-by: Michael Buesch commit 7580509524c85451b073b0b2daa85a2ab64160a1 Author: Michael Buesch Date: Sat Feb 17 18:45:47 2007 +0100 bcm43xx-d80211: Add "is-running" checks on core_stop and core_exit. Signed-off-by: Michael Buesch commit f7b479e192f0d74eb8aeec8826bfd2d0f6df307a Author: Michael Buesch Date: Sat Feb 17 18:32:08 2007 +0100 bcm43xx-d80211: Ignore any A-PHY, as we don't support it, yet. Signed-off-by: Michael Buesch commit d34d14d5fb2e4f7e7e0b067e77a1ac07738a157a Author: Michael Buesch Date: Sat Feb 17 18:19:09 2007 +0100 ssb: Ignore additional 802.11 cores on devices with dangling pins on those cores. Signed-off-by: Michael Buesch commit 3ff05f3690c4ba7d8a837b41b32b966d037912b1 Author: John W. Linville Date: Thu Feb 15 23:49:36 2007 -0500 [PATCH] cfg80211: fix typo in wiphy_new Signed-off-by: John W. Linville commit f10adbae4fde4e3faad3b639387db1fdc768f41e Author: John W. Linville Date: Thu Feb 15 23:36:50 2007 -0500 [PATCH] d80211 leds: update for wiphy api Signed-off-by: John W. Linville commit 4deb57ca513628646cc159236a04143684476398 Author: John W. Linville Date: Thu Feb 15 23:28:31 2007 -0500 [PATCH] adm8211: update for wiphy api Signed-off-by: John W. Linville commit f8d3b044fcf7d8de47dd8f74cd4f3c52a3181040 Author: John W. Linville Date: Thu Feb 15 23:05:09 2007 -0500 [PATCH] adm8211: update for wiphy api Signed-off-by: John W. Linville commit 04d30025922406c522f3094590a67cf8ebafa9ed Author: John W. Linville Date: Thu Feb 15 22:47:19 2007 -0500 [PATCH] rt2x00: update for wiphy api Signed-off-by: John W. Linville commit f2bc1a3a74cf75062df1ee520f4e37f030526e6a Author: Johannes Berg Date: Thu Feb 15 15:42:51 2007 +0100 [PATCH] cfg/nl80211: remove legacy network id This patch removes the legacy network ID. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit 74ea59a931744291802e70a4068c48776286ffff Author: Johannes Berg Date: Thu Feb 15 15:42:50 2007 +0100 [PATCH] cfg80211: pending config This introduces the pending config (not used yet) and by doing so fixes the memory leak in the wext compat code. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit 47d12170db68635c9f468480150c8ef2a3e341cc Author: Johannes Berg Date: Thu Feb 15 15:42:49 2007 +0100 [PATCH] zd1211rw-d80211: update for wiphy api update zd1211rw-d80211. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit 1d0dda002ceb0ed9534c9eb1b608d34ed7805861 Author: Johannes Berg Date: Thu Feb 15 15:42:48 2007 +0100 [PATCH] bcm43xx-d80211: update for wiphy api update bcm43xx-d80211. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit fe205a2adfb7b312764afcccf239fdcec07e4d4c Author: Johannes Berg Date: Thu Feb 15 15:42:47 2007 +0100 [PATCH] d80211: update for wiphy api This patch lets d80211 use the new wiphy stuff from cfg80211. Patch is large because cfg80211 requires the net_dev->ieee80211_ptr now. Net code removal due to cfg80211 handling sysfs for us. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit 66565ab608267762a5376f22798b4180a5446e36 Author: Johannes Berg Date: Thu Feb 15 15:42:46 2007 +0100 [PATCH] wext: clean up This cleans up wext. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit 346e5269686b7b184666617052adb6b4f582adeb Author: Johannes Berg Date: Thu Feb 15 15:42:45 2007 +0100 [PATCH] cfg/nl80211: make association explicit This patch makes association explicit. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit 68c349957620d4ef2dc95953165223c321f97f20 Author: Johannes Berg Date: Thu Feb 15 15:42:44 2007 +0100 [PATCH] introduce wiphy concept This patch introduces struct wiphy and struct wireless_dev. The latter is added to struct net_device as ieee80211_ptr and keeps wireless per-netdev state, the wiphy keeps wireless per-device (hardware) state and is accessible from struct wireless_dev. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit bb32144bde20d026faadb9db632d78828a4c3131 Author: Johannes Berg Date: Thu Feb 15 15:42:43 2007 +0100 [PATCH] update cfg80211/wext and wext code This patch updates the cfg80211/wext compat code as well as the original wext code. To ease development/testing, it allows having cfg80211 including all the compat code as a module, only a registration hook for the wext ioctls needs to be built-in. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit 299f0729b74eef661afa3f0acbc8d19ee1cb49fe Author: Johannes Berg Date: Thu Feb 15 15:42:42 2007 +0100 [PATCH] remove cfg80211/wext-nl compatibility Wireless extensions over netlink can't reliably be used anyway (and probably never will be used...) so remove the code that would allow cfg80211 to be compatible with that. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit 941f5533e48c7e3bfb85541d0ec9e763184dd967 Author: John W. Linville Date: Thu Feb 15 20:41:26 2007 -0500 [PATCH] p54: add USB ID 2001:3704 Signed-off-by: John W. Linville commit bec987f3ceb35d48e8a33567586222115e6691f2 Author: Michael Buesch Date: Thu Feb 15 23:42:13 2007 +0100 ssb: Add sysfs file to read/write the SPROM on PCI devices. The sysfs SPROM file is located in the directory of the PCI device. It physically belongs to the PCI device on the board, so this makes sense. Signed-off-by: Michael Buesch commit fe65044387a1a15d7304d1f19d104f4c54c8289d Author: Michael Wu Date: Thu Feb 15 22:20:10 2007 +0100 [PATCH] d80211: Make common function for frequency/channel selection This patch creates ieee80211_set_channel and moves the channel selection and setting code in ieee80211_ioctl_siwfreq to this function. This allows IBSS code in ieee80211_sta.c to set the channel without using the wireless extensions interface. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit 04622f694489ee966f3a87997b4a1a023393d861 Author: Michael Wu Date: Thu Feb 15 22:20:10 2007 +0100 [PATCH] d80211: Simplify channel & mode configuration This patch simplifies channel & mode setting while eliminating a race between channel configuration and scanning. It also adds a call to ieee80211_hw_config after ops->open. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit da5fe6c969d76187b3ceeda46e4dc527d73ebae6 Author: Michael Wu Date: Thu Feb 15 22:20:09 2007 +0100 [PATCH] d80211: Fix concurrency issues in ieee80211_sta.c This fixes most concurrency issues in ieee80211_sta.c and partially prevents scans from running over an association in progress and vice versa. This is achieved by forcing all potentially racy code to run from a workqueue. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit 303082ed805ce3266a613182e2e0c2b95fef77ca Author: Michael Wu Date: Thu Feb 15 22:20:08 2007 +0100 [PATCH] d80211: remove hosttime from ieee80211_rx_status Nobody fills hosttime in ieee80211_rx_status. Removing it allows ieee80211_rx_status to fit in skb->cb. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit 35b4c9eca49e0464592bebdf36989d77f928ebc5 Author: Michael Wu Date: Thu Feb 15 22:20:08 2007 +0100 [PATCH] d80211: trivial cleanups in ieee80211_i.h This removes some unused things and fixes a typo. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit d02de628f917dadffc36cbb04f25a9e0bd3cb070 Author: Michael Wu Date: Thu Feb 15 22:20:08 2007 +0100 [PATCH] d80211: fix authentication issues This patch prevents the MLME in d80211 from getting stuck when there is no reply to authentication frames. It also allows the bssid to be correctly set during IEEE80211_AUTHENTICATE. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit bc8bcd1d58708094e7b24d03c7f426fd2107d9f4 Author: John W. Linville Date: Thu Feb 15 10:47:35 2007 -0500 [PATCH] zd1211rw-d80211: fix minor typo in debugging printk Signed-off-by: John W. Linville commit 2f7559ed5f94a355dec6c07c79ae61cafc8387f2 Author: Michael Buesch Date: Wed Feb 14 01:00:33 2007 +0100 bcm43xx-d80211: Fix locking info text. Signed-off-by: Michael Buesch commit e5571501b95cbe8e094432ec6710cf887e70fe97 Author: Michael Buesch Date: Wed Feb 14 00:45:58 2007 +0100 bcm43xx-d80211: Fix stupid locking bug on error in chip_reset. Arghhh... Signed-off-by: Michael Buesch commit 935cec9e57f56da9e99e5a286d98da9fc6cf19f2 Author: Michael Buesch Date: Wed Feb 14 00:42:46 2007 +0100 bcm43xx-d80211: Implement card reset routine to properly reset on fatal error conditions. Signed-off-by: Michael Buesch commit f962aa6758ad1b3df8315bb4d203947d3e638cf9 Author: Johannes Berg Date: Thu Feb 1 14:45:18 2007 +0100 [PATCH] maintainers: update all wireless entries This patch updates the MAINTAINERS file putting the new list into place wherever appropriate. For some entries, I replaced netdev, for others I just added this one. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit ed0ff258257e11cde62f6ee8176be4785b7f7964 Author: Ivo van Doorn Date: Sat Feb 3 17:40:30 2007 +0100 [PATCH] d80211-p54: turn-off BEACON_TEMPLATE flag On Saturday 03 February 2007 17:33, Michael Wu wrote: > Yeah, beacons aren't actually handled yet. BEACON_TEMPLATE can just be turned > off for now if it's causing problems, though I don't see a reason why a > beacon would be generated for managed mode. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit e08eef6fc97c44baec64999bda2630cb30f0ff96 Author: Ivo van Doorn Date: Sat Feb 3 17:25:21 2007 +0100 [PATCH] d80211-bcm43xx: Add control structure for beacontemplates Drivers that require beacon templates will also have the control structure at their disposal and should always free it. bcm43xx doesn't use the control structure, but should still free it. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit ff69af48784e69666fad32c252004ba579f70ab7 Author: Ivo van Doorn Date: Sat Feb 3 14:18:49 2007 +0100 [PATCH] eeprom_93cx6 little endian fix This patch makes sure the multiread/multiwrite functions for eeprom_93cx6 work with little endian data. The singleread still works with host endian. Most drivers still want the multiread to work with little endian because this is used for data like the MAC address. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit e226b8aa8921b6b75dcd0142a990b8c7bc1548fd Author: Larry Finger Date: Fri Jan 26 14:11:07 2007 -0600 [PATCH] bcm43xx-d80211: Interrogate hardware-enable switch and update LEDs The current bcm43xx-d80211 driver ignores any wireless-enable switches on mini-PCI and mini-PCI-E cards. This patch implements a new routine to interrogate the radio hardware enabled bit in the interface, logs the initial state and any changes in the switch (if debugging enabled), activates the LED to show the state, and changes the periodic work handler to provide 1 second response to switch changes. The changes in the periodic work specs have not been implemented. Signed-off-by: Larry Finger Signed-off-by: John W. Linville commit 25c80223a0d73e29893955ace7029936031cc6bd Author: Michael Buesch Date: Mon Feb 5 17:25:40 2007 +0100 [PATCH] rt2x00-d80211: Use d80211 API to generate RTS/CTS frames Use the new d80211 API to generate RTS and CTS-to-self frames. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit ece93bda2cd9231d5592223a646878b4812f248c Author: Michael Buesch Date: Mon Feb 5 17:24:55 2007 +0100 [PATCH] bcm43xx-d80211: Use d80211 API to generate RTS/CTS frames Use the new d80211 API to generate RTS and CTS-to-self frames. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 6d19a2e35c98e9fcac768a5d2cde92b25629ec3b Author: Michael Buesch Date: Tue Feb 13 12:05:00 2007 +0100 bcm43xx-d80211: Powerdown bus after attach step to save power. Signed-off-by: Michael Buesch commit 04ac557bc9e6bff4d7d0d15426b6344e67f378e4 Author: Michael Buesch Date: Tue Feb 13 02:02:00 2007 +0100 ssb: Add API to properly handle bus powercontrol. Unexport lowlevel chipcommon clockcontrol. This must not be used by drivers. Signed-off-by: Michael Buesch commit 2f97dbcb2f499d68c81fc51dbdd4a70bc59bdad4 Author: Michael Buesch Date: Tue Feb 13 01:13:35 2007 +0100 bcm43xx-d80211: Do all init work in wireless_core_init(). wireless_core_init() is the only place where HW initialization can be done. Otherwise suspend/resume cycles won't work as expected (also PHYmode switches...). Signed-off-by: Michael Buesch commit 6f6f83b0c4216c5bd3f792252fd944628c954c5e Author: Michael Buesch Date: Tue Feb 13 00:54:04 2007 +0100 bcm43xx-d80211: Fix rmmod crash. Call unregister_hw early. Call ieee80211_unregister_hw early to avoid crashing in the cleanup paths, because the device structs are already kfree()d. Call ieee80211_register_hw late after setting up structs. Signed-off-by: Michael Buesch commit ea3a33d31cef7b26c08f54016f878da2fe7a4f1c Author: Michael Buesch Date: Sun Feb 11 23:37:20 2007 +0100 bcm43xx-d80211: Implement PHYmode switching support. This implements support for switching PHYmode via gmode-bit and coreswitch. Signed-off-by: Michael Buesch commit b793922743a9286b4e9bdddf162dfc8753a65752 Author: Michael Buesch Date: Sun Feb 11 22:03:35 2007 +0100 bcm43xx-d80211: Some BTcoext stuff. Signed-off-by: Michael Buesch commit 754c18a972e4b49f15086f46a034db3bd655835b Author: Michael Buesch Date: Sun Feb 11 21:35:19 2007 +0100 bcm43xx-d80211: Add an assert()ion for dev->started to the IRQ handler. Signed-off-by: Michael Buesch commit 0b9e8f34bc5d12c89e79071d55a713b3308befd0 Author: Michael Buesch Date: Sun Feb 11 20:57:34 2007 +0100 bcm43xx-d80211: Assert SHM-sh offsets to be 16bit aligned. Signed-off-by: Michael Buesch commit 09c49a6d4b5f1a3f2568a0a01b00daf724acdcee Author: Michael Buesch Date: Sun Feb 11 20:50:18 2007 +0100 bcm43xx-d80211: A few fixes and cleanups in LO setup. Signed-off-by: Michael Buesch commit b31e3317c3ba2245eeecd9268481a100ba55f1a7 Author: Michael Buesch Date: Sun Feb 11 17:31:45 2007 +0100 bcm43xx-d80211: Add missing udelay() in loopback gain calc. Signed-off-by: Michael Buesch commit a69176fdabaad15e0cb2ea89b86b66a13e539587 Author: Michael Buesch Date: Sun Feb 11 16:25:42 2007 +0100 bcm43xx-d80211: Assign PCMCIA suspend and resume function pointers. Signed-off-by: Michael Buesch commit 80bc8b1cd25296d53357ea81435679a7beef273a Author: Michael Buesch Date: Sun Feb 11 16:11:11 2007 +0100 b44: Use the generic PCIhost wrapper from SSB. Signed-off-by: Michael Buesch commit 6e38f33c0bcd742d8cf1c3db5a5377003d504911 Author: Michael Buesch Date: Sun Feb 11 16:03:22 2007 +0100 ssb, bcm43xx-d80211: Put the PCIhost wrapper into SSB. Signed-off-by: Michael Buesch commit 53cfd55fd7c4ce774303bc9a756f5f9cfd426239 Author: Michael Buesch Date: Sun Feb 11 14:50:32 2007 +0100 bcm43xx-d80211: Fix 2050 radio init for devices with analog>=2 Signed-off-by: Michael Buesch commit 6003b1f1019048b5a55997f1262660a7feb334d2 Author: Michael Buesch Date: Sun Feb 11 14:43:19 2007 +0100 bcm43xx-d80211, ssb: Fix suspend/resume. This fixes some bugs to get suspend/resume working again. Signed-off-by: Michael Buesch commit 218241c63246c5611520df98ce8ae06016545c9c Author: Michael Buesch Date: Sat Feb 10 23:32:47 2007 +0100 bcm43xx-d80211: Set device parent for the ssb bus. Original patch by Matthew Garrett. Signed-off-by: Michael Buesch commit 2dfaf4f45ce12f64c91e88d6553427d7a4aa1732 Author: Michael Buesch Date: Fri Feb 9 19:51:30 2007 +0100 bcm43xx-d80211: Various PHY fixes. specs changed. Signed-off-by: Michael Buesch commit 7e7e58cbf3b9806b2e5cd7cea030ffedfe7ddae9 Author: Michael Buesch Date: Fri Feb 9 18:31:25 2007 +0100 bcm43xx-d80211: Convert phy_version to analog_type No functional change. Signed-off-by: Michael Buesch commit b3dd9bcbf43740bf1b572b33f8b863bd2debb6de Author: Michael Wu Date: Thu Feb 8 20:27:07 2007 +0100 [PATCH] d80211: use default flags on virtual interfaces There is no need to set dev->flags on virtual interfaces during registration. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit c302a5562f6fd73a4b97202619efff0513743e07 Author: Michael Buesch Date: Thu Feb 8 20:27:07 2007 +0100 [PATCH] d80211: Add API to generate RTS and CTS-to-self frames This adds API calls to generate RTS and CTS-to-self frames. To be called if the device firmware requires the host to generate RTS/CTS frames. Signed-off-by: Michael Buesch Signed-off-by: Jiri Benc commit 3a24be2b42fac52d7ee5f6544fd9dafdeb48201a Author: Ivo van Doorn Date: Thu Feb 8 19:30:02 2007 +0100 [PATCH] d80211: Add control structure for beacontemplates When rt2500usb and rt73usb will start using beacontemplates, they would also need a control structure to be passed along to correctly set the tx parameters. This patch will add a ieee80211_tx_control pointer to the ieee80211_if_init_conf structure. This pointer is only a reference to a local variable so drivers will not need to call kfree() on it. Signed-off-by: Ivo van Doorn Signed-off-by: Jiri Benc commit 37989a7224f27965d3ec30a0745c5f19c3c167e5 Author: Ivo van Doorn Date: Thu Feb 8 19:30:02 2007 +0100 [PATCH] d80211: respect extra_tx_headroom When a driver requested additional header room through the extra_tx_headroom field, the stack should respect that and make sure that all frames that are being send to the stack actually have that extra header room. Signed-off-by: Ivo van Doorn Signed-off-by: Jiri Benc commit c77e8c036dd60fc819f1925a2feb692f02cb4c3e Author: Jon Smirl Date: Mon Feb 5 22:51:44 2007 +0100 [PATCH] d80211: remove redundant casts to iw_handler_def These two casts are redundant. Signed-off-by: Jon Smirl Signed-off-by: Jiri Benc commit 37c95ceeebd38a3f1f686b8fbf5168672d19f825 Author: Zhu Yi Date: Mon Feb 5 22:51:44 2007 +0100 [PATCH] d80211: Fix WMM ACI to UP mapping Fix WMM ACI to UP mapping according to IEEE 802.1d spec. Table 7-2. Signed-off-by: Zhu Yi Signed-off-by: Jiri Benc commit 6a917368d44bd6e3120c87133e786ef78a9584e7 Author: Michael Buesch Date: Mon Feb 5 14:01:08 2007 +0100 bcm43xx-d80211: Ignore ampdu status reports. Signed-off-by: Michael Buesch commit 63f977c3d03c1315063f940c6f80d42eaae6dc33 Author: Ulrich Kunitz Date: Sat Feb 3 02:25:30 2007 -0500 zd1211rw-d80211: Reset device in the probe call This is a port of a patch for the zd1211rw driver, originally by Ulrich Kunitz . This resets the device in the probe call. It might fix the reboot/reset problems a lot of people reported. Signed-off-by: Michael Wu commit ae30d745a453c4dd538b186fe13f1852c879c1f8 Author: Maxime Austruy Date: Sat Feb 3 02:17:37 2007 -0500 zd1211rw-d80211: fix potential leak in usb_init This is a port of a patch for the zd1211rw driver, originally by Maxime Austruy . usb_init should call destroy_workqueue when usb_register fails. Signed-off-by: Michael Wu commit 732d125566c0051c3907f2cba6658eb046c9c6db Author: Ulrich Kunitz Date: Sat Feb 3 02:12:59 2007 -0500 zd1211rw-d80211: Fixed array size issue in reset_mode This is a port of a patch for the zd1211rw driver, originally by Ulrich Kunitz . Andy Green found this issue. Signed-off-by: Michael Wu commit fe288f259e0376bf75d44eb4400ace6304257e35 Author: Michael Wu Date: Sat Feb 3 02:04:22 2007 -0500 zd1211rw-d80211: Support for multicast addresses This is a port of the multicast patch for zd1211rw, originally by Ulrich Kunitz . That patch was based on the earlier work by Benoit Paillaut. Signed-off-by: Michael Wu commit 2845d790d13fd9661dc9c7a9370af75bb91ef905 Author: Daniel Drake Date: Sat Feb 3 01:58:05 2007 -0500 zd1211rw-d80211: Remove addressing abstraction This is a port of a patch for the zd1211rw driver, originally by Daniel Drake . Instead of passing our own custom 32-bit addresses around and translating them, this patch makes all our register address constants absolute and removes the translation. There are two ugly parts: - fw_reg_addr() is needed to compute addresses of firmware registers, as this is dynamic based upon firmware - inc_addr() needs a small hack to handle byte vs word addressing However, both of those are only small, and we don't use fw_regs a whole lot anyway. The bonuses here include simplicity and improved driver readability. Also, the fact that registers are now referenced by 16-bit absolute addresses (as opposed to 32-bit pseudo addresses) means that over 2kb compiled code size has been shaved off. Includes some touchups and sparse fixes from Ulrich Kunitz. Signed-off-by: Michael Wu commit 8b494f7eb621290cf2bcd0ab8beca652cea9d825 Author: Daniel Drake Date: Sat Feb 3 01:58:04 2007 -0500 zd1211rw-d80211: Consistency for address space constants This is a port of a patch for the zd1211rw driver, originally by Daniel Drake . The zd1211rw address space has confused me once too many times. This patch introduces the following naming notation: Memory space is split into segments (cr, fw, eeprom) and segments may contain components (e.g. boot code inside eeprom). These names are arbitrary and only for the description below: x_START: Absolute address of segment start (previously these were named such as CR_BASE_OFFSET, but they weren't really offsets unless you were considering them as an offset to 0) x_LEN: Segment length x_y_LEN: Length of component y of segment x x_y_OFFSET: Relative address of component y into segment x. The absolute address for this component is (x_START + x_y_OFFSET) I also renamed EEPROM registers to EEPROM data. These 'registers' can't be written to using standard I/O and really represent predefined data from the vendor. Signed-off-by: Michael Wu commit 98b3fcdddb9acd1c82ff26c074c4d1b01399f592 Author: Daniel Drake Date: Sat Feb 3 01:58:04 2007 -0500 zd1211rw-d80211: Generic HMAC initialization This is a port of a patch for the zd1211rw driver, originally by Daniel Drake . Many of the registers written during ZD1211 HMAC initialization are duplicated exactly for ZD1211B. Move the identical ones into a generic part, and write the hardware-specific ones separately. Signed-off-by: Michael Wu commit 7ce440d5f3d399f825954080b7a6444c09fbc576 Author: Michael Wu Date: Sat Feb 3 01:52:23 2007 -0500 p54pci: make work for big endian This should make the p54 PCI backend work on big endian platforms. Don't have a big endian machine to test on, however. Signed-off-by: Michael Wu commit b0135032903b067f378c8a0f3112a495a99278c0 Author: Michael Wu Date: Sat Feb 3 01:44:17 2007 -0500 p54usb: silence warnings on BE GCC spews warnings on BE in version 1 init code since cpu_to_le32 actually does something. This silences them. Thanks to Johannes Berg for pointing this out. Signed-off-by: Michael Wu commit f62828a4c76ec5861bebcad25df2a4eace532661 Author: Michael Wu Date: Sat Feb 3 01:32:19 2007 -0500 p54: set MAC address properly This allows the MAC address to be changed from the EEPROM default. Signed-off-by: Michael Wu commit 39393b2596d95cdda19003ede1953f34efa8be82 Author: Michael Wu Date: Sat Feb 3 01:26:57 2007 -0500 adm8211: set MAC address properly This allows the MAC address to be changed from the EEPROM default. Signed-off-by: Michael Wu commit 62801b89e2a7ad9dc0f7bd5689606920eb430b91 Author: Michael Wu Date: Sat Feb 3 00:46:28 2007 -0500 Remove unnecessary includes and SET_MODULE_OWNER This patch removes some unnecessary includes from adm8211, p54, and zd1211rw-d80211 and the use of SET_MODULE_OWNER. Signed-off-by: Michael Wu commit 682d59cf4e39c0c932c771f94437af71b6a9926b Author: Marcus Better Date: Wed Jan 31 19:52:57 2007 +0100 [PATCH] d80211: select CRC32 functions The d80211 stack requires CRC32 functions for the WEP implementation. Signed-off-by: Marcus Better Signed-off-by: Jiri Benc commit 26192f0e00248ce015a8b16a39d9d01711ea39be Author: Jan Kiszka Date: Wed Jan 31 19:52:57 2007 +0100 [PATCH] d80211: fix default key symlink creation/cleanup This gets rid of annoying wlan0: cannot create symlink to default key in my syslog with latest rt2x00. The patch takes care to always delete an existing symlink to the default key before trying to register a new one. Moreover, it avoids to call ieee80211_key_sysfs_add_default for a NULL key. Signed-off-by: Jan Kiszka Signed-off-by: Jiri Benc commit 11646b5a788eb3e4867307efe8c7043c376dd778 Author: Michael Buesch Date: Tue Jan 30 20:18:53 2007 +0100 bcm43xx-d80211: Rename some TSSI related variables. This actually seems to hide a bug with those values. See the FIXME and the added assertion. It triggers for me, which I think it shouldn't. Signed-off-by: Michael Buesch commit 65bb66c92ce3e5149d3adf42cb3a4e8d10e77a6d Author: Michael Buesch Date: Tue Jan 30 18:10:08 2007 +0100 bcm43xx-d80211: Add missing PHY version register masking. Signed-off-by: Michael Buesch commit f9edd41f4a464b230cccab4fe3e78ca096427ca5 Author: Michael Buesch Date: Mon Jan 29 17:45:26 2007 +0100 bcm43xx-d80211: Get rid of d80211 open() and stop() callbacks. These callbacks are useless and can be removed in the stack. Signed-off-by: Michael Buesch commit 4144dcd19ab01ef6ebca7b4c91a166aac88571a6 Author: Michael Buesch Date: Sun Jan 28 01:30:06 2007 +0100 bcm43xx-d80211: const-ify ieee80211_ops. Now that d80211 allows const ieee80211_ops, convert them to be const. Signed-off-by: Michael Buesch commit b0c2e5535f322358e61b19672d0bbc2ea75087d1 Author: Michael Buesch Date: Sun Jan 28 01:12:13 2007 +0100 ssb: pcicore: Remove FIXME. The busnumber is OK. Signed-off-by: Michael Buesch commit 3509d271ec0a7b76a8d9b71257a0c68f42cd5512 Author: Michael Buesch Date: Sun Jan 28 01:02:27 2007 +0100 ssb: Fix pcicore to run in hostmode. This patch fixes PCIcore code to make hostmode code working. From Felix Fietkau , slightly modified by Michael Buesch. Signed-off-by: Michael Buesch commit 461bce2df6da451ba0e4b532e67c02ea5284300f Author: Michael Buesch Date: Sat Jan 27 20:55:17 2007 +0100 b44: Port b44 driver to SSB This ports the b44 driver to the SSB subsystem and also adds a few things to make it run on some native SSB based devices with bcm47xx chip. This patch was originally developed by the OpenWRT project. See the Copyright header for Copyright notices. Signed-off-by: Michael Buesch commit 207d0461e24d0f2728df4f745118740861516269 Author: Michael Buesch Date: Fri Jan 26 23:48:26 2007 +0100 ssb: Fix TODO in chipcommon driver. Signed-off-by: Michael Buesch commit f9d7e76d62e5e28d0c234b40ddf9af5a10a4bd27 Author: Michael Buesch Date: Fri Jan 26 22:12:45 2007 +0100 bcm43xx-d80211: Fix a bunch of FIXMEs in the loopback_gain calculation. Signed-off-by: Michael Buesch commit f02450b13f4e9afa43c5893146831ee09a5a196b Author: Michael Buesch Date: Thu Dec 21 19:16:50 2006 +0100 [PATCH] bcm43xx-d80211: Fix DMA TX skb doublefree This fixes a possible double-free of the TX skb buffers. Always NULL the pointer after freeing. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit c48a13f3b4f04dab5132ca2185d9bfbe1065b77b Author: Ivo van Doorn Date: Wed Jan 3 21:29:36 2007 +0100 [PATCH] rt2x00 should use generic crc-itu-t This patch removes the crc-itu-t files from rt2x00 and makes sure rt2x00 will use the generic crc-itu-t implementation inside the lib folder. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 4686e9cbed6ba974eaa8fcf00f514ee697169072 Author: Ivo van Doorn Date: Wed Jan 3 21:29:36 2007 +0100 [PATCH] crc-itu-t This patch add the crc-itu-t implementation to the lib folder. This crc handler uses the CRC ITU-T V.41 routine that is used in multiple drivers. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit d906b01ca07fc4d79885502758ea5f7f61b80320 Author: Ivo van Doorn Date: Wed Jan 3 21:29:36 2007 +0100 [PATCH] rt2x00 should use generic eeprom_93cx6 This patch removes the eeprom_93cx6 files from rt2x00 and makes sure rt2x00 will use the generic eeprom_93cx6 implementation inside the lib folder. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit c4858df771b7df1098280b90f9bcfbafb2951883 Author: Ivo van Doorn Date: Thu Jan 25 19:39:01 2007 -0500 [PATCH] eeprom_93cx6 This patch adds the eeprom_93cx6 module to the lib folder. This module provides a generic approach for reading and writing words from the eeprom chipsets 93c46 and 93c66. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 2c1b41cdc8555efe213fe1e46529d966094fe181 Author: Michael Buesch Date: Wed Jan 24 12:08:39 2007 +0100 bcm43xx-d80211: Fix loopback gain calculation. There still seem to be a bunch of possible bugs left. I marked most of them with "FIXME". Signed-off-by: Michael Buesch commit 6be51a1a246b4dcd3eae38b920e888535f230d8f Author: Michael Buesch Date: Tue Jan 23 21:51:20 2007 +0100 bcm43xx-d80211: Fix initial LO Calibration. The initial LO calibration wasn't triggered, because of a txctl2 initialization bug. This fixes it, but also seems to reveal another bunch of critical bugs in the LO code. With this applied, transmission will stop on the (first?) periodic work that re-runs calibration. So the card will only run for a few seconds. Not sure what's going on. Signed-off-by: Michael Buesch commit bd30b005c408eaca0f8595a22aa082b811404954 Author: Michael Buesch Date: Tue Jan 23 17:14:37 2007 +0100 ssb: b44 related fixes. Some fallback fixes. Signed-off-by: Michael Buesch commit 2e435a125a77dba5692584dbb684d4d0e2c6a299 Author: Michael Buesch Date: Tue Jan 23 15:40:30 2007 +0100 ssb: add PM config register definitions Signed-off-by: Michael Buesch commit 88e68f44e7d6228ac58795866e33048fbb410ae9 Author: Michael Buesch Date: Tue Jan 23 14:44:43 2007 +0100 ssb: export ssb_clockspeed() Signed-off-by: Michael Buesch commit 6fa37f255d406f2132c04a25da1c38f17af42334 Author: Michael Buesch Date: Tue Jan 23 12:59:07 2007 +0100 ssb: PCIcore hostmode fixes. Signed-off-by: Michael Buesch commit d2a949b6b4cc5a496878c7e4460763ab689513aa Author: Pavel Roskin Date: Sun Jan 21 00:27:40 2007 -0500 bcm43xx_d80211: Fix major memory corruption bug Set phy->lo_control to NULL whenever it's freed. Failure to do so leads to zeroing a block of memory that uses to hold *phy->lo_control, which caused random crashes down the road. Signed-off-by: Pavel Roskin Signed-off-by: Michael Buesch commit 986adc109dd2a6bb766a0860650d5f9ebbf7cac7 Author: Michael Buesch Date: Mon Jan 22 11:25:01 2007 +0100 ssb, usb: Implement SSB based Broadcom USB OHCI driver. Signed-off-by: Michael Buesch commit 2c0184b3bb4af801204c98e9d88bc6fd5f48a9a9 Author: Michael Buesch Date: Wed Jan 17 15:29:12 2007 +0100 ssb: Add missing include to delay.h in ssb/pcmcia.c Signed-off-by: Michael Buesch commit 6ca138d5294cec41165670beb852e2913603f235 Author: Michael Buesch Date: Tue Jan 16 21:37:59 2007 +0100 bcm43xx-d80211: gphy init: Some cleanups and some bugfixes. Signed-off-by: Michael Buesch commit da2ae0422f6e156112ef0e20ba99bc281efb4b21 Author: Michael Buesch Date: Tue Jan 16 17:42:00 2007 +0100 bcm43xx-d80211: Fix semantical errors in LO measure setup. Signed-off-by: Michael Buesch commit 0985f6cdfc3a946b078e8d32531cc5647fc52c64 Author: Michael Buesch Date: Mon Jan 15 21:05:07 2007 +0100 bcm43xx-d80211: Various cleanups all over the code. Signed-off-by: Michael Buesch commit a97dea44a69c94f5c8f785f286e0caba375efa75 Author: Michael Buesch Date: Mon Jan 15 18:59:50 2007 +0100 bcm43xx-d80211: Fix error return codes. This fixes operating two or more interfaces on one card. Signed-off-by: Michael Buesch commit cb4a65ff13124a1479543d0dbe2b888985a62643 Author: Michael Buesch Date: Mon Jan 15 17:57:35 2007 +0100 bcm43xx-d80211: Fix wrong register write in lo_measure_feedthrough(). Signed-off-by: Michael Buesch commit 5eb5564c75ed5a5513fd007ded12d21dd9688e23 Author: Michael Buesch Date: Mon Jan 15 14:54:29 2007 +0100 bcm43xx-d80211: Get rid of "PHY-connected" semantics. "PHY-connected" are really "G-mode" semantics. Signed-off-by: Michael Buesch commit 19bcdc9622510ba3b2b9a88ed6b48b122ad6b0b6 Author: Michael Buesch Date: Mon Jan 15 13:31:05 2007 +0100 bcm43xx-d80211: Remove leds_exit() call in detach stage. It's wrong and crashes. Signed-off-by: Michael Buesch commit 644ef0530a44ba953b83e3d063a8fc4b2dd75aa0 Author: Michael Buesch Date: Mon Jan 15 12:22:31 2007 +0100 bcm43xx-d80211: re-add chipid printk Signed-off-by: Michael Buesch commit 2f75ff13a7b71cb6ad484c7dfc40336336d3e00d Author: Michael Buesch Date: Mon Jan 15 09:21:10 2007 +0100 bcm43xx-d80211: Support for PCMCIA devices. Signed-off-by: Michael Buesch commit 4581d7e6baf7c47aa37153b66fb6290d657fca8e Author: Michael Buesch Date: Mon Jan 15 09:18:21 2007 +0100 ssb: PCMCIA-hostbus support. Support for a Sonics Silicon Backplane on a PCMCIA host device. Signed-off-by: Michael Buesch commit edce6aca4815decd0be491b28b0278a0ec5074e7 Author: Michael Buesch Date: Sun Jan 14 18:15:20 2007 +0100 ssb: Allow disabling of all PCI related stuff. This is useful for devices which are on a native SSB bus without any PCI bus in host or clientmode. Signed-off-by: Michael Buesch commit 66cc887afbca25a0beabef9cc5d78eb3f61e4761 Author: Michael Buesch Date: Sat Jan 13 22:58:20 2007 +0100 ssb: Fix busnumber assignment. Must assign it before scanning the bus. Signed-off-by: Michael Buesch commit d2d697df83c2252af3e87d912453c75cf5423ffe Author: Michael Buesch Date: Sat Jan 13 18:46:58 2007 +0100 ssb, bcm43xx-d80211: Add function to set DMA mask on SSB. Signed-off-by: Michael Buesch commit 9c898e8a848e0a8f65a24ebdfd9acf80516eb925 Author: Michael Buesch Date: Sat Jan 13 18:12:05 2007 +0100 bcm43xx-d80211: Remove bogus call to refresh_templates in add_interface. Signed-off-by: Michael Buesch commit ae3260cf3f50a097d8b6d7d4f919b65381845d31 Author: Michael Buesch Date: Sat Jan 13 17:57:54 2007 +0100 ssb, bcm43xx-d80211: Move DMA translation logic to ssb. Signed-off-by: Michael Buesch commit ed1507b3a3806fa80b64f6377b973fb9434dc5ee Author: Michael Buesch Date: Sat Jan 13 17:04:08 2007 +0100 ssb: Fix typo. SSB_PCICORE_SBTOPCI1_CFG1 does not exist. Signed-off-by: Michael Buesch commit a509a22bcc18c5fe89ee5493a47c3dcf6ad0ef24 Author: Michael Buesch Date: Sat Jan 13 17:02:03 2007 +0100 ssb: Fix dependencies. MIPS core must depend on MIPS platform. Signed-off-by: Michael Buesch commit bd670de894ae685100ec208e9b34e42f23a88e27 Author: Michael Buesch Date: Fri Jan 12 18:34:56 2007 +0100 bcm43xx-d80211: Fix LO feedthrough measurement. Signed-off-by: Michael Buesch commit 23b795c859be3b4333e15f29e50d2cd99872099e Author: Michael Buesch Date: Thu Jan 11 20:04:19 2007 +0100 bcm43xx-d80211: Port driver to the new SSB subsystem. This ports bcm43xx to use the new SSB subsystem. Signed-off-by: Michael Buesch commit 66b0e87de5be2c3aa5dd9795fb463a28391f6d24 Author: Michael Buesch Date: Thu Jan 11 20:02:17 2007 +0100 Implement new SSB subsystem. This implements a new Sonics Silicon Backplane subsystem. Signed-off-by: Michael Buesch commit b9339be884fca2d65a4fd64cc32b4539ba503030 Author: Michael Buesch Date: Thu Jan 11 19:59:49 2007 +0100 Remove obsolete SSB driver library. Signed-off-by: Michael Buesch commit e3973cb079b24875af1f196d03569ce7eb517c92 Author: Michael Wu Date: Wed Jan 10 21:06:39 2007 +0100 [PATCH] d80211: Fix __ieee80211_if_del on live interfaces ieee80211_if_reinit is called in __ieee80211_if_del, which clears the contents of sdata->u. After that, unregister_netdevice is called. If the interface is still up, unregister_netdevice will end up calling dev->stop, and dev->stop expects the contents of sdata->u to be valid. Bad things typically happen at this point. This patch fixes that by setting dev->uninit to ieee80211_if_reinit and eliminating the call to ieee80211_if_reinit in __ieee80211_if_del. This allows ieee80211_if_reinit to be called at a safer time. It also allows the removal of the call to ieee80211_if_shutdown in ieee80211_if_reinit because ieee80211_if_reinit now will never be called while the interface is up. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit f85c9f7b6a0fe662b95595c51aed92d784e93c5e Author: Michael Wu Date: Wed Jan 10 21:06:39 2007 +0100 [PATCH] d80211: Only free WEP crypto ciphers when they have been allocated correctly. On Saturday 06 January 2007 12:00, Gertjan van Wingerde wrote: > The d80211 stack still tries to free the WEP crypto ciphers, even when > allocating them previously has failed. Actually, the code might not even have tried to allocate them. The ciphers are guaranteed to be allocated when the device is registered however, so we should be able to free it safely on unregister. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit 7b154af521a3f560ef546e0c7dd3b0cd0e712b18 Author: Gertjan van Wingerde Date: Wed Jan 10 21:06:39 2007 +0100 [PATCH] d80211: Select CRYPTO_ECB when enabler d80211. The d80211 stack uses ECB mode block ciphers for the WEP implementation. Make sure that support for CRYPTO_ECB is in the kernel when the d80211 stack is enabled (just like the other crypto algorithms). Signed-off-by: Gertjan van Wingerde Signed-off-by: Jiri Benc commit 8e97e9a0fc5fecd5d33a2023b032a9a9767ba4b1 Author: Jan Kiszka Date: Wed Jan 10 21:06:38 2007 +0100 [PATCH] d80211: Fix inconsistent sta_lock usage Hacking a bit on rt2x00 to make it work in master and ad-hoc mode, lockdep popped up on some hostapd ioctls, pointing out remaining inconsistencies related to sta_lock: 1. sta_lock holders must always be protected against softirq 2. bss_tim_set/clear must not be called with sta_lock held, rather an unprotected variant 3. ieee80211_ioctl_remove_sta is not already holding the lock when calling sta_info_free (Comment has been added by Ivo van Doorn to prevent future attempts to use the __set_bit and __clear_bit.) Signed-off-by: Jan Kiszka Signed-off-by: Ivo van Doorn Signed-off-by: Jiri Benc commit f66e5aaa88c1c0a7ee6c6427d6535ed8afd35427 Author: John W. Linville Date: Mon Jan 8 15:57:56 2007 -0500 [PATCH] zd1211rw-d80211: port INIT_DELAYED_WORK changes from mainline Signed-off-by: John W. Linville commit 06906c3343716d59dcbfb19f959467ba8bc016d8 Author: John W. Linville Date: Mon Jan 8 15:25:50 2007 -0500 [PATCH] bcm43xx, rt2x00: fix build breakage from INIT_DELAYED_WORK changes Signed-off-by: John W. Linville commit 626a389af7f50ac89cd7d3b4d5834e552cc45398 Author: John W. Linville Date: Fri Jan 5 11:09:19 2007 -0500 [PATCH] cfg80211: fix build breakage from genlmsg_put signature change Signed-off-by: John W. Linville commit 641b91dd456acc8346a51bc63b92743aeec517e2 Author: Michael Wu Date: Fri Dec 15 20:42:20 2006 +0100 [PATCH] zd1211rw-d80211: Fix compilation for d80211 hwmode API change This fixes compilation for the d80211 hwmode API change. Based on a patch by Michael Buesch . Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit 79d8e835e0a9c7cc88fde8bc16779d8a34bc0694 Author: Michael Wu Date: Fri Dec 15 20:42:20 2006 +0100 [PATCH] p54: Fix compilation for d80211 hwmode API change This fixes compilation for the d80211 hwmode API change. Based on a patch by Michael Buesch . Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit 806c3c787575f998b875280b6ad3f080f94d0961 Author: Michael Wu Date: Fri Dec 15 20:42:20 2006 +0100 [PATCH] adm8211: Fix compilation for d80211 hwmode API change This fixes compilation for the d80211 hwmode API change. Based on a patch by Michael Buesch . Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit c226a31f160969562bca68f50657d403fcb6342b Author: Michael Buesch Date: Fri Dec 15 20:42:20 2006 +0100 [PATCH] rt2x00: Fix compilation for d80211 hwmode API change This fixes compilation for the d80211 hwmode API change. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 968adc064b66181215b51293fa5798db7f89c135 Author: Michael Buesch Date: Thu Dec 14 19:20:25 2006 +0100 [PATCH] bcm43xx-d80211: Fix for PHYmode API change. This fixes the PHYmode list API breakage for the bcm43xx-d80211 driver. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit cfeab74d2321911b8fb6dfc311d7af699f184554 Author: Johannes Berg Date: Sun Dec 10 17:21:37 2006 +0100 [PATCH] fix cfg80211 WE compat code When cfg80211's WE compat code is enabled and the cfg80211 module is loaded (or built-in) WE calls on non-cfg80211 devices will return -ENODEV because cfg80211 internally says -ENODEV for cfg80211 calls on devices that don't have cfg80211 handlers. This patch makes the cfg80211 core return -ENOSYS which is more appropriate since the device exists but the configuration request can't be fulfilled. This will then cause the fallback code to the original WE to trigger. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit bf4562a26932a767167c1acdb87120611a23cc08 Author: Johannes Berg Date: Wed Dec 13 18:00:37 2006 +0100 [PATCH] fix some wireless ext stuff This fixes compilation with d80211/cfg80211/wireless ext. Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit 200d2c6f1f320215184206a5563431ddef71f2d7 Author: Jiri Benc Date: Wed Dec 13 18:00:36 2006 +0100 [PATCH] rt2x00: fix breakage after pkt_type field was removed Fix breakage after pkt_type field was removed from ieee80211_tx_control. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit fe8a02aa94633dd2251f1f14831944e24cb605ee Author: Michael Wu Date: Tue Dec 12 12:55:53 2006 -0500 [PATCH] d80211: fix workqueue breakage d80211: fix workqueue breakage This patch updates d80211 to use the new workqueue API. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit 0c42a1b7a3dceee811762ebfe7e3aba50fc7a541 Author: Michael Wu Date: Tue Dec 12 12:53:52 2006 -0500 [PATCH] d80211: fix wme.c breakage d80211: fix wme.c breakage This fixes wme.c, which was broken by a recent qdisc api change. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit c7717d9b5ec026981f71f2d31b73f51b1e72c1e9 Author: Michael Wu Date: Tue Dec 12 12:52:36 2006 -0500 [PATCH] d80211: fix wep.c breakage d80211: fix wep.c breakage This patch fixes wep.c, which was broken by Al Viro's severing skbuff.h -> mm.h patch. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit ff6a6797032d232d07be32594fdd7369cd927382 Author: Jan Kiszka Date: Wed Jan 3 18:57:36 2007 +0100 [PATCH] d80211: Reinit keys on mode change Switching the interface mode with some encryption keys set and then later touching any key, triggers an oops because ieee80211_if_reinit fails to NULL'ify the related pointers after free'ing the key on mode change. Long explanation, simple fix below. Signed-off-by: Jan Kiszka Signed-off-by: Jiri Benc commit ca363cfd2c934d40dd0c24a52e58921d355b3c01 Author: David Kimdon Date: Wed Jan 3 18:57:35 2007 +0100 [PATCH] d80211: inhibit duplicate authentication requests when setting bssid If we are already authenticating don't send another authentication request. Signed-off-by: David Kimdon Signed-off-by: Jiri Benc commit 90be57e78c95bd0e66a2047c39d91156d63d6f4c Author: David Kimdon Date: Wed Jan 3 18:57:35 2007 +0100 [PATCH] d80211: clear ifsta->associated flag when authentication starts The 'associated' flag might be set if a previous association did not end cleanly. If the 'associated' flag is left set here then when association succeeds ieee80211_set_associated() will think there is nothing to report and will not inform userspace of the event. Signed-off-by: David Kimdon Signed-off-by: Jiri Benc commit 82c4e75463da9d3f38540198c4594cafe336b3e1 Author: Jiri Benc Date: Wed Jan 3 18:57:34 2007 +0100 [PATCH] d80211: do not cancel uninitialized work When ops->hw_scan is set, scan_work is never initialized thus canceling it causes weird problems. Signed-off-by: Jiri Benc commit 4b9e346745833128de719c2995589e2900365084 Author: Michael Wu Date: Sun Dec 31 03:38:07 2006 -0500 adm8211: set phymode in RX This makes adm8211 set the phymode in ieee80211_rx_status. Signed-off-by: Michael Wu commit 8793b19822c4e02754ed0f4310fbfffc348b15e3 Author: Michael Wu Date: Sun Dec 31 03:32:55 2006 -0500 zd1211rw-d80211: Add ID for Linksys WUSBF54G This is a port of a patch for the zd1211rw driver, originally by Daniel Drake . Tested by Henrik Hjelte zd1211b chip 13b1:0024 v4802 high 00-14-bf AL2230_RF pa0 ---- Signed-off-by: Michael Wu commit b3079543bcc9b546299aa6d88d195c17f93e71d3 Author: Michael Wu Date: Sun Dec 31 03:29:52 2006 -0500 zd1211rw-d80211: 2 new ZD1211B device ID's This is a port of a patch for the zd1211rw driver, originally by Daniel Drake . Philips SNU5600, tested by unibrow zd1211b chip 0471:1236 v4810 high 00-12-bf AL2230_RF pa0 g-- SMC Ez Connect 802.11g (SMCWUSB-G), tested by Victorino Sanz Prat zd1211b chip 083a:4505 v4810 ful l 00-13-f7 AL2230_RF pa0 g--N Signed-off-by: Michael Wu commit 5d641a646cfa7c443175ae015502306b5e016710 Author: Michael Wu Date: Sun Dec 31 03:17:16 2006 -0500 p54: set phymode in RX This patch makes p54 report the phymode in RX so d80211 will report scan results correctly. Signed-off-by: Michael Wu commit 38756240a2fdf449988ed85b47ca577305fcd754 Author: Michael Wu Date: Sun Dec 31 03:16:10 2006 -0500 p54: fix issues found by sparse This fixes a number of issues in p54 found by sparse bitwise annotations. Signed-off-by: Michael Wu commit 05d1faf67c2523acfa7f9b216438bdb4d5f30b07 Author: Michael Wu Date: Sun Dec 24 22:18:59 2006 -0500 p54: use link LED This turns the link LED on when a valid BSSID is set. Signed-off-by: Michael Wu commit 867cabba000c261328a05c72113e8582cf42371f Author: Michael Wu Date: Sun Dec 24 19:05:15 2006 -0500 p54: use hardware RX frequency reporting This makes RX report the frequency/channel provided by the hardware. Signed-off-by: Michael Wu commit e95f4c1aff0a70a70269c171d47f29f2fb3d6f90 Author: Michael Wu Date: Sun Dec 24 13:07:56 2006 -0500 p54: remove unnecessary use of __constant_cpu_to_* This converts the use of __constant_cpu_to_* to cpu_to_* since it can be handled at compile time. Signed-off-by: Michael Wu commit f8de05d409320cdb046de17255cabe362068dc99 Author: Michael Wu Date: Sun Dec 24 12:48:51 2006 -0500 p54: fix TX of encrypted frames This fixes the TX code to report the proper frame size so encrypted frames will TX successfully now. Signed-off-by: Michael Wu commit 85b81223f65f3cc83aa8cdbdf34fe88fc3f99f3f Author: Michael Wu Date: Sun Dec 24 12:45:02 2006 -0500 p54: fix device memory allocator This patch fixes the address selection logic in p54_assign_address which eliminates the need for the TX antistalling hack in p54_rx_frame_sent. Signed-off-by: Michael Wu commit acc7a635825dfc3b077630d8be621504ac71637e Author: Jiri Benc Date: Mon Dec 18 21:31:09 2006 +0100 [PATCH] d80211: small documentation fix ieee80211_register_hwmode is allowed to be called before ieee80211_register_hw. Signed-off-by: Jiri Benc commit 9d037dc28e7f453a2dbda17284d78ae18d15f646 Author: Michael Buesch Date: Mon Dec 18 21:31:08 2006 +0100 [PATCH] d80211: constify ieee80211_ops pointer const-ify the ieee80211_ops pointer to allow * The compiler to do opimizations * The drivers to declare this structure const. Signed-off-by: Michael Buesch Signed-off-by: Jiri Benc commit f069487d2f2a306471ed1d5fca0ed5a00730ddef Author: Johannes Berg Date: Mon Dec 18 21:31:08 2006 +0100 [PATCH] d80211: add missing \n in skb queue warning This just adds a missing \n I noticed when I got the warning (see my other mail) Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc commit 65f2120cf1b41abd6720604ab6c41b80017d6227 Author: Michael Buesch Date: Fri Dec 15 14:50:28 2006 +0100 [PATCH] d80211: Turn PHYmode list from an array into a linked list This turns the PHY-modes list into a linked list. The advantage is that drivers can add modes dynamically, as they probe them and don't have to settle to a given arraysize at the beginning of probing. Signed-off-by: Michael Buesch Signed-off-by: Jiri Benc commit e874890656210a235fbcfe0935717f86e5d179d9 Author: Jiri Benc Date: Fri Dec 15 14:50:27 2006 +0100 [PATCH] d80211: simplify classify_1d The switch in classify_1d can be simplified to a bit operation. Signed-off-by: Jiri Benc commit d131a5bef49c2d56e0a7063aaa8753b4749144d1 Author: Zhu Yi Date: Fri Dec 15 14:50:27 2006 +0100 [PATCH] d80211: fix classify_1d() priority selection I don't see any reason why packets with DSCP=0x40 should have lower IEEE 802.1D priority than packets with DSCP=0x20. Spare > Background. No? Signed-off-by: Zhu Yi Signed-off-by: Jiri Benc commit 8b8a44f4b5f38e80de2acf02770cd6f2881ac6c3 Author: Michael Wu Date: Sun Dec 10 22:12:10 2006 -0500 zd1211rw-d80211: Use ieee80211_tx_status This makes zd1211rw-d80211 properly report the TX result of a frame via ieee80211_tx_status. I'm not sure if we can do much better than this since the hardware doesn't explicitly report the success/failure of TXed frames that require ACKs. We have to guess which ACKs match up with which frames we're trying to send. Signed-off-by: Michael Wu commit 134dcb620ee4218aa74ce56a917d50363118ca6d Author: Michael Wu Date: Tue Dec 5 18:47:30 2006 -0500 zd1211rw-d80211: check IEEE80211_TXCTL_USE_CTS_PROTECT This makes zd1211 check for IEEE80211_TXCTL_USE_CTS_PROTECT and set things appropriately in the hardware TX header. Signed-off-by: Michael Wu commit 4293f1af2d94bae5145c2f94bdf7463346b0491e Author: Michael Wu Date: Mon Dec 4 01:36:56 2006 -0500 zd1211rw-d80211: Optimized handling of zero length entries in length info This is a port of a patch for the zd1211rw driver, originally by Ulrich Kunitz . There are a high number of split USB transactions, which contain only one packet but have a length info field. This patch optimizes this code by stopping parsing the length info structure if a zero length field is encountered. Signed-off-by: Michael Wu commit cef953ef64ea37132bcc504cdf18be064ed86ce6 Author: Michael Wu Date: Mon Dec 4 01:36:51 2006 -0500 zd1211rw-d80211: cleanups This is a port of a patch for the zd1211rw driver, originally by Ulrich Kunitz . Bit-field constants in zd_chip.h are now defined using a shift expression. The value 0x08 is now (1 << 3). The fix is intended to improve readability. Remove unused code in zd_mac.c: The unused code intended for debugging rx_status values is no longer useful. Added dump_stack() to ZD_ASSERT macro: Output of the stack helps to debug assertions. Keep in mind that the ZD_ASSERT() macro only results in code, if DEBUG is defined. zd_usb.c: Added driver name to module init and exit functions Signed-off-by: Michael Wu commit 24afbc004731a5002f6b94a003aa9aee1c01e4ba Author: Michael Wu Date: Mon Dec 4 01:31:32 2006 -0500 zd1211rw-d80211: Add ID for Belkin F5D7050 v4000 This is a port of a patch for the zd1211rw driver, originally by Daniel Drake . zd1211b chip 050d:705c v4810 high 00-17-3f AL2230_RF pa0 g--N Tested by Bryan Barnard Signed-off-by: Michael Wu commit 6ad658e68b4222f52b8061e174eca083b0bf02ed Author: Michael Wu Date: Mon Dec 4 01:31:32 2006 -0500 zd1211rw-d80211: Add ID for Planex GW-US54Mini This is a port of a patch for the zd1211rw driver, originally by Daniel Drake . zd1211 chip 14ea:ab13 v4330 high 00-90-cc AL2230_RF pa0 g--- Tested by Tetsuya Yatagai. Signed-off-by: Michael Wu commit a4b4a4aa3b525a7102123e3fbea2e584da15e9ba Author: Michael Wu Date: Mon Dec 4 01:31:32 2006 -0500 zd1211rw-d80211: Add ID for ZyXEL G-220 This is a port of a patch for the zd1211rw driver, originally by Daniel Drake . Tested by Newsome on IRC zd1211 chip 0586:3401 v4330 high 00-13-49 AL2230_RF pa0 g--- Signed-off-by: Michael Wu commit f1f715c965b20f29eea8dc0abe4f52eb2038108d Author: Michael Wu Date: Mon Dec 4 01:31:32 2006 -0500 zd1211rw-d80211: Add 3 more device IDs This is a port of a patch for the zd1211rw driver, originally by Daniel Drake . iNexQ UR055g: Tested by Todor T Zviskov zd1211 chip 1435:0711 v4330 high 00-10-a7 AL2230_RF pa0 g-- ZyXEL AG-225, FCC ID SI5WUB410: Tested by Nathan zd1211 chip 0586:3409 v4810 full 00-13-49 AL7230B_RF pa0 g--- Yakumo QuickWLAN USB: Tested by EdB zd1211 chip 0b3b:1630 v4330 high 00-01-36 RF2959_RF pa0 --- Signed-off-by: Michael Wu commit bfa906cb7a64b2cf5e564ef0834a5015ac0c4a60 Author: Michael Wu Date: Mon Dec 4 01:31:10 2006 -0500 zd1211rw-d80211: Revert "[PATCH] zd1211rw: Removed unneeded packed attributes" This ports the removal of: "[PATCH] zd1211rw: Removed unneeded packed attributes" Quoth Daniel Drake : "A user reported that commit 4e1bbd846d00a245dcf78b6b331d8a9afed8e6d7 (Remove unneeded packed attributes) breaks the zd1211rw driver on ARM." Signed-off-by: Michael Wu commit e4a88827d686bc921232bbec6bd19c6b6b394255 Author: Michael Wu Date: Sun Dec 3 23:47:35 2006 -0500 zd1211rw-d80211: Fix of a locking bug This is a port of a patch for the zd1211rw driver, originally by Ulrich Kunitz . This patch fixes the bug as reported in the kernel bug tracker under the id 7244. The bug was simply that the interrupt lock has been locked outside an interrupt without blocking the interrupt. Signed-off-by: Michael Wu commit eabaa6c4f7b0546bbb5dcb9835d70d2858035bc0 Author: Michael Buesch Date: Wed Dec 13 19:27:08 2006 +0100 [PATCH] d80211: Fix 64bit printk warnings Fix several warnings due to incompatible datatypes on 64bit platforms. Signed-off-by: Michael Buesch Signed-off-by: Jiri Benc commit 0690925f2f99f6d43322ec8507eaabdcf1435c3c Author: Michael Buesch Date: Wed Dec 13 19:27:08 2006 +0100 [PATCH] d80211: Fix passing of invalid pointer ieee80211_hw pointers have to be passed to ops->set_key() and ops->get_tsf(). Signed-off-by: Michael Buesch Signed-off-by: Jiri Benc commit 4ae94181f808da96352478c6d4102e3b0b5dfaac Author: Michael Wu Date: Wed Dec 13 16:32:33 2006 +0100 [PATCH] d80211: move d80211_common.h to net/d80211 This moves d80211_common.h to net/d80211/ieee80211_common.h since d80211 drivers should not include this file. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit 3c00986b08e969cc141bd094dfd8e2f20340c8c7 Author: Michael Wu Date: Wed Dec 13 16:32:33 2006 +0100 [PATCH] d80211: merge d80211_mgmt.h into linux/ieee80211.h This merges d80211_mgmt.h with linux/ieee80211.h, to keep all the general 802.11 definitions in one place. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit 3dbbcd7c8aaec66320f38835ceea04f5ce4bc456 Author: Michael Wu Date: Wed Dec 13 16:32:33 2006 +0100 [PATCH] d80211: merge d80211_shared.h into d80211.h This merges d80211_shared.h into d80211.h. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit a2e9253024872101863b1557b116bc95cd2190ef Author: Michael Wu Date: Wed Dec 13 16:32:33 2006 +0100 [PATCH] d80211: move 802.11 defines to linux/ieee80211.h This moves 802.11 defines from net/d80211.h into linux/ieee80211.h. It also renames IEEE80211_DATA_LEN to IEEE80211_MAX_DATA_LEN to better match the other definitions. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit b1e98aabd2dbf6cd526b077626adbe0df2b37800 Author: John W. Linville Date: Mon Dec 11 21:07:02 2006 -0500 [PATCH] rt2x00: fix breaks from deleted rt2x00crc.h Patch that deleted rt2x00crc.h missed a couple of #include lines. Signed-off-by: John W. Linville commit d261dff34b79a055be18bee7d6ed622412e4f460 Author: Ivo van Doorn Date: Sun Dec 3 19:18:55 2006 +0100 [PATCH] rt2x00: Move CRC into seperate module Move the crc handling of rt61pci and rt73usb into a seperate module. This will create the crc-itu-t module inside the rt2x00 folder. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 87481f1f95061f88723e498c9e97f3e5c1d3749b Author: Ivo van Doorn Date: Sun Dec 3 19:18:55 2006 +0100 [PATCH] rt2x00: Compile fixes As usual, when I make a large patch series, I overlook important bits... This will fix all issues that have arisen from this patch series. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 0400054138b507ba193de53f6c35634b3334d093 Author: Ivo van Doorn Date: Sun Dec 3 19:18:56 2006 +0100 [PATCH] rt2x00: Misc. fixes Misc fixes. * Correctly set the RFCSR value using the setfield function. * Remove the DISABLE_RX register setting during initialization. * Changing the durationid should not add but overwrite. (sparse fix) * Prevent false warnings about ignoring ring initialization, by only mentioning which rings have been initialized. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit be31e81fdedbd1d0c021d1da761d01733513984e Author: Ivo van Doorn Date: Sun Dec 3 19:18:56 2006 +0100 [PATCH] rt2x00: Fix USB packet length and block promisc mode The length of a packet that needs to be send over a USB device, needs to have an even length. odd lengths will cause problems. At the moment there is no solution for the enabling of promisc mode on usb devices. The function is called from interrupt context which means the driver cannot access a register. Scheduling the request is required, but a clean solution needs to be found. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit f74fe0f60097fbbc3b847fa3747f6476930020a6 Author: Ivo van Doorn Date: Sun Dec 3 19:18:57 2006 +0100 [PATCH] rt2x00: Fix various initialization problems Always use kzalloc instead of kmalloc. Remove duplicate init functions. And destroy the workqueue before freeing resources, otherwise a thread on the queue might still want to access that resource. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit b91c14c6d0eee5281dc852942044316078ff3b8d Author: Ivo van Doorn Date: Sun Dec 3 19:18:57 2006 +0100 [PATCH] rt2x00: Fix txdone race condition Always call ieee80211_wake_queue if the ring is not full after the txrun. The ieee80211_wake_queue is responsible for chacking if the queue was stopped or not. The current implementation of checking the ring_full before the txdone run was flawed and race conditions could occur that blocked all tx handling. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 3378b1fb6490462a0654c967a4518550abac8a80 Author: Ivo van Doorn Date: Sun Dec 3 19:18:58 2006 +0100 [PATCH] rt2x00: Call activity_led() Call activity_led() function after each rxdone run to make the led blink. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit c375d29f16ee6dd91b22cc833dcfedfb98266f42 Author: Ivo van Doorn Date: Sun Dec 3 19:18:58 2006 +0100 [PATCH] rt2x00: Fix channel_change_time calculation Correctly initialize the channel_change_time. Make sure that channel is reset afterwards, otherwise the channel is not correctly initialized and rx/tx will fail. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit c149c1737be53742dcf18366298e6cbdfcd1ce0a Author: Ivo van Doorn Date: Sun Dec 3 19:18:59 2006 +0100 [PATCH] rt2x00: Simplify MAC copying No set_field commands are required for the mac registers. This was previously done for the byteordering. But since the MAC is already read in the correct byteorder this had never had to happen at all anyway. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit c56506652557caa84a3ada1ce6aed9c203365be3 Author: Ivo van Doorn Date: Sun Dec 3 19:18:59 2006 +0100 [PATCH] rt2x00: Move rt2x00usb_vendor_request out of header Remove the rt2x00usb_vendor_request from the rt2x00usb.h header, and place it into the rt2x00_vendor_request. This means that the rt2x00_vendor_request function needs a timeout value especially for commands that require a lot of time (i.e. Firmware writing). Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 98e26cfd852467d627b97ffc01125e8283dd6218 Author: Ivo van Doorn Date: Sun Dec 3 19:19:00 2006 +0100 [PATCH] rt2x00: Correctly handle RTS frames Correctly handle rts frames in txdone, by freeing the packet. Also use the is_rts_frame to detect if the frame was rts. This was done incorrectly previously. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 2f0ab4bd867b3d01b8d1515475b1e9dbe9c1c400 Author: Ivo van Doorn Date: Sun Dec 3 19:19:00 2006 +0100 [PATCH] rt2x00: Add SIFS/PIFS/DIFS/EIFS defines Introduce new defines for the SIFS, PIFS, EIFS, DIFS, and make use of it in the drivers. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit d412de332a46756be2cff269a3957e828021dd5e Author: Ivo van Doorn Date: Sun Dec 3 19:19:01 2006 +0100 [PATCH] rt2x00: Add more statistics readin Make sure all statistics the d80211 stack requires. Some of this requires values to be read during interrupt process (add a new function to handle this). And other fields can be read from the registers at request time. Note that rt61pci and rt73usb had an invalid registername the legacy drivers are suggesting the CRC_ERROR count is actually the FCS_ERROR count. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 0c7af5d93bd3d8e0e63cd8a98376aa96e52a57be Author: Ivo van Doorn Date: Sun Dec 3 19:19:01 2006 +0100 [PATCH] rt2x00: RX rate conversion Each received packet has a signal field, this field can be translated into the rate with which the frame has been received. Create a seperate function for this since the conversion is equal for all drivers. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit cde5b123e480a6bc02399d18580c230be8ecb50e Author: Ivo van Doorn Date: Mon Dec 11 20:55:54 2006 -0500 [PATCH] rt2x00: Interface initialization Correctly let the non-monitor and monitor interfaces excist peacefully together. Make sure the configuration is always accurate and allows the correct packets to come through, let the interface enable the radio at the correct time etc. etc. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit e66e72a41cbd362af0195b36a101f8c35dc1aadd Author: Ivo van Doorn Date: Sun Dec 3 19:19:02 2006 +0100 [PATCH] rt2x00: Put link tuning on workqueue Put the link tuning in a workqueue, this prevents the interrupthandlers from being busy for a too long period and blocking new inetrrupt handling. To do this correctly we add a link structure containing all information regarding the link status. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 7bd4d2341c80a094e1451bcf059ccad5c331f955 Author: Ivo van Doorn Date: Sun Dec 3 19:19:02 2006 +0100 [PATCH] rt2x00: WMM ring priority rt61pci and rt73usb have the WMM ring priorities backwards. RING_AC_VO is the most important ring while RING_AC_BK the least important ring. Lets reorder the ring handling. (And fix some small typos in the comments regarding the rings) Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 2230578adeb395cd6ae01a80842655b7fcea9a20 Author: Ivo van Doorn Date: Sun Dec 3 19:19:03 2006 +0100 [PATCH] rt2x00: USB eeprom offset We work with the EEPROM by using the word number as offset. Fix USB drivers to use the correct offset. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 2c3ab31424e81aa3c2c3db5d35143a19dab71760 Author: Ivo van Doorn Date: Sun Dec 3 19:19:03 2006 +0100 [PATCH] rt2x00: Rssi detection Correctly detect the maxssi settings from the EEPROM where available, and correct the dummy values that had been added in the initial patch to support noise and signal measurement. MAX_RX_SSI is a value that is different on each chipset, so it should be a driver specific define. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 5ce7717fa9162b562244bbdf2a519f1c077fa6ae Author: Ivo van Doorn Date: Sun Dec 3 19:19:04 2006 +0100 [PATCH] rt2x00: BBP busy check rt61pci and rt73usb legacy drivers have hinted that there are race conditions with the bbp register handling. This must be fixed by doing a busy check before the actual read command. At the same time we can remove duplicate code by putting that busy check into a seperate function. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit bb39821cd7a573e9d6cb6bbdae5c8489bf8add2b Author: Ivo van Doorn Date: Mon Dec 11 20:52:57 2006 -0500 [PATCH] rt2x00: compile fix for d80211 update Fix rt2x00 compilation problems due to the d80211 update. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 294994cbf0c5f7ff7b2decb785d73a566326fe2a Author: Ivo van Doorn Date: Mon Dec 11 20:51:15 2006 -0500 [PATCH] rt2x00: Byte ordering Overhaul the byteordering mechanism. All byteordering happens at the reading and writing of the register/eeprom/descriptor instead of the get/set_field functions. This makes sparse very happy and reduces the errors significantly. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 1588496d1f832b97338b1cceee86beffcb9a4893 Author: Ivo van Doorn Date: Mon Dec 11 20:49:11 2006 -0500 [PATCH] rt2x00: EEPROM 93Cx6 rt2400pci, rt2500pci and rt61pci share exactly the same code for the eeprom reading. The only difference is that rt61pci has a slightly different register reading approach. In any case we have a lot of duplicate code. Create a new module eeprom_93cx6 inside the rt2x00 folder and make rt2x00 use that. As a bonus the entire eeprom is read into an array to optimize eeprom usage. This also enables dummy eeprom writing where the temporary buffer can be manipulated by the user without permanently harming the eeprom. This feature should have been enabled through ethtool, but 3 days after this patch ethtool was removed from d80211. This feature will be used when debugfs has been implemented. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit aeb1ba21bbb540b1ad4cc6effd33e3d17cecbf6d Author: Ivo van Doorn Date: Mon Dec 11 20:45:36 2006 -0500 [PATCH] rt2x00: descriptors Remove txd and rxd descriptor structures. All access to the descriptors should be done by treating the descriptor as a register. For this we do add a desc structure containing a array, and several methods for reading and writing to that array. This is prepares rt2x00 for the overhaul of the byteordering mechanism. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 43d48e6642adcf5ff578b1df8ac28a85f81495ac Author: Ivo van Doorn Date: Sun Dec 3 19:19:05 2006 +0100 [PATCH] rt2x00: device IDs Add new rt2500usb and rt73usb device id numbers. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 3794660151b43a1f478ff378c8ec81e04397de5c Author: Ivo van Doorn Date: Sun Dec 3 19:19:05 2006 +0100 [PATCH] rt2x00: ethtool Latest d80211 stack no longer provides any ethtool support. At the moment there is no quick replacement possible for the ethtool features (debugfs is under investigation, but requires more work). Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 13ec82516b24c59b79ff929e9ece355830209b4a Author: Jiri Benc Date: Mon Dec 11 19:35:20 2006 -0500 [PATCH] d80211: Reset assoc and auth retry counters (alternate fix) On Tue, 28 Nov 2006 20:56:05 +0100, Ivo van Doorn wrote: > After a succesfull authentication and association the matching retry counter > must be reset to 0. > Failure to do so will result in failure to authenticate after the interface > has been deauthenticated. This does not always happen after the first > deauthentication, but after the interface has been several times been > deauthenticated it will refuse to authenticate. Thanks for spotting this, but your fix makes statistics about authentication/association exported via sysfs useless. The counters should be reset before a new authentication/association attempt (as is done in ieee80211_sta_new_auth). I think this is a more correct fix: Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit cca5b9bc1f21e1d9a616e0f8e2fea04140a51a07 Author: Michael Buesch Date: Mon Dec 11 12:09:42 2006 +0100 [PATCH] d80211: Fix errorcode in ieee80211_update_hw ieee80211_update_hw should return a proper error code instead of hardcoded -1. Signed-off-by: Michael Buesch Signed-off-by: Jiri Benc commit 67e20838fff9a011cb83a02ca21d39d73313a42f Author: Michael Wu Date: Mon Dec 11 12:09:42 2006 +0100 [PATCH] d80211: remove pkt_type/pkt_probe_resp Nobody uses pkt_type, and the information can be obtained from the header. This removes it and the associated code that keeps tracks of it. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit 6b29100c7e405c835eac8ebff2ff11774763da05 Author: Mohamed Abbas Date: Fri Dec 8 13:07:20 2006 +0100 [PATCH] d80211: add mising sta_info_put function This is small patch adding missing sta_info_put function. Signed-off-by: Mohamed Abbas Signed-off-by: Jiri Benc commit 0efae98fe404771fc3660f1d6023ab692149ddec Author: David Kimdon Date: Fri Dec 8 13:07:20 2006 +0100 [PATCH] d80211: do not pass an invalid key index to set_key() d80211: do not pass an invalid key index to set_key() If a hardware key has not been configured then there is no point to calling DISABLE_KEY. Signed-off-by: David Kimdon Signed-off-by: Jiri Benc commit 7511a31ac07b8cb4141fe2d47abd55b9875b84a9 Author: David Kimdon Date: Thu Dec 7 13:49:14 2006 +0100 [PATCH] d80211: fix invalid check for sub interface type AP We should be checking the type member, not the raw pointer. Signed-off-by: David Kimdon Signed-off-by: Jiri Benc commit c2f74bcf14ecc48919bcdfa79ff0adaec8afb25e Author: David Kimdon Date: Thu Dec 7 13:49:14 2006 +0100 [PATCH] d80211: remove unused references to sub interface data In these three cases the pointer returned by IEEE80211_DEV_TO_SUB_IF() is never used. Signed-off-by: David Kimdon Signed-off-by: Jiri Benc commit 11355b4c0aadecd31e43c5e6d5de652030aa0cf5 Author: David Kimdon Date: Thu Dec 7 13:49:13 2006 +0100 [PATCH] d80211: fix potential invalid array index returning key information sdata->keys[] has NUM_DEFAULT_KEYS elements, don't access past that. Signed-off-by: David Kimdon Signed-off-by: Jiri Benc commit e268081fcce1027a23420bfe74c59bf130742bce Author: David Kimdon Date: Thu Dec 7 13:49:13 2006 +0100 [PATCH] d80211: fix potential interface name overflow dev->name and ndev->name are both IFNAMSIZ in length, the ".%d" is not guarenteed to fit in ndev->name. Signed-off-by: David Kimdon Signed-off-by: Jiri Benc commit 3498e00a7cb36a9e309bf4aefd1a012a7971afb3 Author: David Kimdon Date: Thu Dec 7 13:49:13 2006 +0100 [PATCH] d80211: set default_wep_only dynamically Without this change d80211 relies on userspace to let it know when it can configure default wep keys. It is always safe to set default_wep_only if there is a single station interface. This allows for hardware accelleration for the case of a single station interface. Signed-off-by: David Kimdon Signed-off-by: Jiri Benc commit 281859d5a1d5004285bf806286600fa49ecb117b Author: David Kimdon Date: Thu Dec 7 13:49:12 2006 +0100 [PATCH] d80211: allow for hardware crypto of default keys Remove incorrect prohibition of hardware crypto support. This was originally present to prevent hardware crypto when more than one station interface was created for a single hardware device, the code in question is no longer correct and should be removed. Signed-off-by: David Kimdon Signed-off-by: Jiri Benc commit 6b288aad8478cd72d968f0f697cb45d670e8a118 Author: Michael Wu Date: Sat Dec 2 02:25:26 2006 -0500 p54: Don't send startup packet again on next ifup (USB) This prevents the USB backend from sending the startup packet when it has been sent already, so the firmware doesn't get confused. Also, it adds some helpful printks for when things go wrong. Signed-off-by: Michael Wu commit 48e9bd0026e8c8cd374b38752ad2369ee5bcb5a1 Author: Michael Wu Date: Sat Dec 2 02:16:27 2006 -0500 zd1211-d80211: Port zd1211 to Devicescape stack This patch actually does the porting. I've avoided code cleanups in this patch as much as possible to make porting patches for the softmac based zd1211 driver to this one as easy as possible, but some major surgery was still necessary to make this work. There was one major problem with porting zd1211 - the tx callbacks do not indicate whether or not the TX of a particular frame was successful or not, and there is no apparent way to easily obtain that information. Thus, this patch does not bother telling the 802.11 stack whether or not a frame was successfully TXed. It still works but causes ping to report round-trip times that rival ping round-trip times to localhost. Otherwise, this should be fully functional for STA mode. Adhoc and monitor mode will come later. Signed-off-by: Michael Wu commit 01b860217caa5830a290e9166f08639bdb24d3e7 Author: Michael Wu Date: Sat Dec 2 02:00:12 2006 -0500 zd1211-d80211: Hook up Kconfig and Makefiles This integrates the zd1211 directory into the d80211 Kconfig and Makefiles. Signed-off-by: Michael Wu commit 71d055cd44610cc3cd5aa3644f9f354c78e78a66 Author: Michael Wu Date: Sat Dec 2 01:55:00 2006 -0500 zd1211-d80211: Copy zd1211 driver to d80211 directory This copies the zd1211 directory to the d80211 directory in preparation for a port to the Devicescape 802.11 stack. Signed-off-by: Michael Wu commit e3d2ac8a6032cd4510a069d4ec1b009b096cb6f9 Author: Johannes Berg Date: Sun Nov 19 20:31:01 2006 +0100 [PATCH] bcm43xx: update to new d80211 driver API Updates bcm43xx for d80211 API changes. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit e49b6b7b4674bd0a7a1c2bcae2439baa5a9787b1 Author: Ivo van Doorn Date: Tue Nov 28 20:56:05 2006 +0100 [PATCH] d80211: Reset assoc and auth retry counters After a succesfull authentication and association the matching retry counter must be reset to 0. Failure to do so will result in failure to authenticate after the interface has been deauthenticated. This does not always happen after the first deauthentication, but after the interface has been several times been deauthenticated it will refuse to authenticate. Signed-off-by Ivo van Doorn Signed-off-by: John W. Linville commit e37e7b95d2db5c2e7f5fb3a21ade0dd6de43a41d Author: Michael Buesch Date: Thu Nov 16 15:07:33 2006 +0100 [PATCH] bcm43xx-d80211: fix hwcrypto issues (mcast) This fixes various bcm43xx-d80211 hwcrypto issues, which mainly prevented mcast frames from being decrypted properly. This is mostly a rewrite of the key managing code. Note that after this patch v3 firmware is no longer supported. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 9017cd648b8d662f0e24332123f31183204d5dbd Author: Michael Wu Date: Mon Nov 27 12:50:15 2006 -0500 p54: Adjust/add printks in PCI driver This adds a printk that shows the MAC address and hardware type after successfully probing the card, and adjusts two other printks to be consistent with everything else. Signed-off-by: Michael Wu commit 4b49614de5a4e49f6e460499bf892d3aa124a4df Author: Michael Wu Date: Sun Nov 26 22:42:34 2006 -0500 p54: Add support for net2280 based USB devices This adds support for net2280 based devices to the USB backend, and cleans up some code. It also adds some code to prism54common to make things work. Signed-off-by: Michael Wu commit 1ef81d8e28c9079dd4854ab2726511ad1bc8b7d5 Author: Michael Wu Date: Thu Nov 23 23:04:08 2006 -0500 p54: Convert to new API This makes p54 compile again after the recent d80211 API changes. Signed-off-by: Michael Wu commit d0265cfbf3abe3202df61a5fc09382edba0af387 Author: Michael Wu Date: Thu Nov 23 23:02:26 2006 -0500 adm8211: Convert to new API This makes adm8211 compile again after the recent d80211 API changes. Some stats updating was commented out since there is no apparent way to do it properly with the new API. Signed-off-by: Michael Wu commit 6d7c5b83d4ed3c9923f4a31992c6e099f4988313 Author: Michael Wu Date: Thu Nov 23 22:56:49 2006 -0500 adm8211: Reduce delays This eliminates a hardware reset on taking the interface down and makes the delays for the hardware reset smaller, thus reducing the amount of time the driver busywaits. Signed-off-by: Michael Wu commit 6c3428a04215b4eef1a1a8e0294566850d72978b Author: Michael Wu Date: Thu Nov 23 22:50:21 2006 -0500 Update MAINTAINERS entries for wireless drivers This adds an entry to MAINTAINERS for the p54 driver, adds a git repo to the adm8211 entry, and indents the rt2x00 entry to look like all the other entries. Signed-off-by: Michael Wu commit b9aaa4fe6796db377d34680275dc01235aa1b188 Author: Michael Wu Date: Thu Nov 23 22:32:28 2006 -0500 p54: Move eeprom readback packet filling code to common This moves the eeprom readback header filling code to the common code and out of the pci and usb backends. Signed-off-by: Michael Wu commit 5408aefae7b42a66cdbb180961a9b6d2413894fe Author: Michael Wu Date: Thu Nov 23 22:26:30 2006 -0500 p54: Add license boilerplate This adds some copyright/license boilerplates as requested by Jean-Baptiste Note . Signed-off-by: Michael Wu commit d21326b2d9b0d80caf2e4d70a8e53e6ef13b1811 Author: Michael Wu Date: Thu Nov 23 22:19:48 2006 -0500 p54: Add PCI driver This adds a PCI backend to the p54 driver, plus some extra code in p54common to make it work. Supposedly, the softmac firmware does not work with certain PCI prism54 cards, so beware. However, it works fine on my isl3890 and isl3892. Signed-off-by: Michael Wu commit 4bdaf704cda50e874a0ac273991d97857fb03599 Author: Michael Wu Date: Thu Nov 23 22:06:45 2006 -0500 p54: Set dummy maxssi to prevent a divide by zero. Set hw->maxssi to 100 for now to prevent dividing by zero. Signed-off-by: Michael Wu commit 60bb68a3a11112dec41607bdd9882dc71935d223 Author: Johannes Berg Date: Wed Nov 22 17:14:42 2006 +0100 [PATCH] d80211: use ieee80211_hw.dev This fixes up my earlier patches by actually using the dev field in struct ieee80211_hw. Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc commit 1e67847c7d58761d66c4d5c9022dd133a9af88a3 Author: Johannes Berg Date: Mon Nov 20 20:35:53 2006 +0100 [PATCH] d80211: remove calib_int The calibration interval is far too hardware dependent to be useful as a generic stack setting and some hardware doesn't even have that parameter. Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc commit 4044b252cbf02716e5d14aefa102e5cb0613f122 Author: Johannes Berg Date: Mon Nov 20 20:35:53 2006 +0100 [PATCH] d80211: remove IEEE80211_CONF_SW_{EN,DE}CRYPT There's no point in trying to tell a driver globally whether sw or hw crypto is used, if it's sw then we just don't give it keys... Besides, these weren't ever used! Remove IEEE80211_CONF_SW_DECRYPT and IEEE80211_CONF_SW_ENCRYPT. Acked-by: Michael Buesch Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc commit 3641ff21c480e20c9d31f49acc6329b91716e01a Author: Johannes Berg Date: Mon Nov 20 20:35:53 2006 +0100 [PATCH] d80211: remove useless driver name field struct ieee80211_ops has a driver name field that's never used. Remove it. Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc commit a61308a01b78c8bd8891c35833eb42c3e287686c Author: Johannes Berg Date: Mon Nov 20 20:35:52 2006 +0100 [PATCH] d80211: add a perm_addr hardware property After removing knowledge of the master net_dev from drivers, they'll still need a way to tell us which MAC address they have. This is that way, the perm_addr is initially used for all devices. Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc commit cc84885f6d069d57e6441fed53106f384b3e2bf8 Author: Johannes Berg Date: Mon Nov 20 20:35:52 2006 +0100 [PATCH] d80211: change the identifier netdev to ieee80211_hw Traditionally, drivers were given a struct net_device * in order to identify the wireless device. This was the master device, but I'm trying to cut down it's use. Now, there long was a comment that this might change. That time has come, this patch gives back a struct ieee80211_hw pointer. Currently, struct ieee80211_hw contains both static data (almost all of the function pointers except one) and data that could possibly be per-device even for a single driver. Hence patch also introduces struct ieee80211_ops and moves the function pointers from ieee80211_hw into it. This makes ieee80211_hw be the pure hardware description and allows drivers to make have their ieee80211_ops static, thereby reducing the struct size significantly. Note that the patch changes the meaning of ieee80211_hw, previously it was allocated by the driver and given to the stack as a hardware description, now it is allocated by ieee80211alloc_hw() and then the driver fills it before calling ieee80211_register_hw(). A later patch fixes the FIXME introduced here where hw fragmentation is checked by having a function assigned or not---if functions are supposed to be assigned now for all hw we need a new flag for that if some driver has boards that can and other boards that cannot support it. Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc commit 5771853eeb83b4a7c85345187c3cc2850d4625ef Author: Johannes Berg Date: Mon Nov 20 20:35:52 2006 +0100 [PATCH] d80211: reduce master ieee80211_ptr deref in scan routines This patch changes a bunch of prototypes to have struct ieee80211_local* instead of struct net_device* where that makes sense. Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc commit 47626ade8a986fbb11fe4151df9bec2175c69511 Author: Johannes Berg Date: Mon Nov 20 20:35:52 2006 +0100 [PATCH] d80211: reduce mdev usage, change ieee80211_rx_mgmt This patch reduces mdev usage by replacing struct net_device * arguments that are never used except for dereferencing to get struct ieee80211_local from ieee80211_ptr by struct ieee80211_local directly. Also, this patch changes ieee80211_rx_mgmt to no longer be callable when local->apdev is NULL. All callers are updated accordingly, in most cases actually increasing performance by not allocating skbs when they won't be given to anyone anyway. Instead of abusing ieee80211_rx_mgmt also introduce ieee80211_rx_monitor. Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc commit e3562b9e024ae47ac58ca29da177ba972fc32e38 Author: Johannes Berg Date: Mon Nov 20 20:35:51 2006 +0100 [PATCH] d80211: reduce mdev usage This patch reduces mdev usage by replacing struct net_device * arguments that are never used except for dereferencing to get struct ieee80211_local from ieee80211_ptr by struct ieee80211_local directly. Also removes ->master from sub_if_data. Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc commit b8f8db7b5f04a0aa06941e37709a3a5afa6dd7f0 Author: Johannes Berg Date: Mon Nov 20 20:35:51 2006 +0100 [PATCH] d80211: reduce mdev usage This patch reduces mdev usage by replacing struct net_device * arguments that are never used except for dereferencing to get struct ieee80211_local from ieee80211_ptr by struct ieee80211_local directly. Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc commit a15169b371fd891e13b1ee44e760b79ebf8300cc Author: Johannes Berg Date: Mon Nov 20 20:35:50 2006 +0100 [PATCH] d80211: clean up some stupid list and loop code "for (; condition ;)"?? Ever heard of while loops? Also clean up some list handling (still. *sigh*) Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc commit 3fdea01bf9e14f1c3edcffd1c80939749d125a24 Author: David Kimdon Date: Mon Nov 20 20:35:50 2006 +0100 [PATCH] d80211: Remove unused ENABLE_COMPRESSION, DISABLE_COMPRESSION cmds to hw->set_key These two commands are currently unused. They were previously used to enable hardware compression on Atheros hardware. Signed-off-by: David Kimdon Signed-off-by: Jiri Benc commit 39d58f9228dee66c22ce1b1d314c8012b892ea4d Author: Hong Liu Date: Tue Nov 14 10:22:58 2006 +0800 [PATCH] d80211: hardware TKIP support ipw3945 TKIP hwcrypto only support RC4 encryption, so the stack needs to pre compute the michael MIC and the RC4key for it. Resend the patch according to Johannes's comments. Still put the tkip_key in tx_control structure. Signed-off-by: Hong Liu Signed-off-by: John W. Linville commit 7b7a57a8b8a4351e90cdbf2d93ef9e28a07a8f94 Author: David Kimdon Date: Sun Nov 12 10:15:05 2006 -0800 [PATCH] d80211: fix usage of capability field for ibss mode Thanks to sparse for pointing out these errors. 'capability' is stored in struct ieee80211_sta_bss in host byte order, do not swap bytes. Signed-off-by: David Kimdon Signed-off-by: John W. Linville commit 8cbe59ce92269cae5d6e98af5a1e211e5b2398b5 Author: David Kimdon Date: Sun Nov 12 10:11:01 2006 -0800 [PATCH] d80211: endian annotations for ieee80211_frame_info, etc. Thanks to sparse for pointing out these missing endian annotations. All the fields in the AVS capture header (struct ieee80211_frame_info) are in network byte order. The length in the ethernet header is in network byte order. last_seq_ctrl is stored little endian. Signed-off-by: David Kimdon Signed-off-by: John W. Linville commit cf120b4c21494902a58f6a392030ca88fee55ed8 Author: David Kimdon Date: Sun Nov 12 09:52:53 2006 -0800 [PATCH] d80211: remove bitfields from ieee80211_conf All four one-bit bitfields have been subsumed into the new 'flags' structure member and the new IEEE80211_CONF_* definitions. Signed-off-by: David Kimdon Signed-off-by: John W. Linville commit 63ca673ba115a3417731ba27ceeb965c6b46ad77 Author: David Kimdon Date: Sun Nov 12 09:52:49 2006 -0800 [PATCH] d80211: remove bitfields from ieee80211_hw All twelve one-bit bitfields have been subsumed into the new 'flags' structure member and the new IEEE80211_HW_* definitions. Signed-off-by: David Kimdon Signed-off-by: John W. Linville commit 300f951c5fa691c3b2e84c59b7b21e77d6edd0ac Author: David Kimdon Date: Sun Nov 12 09:52:46 2006 -0800 [PATCH] d80211: remove bitfields from ieee80211_key_conf All three one-bit bitfields have been subsumed into the new 'flags' structure member and the new IEEE80211_KEY_* definitions. The 8 bit keyidx bitfield is converted to type s8. Signed-off-by: David Kimdon Signed-off-by: John W. Linville commit 9ab613230c87fe5c72225c8531a38c1efd75d57e Author: David Kimdon Date: Sun Nov 12 09:52:42 2006 -0800 [PATCH] d80211: remove bitfields from ieee80211_tx_status Both one-bit bitfields have been subsumed into the new 'flags' structure member and the new IEEE80211_TX_STATUS_* definitions. Signed-off-by: David Kimdon Signed-off-by: John W. Linville commit 6b3e1ed02ff36400f3bda5dbf47d9aba95cf7d34 Author: David Kimdon Date: Sun Nov 12 09:52:38 2006 -0800 [PATCH] d80211: remove bitfields from ieee80211_tx_control All one-bit bitfields have been subsumed into the new 'flags' structure member and the new IEEE80211_TXCTL_* definitions. The multiple bit members were converted to u8, s8 or u16 as appropriate. Signed-off-by: David Kimdon Signed-off-by: John W. Linville commit 774f233b7915a2c36480eb4d98e6f57938f04b7b Author: Johannes Berg Date: Tue Nov 7 18:06:22 2006 -0500 [PATCH] cfg80211: fix WE compat code The example set_essid I implemented returns -EINVAL in case there are no cfg80211 handlers. This will be returned to userspace. Return -ENOSYS instead to try the original WE. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit 4b5ddcb1f13f937c2677331b7cd6e3c31164c019 Author: Johannes Berg Date: Tue Nov 7 18:03:01 2006 -0500 [PATCH] cfg80211: fix Makefile brokenness This patch fixes the Makefile in net/wireless/ to compile wext-common.c whenever it is needed. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit 065be0509a70dc152eb09b23ec075c6c8d3de02e Author: Michael Buesch Date: Tue Nov 7 20:36:53 2006 +0100 bcm43xx-d80211: Remove netpoll and ethtool stuff. Signed-off-by: Michael Buesch commit cbac49d513ef95f596003d2577cf0c588fd705d5 Author: Michael Buesch Date: Tue Nov 7 20:03:23 2006 +0100 bcm43xx-d80211: Fix bogus LO validation failure. Signed-off-by: Michael Buesch commit 1dfadcf88de93444a5800013c25281cf06c8b2fb Author: Michael Buesch Date: Tue Nov 7 19:16:16 2006 +0100 bcm43xx-d80211: Fix antenna selection for TX and RX. This fixes antenna selection on TX and RX of normal frames and also on TX of firmware generated frames. Signed-off-by: Michael Buesch commit ff55a09d8974844b07cd633dc3fff10ba6e97fb7 Author: Michael Buesch Date: Sat Nov 4 20:55:04 2006 +0100 bcm43xx-d80211: Drain TXstatus queue before enabling IRQs. The microcode TXstatus queue might have old entries pending from a previous run. Drain them, as they would fire immediately after enabling IRQs. This would result in a crash in the TXstatus handling code because of bad cookies. Signed-off-by: Michael Buesch commit ccd72f1177b2ce29a5fab621eeb903fc1a304d54 Author: Larry Finger Date: Thu Nov 2 12:48:11 2006 -0600 [PATCH] rt2x00-d80211: Add wireless statistics These patches modify the rt2x00-d80211 family of drivers to use the wireless statistics. Signed-Off-By: Larry Finger@lwfinger.net> Signed-off-by: John W. Linville commit 0095dbae85569819b64bf615e304f4ee83b62ed2 Author: Larry Finger Date: Thu Nov 2 12:47:40 2006 -0600 [PATCH] adm8211-d80211: Add wireless statistics These patches modify adm8211-d80211 to use the wireless statistics. Signed-Off-By: Larry Finger@lwfinger.net> Signed-off-by: John W. Linville commit ae81ab108f22b77528494bf2139200ce3c537edb Author: Larry Finger Date: Thu Nov 2 12:46:48 2006 -0600 [PATCH] bcm43xx-d80211: Add wireless statistics These patches modify bcm43xx-d80211 to use the wireless statistics. Signed-Off-By: Larry Finger@lwfinger.net> Signed-off-by: John W. Linville commit 448bf25bc9e3d70a211fdf235426472089371c43 Author: Larry Finger Date: Thu Nov 2 12:45:43 2006 -0600 [PATCH] d80211: Add wireless statistics This patch modifies d80211 to support wireless statistics. Signed-Off-By: Larry Finger Signed-off-by: John W. Linville commit 6dc376806716a20314ddc9d4884eb5e107226e71 Author: Michael Wu Date: Thu Nov 2 21:01:23 2006 -0500 [PATCH] adm8211: fix suspend code Apparently, I forgot to port the suspend and resume code in the d80211 port of adm8211. Thanks to Johannes Berg for finding this. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit 0fa8f4e9ebe9fd1d05150fc326a381eb07d0b25a Author: David Kimdon Date: Mon Oct 30 10:08:58 2006 -0800 [PATCH] d80211: switch crypto to use new ciphers API Switch d80211 software crypto to use the new cipher API. Signed-off-by: David Kimdon Acked-by: Herbert Xu Signed-off-by: John W. Linville commit 10bfc9cdf9621385a3b69aa35f9fa86cc6a46bc6 Author: David Kimdon Date: Wed Oct 25 11:16:21 2006 -0700 [PATCH] d80211: remove unused variable in ieee80211_rx_irqsafe tmp is unused. Signed-off-by: David Kimdon Signed-off-by: John W. Linville commit 2519b9a1a90b1d2d4583d33e8d8c15753cdf1829 Author: Michael Wu Date: Thu Oct 19 02:13:47 2006 -0400 [PATCH] adm8211, p54: set freq in ieee80211_rx_status This patch fixes the RX handler in adm8211 and p54 to report the current frequency and channel. Should probably be handled in d80211 instead, but this will fix things for now. It also eliminates some definitions in adm8211.h that are no longer necessary. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit 522e078b9f1f8309770dd161d90ddac1573a7877 Author: Jiri Benc Date: Thu Oct 19 18:25:17 2006 +0200 [PATCH] d80211: extend extra_hdr_room to be a bytecount On Wed, 11 Oct 2006 07:59:23 -0700, David Kimdon wrote: > Perhaps rename it to extra_tx_headroom? > - existing users would then need to take notice of the change > - the name 'extra_tx_headroom' is more descriptive of what it actually is Extend ieee80211_hw's extra_hdr_room to be a bytecount for a device specific TX header instead of being a hardcoded 0/2 byte choice. Based on the patch by Michael Buesch . Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit 709d2483b226439dd95278efb7178e741d039d50 Author: Michael Buesch Date: Thu Nov 2 20:32:33 2006 -0500 [PATCH] bcm43xx: Don't disable IRQs in PHY init. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit ec3dfeaac2c4703589ec63e1ca652324b899df69 Author: Michael Wu Date: Thu Oct 19 02:22:38 2006 -0400 [PATCH] p54: fix stalling in TX queue p54: fix stalling in TX queue This patch makes the p54 TX queue not stall anymore. Probably not the most efficient thing to do, but it's better than stalling. It also adds a small comment to prism54common.h about the origin of the pda definitions and inserts a missing verb in the comment for p54_assign_address. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit 239e917fd5f6a3b613fe715c808e30f0508f6130 Author: Michael Wu Date: Thu Nov 2 19:49:42 2006 -0500 [PATCH] d80211: add p54 driver The attached patch adds support for 3887 based prism54 usb wireless adaptors. It is partially based on the islsm driver by Jean-Baptiste Note, but most of the code is new (and uses d80211 instead of madwifi). It doesn't work perfectly yet, but it can connect to an unsecure AP and send/receive packets as long as you don't hit the tx code too hard. Further hardware support for pci and net2280 usb will follow later. Also, I am unable to make it crash. Thus, it should be in a suitable state for other developers to hack on. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit e802a146efc9d5a638f7e3d1b7996f529d8adabe Author: John W. Linville Date: Thu Nov 2 19:10:37 2006 -0500 [PATCH] cfg80211: add description text to Kconfig tristate line Signed-off-by: John W. Linville commit 932872a134da902757660920ac55d43f17d12de5 Author: Johannes Berg Date: Thu Nov 2 18:22:27 2006 -0500 [PATCH] cfg80211: add wext-compatible client Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit 067d869bb450ae7554fbbe9bad42482d7d25760a Author: Johannes Berg Date: Thu Nov 2 18:16:33 2006 -0500 [PATCH] wireless: move wext to net/wireless/ This patch moves net/core/wireless.c to net/wireless/wext.c. I refrained from further cleanups though I was tempted. Hence this is a pure file move plus various build system adjustments. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit 0c256246ba47def9aa2db533976b67e98980d2da Author: Johannes Berg Date: Thu Nov 2 18:11:08 2006 -0500 [PATCH] cfg80211 and nl80211 This patch adds cfg80211, a new configuration system for wireless hardware as well as nl80211, the netlink-based userspace interface for it. It currently features a bunch of configuration requests, support for adding and removing virtual interfaces, the ability to inject packets and more. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit 41242496aa44ec5c31d4ab11baa349f9242107d5 Author: Michael Buesch Date: Wed Nov 1 18:18:22 2006 +0100 bcm43xx-d80211: Merge all "radio" stuff into phy.c Signed-off-by: Michael Buesch commit 993a0fe4cb1f8cc5f2dd46741e4a40addf9e8393 Author: Michael Buesch Date: Wed Nov 1 17:43:19 2006 +0100 bcm43xx-d80211: merge struct bcm43xx_radioinfo into struct bcm43xx_phy We can't really draw a strict borderline between Radio and PHY stuff, so simply merge it. This also removes the need to handle the two different pointers all the time. Signed-off-by: Michael Buesch commit e35d6b8f16328df008b36592e0ee7064a0910fa5 Author: Michael Buesch Date: Wed Nov 1 16:32:17 2006 +0100 bcm43xx-d80211: Rename struct bcm43xx_phyinfo to struct bcm43xx_phy Signed-off-by: Michael Buesch commit b06a9d5201d3942fdcb570d1d7d48bc0f55823b6 Author: Michael Buesch Date: Mon Oct 30 23:44:28 2006 +0100 bcm43xx-d80211: Fix compilation: Missing files for LO and VSTACK. Forgot to git-add them :-/ Signed-off-by: Michael Buesch commit d2c2ecdb7184370cbc43754369cb2407bc881de7 Author: Michael Buesch Date: Mon Oct 30 01:22:22 2006 +0100 bcm43xx-d80211: Merge new LO-control code. This is the new and incomplete LO calibration and HW-pctl code. Although it seems to behave worse than the old code (due to bugs), it should be a good idea to merge now and begin fixing it. Signed-off-by: Michael Buesch commit 2f61c9d264a722ec81d2a2daf04211cab5f9275a Author: Michael Buesch Date: Sun Oct 29 21:05:09 2006 +0100 bcm43xx-d80211: Remove PHY OFDM routing bit, if we are on A-PHY. On an A-PHY the OFDM registers are base-registers. Signed-off-by: Michael Buesch commit 85650f4ed866b944110ec89f0acb2b841887d346 Author: Michael Buesch Date: Sun Oct 29 18:17:30 2006 +0100 bcm43xx-d80211: Move ILT stuff to OFDM table stuff Signed-off-by: Michael Buesch commit 8d1baff0a51c24ccbe60dabd884a1d01f34cf59a Author: Michael Buesch Date: Sun Oct 29 16:09:40 2006 +0100 bcm43xx-d80211: Add some PHY register definitions. This adds some new known PHY register defines Signed-off-by: Michael Buesch commit cf01b4e7180eb71d05ad6db5bc4011a7d48bc129 Author: Michael Buesch Date: Sat Oct 28 17:16:27 2006 +0200 bcm43xx-d80211: Don't ignore return value of pci_enable_device() Signed-off-by: Michael Buesch commit 677db766bea961e985a6adf9b9ce8375996b61d3 Author: Michael Buesch Date: Sat Oct 28 17:09:12 2006 +0200 bcm43xx-d80211: Fix DMA engine TX buffer unmap crash. meta->skb is NULL for the TX header buffer. We need a specialcase for that. Signed-off-by: Michael Buesch commit 2a046eea58164445a9b7af2dfd597475517e7c04 Author: Michael Buesch Date: Thu Oct 26 16:07:26 2006 +0200 bcm43xx-d80211: Only set USEDEFKEYS hostflag for WEP. Default keys are used implicitely for group keys in WPA. Signed-off-by: Michael Buesch commit 6c2ba9cb83d615650b71d624b077eb05ca461710 Author: Michael Buesch Date: Thu Oct 26 16:00:23 2006 +0200 bcm43xx-d80211: No support for hw encryption with v3 firmware. Various hwenc fixes. Signed-off-by: Michael Buesch commit 7eeca19b4b964301af9a7239387a43c21879c8e3 Author: Michael Buesch Date: Wed Oct 25 21:04:08 2006 +0200 bcm43xx-d80211: Use software encryption for TKIP for now. Signed-off-by: Michael Buesch commit 8aca7b597b31e44869a074364d0b3e36cc598250 Author: Michael Buesch Date: Wed Oct 25 20:41:21 2006 +0200 bcm43xx-d80211: Fix hardware based encryption for v4 firmware. This enables hardware-based encrption. Signed-off-by: Michael Buesch commit 1ad41d62e410dfbdd537e3a9c224df30acd3dbc8 Author: Michael Buesch Date: Wed Oct 25 19:34:37 2006 +0200 bcm43xx-d80211: Rename IRQs Signed-off-by: Michael Buesch commit 6c7d8184d5b431a21077171042b8ef63776f2f48 Author: David Kimdon Date: Fri Oct 13 12:34:57 2006 -0700 [PATCH] d80211: remove initialization of unused xr structure members The structure member xr_end was removed from d80211.h. Signed-off-by: David Kimdon Signed-off-by: John W. Linville commit c056948b282bc764515e7089adf42a6702025063 Author: John W. Linville Date: Mon Oct 23 22:21:05 2006 -0400 [PATCH] bcm43xx-d80211: fix build-break from struct pt_regs * removal Signed-off-by: John W. Linville commit f33692434f7f4db525d36475f89169519fc799a1 Author: John W. Linville Date: Mon Oct 23 22:16:19 2006 -0400 [PATCH] adm8211: remove #include Signed-off-by: John W. Linville commit c28b9c0b331e9511afa6534ef15ff0ad4fdb74ef Author: Michael Wu Date: Mon Oct 23 21:26:35 2006 -0400 [PATCH] Remove struct pt_regs * from d80211 drivers This patch will be necessary once wireless-dev pulls the 2.6.19-rc2 changes which include the removal of the struct pt_regs * argument in interrupt handler callbacks. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit c51763ef08320a65597f6bf39cf4d45f2060de39 Author: Michael Wu Date: Mon Oct 23 21:21:14 2006 -0400 [PATCH] adm8211: small cleanups in adm8211_probe This patch adds a KERN_INFO to a printk that didn't have anything, and shortens another line. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit 4850e5bf472e0daa2fab3d88d8674b81fb7e5e17 Author: Ivo van Doorn Date: Wed Oct 18 18:07:11 2006 +0200 [PATCH] rt2x00: Remove xr_end references This removes the xr_end references from rt2500usb and rt71usb. The rt2x00 pci drivers were already fixed by David Kimdon. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 8dd016455ab2951021431dfad615ed0e9afaff6c Author: Johannes Berg Date: Mon Oct 2 13:14:47 2006 +0200 [PATCH] fix bcm43xx-d80211 for inode diet This patch propagates the inode diet changes to bcm43xx-d80211. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit d9937a49e109fd5e3dc33b127c1bdd603dafa5d8 Author: Johannes Berg Date: Wed Aug 30 10:45:44 2006 +0200 [PATCH] d80211: make _irqsafe functions understandable Scheduling a tasklet when all it'll do is free the skb seems pretty strange, we can just free the skb right away (it'll not be freed but cleaned up later anyway then). Also, this patch adds a few comments about what that code is doing with the skb->cb field, namely storing a pointer and not the actual data in there. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville commit efd92664c3c6c74f141241d505b681c816ab3152 Author: Hong Liu Date: Mon Oct 23 17:06:01 2006 +0200 [PATCH] d80211: fix wep_tfm race The TX/RX path all use the local->wep_tfm to encrypt and decrypt packets. Each {en|de}crypt operation need set a new RC4key, this may corrupt the previous set key that is still being used. Thus cause a lot of decrypton error or encryption with the wrong key. Use two tfm (tx_tfm and rx_tfm) to avoid this race. Signed-off-by: Hong Liu Signed-off-by: Jiri Benc commit b1cea52326edc4abfb0f300f992363c7e6dfcb61 Author: David Kimdon Date: Thu Oct 19 16:42:09 2006 +0200 [PATCH] d80211: fix kernel doc for ieee80211_get_buffered_bc ieee80211_beacon_get() was already described. The doc entry in question describes ieee80211_get_buffered_bc(). Signed-off-by: David Kimdon Signed-off-by: Jiri Benc commit 77355a1e158d7858924c271c3c6ad96a0d1e7332 Author: Ivo van Doorn Date: Thu Oct 19 16:42:09 2006 +0200 [PATCH] d80211: ieee80211_hw handlers should be allowed to sleep This patch changes the ieee80211_if_sta timer structure into a workqueue. This will allow the config(), reset_tsf() and config_interface() handlers in the ieee80211_hw structure to sleep. This is especially required for USB drivers that have to sleep for all register access. Signed-off-by: Ivo van Doorn Signed-off-by: Jiri Benc commit a3cdcec1a299bcb883a7bcc73588163f245b64ae Author: David Kimdon Date: Wed Oct 18 17:32:51 2006 +0200 [PATCH] d80211: remove unused Super AG definitions, purge comment Remove unused Super AG structure members, enums. In struct ieee80211_tx_status the queue_length and queue_number could be useful outside the context of Super AG, so remove the comment and leave the members. Signed-off-by: David Kimdon Signed-off-by: Jiri Benc commit 0fbc8104060588dab4db93f37e3cdfb86ffed062 Author: Modestas Vainius Date: Wed Oct 18 17:32:51 2006 +0200 [PATCH] d80211: Fix TX/RX rates This patch correctly initializes the force_unicast_rateidx and max_ratectrl_rateidx. This was not done previously and caused a bug in rf80211_simple where when rate_control_simple_get_rate() was called, the incorrect rate was selected from the list. Signed-off-by: Modestas Vainius Signed-off-by: Ivo van Doorn Signed-off-by: Jiri Benc commit 59e8ad6835a88cf25f958e9224b0d9b17ccd2d89 Author: David Kimdon Date: Wed Oct 18 17:32:50 2006 +0200 [PATCH] d80211: silence sparse warning: bad constant expression Sparse does not figure out that algs[] isn't really a variable length array. The message is: net/d80211/ieee80211_sta.c:934:12: error: bad constant expression This switches algs[] to be obviously a constant array, and derives the value of num_algs algs[]. The code is correct and equivalent with or without this change. Signed-off-by: David Kimdon Signed-off-by: Jiri Benc commit 22acdd71c90cc4123d3df59a07919d432847e3e7 Author: David Kimdon Date: Wed Oct 18 17:32:50 2006 +0200 [PATCH] d80211: use FCS_LEN instead of hardcoded number. Signed-off-by: David Kimdon Signed-off-by: Jiri Benc commit 14d30b75adeda277aae2ffd0424eba5965799589 Author: Michael Buesch Date: Tue Oct 10 00:11:17 2006 +0200 bcm43xx-d80211: Fix runaway IRQ which caused high CPU usage. We kept triggering the noise-calculation IRQ, because we used the wrong sample registers. Signed-off-by: Michael Buesch commit 455ae5bb4ee0b18ed06ffee0d89b92a8fca3f217 Author: Michael Buesch Date: Thu Oct 5 16:43:47 2006 +0200 bcm43xx-d80211: Remove unused "err" variables. Signed-off-by: Michael Buesch commit faac518bf4a2d2846a7153b0b4f8b99ff8db4166 Author: Michael Buesch Date: Thu Oct 5 16:39:36 2006 +0200 bcm43xx-d80211: Wait for the firmware to respond, before we read revision codes. This solves a race between the device and the host. Signed-off-by: Michael Buesch commit afff04d301dabfe05bb9bc5e4bb378c1b9223d45 Author: David Kimdon Date: Wed Oct 4 18:44:55 2006 +0200 [PATCH] d80211: allow wireless vlan interface to have same MAC an AP interface Wireless vlan interfaces need to have the same mac address as AP interfaces. The STA must not see the change when it is bound to a specific vlan, so the address of the vlan interface must be the same as the address of the AP interface the station associated with. Signed-off-by: David Kimdon Signed-off-by: Jiri Benc commit a1db08ff39daef9a57d50d737bdbb91f8a5e0592 Author: Jouni Malinen Date: Wed Oct 4 18:44:55 2006 +0200 [PATCH] d80211: retain PS frames for at least STA listen interval Start using 2 * listen_int * beacon_int as a timeout for PS buffered unicast frames if that is longer than 10 seconds. Previously, we used fixed 10 second limit regardless of the listen interval. This fixes power saving for STAs that request very long listen interval (over 10 seconds). This was reported by UNH IOL 802.11 AP Base MAC Test Suite v2.4 Test #1.3.2 Part e. While we are at it, remove the station from the TIM when the PS buffer is empty. Signed-off-by: Jouni Malinen Signed-off-by: Jiri Benc commit e57d91e2de0ac577bce30aa6c752627ad735bd4f Author: Elliot Schwartz Date: Wed Oct 4 18:44:54 2006 +0200 [PATCH] d80211: remove rate limit code Remove unused and more or less pointless rate limiting code. This would have just dropped multicast frames arbitrarily when the limit is reached which is quite useless and does not really belong to 802.11 code. Signed-off-by: Elliot Schwartz Signed-off-by: Jiri Benc commit 6e353ac70959573dceff64137badeb170cec0709 Author: Elliot Schwartz Date: Wed Oct 4 18:43:07 2006 +0200 [PATCH] d80211: remove unused xr structure members, interface, etc. This is all unused. Signed-off-by: Elliot Schwartz Signed-off-by: Jiri Benc commit 4fdbfdf950c3260f16bad12bb1a5ebeac97bee37 Author: David Kimdon Date: Wed Oct 4 18:43:06 2006 +0200 [PATCH] d80211: Fix overflow when creating AVS header Fix overflow when converting timespec to microseconds. Without this patch you can get an overflow during the multiplication which can result in a negative number. hostime is define here: 4.4 hosttime The hosttime field is set to the current value of the host maintained clock variable when the frame is received. (from http://www.locustworld.com/tracker/getfile/prism2drivers/doc/capturefrm.txt) it is a u64. Signed-off-by: David Kimdon Signed-off-by: Jiri Benc commit 034b87e99c85b243b94c6955bbdce94825dd6b1f Author: Jiri Benc Date: Mon Oct 2 19:56:36 2006 +0200 [PATCH] d80211: rate_control: do not use atomic allocations when not necessary Allow GFP_KERNEL to be used for allocations of sta entries triggered from the user space. Signed-off-by: Jiri Benc commit 61a4837fa8e08fc3200b8fa026a84b4afcb674ff Author: Jiri Benc Date: Mon Oct 2 19:56:35 2006 +0200 [PATCH] d80211: allow changing of the rate control algorithm Allow changing of the rate control algorithm. This has some limitations: - The rate control algorithm can be set per-wiphy only. - All of network interfaces of the wiphy have to be down to change the algorithm. - All sta entries are flushed when the algorithm is succesfully changed. - The add_sta ioctl can be called only at a running interface from now. Changing of the algorithm is possible by writing a new algorithm name into /sys/class/ieee80211/phyX/rate_ctrl_alg. This will be most likely changed in the future. Signed-off-by: Jiri Benc commit a020fc0696485e6cf460060c7fc03c1066897ba0 Author: Jiri Benc Date: Mon Oct 2 19:56:35 2006 +0200 [PATCH] d80211: proper rate control structures freeing Add a reference counting to the rate control algorithm structure. This prevents unloading of the rate control module when there still exists a sta entry which uses that module. To achieve this some other things need to be done in this patch as well: - The new rate_control_ref structure is introduced. It replaces the rate_ctrl and rate_ctrl_priv fields in the ieee80211_local. - Parameters for most rate control callbacks are changed. Signed-off-by: Jiri Benc commit 4bef5903c5a0b05778d31f605668b6d8576b40d8 Author: Jiri Benc Date: Mon Oct 2 19:56:35 2006 +0200 [PATCH] d80211: rename rate_control.c to rc80211_simple.c To support changing of the rate control modules on the fly we need well-defined names of the modules. Let it be rc80211_*. Rename the only one rate control module (rate_control.c) into rc80211_simple.c. The module alias for the default module is changed to rc80211_default. Signed-off-by: Jiri Benc commit 1e55cd13b3f20ac9560270a5455d58f0cbd2acd9 Author: Jiri Benc Date: Mon Oct 2 19:56:34 2006 +0200 [PATCH] d80211: proper rate_control loading Fix locking issues with loading of rate_control modules. This still doesn't allow changing of the modules on the fly. Signed-off-by: Jiri Benc commit 3e72d14a5a66f204c66ea88e89aa8d45f3f7c2c0 Author: Jiri Benc Date: Mon Oct 2 19:56:34 2006 +0200 [PATCH] d80211: rename rate_control.h to ieee80211_rate.h rate_control.h is not a header for rate_control.c as the name suggests. Furthermore, we want to introduce ieee80211_rate.c which implements some things defined in rate_control.h. This patch renames rate_control.h to ieee80211_rate.h. Signed-off-by: Jiri Benc commit 606451a64310c8084e9a3de07bafb8360989d004 Author: Jiri Benc Date: Mon Oct 2 19:56:34 2006 +0200 [PATCH] d80211: del sta timer on interface close Delete sta timer when the corresponding network interface is brought down. Signed-off-by: Jiri Benc commit 693ca0e679ae17dd34f5392886f99ba36f68940c Author: Jiri Benc Date: Mon Oct 2 19:56:33 2006 +0200 [PATCH] d80211: add missing rtnl_unlock() Add forgotten rtnl_unlock() in the error path of ieee80211_register_hw. Signed-off-by: Jiri Benc commit 54bac502265add95b8ca098ccf62c1d506daeb86 Author: Jiri Benc Date: Mon Oct 2 19:55:04 2006 +0200 [PATCH] d80211: fix is_ieee80211_device The is_ieee80211_device function must ensure that the passed net_device belongs to the hardware device we are working with. Signed-off-by: Jiri Benc commit ff9917a04d2a0b70221348cbcdde2743bae02abd Author: Michael Buesch Date: Mon Oct 2 18:06:33 2006 +0200 bcm43xx-d80211: Prevent crash by setting active wlcore to NULL on wlcore-select failure. wlcore was dangling after select-wireless-core failure. Failure can happen by missing firmware. Signed-off-by: Michael Buesch commit abcbf421f6ee2e1e166e0781f1a42be684b45780 Author: Michael Buesch Date: Mon Oct 2 17:38:06 2006 +0200 bcm43xx-d80211: Set channel cookie to prevent ghost packets. Set the channel radio code cookie (v4 firmware only) to prevent the firmware from sending packets on the wrong channel. Signed-off-by: Michael Buesch commit 42c423cfbde21f7b1d49214228cc996bf95416f1 Author: Michael Buesch Date: Mon Oct 2 17:25:34 2006 +0200 bcm43xx-d80211: Assign all fields in the RX status report. This fixes a crash introduced by a recent change to d80211, which requires some fields (phymode) to be set. Signed-off-by: Michael Buesch commit 388092c43ba32071c0102c28e2cf43c0dc782566 Author: Jiri Benc Date: Wed Sep 27 17:39:27 2006 +0200 [PATCH] d80211: fix invalid pointer dereference When deleted_sta_list is nonempty and sta_list is empty in sta_info_proc_add_task, an invalid sta pointer is dereferenced. Signed-off-by: Jiri Benc commit a5b4c8adb19c9d5d8b6c9b187ae6b2351e74e4a1 Author: Mohamed Abbas Date: Wed Sep 27 17:34:03 2006 +0200 [PATCH] d80211: getting wrong freq value if we did hardware scan This patch modify d80211 to fix getting wrong frequency value for scan implemented in hardware. With harware scan we might get beacon of a network that is on different channel that in local->conf.channel causing set freq to wrong value. Signed-off-by: Mohamed Abbas Signed-off-by: Jiri Benc commit fc0f9a5915098ff5e405b80db3134fa9dd55ee9b Author: Johannes Berg Date: Fri Sep 22 13:56:22 2006 +0200 [PATCH] d80211: LED triggers This patch makes d80211 export LED triggers for rx/tx and introduces functions to allow device drivers to query the trigger names for setting default triggers. It also cleans up the Makefile LED related stuff. Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc commit 871e88d24ff80e9521da300fd5c80e521b869ac9 Author: Johannes Berg Date: Fri Sep 22 13:29:35 2006 +0200 [PATCH] d80211: use list_for_each_entry{,_safe} This patch changes (hopefully!) all occurrences in d80211 of list_for_each to list_for_each_entry (and _safe variants where they were used before). Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc commit ad8c3766f67df906ff45143d393c924ad7297471 Author: Hong Liu Date: Fri Sep 22 13:29:34 2006 +0200 [PATCH] d80211: fix "iwconfig key [x]" behavior "iwconfig key [x]" behavior is not correctly handled in the stack, also modify the giwencode method to show the key info. Signed-off-by: Hong Liu Signed-off-by: Jiri Benc commit c220ae6162f23935ebc141b6734ecad1ab435294 Author: David Kimdon Date: Thu Sep 21 21:20:07 2006 +0200 [PATCH] d80211: allow vlan interfaces to receive ToDS frames Interfaces of type IEEE80211_IF_TYPE_VLAN are similar to AP interfaces. One difference is stations are bound to a particular vlan interface after authentication/association based on management policy (for example a radius server). Interfaces of type IEEE80211_IF_TYPE_VLAN need to be able to receive ToDS frames. Signed-off-by: David Kimdon Signed-off-by: Jiri Benc commit adcdddba34181486f2a1b402f39d66fe05f42595 Author: Michael Wu Date: Thu Sep 21 21:20:07 2006 +0200 [PATCH] d80211: fix WEP on big endian cpus This patch fixes the endian issues with the ICV in WEP, as pointed out by David Kimdon , and uses __le32 where appropriate to make things clear. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit 2c9c2aa6731a429574a987c26769466a19373359 Author: David Kimdon Date: Thu Sep 21 21:20:07 2006 +0200 [PATCH] d80211: Fix type of prism2_hostapd_param crypt.alg crypt.alg is a string, use the correct type. Signed-off-by: David Kimdon Signed-off-by: Jiri Benc commit e960de8522ed214931b6aabdd41b6e4129e4a6cb Author: Michael Buesch Date: Wed Aug 16 11:30:01 2006 +0200 [PATCH] add bcm43xx-d80211 MAINTAINERS entry Add MAINTAINERS for bcm43xx-d80211 Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 40348eb34c2d6f01dc910e89c075b940f08fa621 Author: Ivo van Doorn Date: Thu Sep 21 20:28:22 2006 +0200 [PATCH] rt2x00: remove hardware button support compilation was broken in rt2x00 due to rfkill attributed sneaking into rt2x00dev structure. rfkill will replace current hardware button support evantually, but lets fix rt2x00 compilation and remove hardware button support for this time until rfkill has been finished. Signed-off-by Ivo van Doorn Signed-off-by: John W. Linville commit 250df31f3fb08bb5cbed0edf874353628ece7caa Author: Michael Buesch Date: Tue Sep 26 18:56:10 2006 +0200 bcm43xx-d80211: Remove some BCM947XX ifdefs, as this is (and should be) handled outside of the bcm43xx driver in the ssb driver. Signed-off-by: Michael Buesch commit a1e17c8c6d38c1bc695783f4ea7c8370f7c79ba5 Author: Michael Buesch Date: Tue Sep 26 17:32:54 2006 +0200 bcm43xx-d80211: DMA-mask fixes. This is based on a patch by Larry Finger. Signed-off-by: Michael Buesch commit e0d0a83ab740cae398054d458e82c7061a0aa4e8 Author: Michael Buesch Date: Mon Sep 25 21:41:46 2006 +0200 bcm43xx-d80211: Don't use low level netif and ieee80211_netif_oper functions. Signed-off-by: Michael Buesch commit 51539cf2d4fd958fa9a5ea282a9b7272353d0a49 Author: David Kimdon Date: Thu Sep 21 21:20:06 2006 +0200 [PATCH] d80211: fix multiple device ap support Another fix to the interpretation of dev_alloc_name() return value. dev_alloc_name() returns the number of the unit assigned or a negative errno code. Signed-off-by: David Kimdon Signed-off-by: Jiri Benc commit 361d9a9f51633c7128158d90a05a3b6cc43ee444 Author: Johannes Berg Date: Thu Sep 21 21:20:06 2006 +0200 [PATCH] d80211: clean up those huge else if statements This patch replaces the if (...) else if (...) else if (...) ... statements I complained about earlier with switches. Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc commit 156023b8efbec8a72f7077934d7b12cf1d00fb95 Author: Johannes Berg Date: Thu Sep 21 21:20:06 2006 +0200 [PATCH] d80211: use BUILD_BUG_ON This patch makes d80211 use BUILD_BUG_ON instead of checking at module initialisation time. This check really is only interesting while you hack since if the module was built, then it's either an 'always true' or 'always false' comparison, hence useless to do it at runtime. Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc commit f99ee9c93d75118c2dc54085ff2218407bd8601c Author: mabbas Date: Thu Sep 21 21:20:05 2006 +0200 [PATCH] d80211: diplay supported rates in readable format This patch modify d80211 to report supported rates in readable format in iwlist scan command. Signed-off-by: Mohamed Abbas Signed-off-by: Jiri Benc commit 5b1756ecb9786baeb5127ca60cf0b3431e1d0e84 Author: Hong Liu Date: Thu Sep 21 21:20:05 2006 +0200 [PATCH] d80211: add hardware scan callback Add hardware scan callback to support cards like ipw3945 which implements the scan command in firmware. Signed-off-by: Hong Liu Signed-off-by: Jiri Benc commit 25a49e579344602c14e4cf79fa95098b01e7cf27 Author: Hong Liu Date: Thu Sep 21 21:20:04 2006 +0200 [PATCH] d80211: fix wpa_supplicant reassoc problem After key negotiation completed using wpa_supplicant, wpa_supplicant can't reassoc with the AP if we reboot the AP. It always fails at the 4-way handshake. The problem is the key info is not cleared correctly. Thus when wpa_supplicant send the EAPOL-KEY packet, the d80211 stack finds the old key and uses it to encrypt the packet. The patch removes the sta_info when we disassociate with AP. Signed-off-by: Hong Liu Signed-off-by: Jiri Benc commit 1ba5ef67a57fec0e1b7ca2bb75d7a965e81b8ccc Author: Michael Buesch Date: Sat Sep 9 18:13:36 2006 +0200 bcm43xx-d80211: add SHM constants Signed-off-by: Michael Buesch commit 9039d43efe04c2a48d5d4dbf9053ff434764e8ce Author: Michael Buesch Date: Sat Sep 9 16:02:32 2006 +0200 bcm43xx-d80211: Add support for v4 firmware. Signed-off-by: Michael Buesch commit fd7d77888b4a46e944a0ac7fc3fc06dc5d1dbfd6 Author: Michael Buesch Date: Thu Sep 7 01:10:22 2006 +0200 bcm43xx-d80211: Firmware revision/patchlevel detection. This is based on a patch by Martin Langer. Signed-off-by: Michael Buesch commit edd8f9c5cb25212f18abe7ccacbd7c6570c9d913 Author: Michael Buesch Date: Thu Sep 7 00:33:50 2006 +0200 bcm43xx-d80211: Always make fwpostfix option available. Signed-off-by: Michael Buesch commit bb0c241e9288f35f0ebe70b0da30e4b2d70eb793 Author: Michael Buesch Date: Thu Sep 7 00:30:37 2006 +0200 bcm43xx-d80211: Don't crash if we use v4 firmware. Signed-off-by: Michael Buesch commit b72a789c7822b70f7ee0502875aca3c59dc4bcd4 Author: Michael Buesch Date: Wed Sep 6 02:31:49 2006 +0200 bcm43xx-d80211: convert to ssb abstract bus access API Signed-off-by: Michael Buesch commit 737164ad2c8956053ec9170c9290ffacc649573e Author: Michael Buesch Date: Wed Sep 6 02:26:48 2006 +0200 ssb: Abstract bus accesses. This abstracts all bus accesses so that we can use ssb on devices with the Sonics Silicon Backplane Bus. Signed-off-by: Michael Buesch commit e29495e14131be28da4fb8bf8a2615fd3f625764 Author: Michael Buesch Date: Wed Sep 6 02:22:26 2006 +0200 ssb: fix init sprom read/write race Signed-off-by: Michael Buesch commit 045604ac3cfe969ecfdf6354eec02c51500b6a76 Author: Ivo van Doorn Date: Sun Aug 27 17:39:16 2006 +0200 [PATCH] rt2x00: Misc fixes EEPROM_SIZE should be a value dividable by sizeof(u16) CSR_REG_SIZE should be dividable by sizeof(u32) In USB adapters the eeprom offset is in bytes and not words. Short slot time is 9 instead of 7 Signed-off-by Ivo van Doorn Signed-off-by: John W. Linville commit 3d31dea621f9f7cecd3f3a9357ac17ddb0fae8da Author: Ivo van Doorn Date: Sun Aug 27 17:39:15 2006 +0200 [PATCH] rt2x00: TXD/RTS fixes This fixes several issues with TXD and RTS control. excessive_retries is not a counter and should be set to either 1 or 0. Otherwise all folluw up frames will be marked as excessive retry... ENTRY_RTS_FRAME should be cleared when frame has been send, otherwise the next frame for this txd will also be marked as RTS. Checking for only IEEE80211_STYPE_RTS is bad since it is the same value as a AUTH frame and possible other fields as well. The correct check is to check for a CTL frame with the RTS flag set. Signed-off-by Ivo van Doorn Signed-off-by: John W. Linville commit a65f9a89bb760270e13fa6974f41acc8b34669fb Author: Ivo van Doorn Date: Sun Aug 27 17:39:15 2006 +0200 [PATCH] rt2x00: change variable name When setting the ARCSR registers we could use a better/more descriptive and already available variable name than plain "value". Signed-off-by Ivo van Doorn Signed-off-by: John W. Linville commit febe5c666192e1a913ca5fee494125caaaeb72e3 Author: Ivo van Doorn Date: Sun Aug 27 17:39:14 2006 +0200 [PATCH] rt2x00: Add ieee80211_netif_oper() calls Add ieee80211_netif_oper() calls on the correct places for better flow control. Signed-off-by Ivo van Doorn Signed-off-by: John W. Linville commit dc85b0a95611dd7903de4cbb41a1a6b9be7c8389 Author: Ivo van Doorn Date: Sun Aug 27 17:39:14 2006 +0200 [PATCH] rt2x00: Respect return values Respect the return values given from various functions. The return code should be handed upstream and not replaced by some other (possibly wrong) error code. Signed-off-by Ivo van Doorn Signed-off-by: John W. Linville commit 8ef39871a7146c370e2412300a5fa359bbfc1dde Author: Ivo van Doorn Date: Sun Aug 27 17:39:14 2006 +0200 [PATCH] rt2x00: Register initialization fixes Various register initialization fixes to make the device work properly. This will fix the RX/TX issue for rt61pci. Signed-off-by Ivo van Doorn Signed-off-by: John W. Linville commit 39223ed7c46f64d61a1bf36b8bedab925b126b92 Author: Ivo van Doorn Date: Sun Aug 27 17:39:13 2006 +0200 [PATCH] rt2x00: add/remove interface fix Allow correct configuration of the register depending on working mode. This can only be done by configuring the register before calling rt2x00_add_interface. Second fix is to disable the radio when all monitor interfaces _and_ the non-monitor interface has been removed. Signed-off-by Ivo van Doorn Signed-off-by: John W. Linville commit 9674cdaccd032d0e548f366d86d3f89a5ba538bc Author: Ivo van Doorn Date: Sun Aug 27 17:39:13 2006 +0200 [PATCH] rt2x00: Mac address reading optimization We don't need a seperate array when reading the mac address from eeprom. Read it directly into the perm_addr array which has been correctly memsetted. Also to prevent confusing add eeprom addresses for each eeprom word for the mac address. Signed-off-by Ivo van Doorn Signed-off-by: John W. Linville commit b12784b513f8a588aa20fe9051757ba48092a316 Author: Ivo van Doorn Date: Sun Aug 27 17:39:12 2006 +0200 [PATCH] rt2x00: basic rate mask The enabled rates register apparently only requires the basic rates. For this we add a define containing the basic rates. Now we are working on it anyway, also add a OFDM and CCK rate mask, to clear up some code. Signed-off-by Ivo van Doorn Signed-off-by: John W. Linville commit c6deec8a89ca95c94c7e5ce7a8845805c7674d86 Author: Ivo van Doorn Date: Sun Aug 27 17:39:12 2006 +0200 [PATCH] rt2x00: Add register revision and firmware version to ethtool When chipset is detected it is also a good idea to read the revision number from the register. This can than also be used with "ethtool -d". For rt61pci and rt73usb the firmware version should be stored so it can be accessed for "ethtool -i" For the other device just set firmware version to "N/A" Signed-off-by Ivo van Doorn Signed-off-by: John W. Linville commit 70bb92954ad8c777c3d76599f22ca088b7d7eebd Author: Michael Buesch Date: Sat Aug 26 15:16:02 2006 +0200 [PATCH] bcm43xx-d80211: 4311 support This adds support for the 4311 cards. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit b231d106e26e578b349345adc59763a18c298f57 Author: Michael Buesch Date: Sat Aug 26 13:49:22 2006 +0200 [PATCH] ssb: fix core CC and REV reading Fix CoreCode and CoreRev extraction from coreidhi. And yes, it's ugly. But that's how the device works. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 30d76f7cfe75f829b0fb20f1af827476beb7b86f Author: Michael Buesch Date: Sat Aug 26 12:44:14 2006 +0200 [PATCH] bcm43xx-d80211: add support for 4312 Add support for Broadcom 4312 a/b/g devices. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit ee7d0fea3160b33a00111ebdee471916b3480d98 Author: David Kimdon Date: Fri Aug 25 14:12:37 2006 -0700 [PATCH] d80211: fix crash in ieee80211_rx_michael_mic_report() This fixes a crash at ieee80211.c line 3461, ieee80211_rx_michael_mic_report() (rx->sdata->type == IEEE80211_IF_TYPE_AP). rx.sdata needs to be set before calling ieee80211_rx_michael_mic_report(). Signed-off-by: Elliot Schwartz Signed-off-by: David Kimdon Signed-off-by: John W. Linville commit 32bd206a55d33c930c6081505b2c27b5afd4adc9 Author: Michael Buesch Date: Thu Aug 24 22:47:04 2006 +0200 [PATCH] ssb: add MAINTAINERS entry Add MAINTAINERS entry for the Sonics Silicon Backplane driver. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit cf33ba68918311cf42904631a409068694a060db Author: Michael Buesch Date: Thu Aug 24 22:43:24 2006 +0200 [PATCH] ssb: minor fixes and cleanups Minor fixes and cleanups to the ssb driver. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 7236645d49d9e9fb7eac52dbaa2a78826bf76560 Author: Michael Buesch Date: Wed Aug 23 12:01:07 2006 +0200 [PATCH] bcm43xx: convert driver to use ssb This patch converts the bcm43xx driver to use the new ssb driver backend. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit e43f088eb1445a43c7358d1265fbb4c878f6790b Author: Michael Buesch Date: Wed Aug 23 11:59:58 2006 +0200 [PATCH] Add Sonics Silicon Backplane driver This patch adds a Sonics Silicon Backplane driver backend that can be used by ssb based device drivers auch as bcm43xx and b44. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit ca0b66ba5c61b8c4a3d7b835d49181073ad72c28 Author: Michael Buesch Date: Sat Aug 19 20:18:27 2006 +0200 [PATCH] bcm43xx-d80211: return correct hard_start_xmit error code hard_start_xmit should return a NETDEV_TX_FOO error code. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit bb389479b5096235fe1e3b19a46946951d16a576 Author: Michael Buesch Date: Wed Aug 16 17:52:58 2006 +0200 [PATCH] bcm43xx-d80211: New DMA engine code This is a rewrite of the bcm43xx DMA engine. It adds support for >1G of memory (for chips that support the extension bits) and 64-bit DMA (for chips that support it). Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 507a7567d474bfafcd60649e198ef18369937d44 Author: Michael Buesch Date: Mon Aug 14 21:32:24 2006 +0200 [PATCH] bcm43xx-d80211: Init, shutdown and restart fixes This fixes various bugs in the init and shutdown code that would lead to lockups and crashes. This is best reproducable by receiving a timeout from the netdev watchdog. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 414f928269a9d41642e85abc8fe47e5bc90eed6b Author: Michael Buesch Date: Wed Aug 23 21:16:24 2006 +0200 [PATCH] d80211: add ieee80211_stop_queues() Add ieee80211_stop_queues() to stop all queues with a single call. I will submit a patch for bcm43xx to use this function as soon as this got merged. Signed-off-by: Michael Buesch Signed-off-by: Jiri Benc commit 6014cc141587c2c7e73abb985f7741dc7e24c105 Author: David Kimdon Date: Wed Aug 23 21:16:24 2006 +0200 [PATCH] d80211: fix interface removal Calls to ieee80211_if_remove() should use the ieee80211 interface types. Convert interface type from hostapd to ieee80211 format. Signed-off-by: David Kimdon Signed-off-by: Jiri Benc commit a13193956bee479d72bad1bc1ccbcb831182b3d3 Author: David Kimdon Date: Wed Aug 23 21:16:23 2006 +0200 [PATCH] d80211: fix multiple device support Fix interpretation of dev_alloc_name() return value. dev_alloc_name() returns the number of the unit assigned or a negative errno code. Signed-off-by: David Kimdon Signed-off-by: Jiri Benc commit fb6433e836927581362d71532f010b0d5aa7587b Author: David Kimdon Date: Wed Aug 23 21:16:23 2006 +0200 [PATCH] d80211: allow for large scan results Fix a problem where incomplete scan results could be returned if the environment includes a large number of devices. Do not truncate the scan results and allow a result to contain more than IW_SCAN_MAX_DATA bytes. Signed-off-by: David Kimdon Signed-off-by: Jiri Benc commit d0c2e2f74781b478f708d58aa83cb83812705af1 Author: Jiri Benc Date: Wed Aug 23 21:16:23 2006 +0200 [PATCH] d80211: remove useless and wrong check for interface type The check for interface type in ieee80211_sta_rx_mgmt was wrong (it must allow IBSS interface as well) and unnecessary, because the check is already done in ieee80211_rx_h_mgmt. Signed-off-by: Jiri Benc commit 6b31161b5dbf014abc07eadfa473f921d9c313ea Author: Michael Wu Date: Wed Aug 23 21:16:23 2006 +0200 [PATCH] d80211: Group EIDs by standard, add remaining 802.11d EIDs This patch groups EIDs together by the 802.11 standard they were introduced in and adds the remaining 802.11d EIDs. The spec where the QoS EID was introduced still needs to be found. (does not appear to be 802.11e..) This patch depends on the previous patch that converts the status/reason codes and EIDs to enums. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit 7bac23cee8567c27c42f460cf7e7d9c340674b31 Author: Michael Wu Date: Wed Aug 23 21:16:22 2006 +0200 [PATCH] d80211: switch status codes, reason codes, and EIDs to enums This patch converts the status code, reason code, and EID defines in d80211_mgmt.h to enums. It also adds some status and reason codes, fixes some typos (DENOED, QUITE), and uses the ieee80211.h version of the name where reasonable. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit 30aca4fb6d72c86a693016ca8f9eeb46a7ad9dee Author: Johannes Berg Date: Wed Aug 23 21:16:22 2006 +0200 [PATCH] d80211: fix some documentation This patch fixes some spelling errors, typos etc. in d80211.h Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc commit 505c84c433dd4185c987844a10baf24fada3620d Author: Johannes Berg Date: Wed Aug 23 21:16:22 2006 +0200 [PATCH] d80211: surface powersave debug switch This patch makes the verbose powersave debugging visible in Kconfig. Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc commit 0c2bf906ecdf3c6fda21aa83afb07f015dd7ec20 Author: Johannes Berg Date: Wed Aug 23 21:16:21 2006 +0200 [PATCH] d80211: get rid of MICHAEL_MIC_HWACCEL define The symbol MICHAEL_MIC_HWACCEL is always defined and hence all the ifdefs using it are useless. This patch removes it. Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc commit 9b827dcb576bb80d8dc837d777812fa69b8af438 Author: Johannes Berg Date: Wed Aug 23 21:16:21 2006 +0200 [PATCH] d80211: surface IBSS debug This patch surfaces the IBSS debug switch in Kconfig. Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc commit bebc14745291105672e2cc3c46821e1c0731b330 Author: Johannes Berg Date: Wed Aug 23 21:16:21 2006 +0200 [PATCH] d80211: make lowlevel TX framedump option visible This patch surfaces the lowlevel TX framedump option in Kconfig. Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc commit 5b188f3e93922150f3bee76e4980340db099a272 Author: Johannes Berg Date: Wed Aug 23 21:16:21 2006 +0200 [PATCH] d80211: fix some sparse warnings This patch fixes some warnings from sparse in d80211. Also fixes indentation in places near where the changes were. Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc commit 681294dce73ff7ea535636dd0a5aeb288744db2d Author: Johannes Berg Date: Wed Aug 23 21:16:20 2006 +0200 [PATCH] d80211: clean up includes This patch cleans up includes all over d80211. It might not be complete, but this was the best I could do without analysing it completely. Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc commit d7d4b1971ecdd40b8a0b210ddd06d9d8ac972255 Author: Johannes Berg Date: Wed Aug 23 21:16:20 2006 +0200 [PATCH] d80211: clean up exports This puts all EXPORT_SYMBOL() macros along with the function being exported. Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc commit 358fd4b6115683d3a8f88308268cfd6062c31f47 Author: Johannes Berg Date: Wed Aug 23 21:16:20 2006 +0200 [PATCH] d80211: get rid of sta_aid in favour of keeping track of TIM This patch gets rid of the HUGE sta_aid array that was there in the access point structure and instead keeps track of the TIM. Also reduces stack usage of the ieee80211_beacon_add_tim() function considerably, and fixes a bug where removing a station that had frames buffered wouldn't update the hardware TIM (if necessary). It also removes the MAX_AID_TABLE_SIZE pseudo-configuration option (it was a define with a comment indicating it could be changed) since now having all AIDs available is no longer expensive. Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc commit 94df2a4b3e67e47058b8f58b4705edb9ed7b447a Author: Johannes Berg Date: Wed Aug 23 21:16:19 2006 +0200 [PATCH] d80211: get rid of WME bitfield This patch gets rid of the endian-ness dependent bitfield used for WME. It cleans up wme includes and adds some necessary includes in other files that got them implicitly through wme.h Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc commit ce05baacb2a48dfc5f917c63fcb52537991bbb1a Author: Johannes Berg Date: Wed Aug 23 21:16:19 2006 +0200 [PATCH] d80211: use kzalloc() This changes d80211 to use kzalloc() where applicable. Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc commit 3ee6077477530a8082c8eb62de350c30f703e2a2 Author: Johannes Berg Date: Wed Aug 23 21:16:19 2006 +0200 [PATCH] d80211: pointers as extended booleans This huge patch changes d80211 to treat pointers as "extended booleans", using "if (!ptr)" and "if (ptr)" instead of comparisons with NULL. Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc commit a648a95b7eb82496b6a2148105228cbc1724e2f1 Author: Johannes Berg Date: Wed Aug 23 21:16:18 2006 +0200 [PATCH] d80211: master link This patch adds a link from the wiphy to the master device in sysfs. Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc commit b8fa453a5bff1710a6e170e9d50fa337bee5ebdf Author: Johannes Berg Date: Wed Aug 23 21:16:18 2006 +0200 [PATCH] d80211: relax sysfs permissions The sysfs attributes add_iface and remove_iface both check for CAP_NET_ADMIN whenever something is written. Hence, permissions for the files should be relaxed so that someone who is not root but happens to have CAP_NET_ADMIN can do things. Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc commit 46beec8b164fcc79f43ae3ff11363a9c41fb5cd4 Author: Ivo van Doorn Date: Tue Aug 8 15:46:57 2006 +0200 [PATCH] rt2x00 - scan handlers Move scan structure initialization and handling into some generic handlers in rt2x00.h. This also fixed some obscure coding when waiting for the empty txrings before starting a scan. Signed-off-by Ivo van Doorn Signed-off-by: John W. Linville commit 245f170ec8055938b78b148023b2ae346d1a74c4 Author: Ivo van Doorn Date: Tue Aug 8 15:46:57 2006 +0200 [PATCH] rt2x00 - misc fixes Small misc fixes, - remove unwanted whitespaces - limit lines to 80 characters - byteordering fix - comment fix Signed-off-by Ivo van Doorn Signed-off-by: John W. Linville commit ae46268a603b626bcbcc338668d2239a0b869ed6 Author: Ivo van Doorn Date: Tue Aug 8 15:46:56 2006 +0200 [PATCH] rt2x00 - txpower limits Also check for equality with txpower limits. It doesn't do any harm, but it does prevent compiler warnings in certain conditions. Signed-off-by Ivo van Doorn Signed-off-by: John W. Linville commit f161570f4eaed29f2e5f613e5864845ef8aea6f1 Author: Ivo van Doorn Date: Tue Aug 8 15:46:55 2006 +0200 [PATCH] rt2x00 - optimize MAC reading & initialize perm_addr When reading the MAC addr from MAC register or EEPROM it is always read as little endian. Since we want to store it in a u8 array we don't require byte ordering as long as we correctly read it into an u8 array directly. Also copy the address to perm_addr and enable ethtool to read it. Signed-off-by Ivo van Doorn Signed-off-by: John W. Linville commit 9373556386260af14bd7b86b00890039e3cfd01a Author: Ivo van Doorn Date: Tue Aug 8 15:46:55 2006 +0200 [PATCH] rt2x00 - ieee80211_hw->config no longer requires scheduling d80211 no longer calls ieee80211_hw->config() fom interrupt context. Make gratefully use of this and remove the workqueue scheduling for the config changes. Signed-off-by Ivo van Doorn Signed-off-by: John W. Linville commit c86cb6ed9af8fcdfa50af4dd45e227cc73d822b3 Author: Ivo van Doorn Date: Tue Aug 8 15:46:54 2006 +0200 [PATCH] rt2x00 - pci request/release regions According to Documentation/pci.txt in 2.6.18 pci_request_regions() should be called _before_ pci_enable_device(). And pci_release_regions() should be called after pci_disable_device(). Signed-off-by Ivo van Doorn Signed-off-by: John W. Linville commit 96182eea161325ce32ddb0333833389c97559893 Author: Jouni Malinen Date: Mon Aug 7 16:16:14 2006 -0700 [PATCH] d80211: Fix TKIP replay protection Fixed TKIP replay protection for the case where hwaccel is enabled. rx_initialized flag was not set in this case and the TSC validation was skipped for the frames. Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville commit 1685c42da4f7e3dd98898784c437a1aa4da47bc8 Author: Jouni Malinen Date: Mon Aug 7 16:16:13 2006 -0700 [PATCH] d80211: Fix ieee80211_remove_tx_extra() if key not configured QoS header processing mangled unencrypted WMM frames on software retry. The QoS data needs to be removed even when encryption key is not configured. Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville commit 567c9998a643bffff58a98ceb9e730c90ee262f0 Author: Jouni Malinen Date: Mon Aug 7 16:16:12 2006 -0700 [PATCH] d80211: Send Layer 2 Update frames in kernel Send Layer 2 Update frame from the 802.11 code in kernel to the netdev that the STA is bound to. If the STA is bound to another VLAN netdev, send another update frame. This fixes an issue in which a local bridge table was not updated when hostapd sent this frame. Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville commit 23208021d93298ed427ea6fc5be05554b914c7f3 Author: Jouni Malinen Date: Mon Aug 7 16:16:11 2006 -0700 [PATCH] d80211: Fix PLCP header length comment Fixed a typo in a comment: PLCP header length is in microseconds, not milliseconds. Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville commit a32ae52a6fab91c18c717e56090c5b4687d2e454 Author: Jouni Malinen Date: Mon Aug 7 16:16:10 2006 -0700 [PATCH] d80211: Fix PS-Poll frame dropping Fixed PS-Poll processing for STAs that are not authenticated or associated: - 80211.ko dropped these frames even though it should have sent them to hostapd (this was broken by addition of IBSS support) Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville commit 07568e34443abab77fda67820b8e2e21b7f7f696 Author: Jouni Malinen Date: Mon Aug 7 16:16:09 2006 -0700 [PATCH] d80211: Fix RTS threshold use Fixed dot11RTSThreshold use which was off-by-3: - must add FCS_LEN to the skb->len - frame length needs to be greater than threshold; not greater than or equal Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville commit fa4a808b2689a221f788dd3072ebe1a40af00214 Author: Michael Wu Date: Sat Aug 5 00:57:04 2006 -0700 [PATCH] d80211 drivers: Use IRQF_SHARED instead of SA_SHIRQ This patch switches all instances of SA_SHIRQ to IRQF_SHARED for drivers in the drivers/net/wireless/d80211 directory. According to feature-removal-schedule.txt, the interrupt related SA_* flags are obsolete and should be replaced by the appropriate IRQF_* version before January 2007. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit 53fd42aa190b63c13ba10d33409302637f16eaf7 Author: Michael Wu Date: Sat Aug 5 00:41:58 2006 -0700 [PATCH] adm8211: Use ieee80211_*_queue functions This patch fixes the adm8211 to use the ieee80211_*_queue instead of netif_*_queue functions to stop/wake the queue. Otherwise, the wireless stack happily overflows our tx ring. It also simplifies the some flag setting related to TX queue handling. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit 47820d4f4a7f427af4bf00058197bb409dac6419 Author: Michael Wu Date: Fri Aug 4 23:30:48 2006 -0700 [PATCH] d80211: Fix issues with QOS bitwise ops This patch fixes various bitwise ops dealing with QOS that didn't get converted properly during the transition to IEEE80211_ style names. Thanks to Jouni Malinen for pointing this out. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit 1aa2f822c2ae2f23acc01a0fab49eeab1db4c292 Author: Jiri Benc Date: Thu Aug 3 14:03:04 2006 +0200 [PATCH] d80211: more correct WE support on master interface Master interface needs to have different WE support than virtual interfaces. The current situation leads to Oopses and other strange behaviours because of calling WE functions that shouldn't be supported on master interface. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit 8d48f58ecddfa7f18556bf44cf682d363e169407 Author: Jiri Benc Date: Thu Aug 3 13:59:45 2006 +0200 [PATCH] d80211: fix wrong logical operations Several comparisons were always evaluated to false due to incorrect logical operations. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit d2273a999331dce2e2bf6719e9fbc70ed252ea25 Author: Jiri Benc Date: Tue Aug 1 21:06:38 2006 +0200 [PATCH] d80211: Switch d80211 drivers to IEEE80211_ style definitions This patch makes the d80211 drivers work with the switch to IEEE80211_ style names in d80211.h. Based on the patch by Michael Wu . Signed-off-by: Jiri Benc commit 6884184d15ad8a0c5f4255e1219c170655a9610d Author: Michael Wu Date: Tue Aug 1 21:06:33 2006 +0200 [PATCH] d80211: Switch d80211 to IEEE80211_ style names This patch switches the WLAN_ definitions in d80211.h to IEEE80211_ style definitions found in ieee80211.h. It also switches to MAC_ARG and MAC_FMT. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit 2a053059b358f64991ac003c48f3de1da86c33ab Author: Karol Lewandowski Date: Tue Aug 1 21:06:29 2006 +0200 [PATCH] d80211: return correct value when loading of rate control module fails When loading of rate_control module fails, ieee80211_register_hw returns value from previous check. This patch fixes that. Signed-off-by: Jiri Benc commit 2cf10f1a78222a375297d01a919d55d1a3c2a5a6 Author: Jiri Benc Date: Tue Aug 1 21:06:24 2006 +0200 [PATCH] d80211: return correct error codes for scan requests Do not allow scanning when the network interface is down. Return 0 instead of -EBUSY when scanning is in progress on the same network interface. Signed-off-by: Jiri Benc commit d0d2b7a8ddc378ddea499f1537f6aea83d96d003 Author: Jiri Benc Date: Tue Aug 1 21:06:22 2006 +0200 [PATCH] d80211: make sleeping in hw->config possible This patch makes sleeping in the hw->config callback possible by removing the only atomic caller. The atomic caller was a timer and is replaced by a workqueue. This is based on a patch from Michael Buesch . Signed-off-by: Jiri Benc commit 67d84ea7c42ea954e7f75629ff9a315eddab4a26 Author: John W. Linville Date: Tue Aug 1 11:14:43 2006 -0400 [PATCH] bcm43xx: cast MAC2STR arguments as u8 ptr Lack of the cast caused a build break in my environment. Signed-off-by: John W. Linville commit 7966fcb300a03cdb34deb9c3724b0d8c9a637d61 Author: Ivo van Doorn Date: Wed Jul 26 20:32:01 2006 +0200 [PATCH] RT2x00: Misc. fixes Misc. fixes * Compile fixes * Code style fixes previously overlooked * Better check of return values of functions Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit a5acf08ccb042e0a0d9f16dbd788342fdc2c99d4 Author: Ivo van Doorn Date: Wed Jul 26 20:31:41 2006 +0200 [PATCH] RT2x00: Fix register initialization Thanks to ethtool a lot of problems with initialization of the registers has been discovered. This will correctly initialize all registers. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 5fa7a940e26b9421ee380da88e29eaf08eb8ac10 Author: Ivo van Doorn Date: Wed Jul 26 20:31:25 2006 +0200 [PATCH] RT2x00: Optimize config handlers Optimize the configuration handlers to only run when the current configuration has been changed. This means we need to store the current setting of most configuration options in rt2x00_dev. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit cefb4aca05d40be5edf13d857abcef3923fe64e3 Author: Ivo van Doorn Date: Wed Jul 26 20:31:01 2006 +0200 [PATCH] RT2x00: Correctly initialize tx_status->control field before packet transmission The control field inside the tx_status field of each entry should be correctly initialized when a packet is queued. We can use the same field to pass it to dscape when updating the beacon. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 6f158c99cf8b17400f4f76bbd49b0a3a0d730f06 Author: Ivo van Doorn Date: Wed Jul 26 20:30:39 2006 +0200 [PATCH] RT2x00: Use SET_NETDEV_DEV Use SET_NETDEV_DEV to help userspace detect the wireless interfaces. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 2148dc770972cdf30bf1067a924c342ab9d7d76f Author: Ivo van Doorn Date: Wed Jul 26 20:30:22 2006 +0200 [PATCH] RT2x00: Simplify *_reset() functions The reset function can be greatly simplified. when a reset is required the best thing to do, is to switch the radio off and on again. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 2d7b22b70bb44149160937ea30b8af81e7d73984 Author: Ivo van Doorn Date: Wed Jul 26 20:29:49 2006 +0200 [PATCH] RT2x00: Make suspend and resume handlers work correctly Fix suspend and resume handlers, they should no longer use net_dev->open() and net_dev->stop() since that delivers the wrong behaviour. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit ab0c654bd8ee275f721cd53d86093b8a594dda82 Author: Ivo van Doorn Date: Mon Jul 31 17:18:10 2006 -0400 [PATCH] rt2x00: initialization fixes Fix initialization of driver/hardware. Make a clear seperation between allocation and initialization. Remove the open() and stop() functions since they have been deprecated by dscape some time ago. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit a219eaa5f625eb82d3e39de0666e255d47aad183 Author: Ivo van Doorn Date: Wed Jul 26 20:29:30 2006 +0200 [PATCH] RT2x00: Fix rt61pci interrupt handling rt61pci irq is a bit different compared to the others, when the irq is raised, we should read from the register which ring and which entry has been send. And this entry should be processed. Using a for loop to go through all entries is no longer working since we require certain statistics from the registers. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 1a85872a707cd7dd5601388c0f392684cfa6ba4f Author: Ivo van Doorn Date: Wed Jul 26 20:29:09 2006 +0200 [PATCH] RT2x00: Merge PCI and USB version of data_entry structure Merge the data_entry structure for USB and PCI into a single structure. This means that all access to the data_addr and desc_addr should now be performed through the functions: rt2x00pci_desc_addr() rt2x00pci_data_addr() And for usb: rt2x00usb_urb() rt2x00usb_rxdata_addr() rt2x00usb_rxdesc_addr() rt2x00usb_txdata_addr() rt2x00usb_txdesc_addr() Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 2d0df00d721b5d70b8f644263ce1059ea7fe6484 Author: Ivo van Doorn Date: Wed Jul 26 20:28:45 2006 +0200 [PATCH] RT2x00: Check if read eeprom words are valid Make checks if the EEPROM data read is valid, if it is not, use the default values. Also fix the endian issue when reading the PCI_CONFIG_HEADER. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit f3550a705be4c636ca9c13a9d79fca73e347d0d3 Author: Ivo van Doorn Date: Wed Jul 26 20:28:26 2006 +0200 [PATCH] RT2x00: Allow link tuning while scanning rt2x00 does not know when we are scanning, unless passive_scan() is being used. For consistent behaviour we should not block tuning while scanning. We should however not tune the connection when it has been disabled in the hardware. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 06dc5cad1d49fc0334e6150b5a9cd297c199c646 Author: Ivo van Doorn Date: Wed Jul 26 20:27:57 2006 +0200 [PATCH] RT2x00: Add new rt73usb devices Add new rt73usb devices. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit f47dd623b2a55813ec4ec7b19d08e8e82e29864c Author: Ivo van Doorn Date: Wed Jul 26 20:27:30 2006 +0200 [PATCH] RT2x00: Move scan_work to scanning structure Remove scan_work from rt2x00_dev and place it in the scanning structure. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit eaf65e316ebf6f29fdba623298821588961efbbf Author: Ivo van Doorn Date: Wed Jul 26 20:27:02 2006 +0200 [PATCH] RT2x00: Add software and hardware sequence counting Add software sequence number counting to rt2400pci and rt2500pci, enable hardware sequence number counting for rt2500usb, rt61pci and rt73usb. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 8c5d04ee84640b7de6b59666e1a4e06ffe08f73f Author: Ivo van Doorn Date: Wed Jul 26 20:26:34 2006 +0200 [PATCH] RT2x00: Don't use driver_data and driver_info fields driver_info and driver_data are not required for rt2x00, neither is there any need to check that field when the probe() function is called. The PCI and USB layers already correctly make the checks. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 8a03b154fd0a92115aab7d660f98f8761e8a59d7 Author: Ivo van Doorn Date: Wed Jul 26 20:25:29 2006 +0200 [PATCH] RT2x00: Fix *_set_state() functions Fix problems with waking up the device at initialization time. The debug message should be more descriptive of the problem, when the device fails to wake up. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 78b26c0eafb3dc0e9394c509d54bca5f56979372 Author: Ivo van Doorn Date: Wed Jul 26 20:25:01 2006 +0200 [PATCH] RT2x00: Add RTS frame creation Support RTS. When rts is required, create the frame and send it out before the rest of the frames. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit a3b7582b601e6d62fd7a40cfbe837b41eee2774e Author: Ivo van Doorn Date: Wed Jul 26 20:24:38 2006 +0200 [PATCH] RT2x00: Add TXPOWER_FROM_DEV and TXPOWER_TO_DEV macros Add TXPOWER_FROM_DEV and TXPOWER_TO_DEV macro's to convert the txpower values read from the eeprom to the value dscape expects, and vice versa. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 291b1074884f87dd2b4d7d3d1222887dada7edeb Author: Ivo van Doorn Date: Wed Jul 26 20:24:03 2006 +0200 [PATCH] RT2x00: Clean up device specific rate value initialization ieee80211_rate structure should be initialized by device_rate_entry And the creation of the device specific rate value can be optimized and made more clearer. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 3234f1709509555cfa3667828e2b536136ac8875 Author: Ivo van Doorn Date: Wed Jul 26 20:23:15 2006 +0200 [PATCH] RT2x00: Add ethtool support Add ethtool support. At the moment we support the following features: * read driver info * read registers * read eeprom * enable and disable debug output Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 579e064fc12a901d9f79f0150ca89303e913c393 Author: Ivo van Doorn Date: Mon Jul 31 17:15:36 2006 -0400 [PATCH] rt2x00: merge rt2x00_pci and rt2x00_usb rt2x00_pci and rt2x00_usb are for 99% equal, the only difference they have are not worth the duplicate code. Merge the structures into rt2x00_dev. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit db3fc90f181d07494acc831a120a36ad6599a873 Author: Ivo van Doorn Date: Wed Jul 26 20:22:22 2006 +0200 [PATCH] RT2x00: Add interface structure Move all settings depending on the current association into a seperate interface structure. Altough we only support 1 association type at a time, we do support multiple monitor devices, keep track of the number using the monitor_count field. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 99f89e485b2a22c84b9befb542695c89248cc3ef Author: Ivo van Doorn Date: Mon Jul 31 17:10:03 2006 -0400 [PATCH] rt2x00: coding style fixes Coding style fix. * Reduce the amount of tabs in the code * Put Place function return type and function name on a single line * Replace 'u8 char counter' with 'unsigned int i' * Place '&&', '||' and '|' on the end of a line, instead of at the start. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit b3935d418129ad9f3e695b5d62e1e9aa953477b7 Author: Michael Wu Date: Sun Jul 23 01:43:25 2006 -0700 [PATCH] d80211: Make MACSTR/MAC2STR macro available to drivers d80211: Make MACSTR/MAC2STR macro available to drivers This patch moves the MACSTR/MAC2STR macros to d80211.h so that they are available to drivers. It also converts the adm8211 and bcm43xx drivers to use this macro. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit 06fa88943cfd89904480f7cac6ff3ce11cb915bf Author: Larry Finger Date: Mon Jul 10 17:56:47 2006 -0500 [PATCH] bcm43xx-d80211: Fix an off-by-one condition in handle_irq_noise An assert statement near the start of handle_irq_noise in the d80211 version of bcm43xx_main.c is there to protect against out of bound addressing using variable bcm->noisecalc.nr_samples. The arrays in question have a dimension of 8, thus the value must be < 8. This patch mirrors the one submitted earlier for the softmac version of bcm43xx. Signed-Off-By: Larry.Finger Signed-off-by: John W. Linville commit f2316bf07b1f2f785c8d39ba298fbd64d1b3c506 Author: Michael Wu Date: Sun Jul 23 01:06:02 2006 -0700 [PATCH] adm8211: Add MAINTAINERS entry adm8211: Add MAINTAINERS entry Add MAINTAINERS entry for adm8211 driver. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit 9f47e9bd6be8bf189bd46ff2c022419da52e514a Author: Michael Wu Date: Sun Jul 23 01:04:31 2006 -0700 [PATCH] adm8211: Restore frame header after TX adm8211: Restore frame header after TX This patch makes the adm8211 driver restore the TX frame's header to the original 802.11 header before returning it to the 802.11 stack. This allows the d80211 rate control algorithm to work correctly. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit 6625a83d79136191ed6f7055e43ba489218e2134 Author: Larry Finger Date: Fri Jul 28 15:45:49 2006 -0400 [PATCH] bcm43xx: remove unused routines This patch removes some unused static inline routines from bcm43xx-d80211 version of bcm43xx_main.h. Signed-Off-By: Larry Finger Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 9bb1de44b5c0aa754db201bc9313b1cc2dd59fa2 Author: Michael Wu Date: Thu Jul 13 01:43:21 2006 -0700 [PATCH] adm8211: Properly initialize priv->mode in adm8211_probe Properly initialize priv->mode in adm8211_probe The adm8211 driver refuses to open without this patch. Don't know how I ever tested the driver without this working. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit dd98e33ddda469abf5f546d2d1e72cf8fad7d105 Author: Ivo van Doorn Date: Fri Jul 28 15:24:22 2006 -0400 [PATCH] rt2x00 makefile This patch fixes a copy&paste error by correcting the name for hardware button support in rt61pci. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 953f105783391547e9839c3a8d8221f97155d929 Author: Michael Buesch Date: Mon Jul 10 23:38:38 2006 +0200 [PATCH] bcm43xx-d80211: init routine rewrite Rewrite of the bcm43xx initialization routines. This fixes several issues: * up-down-up-down-up... stale data issue (May fix some DHCP issues) * Fix the init vs IRQ handler race (and remove the workaround) * Fix init for cards with multiple cores (APHY) The active PHY is selected through d80211. * Fix the controller restart code. Controller restart can now also be triggered through echo 1 > /debug/bcm43xx/ethX/restart Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 9f289379ff9c5640b8d21f34e30bff87c867ff58 Author: Michael Buesch Date: Sun Jul 9 03:50:42 2006 +0200 [PATCH] bcm43xx-d80211: opencoded locking Port of the "opencoded locking" patch from bcm43xx-softmac to bcm43xx-d80211. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit cb9a446a0fe6f55284b53de656618ca3c6ea9c67 Author: Michael Buesch Date: Wed Jun 28 20:43:55 2006 +0200 [PATCH] bcm43xx-d80211: Lower mac_suspend timeout Lower mac_suspend timeout. This timeout won't ever trigger, if the hardware and driver is not horribly faulty. It's just a safety net to not lock up the kernel on bugs. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 2816781dfb1c8a2051a962e75cdba34da1811e22 Author: Michael Wu Date: Fri Jul 14 09:30:44 2006 +0200 [PATCH] d80211: Add sparse bitwise annotations This patch adds sparse bitwise annotations to d80211. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit 9dc20a14811532e2a630c66882ff27f71f90e5dd Author: Jiri Benc Date: Thu Jul 13 13:15:52 2006 +0200 [PATCH] bcm43xx-d80211: use SET_NETDEV_DEV This adds /sys/class/net/wmaster0/device symlink. Signed-off-by: Jiri Benc Signed-off-by: Michael Buesch commit 374247361f6872f585ddde1e7bcec4c4564f961e Author: Jiri Benc Date: Thu Jul 13 13:15:51 2006 +0200 [PATCH] bcm43xx-d80211: use host_gen_beacon_template Use new host_gen_beacon_template flag. This also means workaround with "iwconfig essid" after hostapd is run is not necessary anymore. Signed-off-by: Jiri Benc Signed-off-by: Michael Buesch commit a495f8ec7590d6df5edc36bff419f25b8c3440de Author: Michael Wu Date: Thu Jul 13 13:14:40 2006 +0200 [PATCH] d80211: Replace rc4 code with crypto api arc4 This patch replaces the rc4 code used in wep.c with crypto api's arc4 cipher. The struct crypto_tfm passing in tkip isn't great, but it'll get fixed when tkip is converted to use crypto api entirely. (michael_mic) Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit 80f18b79eacc07319afe473023a5358cfbc11ae6 Author: Jiri Benc Date: Thu Jul 13 13:14:39 2006 +0200 [PATCH] d80211: SET_NETDEV_DEV for non-master devices This adds /sys/class/net/*/device symlinks for non-master (wlan* and wmgmt*) devices. Signed-off-by: Jiri Benc commit 162a9ab0920090224f0af03b4a79b45b35dd437f Author: Jiri Benc Date: Thu Jul 13 13:14:39 2006 +0200 [PATCH] d80211: optimize defragmentation Optimize defragmentation by storing all fragments in skb queue and reallocating skb only once all fragments are received. Signed-off-by: Jiri Benc commit bb23c7cde283025bab76f0422003c45a2f82c937 Author: Jiri Benc Date: Thu Jul 13 13:14:39 2006 +0200 [PATCH] d80211: fix defragmentation When multiple virtual interfaces are active and some of them is in promisc mode, defragmentation does not work. Fix it by introducing separate fragment table for each virtual interface. Signed-off-by: Jiri Benc commit 53a84c48f3862bebeccf6b97f750be1e42f2bf14 Author: Jiri Benc Date: Thu Jul 13 13:14:38 2006 +0200 [PATCH] d80211: fix receiving through virtual interfaces This fixes several problems with receiving when multiple interfaces are present or when some interface is in promiscious mode: - Packet type (PACKET_HOST and PACKET_OTHER_HOST) is set correctly now. - Failed decryption of a frame is reported only once for each frame. - Failed decryption of a frame not destined to the interface (e.g. when the interface is in promisc mode) is not reported. - Channel utilization is counted correctly (i.e. once for each frame only, independently on number of active virtual interfaces). To achieve this, ieee80211_rx_handlers needed to be separated into new ieee80211_rx_handlers and ieee80211_rx_pre_handlers structures. Defragmentation still doesn't work correctly in promisc mode. This is fixed by subsequent patches. Signed-off-by: Jiri Benc commit 603ee5045dad050a66b94a847cd5385fc7678ce9 Author: Jiri Benc Date: Thu Jul 13 13:14:38 2006 +0200 [PATCH] d80211: better deallocation of mdev Master device and ieee80211_local are allocated separately now, so master device can be freed by the same function as other virtual interfaces. Signed-off-by: Jiri Benc commit 00f22bc8c0745572f3aebd5b6fd7a721199b3a4a Author: Jiri Benc Date: Thu Jul 13 13:14:38 2006 +0200 [PATCH] d80211: host_gen_beacon_template flag This is a partial support for devices requiring beacon template. Please note that there is no support for PS mode for such cards yet. Signed-off-by: Jiri Benc commit 62cefb2dabe7ea740481200408a07a797b7281ab Author: Jiri Benc Date: Thu Jul 13 13:14:38 2006 +0200 [PATCH] d80211: do not receive through master interface when not scanning Arrived packets should not go into master interface except when scanning - it leads to duplicate packets reception. This also fixes a race when scanning is finished during invoking of rx handlers. Signed-off-by: Jiri Benc commit a5b83f260b3cbe9ed161313e620c66b76e0218cf Author: John W. Linville Date: Wed Jul 12 15:43:34 2006 -0400 [PATCH] d80211: remove referencess to xmit_lock_owner Clean-up sloppy attempt at moving to netif_tx_lock API. When using that API, direct manipulation of xmit_lock_owner is unnecessary and inappropriate. Signed-off-by: John W. Linville commit 5696aa0b3216cd66a3722d4ff206577fb856301c Author: John W. Linville Date: Tue Jul 11 14:13:41 2006 -0400 [PATCH] bcm43xx-d80211: fixup UTS_RELEASE build break Signed-off-by: John W. Linville commit ece0a2612dcfba3792c86978c91b6f10ab97c096 Author: John W. Linville Date: Tue Jul 11 14:08:40 2006 -0400 [PATCH] d80211: use netif_tx_lock API Signed-off-by: John W. Linville commit 6e57badd1b5a6e08d44138714ed7c45a7b478c9c Author: Michael Wu Date: Sat Jun 17 21:52:39 2006 -0700 [PATCH] add adm8211 driver This patch adds support for the ADMtek ADM8211 card. It is not completed yet, but I feel the code is clean enough to merge into wireless-dev. It currently supports STA mode. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit 04760bab97b59f11df8392dc1ff1bd4460392300 Author: Michael Buesch Date: Wed Jun 28 20:38:50 2006 +0200 [PATCH] bcm43xx-d80211: fix mac_suspend refcount This fixes mac_suspend reference counting for ifconfig up ifconfig down ifconfig up Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit a3f47107a994199d9a731c957f463a0a98cf205e Author: Jiri Benc Date: Fri Jun 23 15:09:35 2006 +0200 [PATCH] bcm43xx-d80211: per-queue TX flow control This is an attempt to fix bcm43xx driver. It is for DMA mode only and incomplete even for that mode - ieee80211_hw->tx() callback should return NETDEV_TX_* contants which is not completely fixed by this patch. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit 6fd14676aa2c506ee85f277d8772fe004e94c360 Author: Jiri Benc Date: Fri Jun 23 15:09:34 2006 +0200 [PATCH] bcm43xx-d80211: fix sending of fragments This makes fragmentation work with bcm43xx. Signed-off-by: Jiri Benc Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit ba1a7f1b094bfb061ad231076a8bb3b3b4802935 Author: Ivo van Doorn Date: Fri Jun 23 15:09:33 2006 +0200 [PATCH] rt2x00: per-queue TX flow control Based on Jiri's patch for rt2x00 driver to do TX flow control. Signed-off-by: Ivo van Doorn Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit 15562921dfdaafc37226927cd978f0f685efbec8 Author: Alexander Tsvyashchenko Date: Mon Jun 19 23:59:36 2006 +0200 [PATCH] bcm43xx-d80211: AccessPoint mode related fixes Get AccessPoint mode working in bcm43xx-d80211. This patch is derived from Alexander Tsvyashchenko's original patch. I (mb) extended it by endianess fixes and other bugfixes. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit a4f8b0fa472bf04649f7a28393dcf7b0a55c8728 Author: Gertjan van Wingerde Date: Mon Jul 3 13:53:29 2006 +0200 [PATCH] d80211: Take lowlevel driver's channel change time into account during scanning. Make the dscape stack take the driver-supplied channel change time into account when actively scanning for networks. This particularly has been a problem in the rt2x00 driver, where configuration changes are done via a work-queue, and the subsequent channel changes failed due to the dscape stack scheduling a channel update while the previous one had not been performed yet, resulting in failed scans. Signed-off-by: Gertjan van Wingerde Signed-off-by: Jiri Benc commit 634f9d6f4bbafbcf218ac303087a0e83c5c95cda Author: Michael Buesch Date: Tue Jun 20 11:14:18 2006 +0200 [PATCH] d80211: allow NULL for control in beacon_get bcm43xx has no use for the "control" data provided by ieee80211_beacon_get(), so allow passing a NULL pointer to avoid setting up a dummy struct and throwing the data away afterwards in the driver. Signed-off-by: Michael Buesch Signed-off-by: Jiri Benc commit e360f2605cbb71f359de21a59eff1bcbab150e7f Author: Jiri Benc Date: Tue Jun 20 11:14:18 2006 +0200 [PATCH] d80211: add first_fragment flag to ieee80211_tx_control If a driver needs to find out if the fragment it is supposed to pass to the hardware is the first fragment, the only way to do this is to check if a Fragment Number part of seq_ctrl field in the frame header equals to 0. Let's make it easier. Signed-off-by: Jiri Benc commit 975a964398a0beb665747691350282b0a0b809c1 Author: Jiri Benc Date: Tue Jun 20 11:14:18 2006 +0200 [PATCH] d80211: handle full queue when sending fragments When the queue gets filled up while sending fragments, do not discard the frame. Partially sent frames are stored in a buffer in ieee80211_local (there is place for one frame for each queue there). When the space in hw queue gets available again, stored frame for that queue is sent first. Also, the case when driver returns NETDEV_TX_BUSY is handled properly now. Signed-off-by: Jiri Benc commit 39ee8c857aff2f97fb8c27cee2ee9001833f5a2b Author: Jiri Benc Date: Tue Jun 20 11:14:18 2006 +0200 [PATCH] d80211: per-queue TX flow control Currently, before a packet is passed to the driver, the driver is asked about status of its TX queues (i.e. how many packets are queued in each queue and how large are queues). This is different from the way generic networking works in Linux and it doesn't allow easy implementation of resubmitting fragments to the driver when the queue gets filled up during transmitting. This patch changes the stack not to ask driver about queue status but require driver to do TX flow control. Please note that this breaks drivers. Signed-off-by: Jiri Benc commit 9372ebe184ff953be4f9d05b029477a57dd43bbd Author: Jiri Benc Date: Tue Jun 20 11:14:18 2006 +0200 [PATCH] d80211: update tx.skb after TX handler calls TX and RX handlers are allowed to change skb. Fix (hopefully) the last place where this is not taken into account. This is similar to e81b1bc0f272a50458ab6ae8777f6327af0248e5. Signed-off-by: Jiri Benc commit 57aab842bd18f5d872685802646afd53cf0a15d5 Author: Michael Buesch Date: Tue Jun 13 21:18:53 2006 +0200 [PATCH] bcm43xx: Port PIO fixes to d80211 Port the PIO fixes from bcm43xx-softmac to bcm43xx-d80211. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit b9bbba19dcb6bf94a6d6e962217723ef592f78b7 Author: Michael Buesch Date: Tue Jun 13 21:18:40 2006 +0200 [PATCH] bcm43xx: Port suspend-mac-in-long-pwork from d80211 Port the Suspend MAC In Long Periodic Work patch from bcm43xx-softmac to bcm43xx-d80211. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 4b0f482a8827e310814041f3973bb1d189eb936f Author: Michael Buesch Date: Tue Jun 13 21:18:29 2006 +0200 [PATCH] bcm43xx: Port preemptible-periodic-work to d80211 Port the preemptible periodic work patch from bcm43xx-softmac to bcm43xx-d80211. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 5d138781d5a43c9976c4db117516f9b8f98ea349 Author: Michael Buesch Date: Tue Jun 13 21:18:15 2006 +0200 [PATCH] bcm43xx: Port new locking scheme to d80211 Port the new locking scheme from bcm43xx-softmac to bcm43xx-d80211. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 24ad3664b66237f0d0b250f4594dc65860d1064d Author: Jiri Benc Date: Thu Jun 8 09:49:16 2006 +0200 [PATCH] d80211: remove procfs files Remove procfs support. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit 035e701ee6e9b78e2eb76358f2cf146b93f462de Author: Jiri Benc Date: Thu Jun 8 09:49:15 2006 +0200 [PATCH] d80211: encryption keys sysfs attributes Add /sys/class/ieee80211/phyX/sta/*/key/* and /sys/class/net/X/keys/[0-3]/* attributes. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit 27d760a87d9603c5c3e34ed9dcb2c859b70c5813 Author: Jiri Benc Date: Thu Jun 8 09:49:14 2006 +0200 [PATCH] d80211: rate_control sysfs attributes Add support for sysfs attributes for rate_control modules. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit eae05005a95e4f9ab87b3000663b56b6ef9f523b Author: Jiri Benc Date: Thu Jun 8 09:49:13 2006 +0200 [PATCH] d80211: remove useless parameters There is no necessity for passing ieee80211_local parameter to sta_info_put and sta_info_free now. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit 003393c5b10060e2f49381d5ec67b9fcb198d4e4 Author: Jiri Benc Date: Thu Jun 8 09:49:12 2006 +0200 [PATCH] d80211: sysfs attributes for associated stations Add /sys/class/ieee80211/phyX/sta/* attributes. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit 211cba9a070f71e83576ab1ad89fedf7a38977bb Author: Jiri Benc Date: Thu Jun 8 09:49:11 2006 +0200 [PATCH] d80211: rename sta_info_relase to sta_info_put sta_info structure has reference counting (will be converted to kobject in next patch). Therefore, sta_info_release should be divided into two functions - one for decrementing reference count and one for freeing the structure when the count drops to zero. sta_info_release is the name suitable for the second function. This patch renames sta_info_release to sta_info_put to let next patch introduce a new sta_info_release function. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit 6703164e59bd76f2bb1c33f93f39559b535cd109 Author: Jiri Benc Date: Thu Jun 8 09:49:10 2006 +0200 [PATCH] d80211: network interface sysfs attributes Add /sys/class/net/X/* attributes for 802.11 interfaces. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit 167bb21daaeeecaa82c08e5ea95016d8d2d57795 Author: Jiri Benc Date: Thu Jun 8 09:49:09 2006 +0200 [PATCH] d80211: wiphy sysfs attributes Add /sys/class/ieee80211/phyX/* attributes. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit 706e6bb57f85b2d6921a4c52aa77dd8012d93eeb Author: Jiri Benc Date: Thu Jun 8 09:49:08 2006 +0200 [PATCH] d80211: fix Oops when writing to add_ and remove_iface When add_iface or remove_iface sysfs attribute is opened just before device is unregistered and some data is written there afterwards, their handlers try to access master net_device which is released at that point. A similar problem can happen when ioctl is invoked during unregistering - it is possible that interface the ioctl was called on is still there but master interface has been already freed. Fix these problems by: - checking if the device is unregistered in sysfs handlers and - releasing all interfaces under single rtnl lock. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit 54e18a553c303cc2e48534db0679c06c0071b3e3 Author: Jiri Benc Date: Thu Jun 8 09:49:07 2006 +0200 [PATCH] d80211: separate allocation of ieee80211_local ieee80211_local has a separate class_device. That means it has reference counting independent of master net_device and can be freed at a different time, therefore these two structures cannot be allocated together. Solve this by adding ieee80211_ptr pointer to net_device structure (similar to other pointers already presented there) and using it as a pointer to independently allocated ieee80211_local. This also allows is_ieee80211_device function to be nice finally. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit 275b38a9a2e66b13a7868c2f97f6c8b1c3c2e74c Author: Jiri Benc Date: Thu Jun 8 09:49:06 2006 +0200 [PATCH] d80211: better sysfs registration of symlinks to wiphy Lately, delayed sysfs registration of net_device (in netdev_run_todo) was removed. This allows us to remove hack that used class interface for sysfs registration. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit 28d1cbed7e09cadb00782f8677d2d5bf52dd25de Author: Jiri Benc Date: Thu Jun 8 09:49:05 2006 +0200 [PATCH] d80211: deinit sysfs in case of an error When ieee80211_wme_register fails in ieee80211_init, ieee80211 class was not unregistered. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit 3cdcc4d06bf615a8516f49d331b4540d1b1396fd Author: Michael Buesch Date: Sun Jun 4 12:03:56 2006 +0200 [PATCH] bcm43xx-d80211: Fix 64bit compiler warnings Fix all 64bit compiler warnings. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit f38543e081274fc2ceff124c86a370fa135fa33b Author: Michael Buesch Date: Sun Jun 4 11:41:16 2006 +0200 [PATCH] bcm43xx-d80211: add DMA rx poll workaround to DMA4 Also add the Poll RX DMA Memory workaround to the DMA4 (xmitstatus) path. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 83d1838d037f9b7438e4c74e76c615fa8978a9f6 Author: Stefano Brivio Date: Thu May 11 01:11:16 2006 +0200 [PATCH] bcm43xx-d80211: add PCI ID for bcm4319 Add PCI ID for bcm4319. Signed-off-by: Stefano Brivio Signed-off-by: John W. Linville commit 6528555946e747bafdf2fdaa23bfe1a0ea77c8e5 Author: Stefano Brivio Date: Thu May 11 01:11:00 2006 +0200 [PATCH] bcm43xx-d80211: check for valid MAC address in SPROM Check for valid MAC address in SPROM fields instead of relying on PHY type while setting the MAC address in the networking subsystem, as some devices have multiple PHYs. Signed-off-by: Stefano Brivio Signed-off-by: John W. Linville commit 6936b502e96859fb414ebadb3660f3a0a87a6286 Author: Michael Buesch Date: Fri May 5 19:50:54 2006 +0200 [PATCH] bcm43xx-d80211: Fix access to non-existent PHY registers Fix the conditions under which we poke at the APHY registers in bcm43xx_phy_initg() to avoid a machine check on chips where they don't exist. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 18ffe769cce9106f47a3aea6f75d08c0904bcfa1 Author: Michael Buesch Date: Wed May 3 21:05:53 2006 +0200 [PATCH] bcm43xx-d80211: measure the channel change time Measure the channel change time with the bcm43xx tsf timer and remove the guesswork constant. ;) Tests on my 4306 show that the time comes damn close to reality. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 274417a554a3eb683b49baec9f46b6fdb9189606 Author: Michael Buesch Date: Wed May 3 18:42:04 2006 +0200 [PATCH] bcm43xx-d80211: rewrite interface handling Rewrite the virtual interface handling. With this monitor_during_oper is made possible. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 6873eda03ad4ff9faecd14d97c6f1caaa5c6b9ac Author: John W. Linville Date: Fri May 19 13:46:52 2006 -0400 [PATCH] bcm43xx-d80211: remove out-dated docs and script Per request from Michael Buesch: "Please remove the file scripts/bcm43xx-d80211-sta_up.sh from the wireless-dev tree, as the d80211 user interface has been fixed and it is not really needed anymore. While you are at it, please also remove Documentation/networking/bcm43xx-d80211-HOWTO.txt Documentation/networking/bcm43xx-d80211.txt as they are completely outdated and useless these days." Signed-off-by: John W. Linville commit 132f13ee416630d3eb536d68c9cc6259d622fa0f Author: Jouni Malinen Date: Mon May 15 20:03:04 2006 +0200 [PATCH] d80211: Add support for user space client MLME Add a configuration option for disabling client MLME in kernel code. This is used to enable user space MLME for client mode (e.g., with wpa_supplicant). The kernel MLME implementation is unmodified, but it could be removed or at least made optional in build configuration in the future. Signed-off-by: Jouni Malinen Signed-off-by: Jiri Benc commit f9cb822dd2fb3be86922078193873b5a52bfdecd Author: Jiri Benc Date: Mon May 15 20:03:02 2006 +0200 [PATCH] d80211: fix is_ieee80211_device Sending packets from management interface (wmasterXap) didn't work. This patch fixes that problem; it's not nice but it will go away when the interface between kernel and hostapd is changed to netlink (the packets will be sent through master interface then). Signed-off-by: Jiri Benc commit c398e9826f4255285badf933226e931edcd41ed3 Author: Jiri Benc Date: Mon May 15 20:03:01 2006 +0200 [PATCH] d80211: use alloc_netdev Now when there are no interfaces allocated together anymore, let's use alloc_netdev for allocation of interfaces. We save some code and also the structures are really aligned finally. Signed-off-by: Jiri Benc commit 8b9c174209617565da4364c7636929200a89d7b4 Author: Jiri Benc Date: Mon May 15 20:03:00 2006 +0200 [PATCH] d80211: switching management interface on/off Default management interface (wmasterXap) confuses users. This patch removes it and gives userspace tools (such as hostapd or wpa_supplicant) possibility to switch it on/off as needed. To do this, a new PRISM2_PARAM_MGMT_IF iwpriv ioctl is introduced. When set, it accepts one parameter: 1 for enabling the interface, 0 for disabling it. When read, it returns ifindex of the management interface or -ENOENT when the management interface is switched off. Signed-off-by: Jiri Benc commit 52de810879d23af0161653fe1c2654c2de960b50 Author: Michael Wu Date: Mon May 15 20:02:59 2006 +0200 [PATCH] d80211: Don't discriminate against 802.11b drivers This makes the current hack used to prevent 802.11g cards from scanning with 802.11b channels not break scanning in 802.11b drivers. I added enabled_modes, and replaced all instances of local->hw_modes with local->enabled_modes. local->hw_modes now really means what modes are supported by the hardware. Signed-off-by: Michael Wu Signed-off-by: Jiri Benc commit 64b6e8257d3d43d18fd4532791d26129a365404b Author: Jiri Benc Date: Mon May 15 20:02:56 2006 +0200 [PATCH] d80211: fix recursive locking The rntl mutex was taken recursively in some cases. Signed-off-by: Jiri Benc commit 2686dbbec27b009d3be6bd0f2e82368a693f3a76 Author: Jiri Benc Date: Mon May 15 20:02:55 2006 +0200 [PATCH] d80211: don't config uninitialized interface config_interface callback shouldn't be called before the interface is added by add_interface callback. Signed-off-by: Jiri Benc commit 36043c91b664739a98c22108645dae883f9abeaf Author: Ivo van Doorn Date: Sat Apr 29 11:47:28 2006 +0200 [PATCH] rt2x00 drivers: rt73usb This adds the rt73usb driver to the tree Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 257db91187db30878435c31c12b794a18571bfb6 Author: Ivo van Doorn Date: Sat Apr 29 11:47:26 2006 +0200 [PATCH] rt2x00 drivers: rt61pci This adds the rt61pci driver to the tree Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit fb81e8fd66df43024d22a1513306fc80e265c6ff Author: Ivo van Doorn Date: Sat Apr 29 11:47:24 2006 +0200 [PATCH] rt2x00 drivers: Makefile & CRC From: Ivo van Doorn Change Makefile and KConfig file to add the rt61pci and rt73usb drivers to the list. The firmware needs to be validated by CRC, for this a crc header has been added to the tree as well. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 64c020c7c8337eb165c9ed6d86b27180f190a136 Author: Ivo van Doorn Date: Sat Apr 29 11:36:15 2006 +0200 [PATCH] rt2x00: Compile fix and kernel version cleanup From: Ivo van Doorn During CVS merge I noticed several compilation errors have sneaked into the git version of rt2x00. - PRIO_ENTRIES define has been removed, TX_ENTRIES should be used - poll_delay module argument is of the type short - Remove LINUX_VERSION_CODE checks. - Fix missing byteordering type - Fix typo in usb_device Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 0fa54101cb3089490063150cd8f9080c2cb4f789 Author: Ivo van Doorn Date: Fri Apr 28 16:52:29 2006 +0200 [PATCH] rt2x00 update: Apply correct error handling to dscape stack From: Ivo van Doorn Return correct error code at failure, and request debug report when the function _tx() has received an invalid queue. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit a8a4446676f7f85781a90705326a57fa03f4bd83 Author: Ivo van Doorn Date: Fri Apr 28 16:52:28 2006 +0200 [PATCH] rt2x00 update: Apply correct endian annotation for structures that are DMA'ed to device. From: Ivo van Doorn Use __le32 annotations for all fields in the descriptor, since these should always be treated as little endian. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 79fe45734075ec0cfd7c2d8917325f41cb139e22 Author: Ivo van Doorn Date: Fri Apr 28 16:52:28 2006 +0200 [PATCH] rt2x00 update: Remove packed attributes for structures that don't need it From: Ivo van Doorn Remove __attribute__ ((packed)) for structures that are not being send to the device. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit de697b28323f02c56f91d4987f393c6add25af52 Author: Ivo van Doorn Date: Fri Apr 28 16:52:27 2006 +0200 [PATCH] rt2x00 update: Use static const for the vals arrays From: Ivo van Doorn Make the vals arrays static const. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 3d0d7fa46695ead282c6110209d102c02d8604aa Author: Ivo van Doorn Date: Fri Apr 28 16:52:26 2006 +0200 [PATCH] rt2x00 update: Deactivate monitor_during_oper for now From: Ivo van Doorn Set monitor_during_oper to 0 for the time being, this feature will be supported in the future. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit fb0853591321d9f6c2ae054d0f343b875b697218 Author: Ivo van Doorn Date: Fri Apr 28 16:52:25 2006 +0200 [PATCH] rt2x00 update: Remove casts from void* pointers From: Ivo van Doorn Remove unneeded casts when working with a void* pointer. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 640beace64c995fff75d7045f037252888c03311 Author: Ivo van Doorn Date: Fri Apr 28 00:03:21 2006 +0200 [PATCH] rt2x00: misc fixes From: Ivo van Doorn Misc. small fixes. Add small comments, remove unwanted whitespaces etc. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit af8095eaaaa6af66ae02125c83dc9c13b092a184 Author: Ivo van Doorn Date: Fri Apr 28 00:03:20 2006 +0200 [PATCH] rt2x00: Correctly initialization and uninitialization of device From: Ivo van Doorn Fix several hardware initialization and uninitalization problems by incorrectly flushing workqueues. Fix the memory leak when freeing the ieee80211_hw structure. Allow device to connect to 802.11g networks by default instead of 802.11b. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 71f793bff2f793b5ac58205ab504b41860b495ce Author: Ivo van Doorn Date: Fri Apr 28 00:03:20 2006 +0200 [PATCH] rt2x00: Correctly initialize TX power in registers From: Ivo van Doorn The rate does not need to be configured for each TX packet. The actual rate for sending is determined in the PLCP, the rate configuration is only capable of setting the supported rates, which is dependent of the physical mode we are in. When we the configuration function is called, disable the RX for proper behaviour. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 8cdb737f9f632d4fb853c7b7aa2e4206fd73c7a4 Author: Ivo van Doorn Date: Fri Apr 28 00:03:19 2006 +0200 [PATCH] rt2x00: dscape compatibilitiy Latest dscape patches have broken rt2x00, fix compile issues, and support the new features. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 6af501ab9f369c8ab80f4188d49a114f313b3549 Author: Ivo van Doorn Date: Fri Apr 28 00:03:18 2006 +0200 [PATCH] rt2x00: Support TXRX led handling From: Ivo van Doorn Create more advanced led handling, enable txrx activity by switching on and off the led at interrupt time. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 0bc7c671059a037a378af6043e8512dfb4f7a1ba Author: Ivo van Doorn Date: Fri Apr 28 00:03:17 2006 +0200 [PATCH] rt2x00: Put Hardware button in generic header From: Ivo van Doorn Put the hardware button handling as much as possible in the new generic header for PCI. The individial .c files should now only contain the polling function to check the state of the hardware button. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 4a78189863c41abdda891db5509b0e484165f005 Author: Ivo van Doorn Date: Fri Apr 28 00:03:16 2006 +0200 [PATCH] rt2x00: Move all USB and PCI common data into seperate headers Now that rt2x00_pci and rt2x00_usb structures in the various headers are generic enough, add 2 header files rt2x00pci and rt2x00usb and make them contain all common information of the PCI or USB modules. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 9a313bc086ffd00262cc9f76e0f5553bed0222f1 Author: Ivo van Doorn Date: Fri Apr 28 00:03:16 2006 +0200 [PATCH] rt2x00: Add flag handlers From: Ivo van Doorn Add flag handlers to set the state and capabilities of the driver. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit c5928dff4d992bfd5b4c1532edbf5495678ba803 Author: Ivo van Doorn Date: Fri Apr 28 00:03:15 2006 +0200 [PATCH] rt2x00: Use correct desc_addr and data_addr From: Ivo van Doorn USB buffer don't have a seperate descriptor ring and data ring. The location of the descriptor area and data area depends on the type of ring. Add functions to determine the correct location and use these instead of the invalid desc_addr and data_addr pointers. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 1e73ac333eac71524061468c30394886762cfba3 Author: Ivo van Doorn Date: Fri Apr 28 00:03:14 2006 +0200 [PATCH] rt2x00: Make correct cast in USB interrupt From: Ivo van Doorn The USB interrupt handler receives the entry in the ring and not the ring itself. Also check if the status indicates an error before queueing the work. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 32034c57eb1687205e2bf522e9202cb0e68368d5 Author: Ivo van Doorn Date: Fri Apr 28 00:03:13 2006 +0200 [PATCH] rt2x00: Allocate ring structures in single array Allocate all ring structures seperately and as an array of rings. This will make the rt2x00_pci and rt2x00_usb structures more generic for all rt2x00 modules. Use seperate function to convert a dscape ring ID to the address of the actual ring. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 268c2f64dca524775c6293cd4933a6e1a6d0f53c Author: Ivo van Doorn Date: Fri Apr 28 00:03:13 2006 +0200 [PATCH] rt2x00: PRIO ring should be treated as regular TX ring From: Ivo van Doorn Remove PRIO_ENTRIES define, the prio ring should be treated as a regular TX ring. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 74d4f3c27d949249ff0344ce438639ea32a5efa7 Author: Ivo van Doorn Date: Fri Apr 28 00:03:12 2006 +0200 [PATCH] rt2x00: byte ordering correctness From: Ivo van Doorn Fix various little/big endian conversions. rt2500pci should use cpu_to_le32 and rt2500usb should not. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit b904b37ed1c274a8f9ef85055c3897802d6d3337 Author: Ivo van Doorn Date: Fri Apr 28 00:03:11 2006 +0200 [PATCH] rt2x00: Fix panics in interrupt handlers From: Ivo van Doorn Fix panics when the interrupt handlers are being run while the ring is empty. During the interrupt handling break the loop correctly when an error has been detected, more work is being done after the loop. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit ac73a39c101730b1319082654886bbc2f1c9f13c Author: Ivo van Doorn Date: Fri Apr 28 00:03:10 2006 +0200 [PATCH] rt2x00: Make sure device has reached requested state while suspend/resume From: Ivo van Doorn Add the *_set_state functions which makes sure the device is switching state to awake or sleep. Fix bad behaviour in the suspend routine, and disable the radio before suspending. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 8b657d443adaa4ca4ebc2ce5f06230960b6225a0 Author: Ivo van Doorn Date: Fri Apr 28 00:03:09 2006 +0200 [PATCH] rt2x00: Put net_device structure in data_ring From: Ivo van Doorn Change the structure stored in the data_ring structure from the rt2x00_pci or rt2x00_usb to net_device. This allows for better type checking, and the net_dev is more often used in the interrupt handlers. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 66da8235acd39f92d67f3e6a70ef4d5ab4f8df62 Author: Ivo van Doorn Date: Fri Apr 28 00:03:08 2006 +0200 [PATCH] rt2x00: Make sure TX rings are empty when scanning From: Ivo van Doorn Improve the waiting when a skb buffer needs to be send before the channel switch. This also makes sure no pending packets are still on the TX ring while making the channel switch. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 2a1fc334c59da0d2bc067a3af04f255e36f28306 Author: Ivo van Doorn Date: Fri Apr 28 00:03:07 2006 +0200 [PATCH] rt2x00: Move rx_params to correct location From: Ivo van Doorn Store rx_params seperately outside the ring structure. This is more safer and required because of some changes in the way the rings are stored in the structure in the following patches. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 7c728b1ec92d9eb246d482a2c631f8d77e3e663e Author: Ivo van Doorn Date: Fri Apr 28 00:03:05 2006 +0200 [PATCH] rt2x00: Allocate eeprom memory From: Ivo van Doorn Make the EEPROM array in rt2x00_pci and rt2x00_usb a pointer, and allocate the memory seperately. This is needed for make the structures more generic for all rt2x00 pci or usb modules. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit eec147420544283053abd3456efc48579d1eced2 Author: Ivo van Doorn Date: Fri Apr 28 00:03:04 2006 +0200 [PATCH] rt2x00: Tune link depending on link quality From: Ivo van Doorn Add link tuning capabilities, and call this function every time the rxdone handler has finished. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit d01ebff714db04d818f0220832b8f4a28235e2d4 Author: Ivo van Doorn Date: Fri Apr 28 00:03:03 2006 +0200 [PATCH] rt2x00: Add USB ID's From: Ivo van Doorn Remove the rt73usb ID that accidently sneaked into rt2500usb. And add new rt2500usb ID's. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 3856574a9d44fe6b7d9a5dbe6cbcf6df13c4715c Author: Ivo van Doorn Date: Fri Apr 28 00:03:02 2006 +0200 [PATCH] rt2x00: Add more register defines From: Ivo van Doorn During the work on rt2x00 several new registers could be defined. This will add all those new registers, and will in the next couple of patches be used. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 41d3482bcfaddc9639334b7463299211e340ee50 Author: Ivo van Doorn Date: Fri Apr 28 00:03:01 2006 +0200 [PATCH] rt2x00: Move TSF counting activation to correct funtion From: Ivo van Doorn Move the enabling of TSF counting into *_config_mode where it actually belongs. For rt2500usb this means that the rt2500usb_reset_tsf function is now removed since it is still unknown in what registers the TSF counters are stored in. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 5999330a276c083f3acd39edab1d4d50704fcb1c Author: Ivo van Doorn Date: Fri Apr 28 00:03:00 2006 +0200 [PATCH] rt2x00: Fix antenna configuration From: Ivo van Doorn The handling of the antenna configuration was not completely correct. For all modules the double clearing of some bits can be reduced, and for rt2500pci and rt2500usb some registers were not corretly changed. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit c1de6d8769f16ff9eb20a896cc7b10ebe17fcca8 Author: Ivo van Doorn Date: Fri Apr 28 00:02:58 2006 +0200 [PATCH] rt2x00: Invalid memory allocation check From: Ivo van Doorn Fix invalid check when allocating the memory for the rate structures. Instead of the channel pointer the rates pointer should be verified. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 203dd0790373de25aad8e13105bdf02079919ca0 Author: Ivo van Doorn Date: Fri Apr 28 00:02:57 2006 +0200 [PATCH] rt2x00: make vals static From: Ivo van Doorn The vals[] arrays in *_init_hw_channels can be made static to optimize memory and reduce stack size. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit e1db06a33f410f802673e3643555adb3d9f5d4c1 Author: Ivo van Doorn Date: Fri Apr 28 00:02:56 2006 +0200 [PATCH] rt2x00: Use arraylike accessors for entries in DMA ring From: Ivo van Doorn Make the code a bit more readable by using array like accessors for pointers in a loop. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 956d2dabc4e513e8b5769784e25188b5253fcac9 Author: Ivo van Doorn Date: Fri Apr 28 00:02:55 2006 +0200 [PATCH] rt2x00: Optimize RATE flag handling From: Ivo van Doorn Optimize RATE flags by using the FIELD32() macro's, also make the unit in which the rate is handled the same as is used in the dscape stack. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 7ab5cc3002c9eafdce17195abf9bd549999d5710 Author: Ivo van Doorn Date: Fri Apr 28 00:02:53 2006 +0200 [PATCH] rt2x00: Add eeprom_multiread function From: Ivo van Doorn Add the eeprom_multiread function and clean up the code a bit by using it as well. ;) Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit a030ed520a19eeb52c0bca11a7a3ce7d3051312a Author: Ivo van Doorn Date: Fri Apr 28 00:02:52 2006 +0200 [PATCH] rt2x00: use pci_*_consistent for DMA mapping Add linux/dma-mapping.h header to allow compilation on some architectures. Instead of dma_*_coherent use pci_*consistent functions. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 40733a96bf8ba9e3a418380c9bb9999166d3b688 Author: Ivo van Doorn Date: Fri Apr 28 00:02:50 2006 +0200 [PATCH] rt2x00: use enumerations From: Ivo van Doorn The led_mode defines are equal in all drivers, and should be placed in the common rt2x00.h header. Make the led_mode, tx_status and dev_state defines into enumerations. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 34d2a6f0e14759a01780afcbb6e72426e127edea Author: Ivo van Doorn Date: Fri Apr 28 00:02:48 2006 +0200 [PATCH] rt2x00: code style fix Coding style fix for all rt2x00 drivers. This change was requested on the netdev list some time ago, but the patch send then didn't contain all requested changes. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 6d61f4a40274b90f8e9a7ee537c013045828f7de Author: Jiri Benc Date: Fri Apr 21 22:52:10 2006 +0200 [PATCH] bcm43xx: fix breakage from d80211 patches On Fri, 21 Apr 2006 22:52:08 +0200, Michael Buesch wrote: > Can you please send your hacky patch for the bcm43xx > to me, so I can come up with a clean one? Sure, actually I planned to do it in a few minutes :-) drivers/net/wireless/d80211/bcm43xx/bcm43xx.h | 1 drivers/net/wireless/d80211/bcm43xx/bcm43xx_main.c | 45 +++++++++++++++++++-- 2 files changed, 42 insertions(+), 4 deletions(-) Signed-off-by: John W. Linville commit dd6804a119c5ee1f8314a46a7ee327e6e7719059 Author: Jiri Benc Date: Fri Apr 21 22:53:30 2006 +0200 [PATCH] d80211: add one default interface The wireless card is useless with master interface (wmasterX) only. Adding at least one virtual interface is necessity for every user. To save users a lot of pain (and to maintain backward compatibility) we should add one virtual interface by default. It is called wlanX (hopefully the name users are used to) and set to STA mode. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit 432899381d8d05b9ca55832277493204b1402559 Author: Jiri Benc Date: Fri Apr 21 22:53:29 2006 +0200 [PATCH] d80211: rename master interface Rename master interface to wmasterX to better reflect its purpose. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit a6c41a0ba2892cf3c34bfc2dc4d26d0e2c6df35f Author: Jiri Benc Date: Fri Apr 21 22:53:27 2006 +0200 [PATCH] d80211: get rid of default AP interface There is no need for default non-removable AP interface (wlanX), it just confuses users. This patch removes it and renames master interface from wlanX.11 to wlanX. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit 0c73b189dda4b4e0a2e22071dda94933848eb6cb Author: Jiri Benc Date: Fri Apr 21 22:53:26 2006 +0200 [PATCH] d80211: per-interface generic_elem Allow generic element to be set independently for each interface. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit 5417aaf868884185f632e2855ab58e9d941e46ff Author: Jiri Benc Date: Fri Apr 21 22:53:25 2006 +0200 [PATCH] d80211: per-interface SSID Allow SSID to be set independently for each interface. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit 1496751e1ff07b26e092af6185891a008b777412 Author: Jiri Benc Date: Fri Apr 21 22:53:24 2006 +0200 [PATCH] d80211: don't use pointer in ieee80211_tx_control A problem similar to the one with ieee80211_sub_if_data and skb->cb happens to ieee80211_sub_if_data and ieee80211_tx_control. When originating interface is removed while packet is sent to the driver, bad things will happen. Use ifindex instead. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit 04abe157e6ddb6194af336ec7bffcb53d4905151 Author: Jiri Benc Date: Fri Apr 21 22:53:23 2006 +0200 [PATCH] d80211: fix Oops caused by packets sent directly to master device Sending packets to master interface directly causes dereferencing of pointer containing random data. The same problem happens when originating virtual interface is removed while a packet is queued. We really shouldn't store pointer to ieee80211_sub_if_data in skb->cb. Store ifindex there instead. Also, there is no need for internal ieee80211_tx_packet_data structure to be declared in d80211.h. As this patch touches this structure anyway, let's move it to ieee80211_i.h. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit e3da2a26c99cb1ee511ba579ad57c2a8e4f8314c Author: Jiri Benc Date: Fri Apr 21 22:53:21 2006 +0200 [PATCH] d80211: use is_multicast_ether_addr Replace custom MULTICAST_ADDR macro with is_multicast_ether_addr function. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit 11ea228f7159eed0c9e27c0431bb99b707e3a634 Author: Jiri Benc Date: Fri Apr 21 22:53:20 2006 +0200 [PATCH] d80211: fix SIOCGIWESSID ioctl Flags for SIOCGIWESSID ioctl were not set, thus SSID was never displayed by iwconfig. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit 022c2a9e386bb23698d90513b39eadac225eb5bd Author: Jiri Benc Date: Fri Apr 21 22:11:48 2006 +0200 [PATCH] d80211: fix AP interfaces This allows interfaces of IEEE80211_IF_TYPE_MGMT type (wlan%dap interfaces) to be put UP. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit 1969af5b307dc2cca35b63e47178941f39bcab27 Author: Jiri Benc Date: Fri Apr 21 22:11:47 2006 +0200 [PATCH] d80211: fix monitor interfaces This patch allows monitor interfaces to be set by SIOCSIWMODE and to receive frames. Also, "soft" and "hard" monitor modes are introduced. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit 15679a8df0b2f2f0f2313b1a7a4204bbd219b71d Author: Jiri Benc Date: Fri Apr 21 22:11:46 2006 +0200 [PATCH] d80211: fix handling of received frames Make sure that every frame reaches every interface it belongs to. Previously, some packet (most notably multicast ones) were not delivered to all interface they should be delivered to. This also allows monitor interfaces to work easily, even together with regular interfaces. On a typical setup (i.e. one or two interfaces only) this patch shouldn't have significant impact on performance. However, on a setup with great number of interfaces, things will probably slow down. This is not a problem with design of this patch - things can be relatively easily sped up later. See related comment in 'd80211: remove obsolete stuff' patch. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit e7a9d50985c3583951a663c68a3989b6c1ff9256 Author: Jiri Benc Date: Fri Apr 21 22:11:45 2006 +0200 [PATCH] d80211: set_multicast_list Add set_multicast_list callback. The version of set_multicast_list in struct net_device cannot be used by a driver, because the driver is interested in cumulative flags and cumulative multicast list from all interfaces. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit 814a42456d04a4a410baf0909d81ba9353d6800f Author: Jiri Benc Date: Fri Apr 21 22:11:44 2006 +0200 [PATCH] d80211: master interface auto up/down There is no reason to put master interface to UP/DOWN state manually. Calling of hw->open and hw->stop callbacks logically belongs to ieee80211_master_open and ieee80211_master_stop functions, but then we need to refuse putting master interface to UP state when there is no other interface running, and similarly, to refuse putting master interface DOWN when there are other interfaces running. Because the second is not possible, hw->open and hw->stop need to be called from ieee80211_open/ieee80211_stop. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit f7e50830eadcb56228bc21f9356aad89aaa7e4d3 Author: Jiri Benc Date: Fri Apr 21 22:11:43 2006 +0200 [PATCH] d80211: interface types changeable by SIOCSIWMODE Allow type of interface to be set by SIOCSIWMODE. All of functions responsible for adding/removing/initialization of interfaces were moved to a new file ieee80211_iface.c. Function for removing interface was split into two parts: one for deinitialization of the interface and one for deallocation of the interface. That way, it is possible to change the type of interface just by deinitializing and initializing it. Also, remove set_mac_address callback to a driver, as it is not needed anymore (drivers are notified about MAC addresses through add_interface callback). Please note, that although after this patch interfaces are fully independent and driver can correctly control which combination is allowed, not all multicast frames are received correctly by all respective interfaces. This is addressed by subsequent patches. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit 35d0c7d7c63813991f3ac479eec43957c51a6bd4 Author: Jiri Benc Date: Fri Apr 21 22:11:42 2006 +0200 [PATCH] d80211: rename adm_status to radio_enabled PRISM2_PARAM_ADM_STATUS is not much descriptive name. This patch renames it to PRISM2_PARAM_RADIO_ENABLED, sets radio_enabled to 1 by default (this is no problem as radio must not be enabled until at least one network interface is running) and removes automatic setting of adm_status when interface is in a STA mode. Later, PRISM2_PARAM_RADIO_ENABLED value can be removed and radio_enabled set by SIOCSIWTXPOW handler (ie. by iwconfig txpower off). Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit 8a213e6153a4f0222f8f14bc9d10b21179b5f3d3 Author: Jiri Benc Date: Fri Apr 21 22:11:41 2006 +0200 [PATCH] d80211: fix interface configuration This patch fixes some problems in interface configuration. - Pass interface ID to add_interface and remove_interface callbacks. This ID is used by a driver when calling ieee80211_beacon_get and ieee80211_get_buffered_bc functions. - New configuration callback, config_interface, is introduced. - Allow BSSID to be set per-interface. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit 26b3626dda481a3ebe2ea4f59d27ffd0f3b724be Author: Jiri Benc Date: Fri Apr 21 22:11:40 2006 +0200 [PATCH] d80211: remove obsolete stuff Because any number of interfaces may be added, bss_devs and sta_devs arrays cannot be fixed-size arrays. We can make them linked lists, but they are needed for optimalization only (and even that is questionable with subsequent patches). Let's remove them; we will probably want something similar later to speed up packet receiving, but let's not bother ourselves now. Also, ieee80211_addr_inc is removed. Choosing of MAC address of a new STA should be matter of userspace. It's responsibility of the stack not to allow two STAs with the same MAC address to be up - this feature is introduced in one of subsequent patches. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit bae789f902dd29015938de2d65a4f52474ccd89f Author: Jiri Benc Date: Fri Apr 21 22:11:39 2006 +0200 [PATCH] d80211: ask driver for allowed iface combinations Not all combinations of interfaces (in fact, very few combination of interfaces) are possible to be UP together. When an interface is going UP, let's ask the driver if this is possible. Please note that ieee80211_if_init_conf structure is not complete yet - new fields will need to be added to allow drivers to decide. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit 0e5b90bd2c8aed8d6fa95588c8e84bc32c941a72 Author: Jiri Benc Date: Fri Apr 21 22:11:38 2006 +0200 [PATCH] d80211: rename IEEE80211_SUB_IF_TYPE_ constants As we're going to expose IEEE80211_SUB_IF_TYPE_* constants to drivers, the prefix IEEE80211_SUB_IF_TYPE_ is no longer appropriate. The constants are going to mean the type of 802.11 interface, not the type of sub-interface structure (which is not visible to drivers at all). This patch renames them to IEEE80211_IF_TYPE_*. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit f265668d1d09b6427763a71db22d8d7bfa55c70e Author: Jiri Benc Date: Fri Apr 21 22:11:37 2006 +0200 [PATCH] d80211: remove local->bssid variable BSSID shouldn't be common for all interfaces. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit eace5bb89c149fcda8aa36f612eecb2cb625ed86 Author: Jiri Benc Date: Fri Apr 21 22:11:36 2006 +0200 [PATCH] d80211: non-shared interface types This patch removes "iwmode" variable (local->conf.mode) shared by all interfaces. Instead, every interface has its own type (STA/IBSS/AP/WDS). Please note, that - Actual SIOCSIWMODE ioctl is disabled by this patch and is implemented by one of subsequent patches. - There is no way to ask the driver if given combination of interfaces is possible. Again, it is implemented by one of subsequent patches. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit bf93cd076c00c5dbc345eb3693c04d8fc7cf2a3d Author: Jiri Benc Date: Fri Apr 21 22:11:35 2006 +0200 [PATCH] d80211: add IBSS and monitor interface types Add constants for IBSS and monitor interface types. These constants are used in subsequent patches. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit 8f3f93386580aa20006463af1756a41c378683b2 Author: Jiri Benc Date: Fri Apr 21 22:11:34 2006 +0200 [PATCH] d80211: allow WDS remote to by set by WE Setting of address of WDS remote peer wasn't possible by a WE call. Remote WDS peer can be understood as a remote AP and SIOCSIWAP/SIOCGIWAP are unused in WDS mode, so let's use them. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit 9b748b0683c3c1efcd0de7419e9cd8d52f8130f2 Author: Jiri Benc Date: Fri Apr 21 22:11:33 2006 +0200 [PATCH] d80211: symlinks to wiphy in sysfs This patch adds symlinks under /sys/net/*/wiphy pointing to /sys/class/ieee80211/phyX. This allows new interfaces to be added by writing a new name to e.g. /sys/net/wlan0/wiphy/add_iface. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit 761a79d9e7a365a9c46098a901cdcf4b7c3933d0 Author: Jouni Malinen Date: Fri Apr 21 22:11:32 2006 +0200 [PATCH] d80211: Replace MODULE_PARM with module_param MODULE_PARM macro was removed and this broke net/d80211 build. Fix this by using module_param instead of MODULE_PARM. Signed-off-by: Jouni Malinen Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit 175c1bfae0f7bc3d3f462341a11ece27bc177c06 Author: Jouni Malinen Date: Wed Apr 26 14:45:32 2006 -0400 [PATCH] Move d80211-based drivers into new subdirectory Move rt2x00 and bcm43xx-d80211 drivers into a new drivers/net/wireless/d80211 subdirectory. This directory is used for collecting wireless LAN drivers that use the Devicescape IEEE 802.11 stack (net/d80211). Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville commit ed0f1c27626f7f4e171afc52566b2fc7ca430e4c Author: Michael Buesch Date: Wed Apr 26 14:37:09 2006 -0400 [PATCH] bcm43xx: sysfs code cleanup This cleans up the bcm43xx sysfs code and makes it compliant with the unwritten sysfs rules (at least I hope so). Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit ce193cdeca0fe1c0ea4d2e3c84478bcbb374e797 Author: Michael Buesch Date: Wed Apr 26 14:35:48 2006 -0400 [PATCH] bcm43xx: fix pctl slowclock limit calculation This fixes coverity bug: http://marc.theaimsgroup.com/?l=linux-netdev&m=114417628413880&w=2 Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 85ccbc3594d1562cfd47873bd6473b937e2f7dc6 Author: Adrian Bunk Date: Wed Apr 26 14:32:58 2006 -0400 [PATCH] bcm43xx: fix dyn tssi2dbm memleak This patch fixes a memory leak spotted by the Coverity checker. Signed-off-by: Adrian Bunk Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit ac2b584134891094d3e1308a95b6c292435d13da Author: Michael Buesch Date: Mon Apr 10 02:25:00 2006 +0200 [PATCH] bcm43xx-d80211: protect tx_stat callback from uninitialized device Signed-off-by: John W. Linville commit bbdf0606ad07432f75b4cc73134372a161a513d8 Author: Michael Buesch Date: Mon Apr 10 02:05:42 2006 +0200 [PATCH] bcm43xx-d80211: use pci_iomap() for convenience. This reduces codesize. Signed-off-by: John W. Linville commit ca95a9c8eda263fbdefbfdfbfd32ace1670dab93 Author: Michael Buesch Date: Sat Mar 25 20:36:57 2006 +0100 [PATCH] bcm43xx-d80211: sync GPHY init with the specs. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 9355a746d3ed04e3c9c4c403e50ea084488e4363 Author: Michael Buesch Date: Sat Mar 25 17:04:41 2006 +0100 [PATCH] bcm43xx-d80211: fix the remaining sparse warnings. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 90a3b816b4bf800bc1d59ac039d5672986063be3 Author: Michael Buesch Date: Sat Mar 25 15:48:45 2006 +0100 [PATCH] bcm43xx-d80211: remove some compilerwarnings. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 65e4b99ca5b17d42b37497a2e1f6eea9c240ab32 Author: Michael Buesch Date: Sat Mar 25 15:43:18 2006 +0100 [PATCH] bcm43xx-d80211: fix "include" issues on some platforms. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 3ba5cbb8bd992e118b1d8d87421b4fa4645294a7 Author: Michael Buesch Date: Sat Mar 25 15:37:53 2006 +0100 [PATCH] bcm43xx-d80211: get rid of "/* vim: ..." lines at the end of several files. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit e5810b5894483077e0c7c7706a1125789263c63d Author: Michael Buesch Date: Wed Mar 22 17:58:47 2006 +0100 [PATCH] bcm43xx-d80211: fix nrssi_threshold calculation. patch by doctorzoidberg. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 0662061f69c7b00b3da0eb9cdb5697f8a4f550c4 Author: Michael Buesch Date: Tue Mar 21 18:16:28 2006 +0100 [PATCH] bcm43xx-d80211: sync interference mitigation code to the specs. This also includes a rewritten valuesave-stack. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit b07f972175b6bbd3d613ee311e41d7013f38575c Author: Michael Buesch Date: Mon Mar 20 00:01:04 2006 +0100 [PATCH] bcm43xx-d80211: set default attenuation values. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 79884f6b33b9bbc0cc423bdc0bb47a75a25e670d Author: Michael Buesch Date: Sun Mar 19 22:07:42 2006 +0100 [PATCH] bcm43xx-d80211: add missing lock in TX timeout callback. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 8387f4db18212fd11ed8c4c4efb2b6e219715f4b Author: Michael Buesch Date: Sun Mar 19 21:43:40 2006 +0100 [PATCH] bcm43xx-d80211: some IRQ handler cleanups. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 7b6b87546434d0b7e89cf9e08e6c6f3705346e9c Author: Michael Buesch Date: Sun Mar 19 17:18:48 2006 +0100 [PATCH] bcm43xx-d80211: merge all iwmode code into the set_iwmode function. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 94860e15207a41cf4b594ff1644bfdce6d227d88 Author: Michael Buesch Date: Sat Mar 18 21:28:46 2006 +0100 [PATCH] bcm43xx-d80211: fix some gpio register trashing (hopefully :D) Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 3fabff7e5e39b1759abaa4a7a38e6a97f3e5db5c Author: Michael Buesch Date: Sat Mar 18 20:19:12 2006 +0100 [PATCH] bcm43xx-d80211: remove check for mmio length, as it differs among platforms. (especially embedded) Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit a9e04ba7fe038281b0b81283c08cf018c7f80beb Author: Michael Buesch Date: Wed Mar 15 18:13:53 2006 +0100 [PATCH] bcm43xx-d80211: properly mask txctl1 before writing it to hardware. This should not make a difference, but be careful to not trash the register. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit ecec43331c96090542ec2459b4bccaa222dd94b9 Author: Michael Buesch Date: Wed Mar 15 16:31:45 2006 +0100 [PATCH] bcm43xx-d80211: Do boardflags workarounds for specific boards. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 0d772f0faf45a43de1a23c542fbf25065940912f Author: Michael Buesch Date: Tue Mar 14 18:23:43 2006 +0100 [PATCH] bcm43xx-d80211: Remove the workaround in dummy_transmission, as it causes more trouble than it solves Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit b31c5aa9bf09a9e08b95e9ab8d61b921cfad6eee Author: Michael Buesch Date: Tue Mar 14 16:05:26 2006 +0100 [PATCH] bcm43xx-d80211: Fix crash on ifdown, by being careful in pio/dma freeing. This bug was caused by the packing of the bcm43xx_dma and bcm43xx_pio structures into a union. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 183283cba15fae6f8f12298f113d09733fafd9fb Author: Michael Buesch Date: Mon Mar 13 19:27:34 2006 +0100 [PATCH] bcm43xx-d80211: reduce the size of bcm43xx_private by removing unneeded members. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit e95e2b959488525809b3e0ec7eb1df36ca40666b Author: Michael Buesch Date: Mon Mar 13 15:54:56 2006 +0100 [PATCH] bcm43xx-d80211: add functions bcm43xx_dma_read/write, bcm43xx_dma_tx_suspend/resume. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 5be1027dbb74787db7233ff92b4f955b7ae5ab17 Author: Michael Buesch Date: Mon Mar 13 15:20:05 2006 +0100 [PATCH] bcm43xx-d80211: receive TX status on MMIO or DMA unconditionally regarding the 80211 core rev. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit c08c09d187b24e93384e6887ddc7cef6a124390a Author: Michael Buesch Date: Sun Mar 12 19:44:29 2006 +0100 [PATCH] bcm43xx-d80211: fix some stuff, add a few missing mmiowb(), remove dead code. This may workaround the XMIT ERRORs some people are getting. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 09e8352a7b956416498869228141be3c2ad9805d Author: Michael Buesch Date: Sat Mar 11 12:51:17 2006 +0100 [PATCH] bcm43xx-d80211: Remove the mmio access printing facility overhead. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 5f5da516438ea25665a9e9f85dcd74ea915a7e68 Author: Michael Buesch Date: Sat Mar 11 02:07:43 2006 +0100 [PATCH] bcm43xx-d80211: Abstract the locking mechanism. This is the starting point to make the driver out-of-order-MMIO-stores safe. There are more mmiowb() needed. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit dcc94b64211a663a167121a94c2f4948abc2ccf4 Author: Michael Buesch Date: Sat Mar 11 01:15:11 2006 +0100 [PATCH] bcm43xx-d80211: Set both, the DMAmask and the coherent DMAmask. This has a potential to fix the >1G bug. But I can not test that, yet. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 44891322b632e4086de60866699812fb884feff3 Author: Jouni Malinen Date: Fri Mar 3 18:54:25 2006 -0800 [PATCH] d80211: Replace local AES implementation with Crypto API Replace local AES implementation (net/d80211/aes.c) with calls to Crypto API. Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville commit 274620b3ea0ece0622438a68f987a3f2bf7a6f77 Author: Jouni Malinen Date: Fri Mar 3 18:54:24 2006 -0800 [PATCH] d80211: Allow hostapd_ioctl.h for user space programs This file defines the current ioctl parameters and data structures. It has been used in user space programs and including linux/types.h is undesired when building for non-kernel code. In addition, ieee80211_shared.h was renamed, so let's also get rid of the #include for it in the non-kernel case. Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville commit cf6cab783833bb724141eb6d9104ef28942a6dc5 Author: Ivo van Doorn Date: Tue Feb 28 20:46:54 2006 +0100 [PATCH] RT2x00 update: trivial fixes ieee80211_rx has been renamed __ieee80211_rx. Use DRV_NAME as much as possible instead of a seperate name string. Add new USB device ID. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 0fedd8b6c590c64dd971ea89b30621271fa8db7d Author: Michael Buesch Date: Tue Feb 28 16:07:27 2006 +0100 [PATCH] bcm43xx-d80211: remove magic add_sta.c and add the STA interface through sysfs. Signed-off-by: Michael Buesch commit 38f3e681984a836e2ecd526de67bb57f559a61a4 Author: Michael Buesch Date: Tue Feb 28 15:22:48 2006 +0100 [PATCH] bcm43xx-d80211: properly register device attributes at the right place. Signed-off-by: Michael Buesch commit 815e047a2d08dcc57e69eccb25cf21d5ed1711a9 Author: Ivo van Doorn Date: Sun Feb 12 14:47:58 2006 +0100 [PATCH] RT2x00 coding style fix Various coding style fixes. Add space after "if" and "for" and "else". Don't check for non-NULL before calling kfree(). Remove unneeded initializors. Signed-off-by Ivo van Doorn Signed-off-by: John W. Linville commit b2b945ac949b38750fa911210a7180b75b903fdd Author: Ivo van Doorn Date: Sun Feb 12 00:00:20 2006 +0100 [PATCH] RT2x00 update: Suspend fix After writing PUT_TO_SLEEP to the device, wait untill the CURRENT_STATE of the device indicates it is asleep before returning from the suspend function. When suspend fails, call the resume function to bring the device back to live again. Signed-off-by Ivo van Doorn Signed-off-by: John W. Linville commit c7f1ead1e8ff9766245d7c7cfd783553c5b4dc73 Author: Ivo van Doorn Date: Sun Feb 12 14:47:54 2006 +0100 [PATCH] RT2x00 update: Channel initialization cleanup Cleanup channel initialization and remove device specific value magic calculations. Just deliver a plain list of values. Read the EEPROM for default TXpower values for each channel. Signed-off-by Ivo van Doorn Signed-off-by: John W. Linville commit 20a015f0b8f6afcb6c462ff26ce09da1168107f3 Author: Ivo van Doorn Date: Sun Feb 12 14:47:51 2006 +0100 [PATCH] RT2x00 update: Allocate ieee80211_hw for each device seperately Allocate the ieee80211_hw structure for each device individually to allow the driver to support multiple devices at the same time. Signed-off-by Ivo van Doorn Signed-off-by: John W. Linville commit 152b2bd2a323331194f04324a72e2f42e37780bb Author: Ivo van Doorn Date: Sun Feb 12 00:00:08 2006 +0100 [PATCH] RT2x00 update: CREDITS and MAINTAINERS Add rt2x00 to MAINTAINERS file. Add the rt2x00 developers to the CREDITS file. Signed-off-by Ivo van Doorn Signed-off-by: John W. Linville commit 0f4df2fc596c1b019165842a68aa954811c26a45 Author: Michael Buesch Date: Sat Feb 25 13:46:30 2006 +0100 [PATCH] bcm43xx-d80211: fix typo in comment. Signed-off-by: Michael Buesch commit 5217889cd22f94de28bd974b457c27546171883d Author: Michael Buesch Date: Thu Feb 23 21:15:39 2006 +0100 [PATCH] bcm43xx-d80211: Move TX/RX related functions to its own file. Add basic RTS/CTS code. Signed-off-by: Michael Buesch commit 2d623123c897c779a903d623dd3a14506ac1832d Author: Michael Buesch Date: Tue Feb 21 18:28:16 2006 +0100 [PATCH] bcm43xx-d80211: Remove obsolete FIXME on wireless_handlers. Signed-off-by: Michael Buesch commit 4b03e5cc6173b2ce494bdae7186204aa5220570c Author: Michael Buesch Date: Tue Feb 21 18:08:55 2006 +0100 [PATCH] bcm43xx-d80211: move initialized = 1 to the end of init_board. Note that the periodic work has to be started with initialized==1 Signed-off-by: Michael Buesch commit 4b3009df0ed95cd8d03b981b9991db06b8e39de1 Author: Michael Buesch Date: Tue Feb 21 17:58:18 2006 +0100 [PATCH] bcm43xx-d80211: Workaround init_board vs IRQ race. The proper fix for this is to move IRQ enabling to the end of init_board. But this is nontrivial and needs to be done with care. Stay with this cheap workaround for now. Signed-off-by: Michael Buesch commit 74a6251e4caeb3c1958f50f22ab8544734177f8c Author: Michael Buesch Date: Tue Feb 21 17:30:27 2006 +0100 [PATCH] bcm43xx-d80211: Don't build add_sta, if it already exists. Signed-off-by: Michael Buesch commit e0b0096bec24c0256f3619f381a2b071fd8f86e9 Author: Michael Buesch Date: Tue Feb 21 17:21:39 2006 +0100 [PATCH] bcm43xx-d80211: use -Dwext for wpa_supplicant. This removes the need to patch wpa_supplicant. Signed-off-by: Michael Buesch commit 2080619ad0f15508b1363acb3ddfa9efaacae46d Author: Michael Buesch Date: Mon Feb 20 22:48:55 2006 +0100 [PATCH] bcm43xx-d80211: Fix Kconfig typo (transfer mode default) Signed-off-by: Michael Buesch commit 05b957cb031d77fd206f149011e012aa0e7ce8e8 Author: Michael Buesch Date: Sun Feb 12 22:40:39 2006 +0100 [PATCH] bcm43xx-d80211: rewrite and simplify the periodic task handling. Signed-off-by: Michael Buesch commit 7e633a87ea77faac4bee18fc2370813677fc00fd Author: Michael Buesch Date: Mon Feb 20 18:42:21 2006 +0100 [PATCH] bcm43xx-d80211: Completely remove all WX and add a sysfs interface as substitute for the private WX. Signed-off-by: Michael Buesch commit 9effbcb87038546b1f465b2aa1ee5190ab48ab78 Author: Jouni Malinen Date: Fri Feb 17 13:59:42 2006 -0800 [PATCH] d80211: Whitespace cleanup - no functional changes Let's clean up some of the whitespace use (extra lines, trailing whitespace, incorrect indentation). Signed-off-by: Jouni Malinen Signed-off-by: Jiri Benc commit 498b781583778c21bb766af79951cf1261329c73 Author: Jouni Malinen Date: Fri Feb 17 13:59:41 2006 -0800 [PATCH] d80211: Add radar detection parameters Add parameters for radar detection that were previously left as a to-do item. Signed-off-by: Jouni Malinen Signed-off-by: Jiri Benc commit 756ff9b11d1b3bc8c45bd0eab1fdca15c2df5312 Author: Jouni Malinen Date: Fri Feb 17 13:59:40 2006 -0800 [PATCH] d80211: Remove EXPORT_SYMTAB define No need to define EXPORT_SYMTAB separatel here when this is built inside the current kernel tree. Signed-off-by: Jouni Malinen Signed-off-by: Jiri Benc commit e81b1bc0f272a50458ab6ae8777f6327af0248e5 Author: Jouni Malinen Date: Fri Feb 17 13:59:39 2006 -0800 [PATCH] d80211: Update rx.skb after RX handler calls RX handlers are allowed to change rx.skb pointer in the same way as TX handlers. In other words, ieee80211_rx() must use the new pointer after the RX handler loop has been completed to avoid freeing incorrect skb if the frame ends up being dropped after the skb pointer has been changed. Signed-off-by: Jouni Malinen Signed-off-by: Jiri Benc commit 7f1b85f05a0736ffb468f7bd8c4e45788d2e8acb Author: Jiri Benc Date: Mon Feb 20 12:42:10 2006 +0100 [PATCH] d80211: fix nonterminated sysfs attributes Attributes for phyX class device were not terminated. This patch fixes that. Signed-off-by: Jiri Benc commit 9eb9823c4c71f77deb6e143cfc435c0c97bdab4b Author: Michael Buesch Date: Sun Feb 19 22:22:42 2006 +0100 [PATCH] bcm43xx-d80211: remove old unused struct. Signed-off-by: Michael Buesch commit 969bdd2e22145fbd040c5726ac1d74c8d80c4002 Author: Michael Buesch Date: Sun Feb 19 22:20:16 2006 +0100 [PATCH] bcm43xx-d80211: split channel helper functions, so that they can be called without a valid running core. Signed-off-by: Michael Buesch commit 7ca0f7248a562611d7efb37451fedee106f23062 Author: Michael Buesch Date: Sun Feb 19 14:25:09 2006 +0100 [PATCH] bcm43xx-d80211: remove all remaining standard WX. Signed-off-by: Michael Buesch commit d41910a18a4868fb9c9668ee8eba7d133f1e5f58 Author: Michael Buesch Date: Sun Feb 19 14:12:22 2006 +0100 [PATCH] bcm43xx-d80211: make bcm43xx_sprom_crc() static. Signed-off-by: Michael Buesch commit b6a28bdb0b15815f7e906ddd8f6bd0412ea6c536 Author: Michael Buesch Date: Sun Feb 19 14:09:20 2006 +0100 [PATCH] bcm43xx-d80211: Move sprom lowlevel reading/writing to its own functions. Signed-off-by: Michael Buesch commit 2e954fef1f10859005eeaf02a7c0765f5eeced21 Author: Michael Buesch Date: Sun Feb 19 13:51:41 2006 +0100 [PATCH] bcm43xx-d80211: completely disable wireless_handlers, as we must not override d80211 handlers. Signed-off-by: Michael Buesch commit 2f4a9c4106af70f99f4a270f0dacff8eb80a5590 Author: Michael Buesch Date: Sat Feb 18 21:01:56 2006 +0100 [PATCH] bcm43xx-d80211: Documentation fix by "Bin Zhang" Signed-off-by: Michael Buesch commit 039576eb10cf40e5a3777c935213d6499db6de6f Author: Michael Buesch Date: Sat Feb 18 11:58:25 2006 +0100 [PATCH] bcm43xx-d80211: add note that not all devices support PIO. Signed-off-by: Michael Buesch commit ea1e81ac25ada133332bcbd09abaaa4c819bb601 Author: Michael Buesch Date: Sat Feb 18 11:54:45 2006 +0100 [PATCH] bcm43xx-d80211: fix typo. Missing D80211. Signed-off-by: Michael Buesch commit 5757e201513c88e72a83e03cbf469aa0643698e9 Author: Michael Buesch Date: Sat Feb 18 11:53:39 2006 +0100 [PATCH] bcm43xx-d80211: Partially fix PIO code. Add Kconfig option for PIO or DMA mode (or both). Signed-off-by: Michael Buesch commit 3a286360d1999c4ee4e99bbde393971fda760b02 Author: Michael Buesch Date: Mon Feb 13 00:11:07 2006 +0100 [PATCH] bcm43xx-d80211: Code cleanups. This removes various "inline" statements and reduces codesize. Signed-off-by: Michael Buesch commit bf170026d9e9613ef5b50d5dac767c98a175fc9e Author: Michael Buesch Date: Sun Feb 12 20:25:55 2006 +0100 [PATCH] bcm43xx-d80211: fix LED code. Signed-off-by: Michael Buesch commit e8cd3a7bb0ccef74e8d22eef074d1008482efa7c Author: Michael Buesch Date: Sun Feb 12 14:24:42 2006 +0100 [PATCH] bcm43xx-d80211: Update docs to reflect changes in the driver. Signed-off-by: Michael Buesch commit d6f95f34708b46818e962732abacf9896504c555 Author: Michael Buesch Date: Wed Feb 8 18:06:22 2006 +0100 [PATCH] bcm43xx-d80211: remove WX debugging. Signed-off-by: Michael Buesch commit dfdb908455c92159bac8cf52e32e2bbadec704dd Author: Michael Buesch Date: Wed Feb 8 17:52:49 2006 +0100 [PATCH] bcm43xx-d80211: heavily increase mac_suspend timeout. Signed-off-by: Michael Buesch commit 8817b927683b54389be2066828f9dfa4d0bf8439 Author: Michael Buesch Date: Sun Feb 5 15:38:48 2006 +0100 [PATCH] bcm43xx-d80211: fix NULL pointer deref in bcm43xx_remove_one. Signed-off-by: Michael Buesch commit e2b1a1d2189f6344352c3fdbfac15d46d314d3af Author: Michael Buesch Date: Sun Feb 5 15:31:32 2006 +0100 [PATCH] bcm43xx-d80211: build magic add_sta binary in /tmp instead of $PWD Signed-off-by: Michael Buesch commit 6c54a1c429842dc3529d145b8f53bb401b1be959 Author: Danny van Dyk Date: Wed Feb 1 19:16:35 2006 +0100 [PATCH] Sync bcm43xx_phy_initb6() with specs Signed-off-by: John W. Linville commit 2a4caead012140cec619cbff5a42e74386835f32 Author: Jiri Benc Date: Mon Feb 6 15:59:37 2006 +0100 [PATCH] d80211: rename ieee80211_rx to __ieee80211_rx This patch fix the problem reported by Michael Buesch : net/ieee80211/built-in.o: In function `ieee80211_rx': : multiple definition of `ieee80211_rx' net/d80211/built-in.o:: first defined here The ieee80211_rx function is renamed to __ieee80211_rx. That function must not be called from hard irq (drivers use ieee80211_rx_irqsafe instead), so the new name seems to be appropriate. Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit 7b3b970e16b12aee9fb71d5bab7a1eeaddadc72d Author: Michael Buesch Date: Fri Feb 3 17:57:51 2006 +0100 [PATCH] bcm43xx-d80211: Add beacon template uploading code Signed-off-by: Michael Buesch commit 304cfe9ea32e0191c995719a48d4cadc91551b4e Author: Michael Buesch Date: Thu Feb 2 19:11:08 2006 +0100 [PATCH] bcm43xx-d80211: basic ethtool support (CONTINUED) This patch contains the beginnings of ethtool support for bcm43xx. It only implements get_drvinfo and get_link, but that's enough for ifplugd to use ethtool to know whether we're associated or not and then start or stop dhcp as necessary. Signed-off-by: Jason Lunz Signed-off-by: Michael Buesch commit 6a32835043ef0d6e5c1a1949f31bf9c2b992cb2a Author: Michael Buesch Date: Thu Feb 2 19:11:08 2006 +0100 [PATCH] bcm43xx-d80211: basic ethtool support This patch contains the beginnings of ethtool support for bcm43xx. It only implements get_drvinfo and get_link, but that's enough for ifplugd to use ethtool to know whether we're associated or not and then start or stop dhcp as necessary. Signed-off-by: Jason Lunz Signed-off-by: Michael Buesch commit aba1befe43c897ae96743992a70f9c34538ea087 Author: Michael Buesch Date: Wed Feb 1 21:35:53 2006 +0100 [PATCH] bcm43xx-d80211: Add more initvals sanity checks Add more initvals sanity checks and error out, if one sanity check fails. Signed-off-by: Michael Buesch commit 8115c59d0793f0e5e9b7f5e9b9e767fa0fa980a6 Author: Michael Buesch Date: Tue Jan 31 20:28:38 2006 +0100 [PATCH] bcm43xx: Fix makefile. Remove all the "out-of-tree" stuff. Signed-off-by: Michael Buesch commit 11105274ae9ed9a7dfb2010d902a01cacc1949c4 Author: Ivo van Doorn Date: Mon Jan 30 00:36:29 2006 +0100 [PATCH] RT2x00: rt2500usb Add the rt2500pci driver. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit cfe67aeeb09bc69be8da083c6de5027b76065b6a Author: Ivo van Doorn Date: Mon Jan 30 00:36:25 2006 +0100 [PATCH] RT2x00: rt2500pci Add the rt2500pci driver. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit d2065dad56504703e6781c34633187f5ae2925af Author: Ivo van Doorn Date: Mon Jan 30 00:36:21 2006 +0100 [PATCH] RT2x00: rt2400pci Add the rt2400pci driver. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit d22283cf2925f8d8c225f20114fbd2770f41bef7 Author: Ivo van Doorn Date: Mon Jan 30 00:36:16 2006 +0100 [PATCH] RT2x00: rt2x00.h Add rt2x00.h header which contains all global defines and structures for the Ralink drivers. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 7b3ff982e1517062a0616142794f66a661a0db2a Author: Ivo van Doorn Date: Mon Jan 30 00:36:13 2006 +0100 [PATCH] RT2x00: Makefile + Kconfig Change + add Kconfig & Makefile files to fit the rt2x00 driver into the kernel. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit dca3d6434d0fa2cbf2f6ce82198b86028cc66651 Author: Michael Buesch Date: Tue Jan 31 16:06:54 2006 +0100 [PATCH] bcm43xx: fix Kconfig "depends on" typo. Signed-off-by: Michael Buesch commit 30490d95cee8ff5865d9628e0de9a481e6b506ab Author: Michael Buesch Date: Mon Jan 30 18:20:23 2006 +0100 [PATCH] bcm43xx: add DEBUG Kconfig option. Also fix indention. Signed-off-by: Michael Buesch commit 5385a096b241250f008c5a3a8e49a53604f0074a Author: Jouni Malinen Date: Fri Jan 27 12:56:49 2006 -0800 [PATCH] d80211: Add support for WE-18 This patch addd Linux Wireless Extensions version 18 support into the Devicescape 802.11 stack by converting the WE ioctl registration to use the new dev->wireless_handlers mechanism and by adding support for the ioctls that are needed for WPA/WPA2 in client mode. The private ioctl versions of key configuration and MLME functions were not removed since there are still user space programs using these. The old versions can be removed from here once user space programs get updated to WE-18 (both client and AP functionality). Signed-off-by: Jouni Malinen Signed-off-by: Jiri Benc commit 29b7984702154bf3f7c55e7072eb3d5e59f34d4f Author: Michael Buesch Date: Fri Jan 27 17:45:14 2006 +0100 [PATCH] bcm43xx: Remove redundant COPYING file. Signed-off-by: Michael Buesch commit 5bb9ad3da90d2432a46ae48c7391a7d937baf964 Author: Michael Buesch Date: Fri Jan 27 17:43:32 2006 +0100 [PATCH] bcm43xx: relocate documentation and scripts Move documentation to Documentation subdirectory and move scripts to scripts subdirectory. Signed-off-by: Michael Buesch commit b0138cbdf475d7671fe86e8c5f4d92090c1d242f Author: Michael Buesch Date: Fri Jan 27 17:30:48 2006 +0100 [PATCH] bcm43xx: sync with svn.berlios.de Signed-off-by: Michael Buesch commit d67d9418bbfc5dc550c9bac952c243d7dfab170c Author: John W. Linville Date: Mon Jan 23 19:50:18 2006 -0500 [PATCH] bcm43xx: fixup build for d80211 The dscape stack is now under d80211. This fixes-up the bcm43xx driver to match the new location, and renames the driver to not conflict with the softmac version. Signed-off-by: John W. Linville commit 3005ac5b725030c38baf4292c56f10b152d9cf39 Author: John W. Linville Date: Mon Jan 23 19:48:12 2006 -0500 [PATCH] bcm43xx: patch Kconfig and wireless/Makefile for import Patch Kconfig and wireless/Makefile to merge bcm43xx 'properly' Signed-off-by: John W. Linville commit e567da23cfcb6dd525c6670cccdeb187a772d935 Author: John W. Linville Date: Mon Jan 23 19:45:58 2006 -0500 [PATCH] wireless: import bcm43xx sources Import the bcm43xx driver from the upstream sources here: ftp://ftp.berlios.de/pub/bcm43xx/snapshots/bcm43xx/bcm43xx-dscape-20060123.tar.bz2 Signed-off-by: John W. Linville commit 9ddd7bf43739bb87089e654d0a3239c90aacad76 Author: Jiri Benc Date: Mon Jan 23 18:26:02 2006 +0100 [PATCH] d80211: add missing includes Most headers don't specify all includes they need. This patch fixes that. Signed-off-by: Jiri Benc commit ebcd75c10e0a86f851cb701205352c0b6b9cb9df Author: Jiri Benc Date: Sat Jan 21 17:10:57 2006 +0100 [PATCH] d80211: max_iface_count sysfs attribute This patch allows maximal number of allowed sta/ap interfaces to be get/set by sysfs (/sys/class/ieee80211/phyX/max_iface_count). Signed-off-by: Jiri Benc commit a5ae401e978d289f387f4651aa842fcb5b6fbb99 Author: Jiri Benc Date: Sat Jan 21 16:44:33 2006 +0100 [PATCH] d80211: preliminary sysfs support Support for adding and removing interfaces through sysfs. A new class "ieee80211" is created containing physical devices as subdirectories (named phy%d). Only sta (managed) interfaces can be added for now. To add an interface, invoke: echo -n sta0 > /sys/class/phy0/add_iface To remove interface, invoke: echo -n sta0 > /sys/class/phy0/remove_iface The old ioctl interface is still present. Signed-off-by: Jiri Benc commit c6580f7d453947df0d831e2a8f42e7c4d7a8f689 Author: Jiri Benc Date: Sat Jan 21 16:38:35 2006 +0100 [PATCH] d80211: add module descriptions Adds module description and license to 80211 and rate_control modules. Signed-off-by: Jiri Benc commit 152c1bd18becb40ace2daa57d2edcedd9be1517a Author: Jiri Benc Date: Sat Jan 21 16:37:18 2006 +0100 [PATCH] d80211: fix alignment calculations Better calculation of alignment of structures. In theory, it was possible that some structures might overlap (it didn't really happen as compiler does its own structure alignment). Moreover, pointer arithmetic can be used to get from ieee80211_sub_if_data to net_device now. Signed-off-by: Jiri Benc commit a2abcf73a46702cbd42e6d41b48d9ae4f2c167d3 Author: Jiri Benc Date: Sat Jan 21 16:35:34 2006 +0100 [PATCH] d80211: rename "norm" to "AP" For Devicescape, "norm" means "AP". It is understandable as their devices operate normally in an AP mode. This is not the case of Linux in general, so rename "norm" to "ap". Signed-off-by: Jiri Benc commit 43a67f87a333b94c657057c73444f6d32fe595ef Author: Feyd Date: Sat Jan 21 16:33:50 2006 +0100 [PATCH] d80211: autoload ieee80211_rate_control The attached patch loads rate_control module when initializing the rate control and no algorithm available. Signed-off-by: Jiri Benc commit 7f9244284d231b92f808aa071f1d66d9b8735e66 Author: Robert Jordens Date: Sat Jan 21 16:32:34 2006 +0100 [PATCH] d80211: allow one byte too long mgmt packets Some Apple Airport base stations (notably the "Snow" dual-ethernet ones) send malformed association response packets that are one byte too long. This causes parsing to fail. Allowing the packet to be one byte too long makes the parsing a bit more lax but can hardly go wrong because _one_ spurious byte cannot be a missed information element. Signed-off-by: Robert Jordens Signed-off-by: Jiri Benc commit bd6852d300e480b5f3f1e17b353ac3f49ec6276a Author: Jiri Benc Date: Sat Jan 21 16:31:39 2006 +0100 [PATCH] d80211: export ieee80211_get_hdrlen This patch exports ieee80211_get_hdrlen() for drivers. Signed-off-by: Jiri Benc commit e74c733d844562317012fcac54411111a0874860 Author: Sven Henkel Date: Sat Jan 21 16:30:31 2006 +0100 [PATCH] d80211: do not drop sent eapol PRISM2_PARAM_EAPOL is responsible for eapol packets being dropped or not. So, if I set this parameter to 1, dscape doesn't drop eapol packets anymore on reception, but it still drops to-be-sent eapol packets. I consider this a bug, so I appended a patch that fixes it. Signed-off-by: Jiri Benc commit 14e059576d724ec138af7c9e793368372b6766e1 Author: Jiri Benc Date: Sat Jan 21 16:29:08 2006 +0100 [PATCH] d80211: pass hw channel value to drivers Add hw specific value for the channel to the configuration structure so drivers can switch channels more easily. Signed-off-by: Jiri Benc commit d54d1633995767654feb5f10a29046abd1be3287 Author: Jiri Benc Date: Sat Jan 21 16:24:04 2006 +0100 [PATCH] d80211: add useful macros for driver use It moves IEEE 802.11 constants and ieee80211_hdr structure from ieee80211_i.h to ieee80211.h to allow drivers to use them. Some useful functions were moved as well (and one new was added). Signed-off-by: Jiri Benc commit 1bd9e1ee84ad0c1101514fd80eb94f114eb75924 Author: Michael Buesch Date: Sat Jan 21 16:22:18 2006 +0100 [PATCH] d80211: qdisc lock fix This fixes the qdisc tree locking in wme.c Signed-off-by: Jiri Benc commit 6608f6518ea45b909ff7a4c2d4ad7f1f8926d39d Author: Jiri Benc Date: Sat Jan 21 15:59:43 2006 +0100 [PATCH] d80211: cb cleanup Use sk_buff->cb for storing necessary data only. This allows ieee80211_rx_status and ieee80211_tx_control structures to be larger than cb size. Drivers have to copy the whole ieee80211_tx_control structure passed to their tx method to ieee80211_tx_status. It is somewhat simpler for them, prevents some bugs (it was so easy to forget to copy one of items from ieee80211_tx_control to ieee80211_tx_status), makes future extensions easier and as a bonus driver is no longer required to preserve sk_buff->cb. Signed-off-by: Jiri Benc commit 314930a0d25f8331b98b173b834967aa36dac568 Author: Jiri Benc Date: Sat Jan 21 15:57:17 2006 +0100 [PATCH] d80211: separate alloc and register A driver needs its private structure to do card initialization. The private structure is allocated together with net_device; so we need to allocate net_device first, then allow driver to do its setup and then register net_device to the kernel. Current function ieee80211_register_hw() does not allow this. This patch moves allocation from ieee80211_register_hw() to ieee80211_alloc_hw() and deallocation from ieee80211_unregister_hw() to ieee8021_free_hw(). These changes shifted the meaning of ieee80211_hw_initialized(), so it is renamed to ieee80211_update_hw(). A small memory leak is fixed too. Signed-off-by: Jiri Benc commit 1928c9fed151f1b8ccab727924a314a897172460 Author: Michael Buesch Date: Sat Jan 21 15:55:10 2006 +0100 [PATCH] d80211: atomic kmalloc fix There are several calls of kmalloc with GFP_KERNEL in atomic context in the code. I think the attached patch fixes all calls with GFP_KERNEL in atomic code, by using GFP_ATOMIC. I grepped the source for GFP_KERNEL and I hope I found all errors. Signed-off-by: Jiri Benc commit 9326dc89926241211183484faefcbeaafe9c11d9 Author: Jiri Benc Date: Sat Jan 21 15:52:31 2006 +0100 [PATCH] d80211: remove old kernels support Remove support for old kernels. Signed-off-by: Jiri Benc commit 02015696c733e15682cd0206ba712c6d24747b31 Author: Jiri Benc Date: Sat Jan 21 15:46:39 2006 +0100 [PATCH] d80211: integrate Devicescape ieee80211 Correct Devicescape ieee80211 layer so it can be compiled with current kernel. Add appropriate Makefile and Kconfig. Signed-off-by: Jiri Benc commit 2ac16d15eb33bcb086644c5e696ed43c7f93d8a6 Author: Jiri Benc Date: Sat Jan 21 14:42:17 2006 +0100 [PATCH] d80211: add Devicescape ieee80211 Add ieee80211 layer released by Devicescape (http://www.devicescape.com/opensource/devicescape-ieee80211-20051007.tar.gz) Signed-off-by: Jiri Benc .mailmap | 1 CREDITS | 31 MAINTAINERS | 49 drivers/Kconfig | 2 drivers/Makefile | 1 drivers/misc/Kconfig | 6 drivers/misc/Makefile | 1 drivers/misc/eeprom_93cx6.c | 347 + drivers/net/Kconfig | 11 drivers/net/b44.c | 728 +-- drivers/net/b44.h | 81 drivers/net/wireless/Kconfig | 2 drivers/net/wireless/Makefile | 3 drivers/net/wireless/bcm43xx/Kconfig | 1 drivers/net/wireless/mac80211/Kconfig | 6 drivers/net/wireless/mac80211/Makefile | 6 drivers/net/wireless/mac80211/README | 2 drivers/net/wireless/mac80211/adm8211/Kconfig | 25 drivers/net/wireless/mac80211/adm8211/Makefile | 1 drivers/net/wireless/mac80211/adm8211/adm8211.c | 2172 +++++++++ drivers/net/wireless/mac80211/adm8211/adm8211.h | 622 ++ drivers/net/wireless/mac80211/bcm43xx/Kconfig | 101 drivers/net/wireless/mac80211/bcm43xx/Makefile | 18 drivers/net/wireless/mac80211/bcm43xx/bcm43xx.h | 885 +++ .../wireless/mac80211/bcm43xx/bcm43xx_debugfs.c | 433 ++ .../wireless/mac80211/bcm43xx/bcm43xx_debugfs.h | 110 .../net/wireless/mac80211/bcm43xx/bcm43xx_dma.c | 1383 ++++++ .../net/wireless/mac80211/bcm43xx/bcm43xx_dma.h | 361 ++ .../net/wireless/mac80211/bcm43xx/bcm43xx_leds.c | 300 + .../net/wireless/mac80211/bcm43xx/bcm43xx_leds.h | 56 drivers/net/wireless/mac80211/bcm43xx/bcm43xx_lo.c | 1110 ++++ drivers/net/wireless/mac80211/bcm43xx/bcm43xx_lo.h | 92 .../net/wireless/mac80211/bcm43xx/bcm43xx_main.c | 4029 ++++++++++++++++++ .../net/wireless/mac80211/bcm43xx/bcm43xx_main.h | 156 + .../net/wireless/mac80211/bcm43xx/bcm43xx_pcmcia.c | 163 + .../net/wireless/mac80211/bcm43xx/bcm43xx_pcmcia.h | 22 .../net/wireless/mac80211/bcm43xx/bcm43xx_phy.c | 4286 +++++++++++++++++++ .../net/wireless/mac80211/bcm43xx/bcm43xx_phy.h | 309 + .../net/wireless/mac80211/bcm43xx/bcm43xx_pio.c | 671 +++ .../net/wireless/mac80211/bcm43xx/bcm43xx_pio.h | 170 + .../net/wireless/mac80211/bcm43xx/bcm43xx_power.c | 82 .../net/wireless/mac80211/bcm43xx/bcm43xx_power.h | 41 .../net/wireless/mac80211/bcm43xx/bcm43xx_sysfs.c | 232 + .../net/wireless/mac80211/bcm43xx/bcm43xx_sysfs.h | 9 .../net/wireless/mac80211/bcm43xx/bcm43xx_tables.c | 376 ++ .../net/wireless/mac80211/bcm43xx/bcm43xx_tables.h | 28 .../net/wireless/mac80211/bcm43xx/bcm43xx_xmit.c | 603 +++ .../net/wireless/mac80211/bcm43xx/bcm43xx_xmit.h | 250 + drivers/net/wireless/mac80211/p54/Kconfig | 10 drivers/net/wireless/mac80211/p54/Makefile | 4 drivers/net/wireless/mac80211/p54/net2280.h | 452 ++ drivers/net/wireless/mac80211/p54/prism54.h | 77 drivers/net/wireless/mac80211/p54/prism54common.c | 821 +++ drivers/net/wireless/mac80211/p54/prism54common.h | 328 + drivers/net/wireless/mac80211/p54/prism54magic.h | 77 drivers/net/wireless/mac80211/p54/prism54pci.c | 700 +++ drivers/net/wireless/mac80211/p54/prism54pci.h | 106 drivers/net/wireless/mac80211/p54/prism54usb.c | 946 ++++ drivers/net/wireless/mac80211/p54/prism54usb.h | 133 + drivers/net/wireless/mac80211/rt2x00/Kconfig | 99 drivers/net/wireless/mac80211/rt2x00/Makefile | 14 drivers/net/wireless/mac80211/rt2x00/rt2400pci.c | 1769 +++++++ drivers/net/wireless/mac80211/rt2x00/rt2400pci.h | 918 ++++ drivers/net/wireless/mac80211/rt2x00/rt2500pci.c | 1951 ++++++++ drivers/net/wireless/mac80211/rt2x00/rt2500pci.h | 1185 +++++ drivers/net/wireless/mac80211/rt2x00/rt2500usb.c | 1729 +++++++ drivers/net/wireless/mac80211/rt2x00/rt2500usb.h | 738 +++ drivers/net/wireless/mac80211/rt2x00/rt2x00.h | 1057 ++++ drivers/net/wireless/mac80211/rt2x00/rt2x00debug.c | 353 + drivers/net/wireless/mac80211/rt2x00/rt2x00debug.h | 72 drivers/net/wireless/mac80211/rt2x00/rt2x00dev.c | 831 +++ drivers/net/wireless/mac80211/rt2x00/rt2x00lib.h | 128 + drivers/net/wireless/mac80211/rt2x00/rt2x00mac.c | 426 ++ drivers/net/wireless/mac80211/rt2x00/rt2x00pci.c | 587 ++ drivers/net/wireless/mac80211/rt2x00/rt2x00pci.h | 83 drivers/net/wireless/mac80211/rt2x00/rt2x00usb.c | 637 ++ drivers/net/wireless/mac80211/rt2x00/rt2x00usb.h | 120 drivers/net/wireless/mac80211/rt2x00/rt61pci.c | 2332 +++++++++ drivers/net/wireless/mac80211/rt2x00/rt61pci.h | 1348 +++++ drivers/net/wireless/mac80211/rt2x00/rt73usb.c | 1966 ++++++++ drivers/net/wireless/mac80211/rt2x00/rt73usb.h | 937 ++++ drivers/net/wireless/mac80211/rtl818x/Kconfig | 9 drivers/net/wireless/mac80211/rtl818x/Makefile | 2 drivers/net/wireless/mac80211/rtl818x/rtl8187.h | 126 .../net/wireless/mac80211/rtl818x/rtl8187_dev.c | 726 +++ .../wireless/mac80211/rtl818x/rtl8187_rtl8225.c | 747 +++ .../wireless/mac80211/rtl818x/rtl8187_rtl8225.h | 30 drivers/net/wireless/mac80211/rtl818x/rtl818x.h | 180 + drivers/net/wireless/mac80211/zd1211rw/Kconfig | 19 drivers/net/wireless/mac80211/zd1211rw/Makefile | 11 drivers/net/wireless/mac80211/zd1211rw/zd_chip.c | 1681 +++++++ drivers/net/wireless/mac80211/zd1211rw/zd_chip.h | 910 ++++ drivers/net/wireless/mac80211/zd1211rw/zd_def.h | 57 .../net/wireless/mac80211/zd1211rw/zd_ieee80211.h | 67 drivers/net/wireless/mac80211/zd1211rw/zd_mac.c | 942 ++++ drivers/net/wireless/mac80211/zd1211rw/zd_mac.h | 199 + drivers/net/wireless/mac80211/zd1211rw/zd_rf.c | 170 + drivers/net/wireless/mac80211/zd1211rw/zd_rf.h | 80 .../net/wireless/mac80211/zd1211rw/zd_rf_al2230.c | 436 ++ .../net/wireless/mac80211/zd1211rw/zd_rf_al7230b.c | 491 ++ .../net/wireless/mac80211/zd1211rw/zd_rf_rf2959.c | 279 + drivers/net/wireless/mac80211/zd1211rw/zd_usb.c | 1444 ++++++ drivers/net/wireless/mac80211/zd1211rw/zd_usb.h | 267 + drivers/net/wireless/mac80211/zd1211rw/zd_util.c | 82 drivers/net/wireless/mac80211/zd1211rw/zd_util.h | 29 drivers/net/wireless/zd1211rw/Kconfig | 1 drivers/ssb/Kconfig | 93 drivers/ssb/Makefile | 11 drivers/ssb/driver_chipcommon.c | 402 ++ drivers/ssb/driver_mipscore.c | 258 + drivers/ssb/driver_pcicore.c | 556 ++ drivers/ssb/main.c | 1047 ++++ drivers/ssb/pci.c | 672 +++ drivers/ssb/pcihost_wrapper.c | 104 drivers/ssb/pcmcia.c | 256 + drivers/ssb/scan.c | 407 ++ drivers/ssb/ssb_private.h | 151 + drivers/usb/host/Kconfig | 13 drivers/usb/host/ohci-hcd.c | 21 drivers/usb/host/ohci-ssb.c | 254 + include/linux/crc-itu-t.h | 27 include/linux/eeprom_93cx6.h | 77 include/linux/ieee80211.h | 403 ++ include/linux/nl80211.h | 263 + include/linux/ssb/ssb.h | 403 ++ include/linux/ssb/ssb_driver_chipcommon.h | 387 ++ include/linux/ssb/ssb_driver_extif.h | 163 + include/linux/ssb/ssb_driver_mips.h | 47 include/linux/ssb/ssb_driver_pci.h | 108 include/linux/ssb/ssb_regs.h | 294 + include/net/cfg80211.h | 114 include/net/iw_handler.h | 8 include/net/mac80211.h | 1060 ++++ lib/Kconfig | 8 lib/Makefile | 1 lib/crc-itu-t.c | 64 net/Kconfig | 1 net/Makefile | 4 net/mac80211/Kconfig | 82 net/mac80211/Makefile | 20 net/mac80211/aes_ccm.c | 155 + net/mac80211/aes_ccm.h | 26 net/mac80211/debugfs.c | 433 ++ net/mac80211/debugfs.h | 16 net/mac80211/debugfs_key.c | 252 + net/mac80211/debugfs_key.h | 34 net/mac80211/debugfs_netdev.c | 440 ++ net/mac80211/debugfs_netdev.h | 30 net/mac80211/debugfs_sta.c | 247 + net/mac80211/debugfs_sta.h | 12 net/mac80211/hostapd_ioctl.h | 344 + net/mac80211/ieee80211.c | 5108 ++++++++++++++++++++ net/mac80211/ieee80211_cfg.c | 72 net/mac80211/ieee80211_cfg.h | 9 net/mac80211/ieee80211_common.h | 98 net/mac80211/ieee80211_i.h | 814 +++ net/mac80211/ieee80211_iface.c | 353 + net/mac80211/ieee80211_ioctl.c | 3180 ++++++++++++ net/mac80211/ieee80211_key.h | 106 net/mac80211/ieee80211_led.c | 91 net/mac80211/ieee80211_led.h | 32 net/mac80211/ieee80211_rate.c | 140 + net/mac80211/ieee80211_rate.h | 144 + net/mac80211/ieee80211_sta.c | 3219 +++++++++++++ net/mac80211/michael.c | 104 net/mac80211/michael.h | 20 net/mac80211/rc80211_simple.c | 432 ++ net/mac80211/sta_info.c | 470 ++ net/mac80211/sta_info.h | 169 + net/mac80211/tkip.c | 341 + net/mac80211/tkip.h | 36 net/mac80211/wep.c | 328 + net/mac80211/wep.h | 40 net/mac80211/wme.c | 678 +++ net/mac80211/wme.h | 57 net/mac80211/wpa.c | 846 +++ net/mac80211/wpa.h | 31 net/wireless/Kconfig | 17 net/wireless/Makefile | 1 net/wireless/core.c | 143 + net/wireless/core.h | 32 net/wireless/nl80211.c | 994 ++++ net/wireless/nl80211.h | 24 net/wireless/sysfs.c | 50 184 files changed, 79771 insertions(+), 469 deletions(-) diff --git a/.mailmap b/.mailmap index ebf9bf8..83dcb40 100644 --- a/.mailmap +++ b/.mailmap @@ -57,6 +57,7 @@ Jean Tourrilhes Jeff Garzik Jens Axboe Jens Osterkamp +Johannes Berg John Stultz Juha Yrjola Juha Yrjola diff --git a/CREDITS b/CREDITS index d714030..7a033a4 100644 --- a/CREDITS +++ b/CREDITS @@ -665,6 +665,11 @@ D: Minor updates to SCSI code for the Co S: (ask for current address) S: USA +N: Robin Cornelius +E: robincornelius@users.sourceforge.net +D: Ralink rt2x00 WLAN driver +S: Cornwall, U.K. + N: Mark Corner E: mcorner@umich.edu W: http://www.eecs.umich.edu/~mcorner/ @@ -679,6 +684,11 @@ D: Kernel module SMART utilities S: Santa Cruz, California S: USA +N: Luis Correia +E: lfcorreia@users.sf.net +D: Ralink rt2x00 WLAN driver +S: Belas, Portugal + N: Alan Cox W: http://www.linux.org.uk/diary/ D: Linux Networking (0.99.10->2.0.29) @@ -833,6 +843,12 @@ S: Lancs S: PR4 6AX S: United Kingdom +N: Ivo van Doorn +E: IvDoorn@gmail.com +W: http://www.mendiosus.nl +D: Ralink rt2x00 WLAN driver +S: Haarlem, The Netherlands + N: John G Dorsey E: john+@cs.cmu.edu D: ARM Linux ports to Assabet/Neponset, Spot @@ -3524,6 +3540,12 @@ S: Maastrichterweg 63 S: 5554 GG Valkenswaard S: The Netherlands +N: Mark Wallis +E: mwallis@serialmonkey.com +W: http://mark.serialmonkey.com +D: Ralink rt2x00 WLAN driver +S: Newcastle, Australia + N: Peter Shaobo Wang E: pwang@mmdcorp.com W: http://www.mmdcorp.com/pw/linux @@ -3658,6 +3680,15 @@ S: Alte Regensburger Str. 11a S: 93149 Nittenau S: Germany +N: Gertjan van Wingerde +E: gwingerde@home.nl +D: Ralink rt2x00 WLAN driver +D: Minix V2 file-system +D: Misc fixes +S: Geessinkweg 177 +S: 7544 TX Enschede +S: The Netherlands + N: Lars Wirzenius E: liw@iki.fi D: Linux System Administrator's Guide, author, former maintainer diff --git a/MAINTAINERS b/MAINTAINERS index af1c792..c779b0d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -296,6 +296,14 @@ M: corentin.labbe@geomatys.fr L: lm-sensors@lm-sensors.org S: Maintained +ADM8211 WIRELESS DRIVER +P: Michael Wu +M: flamingice@sourmilk.net +L: linux-wireless@vger.kernel.org +W: http://aluminum.sourmilk.net/adm8211/ +T: git kernel.org:/pub/scm/linux/kernel/git/mwu/mac80211-drivers.git +S: Maintained + ADT746X FAN DRIVER P: Colin Leroy M: colin@colino.net @@ -707,6 +715,15 @@ L: linux-hams@vger.kernel.org W: http://www.baycom.org/~tom/ham/ham.html S: Maintained +BCM43XX WIRELESS DRIVER (DEVICESCAPE BASED VERSION) +P: Michael Buesch +M: mb@bu3sch.de +P: Stefano Brivio +M: st3@riseup.net +L: linux-wireless@vger.kernel.org +W: http://bcm43xx.berlios.de/ +S: Maintained + BCM43XX WIRELESS DRIVER (SOFTMAC BASED VERSION) P: Larry Finger M: Larry.Finger@lwfinger.net @@ -2209,6 +2226,16 @@ M: philb@gnu.org W: http://www.tazenda.demon.co.uk/phil/linux-hp S: Maintained +MAC80211 +P: Jiri Benc +M: jbenc@suse.cz +P: Michael Wu +M: flamingice@sourmilk.net +L: linux-wireless@vger.kernel.org +W: http://linuxwireless.org/ +T: git kernel.org:/pub/scm/linux/kernel/git/jbenc/mac80211.git +S: Maintained + MARVELL YUKON / SYSKONNECT DRIVER P: Mirko Lindner M: mlindner@syskonnect.de @@ -2758,6 +2785,15 @@ L: kpreempt-tech@lists.sourceforge.net W: ftp://ftp.kernel.org/pub/linux/kernel/people/rml/preempt-kernel S: Supported +P54 WIRELESS DRIVER +P: Michael Wu +M: flamingice@sourmilk.net +L: linux-wireless@vger.kernel.org +L: developers@islsm.org +W: http://prism54.org +T: git kernel.org:/pub/scm/linux/kernel/git/mwu/mac80211-drivers.git +S: Maintained + PRISM54 WIRELESS DRIVER P: Prism54 Development Team M: developers@islsm.org @@ -2835,6 +2871,13 @@ M: corey@world.std.com L: linux-wireless@vger.kernel.org S: Maintained +Ralink rt2x00 WLAN driver +P: rt2x00 project +L: linux-wireless@vger.kernel.org +L: rt2400-devel@lists.sourceforge.net +W: http://rt2x00.serialmonkey.com/ +S: Maintained + RANDOM NUMBER DRIVER P: Matt Mackall M: mpm@selenic.com @@ -3120,6 +3163,12 @@ M: tsbogend@alpha.franken.de L: netdev@vger.kernel.org S: Maintained +SONICS SILICON BACKPLANE DRIVER (SSB) +P: Michael Buesch +M: mb@bu3sch.de +L: netdev@vger.kernel.org +S: Maintained + SONY VAIO CONTROL DEVICE DRIVER P: Mattia Dongili M: malattia@linux.it diff --git a/drivers/Kconfig b/drivers/Kconfig index 050323f..b15a5c6 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -56,6 +56,8 @@ source "drivers/w1/Kconfig" source "drivers/hwmon/Kconfig" +source "drivers/ssb/Kconfig" + source "drivers/mfd/Kconfig" source "drivers/media/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 920c975..c68a44e 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -80,3 +80,4 @@ obj-$(CONFIG_GENERIC_TIME) += clocksourc obj-$(CONFIG_DMA_ENGINE) += dma/ obj-$(CONFIG_HID) += hid/ obj-$(CONFIG_PPC_PS3) += ps3/ +obj-$(CONFIG_SSB) += ssb/ diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index a3c525b..607a180 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -178,4 +178,10 @@ config THINKPAD_ACPI_BAY If you are not sure, say Y here. +config EEPROM_93CX6 + tristate "EEPROM 93CX6 support" + ---help--- + This is a driver for the EEPROM chipsets 93c46 and 93c66. + The driver supports both read as well as write commands. + endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index e325164..42b34a9 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_TIFM_7XX1) += tifm_7 obj-$(CONFIG_SGI_IOC4) += ioc4.o obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o +obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o diff --git a/drivers/misc/eeprom_93cx6.c b/drivers/misc/eeprom_93cx6.c new file mode 100644 index 0000000..a948ddc --- /dev/null +++ b/drivers/misc/eeprom_93cx6.c @@ -0,0 +1,347 @@ +/* + Copyright (C) 2004 - 2006 rt2x00 SourceForge Project + + + 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. + */ + +/* + Module: eeprom_93cx6 + Abstract: EEPROM reader routines for 93cx6 chipsets. + Supported chipsets: 93c46 & 93c66. + */ + +#include +#include +#include +#include +#include + +MODULE_AUTHOR("http://rt2x00.serialmonkey.com"); +MODULE_VERSION("1.0"); +MODULE_DESCRIPTION("EEPROM 93cx6 chip driver"); +MODULE_LICENSE("GPL"); + +static inline void eeprom_93cx6_pulse_high(struct eeprom_93cx6 *eeprom) +{ + eeprom->reg_data_clock = 1; + eeprom->register_write(eeprom); + udelay(1); +} + +static inline void eeprom_93cx6_pulse_low(struct eeprom_93cx6 *eeprom) +{ + eeprom->reg_data_clock = 0; + eeprom->register_write(eeprom); + udelay(1); +} + +static void eeprom_93cx6_startup(struct eeprom_93cx6 *eeprom) +{ + /* + * Clear all flags, and enable chip select. + */ + eeprom->register_read(eeprom); + eeprom->reg_data_in = 0; + eeprom->reg_data_out = 0; + eeprom->reg_data_clock = 0; + eeprom->reg_chip_select = 1; + eeprom->register_write(eeprom); + + /* + * kick a pulse. + */ + eeprom_93cx6_pulse_high(eeprom); + eeprom_93cx6_pulse_low(eeprom); +} + +static void eeprom_93cx6_cleanup(struct eeprom_93cx6 *eeprom) +{ + /* + * Clear chip_select and data_in flags. + */ + eeprom->register_read(eeprom); + eeprom->reg_data_in = 0; + eeprom->reg_chip_select = 0; + eeprom->register_write(eeprom); + + /* + * kick a pulse. + */ + eeprom_93cx6_pulse_high(eeprom); + eeprom_93cx6_pulse_low(eeprom); +} + +static void eeprom_93cx6_write_bits(struct eeprom_93cx6 *eeprom, + const u16 data, const u16 count) +{ + unsigned int i; + + eeprom->register_read(eeprom); + + /* + * Clear data flags. + */ + eeprom->reg_data_in = 0; + eeprom->reg_data_out = 0; + + /* + * Start writing all bits. + */ + for (i = count; i > 0; i--) { + /* + * Check if this bit needs to be set. + */ + eeprom->reg_data_in = !!(data & (1 << (i - 1))); + + /* + * Write the bit to the eeprom register. + */ + eeprom->register_write(eeprom); + + /* + * Kick a pulse. + */ + eeprom_93cx6_pulse_high(eeprom); + eeprom_93cx6_pulse_low(eeprom); + } + + eeprom->reg_data_in = 0; + eeprom->register_write(eeprom); +} + +static void eeprom_93cx6_read_bits(struct eeprom_93cx6 *eeprom, + u16 *data, const u16 count) +{ + unsigned int i; + u16 buf = 0; + + eeprom->register_read(eeprom); + + /* + * Clear data flags. + */ + eeprom->reg_data_in = 0; + eeprom->reg_data_out = 0; + + /* + * Start reading all bits. + */ + for (i = count; i > 0; i--) { + eeprom_93cx6_pulse_high(eeprom); + + eeprom->register_read(eeprom); + + /* + * Clear data_in flag. + */ + eeprom->reg_data_in = 0; + + /* + * Read if the bit has been set. + */ + if (eeprom->reg_data_out) + buf |= (1 << (i - 1)); + + eeprom_93cx6_pulse_low(eeprom); + } + + *data = buf; +} + +static void eeprom_93cx6_ewen(struct eeprom_93cx6 *eeprom) +{ + /* + * Initialize the eeprom register + */ + eeprom_93cx6_startup(eeprom); + + /* + * Select the read opcode and the word to be read. + */ + eeprom_93cx6_write_bits(eeprom, PCI_EEPROM_EWEN_OPCODE, 5); + eeprom_93cx6_write_bits(eeprom, 0, 6); + + /* + * Cleanup eeprom register. + */ + eeprom_93cx6_cleanup(eeprom); +} + +static void eeprom_93cx6_ewds(struct eeprom_93cx6 *eeprom) +{ + /* + * Initialize the eeprom register + */ + eeprom_93cx6_startup(eeprom); + + /* + * Select the read opcode and the word to be read. + */ + eeprom_93cx6_write_bits(eeprom, PCI_EEPROM_EWDS_OPCODE, 5); + eeprom_93cx6_write_bits(eeprom, 0, 6); + + /* + * Cleanup eeprom register. + */ + eeprom_93cx6_cleanup(eeprom); +} + +/** + * eeprom_93cx6_read - Read multiple words from eeprom + * @eeprom: Pointer to eeprom structure + * @word: Word index from where we should start reading + * @data: target pointer where the information will have to be stored + * + * This function will read the eeprom data as host-endian word + * into the given data pointer. + */ +void eeprom_93cx6_read(struct eeprom_93cx6 *eeprom, const u8 word, + u16 *data) +{ + u16 command; + + /* + * Initialize the eeprom register + */ + eeprom_93cx6_startup(eeprom); + + /* + * Select the read opcode and the word to be read. + */ + command = (PCI_EEPROM_READ_OPCODE << eeprom->width) | word; + eeprom_93cx6_write_bits(eeprom, command, + PCI_EEPROM_WIDTH_OPCODE + eeprom->width); + + /* + * Read the requested 16 bits. + */ + eeprom_93cx6_read_bits(eeprom, data, 16); + + /* + * Cleanup eeprom register. + */ + eeprom_93cx6_cleanup(eeprom); +} +EXPORT_SYMBOL_GPL(eeprom_93cx6_read); + +/** + * eeprom_93cx6_multiread - Read multiple words from eeprom + * @eeprom: Pointer to eeprom structure + * @word: Word index from where we should start reading + * @data: target pointer where the information will have to be stored + * @words: Number of words that should be read. + * + * This function will read all requested words from the eeprom, + * this is done by calling eeprom_93cx6_read() multiple times. + * But with the additional change that while the eeprom_93cx6_read + * will return host ordered bytes, this method will return little + * endian words. + */ +void eeprom_93cx6_multiread(struct eeprom_93cx6 *eeprom, const u8 word, + __le16 *data, const u16 words) +{ + unsigned int i; + u16 tmp; + + for (i = 0; i < words; i++) { + tmp = 0; + eeprom_93cx6_read(eeprom, word + i, &tmp); + data[i] = cpu_to_le16(tmp); + } +} +EXPORT_SYMBOL_GPL(eeprom_93cx6_multiread); + +/** + * eeprom_93cx6_write - Write multiple words to the eeprom + * @eeprom: Pointer to eeprom structure + * @word: Word index from where we should start writing + * @data: Data that will be written + * + * This function will write the eeprom data as host-endian word + * from the given data pointer. + */ +void eeprom_93cx6_write(struct eeprom_93cx6 *eeprom, const u8 word, + u16 data) +{ + u16 command; + + /* + * select the ewen opcode. + */ + eeprom_93cx6_ewen(eeprom); + + /* + * Initialize the eeprom register + */ + eeprom_93cx6_startup(eeprom); + + /* + * Select the write opcode and the word to be read. + */ + command = (PCI_EEPROM_WRITE_OPCODE << eeprom->width) | word; + eeprom_93cx6_write_bits(eeprom, command, + PCI_EEPROM_WIDTH_OPCODE + eeprom->width); + + /* + * Write the requested 16 bits. + */ + eeprom_93cx6_write_bits(eeprom, data, 16); + + /* + * Cleanup eeprom register. + */ + eeprom_93cx6_cleanup(eeprom); + + /* + * Take a short break. + */ + msleep(10000); + + /* + * select the ewen opcode. + */ + eeprom_93cx6_ewds(eeprom); + + /* + * Cleanup eeprom register. + */ + eeprom_93cx6_cleanup(eeprom); +} +EXPORT_SYMBOL_GPL(eeprom_93cx6_write); + + +/** + * eeprom_93cx6_multiwrite - Write multiple words to the eeprom + * @eeprom: Pointer to eeprom structure + * @word: Word index from where we should start writing + * @data: Pointer where the information will be read from + * @words: Number of words that should be written. + * + * This function will write all requested words to the eeprom, + * this is done by calling eeprom_93cx6_write() multiple times. + * This method accepts little endian data, so it will first be + * converted into host endian. + */ +void eeprom_93cx6_multiwrite(struct eeprom_93cx6 *eeprom, const u8 word, + __le16 *data, const u16 words) +{ + unsigned int i; + + for (i = 0; i < words; i++) + eeprom_93cx6_write(eeprom, word + i, le16_to_cpu(data[i])); +} +EXPORT_SYMBOL_GPL(eeprom_93cx6_multiwrite); diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index dcdad21..bcc542e 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -1388,7 +1388,7 @@ config APRICOT config B44 tristate "Broadcom 4400 ethernet support" - depends on NET_PCI && PCI + select SSB select MII help If you have a network (Ethernet) controller of this type, say Y and @@ -1399,6 +1399,15 @@ config B44 . The module will be called b44. +config B44_PCI + bool "Broadcom 4400 PCI device support" + depends on B44 && NET_PCI + default y + help + Support for b44 PCI devices. + + Say Y + config FORCEDETH tristate "nForce Ethernet support" depends on NET_PCI && PCI diff --git a/drivers/net/b44.c b/drivers/net/b44.c index 879a2ff..1ba92c7 100644 --- a/drivers/net/b44.c +++ b/drivers/net/b44.c @@ -1,8 +1,11 @@ -/* b44.c: Broadcom 4400 device driver. +/* b44.c: Broadcom 44xx/47xx Fast Ethernet device driver. * * Copyright (C) 2002 David S. Miller (davem@redhat.com) - * Fixed by Pekka Pietikainen (pp@ee.oulu.fi) + * Copyright (C) 2004 Pekka Pietikainen (pp@ee.oulu.fi) + * Copyright (C) 2004 Florian Schirmer (jolt@tuxbox.org) + * Copyright (C) 2006 Felix Fietkau (nbd@openwrt.org) * Copyright (C) 2006 Broadcom Corporation. + * Copyright (C) 2007 Michael Buesch * * Distribute under GPL. */ @@ -20,17 +23,18 @@ #include #include #include #include +#include #include #include #include + #include "b44.h" #define DRV_MODULE_NAME "b44" #define PFX DRV_MODULE_NAME ": " -#define DRV_MODULE_VERSION "1.01" -#define DRV_MODULE_RELDATE "Jun 16, 2006" +#define DRV_MODULE_VERSION "2.0" #define B44_DEF_MSG_ENABLE \ (NETIF_MSG_DRV | \ @@ -84,10 +88,10 @@ #define B44_ETHIPV6UDP_HLEN 62 #define B44_ETHIPV4UDP_HLEN 42 static char version[] __devinitdata = - DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; + DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION "\n"; -MODULE_AUTHOR("Florian Schirmer, Pekka Pietikainen, David S. Miller"); -MODULE_DESCRIPTION("Broadcom 4400 10/100 PCI ethernet driver"); +MODULE_AUTHOR("Felix Fietkau, Florian Schirmer, Pekka Pietikainen, David S. Miller"); +MODULE_DESCRIPTION("Broadcom 44xx/47xx 10/100 PCI ethernet driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_MODULE_VERSION); @@ -95,18 +99,28 @@ static int b44_debug = -1; /* -1 == use module_param(b44_debug, int, 0); MODULE_PARM_DESC(b44_debug, "B44 bitmapped debugging message enable value"); -static struct pci_device_id b44_pci_tbl[] = { - { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_BCM4401, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, - { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_BCM4401B0, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, - { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_BCM4401B1, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, - { } /* terminate list with empty entry */ -}; +#ifdef CONFIG_B44_PCI +static const struct pci_device_id b44_pci_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_BCM4401) }, + { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_BCM4401B0) }, + { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_BCM4401B1) }, + { 0 } /* terminate list with empty entry */ +}; MODULE_DEVICE_TABLE(pci, b44_pci_tbl); +static struct pci_driver b44_pci_driver = { + .name = DRV_MODULE_NAME, + .id_table = b44_pci_tbl, +}; +#endif /* CONFIG_B44_PCI */ + +static const struct ssb_device_id b44_ssb_tbl[] = { + SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_ETHERNET, SSB_ANY_REV), + SSB_DEVTABLE_END +}; +MODULE_DEVICE_TABLE(ssb, b44_ssb_tbl); + static void b44_halt(struct b44 *); static void b44_init_rings(struct b44 *); @@ -118,6 +132,7 @@ static void b44_init_hw(struct b44 *, in static int dma_desc_align_mask; static int dma_desc_sync_size; +static int instance; static const char b44_gstrings[][ETH_GSTRING_LEN] = { #define _B44(x...) # x, @@ -125,35 +140,35 @@ B44_STAT_REG_DECLARE #undef _B44 }; -static inline void b44_sync_dma_desc_for_device(struct pci_dev *pdev, - dma_addr_t dma_base, - unsigned long offset, - enum dma_data_direction dir) +static inline void b44_sync_dma_desc_for_device(struct ssb_device *sdev, + dma_addr_t dma_base, + unsigned long offset, + enum dma_data_direction dir) { - dma_sync_single_range_for_device(&pdev->dev, dma_base, - offset & dma_desc_align_mask, - dma_desc_sync_size, dir); + dma_sync_single_range_for_device(sdev->dev, dma_base, + offset & dma_desc_align_mask, + dma_desc_sync_size, dir); } -static inline void b44_sync_dma_desc_for_cpu(struct pci_dev *pdev, - dma_addr_t dma_base, - unsigned long offset, - enum dma_data_direction dir) +static inline void b44_sync_dma_desc_for_cpu(struct ssb_device *sdev, + dma_addr_t dma_base, + unsigned long offset, + enum dma_data_direction dir) { - dma_sync_single_range_for_cpu(&pdev->dev, dma_base, - offset & dma_desc_align_mask, - dma_desc_sync_size, dir); + dma_sync_single_range_for_cpu(sdev->dev, dma_base, + offset & dma_desc_align_mask, + dma_desc_sync_size, dir); } static inline unsigned long br32(const struct b44 *bp, unsigned long reg) { - return readl(bp->regs + reg); + return ssb_read32(bp->sdev, reg); } static inline void bw32(const struct b44 *bp, unsigned long reg, unsigned long val) { - writel(val, bp->regs + reg); + ssb_write32(bp->sdev, reg, val); } static int b44_wait_bit(struct b44 *bp, unsigned long reg, @@ -181,117 +196,29 @@ static int b44_wait_bit(struct b44 *bp, return 0; } -/* Sonics SiliconBackplane support routines. ROFL, you should see all the - * buzz words used on this company's website :-) - * - * All of these routines must be invoked with bp->lock held and - * interrupts disabled. - */ - -#define SB_PCI_DMA 0x40000000 /* Client Mode PCI memory access space (1 GB) */ -#define BCM4400_PCI_CORE_ADDR 0x18002000 /* Address of PCI core on BCM4400 cards */ - -static u32 ssb_get_core_rev(struct b44 *bp) -{ - return (br32(bp, B44_SBIDHIGH) & SBIDHIGH_RC_MASK); -} - -static u32 ssb_pci_setup(struct b44 *bp, u32 cores) -{ - u32 bar_orig, pci_rev, val; - - pci_read_config_dword(bp->pdev, SSB_BAR0_WIN, &bar_orig); - pci_write_config_dword(bp->pdev, SSB_BAR0_WIN, BCM4400_PCI_CORE_ADDR); - pci_rev = ssb_get_core_rev(bp); - - val = br32(bp, B44_SBINTVEC); - val |= cores; - bw32(bp, B44_SBINTVEC, val); - - val = br32(bp, SSB_PCI_TRANS_2); - val |= SSB_PCI_PREF | SSB_PCI_BURST; - bw32(bp, SSB_PCI_TRANS_2, val); - - pci_write_config_dword(bp->pdev, SSB_BAR0_WIN, bar_orig); - - return pci_rev; -} - -static void ssb_core_disable(struct b44 *bp) -{ - if (br32(bp, B44_SBTMSLOW) & SBTMSLOW_RESET) - return; - - bw32(bp, B44_SBTMSLOW, (SBTMSLOW_REJECT | SBTMSLOW_CLOCK)); - b44_wait_bit(bp, B44_SBTMSLOW, SBTMSLOW_REJECT, 100000, 0); - b44_wait_bit(bp, B44_SBTMSHIGH, SBTMSHIGH_BUSY, 100000, 1); - bw32(bp, B44_SBTMSLOW, (SBTMSLOW_FGC | SBTMSLOW_CLOCK | - SBTMSLOW_REJECT | SBTMSLOW_RESET)); - br32(bp, B44_SBTMSLOW); - udelay(1); - bw32(bp, B44_SBTMSLOW, (SBTMSLOW_REJECT | SBTMSLOW_RESET)); - br32(bp, B44_SBTMSLOW); - udelay(1); -} - -static void ssb_core_reset(struct b44 *bp) +static inline void __b44_cam_read(struct b44 *bp, unsigned char *data, int index) { u32 val; - ssb_core_disable(bp); - bw32(bp, B44_SBTMSLOW, (SBTMSLOW_RESET | SBTMSLOW_CLOCK | SBTMSLOW_FGC)); - br32(bp, B44_SBTMSLOW); - udelay(1); - - /* Clear SERR if set, this is a hw bug workaround. */ - if (br32(bp, B44_SBTMSHIGH) & SBTMSHIGH_SERR) - bw32(bp, B44_SBTMSHIGH, 0); - - val = br32(bp, B44_SBIMSTATE); - if (val & (SBIMSTATE_IBE | SBIMSTATE_TO)) - bw32(bp, B44_SBIMSTATE, val & ~(SBIMSTATE_IBE | SBIMSTATE_TO)); - - bw32(bp, B44_SBTMSLOW, (SBTMSLOW_CLOCK | SBTMSLOW_FGC)); - br32(bp, B44_SBTMSLOW); - udelay(1); - - bw32(bp, B44_SBTMSLOW, (SBTMSLOW_CLOCK)); - br32(bp, B44_SBTMSLOW); - udelay(1); -} + bw32(bp, B44_CAM_CTRL, (CAM_CTRL_READ | + (index << CAM_CTRL_INDEX_SHIFT))); -static int ssb_core_unit(struct b44 *bp) -{ -#if 0 - u32 val = br32(bp, B44_SBADMATCH0); - u32 base; + b44_wait_bit(bp, B44_CAM_CTRL, CAM_CTRL_BUSY, 100, 1); - type = val & SBADMATCH0_TYPE_MASK; - switch (type) { - case 0: - base = val & SBADMATCH0_BS0_MASK; - break; + val = br32(bp, B44_CAM_DATA_LO); - case 1: - base = val & SBADMATCH0_BS1_MASK; - break; + data[2] = (val >> 24) & 0xFF; + data[3] = (val >> 16) & 0xFF; + data[4] = (val >> 8) & 0xFF; + data[5] = (val >> 0) & 0xFF; - case 2: - default: - base = val & SBADMATCH0_BS2_MASK; - break; - }; -#endif - return 0; -} + val = br32(bp, B44_CAM_DATA_HI); -static int ssb_is_core_up(struct b44 *bp) -{ - return ((br32(bp, B44_SBTMSLOW) & (SBTMSLOW_RESET | SBTMSLOW_REJECT | SBTMSLOW_CLOCK)) - == SBTMSLOW_CLOCK); + data[0] = (val >> 8) & 0xFF; + data[1] = (val >> 0) & 0xFF; } -static void __b44_cam_write(struct b44 *bp, unsigned char *data, int index) +static inline void __b44_cam_write(struct b44 *bp, unsigned char *data, int index) { u32 val; @@ -327,14 +254,14 @@ static void b44_enable_ints(struct b44 * bw32(bp, B44_IMASK, bp->imask); } -static int b44_readphy(struct b44 *bp, int reg, u32 *val) +static int __b44_readphy(struct b44 *bp, int phy_addr, int reg, u32 *val) { int err; bw32(bp, B44_EMAC_ISTAT, EMAC_INT_MII); bw32(bp, B44_MDIO_DATA, (MDIO_DATA_SB_START | (MDIO_OP_READ << MDIO_DATA_OP_SHIFT) | - (bp->phy_addr << MDIO_DATA_PMD_SHIFT) | + (phy_addr << MDIO_DATA_PMD_SHIFT) | (reg << MDIO_DATA_RA_SHIFT) | (MDIO_TA_VALID << MDIO_DATA_TA_SHIFT))); err = b44_wait_bit(bp, B44_EMAC_ISTAT, EMAC_INT_MII, 100, 0); @@ -343,18 +270,34 @@ static int b44_readphy(struct b44 *bp, i return err; } -static int b44_writephy(struct b44 *bp, int reg, u32 val) +static int __b44_writephy(struct b44 *bp, int phy_addr, int reg, u32 val) { bw32(bp, B44_EMAC_ISTAT, EMAC_INT_MII); bw32(bp, B44_MDIO_DATA, (MDIO_DATA_SB_START | (MDIO_OP_WRITE << MDIO_DATA_OP_SHIFT) | - (bp->phy_addr << MDIO_DATA_PMD_SHIFT) | + (phy_addr << MDIO_DATA_PMD_SHIFT) | (reg << MDIO_DATA_RA_SHIFT) | (MDIO_TA_VALID << MDIO_DATA_TA_SHIFT) | (val & MDIO_DATA_DATA))); return b44_wait_bit(bp, B44_EMAC_ISTAT, EMAC_INT_MII, 100, 0); } +static inline int b44_readphy(struct b44 *bp, int reg, u32 *val) +{ + if (bp->phy_addr == B44_PHY_ADDR_NO_PHY) + return 0; + + return __b44_readphy(bp, bp->phy_addr, reg, val); +} + +static inline int b44_writephy(struct b44 *bp, int reg, u32 val) +{ + if (bp->phy_addr == B44_PHY_ADDR_NO_PHY) + return 0; + + return __b44_writephy(bp, bp->phy_addr, reg, val); +} + /* miilib interface */ /* FIXME FIXME: phy_id is ignored, bp->phy_addr use is unconditional * due to code existing before miilib use was added to this driver. @@ -383,6 +326,8 @@ static int b44_phy_reset(struct b44 *bp) u32 val; int err; + if (bp->phy_addr == B44_PHY_ADDR_NO_PHY) + return 0; err = b44_writephy(bp, MII_BMCR, BMCR_RESET); if (err) return err; @@ -441,11 +386,52 @@ static void b44_set_flow_ctrl(struct b44 __b44_set_flow_ctrl(bp, pause_enab); } +#ifdef SSB_DRIVER_MIPS +extern char *nvram_get(char *name); +static void b44_wap54g10_workaround(struct b44 *bp) +{ + const char *str; + u32 val; + int err; + + /* + * workaround for bad hardware design in Linksys WAP54G v1.0 + * see https://dev.openwrt.org/ticket/146 + * check and reset bit "isolate" + */ + str = nvram_get("boardnum"); + if (!str) + return; + if (simple_strtoul(str, NULL, 0) == 2) { + err = __b44_readphy(bp, 0, MII_BMCR, &val); + if (err) + goto error; + if (!(val & BMCR_ISOLATE)) + return; + val &= ~BMCR_ISOLATE; + err = __b44_writephy(bp, 0, MII_BMCR, val); + if (err) + goto error; + } + return; +error: + printk(KERN_WARNING PFX "PHY: cannot reset MII transceiver isolate bit.\n"); +} +#else +static inline void b44_wap54g10_workaround(struct b44 *bp) +{ +} +#endif + static int b44_setup_phy(struct b44 *bp) { u32 val; int err; + b44_wap54g10_workaround(bp); + + if (bp->phy_addr == B44_PHY_ADDR_NO_PHY) + return 0; if ((err = b44_readphy(bp, B44_MII_ALEDCTRL, &val)) != 0) goto out; if ((err = b44_writephy(bp, B44_MII_ALEDCTRL, @@ -541,6 +527,19 @@ static void b44_check_phy(struct b44 *bp { u32 bmsr, aux; + if (bp->phy_addr == B44_PHY_ADDR_NO_PHY) { + bp->flags |= B44_FLAG_100_BASE_T; + bp->flags |= B44_FLAG_FULL_DUPLEX; + if (!netif_carrier_ok(bp->dev)) { + u32 val = br32(bp, B44_TX_CTRL); + val |= TX_CTRL_DUPLEX; + bw32(bp, B44_TX_CTRL, val); + netif_carrier_on(bp->dev); + b44_link_report(bp); + } + return; + } + if (!b44_readphy(bp, MII_BMSR, &bmsr) && !b44_readphy(bp, B44_MII_AUXCTRL, &aux) && (bmsr != 0xffff)) { @@ -617,10 +616,10 @@ static void b44_tx(struct b44 *bp) BUG_ON(skb == NULL); - pci_unmap_single(bp->pdev, - pci_unmap_addr(rp, mapping), + dma_unmap_single(bp->sdev->dev, + rp->mapping, skb->len, - PCI_DMA_TODEVICE); + DMA_TO_DEVICE); rp->skb = NULL; dev_kfree_skb_irq(skb); } @@ -657,9 +656,9 @@ static int b44_alloc_rx_skb(struct b44 * if (skb == NULL) return -ENOMEM; - mapping = pci_map_single(bp->pdev, skb->data, + mapping = dma_map_single(bp->sdev->dev, skb->data, RX_PKT_BUF_SZ, - PCI_DMA_FROMDEVICE); + DMA_FROM_DEVICE); /* Hardware bug work-around, the chip is unable to do PCI DMA to/from anything above 1GB :-( */ @@ -667,18 +666,19 @@ static int b44_alloc_rx_skb(struct b44 * mapping + RX_PKT_BUF_SZ > DMA_30BIT_MASK) { /* Sigh... */ if (!dma_mapping_error(mapping)) - pci_unmap_single(bp->pdev, mapping, RX_PKT_BUF_SZ,PCI_DMA_FROMDEVICE); + dma_unmap_single(bp->sdev->dev, mapping, + RX_PKT_BUF_SZ, DMA_FROM_DEVICE); dev_kfree_skb_any(skb); skb = __dev_alloc_skb(RX_PKT_BUF_SZ,GFP_DMA); if (skb == NULL) return -ENOMEM; - mapping = pci_map_single(bp->pdev, skb->data, + mapping = dma_map_single(bp->sdev->dev, skb->data, RX_PKT_BUF_SZ, - PCI_DMA_FROMDEVICE); + DMA_FROM_DEVICE); if (dma_mapping_error(mapping) || mapping + RX_PKT_BUF_SZ > DMA_30BIT_MASK) { if (!dma_mapping_error(mapping)) - pci_unmap_single(bp->pdev, mapping, RX_PKT_BUF_SZ,PCI_DMA_FROMDEVICE); + dma_unmap_single(bp->sdev->dev, mapping, RX_PKT_BUF_SZ,DMA_FROM_DEVICE); dev_kfree_skb_any(skb); return -ENOMEM; } @@ -693,7 +693,7 @@ static int b44_alloc_rx_skb(struct b44 * rh->flags = 0; map->skb = skb; - pci_unmap_addr_set(map, mapping, mapping); + map->mapping = mapping; if (src_map != NULL) src_map->skb = NULL; @@ -707,9 +707,9 @@ static int b44_alloc_rx_skb(struct b44 * dp->addr = cpu_to_le32((u32) mapping + bp->rx_offset + bp->dma_offset); if (bp->flags & B44_FLAG_RX_RING_HACK) - b44_sync_dma_desc_for_device(bp->pdev, bp->rx_ring_dma, - dest_idx * sizeof(dp), - DMA_BIDIRECTIONAL); + b44_sync_dma_desc_for_device(bp->sdev, bp->rx_ring_dma, + dest_idx * sizeof(dp), + DMA_BIDIRECTIONAL); return RX_PKT_BUF_SZ; } @@ -732,13 +732,12 @@ static void b44_recycle_rx(struct b44 *b rh = (struct rx_header *) src_map->skb->data; rh->len = 0; rh->flags = 0; - pci_unmap_addr_set(dest_map, mapping, - pci_unmap_addr(src_map, mapping)); + dest_map->mapping = src_map->mapping; if (bp->flags & B44_FLAG_RX_RING_HACK) - b44_sync_dma_desc_for_cpu(bp->pdev, bp->rx_ring_dma, - src_idx * sizeof(src_desc), - DMA_BIDIRECTIONAL); + b44_sync_dma_desc_for_cpu(bp->sdev, bp->rx_ring_dma, + src_idx * sizeof(src_desc), + DMA_BIDIRECTIONAL); ctrl = src_desc->ctrl; if (dest_idx == (B44_RX_RING_SIZE - 1)) @@ -752,13 +751,13 @@ static void b44_recycle_rx(struct b44 *b src_map->skb = NULL; if (bp->flags & B44_FLAG_RX_RING_HACK) - b44_sync_dma_desc_for_device(bp->pdev, bp->rx_ring_dma, - dest_idx * sizeof(dest_desc), - DMA_BIDIRECTIONAL); + b44_sync_dma_desc_for_device(bp->sdev, bp->rx_ring_dma, + dest_idx * sizeof(dest_desc), + DMA_BIDIRECTIONAL); - pci_dma_sync_single_for_device(bp->pdev, le32_to_cpu(src_desc->addr), - RX_PKT_BUF_SZ, - PCI_DMA_FROMDEVICE); + dma_sync_single_for_device(bp->sdev->dev, le32_to_cpu(src_desc->addr), + RX_PKT_BUF_SZ, + DMA_FROM_DEVICE); } static int b44_rx(struct b44 *bp, int budget) @@ -774,13 +773,13 @@ static int b44_rx(struct b44 *bp, int bu while (cons != prod && budget > 0) { struct ring_info *rp = &bp->rx_buffers[cons]; struct sk_buff *skb = rp->skb; - dma_addr_t map = pci_unmap_addr(rp, mapping); + dma_addr_t map = rp->mapping; struct rx_header *rh; u16 len; - pci_dma_sync_single_for_cpu(bp->pdev, map, + dma_sync_single_for_cpu(bp->sdev->dev, map, RX_PKT_BUF_SZ, - PCI_DMA_FROMDEVICE); + DMA_FROM_DEVICE); rh = (struct rx_header *) skb->data; len = le16_to_cpu(rh->len); if ((len > (RX_PKT_BUF_SZ - bp->rx_offset)) || @@ -812,11 +811,11 @@ static int b44_rx(struct b44 *bp, int bu skb_size = b44_alloc_rx_skb(bp, cons, bp->rx_prod); if (skb_size < 0) goto drop_it; - pci_unmap_single(bp->pdev, map, - skb_size, PCI_DMA_FROMDEVICE); + dma_unmap_single(bp->sdev->dev, map, + skb_size, DMA_FROM_DEVICE); /* Leave out rx_header */ - skb_put(skb, len+bp->rx_offset); - skb_pull(skb,bp->rx_offset); + skb_put(skb, len+bp->rx_offset); + skb_pull(skb,bp->rx_offset); } else { struct sk_buff *copy_skb; @@ -985,23 +984,24 @@ static int b44_start_xmit(struct sk_buff goto err_out; } - mapping = pci_map_single(bp->pdev, skb->data, len, PCI_DMA_TODEVICE); + mapping = dma_map_single(bp->sdev->dev, skb->data, len, DMA_TO_DEVICE); if (dma_mapping_error(mapping) || mapping + len > DMA_30BIT_MASK) { /* Chip can't handle DMA to/from >1GB, use bounce buffer */ if (!dma_mapping_error(mapping)) - pci_unmap_single(bp->pdev, mapping, len, PCI_DMA_TODEVICE); + dma_unmap_single(bp->sdev->dev, mapping, len, + DMA_TO_DEVICE); bounce_skb = __dev_alloc_skb(TX_PKT_BUF_SZ, GFP_ATOMIC|GFP_DMA); if (!bounce_skb) goto err_out; - mapping = pci_map_single(bp->pdev, bounce_skb->data, - len, PCI_DMA_TODEVICE); + mapping = dma_map_single(bp->sdev->dev, bounce_skb->data, + len, DMA_TO_DEVICE); if (dma_mapping_error(mapping) || mapping + len > DMA_30BIT_MASK) { if (!dma_mapping_error(mapping)) - pci_unmap_single(bp->pdev, mapping, - len, PCI_DMA_TODEVICE); + dma_unmap_single(bp->sdev->dev, mapping, + len, DMA_TO_DEVICE); dev_kfree_skb_any(bounce_skb); goto err_out; } @@ -1014,7 +1014,7 @@ static int b44_start_xmit(struct sk_buff entry = bp->tx_prod; bp->tx_buffers[entry].skb = skb; - pci_unmap_addr_set(&bp->tx_buffers[entry], mapping, mapping); + bp->tx_buffers[entry].mapping = mapping; ctrl = (len & DESC_CTRL_LEN); ctrl |= DESC_CTRL_IOC | DESC_CTRL_SOF | DESC_CTRL_EOF; @@ -1025,9 +1025,9 @@ static int b44_start_xmit(struct sk_buff bp->tx_ring[entry].addr = cpu_to_le32((u32) mapping+bp->dma_offset); if (bp->flags & B44_FLAG_TX_RING_HACK) - b44_sync_dma_desc_for_device(bp->pdev, bp->tx_ring_dma, - entry * sizeof(bp->tx_ring[0]), - DMA_TO_DEVICE); + b44_sync_dma_desc_for_device(bp->sdev, bp->tx_ring_dma, + entry * sizeof(bp->tx_ring[0]), + DMA_TO_DEVICE); entry = NEXT_TX(entry); @@ -1100,10 +1100,8 @@ static void b44_free_rings(struct b44 *b if (rp->skb == NULL) continue; - pci_unmap_single(bp->pdev, - pci_unmap_addr(rp, mapping), - RX_PKT_BUF_SZ, - PCI_DMA_FROMDEVICE); + dma_unmap_single(bp->sdev->dev, rp->mapping, RX_PKT_BUF_SZ, + DMA_FROM_DEVICE); dev_kfree_skb_any(rp->skb); rp->skb = NULL; } @@ -1114,10 +1112,8 @@ static void b44_free_rings(struct b44 *b if (rp->skb == NULL) continue; - pci_unmap_single(bp->pdev, - pci_unmap_addr(rp, mapping), - rp->skb->len, - PCI_DMA_TODEVICE); + dma_unmap_single(bp->sdev->dev, rp->mapping, rp->skb->len, + DMA_TO_DEVICE); dev_kfree_skb_any(rp->skb); rp->skb = NULL; } @@ -1139,14 +1135,14 @@ static void b44_init_rings(struct b44 *b memset(bp->tx_ring, 0, B44_TX_RING_BYTES); if (bp->flags & B44_FLAG_RX_RING_HACK) - dma_sync_single_for_device(&bp->pdev->dev, bp->rx_ring_dma, - DMA_TABLE_BYTES, - PCI_DMA_BIDIRECTIONAL); + dma_sync_single_for_device(bp->sdev->dev, bp->rx_ring_dma, + DMA_TABLE_BYTES, + DMA_BIDIRECTIONAL); if (bp->flags & B44_FLAG_TX_RING_HACK) - dma_sync_single_for_device(&bp->pdev->dev, bp->tx_ring_dma, - DMA_TABLE_BYTES, - PCI_DMA_TODEVICE); + dma_sync_single_for_device(bp->sdev->dev, bp->tx_ring_dma, + DMA_TABLE_BYTES, + DMA_TO_DEVICE); for (i = 0; i < bp->rx_pending; i++) { if (b44_alloc_rx_skb(bp, -1, i) < 0) @@ -1166,24 +1162,24 @@ static void b44_free_consistent(struct b bp->tx_buffers = NULL; if (bp->rx_ring) { if (bp->flags & B44_FLAG_RX_RING_HACK) { - dma_unmap_single(&bp->pdev->dev, bp->rx_ring_dma, - DMA_TABLE_BYTES, - DMA_BIDIRECTIONAL); + dma_unmap_single(bp->sdev->dev, bp->rx_ring_dma, + DMA_TABLE_BYTES, + DMA_BIDIRECTIONAL); kfree(bp->rx_ring); } else - pci_free_consistent(bp->pdev, DMA_TABLE_BYTES, + dma_free_coherent(bp->sdev->dev, DMA_TABLE_BYTES, bp->rx_ring, bp->rx_ring_dma); bp->rx_ring = NULL; bp->flags &= ~B44_FLAG_RX_RING_HACK; } if (bp->tx_ring) { if (bp->flags & B44_FLAG_TX_RING_HACK) { - dma_unmap_single(&bp->pdev->dev, bp->tx_ring_dma, - DMA_TABLE_BYTES, - DMA_TO_DEVICE); + dma_unmap_single(bp->sdev->dev, bp->tx_ring_dma, + DMA_TABLE_BYTES, + DMA_TO_DEVICE); kfree(bp->tx_ring); } else - pci_free_consistent(bp->pdev, DMA_TABLE_BYTES, + dma_free_coherent(bp->sdev->dev, DMA_TABLE_BYTES, bp->tx_ring, bp->tx_ring_dma); bp->tx_ring = NULL; bp->flags &= ~B44_FLAG_TX_RING_HACK; @@ -1194,22 +1190,22 @@ static void b44_free_consistent(struct b * Must not be invoked with interrupt sources disabled and * the hardware shutdown down. Can sleep. */ -static int b44_alloc_consistent(struct b44 *bp) +static int b44_alloc_consistent(struct b44 *bp, gfp_t gfp) { int size; size = B44_RX_RING_SIZE * sizeof(struct ring_info); - bp->rx_buffers = kzalloc(size, GFP_KERNEL); + bp->rx_buffers = kzalloc(size, gfp); if (!bp->rx_buffers) goto out_err; size = B44_TX_RING_SIZE * sizeof(struct ring_info); - bp->tx_buffers = kzalloc(size, GFP_KERNEL); + bp->tx_buffers = kzalloc(size, gfp); if (!bp->tx_buffers) goto out_err; size = DMA_TABLE_BYTES; - bp->rx_ring = pci_alloc_consistent(bp->pdev, size, &bp->rx_ring_dma); + bp->rx_ring = dma_alloc_coherent(bp->sdev->dev, size, &bp->rx_ring_dma, gfp); if (!bp->rx_ring) { /* Allocation may have failed due to pci_alloc_consistent insisting on use of GFP_DMA, which is more restrictive @@ -1217,13 +1213,13 @@ static int b44_alloc_consistent(struct b struct dma_desc *rx_ring; dma_addr_t rx_ring_dma; - rx_ring = kzalloc(size, GFP_KERNEL); + rx_ring = kzalloc(size, gfp); if (!rx_ring) goto out_err; - rx_ring_dma = dma_map_single(&bp->pdev->dev, rx_ring, - DMA_TABLE_BYTES, - DMA_BIDIRECTIONAL); + rx_ring_dma = dma_map_single(bp->sdev->dev, rx_ring, + DMA_TABLE_BYTES, + DMA_BIDIRECTIONAL); if (dma_mapping_error(rx_ring_dma) || rx_ring_dma + size > DMA_30BIT_MASK) { @@ -1236,21 +1232,21 @@ static int b44_alloc_consistent(struct b bp->flags |= B44_FLAG_RX_RING_HACK; } - bp->tx_ring = pci_alloc_consistent(bp->pdev, size, &bp->tx_ring_dma); + bp->tx_ring = dma_alloc_coherent(bp->sdev->dev, size, &bp->tx_ring_dma, gfp); if (!bp->tx_ring) { - /* Allocation may have failed due to pci_alloc_consistent + /* Allocation may have failed due to dma_alloc_coherent insisting on use of GFP_DMA, which is more restrictive than necessary... */ struct dma_desc *tx_ring; dma_addr_t tx_ring_dma; - tx_ring = kzalloc(size, GFP_KERNEL); + tx_ring = kzalloc(size, gfp); if (!tx_ring) goto out_err; - tx_ring_dma = dma_map_single(&bp->pdev->dev, tx_ring, - DMA_TABLE_BYTES, - DMA_TO_DEVICE); + tx_ring_dma = dma_map_single(bp->sdev->dev, tx_ring, + DMA_TABLE_BYTES, + DMA_TO_DEVICE); if (dma_mapping_error(tx_ring_dma) || tx_ring_dma + size > DMA_30BIT_MASK) { @@ -1285,7 +1281,9 @@ static void b44_clear_stats(struct b44 * /* bp->lock is held. */ static void b44_chip_reset(struct b44 *bp) { - if (ssb_is_core_up(bp)) { + struct ssb_device *sdev = bp->sdev; + + if (ssb_device_is_enabled(bp->sdev)) { bw32(bp, B44_RCV_LAZY, 0); bw32(bp, B44_ENET_CTRL, ENET_CTRL_DISABLE); b44_wait_bit(bp, B44_ENET_CTRL, ENET_CTRL_DISABLE, 200, 1); @@ -1297,19 +1295,25 @@ static void b44_chip_reset(struct b44 *b } bw32(bp, B44_DMARX_CTRL, 0); bp->rx_prod = bp->rx_cons = 0; - } else { - ssb_pci_setup(bp, (bp->core_unit == 0 ? - SBINTVEC_ENET0 : - SBINTVEC_ENET1)); - } - - ssb_core_reset(bp); + } else + ssb_pcicore_dev_irqvecs_enable(&sdev->bus->pcicore, sdev); + ssb_device_enable(bp->sdev, 0); b44_clear_stats(bp); - /* Make PHY accessible. */ - bw32(bp, B44_MDIO_CTRL, (MDIO_CTRL_PREAMBLE | - (0x0d & MDIO_CTRL_MAXF_MASK))); + switch (sdev->bus->bustype) { + case SSB_BUSTYPE_SSB: + bw32(bp, B44_MDIO_CTRL, (MDIO_CTRL_PREAMBLE | + (((ssb_clockspeed(sdev->bus) + (B44_MDC_RATIO / 2)) / B44_MDC_RATIO) + & MDIO_CTRL_MAXF_MASK))); + break; + case SSB_BUSTYPE_PCI: + case SSB_BUSTYPE_PCMCIA: + bw32(bp, B44_MDIO_CTRL, (MDIO_CTRL_PREAMBLE | + (0x0d & MDIO_CTRL_MAXF_MASK))); + break; + } + br32(bp, B44_MDIO_CTRL); if (!(br32(bp, B44_DEVCTRL) & DEVCTRL_IPP)) { @@ -1352,6 +1356,7 @@ static int b44_set_mac_addr(struct net_d { struct b44 *bp = netdev_priv(dev); struct sockaddr *addr = p; + u32 val; if (netif_running(dev)) return -EBUSY; @@ -1362,7 +1367,11 @@ static int b44_set_mac_addr(struct net_d memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); spin_lock_irq(&bp->lock); - __b44_set_mac_addr(bp); + + val = br32(bp, B44_RXCONFIG); + if (!(val & RXCONFIG_CAM_ABSENT)) + __b44_set_mac_addr(bp); + spin_unlock_irq(&bp->lock); return 0; @@ -1419,7 +1428,7 @@ static int b44_open(struct net_device *d struct b44 *bp = netdev_priv(dev); int err; - err = b44_alloc_consistent(bp); + err = b44_alloc_consistent(bp, GFP_KERNEL); if (err) goto out; @@ -1448,18 +1457,6 @@ out: return err; } -#if 0 -/*static*/ void b44_dump_state(struct b44 *bp) -{ - u32 val32, val32_2, val32_3, val32_4, val32_5; - u16 val16; - - pci_read_config_word(bp->pdev, PCI_STATUS, &val16); - printk("DEBUG: PCI status [%04x] \n", val16); - -} -#endif - #ifdef CONFIG_NET_POLL_CONTROLLER /* * Polling receive - used by netconsole and other diagnostic tools @@ -1574,7 +1571,6 @@ static void b44_setup_pseudo_magicp(stru static void b44_setup_wol(struct b44 *bp) { u32 val; - u16 pmval; bw32(bp, B44_RXCONFIG, RXCONFIG_ALLMULTI); @@ -1599,12 +1595,14 @@ static void b44_setup_wol(struct b44 *bp b44_setup_pseudo_magicp(bp); } - val = br32(bp, B44_SBTMSLOW); - bw32(bp, B44_SBTMSLOW, val | SBTMSLOW_PE); - - pci_read_config_word(bp->pdev, SSB_PMCSR, &pmval); - pci_write_config_word(bp->pdev, SSB_PMCSR, pmval | SSB_PE); - +#ifdef CONFIG_B44_PCI + if (bp->sdev->bus->bustype != SSB_BUSTYPE_SSB) { + val = br32(bp, SSB_TMSLOW); + bw32(bp, SSB_TMSLOW, val | SSB_TMSLOW_PE); + } + pci_read_config_word(bp->sdev->bus->host_pci, SSB_PMCSR, (u16 *)(&val)); + pci_write_config_word(bp->sdev->bus->host_pci, SSB_PMCSR, val | SSB_PE); +#endif /* CONFIG_B44_PCI */ } static int b44_close(struct net_device *dev) @@ -1619,9 +1617,6 @@ static int b44_close(struct net_device * spin_lock_irq(&bp->lock); -#if 0 - b44_dump_state(bp); -#endif b44_halt(bp); b44_free_rings(bp); netif_carrier_off(dev); @@ -1704,7 +1699,7 @@ static void __b44_set_rx_mode(struct net val = br32(bp, B44_RXCONFIG); val &= ~(RXCONFIG_PROMISC | RXCONFIG_ALLMULTI); - if (dev->flags & IFF_PROMISC) { + if ((dev->flags & IFF_PROMISC) || (val & RXCONFIG_CAM_ABSENT)) { val |= RXCONFIG_PROMISC; bw32(bp, B44_RXCONFIG, val); } else { @@ -1752,11 +1747,19 @@ static void b44_set_msglevel(struct net_ static void b44_get_drvinfo (struct net_device *dev, struct ethtool_drvinfo *info) { struct b44 *bp = netdev_priv(dev); - struct pci_dev *pci_dev = bp->pdev; + struct ssb_bus *bus = bp->sdev->bus; - strcpy (info->driver, DRV_MODULE_NAME); - strcpy (info->version, DRV_MODULE_VERSION); - strcpy (info->bus_info, pci_name(pci_dev)); + strncpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); + strncpy(info->version, DRV_MODULE_VERSION, sizeof(info->driver)); + switch (bus->bustype) { + case SSB_BUSTYPE_PCI: + strncpy(info->bus_info, pci_name(bus->host_pci), sizeof(info->bus_info)); + break; + case SSB_BUSTYPE_PCMCIA: + case SSB_BUSTYPE_SSB: + strncpy(info->bus_info, "SSB", sizeof(info->bus_info)); + break; + } } static int b44_nway_reset(struct net_device *dev) @@ -2056,33 +2059,23 @@ out: return err; } -/* Read 128-bytes of EEPROM. */ -static int b44_read_eeprom(struct b44 *bp, u8 *data) -{ - long i; - __le16 *ptr = (__le16 *) data; - - for (i = 0; i < 128; i += 2) - ptr[i / 2] = cpu_to_le16(readw(bp->regs + 4096 + i)); - - return 0; -} - static int __devinit b44_get_invariants(struct b44 *bp) { - u8 eeprom[128]; - int err; + struct ssb_device *sdev = bp->sdev; + int err = 0; + u8 *addr; - err = b44_read_eeprom(bp, &eeprom[0]); - if (err) - goto out; + bp->dma_offset = ssb_dma_translation(sdev); - bp->dev->dev_addr[0] = eeprom[79]; - bp->dev->dev_addr[1] = eeprom[78]; - bp->dev->dev_addr[2] = eeprom[81]; - bp->dev->dev_addr[3] = eeprom[80]; - bp->dev->dev_addr[4] = eeprom[83]; - bp->dev->dev_addr[5] = eeprom[82]; + if (sdev->bus->bustype == SSB_BUSTYPE_SSB && + instance > 1) { + addr = sdev->bus->sprom.r1.et1mac; + bp->phy_addr = sdev->bus->sprom.r1.et1phyaddr; + } else { + addr = sdev->bus->sprom.r1.et0mac; + bp->phy_addr = sdev->bus->sprom.r1.et0phyaddr; + } + memcpy(bp->dev->dev_addr, addr, 6); if (!is_valid_ether_addr(&bp->dev->dev_addr[0])){ printk(KERN_ERR PFX "Invalid MAC address found in EEPROM\n"); @@ -2091,108 +2084,55 @@ static int __devinit b44_get_invariants( memcpy(bp->dev->perm_addr, bp->dev->dev_addr, bp->dev->addr_len); - bp->phy_addr = eeprom[90] & 0x1f; - /* With this, plus the rx_header prepended to the data by the * hardware, we'll land the ethernet header on a 2-byte boundary. */ bp->rx_offset = 30; - bp->imask = IMASK_DEF; - - bp->core_unit = ssb_core_unit(bp); - bp->dma_offset = SB_PCI_DMA; - /* XXX - really required? bp->flags |= B44_FLAG_BUGGY_TXPTR; - */ + */ - if (ssb_get_core_rev(bp) >= 7) - bp->flags |= B44_FLAG_B0_ANDLATER; + if (bp->sdev->id.revision >= 7) + bp->flags |= B44_FLAG_B0_ANDLATER; -out: return err; } -static int __devinit b44_init_one(struct pci_dev *pdev, - const struct pci_device_id *ent) +static int __devinit b44_init_one(struct ssb_device *sdev, + const struct ssb_device_id *ent) { static int b44_version_printed = 0; - unsigned long b44reg_base, b44reg_len; struct net_device *dev; struct b44 *bp; int err, i; + instance++; + if (b44_version_printed++ == 0) printk(KERN_INFO "%s", version); - err = pci_enable_device(pdev); - if (err) { - dev_err(&pdev->dev, "Cannot enable PCI device, " - "aborting.\n"); - return err; - } - - if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) { - dev_err(&pdev->dev, - "Cannot find proper PCI device " - "base address, aborting.\n"); - err = -ENODEV; - goto err_out_disable_pdev; - } - - err = pci_request_regions(pdev, DRV_MODULE_NAME); - if (err) { - dev_err(&pdev->dev, - "Cannot obtain PCI resources, aborting.\n"); - goto err_out_disable_pdev; - } - - pci_set_master(pdev); - - err = pci_set_dma_mask(pdev, (u64) DMA_30BIT_MASK); - if (err) { - dev_err(&pdev->dev, "No usable DMA configuration, aborting.\n"); - goto err_out_free_res; - } - - err = pci_set_consistent_dma_mask(pdev, (u64) DMA_30BIT_MASK); - if (err) { - dev_err(&pdev->dev, "No usable DMA configuration, aborting.\n"); - goto err_out_free_res; - } - - b44reg_base = pci_resource_start(pdev, 0); - b44reg_len = pci_resource_len(pdev, 0); - dev = alloc_etherdev(sizeof(*bp)); if (!dev) { - dev_err(&pdev->dev, "Etherdev alloc failed, aborting.\n"); + dev_err(sdev->dev, "Etherdev alloc failed, aborting.\n"); err = -ENOMEM; - goto err_out_free_res; + goto out; } SET_MODULE_OWNER(dev); - SET_NETDEV_DEV(dev,&pdev->dev); + SET_NETDEV_DEV(dev, sdev->dev); /* No interesting netdevice features in this card... */ dev->features |= 0; bp = netdev_priv(dev); - bp->pdev = pdev; + bp->sdev = sdev; bp->dev = dev; bp->msg_enable = netif_msg_init(b44_debug, B44_DEF_MSG_ENABLE); spin_lock_init(&bp->lock); - bp->regs = ioremap(b44reg_base, b44reg_len); - if (bp->regs == 0UL) { - dev_err(&pdev->dev, "Cannot map device registers, aborting.\n"); - err = -ENOMEM; - goto err_out_free_dev; - } - bp->rx_pending = B44_DEF_RX_RING_PENDING; bp->tx_pending = B44_DEF_TX_RING_PENDING; @@ -2211,16 +2151,22 @@ #ifdef CONFIG_NET_POLL_CONTROLLER dev->poll_controller = b44_poll_controller; #endif dev->change_mtu = b44_change_mtu; - dev->irq = pdev->irq; + dev->irq = sdev->irq; SET_ETHTOOL_OPS(dev, &b44_ethtool_ops); netif_carrier_off(dev); + err = ssb_dma_set_mask(sdev, DMA_30BIT_MASK); + if (err) { + dev_err(sdev->dev, + "Required 30BIT DMA mask unsupported by the system.\n"); + goto err_out_free_dev; + } err = b44_get_invariants(bp); if (err) { - dev_err(&pdev->dev, + dev_err(sdev->dev, "Problem fetching invariants of chip, aborting.\n"); - goto err_out_iounmap; + goto err_out_free_dev; } bp->mii_if.dev = dev; @@ -2239,61 +2185,52 @@ #endif err = register_netdev(dev); if (err) { - dev_err(&pdev->dev, "Cannot register net device, aborting.\n"); - goto err_out_iounmap; + dev_err(sdev->dev, "Cannot register net device, aborting.\n"); + goto out; } - pci_set_drvdata(pdev, dev); - - pci_save_state(bp->pdev); + ssb_set_drvdata(sdev, dev); /* Chip reset provides power to the b44 MAC & PCI cores, which * is necessary for MAC register access. */ b44_chip_reset(bp); - printk(KERN_INFO "%s: Broadcom 4400 10/100BaseT Ethernet ", dev->name); + printk(KERN_INFO "%s: Broadcom 44xx/47xx 10/100BaseT Ethernet ", dev->name); for (i = 0; i < 6; i++) printk("%2.2x%c", dev->dev_addr[i], i == 5 ? '\n' : ':'); - return 0; + /* Initialize phy */ + spin_lock_irq(&bp->lock); + b44_chip_reset(bp); + spin_unlock_irq(&bp->lock); -err_out_iounmap: - iounmap(bp->regs); + return 0; err_out_free_dev: free_netdev(dev); -err_out_free_res: - pci_release_regions(pdev); - -err_out_disable_pdev: - pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); +out: return err; } -static void __devexit b44_remove_one(struct pci_dev *pdev) +static void __devexit b44_remove_one(struct ssb_device *pdev) { - struct net_device *dev = pci_get_drvdata(pdev); - struct b44 *bp = netdev_priv(dev); + struct net_device *dev = ssb_get_drvdata(pdev); unregister_netdev(dev); - iounmap(bp->regs); free_netdev(dev); - pci_release_regions(pdev); - pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); + ssb_set_drvdata(pdev, NULL); } -static int b44_suspend(struct pci_dev *pdev, pm_message_t state) +static int b44_suspend(struct ssb_device *sdev, pm_message_t state) { - struct net_device *dev = pci_get_drvdata(pdev); + struct net_device *dev = ssb_get_drvdata(sdev); struct b44 *bp = netdev_priv(dev); if (!netif_running(dev)) - return 0; + return 0; del_timer_sync(&bp->timer); @@ -2311,33 +2248,22 @@ static int b44_suspend(struct pci_dev *p b44_init_hw(bp, B44_PARTIAL_RESET); b44_setup_wol(bp); } - pci_disable_device(pdev); + return 0; } -static int b44_resume(struct pci_dev *pdev) +static int b44_resume(struct ssb_device *sdev) { - struct net_device *dev = pci_get_drvdata(pdev); + struct net_device *dev = ssb_get_drvdata(sdev); struct b44 *bp = netdev_priv(dev); int rc = 0; - pci_restore_state(pdev); - rc = pci_enable_device(pdev); - if (rc) { - printk(KERN_ERR PFX "%s: pci_enable_device failed\n", - dev->name); - return rc; - } - - pci_set_master(pdev); - if (!netif_running(dev)) return 0; rc = request_irq(dev->irq, b44_interrupt, IRQF_SHARED, dev->name, dev); if (rc) { printk(KERN_ERR PFX "%s: request_irq failed\n", dev->name); - pci_disable_device(pdev); return rc; } @@ -2356,29 +2282,53 @@ static int b44_resume(struct pci_dev *pd return 0; } -static struct pci_driver b44_driver = { +static struct ssb_driver b44_ssb_driver = { .name = DRV_MODULE_NAME, - .id_table = b44_pci_tbl, + .id_table = b44_ssb_tbl, .probe = b44_init_one, .remove = __devexit_p(b44_remove_one), - .suspend = b44_suspend, - .resume = b44_resume, + .suspend = b44_suspend, + .resume = b44_resume, }; +static inline int b44_pci_init(void) +{ + int err = 0; +#ifdef CONFIG_B44_PCI + err = ssb_pcihost_register(&b44_pci_driver); +#endif + return err; +} + +static inline void b44_pci_exit(void) +{ +#ifdef CONFIG_B44_PCI + ssb_pcihost_unregister(&b44_pci_driver); +#endif +} + static int __init b44_init(void) { unsigned int dma_desc_align_size = dma_get_cache_alignment(); + int err; /* Setup paramaters for syncing RX/TX DMA descriptors */ dma_desc_align_mask = ~(dma_desc_align_size - 1); dma_desc_sync_size = max_t(unsigned int, dma_desc_align_size, sizeof(struct dma_desc)); - return pci_register_driver(&b44_driver); + err = b44_pci_init(); + if (err) + return err; + err = ssb_driver_register(&b44_ssb_driver); + if (err) + b44_pci_exit(); + return err; } static void __exit b44_cleanup(void) { - pci_unregister_driver(&b44_driver); + ssb_driver_unregister(&b44_ssb_driver); + b44_pci_exit(); } module_init(b44_init); diff --git a/drivers/net/b44.h b/drivers/net/b44.h index 18fc133..63f338f 100644 --- a/drivers/net/b44.h +++ b/drivers/net/b44.h @@ -129,6 +129,7 @@ #define RXCONFIG_LPBACK 0x00000010 /* L #define RXCONFIG_FLOW 0x00000020 /* Flow Control Enable */ #define RXCONFIG_FLOW_ACCEPT 0x00000040 /* Accept Unicast Flow Control Frame */ #define RXCONFIG_RFILT 0x00000080 /* Reject Filter */ +#define RXCONFIG_CAM_ABSENT 0x00000100 /* CAM Absent */ #define B44_RXMAXLEN 0x0404UL /* EMAC RX Max Packet Length */ #define B44_TXMAXLEN 0x0408UL /* EMAC TX Max Packet Length */ #define B44_MDIO_CTRL 0x0410UL /* EMAC MDIO Control */ @@ -227,76 +228,6 @@ #define B44_RX_SYM 0x05D0UL /* MIB RX Sy #define B44_RX_PAUSE 0x05D4UL /* MIB RX Pause Packets */ #define B44_RX_NPAUSE 0x05D8UL /* MIB RX Non-Pause Packets */ -/* Silicon backplane register definitions */ -#define B44_SBIMSTATE 0x0F90UL /* SB Initiator Agent State */ -#define SBIMSTATE_PC 0x0000000f /* Pipe Count */ -#define SBIMSTATE_AP_MASK 0x00000030 /* Arbitration Priority */ -#define SBIMSTATE_AP_BOTH 0x00000000 /* Use both timeslices and token */ -#define SBIMSTATE_AP_TS 0x00000010 /* Use timeslices only */ -#define SBIMSTATE_AP_TK 0x00000020 /* Use token only */ -#define SBIMSTATE_AP_RSV 0x00000030 /* Reserved */ -#define SBIMSTATE_IBE 0x00020000 /* In Band Error */ -#define SBIMSTATE_TO 0x00040000 /* Timeout */ -#define B44_SBINTVEC 0x0F94UL /* SB Interrupt Mask */ -#define SBINTVEC_PCI 0x00000001 /* Enable interrupts for PCI */ -#define SBINTVEC_ENET0 0x00000002 /* Enable interrupts for enet 0 */ -#define SBINTVEC_ILINE20 0x00000004 /* Enable interrupts for iline20 */ -#define SBINTVEC_CODEC 0x00000008 /* Enable interrupts for v90 codec */ -#define SBINTVEC_USB 0x00000010 /* Enable interrupts for usb */ -#define SBINTVEC_EXTIF 0x00000020 /* Enable interrupts for external i/f */ -#define SBINTVEC_ENET1 0x00000040 /* Enable interrupts for enet 1 */ -#define B44_SBTMSLOW 0x0F98UL /* SB Target State Low */ -#define SBTMSLOW_RESET 0x00000001 /* Reset */ -#define SBTMSLOW_REJECT 0x00000002 /* Reject */ -#define SBTMSLOW_CLOCK 0x00010000 /* Clock Enable */ -#define SBTMSLOW_FGC 0x00020000 /* Force Gated Clocks On */ -#define SBTMSLOW_PE 0x40000000 /* Power Management Enable */ -#define SBTMSLOW_BE 0x80000000 /* BIST Enable */ -#define B44_SBTMSHIGH 0x0F9CUL /* SB Target State High */ -#define SBTMSHIGH_SERR 0x00000001 /* S-error */ -#define SBTMSHIGH_INT 0x00000002 /* Interrupt */ -#define SBTMSHIGH_BUSY 0x00000004 /* Busy */ -#define SBTMSHIGH_GCR 0x20000000 /* Gated Clock Request */ -#define SBTMSHIGH_BISTF 0x40000000 /* BIST Failed */ -#define SBTMSHIGH_BISTD 0x80000000 /* BIST Done */ -#define B44_SBIDHIGH 0x0FFCUL /* SB Identification High */ -#define SBIDHIGH_RC_MASK 0x0000000f /* Revision Code */ -#define SBIDHIGH_CC_MASK 0x0000fff0 /* Core Code */ -#define SBIDHIGH_CC_SHIFT 4 -#define SBIDHIGH_VC_MASK 0xffff0000 /* Vendor Code */ -#define SBIDHIGH_VC_SHIFT 16 - -/* SSB PCI config space registers. */ -#define SSB_PMCSR 0x44 -#define SSB_PE 0x100 -#define SSB_BAR0_WIN 0x80 -#define SSB_BAR1_WIN 0x84 -#define SSB_SPROM_CONTROL 0x88 -#define SSB_BAR1_CONTROL 0x8c - -/* SSB core and host control registers. */ -#define SSB_CONTROL 0x0000UL -#define SSB_ARBCONTROL 0x0010UL -#define SSB_ISTAT 0x0020UL -#define SSB_IMASK 0x0024UL -#define SSB_MBOX 0x0028UL -#define SSB_BCAST_ADDR 0x0050UL -#define SSB_BCAST_DATA 0x0054UL -#define SSB_PCI_TRANS_0 0x0100UL -#define SSB_PCI_TRANS_1 0x0104UL -#define SSB_PCI_TRANS_2 0x0108UL -#define SSB_SPROM 0x0800UL - -#define SSB_PCI_MEM 0x00000000 -#define SSB_PCI_IO 0x00000001 -#define SSB_PCI_CFG0 0x00000002 -#define SSB_PCI_CFG1 0x00000003 -#define SSB_PCI_PREF 0x00000004 -#define SSB_PCI_BURST 0x00000008 -#define SSB_PCI_MASK0 0xfc000000 -#define SSB_PCI_MASK1 0xfc000000 -#define SSB_PCI_MASK2 0xc0000000 - /* 4400 PHY registers */ #define B44_MII_AUXCTRL 24 /* Auxiliary Control */ #define MII_AUXCTRL_DUPLEX 0x0001 /* Full Duplex */ @@ -346,10 +277,12 @@ #define RX_FLAG_ERRORS (RX_FLAG_ODD | RX struct ring_info { struct sk_buff *skb; - DECLARE_PCI_UNMAP_ADDR(mapping); + dma_addr_t mapping; }; #define B44_MCAST_TABLE_SIZE 32 +#define B44_PHY_ADDR_NO_PHY 30 +#define B44_MDC_RATIO 5000000 #define B44_STAT_REG_DECLARE \ _B44(tx_good_octets) \ @@ -410,6 +343,8 @@ B44_STAT_REG_DECLARE #undef _B44 }; +struct ssb_device; + struct b44 { spinlock_t lock; @@ -452,8 +387,7 @@ #define B44_FLAG_WOL_ENABLE 0x80000000 struct net_device_stats stats; struct b44_hw_stats hw_stats; - void __iomem *regs; - struct pci_dev *pdev; + struct ssb_device *sdev; struct net_device *dev; dma_addr_t rx_ring_dma, tx_ring_dma; @@ -461,7 +395,6 @@ #define B44_FLAG_WOL_ENABLE 0x80000000 u32 rx_pending; u32 tx_pending; u8 phy_addr; - u8 core_unit; struct mii_if_info mii_if; }; diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index c4b3dc2..6cbf2de 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -542,4 +542,6 @@ source "drivers/net/wireless/hostap/Kcon source "drivers/net/wireless/bcm43xx/Kconfig" source "drivers/net/wireless/zd1211rw/Kconfig" +source "drivers/net/wireless/mac80211/Kconfig" + endmenu diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index d212460..6d2375b 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -44,3 +44,6 @@ obj-$(CONFIG_PCMCIA_WL3501) += wl3501_cs obj-$(CONFIG_USB_ZD1201) += zd1201.o obj-$(CONFIG_LIBERTAS_USB) += libertas/ + +# Drivers using Devicescape IEEE 802.11 stack (net/mac80211) +obj-y += mac80211/ diff --git a/drivers/net/wireless/bcm43xx/Kconfig b/drivers/net/wireless/bcm43xx/Kconfig index ce397e4..f69510d 100644 --- a/drivers/net/wireless/bcm43xx/Kconfig +++ b/drivers/net/wireless/bcm43xx/Kconfig @@ -1,6 +1,7 @@ config BCM43XX tristate "Broadcom BCM43xx wireless support" depends on PCI && IEEE80211 && IEEE80211_SOFTMAC && WLAN_80211 && EXPERIMENTAL + depends on BCM43XX_MAC80211 != 'y' select WIRELESS_EXT select FW_LOADER select HW_RANDOM diff --git a/drivers/net/wireless/mac80211/Kconfig b/drivers/net/wireless/mac80211/Kconfig new file mode 100644 index 0000000..fadf1b5 --- /dev/null +++ b/drivers/net/wireless/mac80211/Kconfig @@ -0,0 +1,6 @@ +source "drivers/net/wireless/mac80211/bcm43xx/Kconfig" +source "drivers/net/wireless/mac80211/rt2x00/Kconfig" +source "drivers/net/wireless/mac80211/adm8211/Kconfig" +source "drivers/net/wireless/mac80211/p54/Kconfig" +source "drivers/net/wireless/mac80211/zd1211rw/Kconfig" +source "drivers/net/wireless/mac80211/rtl818x/Kconfig" diff --git a/drivers/net/wireless/mac80211/Makefile b/drivers/net/wireless/mac80211/Makefile new file mode 100644 index 0000000..2d64145 --- /dev/null +++ b/drivers/net/wireless/mac80211/Makefile @@ -0,0 +1,6 @@ +obj-$(CONFIG_BCM43XX_MAC80211) += bcm43xx/ +obj-$(CONFIG_RT2X00) += rt2x00/ +obj-$(CONFIG_ADM8211) += adm8211/ +obj-$(CONFIG_P54_COMMON) += p54/ +obj-$(CONFIG_ZD1211RW_MAC80211) += zd1211rw/ +obj-$(CONFIG_RTL818X) += rtl818x/ diff --git a/drivers/net/wireless/mac80211/README b/drivers/net/wireless/mac80211/README new file mode 100644 index 0000000..da9551e --- /dev/null +++ b/drivers/net/wireless/mac80211/README @@ -0,0 +1,2 @@ +This directory contains IEEE 802.11 wireless LAN drivers that are using +Devicescape IEEE 802.11 stack (net/mac80211). diff --git a/drivers/net/wireless/mac80211/adm8211/Kconfig b/drivers/net/wireless/mac80211/adm8211/Kconfig new file mode 100644 index 0000000..c5b356b --- /dev/null +++ b/drivers/net/wireless/mac80211/adm8211/Kconfig @@ -0,0 +1,25 @@ +config ADM8211 + tristate "ADMtek ADM8211 support" + depends on PCI && WLAN_80211 && MAC80211 && EXPERIMENTAL + select CRC32 + ---help--- + This driver is for ADM8211A, ADM8211B, and ADM8211C based cards. + These are PCI/mini-PCI/Cardbus 802.11b chips found in cards such as: + + Xterasys Cardbus XN-2411b + Blitz NetWave Point PC + TrendNet 221pc + Belkin F5D6001 + SMC 2635W + Linksys WPC11 v1 + Fiberline FL-WL-200X + 3com Office Connect (3CRSHPW796) + Corega WLPCIB-11 + SMC 2602W V2 EU + D-Link DWL-520 Revision C + + However, some of these cards have been replaced with other chips + like the RTL8180L (Xterasys Cardbus XN-2411b, Belkin F5D6001) or + the Ralink RT2400 (SMC2635W) without a model number change. + + Thanks to Infineon-ADMtek for their support of this driver. diff --git a/drivers/net/wireless/mac80211/adm8211/Makefile b/drivers/net/wireless/mac80211/adm8211/Makefile new file mode 100644 index 0000000..9cca7e5 --- /dev/null +++ b/drivers/net/wireless/mac80211/adm8211/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_ADM8211) += adm8211.o diff --git a/drivers/net/wireless/mac80211/adm8211/adm8211.c b/drivers/net/wireless/mac80211/adm8211/adm8211.c new file mode 100644 index 0000000..f0b8a2d --- /dev/null +++ b/drivers/net/wireless/mac80211/adm8211/adm8211.c @@ -0,0 +1,2172 @@ + +/* + * Linux device driver for ADMtek ADM8211 (IEEE 802.11b MAC/BBP) + * + * Copyright (c) 2003, Jouni Malinen + * Copyright (c) 2004-2006, Michael Wu + * Some parts copyright (c) 2003 by David Young + * and used with permission. + * + * Much thanks to Infineon-ADMtek for their support of this driver. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See README and COPYING for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "adm8211.h" + +MODULE_AUTHOR("Michael Wu , Jouni Malinen "); +MODULE_DESCRIPTION("Driver for IEEE 802.11b wireless LAN cards based on ADMtek ADM8211"); +MODULE_SUPPORTED_DEVICE("ADM8211"); +MODULE_LICENSE("GPL"); + +static unsigned int tx_ring_size __read_mostly = 16; +static unsigned int rx_ring_size __read_mostly = 16; +static int debug __read_mostly = 1; + +module_param(tx_ring_size, uint, 0); +module_param(rx_ring_size, uint, 0); +module_param(debug, int, 0); + +static const char version[] = KERN_INFO "adm8211: " +"Copyright 2003, Jouni Malinen ; " +"Copyright 2004-2006, Michael Wu \n"; + + +static struct pci_device_id adm8211_pci_id_table[] __devinitdata = { + /* ADMtek ADM8211 */ + { PCI_DEVICE(0x10B7, 0x6000) }, /* 3Com 3CRSHPW796 */ + { PCI_DEVICE(0x1200, 0x8201) }, /* ? */ + { PCI_DEVICE(0x1317, 0x8201) }, /* ADM8211A */ + { PCI_DEVICE(0x1317, 0x8211) }, /* ADM8211B/C */ + { 0 } +}; + +#define ADM8211_INTMASK \ +(ADM8211_IER_NIE | ADM8211_IER_AIE | ADM8211_IER_RCIE | ADM8211_IER_TCIE | \ +ADM8211_IER_TDUIE | ADM8211_IER_GPTIE) + +#define PLCP_SIGNAL_1M 0x0a +#define PLCP_SIGNAL_2M 0x14 +#define PLCP_SIGNAL_5M5 0x37 +#define PLCP_SIGNAL_11M 0x6e + +#define ADM8211_RX_MAX_SSI 100 + +struct adm8211_tx_hdr { + u8 da[6]; + u8 signal; /* PLCP signal / TX rate in 100 Kbps */ + u8 service; + __le16 frame_body_size; + __le16 frame_control; + __le16 plcp_frag_tail_len; + __le16 plcp_frag_head_len; + __le16 dur_frag_tail; + __le16 dur_frag_head; + u8 addr4[6]; + +#define ADM8211_TXHDRCTL_SHORT_PREAMBLE (1 << 0) +#define ADM8211_TXHDRCTL_MORE_FRAG (1 << 1) +#define ADM8211_TXHDRCTL_MORE_DATA (1 << 2) +#define ADM8211_TXHDRCTL_FRAG_NO (1 << 3) /* ? */ +#define ADM8211_TXHDRCTL_ENABLE_RTS (1 << 4) +#define ADM8211_TXHDRCTL_ENABLE_WEP_ENGINE (1 << 5) +#define ADM8211_TXHDRCTL_ENABLE_EXTEND_HEADER (1 << 15) /* ? */ + __le16 header_control; + __le16 frag; + u8 reserved_0; + u8 retry_limit; + + u32 wep2key0; + u32 wep2key1; + u32 wep2key2; + u32 wep2key3; + + u8 keyid; + u8 entry_control; // huh?? + u16 reserved_1; + u32 reserved_2; +} __attribute__ ((packed)); + + +#define RX_COPY_BREAK 128 +#define RX_PKT_SIZE 2500 + +/* Serial EEPROM reading for 93C66/93C46 */ +#define EE_ENB (0x4000 | ADM8211_SPR_SRS | ADM8211_SPR_SCS) +#define EE_READ_CMD (6) +#define eeprom_delay() ADM8211_CSR_READ(SPR); + + +static u16 adm8211_eeprom_read_word(struct ieee80211_hw *dev, unsigned int addr, + unsigned int addr_len) +{ + struct adm8211_priv *priv = dev->priv; + unsigned int read_cmd = addr | (EE_READ_CMD << addr_len); + int i; + u16 retval = 0; + + ADM8211_CSR_WRITE(SPR, __constant_cpu_to_le32(EE_ENB & ~ADM8211_SPR_SCS)); + eeprom_delay(); + ADM8211_CSR_WRITE(SPR, __constant_cpu_to_le32(EE_ENB)); + eeprom_delay(); + + /* Shift the read command bits out. */ + for (i = 4 + addr_len; i >= 0; i--) { + u32 dataval = EE_ENB | ((read_cmd & (1 << i)) ? ADM8211_SPR_SDI : 0); + ADM8211_CSR_WRITE(SPR, cpu_to_le32(dataval)); + eeprom_delay(); + ADM8211_CSR_WRITE(SPR, cpu_to_le32(dataval | ADM8211_SPR_SCLK)); + eeprom_delay(); + } + + ADM8211_CSR_WRITE(SPR, __constant_cpu_to_le32(EE_ENB)); + eeprom_delay(); + + for (i = 16; i > 0; i--) { + ADM8211_CSR_WRITE(SPR, __constant_cpu_to_le32(EE_ENB | ADM8211_SPR_SCLK)); + eeprom_delay(); + retval <<= 1; + if (ADM8211_CSR_READ(SPR) & __constant_cpu_to_le32(ADM8211_SPR_SDO)) + retval |= 1; + ADM8211_CSR_WRITE(SPR, __constant_cpu_to_le32(EE_ENB)); + eeprom_delay(); + } + + /* Terminate the EEPROM access. */ + ADM8211_CSR_WRITE(SPR, __constant_cpu_to_le32(EE_ENB & ~ADM8211_SPR_SCS)); + + return retval; +} + + +static int adm8211_read_eeprom(struct ieee80211_hw *dev) +{ + struct adm8211_priv *priv = dev->priv; + unsigned int addr_len, words, i; + struct ieee80211_chan_range chan_range; + u16 cr49; + + if (ADM8211_CSR_READ(CSR_TEST0) & __constant_cpu_to_le32 (ADM8211_CSR_TEST0_EPTYP)) { + printk(KERN_DEBUG "%s (adm8211): EEPROM type: 93C66\n", pci_name(priv->pdev)); + /* 256 * 16-bit = 512 bytes */ + addr_len = 8; + words = 256; + } else { + printk(KERN_DEBUG "%s (adm8211): EEPROM type 93C46\n", pci_name(priv->pdev)); + /* 64 * 16-bit = 128 bytes */ + addr_len = 6; + words = 64; + } + + priv->eeprom_len = words * 2; + priv->eeprom = kmalloc(priv->eeprom_len, GFP_KERNEL); + if (priv->eeprom == NULL) + return -ENOMEM; + + for (i = 0; i < words; i++) + *((u16 *) &((u8 *)priv->eeprom)[i * 2]) = + adm8211_eeprom_read_word(dev, i, addr_len); + + cr49 = le16_to_cpu(priv->eeprom->cr49); + priv->rf_type = (cr49 >> 3) & 0x7; + switch (priv->rf_type) { + case ADM8211_TYPE_INTERSIL: + case ADM8211_TYPE_RFMD: + case ADM8211_TYPE_MARVEL: + case ADM8211_TYPE_AIROHA: + case ADM8211_TYPE_ADMTEK: + break; + + default: + if (priv->revid < ADM8211_REV_CA) + priv->rf_type = ADM8211_TYPE_RFMD; + else + priv->rf_type = ADM8211_TYPE_AIROHA; + + printk(KERN_WARNING "%s (adm8211): Invalid or unsupported RFtype: %d, assuming %d\n", + pci_name(priv->pdev), (cr49 >> 3) & 0x7, priv->rf_type); + } + + priv->bbp_type = cr49 & 0x7; + switch (priv->bbp_type) { + case ADM8211_TYPE_INTERSIL: + case ADM8211_TYPE_RFMD: + case ADM8211_TYPE_MARVEL: + case ADM8211_TYPE_AIROHA: + case ADM8211_TYPE_ADMTEK: + break; + + default: + if (priv->revid < ADM8211_REV_CA) + priv->bbp_type = ADM8211_TYPE_RFMD; + else + priv->bbp_type = ADM8211_TYPE_ADMTEK; + + printk(KERN_WARNING "%s (adm8211): Invalid or unsupported BBPtype: %d, assuming %d\n", + pci_name(priv->pdev), cr49 >> 3, priv->bbp_type); + } + + if (priv->eeprom->country_code >= ARRAY_SIZE(cranges)) { + printk(KERN_WARNING "%s (adm8211): Invalid country code (%d) in EEPROM, assuming ETSI\n", + pci_name(priv->pdev), priv->eeprom->country_code); + + chan_range = cranges[2]; + } else + chan_range = cranges[priv->eeprom->country_code]; + + printk(KERN_DEBUG "%s (adm8211): Channel range: %d - %d\n", + pci_name(priv->pdev), (int)chan_range.min, (int)chan_range.max); + + priv->modes[0].num_channels = chan_range.max - chan_range.min + 1; + priv->modes[0].channels = kmalloc(priv->modes[0].num_channels * sizeof(struct ieee80211_channel), GFP_KERNEL); + if (priv->modes[0].channels == NULL) { + kfree(priv->eeprom); + return -ENOMEM; + } + + memcpy(priv->modes[0].channels, &adm8211_channels[chan_range.min-1], + priv->modes[0].num_channels * sizeof(struct ieee80211_channel)); + + switch (priv->eeprom->specific_bbptype) { + case ADM8211_BBP_RFMD3000: + case ADM8211_BBP_RFMD3002: + case ADM8211_BBP_ADM8011: + priv->specific_bbptype = priv->eeprom->specific_bbptype; + break; + + default: + if (priv->revid < ADM8211_REV_CA) + priv->specific_bbptype = ADM8211_BBP_RFMD3000; + else + priv->specific_bbptype = ADM8211_BBP_ADM8011; + + printk(KERN_WARNING "%s (adm8211): Invalid or unsupported specific BBP: %d, assuming %d\n", + pci_name(priv->pdev), priv->eeprom->specific_bbptype, priv->specific_bbptype); + } + + switch (priv->eeprom->specific_rftype) { + case ADM8211_RFMD2948: + case ADM8211_RFMD2958: + case ADM8211_RFMD2958_RF3000_CONTROL_POWER: + case ADM8211_MAX2820: + case ADM8211_AL2210L: + priv->transceiver_type = priv->eeprom->specific_rftype; + break; + + default: + if (priv->revid == ADM8211_REV_BA) + priv->transceiver_type = ADM8211_RFMD2958_RF3000_CONTROL_POWER; + else if (priv->revid == ADM8211_REV_CA) + priv->transceiver_type = ADM8211_AL2210L; + else if (priv->revid == ADM8211_REV_AB) + priv->transceiver_type = ADM8211_RFMD2948; + + printk(KERN_WARNING "%s (adm8211): Invalid or unsupported transceiver: %d, assuming %d\n", + pci_name(priv->pdev), priv->eeprom->specific_rftype, priv->transceiver_type); + + break; + } + + printk(KERN_DEBUG "%s (adm8211): RFtype=%d BBPtype=%d Specific BBP=%d Transceiver=%d\n", + pci_name(priv->pdev), priv->rf_type, priv->bbp_type, + priv->specific_bbptype, priv->transceiver_type); + + return 0; +} + +static inline void adm8211_write_sram(struct ieee80211_hw *dev, u32 addr, __le32 data) +{ + struct adm8211_priv *priv = dev->priv; + + ADM8211_CSR_WRITE(WEPCTL, cpu_to_le32(addr | ADM8211_WEPCTL_TABLE_WR | + (priv->revid < ADM8211_REV_BA ? + 0 : ADM8211_WEPCTL_SEL_WEPTABLE )) ); + ADM8211_CSR_READ(WEPCTL); + mdelay(1); + + ADM8211_CSR_WRITE(WESK, data); + ADM8211_CSR_READ(WESK); + mdelay(1); +} + +static void adm8211_write_sram_bytes(struct ieee80211_hw *dev, + unsigned int addr, u8 *buf, unsigned int len) +{ + struct adm8211_priv *priv = dev->priv; + __le32 reg = ADM8211_CSR_READ(WEPCTL); + unsigned int i; + + if (priv->revid < ADM8211_REV_BA) { + for (i = 0; i < len; i += 2) { + u16 val = buf[i] | buf[i + 1] << 8; + adm8211_write_sram(dev, addr + i / 2, cpu_to_le32(val)); + } + } else { + for (i = 0; i < len; i += 4) { + u32 val = (buf[i + 0] << 0 ) | (buf[i + 1] << 8 ) | + (buf[i + 2] << 16) | (buf[i + 3] << 24); + adm8211_write_sram(dev, addr + i / 4, cpu_to_le32(val)); + } + } + + ADM8211_CSR_WRITE(WEPCTL, reg); +} + +static void adm8211_clear_sram(struct ieee80211_hw *dev) +{ + struct adm8211_priv *priv = dev->priv; + __le32 reg = ADM8211_CSR_READ(WEPCTL); + unsigned int addr; + + for (addr = 0; addr < ADM8211_SRAM_SIZE; addr++) + adm8211_write_sram(dev, addr, 0); + + ADM8211_CSR_WRITE(WEPCTL, reg); +} + +static int adm8211_get_stats(struct ieee80211_hw *dev, + struct ieee80211_low_level_stats *stats) +{ + struct adm8211_priv *priv = dev->priv; + + memcpy(stats, &priv->stats, sizeof(*stats)); + + return 0; +} + +static void adm8211_set_rx_mode(struct ieee80211_hw *dev, + unsigned short flags, int mc_count) +{ + struct adm8211_priv *priv = dev->priv; + unsigned int bit_nr; + __le32 mc_filter[2]; + struct dev_mc_list *mclist; + void *tmp; + + mc_filter[1] = mc_filter[0] = 0; + if (flags & IFF_PROMISC) { + priv->nar |= ADM8211_NAR_PR; + priv->nar &= ~ADM8211_NAR_MM; + mc_filter[1] = mc_filter[0] = __constant_cpu_to_le32(~0); + } else if ((flags & IFF_ALLMULTI) || (mc_count > -1)) { + priv->nar &= ~ADM8211_NAR_PR; + priv->nar |= ADM8211_NAR_MM; + mc_filter[1] = mc_filter[0] = __constant_cpu_to_le32(~0); + } else { + priv->nar &= ~(ADM8211_NAR_MM | ADM8211_NAR_PR); + mc_filter[1] = mc_filter[0] = 0; + mclist = NULL; + while ((mclist = ieee80211_get_mc_list_item(dev, mclist, &tmp))) { + bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26; + + bit_nr &= 0x3F; + mc_filter[bit_nr >> 5] |= cpu_to_le32(1 << (bit_nr & 31)); + } + } + + ADM8211_IDLE_RX(); + + ADM8211_CSR_WRITE(MAR0, mc_filter[0]); + ADM8211_CSR_WRITE(MAR1, mc_filter[1]); + ADM8211_CSR_READ(NAR); + + ADM8211_RESTORE(); +} + +static int adm8211_get_tx_stats(struct ieee80211_hw *dev, + struct ieee80211_tx_queue_stats *stats) +{ + struct adm8211_priv *priv = dev->priv; + struct ieee80211_tx_queue_stats_data *data = &stats->data[0]; + + data->len = priv->cur_tx - priv->dirty_tx; + data->limit = priv->tx_ring_size - 2; + data->count = priv->dirty_tx; + + return 0; +} + +static void adm8211_interrupt_tci(struct ieee80211_hw *dev) +{ + struct adm8211_priv *priv = dev->priv; + //struct net_device_stats *stats = ieee80211_dev_stats(dev); + unsigned dirty_tx; + + spin_lock(&priv->lock); + + for (dirty_tx = priv->dirty_tx; + priv->cur_tx - dirty_tx > 0; dirty_tx++) { + unsigned entry = dirty_tx % priv->tx_ring_size; + u32 status = le32_to_cpu(priv->tx_ring[entry].status); + + if (status & TDES0_CONTROL_OWN || + !(status & TDES0_CONTROL_DONE)) + break; + + if (status & TDES0_STATUS_ES) { + // stats->tx_errors++; + priv->tx_buffers[entry].tx_status.flags &= + ~IEEE80211_TX_STATUS_ACK; + + /* if (status & (TDES0_STATUS_TUF | TDES0_STATUS_TRO)) + stats->tx_fifo_errors++;*/ + } else + priv->tx_buffers[entry].tx_status.flags |= + IEEE80211_TX_STATUS_ACK; + + pci_unmap_single(priv->pdev, priv->tx_buffers[entry].mapping, + priv->tx_buffers[entry].skb->len, PCI_DMA_TODEVICE); + + if ((priv->tx_buffers[entry].tx_status.control.flags & + IEEE80211_TXCTL_REQ_TX_STATUS) || + !is_multicast_ether_addr(ieee80211_get_DA(&priv->tx_buffers[entry].hdr))) { + struct ieee80211_hdr *hdr; + size_t hdrlen = ieee80211_get_hdrlen(le16_to_cpu(priv->tx_buffers[entry].hdr.frame_control)); + hdr = (struct ieee80211_hdr *) skb_pull(priv->tx_buffers[entry].skb, + sizeof(struct adm8211_tx_hdr) - hdrlen); + memcpy(hdr, &priv->tx_buffers[entry].hdr, hdrlen); + ieee80211_tx_status_irqsafe(dev, priv->tx_buffers[entry].skb, + &priv->tx_buffers[entry].tx_status); + } else + dev_kfree_skb_irq(priv->tx_buffers[entry].skb); + priv->tx_buffers[entry].skb = NULL; + } + + if (priv->cur_tx - dirty_tx < priv->tx_ring_size - 2) + ieee80211_wake_queue(dev, 0); + + priv->dirty_tx = dirty_tx; + spin_unlock(&priv->lock); +} + + +static void adm8211_interrupt_rci(struct ieee80211_hw *dev) +{ + struct adm8211_priv *priv = dev->priv; + //struct net_device_stats *stats = ieee80211_dev_stats(dev); + unsigned int entry = priv->cur_rx % priv->rx_ring_size; + u32 status; + unsigned pktlen; + struct sk_buff *skb, *newskb; + unsigned int limit = priv->rx_ring_size; + static const u8 rate_tbl[] = {10, 20, 55, 110, 220}; + u8 rssi, rate; + + while (!(priv->rx_ring[entry].status & + __constant_cpu_to_le32(RDES0_STATUS_OWN))) { + if (limit-- == 0) + break; + + status = le32_to_cpu(priv->rx_ring[entry].status); + rate = (status & RDES0_STATUS_RXDR) >> 12; + rssi = le32_to_cpu(priv->rx_ring[entry].length) & + RDES1_STATUS_RSSI; + + pktlen = status & RDES0_STATUS_FL; + if (pktlen > RX_PKT_SIZE) { + if (net_ratelimit()) + printk(KERN_DEBUG "%s: too long frame (pktlen=%d)\n", + wiphy_name(dev->wiphy), pktlen); + pktlen = RX_PKT_SIZE; + } + + if (!priv->soft_rx_crc && status & RDES0_STATUS_ES) { + skb = NULL; /* old buffer will be reused */ + /*stats->rx_errors++; + if (status & (RDES0_STATUS_CRC16E | RDES0_STATUS_CRC32E)) + stats->rx_crc_errors++;*/ + + } else if (pktlen < RX_COPY_BREAK) { + skb = dev_alloc_skb(pktlen); + if (skb) { + pci_dma_sync_single_for_cpu( + priv->pdev, + priv->rx_buffers[entry].mapping, + pktlen, PCI_DMA_FROMDEVICE); + memcpy(skb_put(skb, pktlen), + priv->rx_buffers[entry].skb->tail, + pktlen); + pci_dma_sync_single_for_device( + priv->pdev, + priv->rx_buffers[entry].mapping, + RX_PKT_SIZE, PCI_DMA_FROMDEVICE); + } + } else { + newskb = dev_alloc_skb(RX_PKT_SIZE); + if (newskb) { + skb = priv->rx_buffers[entry].skb; + skb_put(skb, pktlen); + pci_unmap_single( + priv->pdev, + priv->rx_buffers[entry].mapping, + RX_PKT_SIZE, PCI_DMA_FROMDEVICE); + priv->rx_buffers[entry].skb = newskb; + priv->rx_buffers[entry].mapping = + pci_map_single(priv->pdev, + newskb->tail, + RX_PKT_SIZE, + PCI_DMA_FROMDEVICE); + } else { + skb = NULL; + //stats->rx_dropped++; + } + + priv->rx_ring[entry].buffer1 = + cpu_to_le32(priv->rx_buffers[entry].mapping); + } + + priv->rx_ring[entry].status = cpu_to_le32( RDES0_STATUS_OWN | RDES0_STATUS_SQL ); + priv->rx_ring[entry].length = + cpu_to_le32(RX_PKT_SIZE | + (entry == priv->rx_ring_size - 1 ? + RDES1_CONTROL_RER : 0)); + + if (skb) { + struct ieee80211_rx_status rx_status = {0}; + + if (priv->revid < ADM8211_REV_CA) + rx_status.ssi = rssi; + else + rx_status.ssi = 100 - rssi; + + if (rate <= 4) + rx_status.rate = rate_tbl[rate]; + + rx_status.channel = priv->channel; + rx_status.freq = adm8211_channels[priv->channel - 1].freq; + rx_status.phymode = MODE_IEEE80211B; + + /* remove FCS */ + /* TODO: remove this and set flag in ieee80211_hw instead? */ + if (dev->flags & IFF_PROMISC) + skb_trim(skb, skb->len - FCS_LEN); + + ieee80211_rx_irqsafe(dev, skb, &rx_status); + } + + entry = (++priv->cur_rx) % priv->rx_ring_size; + } + + //stats->rx_missed_errors += le32_to_cpu(ADM8211_CSR_READ(LPC)) & 0xFFFF; +} + + +static irqreturn_t adm8211_interrupt(int irq, void *dev_id) +{ +#define ADM8211_INT(x) if (unlikely(stsr & ADM8211_STSR_ ## x)) printk(KERN_DEBUG "%s: " #x "\n", wiphy_name(dev->wiphy)) + + struct ieee80211_hw *dev = dev_id; + struct adm8211_priv *priv = dev->priv; + unsigned int count = 0; + u32 stsr; + + do { + stsr = le32_to_cpu(ADM8211_CSR_READ(STSR)); + ADM8211_CSR_WRITE(STSR, cpu_to_le32(stsr)); + if (stsr == 0xffffffff) + return IRQ_HANDLED; + + if (!(stsr & (ADM8211_STSR_NISS | ADM8211_STSR_AISS))) + break; + + if (stsr & ADM8211_STSR_RCI) + adm8211_interrupt_rci(dev); + if (stsr & ADM8211_STSR_TCI) + adm8211_interrupt_tci(dev); + + if ((stsr & (ADM8211_STSR_LinkOn | ADM8211_STSR_LinkOff)) + != (ADM8211_STSR_LinkOn | ADM8211_STSR_LinkOff)) { + if (stsr & ADM8211_STSR_LinkOn) + printk(KERN_DEBUG "%s: LinkOn\n", + wiphy_name(dev->wiphy)); + + if (stsr & ADM8211_STSR_LinkOff) + printk(KERN_DEBUG "%s: LinkOff\n", + wiphy_name(dev->wiphy)); + } + + ADM8211_INT(PCF); + ADM8211_INT(BCNTC); + ADM8211_INT(GPINT); + ADM8211_INT(ATIMTC); + ADM8211_INT(TSFTF); + ADM8211_INT(TSCZ); + ADM8211_INT(SQL); + ADM8211_INT(WEPTD); + ADM8211_INT(ATIME); + /*ADM8211_INT(TBTT);*/ + ADM8211_INT(TEIS); + ADM8211_INT(FBE); + ADM8211_INT(REIS); + ADM8211_INT(GPTT); + ADM8211_INT(RPS); + ADM8211_INT(RDU); + ADM8211_INT(TUF); + /*ADM8211_INT(TRT);*/ + /*ADM8211_INT(TLT);*/ + /*ADM8211_INT(TDU);*/ + ADM8211_INT(TPS); + + } while (count++ < 20); + + return IRQ_RETVAL(count); + +#undef ADM8211_INT +} + +#define WRITE_SYN(valmask,valshift,addrmask,addrshift,bits,prewrite,postwrite) do {\ + struct adm8211_priv *priv = dev->priv;\ + unsigned int i;\ + u32 reg, bitbuf;\ + \ + value &= valmask;\ + addr &= addrmask;\ + bitbuf = (value << valshift) | (addr << addrshift);\ + \ + ADM8211_CSR_WRITE(SYNRF, __constant_cpu_to_le32(ADM8211_SYNRF_IF_SELECT_1)); \ + ADM8211_CSR_READ(SYNRF);\ + ADM8211_CSR_WRITE(SYNRF, __constant_cpu_to_le32(ADM8211_SYNRF_IF_SELECT_0)); \ + ADM8211_CSR_READ(SYNRF);\ + \ + if (prewrite) {\ + ADM8211_CSR_WRITE(SYNRF, __constant_cpu_to_le32 (ADM8211_SYNRF_WRITE_SYNDATA_0));\ + ADM8211_CSR_READ(SYNRF);\ + }\ + \ + for (i = 0; i <= bits; i++) {\ + if ( bitbuf & (1 << (bits - i)) )\ + reg = ADM8211_SYNRF_WRITE_SYNDATA_1;\ + else\ + reg = ADM8211_SYNRF_WRITE_SYNDATA_0;\ + \ + ADM8211_CSR_WRITE(SYNRF, cpu_to_le32(reg));\ + ADM8211_CSR_READ(SYNRF);\ + \ + ADM8211_CSR_WRITE(SYNRF, cpu_to_le32(reg | ADM8211_SYNRF_WRITE_CLOCK_1));\ + ADM8211_CSR_READ(SYNRF);\ + ADM8211_CSR_WRITE(SYNRF, cpu_to_le32(reg | ADM8211_SYNRF_WRITE_CLOCK_0));\ + ADM8211_CSR_READ(SYNRF);\ + }\ + \ + if (postwrite == 1) {\ + ADM8211_CSR_WRITE(SYNRF, cpu_to_le32(reg | ADM8211_SYNRF_IF_SELECT_0));\ + ADM8211_CSR_READ(SYNRF);\ + }\ + if (postwrite == 2) {\ + ADM8211_CSR_WRITE(SYNRF, cpu_to_le32(reg | ADM8211_SYNRF_IF_SELECT_1));\ + ADM8211_CSR_READ(SYNRF);\ + }\ + \ + ADM8211_CSR_WRITE(SYNRF, 0);\ + ADM8211_CSR_READ(SYNRF);\ +} while (0) + +static void adm8211_rf_write_syn_max2820 (struct ieee80211_hw *dev, u16 addr, u32 value) +{ + WRITE_SYN(0x00FFF, 0, 0x0F, 12, 15, 1, 1); +} + +static void adm8211_rf_write_syn_al2210l (struct ieee80211_hw *dev, u16 addr, u32 value) +{ + WRITE_SYN(0xFFFFF, 4, 0x0F, 0, 23, 1, 1); +} + +static void adm8211_rf_write_syn_rfmd2958 (struct ieee80211_hw *dev, u16 addr, u32 value) +{ + WRITE_SYN(0x3FFFF, 0, 0x1F, 18, 23, 0, 1); +} + +static void adm8211_rf_write_syn_rfmd2948 (struct ieee80211_hw *dev, u16 addr, u32 value) +{ + WRITE_SYN(0x0FFFF, 4, 0x0F, 0, 21, 0, 2); +} + +static int adm8211_write_bbp(struct ieee80211_hw *dev, u8 addr, u8 data) +{ + struct adm8211_priv *priv = dev->priv; + unsigned int timeout; + u32 reg; + + timeout = 10; + while (timeout > 0) { + reg = le32_to_cpu(ADM8211_CSR_READ(BBPCTL)); + if (!(reg & (ADM8211_BBPCTL_WR | ADM8211_BBPCTL_RD))) + break; + timeout--; + mdelay(2); + } + + if (timeout == 0) { + printk(KERN_DEBUG "%s: adm8211_write_bbp(%d,%d) failed" + " prewrite (reg=0x%08x)\n", + wiphy_name(dev->wiphy), addr, data, reg); + return -ETIMEDOUT; + } + + switch (priv->bbp_type) { + case ADM8211_TYPE_INTERSIL: + reg = ADM8211_BBPCTL_MMISEL; /* three wire interface */ + break; + case ADM8211_TYPE_RFMD: + reg = (0x20<<24) | ADM8211_BBPCTL_TXCE | ADM8211_BBPCTL_CCAP | + (0x01<<18); + break; + case ADM8211_TYPE_ADMTEK: + reg = (0x20<<24) | ADM8211_BBPCTL_TXCE | ADM8211_BBPCTL_CCAP | + (0x05<<18); + break; + } + reg |= ADM8211_BBPCTL_WR | (addr << 8) | data; + + ADM8211_CSR_WRITE(BBPCTL, cpu_to_le32(reg)); + + timeout = 10; + while (timeout > 0) { + reg = le32_to_cpu(ADM8211_CSR_READ(BBPCTL)); + if (!(reg & ADM8211_BBPCTL_WR)) + break; + timeout--; + mdelay(2); + } + + if (timeout == 0) { + ADM8211_CSR_WRITE(BBPCTL, ADM8211_CSR_READ(BBPCTL) & + __constant_cpu_to_le32(~ADM8211_BBPCTL_WR)); + printk(KERN_DEBUG "%s: adm8211_write_bbp(%d,%d) failed" + " postwrite (reg=0x%08x)\n", + wiphy_name(dev->wiphy), addr, data, reg); + return -ETIMEDOUT; + } + + return 0; +} + +static int adm8211_rf_set_channel(struct ieee80211_hw *dev, unsigned int channel) +{ + static const u32 adm8211_rfmd2958_reg5[] = + {0x22BD, 0x22D2, 0x22E8, 0x22FE, 0x2314, 0x232A, 0x2340, + 0x2355, 0x236B, 0x2381, 0x2397, 0x23AD, 0x23C2, 0x23F7}; + static const u32 adm8211_rfmd2958_reg6[] = + {0x05D17, 0x3A2E8, 0x2E8BA, 0x22E8B, 0x1745D, 0x0BA2E, 0x00000, + 0x345D1, 0x28BA2, 0x1D174, 0x11745, 0x05D17, 0x3A2E8, 0x11745}; + + struct adm8211_priv *priv = dev->priv; + u8 ant_power = priv->ant_power > 0x3F ? + priv->eeprom->antenna_power[channel-1] : priv->ant_power; + u8 tx_power = priv->tx_power > 0x3F ? + priv->eeprom->tx_power[channel-1] : priv->tx_power; + u8 lpf_cutoff = priv->lpf_cutoff == 0xFF ? + priv->eeprom->lpf_cutoff[channel-1] : priv->lpf_cutoff; + u8 lnags_thresh = priv->lnags_threshold == 0xFF ? + priv->eeprom->lnags_threshold[channel-1] : priv->lnags_threshold; + u32 reg; + + if (channel < 1 || channel > 14) + return -EINVAL; + + ADM8211_IDLE(); + + /* Program synthesizer to new channel */ + switch (priv->transceiver_type) { + case ADM8211_RFMD2958: + case ADM8211_RFMD2958_RF3000_CONTROL_POWER: + adm8211_rf_write_syn_rfmd2958(dev, 0x00, 0x04007); + adm8211_rf_write_syn_rfmd2958(dev, 0x02, 0x00033); + + adm8211_rf_write_syn_rfmd2958(dev, 0x05, + adm8211_rfmd2958_reg5[channel-1]); + adm8211_rf_write_syn_rfmd2958(dev, 0x06, + adm8211_rfmd2958_reg6[channel-1]); + break; + + case ADM8211_RFMD2948: + adm8211_rf_write_syn_rfmd2948(dev, SI4126_MAIN_CONF, SI4126_MAIN_XINDIV2); + adm8211_rf_write_syn_rfmd2948(dev, SI4126_POWERDOWN, + SI4126_POWERDOWN_PDIB | SI4126_POWERDOWN_PDRB); + adm8211_rf_write_syn_rfmd2948(dev, SI4126_PHASE_DET_GAIN, 0); + adm8211_rf_write_syn_rfmd2948(dev, SI4126_RF2_N_DIV, + (channel == 14 ? 2110 : (2033 + (channel * 5)))); + adm8211_rf_write_syn_rfmd2948(dev, SI4126_IF_N_DIV, 1496); + adm8211_rf_write_syn_rfmd2948(dev, SI4126_RF2_R_DIV, 44); + adm8211_rf_write_syn_rfmd2948(dev, SI4126_IF_R_DIV, 44); + break; + + case ADM8211_MAX2820: + adm8211_rf_write_syn_max2820(dev, 0x3, + (channel == 14 ? 0x054 : (0x7 + (channel * 5)))); + break; + + case ADM8211_AL2210L: + adm8211_rf_write_syn_al2210l(dev, 0x0, + (channel == 14 ? 0x229B4 : (0x22967 + (channel * 5)))); + break; + + default: + printk(KERN_DEBUG "%s: unsupported transceiver type %d\n", + wiphy_name(dev->wiphy), priv->transceiver_type); + break; + } + + /* write BBP regs */ + if (priv->bbp_type == ADM8211_TYPE_RFMD) { + + /* SMC 2635W specific? adm8211b doesn't use the 2948 though.. */ + /* TODO: remove if SMC 2635W doesn't need this */ + if (priv->transceiver_type == ADM8211_RFMD2948) { + reg = le32_to_cpu(ADM8211_CSR_READ(GPIO)); + reg &= 0xfffc0000; + reg |= ADM8211_CSR_GPIO_EN0; + if (channel != 14) + reg |= ADM8211_CSR_GPIO_O0; + ADM8211_CSR_WRITE(GPIO, cpu_to_le32(reg)); + } + + if (priv->transceiver_type == ADM8211_RFMD2958) { + /* set PCNT2 */ + adm8211_rf_write_syn_rfmd2958(dev, 0x0B, 0x07100); + /* set PCNT1 P_DESIRED/MID_BIAS */ + reg = le16_to_cpu(priv->eeprom->cr49); + reg >>= 13; + reg <<= 15; + reg |= ant_power<<9; + adm8211_rf_write_syn_rfmd2958(dev, 0x0A, reg); + /* set TXRX TX_GAIN */ + adm8211_rf_write_syn_rfmd2958(dev, 0x09, 0x00050 | + (priv->revid < ADM8211_REV_CA ? tx_power : 0)); + } else { + reg = le32_to_cpu(ADM8211_CSR_READ(PLCPHD)); + reg &= 0xff00ffff; + reg |= tx_power<<18; + ADM8211_CSR_WRITE(PLCPHD, cpu_to_le32(reg)); + } + + ADM8211_CSR_WRITE(SYNRF, __constant_cpu_to_le32(ADM8211_SYNRF_SELRF | + ADM8211_SYNRF_PE1 | ADM8211_SYNRF_PHYRST)); + ADM8211_CSR_READ(SYNRF); + mdelay(30); + + /* RF3000 BBP */ + if (priv->transceiver_type != ADM8211_RFMD2958) + adm8211_write_bbp(dev, RF3000_TX_VAR_GAIN__TX_LEN_EXT, + tx_power<<2); + adm8211_write_bbp(dev, RF3000_LOW_GAIN_CALIB, lpf_cutoff); + adm8211_write_bbp(dev, RF3000_HIGH_GAIN_CALIB, lnags_thresh); + adm8211_write_bbp(dev, 0x1c, priv->revid == ADM8211_REV_BA + ? priv->eeprom->cr28 : 0); + adm8211_write_bbp(dev, 0x1d, priv->eeprom->cr29); + + ADM8211_CSR_WRITE(SYNRF, 0); + + } else if (priv->bbp_type != ADM8211_TYPE_ADMTEK) { /* Nothing to do for ADMtek BBP */ + printk(KERN_DEBUG "%s: unsupported BBP type %d\n", + wiphy_name(dev->wiphy), priv->bbp_type); + } + + ADM8211_RESTORE(); + + /* update current channel for adhoc (and maybe AP mode) */ + reg = le32_to_cpu(ADM8211_CSR_READ(CAP0)); + reg &= ~0xF; + reg |= channel; + ADM8211_CSR_WRITE(CAP0, cpu_to_le32(reg)); + + return 0; +} + +static void adm8211_update_mode(struct ieee80211_hw *dev) +{ + struct adm8211_priv *priv = dev->priv; + + ADM8211_IDLE(); + + priv->soft_rx_crc = 0; + switch (priv->mode) { + case IEEE80211_IF_TYPE_STA: + priv->nar &= ~(ADM8211_NAR_PR | ADM8211_NAR_EA); + priv->nar |= ADM8211_NAR_ST | ADM8211_NAR_SR; + break; + case IEEE80211_IF_TYPE_IBSS: + priv->nar &= ~ADM8211_NAR_PR; + priv->nar |= ADM8211_NAR_EA | ADM8211_NAR_ST | ADM8211_NAR_SR; + + /* don't trust the error bits on rev 0x20 and up in adhoc */ + if (priv->revid >= ADM8211_REV_BA) + priv->soft_rx_crc = 1; + break; + case IEEE80211_IF_TYPE_MNTR: + priv->nar &= ~(ADM8211_NAR_EA | ADM8211_NAR_ST); + priv->nar |= ADM8211_NAR_PR | ADM8211_NAR_SR; + break; + } + + ADM8211_RESTORE(); +} + +static void adm8211_hw_init_syn(struct ieee80211_hw *dev) +{ + struct adm8211_priv *priv = dev->priv; + + switch (priv->transceiver_type) { + case ADM8211_RFMD2958: + case ADM8211_RFMD2958_RF3000_CONTROL_POWER: + /* comments taken from ADMtek driver */ + + /* Reset RF2958 after power on */ + adm8211_rf_write_syn_rfmd2958(dev, 0x1F, 0x00000); + /* Initialize RF VCO Core Bias to maximum */ + adm8211_rf_write_syn_rfmd2958(dev, 0x0C, 0x3001F); + /* Initialize IF PLL */ + adm8211_rf_write_syn_rfmd2958(dev, 0x01, 0x29C03); + /* Initialize IF PLL Coarse Tuning */ + adm8211_rf_write_syn_rfmd2958(dev, 0x03, 0x1FF6F); + /* Initialize RF PLL */ + adm8211_rf_write_syn_rfmd2958(dev, 0x04, 0x29403); + /* Initialize RF PLL Coarse Tuning */ + adm8211_rf_write_syn_rfmd2958(dev, 0x07, 0x1456F); + /* Initialize TX gain and filter BW (R9) */ + adm8211_rf_write_syn_rfmd2958(dev, 0x09, + (priv->transceiver_type == ADM8211_RFMD2958 + ? 0x10050 : 0x00050) ); + /* Initialize CAL register */ + adm8211_rf_write_syn_rfmd2958(dev, 0x08, 0x3FFF8); + break; + + case ADM8211_MAX2820: + adm8211_rf_write_syn_max2820(dev, 0x1, 0x01E); + adm8211_rf_write_syn_max2820(dev, 0x2, 0x001); + adm8211_rf_write_syn_max2820(dev, 0x3, 0x054); + adm8211_rf_write_syn_max2820(dev, 0x4, 0x310); + adm8211_rf_write_syn_max2820(dev, 0x5, 0x000); + break; + + case ADM8211_AL2210L: + adm8211_rf_write_syn_al2210l(dev, 0x0, 0x0196C); + adm8211_rf_write_syn_al2210l(dev, 0x1, 0x007CB); + adm8211_rf_write_syn_al2210l(dev, 0x2, 0x3582F); + adm8211_rf_write_syn_al2210l(dev, 0x3, 0x010A9); + adm8211_rf_write_syn_al2210l(dev, 0x4, 0x77280); + adm8211_rf_write_syn_al2210l(dev, 0x5, 0x45641); + adm8211_rf_write_syn_al2210l(dev, 0x6, 0xEA130); + adm8211_rf_write_syn_al2210l(dev, 0x7, 0x80000); + adm8211_rf_write_syn_al2210l(dev, 0x8, 0x7850F); + adm8211_rf_write_syn_al2210l(dev, 0x9, 0xF900C); + adm8211_rf_write_syn_al2210l(dev, 0xA, 0x00000); + adm8211_rf_write_syn_al2210l(dev, 0xB, 0x00000); + break; + + case ADM8211_RFMD2948: + default: + break; + } +} + +static int adm8211_hw_init_bbp(struct ieee80211_hw *dev) +{ + struct adm8211_priv *priv = dev->priv; + u32 reg; + + /* write addresses */ + if (priv->bbp_type == ADM8211_TYPE_INTERSIL) { + ADM8211_CSR_WRITE(MMIWA, __constant_cpu_to_le32(0x100E0C0A)); + ADM8211_CSR_WRITE(MMIRD0, __constant_cpu_to_le32(0x00007c7e)); + ADM8211_CSR_WRITE(MMIRD1, __constant_cpu_to_le32(0x00100000)); + } else if (priv->bbp_type == ADM8211_TYPE_RFMD || + priv->bbp_type == ADM8211_TYPE_ADMTEK) { + + /* check specific BBP type */ + switch (priv->specific_bbptype) { + case ADM8211_BBP_RFMD3000: + case ADM8211_BBP_RFMD3002: + ADM8211_CSR_WRITE(MMIWA, __constant_cpu_to_le32(0x00009101)); + ADM8211_CSR_WRITE(MMIRD0, __constant_cpu_to_le32(0x00000301)); + break; + + case ADM8211_BBP_ADM8011: + ADM8211_CSR_WRITE(MMIWA, __constant_cpu_to_le32(0x00008903)); + ADM8211_CSR_WRITE(MMIRD0, __constant_cpu_to_le32(0x00001716)); + + reg = le32_to_cpu(ADM8211_CSR_READ(BBPCTL)); + reg &= ~ADM8211_BBPCTL_TYPE; + reg |= 0x5 << 18; + ADM8211_CSR_WRITE(BBPCTL, cpu_to_le32(reg)); + break; + } + + switch (priv->revid) { + case ADM8211_REV_CA: + if (priv->transceiver_type == ADM8211_RFMD2958 || + priv->transceiver_type == ADM8211_RFMD2958_RF3000_CONTROL_POWER || + priv->transceiver_type == ADM8211_RFMD2948) + ADM8211_CSR_WRITE(SYNCTL, __constant_cpu_to_le32(0x1 << 22)); + else if (priv->transceiver_type == ADM8211_MAX2820 || + priv->transceiver_type == ADM8211_AL2210L) + ADM8211_CSR_WRITE(SYNCTL, __constant_cpu_to_le32(0x3 << 22)); + break; + + case ADM8211_REV_BA: + reg = le32_to_cpu(ADM8211_CSR_READ(MMIRD1)); + reg &= 0x0000FFFF; + reg |= 0x7e100000; + ADM8211_CSR_WRITE(MMIRD1, cpu_to_le32(reg)); + break; + + case ADM8211_REV_AB: + case ADM8211_REV_AF: + default: + ADM8211_CSR_WRITE(MMIRD1, __constant_cpu_to_le32(0x7e100000)); + break; + } + + /* For RFMD */ + ADM8211_CSR_WRITE(MACTEST, __constant_cpu_to_le32(0x800)); + } + + adm8211_hw_init_syn(dev); + + /* Set RF Power control IF pin to PE1+PHYRST# */ + ADM8211_CSR_WRITE(SYNRF, __constant_cpu_to_le32(ADM8211_SYNRF_SELRF | + ADM8211_SYNRF_PE1 | ADM8211_SYNRF_PHYRST)); + ADM8211_CSR_READ(SYNRF); + mdelay(20); + + /* write BBP regs */ + if (priv->bbp_type == ADM8211_TYPE_RFMD) { + /* RF3000 BBP */ + /* another set: + * 11: c8 + * 14: 14 + * 15: 50 (chan 1..13; chan 14: d0) + * 1c: 00 + * 1d: 84 + */ + adm8211_write_bbp(dev, RF3000_CCA_CTRL, 0x80); + adm8211_write_bbp(dev, RF3000_DIVERSITY__RSSI, 0x80); /* antenna selection: diversity */ + adm8211_write_bbp(dev, RF3000_TX_VAR_GAIN__TX_LEN_EXT, 0x74); + adm8211_write_bbp(dev, RF3000_LOW_GAIN_CALIB, 0x38); + adm8211_write_bbp(dev, RF3000_HIGH_GAIN_CALIB, 0x40); + + if (priv->eeprom->major_version < 2) { + adm8211_write_bbp(dev, 0x1c, 0x00); + adm8211_write_bbp(dev, 0x1d, 0x80); + } else { + if (priv->revid == ADM8211_REV_BA) + adm8211_write_bbp(dev, 0x1c, priv->eeprom->cr28); + else + adm8211_write_bbp(dev, 0x1c, 0x00); + + adm8211_write_bbp(dev, 0x1d, priv->eeprom->cr29); + } + } else if (priv->bbp_type == ADM8211_TYPE_ADMTEK) { + adm8211_write_bbp(dev, 0x00, 0xFF); /* reset baseband */ + adm8211_write_bbp(dev, 0x07, 0x0A); /* antenna selection: diversity */ + + /* TODO: find documentation for this */ + switch (priv->transceiver_type) { + case ADM8211_RFMD2958: + case ADM8211_RFMD2958_RF3000_CONTROL_POWER: + adm8211_write_bbp(dev, 0x00, 0x00); + adm8211_write_bbp(dev, 0x01, 0x00); + adm8211_write_bbp(dev, 0x02, 0x00); + adm8211_write_bbp(dev, 0x03, 0x00); + adm8211_write_bbp(dev, 0x06, 0x0f); + adm8211_write_bbp(dev, 0x09, 0x00); + adm8211_write_bbp(dev, 0x0a, 0x00); + adm8211_write_bbp(dev, 0x0b, 0x00); + adm8211_write_bbp(dev, 0x0c, 0x00); + adm8211_write_bbp(dev, 0x0f, 0xAA); + adm8211_write_bbp(dev, 0x10, 0x8c); + adm8211_write_bbp(dev, 0x11, 0x43); + adm8211_write_bbp(dev, 0x18, 0x40); + adm8211_write_bbp(dev, 0x20, 0x23); + adm8211_write_bbp(dev, 0x21, 0x02); + adm8211_write_bbp(dev, 0x22, 0x28); + adm8211_write_bbp(dev, 0x23, 0x30); + adm8211_write_bbp(dev, 0x24, 0x2d); + adm8211_write_bbp(dev, 0x28, 0x35); + adm8211_write_bbp(dev, 0x2a, 0x8c); + adm8211_write_bbp(dev, 0x2b, 0x81); + adm8211_write_bbp(dev, 0x2c, 0x44); + adm8211_write_bbp(dev, 0x2d, 0x0A); + adm8211_write_bbp(dev, 0x29, 0x40); + adm8211_write_bbp(dev, 0x60, 0x08); + adm8211_write_bbp(dev, 0x64, 0x01); + break; + + case ADM8211_MAX2820: + adm8211_write_bbp(dev, 0x00, 0x00); + adm8211_write_bbp(dev, 0x01, 0x00); + adm8211_write_bbp(dev, 0x02, 0x00); + adm8211_write_bbp(dev, 0x03, 0x00); + adm8211_write_bbp(dev, 0x06, 0x0f); + adm8211_write_bbp(dev, 0x09, 0x05); + adm8211_write_bbp(dev, 0x0a, 0x02); + adm8211_write_bbp(dev, 0x0b, 0x00); + adm8211_write_bbp(dev, 0x0c, 0x0f); + adm8211_write_bbp(dev, 0x0f, 0x55); + adm8211_write_bbp(dev, 0x10, 0x8d); + adm8211_write_bbp(dev, 0x11, 0x43); + adm8211_write_bbp(dev, 0x18, 0x4a); + adm8211_write_bbp(dev, 0x20, 0x20); + adm8211_write_bbp(dev, 0x21, 0x02); + adm8211_write_bbp(dev, 0x22, 0x23); + adm8211_write_bbp(dev, 0x23, 0x30); + adm8211_write_bbp(dev, 0x24, 0x2d); + adm8211_write_bbp(dev, 0x2a, 0x8c); + adm8211_write_bbp(dev, 0x2b, 0x81); + adm8211_write_bbp(dev, 0x2c, 0x44); + adm8211_write_bbp(dev, 0x29, 0x4a); + adm8211_write_bbp(dev, 0x60, 0x2b); + adm8211_write_bbp(dev, 0x64, 0x01); + break; + + case ADM8211_AL2210L: + adm8211_write_bbp(dev, 0x00, 0x00); + adm8211_write_bbp(dev, 0x01, 0x00); + adm8211_write_bbp(dev, 0x02, 0x00); + adm8211_write_bbp(dev, 0x03, 0x00); + adm8211_write_bbp(dev, 0x06, 0x0f); + adm8211_write_bbp(dev, 0x07, 0x05); + adm8211_write_bbp(dev, 0x08, 0x03); + adm8211_write_bbp(dev, 0x09, 0x00); + adm8211_write_bbp(dev, 0x0a, 0x00); + adm8211_write_bbp(dev, 0x0b, 0x00); + adm8211_write_bbp(dev, 0x0c, 0x10); + adm8211_write_bbp(dev, 0x0f, 0x55); + adm8211_write_bbp(dev, 0x10, 0x8d); + adm8211_write_bbp(dev, 0x11, 0x43); + adm8211_write_bbp(dev, 0x18, 0x4a); + adm8211_write_bbp(dev, 0x20, 0x20); + adm8211_write_bbp(dev, 0x21, 0x02); + adm8211_write_bbp(dev, 0x22, 0x23); + adm8211_write_bbp(dev, 0x23, 0x30); + adm8211_write_bbp(dev, 0x24, 0x2d); + adm8211_write_bbp(dev, 0x2a, 0xaa); + adm8211_write_bbp(dev, 0x2b, 0x81); + adm8211_write_bbp(dev, 0x2c, 0x44); + adm8211_write_bbp(dev, 0x29, 0xfa); + adm8211_write_bbp(dev, 0x60, 0x2d); + adm8211_write_bbp(dev, 0x64, 0x01); + break; + + case ADM8211_RFMD2948: + break; + + default: + printk(KERN_DEBUG "%s: unsupported transceiver type %d\n", + wiphy_name(dev->wiphy), priv->transceiver_type); + break; + } + } else { + printk(KERN_DEBUG "%s: unsupported BBP type %d\n", + wiphy_name(dev->wiphy), priv->bbp_type); + } + + ADM8211_CSR_WRITE(SYNRF, 0); + + /* Set RF CAL control source to MAC control */ + reg = le32_to_cpu(ADM8211_CSR_READ(SYNCTL)); + reg |= ADM8211_SYNCTL_SELCAL; + ADM8211_CSR_WRITE(SYNCTL, cpu_to_le32(reg)); + + return 0; +} + +// configures hw beacons/probe responses +static int adm8211_set_rate(struct ieee80211_hw *dev) +{ + struct adm8211_priv *priv = dev->priv; + u32 reg; + int i = 0; + u8 rate_buf[12] = {0}; + + /* write supported rates */ + if (priv->revid != ADM8211_REV_BA) { + rate_buf[0] = ARRAY_SIZE(adm8211_rates); + for (i = 0; i < ARRAY_SIZE(adm8211_rates); i++) + rate_buf[i+1] = (adm8211_rates[i].rate/5) | 0x80; + } else { + /* workaround for rev BA specific bug */ + rate_buf[0]=4; + rate_buf[1]=0x82; + rate_buf[2]=0x04; + rate_buf[3]=0x0b; + rate_buf[4]=0x16; + } + + adm8211_write_sram_bytes(dev, ADM8211_SRAM_SUPP_RATE, rate_buf, ARRAY_SIZE(adm8211_rates)+1); + + reg = le32_to_cpu(ADM8211_CSR_READ(PLCPHD)) & 0x00FFFFFF; /* keep bits 0-23 */ + reg |= (1 << 15); /* short preamble */ + reg |= 110 << 24; + ADM8211_CSR_WRITE(PLCPHD, cpu_to_le32(reg)); + + /* MTMLT = 512 TU (max TX MSDU lifetime) + * BCNTSIG = plcp_signal (beacon, probe resp, and atim TX rate) + * SRTYLIM = 224 (short retry limit, value in TX header used by default) */ + ADM8211_CSR_WRITE(TXLMT, cpu_to_le32((512<<16) | (110<<8) | (224<<0))); + + return 0; +} + +static void adm8211_hw_init(struct ieee80211_hw *dev) +{ + struct adm8211_priv *priv = dev->priv; + u32 reg; + u8 cacheline; + + reg = le32_to_cpu(ADM8211_CSR_READ(PAR)); + reg |= ADM8211_PAR_MRLE | ADM8211_PAR_MRME; + reg &= ~(ADM8211_PAR_BAR | ADM8211_PAR_CAL); + + if (!pci_set_mwi(priv->pdev)) { + reg |= (0x1<<24); + pci_read_config_byte(priv->pdev, PCI_CACHE_LINE_SIZE, &cacheline); + + switch (cacheline) { + case 0x8: reg |= (0x1<<14); + break; + case 0x16: reg |= (0x2<<14); + break; + case 0x32: reg |= (0x3<<14); + break; + default: reg |= (0x0<<14); + break; + } + } + + ADM8211_CSR_WRITE(PAR, cpu_to_le32(reg)); + + reg = le32_to_cpu(ADM8211_CSR_READ(CSR_TEST1)); + reg &= ~(0xF<<28); + reg |= ((1 << 28) | (1 << 31)); + ADM8211_CSR_WRITE(CSR_TEST1, cpu_to_le32(reg)); + + /* lose link after 4 lost beacons */ + reg = (0x04 << 21) | ADM8211_WCSR_TSFTWE | ADM8211_WCSR_LSOE; + ADM8211_CSR_WRITE(WCSR, cpu_to_le32(reg)); + + /* Disable APM, enable receive FIFO threshold, and set drain receive + * threshold to store-and-forward */ + reg = le32_to_cpu(ADM8211_CSR_READ(CMDR)); + reg &= ~(ADM8211_CMDR_APM | ADM8211_CMDR_DRT); + reg |= ADM8211_CMDR_RTE | ADM8211_CMDR_DRT_SF; + ADM8211_CSR_WRITE(CMDR, cpu_to_le32(reg)); + + adm8211_set_rate(dev); + + /* 4-bit values: + * PWR1UP = 8 * 2 ms + * PWR0PAPE = 8 us or 5 us + * PWR1PAPE = 1 us or 3 us + * PWR0TRSW = 5 us + * PWR1TRSW = 12 us + * PWR0PE2 = 13 us + * PWR1PE2 = 1 us + * PWR0TXPE = 8 or 6 */ + if (priv->revid < ADM8211_REV_CA) + ADM8211_CSR_WRITE(TOFS2, __constant_cpu_to_le32(0x8815cd18)); + else + ADM8211_CSR_WRITE(TOFS2, __constant_cpu_to_le32(0x8535cd16)); + + /* Enable store and forward for transmit */ + priv->nar = ADM8211_NAR_SF | ADM8211_NAR_PB; + ADM8211_CSR_WRITE(NAR, cpu_to_le32(priv->nar)); + + /* Reset RF */ + ADM8211_CSR_WRITE(SYNRF, __constant_cpu_to_le32(ADM8211_SYNRF_RADIO)); + ADM8211_CSR_READ(SYNRF); + mdelay(10); + ADM8211_CSR_WRITE(SYNRF, 0); + ADM8211_CSR_READ(SYNRF); + mdelay(5); + + /* Set CFP Max Duration to 0x10 TU */ + reg = le32_to_cpu(ADM8211_CSR_READ(CFPP)); + reg &= ~(0xffff<<8); + reg |= 0x0010<<8; + ADM8211_CSR_WRITE(CFPP, cpu_to_le32(reg)); + + /* USCNT = 0x16 (number of system clocks, 22 MHz, in 1us + * TUCNT = 0x3ff - Tu counter 1024 us */ + ADM8211_CSR_WRITE(TOFS0, __constant_cpu_to_le32((0x16 << 24) | 0x3ff)); + + /* SLOT=20 us, SIFS=110 cycles of 22 MHz (5 us), + * DIFS=50 us, EIFS=100 us */ + if (priv->revid < ADM8211_REV_CA) + ADM8211_CSR_WRITE(IFST, __constant_cpu_to_le32( + (20 << 23) | (110 << 15) | + (50 << 9) | 100)); + else + ADM8211_CSR_WRITE(IFST, __constant_cpu_to_le32( + (20 << 23) | (24 << 15) | + (50 << 9) | 100)); + + /* PCNT = 1 (MAC idle time awake/sleep, unit S) + * RMRD = 2346 * 8 + 1 us (max RX duration) */ + ADM8211_CSR_WRITE(RMD, __constant_cpu_to_le32((1 << 16) | 18769)); + + /* MART=65535 us, MIRT=256 us, TSFTOFST=0 us */ + ADM8211_CSR_WRITE(RSPT, __constant_cpu_to_le32(0xffffff00)); + + /* Initialize BBP (and SYN) */ + adm8211_hw_init_bbp(dev); + + /* make sure interrupts are off */ + ADM8211_CSR_WRITE(IER, 0); + + /* ACK interrupts */ + ADM8211_CSR_WRITE(STSR, ADM8211_CSR_READ(STSR)); + + /* Setup WEP (turns it off for now) */ + reg = le32_to_cpu(ADM8211_CSR_READ(MACTEST)); + reg &= ~(7<<20); + ADM8211_CSR_WRITE(MACTEST, cpu_to_le32(reg)); + + reg = le32_to_cpu(ADM8211_CSR_READ(WEPCTL)); + reg &= ~ADM8211_WEPCTL_WEPENABLE; + reg |= ADM8211_WEPCTL_WEPRXBYP; + ADM8211_CSR_WRITE(WEPCTL, cpu_to_le32(reg)); + + /* Clear the missed-packet counter. */ + ADM8211_CSR_READ(LPC); + + /* set mac address */ + ADM8211_CSR_WRITE(PAR0, *(u32 *)priv->mac_addr); + ADM8211_CSR_WRITE(PAR1, *(u16 *)(priv->mac_addr + 4)); +} + +static int adm8211_hw_reset(struct ieee80211_hw *dev) +{ + struct adm8211_priv *priv = dev->priv; + u32 reg; + __le32 tmp; + int timeout = 100; + + /* Power-on issue */ + /* TODO: check if this is necessary */ + ADM8211_CSR_WRITE(FRCTL, 0); + + /* Reset the chip */ + tmp = ADM8211_CSR_READ(PAR); + ADM8211_CSR_WRITE(PAR, ADM8211_PAR_SWR); + + while ((ADM8211_CSR_READ(PAR) & __constant_cpu_to_le32(ADM8211_PAR_SWR)) && timeout--) + mdelay(50); + + if (timeout <= 0) + return -ETIMEDOUT; + + ADM8211_CSR_WRITE(PAR, tmp); + + if (priv->revid == ADM8211_REV_BA && + ( priv->transceiver_type == ADM8211_RFMD2958_RF3000_CONTROL_POWER + || priv->transceiver_type == ADM8211_RFMD2958)) { + reg = le32_to_cpu(ADM8211_CSR_READ(CSR_TEST1)); + reg |= (1 << 4) | (1 << 5); + ADM8211_CSR_WRITE(CSR_TEST1, cpu_to_le32(reg)); + } else if (priv->revid == ADM8211_REV_CA) { + reg = le32_to_cpu(ADM8211_CSR_READ(CSR_TEST1)); + reg &= ~((1 << 4) | (1 << 5)); + ADM8211_CSR_WRITE(CSR_TEST1, cpu_to_le32(reg)); + } + + ADM8211_CSR_WRITE(FRCTL, 0); + + reg = le32_to_cpu(ADM8211_CSR_READ(CSR_TEST0)); + reg |= ADM8211_CSR_TEST0_EPRLD; /* EEPROM Recall */ + ADM8211_CSR_WRITE(CSR_TEST0, cpu_to_le32(reg)); + + adm8211_clear_sram(dev); + + return 0; +} + +static u64 adm8211_get_tsft(struct ieee80211_hw *dev) +{ + struct adm8211_priv *priv = dev->priv; + u32 tsftl; + u64 tsft; + + tsftl = le32_to_cpu(ADM8211_CSR_READ(TSFTL)); + tsft = le32_to_cpu(ADM8211_CSR_READ(TSFTH)); + tsft <<= 32; + tsft |= tsftl; + + return tsft; +} + +static void adm8211_set_interval(struct ieee80211_hw *dev, + unsigned short bi, unsigned short li) +{ + struct adm8211_priv *priv = dev->priv; + u32 reg; + + /* BP (beacon interval) = data->beacon_interval + * LI (listen interval) = data->listen_interval (in beacon intervals) */ + reg = (bi << 16) | li; + ADM8211_CSR_WRITE(BPLI, cpu_to_le32(reg)); +} + +static void adm8211_set_bssid(struct ieee80211_hw *dev, u8 *bssid) +{ + struct adm8211_priv *priv = dev->priv; + u32 reg; + + reg = bssid[0] | (bssid[1] << 8) | (bssid[2] << 16) | (bssid[3] << 24); + ADM8211_CSR_WRITE(BSSID0, cpu_to_le32(reg)); + reg = le32_to_cpu(ADM8211_CSR_READ(ABDA1)); + reg &= 0x0000ffff; + reg |= (bssid[4] << 16) | (bssid[5] << 24); + ADM8211_CSR_WRITE(ABDA1, cpu_to_le32(reg)); +} + +static int adm8211_set_ssid(struct ieee80211_hw *dev, u8 *ssid, size_t ssid_len) +{ + struct adm8211_priv *priv = dev->priv; + u8 buf[36]; + + if (ssid_len > 32) + return -EINVAL; + + memset(buf, 0, sizeof(buf)); + buf[0] = ssid_len; + memcpy(buf + 1, ssid, ssid_len); + adm8211_write_sram_bytes(dev, ADM8211_SRAM_SSID, buf, 33); + //adm8211_set_beacon(dev); + return 0; +} + +static int adm8211_config(struct ieee80211_hw *dev, struct ieee80211_conf *conf) +{ + struct adm8211_priv *priv = dev->priv; + + if (conf->channel != priv->channel) { + priv->channel = conf->channel; + adm8211_rf_set_channel(dev, priv->channel); + } + + return 0; +} + +static int adm8211_config_interface(struct ieee80211_hw *dev, int if_id, + struct ieee80211_if_conf *conf) +{ + struct adm8211_priv *priv = dev->priv; + + if (memcmp(conf->bssid, priv->bssid, ETH_ALEN)) { + adm8211_set_bssid(dev, conf->bssid); + memcpy(priv->bssid, conf->bssid, ETH_ALEN); + } + + if (conf->ssid_len != priv->ssid_len || + memcmp(conf->ssid, priv->ssid, conf->ssid_len)) { + adm8211_set_ssid(dev, conf->ssid, conf->ssid_len); + priv->ssid_len = conf->ssid_len; + memcpy(priv->ssid, conf->ssid, conf->ssid_len); + } + + return 0; +} + +static int adm8211_add_interface(struct ieee80211_hw *dev, + struct ieee80211_if_init_conf *conf) +{ + struct adm8211_priv *priv = dev->priv; + /* NOTE: using IEEE80211_IF_TYPE_MGMT to indicate no mode selected */ + if (priv->mode != IEEE80211_IF_TYPE_MGMT) + return -1; + + switch (conf->type) { + case IEEE80211_IF_TYPE_STA: + case IEEE80211_IF_TYPE_MNTR: + priv->mode = conf->type; + break; + default: + return -EOPNOTSUPP; + } + + priv->mac_addr = conf->mac_addr; + + return 0; +} + +static void adm8211_remove_interface(struct ieee80211_hw *dev, + struct ieee80211_if_init_conf *conf) +{ + struct adm8211_priv *priv = dev->priv; + priv->mode = IEEE80211_IF_TYPE_MGMT; +} + +static int adm8211_init_rings(struct ieee80211_hw *dev) +{ + struct adm8211_priv *priv = dev->priv; + struct adm8211_desc *desc = NULL; + struct adm8211_rx_ring_info *rx_info; + struct adm8211_tx_ring_info *tx_info; + unsigned int i; + + for (i = 0; i < priv->rx_ring_size; i++) { + desc = &priv->rx_ring[i]; + desc->status = 0; + desc->length = cpu_to_le32(RX_PKT_SIZE); + priv->rx_buffers[i].skb = NULL; + } + /* Mark the end of RX ring; hw returns to base address after this + * descriptor */ + desc->length |= cpu_to_le32(RDES1_CONTROL_RER); + + for (i = 0; i < priv->rx_ring_size; i++) { + desc = &priv->rx_ring[i]; + rx_info = &priv->rx_buffers[i]; + + rx_info->skb = dev_alloc_skb(RX_PKT_SIZE); + if (rx_info->skb == NULL) + break; + rx_info->mapping = pci_map_single(priv->pdev, rx_info->skb->tail, + RX_PKT_SIZE, + PCI_DMA_FROMDEVICE); + desc->buffer1 = cpu_to_le32(rx_info->mapping); + desc->status = cpu_to_le32(RDES0_STATUS_OWN | RDES0_STATUS_SQL); + } + + /* Setup TX ring. TX buffers descriptors will be filled in as needed */ + for (i = 0; i < priv->tx_ring_size; i++) { + desc = &priv->tx_ring[i]; + tx_info = &priv->tx_buffers[i]; + + tx_info->skb = NULL; + tx_info->mapping = 0; + desc->status = 0; + } + desc->length = cpu_to_le32(TDES1_CONTROL_TER); + + priv->cur_rx = priv->cur_tx = priv->dirty_tx = 0; + ADM8211_CSR_WRITE(RDB, cpu_to_le32(priv->rx_ring_dma)); + ADM8211_CSR_WRITE(TDBD, cpu_to_le32(priv->tx_ring_dma)); + + return 0; +} + +static void adm8211_free_rings(struct ieee80211_hw *dev) +{ + struct adm8211_priv *priv = dev->priv; + unsigned int i; + + for (i = 0; i < priv->rx_ring_size; i++) { + if (!priv->rx_buffers[i].skb) + continue; + + pci_unmap_single( + priv->pdev, + priv->rx_buffers[i].mapping, + RX_PKT_SIZE, PCI_DMA_FROMDEVICE); + + dev_kfree_skb(priv->rx_buffers[i].skb); + } + + for (i = 0; i < priv->tx_ring_size; i++) { + if (!priv->tx_buffers[i].skb) + continue; + + pci_unmap_single( + priv->pdev, + priv->tx_buffers[i].mapping, + priv->tx_buffers[i].skb->len, PCI_DMA_TODEVICE); + + dev_kfree_skb(priv->tx_buffers[i].skb); + } +} + +static int adm8211_open(struct ieee80211_hw *dev) +{ + struct adm8211_priv *priv = dev->priv; + int retval; + + /* Power up MAC and RF chips */ + retval = adm8211_hw_reset(dev); + if (retval) { + printk(KERN_ERR "%s: hardware reset failed\n", + wiphy_name(dev->wiphy)); + goto fail; + } + + retval = adm8211_init_rings(dev); + if (retval) { + printk(KERN_ERR "%s: failed to initialize rings\n", + wiphy_name(dev->wiphy)); + goto fail; + } + + /* Init hardware */ + adm8211_hw_init(dev); + adm8211_rf_set_channel(dev, priv->channel); + + retval = request_irq(priv->pdev->irq, &adm8211_interrupt, + IRQF_SHARED, "adm8211", dev); + if (retval) { + printk(KERN_ERR "%s: failed to register IRQ handler\n", + wiphy_name(dev->wiphy)); + goto fail; + } + + ADM8211_CSR_WRITE(IER, __constant_cpu_to_le32(ADM8211_INTMASK)); + adm8211_update_mode(dev); + ADM8211_CSR_WRITE(RDR, 0); + + adm8211_set_interval(dev, 100, 10); + return 0; + +fail: + return retval; +} + +static int adm8211_stop(struct ieee80211_hw *dev) +{ + struct adm8211_priv *priv = dev->priv; + + priv->nar = 0; + ADM8211_CSR_WRITE(NAR, 0); + ADM8211_CSR_WRITE(IER, 0); + ADM8211_CSR_READ(NAR); + + free_irq(priv->pdev->irq, dev); + + adm8211_free_rings(dev); + return 0; +} + +static int adm8211_reset(struct ieee80211_hw *dev) +{ + struct adm8211_priv *priv = dev->priv; + int retval = 0; + + priv->nar = 0; + ADM8211_CSR_WRITE(NAR, 0); + ADM8211_CSR_WRITE(IER, 0); + ADM8211_CSR_READ(NAR); + + adm8211_free_rings(dev); + + retval = adm8211_hw_reset(dev); + if (retval) { + printk(KERN_ERR "%s: hardware reset failed\n", + wiphy_name(dev->wiphy)); + goto fail; + } + + retval = adm8211_init_rings(dev); + if (retval) { + printk(KERN_ERR "%s: failed to initialize rings\n", + wiphy_name(dev->wiphy)); + goto fail; + } + + adm8211_hw_init(dev); + adm8211_rf_set_channel(dev, priv->channel); + + ADM8211_CSR_WRITE(IER, __constant_cpu_to_le32(ADM8211_INTMASK)); + adm8211_update_mode(dev); + ADM8211_CSR_WRITE(RDR, 0); + +fail: + return retval; +} + +static void adm8211_calc_durations(int *dur, int *plcp, size_t payload_len, int len, + int plcp_signal, int short_preamble) +{ + /* Alternative calculation from NetBSD: */ + +/* IEEE 802.11b durations for DSSS PHY in microseconds */ +#define IEEE80211_DUR_DS_LONG_PREAMBLE 144 +#define IEEE80211_DUR_DS_SHORT_PREAMBLE 72 +#define IEEE80211_DUR_DS_FAST_PLCPHDR 24 +#define IEEE80211_DUR_DS_SLOW_PLCPHDR 48 +#define IEEE80211_DUR_DS_SLOW_ACK 112 +#define IEEE80211_DUR_DS_FAST_ACK 56 +#define IEEE80211_DUR_DS_SLOW_CTS 112 +#define IEEE80211_DUR_DS_FAST_CTS 56 +#define IEEE80211_DUR_DS_SLOT 20 +#define IEEE80211_DUR_DS_SIFS 10 + + int remainder; + + *dur = (80 * (24 + payload_len) + plcp_signal - 1) + / plcp_signal; + + if (plcp_signal <= PLCP_SIGNAL_2M) + /* 1-2Mbps WLAN: send ACK/CTS at 1Mbps */ + *dur += 3 * (IEEE80211_DUR_DS_SIFS + + IEEE80211_DUR_DS_SHORT_PREAMBLE + + IEEE80211_DUR_DS_FAST_PLCPHDR) + + IEEE80211_DUR_DS_SLOW_CTS + IEEE80211_DUR_DS_SLOW_ACK; + else + /* 5-11Mbps WLAN: send ACK/CTS at 2Mbps */ + *dur += 3 * (IEEE80211_DUR_DS_SIFS + + IEEE80211_DUR_DS_SHORT_PREAMBLE + + IEEE80211_DUR_DS_FAST_PLCPHDR) + + IEEE80211_DUR_DS_FAST_CTS + IEEE80211_DUR_DS_FAST_ACK; + + /* lengthen duration if long preamble */ + if (!short_preamble) + *dur += 3 * (IEEE80211_DUR_DS_LONG_PREAMBLE - + IEEE80211_DUR_DS_SHORT_PREAMBLE) + + 3 * (IEEE80211_DUR_DS_SLOW_PLCPHDR - + IEEE80211_DUR_DS_FAST_PLCPHDR); + + + *plcp = (80 * len) / plcp_signal; + remainder = (80 * len) % plcp_signal; + if (plcp_signal == PLCP_SIGNAL_11M && + remainder <= 30 && remainder > 0) + *plcp = (*plcp | 0x8000) + 1; + else if (remainder) + (*plcp)++; +} + +/* Transmit skb w/adm8211_tx_hdr (802.11 header created by hardware) */ +static void adm8211_tx_raw(struct ieee80211_hw *dev, struct sk_buff *skb, + u16 plcp_signal, struct ieee80211_tx_control *control, + struct ieee80211_hdr *hdr) +{ + struct adm8211_priv *priv = dev->priv; + unsigned long flags; + dma_addr_t mapping; + unsigned entry; + u32 flag; + + mapping = pci_map_single(priv->pdev, skb->data, skb->len, + PCI_DMA_TODEVICE); + + spin_lock_irqsave(&priv->lock, flags); + + if (priv->cur_tx - priv->dirty_tx == priv->tx_ring_size / 2) + flag = TDES1_CONTROL_IC | TDES1_CONTROL_LS | TDES1_CONTROL_FS; + else + flag = TDES1_CONTROL_LS | TDES1_CONTROL_FS; + + if (priv->cur_tx - priv->dirty_tx == priv->tx_ring_size - 2) + ieee80211_stop_queue(dev, 0); + + entry = priv->cur_tx % priv->tx_ring_size; + + priv->tx_buffers[entry].skb = skb; + priv->tx_buffers[entry].mapping = mapping; + memcpy(&priv->tx_buffers[entry].tx_status.control, control, sizeof(*control)); + memcpy(&priv->tx_buffers[entry].hdr, hdr, sizeof(*hdr)); + priv->tx_ring[entry].buffer1 = cpu_to_le32(mapping); + + if (entry == priv->tx_ring_size - 1) + flag |= TDES1_CONTROL_TER; + priv->tx_ring[entry].length = cpu_to_le32(flag | skb->len); + + /* Set TX rate (SIGNAL field in PLCP PPDU format) */ + flag = TDES0_CONTROL_OWN | (plcp_signal << 20) | 8 /* ? */; + priv->tx_ring[entry].status = cpu_to_le32(flag); + + priv->cur_tx++; + + spin_unlock_irqrestore(&priv->lock, flags); + + /* Trigger transmit poll */ + ADM8211_CSR_WRITE(TDR, 0); +} + +/* Put adm8211_tx_hdr on skb and transmit */ +static int adm8211_tx(struct ieee80211_hw *dev, struct sk_buff *skb, + struct ieee80211_tx_control *control) +{ + struct adm8211_tx_hdr *txhdr; + u16 fc; + size_t payload_len, hdrlen; + int plcp, dur, len; + int plcp_signal; + int short_preamble; + struct ieee80211_hdr hdr; + + if (control->tx_rate < 0) { + short_preamble = 1; + plcp_signal = -control->tx_rate; + } else { + short_preamble = 0; + plcp_signal = control->tx_rate; + } + + memcpy(&hdr, skb->data, sizeof(hdr)); + + fc = le16_to_cpu(hdr.frame_control) & ~IEEE80211_FCTL_PROTECTED; + hdrlen = ieee80211_get_hdrlen(fc); + skb_pull(skb, hdrlen); + payload_len = skb->len; + + txhdr = (struct adm8211_tx_hdr *) skb_push(skb, sizeof(*txhdr)); + memset(txhdr, 0, sizeof(*txhdr)); + memcpy(txhdr->da, ieee80211_get_DA(&hdr), ETH_ALEN); + txhdr->signal = plcp_signal; + txhdr->frame_body_size = cpu_to_le16(payload_len); + txhdr->frame_control = hdr.frame_control; + + len = hdrlen + payload_len + FCS_LEN; + if (fc & IEEE80211_FCTL_PROTECTED) + len += 8; + + txhdr->frag = cpu_to_le16(0x0FFF); + adm8211_calc_durations(&dur, &plcp, payload_len, + len, plcp_signal, short_preamble); + txhdr->plcp_frag_head_len = cpu_to_le16(plcp); + txhdr->plcp_frag_tail_len = cpu_to_le16(plcp); + txhdr->dur_frag_head = cpu_to_le16(dur); + txhdr->dur_frag_tail = cpu_to_le16(dur); + + txhdr->header_control = cpu_to_le16(ADM8211_TXHDRCTL_ENABLE_EXTEND_HEADER); + + if (short_preamble) + txhdr->header_control |= cpu_to_le16(ADM8211_TXHDRCTL_SHORT_PREAMBLE); + + if (control->flags & IEEE80211_TXCTL_USE_RTS_CTS) + txhdr->header_control |= cpu_to_le16(ADM8211_TXHDRCTL_ENABLE_RTS); + + if (fc & IEEE80211_FCTL_PROTECTED) + txhdr->header_control |= cpu_to_le16(ADM8211_TXHDRCTL_ENABLE_WEP_ENGINE); + + txhdr->retry_limit = control->retry_limit; + + adm8211_tx_raw(dev, skb, plcp_signal, control, &hdr); + + return NETDEV_TX_OK; +} + +static int adm8211_alloc_rings(struct ieee80211_hw *dev) +{ + struct adm8211_priv *priv = dev->priv; + unsigned int ring_size; + + priv->rx_buffers = kmalloc(sizeof(struct adm8211_rx_ring_info) * priv->rx_ring_size + + sizeof(struct adm8211_tx_ring_info) * priv->tx_ring_size, GFP_KERNEL); + if (!priv->rx_buffers) + return -ENOMEM; + + priv->tx_buffers = ((void *)priv->rx_buffers) + sizeof(struct adm8211_rx_ring_info) * priv->rx_ring_size; + + /* Allocate TX/RX descriptors */ + ring_size = sizeof(struct adm8211_desc) * priv->rx_ring_size + + sizeof(struct adm8211_desc) * priv->tx_ring_size; + priv->rx_ring = pci_alloc_consistent(priv->pdev, ring_size, + &priv->rx_ring_dma); + + if (!priv->rx_ring) { + kfree(priv->rx_buffers); + priv->rx_buffers = NULL; + priv->tx_buffers = NULL; + return -ENOMEM; + } + + priv->tx_ring = (struct adm8211_desc *) (priv->rx_ring + priv->rx_ring_size); + priv->tx_ring_dma = priv->rx_ring_dma + + sizeof(struct adm8211_desc) * priv->rx_ring_size; + + return 0; +} + +static const struct ieee80211_ops adm8211_ops = { + .tx = adm8211_tx, + .reset = adm8211_reset, + .open = adm8211_open, + .stop = adm8211_stop, + .add_interface = adm8211_add_interface, + .remove_interface = adm8211_remove_interface, + .config = adm8211_config, + .config_interface = adm8211_config_interface, + .set_multicast_list = adm8211_set_rx_mode, + .get_stats = adm8211_get_stats, + .get_tx_stats = adm8211_get_tx_stats, + .get_tsf = adm8211_get_tsft +}; + +static int __devinit adm8211_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct ieee80211_hw *dev; + struct adm8211_priv *priv; + unsigned long mem_addr, mem_len; + unsigned int io_addr, io_len; + int err; + u32 reg; + u8 perm_addr[ETH_ALEN]; + +#ifndef MODULE + static unsigned int cardidx; + if (!cardidx++) + printk(version); +#endif + + err = pci_enable_device(pdev); + if (err) { + printk(KERN_ERR "%s (adm8211): Cannot enable new PCI device\n", pci_name(pdev)); + return err; + } + + io_addr = pci_resource_start(pdev, 0); + io_len = pci_resource_len(pdev, 0); + mem_addr = pci_resource_start(pdev, 1); + mem_len = pci_resource_len(pdev, 1); + if (io_len < 256 || mem_len < 1024) { + printk(KERN_ERR "%s (adm8211): Too short PCI resources\n", pci_name(pdev)); + goto err_disable_pdev; + } + + + /* check signature */ + pci_read_config_dword(pdev, 0x80 /* CR32 */, ®); + if (reg != ADM8211_SIG1 && reg != ADM8211_SIG2) { + printk(KERN_ERR "%s (adm8211): Invalid signature (0x%x)\n", pci_name(pdev), reg); + goto err_disable_pdev; + } + + err = pci_request_regions(pdev, "adm8211"); + if (err) { + printk(KERN_ERR "%s (adm8211): Cannot obtain PCI resources\n", pci_name(pdev)); + return err; /* someone else grabbed it? don't disable it */ + } + + if (pci_set_dma_mask(pdev, DMA_32BIT_MASK) || + pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK)) { + printk(KERN_ERR "%s (adm8211): No suitable DMA available\n", pci_name(pdev)); + goto err_free_reg; + } + + pci_set_master(pdev); + + dev = ieee80211_alloc_hw(sizeof(*priv), &adm8211_ops); + if (!dev) { + printk(KERN_ERR "%s (adm8211): ieee80211 alloc failed\n", pci_name(pdev)); + err = -ENOMEM; + goto err_free_reg; + } + priv = dev->priv; + priv->pdev = pdev; + + spin_lock_init(&priv->lock); + + SET_IEEE80211_DEV(dev, &pdev->dev); + + pci_set_drvdata(pdev, dev); + priv->msg_enable = netif_msg_init(debug, NETIF_MSG_DRV | NETIF_MSG_PROBE); + + priv->map = pci_iomap(pdev, 1, mem_len); + if (!priv->map) + priv->map = pci_iomap(pdev, 0, io_len); + + if (!priv->map) { + printk(KERN_ERR "%s (adm8211): Cannot map device memory\n", pci_name(pdev)); + goto err_free_dev; + } + + priv->rx_ring_size = rx_ring_size; + priv->tx_ring_size = tx_ring_size; + + if (adm8211_alloc_rings(dev)) { + printk(KERN_ERR "%s (adm8211): Cannot allocate TX/RX ring\n", pci_name(pdev)); + goto err_iounmap; + } + + pci_read_config_byte(pdev, PCI_CLASS_REVISION, &priv->revid); + + put_unaligned(ADM8211_CSR_READ(PAR0), (u32 *) perm_addr); + put_unaligned(ADM8211_CSR_READ(PAR1) & (__force u32) __constant_cpu_to_le32 (0xffff), + (u16 *) &perm_addr[4]); + + if (!is_valid_ether_addr(perm_addr)) { + printk(KERN_WARNING "%s (adm8211): Invalid hwaddr! Using randomly generated hwaddr\n", pci_name(pdev)); + random_ether_addr(perm_addr); + } + SET_IEEE80211_PERM_ADDR(dev, perm_addr); + + dev->extra_tx_headroom = sizeof(struct adm8211_tx_hdr); + dev->flags = IEEE80211_HW_WEP_INCLUDE_IV | IEEE80211_HW_NO_TKIP_WMM_HWACCEL; + // however, IEEE80211_HW_RX_INCLUDES_FCS in promisc mode + + dev->channel_change_time = 1000; + dev->max_rssi = ADM8211_RX_MAX_SSI;// FIXME - This is an approximation + + priv->modes[0].mode = MODE_IEEE80211B; + /* channel info filled in by adm8211_read_eeprom */ + memcpy(priv->rates, adm8211_rates, sizeof(adm8211_rates)); + priv->modes[0].num_rates = ARRAY_SIZE(adm8211_rates); + priv->modes[0].rates = priv->rates; + + dev->queues = 1; // ADM8211C supports more, maybe ADM8211B + + priv->retry_limit = 3; + priv->ant_power = 0x40; + priv->tx_power = 0x40; + priv->lpf_cutoff = 0xFF; + priv->lnags_threshold = 0xFF; + priv->mode = IEEE80211_IF_TYPE_MGMT; + + /* Power-on issue. EEPROM won't read correctly without */ + if (priv->revid >= ADM8211_REV_BA) { + ADM8211_CSR_WRITE(FRCTL, 0); + ADM8211_CSR_READ(FRCTL); + ADM8211_CSR_WRITE(FRCTL, 1); + ADM8211_CSR_READ(FRCTL); + mdelay(100); + } + + err = adm8211_read_eeprom(dev); + if (err) { + printk(KERN_ERR "%s (adm8211): Cannot allocate eeprom buffer\n", pci_name(pdev)); + goto err_free_desc; + } + + priv->channel = priv->modes[0].channels[0].chan; + + err = ieee80211_register_hwmode(dev, &priv->modes[0]); + if (err) { + printk(KERN_ERR "%s (adm8211): Cannot register hwmode\n", pci_name(pdev)); + goto err_free_desc; + } + + err = ieee80211_register_hw(dev); + if (err) { + printk(KERN_ERR "%s (adm8211): Cannot register hardware\n", pci_name(pdev)); + goto err_free_desc; + } + + printk(KERN_INFO "%s: hwaddr " MAC_FMT ", Rev 0x%02x\n", + wiphy_name(dev->wiphy), MAC_ARG(dev->wiphy->perm_addr), priv->revid); + + return 0; + + err_free_desc: + pci_free_consistent(pdev, + sizeof(struct adm8211_desc) * priv->rx_ring_size + + sizeof(struct adm8211_desc) * priv->tx_ring_size, + priv->rx_ring, priv->rx_ring_dma); + kfree(priv->rx_buffers); + + err_iounmap: + pci_iounmap(pdev, priv->map); + + err_free_dev: + pci_set_drvdata(pdev, NULL); + ieee80211_free_hw(dev); + + err_free_reg: + pci_release_regions(pdev); + + err_disable_pdev: + pci_disable_device(pdev); + return err; +} + + +static void __devexit adm8211_remove(struct pci_dev *pdev) +{ + struct ieee80211_hw *dev = pci_get_drvdata(pdev); + struct adm8211_priv *priv; + + if (!dev) + return; + + ieee80211_unregister_hw(dev); + + priv = dev->priv; + + pci_free_consistent(pdev, + sizeof(struct adm8211_desc) * priv->rx_ring_size + + sizeof(struct adm8211_desc) * priv->tx_ring_size, + priv->rx_ring, priv->rx_ring_dma); + + kfree(priv->rx_buffers); + kfree(priv->eeprom); + pci_iounmap(pdev, priv->map); + pci_release_regions(pdev); + pci_disable_device(pdev); + ieee80211_free_hw(dev); +} + + +#ifdef CONFIG_PM +static int adm8211_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct ieee80211_hw *dev = pci_get_drvdata(pdev); + struct adm8211_priv *priv = dev->priv; + + if (priv->mode != IEEE80211_IF_TYPE_MGMT) { + ieee80211_stop_queues(dev); + adm8211_stop(dev); + } + + pci_save_state(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + return 0; +} + +static int adm8211_resume(struct pci_dev *pdev) +{ + struct ieee80211_hw *dev = pci_get_drvdata(pdev); + struct adm8211_priv *priv = dev->priv; + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + + if (priv->mode != IEEE80211_IF_TYPE_MGMT) { + adm8211_open(dev); + ieee80211_start_queues(dev); + } + + return 0; +} +#endif /* CONFIG_PM */ + + +MODULE_DEVICE_TABLE(pci, adm8211_pci_id_table); + +/* TODO: enable_wake */ +static struct pci_driver adm8211_driver = { + .name = "adm8211", + .id_table = adm8211_pci_id_table, + .probe = adm8211_probe, + .remove = __devexit_p(adm8211_remove), +#ifdef CONFIG_PM + .suspend = adm8211_suspend, + .resume = adm8211_resume, +#endif /* CONFIG_PM */ +}; + + + +static int __init adm8211_init(void) +{ +#ifdef MODULE + printk(version); +#endif + + return pci_register_driver(&adm8211_driver); +} + + +static void __exit adm8211_exit(void) +{ + pci_unregister_driver(&adm8211_driver); +} + + +module_init(adm8211_init); +module_exit(adm8211_exit); diff --git a/drivers/net/wireless/mac80211/adm8211/adm8211.h b/drivers/net/wireless/mac80211/adm8211/adm8211.h new file mode 100644 index 0000000..fd7c72f --- /dev/null +++ b/drivers/net/wireless/mac80211/adm8211/adm8211.h @@ -0,0 +1,622 @@ +#ifndef ADM8211_H +#define ADM8211_H + +/* ADM8211 Registers */ + +/* CR32 (SIG) signature */ +#define ADM8211_SIG1 0x82011317 /* ADM8211A */ +#define ADM8211_SIG2 0x82111317 /* ADM8211B/ADM8211C */ + +#define ADM8211_CSR_READ(r) ioread32(&priv->map->r) +#define ADM8211_CSR_WRITE(r, val) iowrite32((__force u32)(val), &priv->map->r) + +/* CSR (Host Control and Status Registers) */ +struct adm8211_csr { + __le32 PAR; /* 0x00 CSR0 */ + __le32 FRCTL; /* 0x04 CSR0A */ + __le32 TDR; /* 0x08 CSR1 */ + __le32 WTDP; /* 0x0C CSR1A */ + __le32 RDR; /* 0x10 CSR2 */ + __le32 WRDP; /* 0x14 CSR2A */ + __le32 RDB; /* 0x18 CSR3 */ + __le32 TDBH; /* 0x1C CSR3A */ + __le32 TDBD; /* 0x20 CSR4 */ + __le32 TDBP; /* 0x24 CSR4A */ + __le32 STSR; /* 0x28 CSR5 */ + __le32 TDBB; /* 0x2C CSR5A */ + __le32 NAR; /* 0x30 CSR6 */ + __le32 CSR6A; /* reserved */ + __le32 IER; /* 0x38 CSR7 */ + __le32 TKIPSCEP; /* 0x3C CSR7A */ + __le32 LPC; /* 0x40 CSR8 */ + __le32 CSR_TEST1; /* 0x44 CSR8A */ + __le32 SPR; /* 0x48 CSR9 */ + __le32 CSR_TEST0; /* 0x4C CSR9A */ + __le32 WCSR; /* 0x50 CSR10 */ + __le32 WPDR; /* 0x54 CSR10A */ + __le32 GPTMR; /* 0x58 CSR11 */ + __le32 GPIO; /* 0x5C CSR11A */ + __le32 BBPCTL; /* 0x60 CSR12 */ + __le32 SYNCTL; /* 0x64 CSR12A */ + __le32 PLCPHD; /* 0x68 CSR13 */ + __le32 MMIWA; /* 0x6C CSR13A */ + __le32 MMIRD0; /* 0x70 CSR14 */ + __le32 MMIRD1; /* 0x74 CSR14A */ + __le32 TXBR; /* 0x78 CSR15 */ + __le32 SYNDATA; /* 0x7C CSR15A */ + __le32 ALCS; /* 0x80 CSR16 */ + __le32 TOFS2; /* 0x84 CSR17 */ + __le32 CMDR; /* 0x88 CSR18 */ + __le32 PCIC; /* 0x8C CSR19 */ + __le32 PMCSR; /* 0x90 CSR20 */ + __le32 PAR0; /* 0x94 CSR21 */ + __le32 PAR1; /* 0x98 CSR22 */ + __le32 MAR0; /* 0x9C CSR23 */ + __le32 MAR1; /* 0xA0 CSR24 */ + __le32 ATIMDA0; /* 0xA4 CSR25 */ + __le32 ABDA1; /* 0xA8 CSR26 */ + __le32 BSSID0; /* 0xAC CSR27 */ + __le32 TXLMT; /* 0xB0 CSR28 */ + __le32 MIBCNT; /* 0xB4 CSR29 */ + __le32 BCNT; /* 0xB8 CSR30 */ + __le32 TSFTH; /* 0xBC CSR31 */ + __le32 TSC; /* 0xC0 CSR32 */ + __le32 SYNRF; /* 0xC4 CSR33 */ + __le32 BPLI; /* 0xC8 CSR34 */ + __le32 CAP0; /* 0xCC CSR35 */ + __le32 CAP1; /* 0xD0 CSR36 */ + __le32 RMD; /* 0xD4 CSR37 */ + __le32 CFPP; /* 0xD8 CSR38 */ + __le32 TOFS0; /* 0xDC CSR39 */ + __le32 TOFS1; /* 0xE0 CSR40 */ + __le32 IFST; /* 0xE4 CSR41 */ + __le32 RSPT; /* 0xE8 CSR42 */ + __le32 TSFTL; /* 0xEC CSR43 */ + __le32 WEPCTL; /* 0xF0 CSR44 */ + __le32 WESK; /* 0xF4 CSR45 */ + __le32 WEPCNT; /* 0xF8 CSR46 */ + __le32 MACTEST; /* 0xFC CSR47 */ + __le32 FER; /* 0x100 */ + __le32 FEMR; /* 0x104 */ + __le32 FPSR; /* 0x108 */ + __le32 FFER; /* 0x10C */ +} __attribute__ ((packed)); + +/* CSR0 - PAR (PCI Address Register) */ +#define ADM8211_PAR_MWIE (1 << 24) +#define ADM8211_PAR_MRLE (1 << 23) +#define ADM8211_PAR_MRME (1 << 21) +#define ADM8211_PAR_RAP ((1 << 18) | (1 << 17)) +#define ADM8211_PAR_CAL ((1 << 15) | (1 << 14)) +#define ADM8211_PAR_PBL 0x00003f00 +#define ADM8211_PAR_BLE (1 << 7) +#define ADM8211_PAR_DSL 0x0000007c +#define ADM8211_PAR_BAR (1 << 1) +#define ADM8211_PAR_SWR (1 << 0) + +/* CSR1 - FRCTL (Frame Control Register) */ +#define ADM8211_FRCTL_PWRMGT (1 << 31) +#define ADM8211_FRCTL_MAXPSP (1 << 27) +#define ADM8211_FRCTL_DRVPRSP (1 << 26) +#define ADM8211_FRCTL_DRVBCON (1 << 25) +#define ADM8211_FRCTL_AID 0x0000ffff +#define ADM8211_FRCTL_AID_ON 0x0000c000 + +/* CSR5 - STSR (Status Register) */ +#define ADM8211_STSR_PCF (1 << 31) +#define ADM8211_STSR_BCNTC (1 << 30) +#define ADM8211_STSR_GPINT (1 << 29) +#define ADM8211_STSR_LinkOff (1 << 28) +#define ADM8211_STSR_ATIMTC (1 << 27) +#define ADM8211_STSR_TSFTF (1 << 26) +#define ADM8211_STSR_TSCZ (1 << 25) +#define ADM8211_STSR_LinkOn (1 << 24) +#define ADM8211_STSR_SQL (1 << 23) +#define ADM8211_STSR_WEPTD (1 << 22) +#define ADM8211_STSR_ATIME (1 << 21) +#define ADM8211_STSR_TBTT (1 << 20) +#define ADM8211_STSR_NISS (1 << 16) +#define ADM8211_STSR_AISS (1 << 15) +#define ADM8211_STSR_TEIS (1 << 14) +#define ADM8211_STSR_FBE (1 << 13) +#define ADM8211_STSR_REIS (1 << 12) +#define ADM8211_STSR_GPTT (1 << 11) +#define ADM8211_STSR_RPS (1 << 8) +#define ADM8211_STSR_RDU (1 << 7) +#define ADM8211_STSR_RCI (1 << 6) +#define ADM8211_STSR_TUF (1 << 5) +#define ADM8211_STSR_TRT (1 << 4) +#define ADM8211_STSR_TLT (1 << 3) +#define ADM8211_STSR_TDU (1 << 2) +#define ADM8211_STSR_TPS (1 << 1) +#define ADM8211_STSR_TCI (1 << 0) + +/* CSR6 - NAR (Network Access Register) */ +#define ADM8211_NAR_TXCF (1 << 31) +#define ADM8211_NAR_HF (1 << 30) +#define ADM8211_NAR_UTR (1 << 29) +#define ADM8211_NAR_SQ (1 << 28) +#define ADM8211_NAR_CFP (1 << 27) +#define ADM8211_NAR_SF (1 << 21) +#define ADM8211_NAR_TR ((1 << 15) | (1 << 14)) +#define ADM8211_NAR_ST (1 << 13) +#define ADM8211_NAR_OM ((1 << 11) | (1 << 10)) +#define ADM8211_NAR_MM (1 << 7) +#define ADM8211_NAR_PR (1 << 6) +#define ADM8211_NAR_EA (1 << 5) +#define ADM8211_NAR_PB (1 << 3) +#define ADM8211_NAR_STPDMA (1 << 2) +#define ADM8211_NAR_SR (1 << 1) +#define ADM8211_NAR_CTX (1 << 0) + +#define ADM8211_IDLE() do { \ + if (priv->nar & (ADM8211_NAR_SR | ADM8211_NAR_ST)) {\ + ADM8211_CSR_WRITE(NAR, cpu_to_le32(priv->nar & ~(ADM8211_NAR_SR | ADM8211_NAR_ST)));\ + ADM8211_CSR_READ(NAR);\ + mdelay(20);\ + }\ +} while (0) + +#define ADM8211_IDLE_RX() do { \ + if (priv->nar & ADM8211_NAR_SR) {\ + ADM8211_CSR_WRITE(NAR, cpu_to_le32(priv->nar & ~ADM8211_NAR_SR));\ + ADM8211_CSR_READ(NAR);\ + mdelay(20);\ + }\ +} while (0) + +#define ADM8211_IDLE_TX() do { \ + if (priv->nar & ADM8211_NAR_ST) {\ + ADM8211_CSR_WRITE(NAR, cpu_to_le32(priv->nar & ~ADM8211_NAR_ST));\ + ADM8211_CSR_READ(NAR);\ + mdelay(20);\ + }\ +} while (0) + +#define ADM8211_RESTORE() do { \ + if (priv->nar & (ADM8211_NAR_SR | ADM8211_NAR_ST)) \ + ADM8211_CSR_WRITE(NAR, cpu_to_le32(priv->nar));\ +} while (0) + +/* CSR7 - IER (Interrupt Enable Register) */ +#define ADM8211_IER_PCFIE (1 << 31) +#define ADM8211_IER_BCNTCIE (1 << 30) +#define ADM8211_IER_GPIE (1 << 29) +#define ADM8211_IER_LinkOffIE (1 << 28) +#define ADM8211_IER_ATIMTCIE (1 << 27) +#define ADM8211_IER_TSFTFIE (1 << 26) +#define ADM8211_IER_TSCZE (1 << 25) +#define ADM8211_IER_LinkOnIE (1 << 24) +#define ADM8211_IER_SQLIE (1 << 23) +#define ADM8211_IER_WEPIE (1 << 22) +#define ADM8211_IER_ATIMEIE (1 << 21) +#define ADM8211_IER_TBTTIE (1 << 20) +#define ADM8211_IER_NIE (1 << 16) +#define ADM8211_IER_AIE (1 << 15) +#define ADM8211_IER_TEIE (1 << 14) +#define ADM8211_IER_FBEIE (1 << 13) +#define ADM8211_IER_REIE (1 << 12) +#define ADM8211_IER_GPTIE (1 << 11) +#define ADM8211_IER_RSIE (1 << 8) +#define ADM8211_IER_RUIE (1 << 7) +#define ADM8211_IER_RCIE (1 << 6) +#define ADM8211_IER_TUIE (1 << 5) +#define ADM8211_IER_TRTIE (1 << 4) +#define ADM8211_IER_TLTTIE (1 << 3) +#define ADM8211_IER_TDUIE (1 << 2) +#define ADM8211_IER_TPSIE (1 << 1) +#define ADM8211_IER_TCIE (1 << 0) + +/* CSR9 - SPR (Serial Port Register) */ +#define ADM8211_SPR_SRS (1 << 11) +#define ADM8211_SPR_SDO (1 << 3) +#define ADM8211_SPR_SDI (1 << 2) +#define ADM8211_SPR_SCLK (1 << 1) +#define ADM8211_SPR_SCS (1 << 0) + +/* CSR9A - CSR_TEST0 */ +#define ADM8211_CSR_TEST0_EPNE (1 << 18) +#define ADM8211_CSR_TEST0_EPSNM (1 << 17) +#define ADM8211_CSR_TEST0_EPTYP (1 << 16) +#define ADM8211_CSR_TEST0_EPRLD (1 << 15) + +/* CSR10 - WCSR (Wake-up Control/Status Register) */ +#define ADM8211_WCSR_CRCT (1 << 30) +#define ADM8211_WCSR_TSFTWE (1 << 20) +#define ADM8211_WCSR_TIMWE (1 << 19) +#define ADM8211_WCSR_ATIMWE (1 << 18) +#define ADM8211_WCSR_KEYWE (1 << 17) +#define ADM8211_WCSR_MPRE (1 << 9) +#define ADM8211_WCSR_LSOE (1 << 8) +#define ADM8211_WCSR_KEYUP (1 << 6) +#define ADM8211_WCSR_TSFTW (1 << 5) +#define ADM8211_WCSR_TIMW (1 << 4) +#define ADM8211_WCSR_ATIMW (1 << 3) +#define ADM8211_WCSR_MPR (1 << 1) +#define ADM8211_WCSR_LSO (1 << 0) + +/* CSR11A - GPIO */ +#define ADM8211_CSR_GPIO_EN5 (1 << 17) +#define ADM8211_CSR_GPIO_EN4 (1 << 16) +#define ADM8211_CSR_GPIO_EN3 (1 << 15) +#define ADM8211_CSR_GPIO_EN2 (1 << 14) +#define ADM8211_CSR_GPIO_EN1 (1 << 13) +#define ADM8211_CSR_GPIO_EN0 (1 << 12) +#define ADM8211_CSR_GPIO_O5 (1 << 11) +#define ADM8211_CSR_GPIO_O4 (1 << 10) +#define ADM8211_CSR_GPIO_O3 (1 << 9) +#define ADM8211_CSR_GPIO_O2 (1 << 8) +#define ADM8211_CSR_GPIO_O1 (1 << 7) +#define ADM8211_CSR_GPIO_O0 (1 << 6) +#define ADM8211_CSR_GPIO_IN 0x0000003f + +/* CSR12 - BBPCTL (BBP Control port) */ +#define ADM8211_BBPCTL_MMISEL (1 << 31) +#define ADM8211_BBPCTL_SPICADD (0x7F << 24) +#define ADM8211_BBPCTL_RF3000 (0x20 << 24) +#define ADM8211_BBPCTL_TXCE (1 << 23) +#define ADM8211_BBPCTL_RXCE (1 << 22) +#define ADM8211_BBPCTL_CCAP (1 << 21) +#define ADM8211_BBPCTL_TYPE 0x001c0000 +#define ADM8211_BBPCTL_WR (1 << 17) +#define ADM8211_BBPCTL_RD (1 << 16) +#define ADM8211_BBPCTL_ADDR 0x0000ff00 +#define ADM8211_BBPCTL_DATA 0x000000ff + +/* CSR12A - SYNCTL (Synthesizer Control port) */ +#define ADM8211_SYNCTL_WR (1 << 31) +#define ADM8211_SYNCTL_RD (1 << 30) +#define ADM8211_SYNCTL_CS0 (1 << 29) +#define ADM8211_SYNCTL_CS1 (1 << 28) +#define ADM8211_SYNCTL_CAL (1 << 27) +#define ADM8211_SYNCTL_SELCAL (1 << 26) +#define ADM8211_SYNCTL_RFtype ((1 << 24) || (1 << 23) || (1 << 22)) +#define ADM8211_SYNCTL_RFMD (1 << 22) +#define ADM8211_SYNCTL_GENERAL (0x7 << 22) +/* SYNCTL 21:0 Data (Si4126: 18-bit data, 4-bit address) */ + +/* CSR18 - CMDR (Command Register) */ +#define ADM8211_CMDR_PM (1 << 19) +#define ADM8211_CMDR_APM (1 << 18) +#define ADM8211_CMDR_RTE (1 << 4) +#define ADM8211_CMDR_DRT ((1 << 3) | (1 << 2)) +#define ADM8211_CMDR_DRT_8DW (0x0 << 2) +#define ADM8211_CMDR_DRT_16DW (0x1 << 2) +#define ADM8211_CMDR_DRT_SF (0x2 << 2) + +/* CSR33 - SYNRF (SYNRF direct control) */ +#define ADM8211_SYNRF_SELSYN (1 << 31) +#define ADM8211_SYNRF_SELRF (1 << 30) +#define ADM8211_SYNRF_LERF (1 << 29) +#define ADM8211_SYNRF_LEIF (1 << 28) +#define ADM8211_SYNRF_SYNCLK (1 << 27) +#define ADM8211_SYNRF_SYNDATA (1 << 26) +#define ADM8211_SYNRF_PE1 (1 << 25) +#define ADM8211_SYNRF_PE2 (1 << 24) +#define ADM8211_SYNRF_PA_PE (1 << 23) +#define ADM8211_SYNRF_TR_SW (1 << 22) +#define ADM8211_SYNRF_TR_SWN (1 << 21) +#define ADM8211_SYNRF_RADIO (1 << 20) +#define ADM8211_SYNRF_CAL_EN (1 << 19) +#define ADM8211_SYNRF_PHYRST (1 << 18) + +#define ADM8211_SYNRF_IF_SELECT_0 (1 << 31) +#define ADM8211_SYNRF_IF_SELECT_1 ((1 << 31) | (1 << 28)) +#define ADM8211_SYNRF_WRITE_SYNDATA_0 (1 << 31) +#define ADM8211_SYNRF_WRITE_SYNDATA_1 ((1 << 31) | (1 << 26)) +#define ADM8211_SYNRF_WRITE_CLOCK_0 (1 << 31) +#define ADM8211_SYNRF_WRITE_CLOCK_1 ((1 << 31) | (1 << 27)) + +/* CSR44 - WEPCTL (WEP Control) */ +#define ADM8211_WEPCTL_WEPENABLE (1 << 31) +#define ADM8211_WEPCTL_WPAENABLE (1 << 30) +#define ADM8211_WEPCTL_CURRENT_TABLE (1 << 29) +#define ADM8211_WEPCTL_TABLE_WR (1 << 28) +#define ADM8211_WEPCTL_TABLE_RD (1 << 27) +#define ADM8211_WEPCTL_WEPRXBYP (1 << 25) +#define ADM8211_WEPCTL_SEL_WEPTABLE (1 << 23) +#define ADM8211_WEPCTL_ADDR (0x000001ff) + +/* CSR45 - WESK (Data Entry for Share/Individual Key) */ +#define ADM8211_WESK_DATA (0x0000ffff) + +/* FER (Function Event Register) */ +#define ADM8211_FER_INTR_EV_ENT (1 << 15) + + +/* Si4126 RF Synthesizer - Control Registers */ +#define SI4126_MAIN_CONF 0 +#define SI4126_PHASE_DET_GAIN 1 +#define SI4126_POWERDOWN 2 +#define SI4126_RF1_N_DIV 3 /* only Si4136 */ +#define SI4126_RF2_N_DIV 4 +#define SI4126_IF_N_DIV 5 +#define SI4126_RF1_R_DIV 6 /* only Si4136 */ +#define SI4126_RF2_R_DIV 7 +#define SI4126_IF_R_DIV 8 + +/* Main Configuration */ +#define SI4126_MAIN_XINDIV2 (1 << 6) +#define SI4126_MAIN_IFDIV ((1 << 11) | (1 << 10)) +/* Powerdown */ +#define SI4126_POWERDOWN_PDIB (1 << 1) +#define SI4126_POWERDOWN_PDRB (1 << 0) + + +/* RF3000 BBP - Control Port Registers */ +/* 0x00 - reserved */ +#define RF3000_MODEM_CTRL__RX_STATUS 0x01 +#define RF3000_CCA_CTRL 0x02 +#define RF3000_DIVERSITY__RSSI 0x03 +#define RF3000_RX_SIGNAL_FIELD 0x04 +#define RF3000_RX_LEN_MSB 0x05 +#define RF3000_RX_LEN_LSB 0x06 +#define RF3000_RX_SERVICE_FIELD 0x07 +#define RF3000_TX_VAR_GAIN__TX_LEN_EXT 0x11 +#define RF3000_TX_LEN_MSB 0x12 +#define RF3000_TX_LEN_LSB 0x13 +#define RF3000_LOW_GAIN_CALIB 0x14 +#define RF3000_HIGH_GAIN_CALIB 0x15 + +/* ADM8211 revisions */ +#define ADM8211_REV_AB 0x11 +#define ADM8211_REV_AF 0x15 +#define ADM8211_REV_BA 0x20 +#define ADM8211_REV_CA 0x30 + +struct adm8211_desc { + __le32 status; + __le32 length; + __le32 buffer1; + __le32 buffer2; +}; + +#define RDES0_STATUS_OWN (1 << 31) +#define RDES0_STATUS_ES (1 << 30) +#define RDES0_STATUS_SQL (1 << 29) +#define RDES0_STATUS_DE (1 << 28) +#define RDES0_STATUS_FS (1 << 27) +#define RDES0_STATUS_LS (1 << 26) +#define RDES0_STATUS_PCF (1 << 25) +#define RDES0_STATUS_SFDE (1 << 24) +#define RDES0_STATUS_SIGE (1 << 23) +#define RDES0_STATUS_CRC16E (1 << 22) +#define RDES0_STATUS_RXTOE (1 << 21) +#define RDES0_STATUS_CRC32E (1 << 20) +#define RDES0_STATUS_ICVE (1 << 19) +#define RDES0_STATUS_DA1 (1 << 17) +#define RDES0_STATUS_DA0 (1 << 16) +#define RDES0_STATUS_RXDR ((1 << 15) | (1 << 14) | (1 << 13) | (1 << 12)) +#define RDES0_STATUS_FL (0x00000fff) + +#define RDES1_CONTROL_RER (1 << 25) +#define RDES1_CONTROL_RCH (1 << 24) +#define RDES1_CONTROL_RBS2 (0x00fff000) +#define RDES1_CONTROL_RBS1 (0x00000fff) + +#define RDES1_STATUS_RSSI (0x0000007f) + + +#define TDES0_CONTROL_OWN (1 << 31) +#define TDES0_CONTROL_DONE (1 << 30) +#define TDES0_CONTROL_TXDR (0x0ff00000) + +#define TDES0_STATUS_OWN (1 << 31) +#define TDES0_STATUS_DONE (1 << 30) +#define TDES0_STATUS_ES (1 << 29) +#define TDES0_STATUS_TLT (1 << 28) +#define TDES0_STATUS_TRT (1 << 27) +#define TDES0_STATUS_TUF (1 << 26) +#define TDES0_STATUS_TRO (1 << 25) +#define TDES0_STATUS_SOFBR (1 << 24) +#define TDES0_STATUS_ACR (0x00000fff) + +#define TDES1_CONTROL_IC (1 << 31) +#define TDES1_CONTROL_LS (1 << 30) +#define TDES1_CONTROL_FS (1 << 29) +#define TDES1_CONTROL_TER (1 << 25) +#define TDES1_CONTROL_TCH (1 << 24) +#define TDES1_CONTROL_RBS2 (0x00fff000) +#define TDES1_CONTROL_RBS1 (0x00000fff) + +/* SRAM offsets */ +#define ADM8211_SRAM(x) (priv->revid < ADM8211_REV_BA ? \ + ADM8211_SRAM_A_ ## x : ADM8211_SRAM_B_ ## x) + +#define ADM8211_SRAM_INDIV_KEY 0x0000 +#define ADM8211_SRAM_A_SHARE_KEY 0x0160 +#define ADM8211_SRAM_B_SHARE_KEY 0x00c0 + +#define ADM8211_SRAM_A_SSID 0x0180 +#define ADM8211_SRAM_B_SSID 0x00d4 +#define ADM8211_SRAM_SSID ADM8211_SRAM(SSID) + +#define ADM8211_SRAM_A_SUPP_RATE 0x0191 +#define ADM8211_SRAM_B_SUPP_RATE 0x00dd +#define ADM8211_SRAM_SUPP_RATE ADM8211_SRAM(SUPP_RATE) + +#define ADM8211_SRAM_A_SIZE 0x0200 +#define ADM8211_SRAM_B_SIZE 0x01c0 +#define ADM8211_SRAM_SIZE ADM8211_SRAM(SIZE) + +struct adm8211_rx_ring_info { + struct sk_buff *skb; + dma_addr_t mapping; +}; + +struct adm8211_tx_ring_info { + struct sk_buff *skb; + dma_addr_t mapping; + struct ieee80211_tx_status tx_status; + struct ieee80211_hdr hdr; +}; + +struct adm8211_eeprom { + __le16 signature; /* 0x00 */ + u8 major_version; /* 0x02 */ + u8 minor_version; /* 0x03 */ + u8 reserved_1[4]; /* 0x04 */ + u8 hwaddr[6]; /* 0x08 */ + u8 reserved_2[8]; /* 0x1E */ + __le16 cr49; /* 0x16 */ + u8 cr03; /* 0x18 */ + u8 cr28; /* 0x19 */ + u8 cr29; /* 0x1A */ + u8 country_code; /* 0x1B */ + +/* specific bbp types */ +#define ADM8211_BBP_RFMD3000 0x00 +#define ADM8211_BBP_RFMD3002 0x01 +#define ADM8211_BBP_ADM8011 0x04 + u8 specific_bbptype; /* 0x1C */ + u8 specific_rftype; /* 0x1D */ + u8 reserved_3[2]; /* 0x1E */ + __le16 device_id; /* 0x20 */ + __le16 vendor_id; /* 0x22 */ + __le16 subsystem_id; /* 0x24 */ + __le16 subsystem_vendor_id; /* 0x26 */ + u8 maxlat; /* 0x28 */ + u8 mingnt; /* 0x29 */ + __le16 cis_pointer_low; /* 0x2A */ + __le16 cis_pointer_high; /* 0x2C */ + __le16 csr18; /* 0x2E */ + u8 reserved_4[16]; /* 0x30 */ + u8 d1_pwrdara; /* 0x40 */ + u8 d0_pwrdara; /* 0x41 */ + u8 d3_pwrdara; /* 0x42 */ + u8 d2_pwrdara; /* 0x43 */ + u8 antenna_power[14]; /* 0x44 */ + __le16 cis_wordcnt; /* 0x52 */ + u8 tx_power[14]; /* 0x54 */ + u8 lpf_cutoff[14]; /* 0x62 */ + u8 lnags_threshold[14]; /* 0x70 */ + __le16 checksum; /* 0x7E */ + u8 cis_data[0]; /* 0x80, 384 bytes */ +} __attribute__ ((packed)); + +static const struct ieee80211_rate adm8211_rates[] = { + { .rate = 10, + .val = 10, + .val2 = -10, + .flags = IEEE80211_RATE_CCK_2 }, + { .rate = 20, + .val = 20, + .val2 = -20, + .flags = IEEE80211_RATE_CCK_2 }, + { .rate = 55, + .val = 55, + .val2 = -55, + .flags = IEEE80211_RATE_CCK_2 }, + { .rate = 110, + .val = 110, + .val2 = -110, + .flags = IEEE80211_RATE_CCK_2 } +}; + +struct ieee80211_chan_range { + u8 min; + u8 max; +}; + +struct adm8211_priv { + struct pci_dev *pdev; + spinlock_t lock; + struct adm8211_csr __iomem *map; + struct adm8211_desc *rx_ring; + struct adm8211_desc *tx_ring; + dma_addr_t rx_ring_dma; + dma_addr_t tx_ring_dma; + struct adm8211_rx_ring_info *rx_buffers; + struct adm8211_tx_ring_info *tx_buffers; + unsigned rx_ring_size, tx_ring_size; + unsigned cur_tx, dirty_tx, cur_rx; + + struct ieee80211_low_level_stats stats; + struct ieee80211_hw_mode modes[1]; + struct ieee80211_rate rates[ARRAY_SIZE(adm8211_rates)]; + int mode; + + int channel; + u8 bssid[ETH_ALEN]; + u8 ssid[32]; + size_t ssid_len; + u8 *mac_addr; + + u32 msg_enable; + + int (*eth_header_parse)(struct sk_buff *skb, unsigned char *haddr); + + u8 soft_rx_crc; + u8 retry_limit; + + u8 ant_power; + u8 tx_power; + u8 lpf_cutoff; + u8 lnags_threshold; + struct adm8211_eeprom *eeprom; + size_t eeprom_len; + + u8 revid; + + u32 nar; + +#define ADM8211_TYPE_INTERSIL 0x00 +#define ADM8211_TYPE_RFMD 0x01 +#define ADM8211_TYPE_MARVEL 0x02 +#define ADM8211_TYPE_AIROHA 0x03 +#define ADM8211_TYPE_ADMTEK 0x05 + unsigned int rf_type:3; + unsigned int bbp_type:3; + + u8 specific_bbptype; + enum { + ADM8211_RFMD2948 = 0x0, + ADM8211_RFMD2958 = 0x1, + ADM8211_RFMD2958_RF3000_CONTROL_POWER = 0x2, + ADM8211_MAX2820 = 0x8, + ADM8211_AL2210L = 0xC, /* Airoha */ + } transceiver_type; +}; + +static const struct ieee80211_chan_range cranges[] = { + {1, 11}, /* FCC */ + {1, 11}, /* IC */ + {1, 13}, /* ETSI */ + {10, 11}, /* SPAIN */ + {10, 13}, /* FRANCE */ + {14, 14}, /* MMK */ + {1, 14}, /* MMK2 */ +}; + +static const struct ieee80211_channel adm8211_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}, +}; + +#endif /* ADM8211_H */ diff --git a/drivers/net/wireless/mac80211/bcm43xx/Kconfig b/drivers/net/wireless/mac80211/bcm43xx/Kconfig new file mode 100644 index 0000000..8f9df0e --- /dev/null +++ b/drivers/net/wireless/mac80211/bcm43xx/Kconfig @@ -0,0 +1,101 @@ +config BCM43XX_MAC80211 + tristate "Broadcom BCM43xx wireless support (mac80211 stack)" + depends on MAC80211 && WLAN_80211 && EXPERIMENTAL + select FW_LOADER + select SSB + select HW_RANDOM + ---help--- + This is an experimental driver for the Broadcom 43xx wireless chip, + found in the Apple Airport Extreme and various other devices. + +config BCM43XX_MAC80211_PCI + bool "BCM43xx PCI device support" + depends on BCM43XX_MAC80211 && PCI + select SSB_PCIHOST + select SSB_DRIVER_PCICORE + default y + ---help--- + Broadcom 43xx PCI device support. + + Say Y, if you have a BCM43xx device connected through the PCI bus. + Please note that most PC-CARD devices are (to the kernel) PCI devices, + too and not PCMCIA. + It's safe to select Y here, even if you don't have a BCM43xx PCI device. + +config BCM43XX_MAC80211_PCMCIA + bool "BCM43xx PCMCIA device support" + depends on BCM43XX_MAC80211 && PCMCIA + select SSB_PCMCIAHOST + ---help--- + Broadcom 43xx PCMCIA device support. + + Support for 16bit PCMCIA devices. + Please note that most PC-CARD devices are _NOT_ 16bit PCMCIA + devices, but 32bit CardBUS devices. CardBUS devices are supported + by "BCM43xx PCI device support". + + With this config option you can drive bcm43xx cards in + CompactFlash formfactor in a PCMCIA adaptor. + CF bcm43xx cards can sometimes be found in handheld PCs. + + It's safe to select Y here, even if you don't have a BCM43xx PCMCIA device. + + If unsure, say N. + +config BCM43XX_MAC80211_DEBUG + bool "Broadcom BCM43xx debugging (RECOMMENDED)" + depends on BCM43XX_MAC80211 + select SSB_DEBUG if !SSB_SILENT + default y + ---help--- + Broadcom 43xx debugging messages. + Say Y, because the driver is still very experimental and + this will help you get it running. + +config BCM43XX_MAC80211_DMA + bool + depends on BCM43XX_MAC80211 +config BCM43XX_MAC80211_PIO + bool + depends on BCM43XX_MAC80211 + +choice + prompt "BCM43xx data transfer mode" + depends on BCM43XX_MAC80211 + default BCM43XX_MAC80211_DMA_AND_PIO_MODE + +config BCM43XX_MAC80211_DMA_AND_PIO_MODE + bool "DMA + PIO" + select BCM43XX_MAC80211_DMA + select BCM43XX_MAC80211_PIO + ---help--- + Include both, Direct Memory Access (DMA) and Programmed I/O (PIO) + data transfer modes. + The actually used mode is selectable through the module + parameter "pio". If the module parameter is pio=0, DMA is used. + Otherwise PIO is used. DMA is default. + + If unsure, choose this option. + +config BCM43XX_MAC80211_DMA_MODE + bool "DMA (Direct Memory Access) only" + select BCM43XX_MAC80211_DMA + ---help--- + Only include Direct Memory Access (DMA). + This reduces the size of the driver module, by omitting the PIO code. + +config BCM43XX_MAC80211_PIO_MODE + bool "PIO (Programmed I/O) only" + select BCM43XX_MAC80211_PIO + ---help--- + Only include Programmed I/O (PIO). + This reduces the size of the driver module, by omitting the DMA code. + Please note that PIO transfers are slow (compared to DMA). + + Also note that not all devices of the 43xx series support PIO. + The 4306 (Apple Airport Extreme and others) supports PIO, while + the 4318 is known to _not_ support PIO. + + Only use PIO, if DMA does not work for you. + +endchoice diff --git a/drivers/net/wireless/mac80211/bcm43xx/Makefile b/drivers/net/wireless/mac80211/bcm43xx/Makefile new file mode 100644 index 0000000..ce66b7b --- /dev/null +++ b/drivers/net/wireless/mac80211/bcm43xx/Makefile @@ -0,0 +1,18 @@ +obj-$(CONFIG_BCM43XX_MAC80211) += bcm43xx-mac80211.o + +bcm43xx-mac80211-obj-$(CONFIG_BCM43XX_MAC80211_PCMCIA) += bcm43xx_pcmcia.o + +bcm43xx-mac80211-obj-$(CONFIG_BCM43XX_MAC80211_DEBUG) += bcm43xx_debugfs.o + +bcm43xx-mac80211-obj-$(CONFIG_BCM43XX_MAC80211_DMA) += bcm43xx_dma.o +bcm43xx-mac80211-obj-$(CONFIG_BCM43XX_MAC80211_PIO) += bcm43xx_pio.o + +bcm43xx-mac80211-objs := bcm43xx_main.o \ + bcm43xx_tables.o \ + bcm43xx_phy.o \ + bcm43xx_power.o \ + bcm43xx_sysfs.o \ + bcm43xx_leds.o \ + bcm43xx_xmit.o \ + bcm43xx_lo.o \ + $(bcm43xx-mac80211-obj-y) diff --git a/drivers/net/wireless/mac80211/bcm43xx/bcm43xx.h b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx.h new file mode 100644 index 0000000..63eddaf --- /dev/null +++ b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx.h @@ -0,0 +1,885 @@ +#ifndef BCM43xx_H_ +#define BCM43xx_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "bcm43xx_debugfs.h" +#include "bcm43xx_leds.h" +#include "bcm43xx_lo.h" +#include "bcm43xx_phy.h" + + +#define PFX KBUILD_MODNAME ": " + +#define BCM43xx_IRQWAIT_MAX_RETRIES 50 + +#define BCM43xx_IO_SIZE 8192 + +#define BCM43xx_RX_MAX_SSI 60 + +/* MMIO offsets */ +#define BCM43xx_MMIO_DMA0_REASON 0x20 +#define BCM43xx_MMIO_DMA0_IRQ_MASK 0x24 +#define BCM43xx_MMIO_DMA1_REASON 0x28 +#define BCM43xx_MMIO_DMA1_IRQ_MASK 0x2C +#define BCM43xx_MMIO_DMA2_REASON 0x30 +#define BCM43xx_MMIO_DMA2_IRQ_MASK 0x34 +#define BCM43xx_MMIO_DMA3_REASON 0x38 +#define BCM43xx_MMIO_DMA3_IRQ_MASK 0x3C +#define BCM43xx_MMIO_DMA4_REASON 0x40 +#define BCM43xx_MMIO_DMA4_IRQ_MASK 0x44 +#define BCM43xx_MMIO_DMA5_REASON 0x48 +#define BCM43xx_MMIO_DMA5_IRQ_MASK 0x4C +#define BCM43xx_MMIO_MACCTL 0x120 +#define BCM43xx_MMIO_STATUS_BITFIELD 0x120//TODO replace all instances by MACCTL +#define BCM43xx_MMIO_STATUS2_BITFIELD 0x124 +#define BCM43xx_MMIO_GEN_IRQ_REASON 0x128 +#define BCM43xx_MMIO_GEN_IRQ_MASK 0x12C +#define BCM43xx_MMIO_RAM_CONTROL 0x130 +#define BCM43xx_MMIO_RAM_DATA 0x134 +#define BCM43xx_MMIO_PS_STATUS 0x140 +#define BCM43xx_MMIO_RADIO_HWENABLED_HI 0x158 +#define BCM43xx_MMIO_SHM_CONTROL 0x160 +#define BCM43xx_MMIO_SHM_DATA 0x164 +#define BCM43xx_MMIO_SHM_DATA_UNALIGNED 0x166 +#define BCM43xx_MMIO_XMITSTAT_0 0x170 +#define BCM43xx_MMIO_XMITSTAT_1 0x174 +#define BCM43xx_MMIO_REV3PLUS_TSF_LOW 0x180 /* core rev >= 3 only */ +#define BCM43xx_MMIO_REV3PLUS_TSF_HIGH 0x184 /* core rev >= 3 only */ + +/* 32-bit DMA */ +#define BCM43xx_MMIO_DMA32_BASE0 0x200 +#define BCM43xx_MMIO_DMA32_BASE1 0x220 +#define BCM43xx_MMIO_DMA32_BASE2 0x240 +#define BCM43xx_MMIO_DMA32_BASE3 0x260 +#define BCM43xx_MMIO_DMA32_BASE4 0x280 +#define BCM43xx_MMIO_DMA32_BASE5 0x2A0 +/* 64-bit DMA */ +#define BCM43xx_MMIO_DMA64_BASE0 0x200 +#define BCM43xx_MMIO_DMA64_BASE1 0x240 +#define BCM43xx_MMIO_DMA64_BASE2 0x280 +#define BCM43xx_MMIO_DMA64_BASE3 0x2C0 +#define BCM43xx_MMIO_DMA64_BASE4 0x300 +#define BCM43xx_MMIO_DMA64_BASE5 0x340 +/* PIO */ +#define BCM43xx_MMIO_PIO1_BASE 0x300 +#define BCM43xx_MMIO_PIO2_BASE 0x310 +#define BCM43xx_MMIO_PIO3_BASE 0x320 +#define BCM43xx_MMIO_PIO4_BASE 0x330 + +#define BCM43xx_MMIO_PHY_VER 0x3E0 +#define BCM43xx_MMIO_PHY_RADIO 0x3E2 +#define BCM43xx_MMIO_PHY0 0x3E6 +#define BCM43xx_MMIO_ANTENNA 0x3E8 +#define BCM43xx_MMIO_CHANNEL 0x3F0 +#define BCM43xx_MMIO_CHANNEL_EXT 0x3F4 +#define BCM43xx_MMIO_RADIO_CONTROL 0x3F6 +#define BCM43xx_MMIO_RADIO_DATA_HIGH 0x3F8 +#define BCM43xx_MMIO_RADIO_DATA_LOW 0x3FA +#define BCM43xx_MMIO_PHY_CONTROL 0x3FC +#define BCM43xx_MMIO_PHY_DATA 0x3FE +#define BCM43xx_MMIO_MACFILTER_CONTROL 0x420 +#define BCM43xx_MMIO_MACFILTER_DATA 0x422 +#define BCM43xx_MMIO_RCMTA_COUNT 0x43C +#define BCM43xx_MMIO_RADIO_HWENABLED_LO 0x49A +#define BCM43xx_MMIO_GPIO_CONTROL 0x49C +#define BCM43xx_MMIO_GPIO_MASK 0x49E +#define BCM43xx_MMIO_TSF_0 0x632 /* core rev < 3 only */ +#define BCM43xx_MMIO_TSF_1 0x634 /* core rev < 3 only */ +#define BCM43xx_MMIO_TSF_2 0x636 /* core rev < 3 only */ +#define BCM43xx_MMIO_TSF_3 0x638 /* core rev < 3 only */ +#define BCM43xx_MMIO_RNG 0x65A +#define BCM43xx_MMIO_POWERUP_DELAY 0x6A8 + +/* SPROM boardflags_lo values */ +#define BCM43xx_BFL_BTCOEXIST 0x0001 /* implements Bluetooth coexistance */ +#define BCM43xx_BFL_PACTRL 0x0002 /* GPIO 9 controlling the PA */ +#define BCM43xx_BFL_AIRLINEMODE 0x0004 /* implements GPIO 13 radio disable indication */ +#define BCM43xx_BFL_RSSI 0x0008 /* software calculates nrssi slope. */ +#define BCM43xx_BFL_ENETSPI 0x0010 /* has ephy roboswitch spi */ +#define BCM43xx_BFL_XTAL_NOSLOW 0x0020 /* no slow clock available */ +#define BCM43xx_BFL_CCKHIPWR 0x0040 /* can do high power CCK transmission */ +#define BCM43xx_BFL_ENETADM 0x0080 /* has ADMtek switch */ +#define BCM43xx_BFL_ENETVLAN 0x0100 /* can do vlan */ +#define BCM43xx_BFL_AFTERBURNER 0x0200 /* supports Afterburner mode */ +#define BCM43xx_BFL_NOPCI 0x0400 /* leaves PCI floating */ +#define BCM43xx_BFL_FEM 0x0800 /* supports the Front End Module */ +#define BCM43xx_BFL_EXTLNA 0x1000 /* has an external LNA */ +#define BCM43xx_BFL_HGPA 0x2000 /* had high gain PA */ +#define BCM43xx_BFL_BTCMOD 0x4000 /* BFL_BTCOEXIST is given in alternate GPIOs */ +#define BCM43xx_BFL_ALTIQ 0x8000 /* alternate I/Q settings */ + +/* GPIO register offset, in both ChipCommon and PCI core. */ +#define BCM43xx_GPIO_CONTROL 0x6c + +/* SHM Routing */ +enum { + BCM43xx_SHM_UCODE, /* Microcode memory */ + BCM43xx_SHM_SHARED, /* Shared memory */ + BCM43xx_SHM_SCRATCH, /* Scratch memory */ + BCM43xx_SHM_HW, /* Internal hardware register */ + BCM43xx_SHM_RCMTA, /* Receive match transmitter address (rev >= 5 only) */ +}; +/* SHM Routing modifiers */ +#define BCM43xx_SHM_AUTOINC_R 0x0200 /* Auto-increment address on read */ +#define BCM43xx_SHM_AUTOINC_W 0x0100 /* Auto-increment address on write */ +#define BCM43xx_SHM_AUTOINC_RW (BCM43xx_SHM_AUTOINC_R | \ + BCM43xx_SHM_AUTOINC_W) + +/* Misc SHM_SHARED offsets */ +#define BCM43xx_SHM_SH_WLCOREREV 0x0016 /* 802.11 core revision */ +#define BCM43xx_SHM_SH_PCTLWDPOS 0x0008 +#define BCM43xx_SHM_SH_RXPADOFF 0x0034 /* RX Padding data offset (PIO only) */ +#define BCM43xx_SHM_SH_PHYVER 0x0050 /* PHY version */ +#define BCM43xx_SHM_SH_PHYTYPE 0x0052 /* PHY type */ +#define BCM43xx_SHM_SH_ANTSWAP 0x005C /* Antenna swap threshold */ +#define BCM43xx_SHM_SH_HOSTFLO 0x005E /* Hostflags for ucode options (low) */ +#define BCM43xx_SHM_SH_HOSTFHI 0x0060 /* Hostflags for ucode options (high) */ +#define BCM43xx_SHM_SH_RADAR 0x0066 /* Radar register */ +#define BCM43xx_SHM_SH_PHYTXNOI 0x006E /* PHY noise directly after TX (lower 8bit only) */ +#define BCM43xx_SHM_SH_RFRXSP1 0x0072 /* RF RX SP Register 1 */ +#define BCM43xx_SHM_SH_CHAN 0x00A0 /* Current channel (low 8bit only) */ +#define BCM43xx_SHM_SH_CHAN_5GHZ 0x0100 /* Bit set, if 5Ghz channel */ +#define BCM43xx_SHM_SH_BCMCFIFOID 0x0108 /* Last posted cookie to the bcast/mcast FIFO */ +/* SHM_SHARED TX FIFO variables */ +#define BCM43xx_SHM_SH_SIZE01 0x0098 /* TX FIFO size for FIFO 0 (low) and 1 (high) */ +#define BCM43xx_SHM_SH_SIZE23 0x009A /* TX FIFO size for FIFO 2 and 3 */ +#define BCM43xx_SHM_SH_SIZE45 0x009C /* TX FIFO size for FIFO 4 and 5 */ +#define BCM43xx_SHM_SH_SIZE67 0x009E /* TX FIFO size for FIFO 6 and 7 */ +/* SHM_SHARED background noise */ +#define BCM43xx_SHM_SH_JSSI0 0x0088 /* Measure JSSI 0 */ +#define BCM43xx_SHM_SH_JSSI1 0x008A /* Measure JSSI 1 */ +#define BCM43xx_SHM_SH_JSSIAUX 0x008C /* Measure JSSI AUX */ +/* SHM_SHARED crypto engine */ +#define BCM43xx_SHM_SH_DEFAULTIV 0x003C /* Default IV location */ +#define BCM43xx_SHM_SH_NRRXTRANS 0x003E /* # of soft RX transmitter addresses (max 8) */ +#define BCM43xx_SHM_SH_KTP 0x0056 /* Key table pointer */ +#define BCM43xx_SHM_SH_TKIPTSCTTAK 0x0318 +#define BCM43xx_SHM_SH_KEYIDXBLOCK 0x05D4 /* Key index/algorithm block (v4 firmware) */ +#define BCM43xx_SHM_SH_PSM 0x05F4 /* PSM transmitter address match block (rev < 5) */ +/* SHM_SHARED WME variables */ +#define BCM43xx_SHM_SH_EDCFSTAT 0x000E /* EDCF status */ +#define BCM43xx_SHM_SH_TXFCUR 0x0030 /* TXF current index */ +#define BCM43xx_SHM_SH_EDCFQ 0x0240 /* EDCF Q info */ +/* SHM_SHARED powersave mode related */ +#define BCM43xx_SHM_SH_SLOTT 0x0010 /* Slot time */ +#define BCM43xx_SHM_SH_DTIMPER 0x0012 /* DTIM period */ +#define BCM43xx_SHM_SH_NOSLPZNATDTIM 0x004C /* NOSLPZNAT DTIM */ +/* SHM_SHARED beacon variables */ +#define BCM43xx_SHM_SH_BTL0 0x0018 /* Beacon template length 0 */ +#define BCM43xx_SHM_SH_BTL1 0x001A /* Beacon template length 1 */ +#define BCM43xx_SHM_SH_BTSFOFF 0x001C /* Beacon TSF offset */ +#define BCM43xx_SHM_SH_TIMBPOS 0x001E /* TIM B position in beacon */ +#define BCM43xx_SHM_SH_SFFBLIM 0x0044 /* Short frame fallback retry limit */ +#define BCM43xx_SHM_SH_LFFBLIM 0x0046 /* Long frame fallback retry limit */ +#define BCM43xx_SHM_SH_BEACPHYCTL 0x0054 /* Beacon PHY TX control word (see PHY TX control) */ +/* SHM_SHARED ACK/CTS control */ +#define BCM43xx_SHM_SH_ACKCTSPHYCTL 0x0022 /* ACK/CTS PHY control word (see PHY TX control) */ +/* SHM_SHARED probe response variables */ +#define BCM43xx_SHM_SH_PRSSID 0x0160 /* Probe Response SSID */ +#define BCM43xx_SHM_SH_PRSSIDLEN 0x0048 /* Probe Response SSID length */ +#define BCM43xx_SHM_SH_PRTLEN 0x004A /* Probe Response template length */ +#define BCM43xx_SHM_SH_PRMAXTIME 0x0074 /* Probe Response max time */ +#define BCM43xx_SHM_SH_PRPHYCTL 0x0188 /* Probe Response PHY TX control word */ +/* SHM_SHARED rate tables */ +#define BCM43xx_SHM_SH_OFDMDIRECT 0x01C0 /* Pointer to OFDM direct map */ +#define BCM43xx_SHM_SH_OFDMBASIC 0x01E0 /* Pointer to OFDM basic rate map */ +#define BCM43xx_SHM_SH_CCKDIRECT 0x0200 /* Pointer to CCK direct map */ +#define BCM43xx_SHM_SH_CCKBASIC 0x0220 /* Pointer to CCK basic rate map */ +/* SHM_SHARED microcode soft registers */ +#define BCM43xx_SHM_SH_UCODEREV 0x0000 /* Microcode revision */ +#define BCM43xx_SHM_SH_UCODEPATCH 0x0002 /* Microcode patchlevel */ +#define BCM43xx_SHM_SH_UCODEDATE 0x0004 /* Microcode date */ +#define BCM43xx_SHM_SH_UCODETIME 0x0006 /* Microcode time */ +#define BCM43xx_SHM_SH_UCODESTAT 0x0040 /* Microcode debug status code */ +#define BCM43xx_SHM_SH_UCODESTAT_INVALID 0 +#define BCM43xx_SHM_SH_UCODESTAT_INIT 1 +#define BCM43xx_SHM_SH_UCODESTAT_ACTIVE 2 +#define BCM43xx_SHM_SH_UCODESTAT_SUSP 3 /* suspended */ +#define BCM43xx_SHM_SH_UCODESTAT_SLEEP 4 /* asleep (PS) */ +#define BCM43xx_SHM_SH_MAXBFRAMES 0x0080 /* Maximum number of frames in a burst */ +#define BCM43xx_SHM_SH_SPUWKUP 0x0094 /* pre-wakeup for synth PU in us */ +#define BCM43xx_SHM_SH_PRETBTT 0x0096 /* pre-TBTT in us */ + +/* SHM_SCRATCH offsets */ +#define BCM43xx_SHM_SC_MINCONT 0x0003 /* Minimum contention window */ +#define BCM43xx_SHM_SC_MAXCONT 0x0004 /* Maximum contention window */ +#define BCM43xx_SHM_SC_CURCONT 0x0005 /* Current contention window */ +#define BCM43xx_SHM_SC_SRLIMIT 0x0006 /* Short retry count limit */ +#define BCM43xx_SHM_SC_LRLIMIT 0x0007 /* Long retry count limit */ +#define BCM43xx_SHM_SC_DTIMC 0x0008 /* Current DTIM count */ +#define BCM43xx_SHM_SC_BTL0LEN 0x0015 /* Beacon 0 template length */ +#define BCM43xx_SHM_SC_BTL1LEN 0x0016 /* Beacon 1 template length */ +#define BCM43xx_SHM_SC_SCFB 0x0017 /* Short frame transmit count threshold for rate fallback */ +#define BCM43xx_SHM_SC_LCFB 0x0018 /* Long frame transmit count threshold for rate fallback */ + + +/* Hardware Radio Enable masks */ +#define BCM43xx_MMIO_RADIO_HWENABLED_HI_MASK (1 << 16) +#define BCM43xx_MMIO_RADIO_HWENABLED_LO_MASK (1 << 4) + +/* HostFlags. See bcm43xx_hf_read/write() */ +#define BCM43xx_HF_ANTDIVHELP 0x00000001 /* ucode antenna div helper */ +#define BCM43xx_HF_SYMW 0x00000002 /* G-PHY SYM workaround */ +#define BCM43xx_HF_RXPULLW 0x00000004 /* RX pullup workaround */ +#define BCM43xx_HF_CCKBOOST 0x00000008 /* 4dB CCK power boost (exclusive with OFDM boost) */ +#define BCM43xx_HF_BTCOEX 0x00000010 /* Bluetooth coexistance */ +#define BCM43xx_HF_GDCW 0x00000020 /* G-PHY DV canceller filter bw workaround */ +#define BCM43xx_HF_OFDMPABOOST 0x00000040 /* Enable PA gain boost for OFDM */ +#define BCM43xx_HF_ACPR 0x00000080 /* Disable for Japan, channel 14 */ +#define BCM43xx_HF_EDCF 0x00000100 /* on if WME and MAC suspended */ +#define BCM43xx_HF_TSSIRPSMW 0x00000200 /* TSSI reset PSM ucode workaround */ +#define BCM43xx_HF_DSCRQ 0x00000400 /* Disable slow clock request in ucode */ +#define BCM43xx_HF_ACIW 0x00000800 /* ACI workaround: shift bits by 2 on PHY CRS */ +#define BCM43xx_HF_2060W 0x00001000 /* 2060 radio workaround */ +#define BCM43xx_HF_RADARW 0x00002000 /* Radar workaround */ +#define BCM43xx_HF_USEDEFKEYS 0x00004000 /* Enable use of default keys */ +#define BCM43xx_HF_BT4PRIOCOEX 0x00010000 /* Bluetooth 2-priority coexistance */ +#define BCM43xx_HF_FWKUP 0x00020000 /* Fast wake-up ucode */ +#define BCM43xx_HF_VCORECALC 0x00040000 /* Force VCO recalculation when powering up synthpu */ +#define BCM43xx_HF_PCISCW 0x00080000 /* PCI slow clock workaround */ +#define BCM43xx_HF_4318TSSI 0x00200000 /* 4318 TSSI */ +#define BCM43xx_HF_FBCMCFIFO 0x00400000 /* Flush bcast/mcast FIFO immediately */ +#define BCM43xx_HF_HWPCTL 0x00800000 /* Enable hardwarre power control */ +#define BCM43xx_HF_BTCOEXALT 0x01000000 /* Bluetooth coexistance in alternate pins */ +#define BCM43xx_HF_TXBTCHECK 0x02000000 /* Bluetooth check during transmission */ +#define BCM43xx_HF_SKCFPUP 0x04000000 /* Skip CFP update */ + + +/* MacFilter offsets. */ +#define BCM43xx_MACFILTER_SELF 0x0000 +#define BCM43xx_MACFILTER_ASSOC 0x0003 + +/* PowerControl */ +#define BCM43xx_PCTL_IN 0xB0 +#define BCM43xx_PCTL_OUT 0xB4 +#define BCM43xx_PCTL_OUTENABLE 0xB8 +#define BCM43xx_PCTL_XTAL_POWERUP 0x40 +#define BCM43xx_PCTL_PLL_POWERDOWN 0x80 + +/* PowerControl Clock Modes */ +#define BCM43xx_PCTL_CLK_FAST 0x00 +#define BCM43xx_PCTL_CLK_SLOW 0x01 +#define BCM43xx_PCTL_CLK_DYNAMIC 0x02 + +#define BCM43xx_PCTL_FORCE_SLOW 0x0800 +#define BCM43xx_PCTL_FORCE_PLL 0x1000 +#define BCM43xx_PCTL_DYN_XTAL 0x2000 + +/* PHYVersioning */ +#define BCM43xx_PHYTYPE_A 0x00 +#define BCM43xx_PHYTYPE_B 0x01 +#define BCM43xx_PHYTYPE_G 0x02 + +/* PHYRegisters */ +#define BCM43xx_PHY_ILT_A_CTRL 0x0072 +#define BCM43xx_PHY_ILT_A_DATA1 0x0073 +#define BCM43xx_PHY_ILT_A_DATA2 0x0074 +#define BCM43xx_PHY_G_LO_CONTROL 0x0810 +#define BCM43xx_PHY_ILT_G_CTRL 0x0472 +#define BCM43xx_PHY_ILT_G_DATA1 0x0473 +#define BCM43xx_PHY_ILT_G_DATA2 0x0474 +#define BCM43xx_PHY_A_PCTL 0x007B +#define BCM43xx_PHY_G_PCTL 0x0029 +#define BCM43xx_PHY_A_CRS 0x0029 +#define BCM43xx_PHY_RADIO_BITFIELD 0x0401 +#define BCM43xx_PHY_G_CRS 0x0429 +#define BCM43xx_PHY_NRSSILT_CTRL 0x0803 +#define BCM43xx_PHY_NRSSILT_DATA 0x0804 + +/* RadioRegisters */ +#define BCM43xx_RADIOCTL_ID 0x01 + +/* MAC Control bitfield */ +#define BCM43xx_MACCTL_ENABLED 0x00000001 /* MAC Enabled */ +#define BCM43xx_MACCTL_PSM_RUN 0x00000002 /* Run Microcode */ +#define BCM43xx_MACCTL_PSM_JMP0 0x00000004 /* Microcode jump to 0 */ +#define BCM43xx_MACCTL_SHM_ENABLED 0x00000100 /* SHM Enabled */ +#define BCM43xx_MACCTL_SHM_UPPER 0x00000200 /* SHM Upper */ +#define BCM43xx_MACCTL_IHR_ENABLED 0x00000400 /* IHR Region Enabled */ +#define BCM43xx_MACCTL_PSM_DBG 0x00002000 /* Microcode debugging enabled */ +#define BCM43xx_MACCTL_GPOUTSMSK 0x0000C000 /* GPOUT Select Mask */ +#define BCM43xx_MACCTL_BE 0x00010000 /* Big Endian mode */ +#define BCM43xx_MACCTL_INFRA 0x00020000 /* Infrastructure mode */ +#define BCM43xx_MACCTL_AP 0x00040000 /* AccessPoint mode */ +#define BCM43xx_MACCTL_RADIOLOCK 0x00080000 /* Radio lock */ +#define BCM43xx_MACCTL_BEACPROMISC 0x00100000 /* Beacon Promiscuous */ +#define BCM43xx_MACCTL_KEEP_BADPLCP 0x00200000 /* Keep frames with bad PLCP */ +#define BCM43xx_MACCTL_KEEP_CTL 0x00400000 /* Keep control frames */ +#define BCM43xx_MACCTL_KEEP_BAD 0x00800000 /* Keep bad frames (FCS) */ +#define BCM43xx_MACCTL_PROMISC 0x01000000 /* Promiscuous mode */ +#define BCM43xx_MACCTL_HWPS 0x02000000 /* Hardware Power Saving */ +#define BCM43xx_MACCTL_AWAKE 0x04000000 /* Device is awake */ +#define BCM43xx_MACCTL_CLOSEDNET 0x08000000 /* Closed net (no SSID bcast) */ +#define BCM43xx_MACCTL_TBTTHOLD 0x10000000 /* TBTT Hold */ +#define BCM43xx_MACCTL_DISCTXSTAT 0x20000000 /* Discard TX status */ +#define BCM43xx_MACCTL_DISCPMQ 0x40000000 /* Discard Power Management Queue */ +#define BCM43xx_MACCTL_GMODE 0x80000000 /* G Mode */ + +/* StatusBitField *///FIXME rename these all +#define BCM43xx_SBF_MAC_ENABLED 0x00000001 +#define BCM43xx_SBF_2 0x00000002 /*FIXME: fix name*/ +#define BCM43xx_SBF_CORE_READY 0x00000004 +#define BCM43xx_SBF_400 0x00000400 /*FIXME: fix name*/ +#define BCM43xx_SBF_4000 0x00004000 /*FIXME: fix name*/ +#define BCM43xx_SBF_8000 0x00008000 /*FIXME: fix name*/ +#define BCM43xx_SBF_XFER_REG_BYTESWAP 0x00010000 +#define BCM43xx_SBF_MODE_NOTADHOC 0x00020000 +#define BCM43xx_SBF_MODE_AP 0x00040000 +#define BCM43xx_SBF_RADIOREG_LOCK 0x00080000 +#define BCM43xx_SBF_MODE_MONITOR 0x00400000 +#define BCM43xx_SBF_MODE_PROMISC 0x01000000 +#define BCM43xx_SBF_PS1 0x02000000 +#define BCM43xx_SBF_PS2 0x04000000 +#define BCM43xx_SBF_NO_SSID_BCAST 0x08000000 +#define BCM43xx_SBF_TIME_UPDATE 0x10000000 +#define BCM43xx_SBF_80000000 0x80000000 /*FIXME: fix name*/ + +/* 802.11 core specific TM State Low flags */ +#define BCM43xx_TMSLOW_GMODE 0x20000000 /* G Mode Enable */ +#define BCM43xx_TMSLOW_PLLREFSEL 0x00200000 /* PLL Frequency Reference Select */ +#define BCM43xx_TMSLOW_MACPHYCLKEN 0x00100000 /* MAC PHY Clock Control Enable (rev >= 5) */ +#define BCM43xx_TMSLOW_PHYRESET 0x00080000 /* PHY Reset */ +#define BCM43xx_TMSLOW_PHYCLKEN 0x00040000 /* PHY Clock Enable */ + +/* 802.11 core specific TM State High flags */ +#define BCM43xx_TMSHIGH_FCLOCK 0x00040000 /* Fast Clock Available (rev >= 5)*/ +#define BCM43xx_TMSHIGH_APHY 0x00020000 /* A-PHY available (rev >= 5) */ +#define BCM43xx_TMSHIGH_GPHY 0x00010000 /* G-PHY available (rev >= 5) */ + +/* Generic-Interrupt reasons. */ +#define BCM43xx_IRQ_MAC_SUSPENDED 0x00000001 +#define BCM43xx_IRQ_BEACON 0x00000002 +#define BCM43xx_IRQ_TBTT_INDI 0x00000004 +#define BCM43xx_IRQ_BEACON_TX_OK 0x00000008 +#define BCM43xx_IRQ_BEACON_CANCEL 0x00000010 +#define BCM43xx_IRQ_ATIM_END 0x00000020 +#define BCM43xx_IRQ_PMQ 0x00000040 +#define BCM43xx_IRQ_PIO_WORKAROUND 0x00000100 +#define BCM43xx_IRQ_MAC_TXERR 0x00000200 +#define BCM43xx_IRQ_PHY_TXERR 0x00000800 +#define BCM43xx_IRQ_PMEVENT 0x00001000 +#define BCM43xx_IRQ_TIMER0 0x00002000 +#define BCM43xx_IRQ_TIMER1 0x00004000 +#define BCM43xx_IRQ_DMA 0x00008000 +#define BCM43xx_IRQ_TXFIFO_FLUSH_OK 0x00010000 +#define BCM43xx_IRQ_CCA_MEASURE_OK 0x00020000 +#define BCM43xx_IRQ_NOISESAMPLE_OK 0x00040000 +#define BCM43xx_IRQ_UCODE_DEBUG 0x08000000 +#define BCM43xx_IRQ_RFKILL 0x10000000 +#define BCM43xx_IRQ_TX_OK 0x20000000 +#define BCM43xx_IRQ_PHY_G_CHANGED 0x40000000 +#define BCM43xx_IRQ_TIMEOUT 0x80000000 + +#define BCM43xx_IRQ_ALL 0xFFFFFFFF +#define BCM43xx_IRQ_MASKTEMPLATE (BCM43xx_IRQ_MAC_SUSPENDED | \ + BCM43xx_IRQ_BEACON | \ + BCM43xx_IRQ_TBTT_INDI | \ + BCM43xx_IRQ_ATIM_END | \ + BCM43xx_IRQ_PMQ | \ + BCM43xx_IRQ_MAC_TXERR | \ + BCM43xx_IRQ_PHY_TXERR | \ + BCM43xx_IRQ_DMA | \ + BCM43xx_IRQ_TXFIFO_FLUSH_OK | \ + BCM43xx_IRQ_NOISESAMPLE_OK | \ + BCM43xx_IRQ_UCODE_DEBUG | \ + BCM43xx_IRQ_RFKILL | \ + BCM43xx_IRQ_TX_OK) + +/* Device specific rate values. + * The actual values defined here are (rate_in_mbps * 2). + * Some code depends on this. Don't change it. */ +#define BCM43xx_CCK_RATE_1MB 0x02 +#define BCM43xx_CCK_RATE_2MB 0x04 +#define BCM43xx_CCK_RATE_5MB 0x0B +#define BCM43xx_CCK_RATE_11MB 0x16 +#define BCM43xx_OFDM_RATE_6MB 0x0C +#define BCM43xx_OFDM_RATE_9MB 0x12 +#define BCM43xx_OFDM_RATE_12MB 0x18 +#define BCM43xx_OFDM_RATE_18MB 0x24 +#define BCM43xx_OFDM_RATE_24MB 0x30 +#define BCM43xx_OFDM_RATE_36MB 0x48 +#define BCM43xx_OFDM_RATE_48MB 0x60 +#define BCM43xx_OFDM_RATE_54MB 0x6C +/* Convert a bcm43xx rate value to a rate in 100kbps */ +#define BCM43xx_RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2) + + +#define BCM43xx_DEFAULT_SHORT_RETRY_LIMIT 7 +#define BCM43xx_DEFAULT_LONG_RETRY_LIMIT 4 + +/* Max size of a security key */ +#define BCM43xx_SEC_KEYSIZE 16 +/* Security algorithms. */ +enum { + BCM43xx_SEC_ALGO_NONE = 0, /* unencrypted, as of TX header. */ + BCM43xx_SEC_ALGO_WEP40, + BCM43xx_SEC_ALGO_TKIP, + BCM43xx_SEC_ALGO_AES, + BCM43xx_SEC_ALGO_WEP104, + BCM43xx_SEC_ALGO_AES_LEGACY, +}; + + +#ifdef assert +# undef assert +#endif +#ifdef CONFIG_BCM43XX_MAC80211_DEBUG +# define assert(expr) \ + do { \ + if (unlikely(!(expr))) { \ + printk(KERN_ERR PFX "ASSERTION FAILED (%s) at: %s:%d:%s()\n", \ + #expr, __FILE__, __LINE__, __FUNCTION__); \ + } \ + } while (0) +# define BCM43xx_DEBUG 1 +#else +# define assert(expr) do { /* nothing */ } while (0) +# define BCM43xx_DEBUG 0 +#endif + +/* rate limited printk(). */ +#ifdef printkl +# undef printkl +#endif +#define printkl(f, x...) do { if (printk_ratelimit()) printk(f ,##x); } while (0) +/* rate limited printk() for debugging */ +#ifdef dprintkl +# undef dprintkl +#endif +#ifdef CONFIG_BCM43XX_MAC80211_DEBUG +# define dprintkl printkl +#else +# define dprintkl(f, x...) do { /* nothing */ } while (0) +#endif + +/* debugging printk() */ +#ifdef dprintk +# undef dprintk +#endif +#ifdef CONFIG_BCM43XX_MAC80211_DEBUG +# define dprintk(f, x...) do { printk(f ,##x); } while (0) +#else +# define dprintk(f, x...) do { /* nothing */ } while (0) +#endif + + +struct net_device; +struct pci_dev; +struct bcm43xx_dmaring; +struct bcm43xx_pioqueue; + +struct bcm43xx_initval { + u16 offset; + u16 size; + u32 value; +} __attribute__((__packed__)); + +#define BCM43xx_PHYMODE(phytype) (1 << (phytype)) +#define BCM43xx_PHYMODE_A BCM43xx_PHYMODE(BCM43xx_PHYTYPE_A) +#define BCM43xx_PHYMODE_B BCM43xx_PHYMODE(BCM43xx_PHYTYPE_B) +#define BCM43xx_PHYMODE_G BCM43xx_PHYMODE(BCM43xx_PHYTYPE_G) + +struct bcm43xx_phy { + /* Possible PHYMODEs on this PHY */ + u8 possible_phymodes; + /* GMODE bit enabled? */ + u8 gmode; + /* Possible ieee80211 subsystem hwmodes for this PHY. + * Which mode is selected, depends on thr GMODE enabled bit */ +#define BCM43xx_MAX_PHYHWMODES 2 + struct ieee80211_hw_mode hwmodes[BCM43xx_MAX_PHYHWMODES]; + + /* Analog Type */ + u8 analog; + /* BCM43xx_PHYTYPE_ */ + u8 type; + /* PHY revision number. */ + u8 rev; + + /* Radio versioning */ + u16 radio_manuf; /* Radio manufacturer */ + u16 radio_ver; /* Radio version */ + u8 radio_rev; /* Radio revision */ + + u8 radio_on:1; /* Radio switched on/off */ + u8 locked:1; /* Only used in bcm43xx_phy_{un}lock() */ + u8 dyn_tssi_tbl:1; /* tssi2dbm is kmalloc()ed. */ + + /* ACI (adjacent channel interference) flags. */ + u8 aci_enable:1; + u8 aci_wlan_automatic:1; + u8 aci_hw_rssi:1; + + u16 minlowsig[2]; + u16 minlowsigpos[2]; + + /* TSSI to dBm table in use */ + const s8 *tssi2dbm; + /* Target idle TSSI */ + int tgt_idle_tssi; + /* Current idle TSSI */ + int cur_idle_tssi; + + /* LocalOscillator control values. */ + struct bcm43xx_txpower_lo_control *lo_control; + /* Values from bcm43xx_calc_loopback_gain() */ + s16 max_lb_gain; /* Maximum Loopback gain in hdB */ + s16 trsw_rx_gain; /* TRSW RX gain in hdB */ + s16 lna_lod_gain; /* LNA lod */ + s16 lna_gain; /* LNA */ + s16 pga_gain; /* PGA */ + + /* PHY lock for core.rev < 3 + * This lock is only used by bcm43xx_phy_{un}lock() + */ + spinlock_t lock; + + /* Desired TX power level (in dBm). + * This is set by the user and adjusted in bcm43xx_phy_xmitpower(). */ + u8 power_level; + /* TX Power control values. */ + /* B/G PHY */ + struct { + /* Current Radio Attenuation for TXpower recalculation. */ + u16 rfatt; + /* Current Baseband Attenuation for TXpower recalculation. */ + u16 bbatt; + /* Current TXpower control value for TXpower recalculation. */ + u16 txctl1; + }; + /* A PHY */ + struct { + u16 txpwr_offset; + }; + + /* Current Interference Mitigation mode */ + int interfmode; + /* Stack of saved values from the Interference Mitigation code. + * Each value in the stack is layed out as follows: + * bit 0-11: offset + * bit 12-15: register ID + * bit 16-32: value + * register ID is: 0x1 PHY, 0x2 Radio, 0x3 ILT + */ +#define BCM43xx_INTERFSTACK_SIZE 26 + u32 interfstack[BCM43xx_INTERFSTACK_SIZE];//FIXME: use a data structure + + /* Saved values from the NRSSI Slope calculation */ + s16 nrssi[2]; + s32 nrssislope; + /* In memory nrssi lookup table. */ + s8 nrssi_lt[64]; + + /* current channel */ + u8 channel; + + u16 lofcal; + + u16 initval;//FIXME rename? +}; + +/* Data structures for DMA transmission, per 80211 core. */ +struct bcm43xx_dma { + struct bcm43xx_dmaring *tx_ring0; + struct bcm43xx_dmaring *tx_ring1; + struct bcm43xx_dmaring *tx_ring2; + struct bcm43xx_dmaring *tx_ring3; + struct bcm43xx_dmaring *tx_ring4; + struct bcm43xx_dmaring *tx_ring5; + + struct bcm43xx_dmaring *rx_ring0; + struct bcm43xx_dmaring *rx_ring3; /* only available on core.rev < 5 */ +}; + +/* Data structures for PIO transmission, per 80211 core. */ +struct bcm43xx_pio { + struct bcm43xx_pioqueue *queue0; + struct bcm43xx_pioqueue *queue1; + struct bcm43xx_pioqueue *queue2; + struct bcm43xx_pioqueue *queue3; +}; + +/* Context information for a noise calculation (Link Quality). */ +struct bcm43xx_noise_calculation { + u8 channel_at_start; + u8 calculation_running:1; + u8 nr_samples; + s8 samples[8][4]; +}; + +struct bcm43xx_stats { + u8 link_noise; + /* Store the last TX/RX times here for updating the leds. */ + unsigned long last_tx; + unsigned long last_rx; +}; + +struct bcm43xx_key { + u8 enabled; + u8 algorithm; + u8 address[6]; +}; + +struct bcm43xx_wldev; + +/* Data structure for the WLAN parts (802.11 cores) of the bcm43xx chip. */ +struct bcm43xx_wl { + /* Pointer to the active wireless device on this chip */ + struct bcm43xx_wldev *current_dev; + /* Pointer to the ieee80211 hardware data structure */ + struct ieee80211_hw *hw; + + spinlock_t irq_lock; + struct mutex mutex; + spinlock_t leds_lock; + + /* We can only have one operating interface (802.11 core) + * at a time. General information about this interface follows. + */ + + /* Opaque ID of the operating interface (!= monitor + * interface) from the ieee80211 subsystem. + * Do not modify. + */ + int if_id; + /* MAC address. */ + u8 *mac_addr; + /* Current BSSID (if any). */ + u8 *bssid; + /* Interface type. (IEEE80211_IF_TYPE_XXX) */ + int if_type; + /* Counter of active monitor interfaces. */ + int monitor; + /* Is the card operating in AP, STA or IBSS mode? */ + unsigned int operating:1; + /* Promisc mode active? + * Note that (monitor != 0) implies promisc. + */ + unsigned int promisc:1; + /* Stats about the wireless interface */ + struct ieee80211_low_level_stats ieee_stats; + + struct hwrng rng; + u8 rng_initialized; + char rng_name[30 + 1]; + + /* List of all wireless devices on this chip */ + struct list_head devlist; + u8 nr_devs; +}; + +/* Pointers to the firmware data and meta information about it. */ +struct bcm43xx_firmware { + /* Microcode */ + const struct firmware *ucode; + /* PCM code */ + const struct firmware *pcm; + /* Initial MMIO values 0 */ + const struct firmware *initvals0; + /* Initial MMIO values 1 */ + const struct firmware *initvals1; + /* Firmware revision */ + u16 rev; + /* Firmware patchlevel */ + u16 patch; +}; + +/* Device (802.11 core) initialization status. */ +enum { + BCM43xx_STAT_UNINIT, /* Uninitialized. */ + BCM43xx_STAT_INITIALIZING, /* bcm43xx_wireless_core_init() in progress. */ + BCM43xx_STAT_INITIALIZED, /* Initialized. Note that this doesn't mean it's started. */ +}; +#define bcm43xx_status(bcm) atomic_read(&(bcm)->init_status) +#define bcm43xx_set_status(bcm, stat) do { \ + atomic_set(&(bcm)->init_status, (stat)); \ + smp_wmb(); \ + } while (0) + +/* XXX--- HOW LOCKING WORKS IN BCM43xx ---XXX + * + * You should always acquire both, wl->mutex and wl->irq_lock unless: + * - You don't need to acquire wl->irq_lock, if the interface is stopped. + * - You don't need to acquire wl->mutex in the IRQ handler, IRQ tasklet + * and packet TX path (and _ONLY_ there.) + */ + +/* Data structure for one wireless device (802.11 core) */ +struct bcm43xx_wldev { + struct ssb_device *dev; + struct bcm43xx_wl *wl; + + /* Driver initialization status BCM43xx_STAT_*** */ + atomic_t init_status; + /* Interface started? (bcm43xx_wireless_core_start()) */ + u8 started; + + u16 was_initialized:1, /* for suspend/resume. */ + was_started:1, /* for suspend/resume. */ + __using_pio:1, /* Internal, use bcm43xx_using_pio(). */ + bad_frames_preempt:1, /* Use "Bad Frames Preemption" (default off) */ + reg124_set_0x4:1, /* Some variable to keep track of IRQ stuff. */ + short_preamble:1, /* TRUE, if short preamble is enabled. */ + short_slot:1, /* TRUE, if short slot timing is enabled. */ + radio_hw_enable:1; /* saved state of radio hardware enabled state */ + + /* PHY/Radio device. */ + struct bcm43xx_phy phy; + union { + /* DMA engines. */ + struct bcm43xx_dma dma; + /* PIO engines. */ + struct bcm43xx_pio pio; + }; + + /* Various statistics about the physical device. */ + struct bcm43xx_stats stats; + +#define BCM43xx_NR_LEDS 4 + struct bcm43xx_led leds[BCM43xx_NR_LEDS]; + + /* Reason code of the last interrupt. */ + u32 irq_reason; + u32 dma_reason[6]; + /* saved irq enable/disable state bitfield. */ + u32 irq_savedstate; + /* Link Quality calculation context. */ + struct bcm43xx_noise_calculation noisecalc; + /* if > 0 MAC is suspended. if == 0 MAC is enabled. */ + int mac_suspended; + + /* Interrupt Service Routine tasklet (bottom-half) */ + struct tasklet_struct isr_tasklet; + + /* Periodic tasks */ + struct delayed_work periodic_work; + unsigned int periodic_state; + + struct work_struct restart_work; + + /* encryption/decryption */ + u16 ktp; /* Key table pointer */ + u8 max_nr_keys; + struct bcm43xx_key key[58]; + + /* Cached beacon template while uploading the template. */ + struct sk_buff *cached_beacon; + + /* Firmware data */ + struct bcm43xx_firmware fw; + + /* Devicelist in struct bcm43xx_wl (all 802.11 cores) */ + struct list_head list; + + /* Debugging stuff follows. */ +#ifdef CONFIG_BCM43XX_MAC80211_DEBUG + struct bcm43xx_dfsentry *dfsentry; +#endif +}; + + +static inline +struct bcm43xx_wl * hw_to_bcm43xx_wl(struct ieee80211_hw *hw) +{ + return hw->priv; +} + +/* Helper function, which returns a boolean. + * TRUE, if PIO is used; FALSE, if DMA is used. + */ +#if defined(CONFIG_BCM43XX_MAC80211_DMA) && defined(CONFIG_BCM43XX_MAC80211_PIO) +static inline +int bcm43xx_using_pio(struct bcm43xx_wldev *dev) +{ + return dev->__using_pio; +} +#elif defined(CONFIG_BCM43XX_MAC80211_DMA) +static inline +int bcm43xx_using_pio(struct bcm43xx_wldev *dev) +{ + return 0; +} +#elif defined(CONFIG_BCM43XX_MAC80211_PIO) +static inline +int bcm43xx_using_pio(struct bcm43xx_wldev *dev) +{ + return 1; +} +#else +# error "Using neither DMA nor PIO? Confused..." +#endif + + +static inline +struct bcm43xx_wldev * dev_to_bcm43xx_wldev(struct device *dev) +{ + struct ssb_device *ssb_dev = dev_to_ssb_dev(dev); + return ssb_get_drvdata(ssb_dev); +} + +/* Is the device operating in a specified mode (IEEE80211_IF_TYPE_XXX). */ +static inline +int bcm43xx_is_mode(struct bcm43xx_wl *wl, int type) +{ + if (type == IEEE80211_IF_TYPE_MNTR) + return !!(wl->monitor); + return (wl->operating && + wl->if_type == type); +} + +static inline +u16 bcm43xx_read16(struct bcm43xx_wldev *dev, u16 offset) +{ + return ssb_read16(dev->dev, offset); +} + +static inline +void bcm43xx_write16(struct bcm43xx_wldev *dev, u16 offset, u16 value) +{ + ssb_write16(dev->dev, offset, value); +} + +static inline +u32 bcm43xx_read32(struct bcm43xx_wldev *dev, u16 offset) +{ + return ssb_read32(dev->dev, offset); +} + +static inline +void bcm43xx_write32(struct bcm43xx_wldev *dev, u16 offset, u32 value) +{ + ssb_write32(dev->dev, offset, value); +} + +/** Limit a value between two limits */ +#ifdef limit_value +# undef limit_value +#endif +#define limit_value(value, min, max) \ + ({ \ + typeof(value) __value = (value); \ + typeof(value) __min = (min); \ + typeof(value) __max = (max); \ + if (__value < __min) \ + __value = __min; \ + else if (__value > __max) \ + __value = __max; \ + __value; \ + }) + +#endif /* BCM43xx_H_ */ diff --git a/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_debugfs.c b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_debugfs.c new file mode 100644 index 0000000..b24bf63 --- /dev/null +++ b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_debugfs.c @@ -0,0 +1,433 @@ +/* + + Broadcom BCM43xx wireless driver + + debugfs driver debugging code + + Copyright (c) 2005 Michael Buesch + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + + + +#include +#include +#include +#include +#include +#include + +#include "bcm43xx.h" +#include "bcm43xx_main.h" +#include "bcm43xx_debugfs.h" +#include "bcm43xx_dma.h" +#include "bcm43xx_pio.h" +#include "bcm43xx_xmit.h" + +#define REALLY_BIG_BUFFER_SIZE (1024*256) + +static struct bcm43xx_debugfs fs; +static char big_buffer[1024*256]; +static DEFINE_MUTEX(big_buffer_mutex); + + +static ssize_t write_file_dummy(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + return count; +} + +static int open_file_generic(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +#define fappend(fmt, x...) pos += snprintf(buf + pos, len - pos, fmt , ##x) + +static ssize_t drvinfo_read_file(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + const size_t len = ARRAY_SIZE(big_buffer); + char *buf = big_buffer; + size_t pos = 0; + ssize_t res; + + mutex_lock(&big_buffer_mutex); + /* This is where the information is written to the "driver" file */ + fappend(KBUILD_MODNAME " driver\n"); + fappend("Compiled at: %s %s\n", __DATE__, __TIME__); + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + mutex_unlock(&big_buffer_mutex); + + return res; +} + +static ssize_t tsf_read_file(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct bcm43xx_wldev *dev = file->private_data; + const size_t len = ARRAY_SIZE(big_buffer); + char *buf = big_buffer; + size_t pos = 0; + ssize_t res; + unsigned long flags; + u64 tsf; + + mutex_lock(&big_buffer_mutex); + mutex_lock(&dev->wl->mutex); + spin_lock_irqsave(&dev->wl->irq_lock, flags); + if (bcm43xx_status(dev) != BCM43xx_STAT_INITIALIZED) { + fappend("Board not initialized.\n"); + goto out; + } + bcm43xx_tsf_read(dev, &tsf); + fappend("0x%08x%08x\n", + (unsigned int)((tsf & 0xFFFFFFFF00000000ULL) >> 32), + (unsigned int)(tsf & 0xFFFFFFFFULL)); + +out: + spin_unlock_irqrestore(&dev->wl->irq_lock, flags); + mutex_unlock(&dev->wl->mutex); + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + mutex_unlock(&big_buffer_mutex); + + return res; +} + +static ssize_t tsf_write_file(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct bcm43xx_wldev *dev = file->private_data; + char *buf = big_buffer; + ssize_t buf_size; + ssize_t res; + unsigned long flags; + u64 tsf; + + mutex_lock(&big_buffer_mutex); + buf_size = min(count, ARRAY_SIZE(big_buffer) - 1); + if (copy_from_user(buf, user_buf, buf_size)) { + res = -EFAULT; + goto out_unlock_bb; + } + mutex_lock(&dev->wl->mutex); + spin_lock_irqsave(&dev->wl->irq_lock, flags); + if (bcm43xx_status(dev) != BCM43xx_STAT_INITIALIZED) { + printk(KERN_INFO PFX "debugfs: Board not initialized.\n"); + res = -EFAULT; + goto out_unlock; + } + if (sscanf(buf, "%llu", (unsigned long long *)(&tsf)) != 1) { + printk(KERN_INFO PFX "debugfs: invalid values for \"tsf\"\n"); + res = -EINVAL; + goto out_unlock; + } + bcm43xx_tsf_write(dev, tsf); + mmiowb(); + res = buf_size; + +out_unlock: + spin_unlock_irqrestore(&dev->wl->irq_lock, flags); + mutex_unlock(&dev->wl->mutex); +out_unlock_bb: + mutex_unlock(&big_buffer_mutex); + + return res; +} + +static ssize_t txstat_read_file(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct bcm43xx_wldev *dev = file->private_data; + struct bcm43xx_dfsentry *e = dev->dfsentry; + struct bcm43xx_txstatus_log *log = &e->txstatlog; + unsigned long flags; + char *buf = log->printbuf; + const size_t len = ARRAY_SIZE(log->printbuf); + size_t pos = 0; + ssize_t res; + int i, idx; + struct bcm43xx_txstatus *stat; + + mutex_lock(&big_buffer_mutex); + spin_lock_irqsave(&log->lock, flags); + if (!log->printing) { + log->printing = 1; + fappend("bcm43xx TX status reports:\n\n" + "index | cookie | seq | phy_stat | frame_count | " + "rts_count | supp_reason | pm_indicated | " + "intermediate | for_ampdu | acked\n" + "---\n"); + i = log->end + 1; + idx = 0; + while (1) { + if (i == BCM43xx_NR_LOGGED_TXSTATUS) + i = 0; + stat = &(log->log[i]); + if (stat->cookie) { + fappend("%03d | " + "0x%04X | 0x%04X | 0x%02X | " + "0x%X | 0x%X | " + "%u | %u | " + "%u | %u | %u\n", + idx, + stat->cookie, stat->seq, stat->phy_stat, + stat->frame_count, stat->rts_count, + stat->supp_reason, stat->pm_indicated, + stat->intermediate, stat->for_ampdu, + stat->acked); + idx++; + } + if (i == log->end) + break; + i++; + } + log->buf_avail = pos; + } + memcpy(big_buffer, buf, + min(log->buf_avail, ARRAY_SIZE(big_buffer))); + spin_unlock_irqrestore(&log->lock, flags); + + res = simple_read_from_buffer(userbuf, count, ppos, + big_buffer, + log->buf_avail); + if (*ppos == log->buf_avail) { + spin_lock_irqsave(&log->lock, flags); + log->printing = 0; + spin_unlock_irqrestore(&log->lock, flags); + } + mutex_unlock(&big_buffer_mutex); + + return res; +} + +static ssize_t restart_write_file(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct bcm43xx_wldev *dev = file->private_data; + char *buf = big_buffer; + ssize_t buf_size; + ssize_t res; + unsigned long flags; + + mutex_lock(&big_buffer_mutex); + buf_size = min(count, ARRAY_SIZE(big_buffer) - 1); + if (copy_from_user(buf, user_buf, buf_size)) { + res = -EFAULT; + goto out_unlock_bb; + } + mutex_lock(&dev->wl->mutex); + spin_lock_irqsave(&dev->wl->irq_lock, flags); + if (bcm43xx_status(dev) != BCM43xx_STAT_INITIALIZED) { + printk(KERN_INFO PFX "debugfs: Board not initialized.\n"); + res = -EFAULT; + goto out_unlock; + } + if (count > 0 && buf[0] == '1') { + bcm43xx_controller_restart(dev, "manually restarted"); + res = count; + } else + res = -EINVAL; + +out_unlock: + spin_unlock_irqrestore(&dev->wl->irq_lock, flags); + mutex_unlock(&dev->wl->mutex); +out_unlock_bb: + mutex_unlock(&big_buffer_mutex); + + return res; +} + +#undef fappend + + +static struct file_operations drvinfo_fops = { + .read = drvinfo_read_file, + .write = write_file_dummy, + .open = open_file_generic, +}; + +static struct file_operations tsf_fops = { + .read = tsf_read_file, + .write = tsf_write_file, + .open = open_file_generic, +}; + +static struct file_operations txstat_fops = { + .read = txstat_read_file, + .write = write_file_dummy, + .open = open_file_generic, +}; + +static struct file_operations restart_fops = { + .write = restart_write_file, + .open = open_file_generic, +}; + + +void bcm43xx_debugfs_add_device(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_dfsentry *e; + struct bcm43xx_txstatus_log *log; + char devdir[16]; + + assert(dev); + e = kzalloc(sizeof(*e), GFP_KERNEL); + if (!e) { + printk(KERN_ERR PFX "out of memory\n"); + return; + } + e->dev = dev; + log = &e->txstatlog; + log->log = kcalloc(BCM43xx_NR_LOGGED_TXSTATUS, + sizeof(struct bcm43xx_txstatus), + GFP_KERNEL); + if (!log->log) { + printk(KERN_ERR PFX "debugfs txstatus log OOM\n"); + kfree(e); + return; + } + log->end = -1; + spin_lock_init(&log->lock); + + dev->dfsentry = e; + + snprintf(devdir, sizeof(devdir), "%s", wiphy_name(dev->wl->hw->wiphy)); + e->subdir = debugfs_create_dir(devdir, fs.root); + e->dentry_tsf = debugfs_create_file("tsf", 0666, e->subdir, + dev, &tsf_fops); + if (!e->dentry_tsf) + printk(KERN_ERR PFX "debugfs: creating \"tsf\" for \"%s\" failed!\n", devdir); + e->dentry_txstat = debugfs_create_file("tx_status", 0444, e->subdir, + dev, &txstat_fops); + if (!e->dentry_txstat) + printk(KERN_ERR PFX "debugfs: creating \"tx_status\" for \"%s\" failed!\n", devdir); + e->dentry_restart = debugfs_create_file("restart", 0222, e->subdir, + dev, &restart_fops); + if (!e->dentry_restart) + printk(KERN_ERR PFX "debugfs: creating \"restart\" for \"%s\" failed!\n", devdir); +} + +void bcm43xx_debugfs_remove_device(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_dfsentry *e; + + if (!dev) + return; + + e = dev->dfsentry; + assert(e); + debugfs_remove(e->dentry_tsf); + debugfs_remove(e->dentry_txstat); + debugfs_remove(e->dentry_restart); + debugfs_remove(e->subdir); + kfree(e->txstatlog.log); + kfree(e); +} + +void bcm43xx_debugfs_log_txstat(struct bcm43xx_wldev *dev, + const struct bcm43xx_txstatus *status) +{ + struct bcm43xx_dfsentry *e = dev->dfsentry; + struct bcm43xx_txstatus_log *log; + struct bcm43xx_txstatus *cur; + int i; + + log = &e->txstatlog; + assert(irqs_disabled()); + spin_lock(&log->lock); + i = log->end + 1; + if (i == BCM43xx_NR_LOGGED_TXSTATUS) + i = 0; + log->end = i; + cur = &(log->log[i]); + memcpy(cur, status, sizeof(*cur)); + spin_unlock(&log->lock); +} + +void bcm43xx_debugfs_init(void) +{ + memset(&fs, 0, sizeof(fs)); + fs.root = debugfs_create_dir(KBUILD_MODNAME, NULL); + if (!fs.root) + printk(KERN_ERR PFX "debugfs: creating \"" KBUILD_MODNAME "\" subdir failed!\n"); + fs.dentry_driverinfo = debugfs_create_file("driver", 0444, fs.root, NULL, &drvinfo_fops); + if (!fs.dentry_driverinfo) + printk(KERN_ERR PFX "debugfs: creating \"" KBUILD_MODNAME "/driver\" failed!\n"); +} + +void bcm43xx_debugfs_exit(void) +{ + debugfs_remove(fs.dentry_driverinfo); + debugfs_remove(fs.root); +} + +void bcm43xx_printk_dump(const char *data, + size_t size, + const char *description) +{ + unsigned int i; + char c; + + printk(KERN_INFO PFX "Data dump (%s, %lu bytes):", + description, (unsigned long)size); + for (i = 0; i < size; i++) { + c = data[i]; + if (i % 8 == 0) + printk("\n" KERN_INFO PFX "0x%08x: 0x%02x, ", i, c & 0xff); + else + printk("0x%02x, ", c & 0xff); + } + printk("\n"); +} + +void bcm43xx_printk_bitdump(const unsigned char *data, + size_t bytes, int msb_to_lsb, + const char *description) +{ + unsigned int i; + int j; + const unsigned char *d; + + printk(KERN_INFO PFX "*** Bitdump (%s, %lu bytes, %s) ***", + description, (unsigned long)bytes, + msb_to_lsb ? "MSB to LSB" : "LSB to MSB"); + for (i = 0; i < bytes; i++) { + d = data + i; + if (i % 8 == 0) + printk("\n" KERN_INFO PFX "0x%08x: ", i); + if (msb_to_lsb) { + for (j = 7; j >= 0; j--) { + if (*d & (1 << j)) + printk("1"); + else + printk("0"); + } + } else { + for (j = 0; j < 8; j++) { + if (*d & (1 << j)) + printk("1"); + else + printk("0"); + } + } + printk(" "); + } + printk("\n"); +} diff --git a/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_debugfs.h b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_debugfs.h new file mode 100644 index 0000000..42c3062 --- /dev/null +++ b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_debugfs.h @@ -0,0 +1,110 @@ +#ifndef BCM43xx_DEBUGFS_H_ +#define BCM43xx_DEBUGFS_H_ + +struct bcm43xx_wldev; +struct bcm43xx_txstatus; + +#ifdef CONFIG_BCM43XX_MAC80211_DEBUG + +struct dentry; + +#define BCM43xx_NR_LOGGED_TXSTATUS 100 + +struct bcm43xx_txstatus_log { + struct bcm43xx_txstatus *log; + int end; + int printing; + char printbuf[(BCM43xx_NR_LOGGED_TXSTATUS * 70) + 200]; + size_t buf_avail; + spinlock_t lock; +}; + +struct bcm43xx_dfsentry { + struct dentry *subdir; + struct dentry *dentry_tsf; + struct dentry *dentry_txstat; + struct dentry *dentry_restart; + + struct bcm43xx_wldev *dev; + + struct bcm43xx_txstatus_log txstatlog; +}; + +struct bcm43xx_debugfs { + struct dentry *root; + struct dentry *dentry_driverinfo; +}; + +void bcm43xx_debugfs_init(void); +void bcm43xx_debugfs_exit(void); +void bcm43xx_debugfs_add_device(struct bcm43xx_wldev *dev); +void bcm43xx_debugfs_remove_device(struct bcm43xx_wldev *dev); +void bcm43xx_debugfs_log_txstat(struct bcm43xx_wldev *dev, + const struct bcm43xx_txstatus *status); + +/* Debug helper: Dump binary data through printk. */ +void bcm43xx_printk_dump(const char *data, + size_t size, + const char *description); +/* Debug helper: Dump bitwise binary data through printk. */ +void bcm43xx_printk_bitdump(const unsigned char *data, + size_t bytes, int msb_to_lsb, + const char *description); +#define bcm43xx_printk_bitdumpt(pointer, msb_to_lsb, description) \ + do { \ + bcm43xx_printk_bitdump((const unsigned char *)(pointer), \ + sizeof(*(pointer)), \ + (msb_to_lsb), \ + (description)); \ + } while (0) + +#else /* CONFIG_BCM43XX_MAC80211_DEBUG*/ + +static inline +void bcm43xx_debugfs_init(void) { } +static inline +void bcm43xx_debugfs_exit(void) { } +static inline +void bcm43xx_debugfs_add_device(struct bcm43xx_wldev *dev) { } +static inline +void bcm43xx_debugfs_remove_device(struct bcm43xx_wldev *dev) { } +static inline +void bcm43xx_debugfs_log_txstat(struct bcm43xx_wldev *dev, + const struct bcm43xx_txstatus *status) { } + +static inline +void bcm43xx_printk_dump(const char *data, + size_t size, + const char *description) +{ +} +static inline +void bcm43xx_printk_bitdump(const unsigned char *data, + size_t bytes, int msb_to_lsb, + const char *description) +{ +} +#define bcm43xx_printk_bitdumpt(pointer, msb_to_lsb, description) do { /* nothing */ } while (0) + +#endif /* CONFIG_BCM43XX_MAC80211_DEBUG*/ + +/* Ugly helper macros to make incomplete code more verbose on runtime */ +#ifdef TODO +# undef TODO +#endif +#define TODO() \ + do { \ + printk(KERN_INFO PFX "TODO: Incomplete code in %s() at %s:%d\n", \ + __FUNCTION__, __FILE__, __LINE__); \ + } while (0) + +#ifdef FIXME +# undef FIXME +#endif +#define FIXME() \ + do { \ + printk(KERN_INFO PFX "FIXME: Possibly broken code in %s() at %s:%d\n", \ + __FUNCTION__, __FILE__, __LINE__); \ + } while (0) + +#endif /* BCM43xx_DEBUGFS_H_ */ diff --git a/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_dma.c b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_dma.c new file mode 100644 index 0000000..0f66db5 --- /dev/null +++ b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_dma.c @@ -0,0 +1,1383 @@ +/* + + Broadcom BCM43xx wireless driver + + DMA ringbuffer and descriptor allocation/management + + Copyright (c) 2005, 2006 Michael Buesch + + Some code in this file is derived from the b44.c driver + Copyright (C) 2002 David S. Miller + Copyright (C) Pekka Pietikainen + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "bcm43xx.h" +#include "bcm43xx_dma.h" +#include "bcm43xx_main.h" +#include "bcm43xx_debugfs.h" +#include "bcm43xx_power.h" +#include "bcm43xx_xmit.h" + +#include +#include +#include +#include + + +/* 32bit DMA ops. */ +static +struct bcm43xx_dmadesc_generic * op32_idx2desc(struct bcm43xx_dmaring *ring, + int slot, + struct bcm43xx_dmadesc_meta **meta) +{ + struct bcm43xx_dmadesc32 *desc; + + *meta = &(ring->meta[slot]); + desc = ring->descbase; + desc = &(desc[slot]); + + return (struct bcm43xx_dmadesc_generic *)desc; +} + +static void op32_fill_descriptor(struct bcm43xx_dmaring *ring, + struct bcm43xx_dmadesc_generic *desc, + dma_addr_t dmaaddr, u16 bufsize, + int start, int end, int irq) +{ + struct bcm43xx_dmadesc32 *descbase = ring->descbase; + int slot; + u32 ctl; + u32 addr; + u32 addrext; + + slot = (int)(&(desc->dma32) - descbase); + assert(slot >= 0 && slot < ring->nr_slots); + + addr = (u32)(dmaaddr & ~SSB_DMA_TRANSLATION_MASK); + addrext = (u32)(dmaaddr & SSB_DMA_TRANSLATION_MASK) + >> SSB_DMA_TRANSLATION_SHIFT; + addr |= ssb_dma_translation(ring->dev->dev); + ctl = (bufsize - ring->frameoffset) + & BCM43xx_DMA32_DCTL_BYTECNT; + if (slot == ring->nr_slots - 1) + ctl |= BCM43xx_DMA32_DCTL_DTABLEEND; + if (start) + ctl |= BCM43xx_DMA32_DCTL_FRAMESTART; + if (end) + ctl |= BCM43xx_DMA32_DCTL_FRAMEEND; + if (irq) + ctl |= BCM43xx_DMA32_DCTL_IRQ; + ctl |= (addrext << BCM43xx_DMA32_DCTL_ADDREXT_SHIFT) + & BCM43xx_DMA32_DCTL_ADDREXT_MASK; + + desc->dma32.control = cpu_to_le32(ctl); + desc->dma32.address = cpu_to_le32(addr); +} + +static void op32_poke_tx(struct bcm43xx_dmaring *ring, int slot) +{ + bcm43xx_dma_write(ring, BCM43xx_DMA32_TXINDEX, + (u32)(slot * sizeof(struct bcm43xx_dmadesc32))); +} + +static void op32_tx_suspend(struct bcm43xx_dmaring *ring) +{ + bcm43xx_dma_write(ring, BCM43xx_DMA32_TXCTL, + bcm43xx_dma_read(ring, BCM43xx_DMA32_TXCTL) + | BCM43xx_DMA32_TXSUSPEND); +} + +static void op32_tx_resume(struct bcm43xx_dmaring *ring) +{ + bcm43xx_dma_write(ring, BCM43xx_DMA32_TXCTL, + bcm43xx_dma_read(ring, BCM43xx_DMA32_TXCTL) + & ~BCM43xx_DMA32_TXSUSPEND); +} + +static int op32_get_current_rxslot(struct bcm43xx_dmaring *ring) +{ + u32 val; + + val = bcm43xx_dma_read(ring, BCM43xx_DMA32_RXSTATUS); + val &= BCM43xx_DMA32_RXDPTR; + + return (val / sizeof(struct bcm43xx_dmadesc32)); +} + +static void op32_set_current_rxslot(struct bcm43xx_dmaring *ring, + int slot) +{ + bcm43xx_dma_write(ring, BCM43xx_DMA32_RXINDEX, + (u32)(slot * sizeof(struct bcm43xx_dmadesc32))); +} + +static const struct bcm43xx_dma_ops dma32_ops = { + .idx2desc = op32_idx2desc, + .fill_descriptor = op32_fill_descriptor, + .poke_tx = op32_poke_tx, + .tx_suspend = op32_tx_suspend, + .tx_resume = op32_tx_resume, + .get_current_rxslot = op32_get_current_rxslot, + .set_current_rxslot = op32_set_current_rxslot, +}; + +/* 64bit DMA ops. */ +static +struct bcm43xx_dmadesc_generic * op64_idx2desc(struct bcm43xx_dmaring *ring, + int slot, + struct bcm43xx_dmadesc_meta **meta) +{ + struct bcm43xx_dmadesc64 *desc; + + *meta = &(ring->meta[slot]); + desc = ring->descbase; + desc = &(desc[slot]); + + return (struct bcm43xx_dmadesc_generic *)desc; +} + +static void op64_fill_descriptor(struct bcm43xx_dmaring *ring, + struct bcm43xx_dmadesc_generic *desc, + dma_addr_t dmaaddr, u16 bufsize, + int start, int end, int irq) +{ + struct bcm43xx_dmadesc64 *descbase = ring->descbase; + int slot; + u32 ctl0 = 0, ctl1 = 0; + u32 addrlo, addrhi; + u32 addrext; + + slot = (int)(&(desc->dma64) - descbase); + assert(slot >= 0 && slot < ring->nr_slots); + + addrlo = (u32)(dmaaddr & 0xFFFFFFFF); + addrhi = (((u64)dmaaddr >> 32) & ~SSB_DMA_TRANSLATION_MASK); + addrext = (((u64)dmaaddr >> 32) & SSB_DMA_TRANSLATION_MASK) + >> SSB_DMA_TRANSLATION_SHIFT; + addrhi |= ssb_dma_translation(ring->dev->dev); + if (slot == ring->nr_slots - 1) + ctl0 |= BCM43xx_DMA64_DCTL0_DTABLEEND; + if (start) + ctl0 |= BCM43xx_DMA64_DCTL0_FRAMESTART; + if (end) + ctl0 |= BCM43xx_DMA64_DCTL0_FRAMEEND; + if (irq) + ctl0 |= BCM43xx_DMA64_DCTL0_IRQ; + ctl1 |= (bufsize - ring->frameoffset) + & BCM43xx_DMA64_DCTL1_BYTECNT; + ctl1 |= (addrext << BCM43xx_DMA64_DCTL1_ADDREXT_SHIFT) + & BCM43xx_DMA64_DCTL1_ADDREXT_MASK; + + desc->dma64.control0 = cpu_to_le32(ctl0); + desc->dma64.control1 = cpu_to_le32(ctl1); + desc->dma64.address_low = cpu_to_le32(addrlo); + desc->dma64.address_high = cpu_to_le32(addrhi); +} + +static void op64_poke_tx(struct bcm43xx_dmaring *ring, int slot) +{ + bcm43xx_dma_write(ring, BCM43xx_DMA64_TXINDEX, + (u32)(slot * sizeof(struct bcm43xx_dmadesc64))); +} + +static void op64_tx_suspend(struct bcm43xx_dmaring *ring) +{ + bcm43xx_dma_write(ring, BCM43xx_DMA64_TXCTL, + bcm43xx_dma_read(ring, BCM43xx_DMA64_TXCTL) + | BCM43xx_DMA64_TXSUSPEND); +} + +static void op64_tx_resume(struct bcm43xx_dmaring *ring) +{ + bcm43xx_dma_write(ring, BCM43xx_DMA64_TXCTL, + bcm43xx_dma_read(ring, BCM43xx_DMA64_TXCTL) + & ~BCM43xx_DMA64_TXSUSPEND); +} + +static int op64_get_current_rxslot(struct bcm43xx_dmaring *ring) +{ + u32 val; + + val = bcm43xx_dma_read(ring, BCM43xx_DMA64_RXSTATUS); + val &= BCM43xx_DMA64_RXSTATDPTR; + + return (val / sizeof(struct bcm43xx_dmadesc64)); +} + +static void op64_set_current_rxslot(struct bcm43xx_dmaring *ring, + int slot) +{ + bcm43xx_dma_write(ring, BCM43xx_DMA64_RXINDEX, + (u32)(slot * sizeof(struct bcm43xx_dmadesc64))); +} + +static const struct bcm43xx_dma_ops dma64_ops = { + .idx2desc = op64_idx2desc, + .fill_descriptor = op64_fill_descriptor, + .poke_tx = op64_poke_tx, + .tx_suspend = op64_tx_suspend, + .tx_resume = op64_tx_resume, + .get_current_rxslot = op64_get_current_rxslot, + .set_current_rxslot = op64_set_current_rxslot, +}; + + +static inline int free_slots(struct bcm43xx_dmaring *ring) +{ + return (ring->nr_slots - ring->used_slots); +} + +static inline int next_slot(struct bcm43xx_dmaring *ring, int slot) +{ + assert(slot >= -1 && slot <= ring->nr_slots - 1); + if (slot == ring->nr_slots - 1) + return 0; + return slot + 1; +} + +static inline int prev_slot(struct bcm43xx_dmaring *ring, int slot) +{ + assert(slot >= 0 && slot <= ring->nr_slots - 1); + if (slot == 0) + return ring->nr_slots - 1; + return slot - 1; +} + +/* Request a slot for usage. */ +static inline +int request_slot(struct bcm43xx_dmaring *ring) +{ + int slot; + + assert(ring->tx); + assert(!ring->stopped); + assert(free_slots(ring) != 0); + + slot = next_slot(ring, ring->current_slot); + ring->current_slot = slot; + ring->used_slots++; + +#ifdef CONFIG_BCM43XX_MAC80211_DEBUG + if (ring->used_slots > ring->max_used_slots) + ring->max_used_slots = ring->used_slots; +#endif /* CONFIG_BCM43XX_MAC80211_DEBUG*/ + + return slot; +} + +/* Return a slot to the free slots. */ +static inline +void return_slot(struct bcm43xx_dmaring *ring, int slot) +{ + assert(ring->tx); + + ring->used_slots--; +} + +u16 bcm43xx_dmacontroller_base(int dma64bit, int controller_idx) +{ + static const u16 map64[] = { + BCM43xx_MMIO_DMA64_BASE0, + BCM43xx_MMIO_DMA64_BASE1, + BCM43xx_MMIO_DMA64_BASE2, + BCM43xx_MMIO_DMA64_BASE3, + BCM43xx_MMIO_DMA64_BASE4, + BCM43xx_MMIO_DMA64_BASE5, + }; + static const u16 map32[] = { + BCM43xx_MMIO_DMA32_BASE0, + BCM43xx_MMIO_DMA32_BASE1, + BCM43xx_MMIO_DMA32_BASE2, + BCM43xx_MMIO_DMA32_BASE3, + BCM43xx_MMIO_DMA32_BASE4, + BCM43xx_MMIO_DMA32_BASE5, + }; + + if (dma64bit) { + assert(controller_idx >= 0 && + controller_idx < ARRAY_SIZE(map64)); + return map64[controller_idx]; + } + assert(controller_idx >= 0 && + controller_idx < ARRAY_SIZE(map32)); + return map32[controller_idx]; +} + +static inline +dma_addr_t map_descbuffer(struct bcm43xx_dmaring *ring, + unsigned char *buf, + size_t len, + int tx) +{ + dma_addr_t dmaaddr; + + if (tx) { + dmaaddr = dma_map_single(ring->dev->dev->dev, + buf, len, + DMA_TO_DEVICE); + } else { + dmaaddr = dma_map_single(ring->dev->dev->dev, + buf, len, + DMA_FROM_DEVICE); + } + + return dmaaddr; +} + +static inline +void unmap_descbuffer(struct bcm43xx_dmaring *ring, + dma_addr_t addr, + size_t len, + int tx) +{ + if (tx) { + dma_unmap_single(ring->dev->dev->dev, + addr, len, + DMA_TO_DEVICE); + } else { + dma_unmap_single(ring->dev->dev->dev, + addr, len, + DMA_FROM_DEVICE); + } +} + +static inline +void sync_descbuffer_for_cpu(struct bcm43xx_dmaring *ring, + dma_addr_t addr, + size_t len) +{ + assert(!ring->tx); + + dma_sync_single_for_cpu(ring->dev->dev->dev, + addr, len, DMA_FROM_DEVICE); +} + +static inline +void sync_descbuffer_for_device(struct bcm43xx_dmaring *ring, + dma_addr_t addr, + size_t len) +{ + assert(!ring->tx); + + dma_sync_single_for_device(ring->dev->dev->dev, + addr, len, DMA_FROM_DEVICE); +} + +static inline +void free_descriptor_buffer(struct bcm43xx_dmaring *ring, + struct bcm43xx_dmadesc_meta *meta, + int irq_context) +{ + if (meta->skb) { + if (irq_context) + dev_kfree_skb_irq(meta->skb); + else + dev_kfree_skb(meta->skb); + meta->skb = NULL; + } +} + +static int alloc_ringmemory(struct bcm43xx_dmaring *ring) +{ + struct device *dev = ring->dev->dev->dev; + + ring->descbase = dma_alloc_coherent(dev, BCM43xx_DMA_RINGMEMSIZE, + &(ring->dmabase), GFP_KERNEL); + if (!ring->descbase) { + printk(KERN_ERR PFX "DMA ringmemory allocation failed\n"); + return -ENOMEM; + } + memset(ring->descbase, 0, BCM43xx_DMA_RINGMEMSIZE); + + return 0; +} + +static void free_ringmemory(struct bcm43xx_dmaring *ring) +{ + struct device *dev = ring->dev->dev->dev; + + dma_free_coherent(dev, BCM43xx_DMA_RINGMEMSIZE, + ring->descbase, ring->dmabase); +} + +/* Reset the RX DMA channel */ +int bcm43xx_dmacontroller_rx_reset(struct bcm43xx_wldev *dev, + u16 mmio_base, int dma64) +{ + int i; + u32 value; + u16 offset; + + offset = dma64 ? BCM43xx_DMA64_RXCTL : BCM43xx_DMA32_RXCTL; + bcm43xx_write32(dev, mmio_base + offset, 0); + for (i = 0; i < 1000; i++) { + offset = dma64 ? BCM43xx_DMA64_RXSTATUS : BCM43xx_DMA32_RXSTATUS; + value = bcm43xx_read32(dev, mmio_base + offset); + if (dma64) { + value &= BCM43xx_DMA64_RXSTAT; + if (value == BCM43xx_DMA64_RXSTAT_DISABLED) { + i = -1; + break; + } + } else { + value &= BCM43xx_DMA32_RXSTATE; + if (value == BCM43xx_DMA32_RXSTAT_DISABLED) { + i = -1; + break; + } + } + udelay(10); + } + if (i != -1) { + printk(KERN_ERR PFX "Error: Wait on DMA RX status timed out.\n"); + return -ENODEV; + } + + return 0; +} + +/* Reset the RX DMA channel */ +int bcm43xx_dmacontroller_tx_reset(struct bcm43xx_wldev *dev, + u16 mmio_base, int dma64) +{ + int i; + u32 value; + u16 offset; + + for (i = 0; i < 1000; i++) { + offset = dma64 ? BCM43xx_DMA64_TXSTATUS : BCM43xx_DMA32_TXSTATUS; + value = bcm43xx_read32(dev, mmio_base + offset); + if (dma64) { + value &= BCM43xx_DMA64_TXSTAT; + if (value == BCM43xx_DMA64_TXSTAT_DISABLED || + value == BCM43xx_DMA64_TXSTAT_IDLEWAIT || + value == BCM43xx_DMA64_TXSTAT_STOPPED) + break; + } else { + value &= BCM43xx_DMA32_TXSTATE; + if (value == BCM43xx_DMA32_TXSTAT_DISABLED || + value == BCM43xx_DMA32_TXSTAT_IDLEWAIT || + value == BCM43xx_DMA32_TXSTAT_STOPPED) + break; + } + udelay(10); + } + offset = dma64 ? BCM43xx_DMA64_TXCTL : BCM43xx_DMA32_TXCTL; + bcm43xx_write32(dev, mmio_base + offset, 0); + for (i = 0; i < 1000; i++) { + offset = dma64 ? BCM43xx_DMA64_TXSTATUS : BCM43xx_DMA32_TXSTATUS; + value = bcm43xx_read32(dev, mmio_base + offset); + if (dma64) { + value &= BCM43xx_DMA64_TXSTAT; + if (value == BCM43xx_DMA64_TXSTAT_DISABLED) { + i = -1; + break; + } + } else { + value &= BCM43xx_DMA32_TXSTATE; + if (value == BCM43xx_DMA32_TXSTAT_DISABLED) { + i = -1; + break; + } + } + udelay(10); + } + if (i != -1) { + printk(KERN_ERR PFX "Error: Wait on DMA TX status timed out.\n"); + return -ENODEV; + } + /* ensure the reset is completed. */ + udelay(300); + + return 0; +} + +static int setup_rx_descbuffer(struct bcm43xx_dmaring *ring, + struct bcm43xx_dmadesc_generic *desc, + struct bcm43xx_dmadesc_meta *meta, + gfp_t gfp_flags) +{ + struct bcm43xx_rxhdr_fw4 *rxhdr; + struct bcm43xx_hwtxstatus *txstat; + dma_addr_t dmaaddr; + struct sk_buff *skb; + + assert(!ring->tx); + + skb = __dev_alloc_skb(ring->rx_buffersize, gfp_flags); + if (unlikely(!skb)) + return -ENOMEM; + dmaaddr = map_descbuffer(ring, skb->data, + ring->rx_buffersize, 0); + if (dma_mapping_error(dmaaddr)) { + /* ugh. try to realloc in zone_dma */ + gfp_flags |= GFP_DMA; + + dev_kfree_skb_any(skb); + + skb = __dev_alloc_skb(ring->rx_buffersize, gfp_flags); + if (unlikely(!skb)) + return -ENOMEM; + dmaaddr = map_descbuffer(ring, skb->data, + ring->rx_buffersize, 0); + } + + if (dma_mapping_error(dmaaddr)) { + dev_kfree_skb_any(skb); + return -EIO; + } + + meta->skb = skb; + meta->dmaaddr = dmaaddr; + ring->ops->fill_descriptor(ring, desc, dmaaddr, + ring->rx_buffersize, 0, 0, 0); + + rxhdr = (struct bcm43xx_rxhdr_fw4 *)(skb->data); + rxhdr->frame_len = 0; + txstat = (struct bcm43xx_hwtxstatus *)(skb->data); + txstat->cookie = 0; + + return 0; +} + +/* Allocate the initial descbuffers. + * This is used for an RX ring only. + */ +static int alloc_initial_descbuffers(struct bcm43xx_dmaring *ring) +{ + int i, err = -ENOMEM; + struct bcm43xx_dmadesc_generic *desc; + struct bcm43xx_dmadesc_meta *meta; + + for (i = 0; i < ring->nr_slots; i++) { + desc = ring->ops->idx2desc(ring, i, &meta); + + err = setup_rx_descbuffer(ring, desc, meta, GFP_KERNEL); + if (err) { + printk(KERN_ERR PFX "Failed to allocate initial descbuffers\n"); + goto err_unwind; + } + } + mb(); + ring->used_slots = ring->nr_slots; + err = 0; +out: + return err; + +err_unwind: + for (i--; i >= 0; i--) { + desc = ring->ops->idx2desc(ring, i, &meta); + + unmap_descbuffer(ring, meta->dmaaddr, ring->rx_buffersize, 0); + dev_kfree_skb(meta->skb); + } + goto out; +} + +/* Do initial setup of the DMA controller. + * Reset the controller, write the ring busaddress + * and switch the "enable" bit on. + */ +static int dmacontroller_setup(struct bcm43xx_dmaring *ring) +{ + int err = 0; + u32 value; + u32 addrext; + u32 trans = ssb_dma_translation(ring->dev->dev); + + if (ring->tx) { + if (ring->dma64) { + u64 ringbase = (u64)(ring->dmabase); + + addrext = ((ringbase >> 32) & SSB_DMA_TRANSLATION_MASK) + >> SSB_DMA_TRANSLATION_SHIFT; + value = BCM43xx_DMA64_TXENABLE; + value |= (addrext << BCM43xx_DMA64_TXADDREXT_SHIFT) + & BCM43xx_DMA64_TXADDREXT_MASK; + bcm43xx_dma_write(ring, BCM43xx_DMA64_TXCTL, value); + bcm43xx_dma_write(ring, BCM43xx_DMA64_TXRINGLO, + (ringbase & 0xFFFFFFFF)); + bcm43xx_dma_write(ring, BCM43xx_DMA64_TXRINGHI, + ((ringbase >> 32) & ~SSB_DMA_TRANSLATION_MASK) + | trans); + } else { + u32 ringbase = (u32)(ring->dmabase); + + addrext = (ringbase & SSB_DMA_TRANSLATION_MASK) + >> SSB_DMA_TRANSLATION_SHIFT; + value = BCM43xx_DMA32_TXENABLE; + value |= (addrext << BCM43xx_DMA32_TXADDREXT_SHIFT) + & BCM43xx_DMA32_TXADDREXT_MASK; + bcm43xx_dma_write(ring, BCM43xx_DMA32_TXCTL, value); + bcm43xx_dma_write(ring, BCM43xx_DMA32_TXRING, + (ringbase & ~SSB_DMA_TRANSLATION_MASK) + | trans); + } + } else { + err = alloc_initial_descbuffers(ring); + if (err) + goto out; + if (ring->dma64) { + u64 ringbase = (u64)(ring->dmabase); + + addrext = ((ringbase >> 32) & SSB_DMA_TRANSLATION_MASK) + >> SSB_DMA_TRANSLATION_SHIFT; + value = (ring->frameoffset << BCM43xx_DMA64_RXFROFF_SHIFT); + value |= BCM43xx_DMA64_RXENABLE; + value |= (addrext << BCM43xx_DMA64_RXADDREXT_SHIFT) + & BCM43xx_DMA64_RXADDREXT_MASK; + bcm43xx_dma_write(ring, BCM43xx_DMA64_RXCTL, value); + bcm43xx_dma_write(ring, BCM43xx_DMA64_RXRINGLO, + (ringbase & 0xFFFFFFFF)); + bcm43xx_dma_write(ring, BCM43xx_DMA64_RXRINGHI, + ((ringbase >> 32) & ~SSB_DMA_TRANSLATION_MASK) + | trans); + bcm43xx_dma_write(ring, BCM43xx_DMA64_RXINDEX, 200); + } else { + u32 ringbase = (u32)(ring->dmabase); + + addrext = (ringbase & SSB_DMA_TRANSLATION_MASK) + >> SSB_DMA_TRANSLATION_SHIFT; + value = (ring->frameoffset << BCM43xx_DMA32_RXFROFF_SHIFT); + value |= BCM43xx_DMA32_RXENABLE; + value |= (addrext << BCM43xx_DMA32_RXADDREXT_SHIFT) + & BCM43xx_DMA32_RXADDREXT_MASK; + bcm43xx_dma_write(ring, BCM43xx_DMA32_RXCTL, value); + bcm43xx_dma_write(ring, BCM43xx_DMA32_RXRING, + (ringbase & ~SSB_DMA_TRANSLATION_MASK) + | trans); + bcm43xx_dma_write(ring, BCM43xx_DMA32_RXINDEX, 200); + } + } + +out: + return err; +} + +/* Shutdown the DMA controller. */ +static void dmacontroller_cleanup(struct bcm43xx_dmaring *ring) +{ + if (ring->tx) { + bcm43xx_dmacontroller_tx_reset(ring->dev, ring->mmio_base, ring->dma64); + if (ring->dma64) { + bcm43xx_dma_write(ring, BCM43xx_DMA64_TXRINGLO, 0); + bcm43xx_dma_write(ring, BCM43xx_DMA64_TXRINGHI, 0); + } else + bcm43xx_dma_write(ring, BCM43xx_DMA32_TXRING, 0); + } else { + bcm43xx_dmacontroller_rx_reset(ring->dev, ring->mmio_base, ring->dma64); + if (ring->dma64) { + bcm43xx_dma_write(ring, BCM43xx_DMA64_RXRINGLO, 0); + bcm43xx_dma_write(ring, BCM43xx_DMA64_RXRINGHI, 0); + } else + bcm43xx_dma_write(ring, BCM43xx_DMA32_RXRING, 0); + } +} + +static void free_all_descbuffers(struct bcm43xx_dmaring *ring) +{ + struct bcm43xx_dmadesc_generic *desc; + struct bcm43xx_dmadesc_meta *meta; + int i; + + if (!ring->used_slots) + return; + for (i = 0; i < ring->nr_slots; i++) { + desc = ring->ops->idx2desc(ring, i, &meta); + + if (!meta->skb) { + assert(ring->tx); + continue; + } + if (ring->tx) { + unmap_descbuffer(ring, meta->dmaaddr, + meta->skb->len, 1); + } else { + unmap_descbuffer(ring, meta->dmaaddr, + ring->rx_buffersize, 0); + } + free_descriptor_buffer(ring, meta, 0); + } +} + +static u64 supported_dma_mask(struct bcm43xx_wldev *dev) +{ + u32 tmp; + u16 mmio_base; + + tmp = bcm43xx_read32(dev, SSB_TMSHIGH); + if (tmp & SSB_TMSHIGH_DMA64) + return DMA_64BIT_MASK; + mmio_base = bcm43xx_dmacontroller_base(0, 0); + bcm43xx_write32(dev, + mmio_base + BCM43xx_DMA32_TXCTL, + BCM43xx_DMA32_TXADDREXT_MASK); + tmp = bcm43xx_read32(dev, + mmio_base + BCM43xx_DMA32_TXCTL); + if (tmp & BCM43xx_DMA32_TXADDREXT_MASK) + return DMA_32BIT_MASK; + + return DMA_30BIT_MASK; +} + +/* Main initialization function. */ +static +struct bcm43xx_dmaring * bcm43xx_setup_dmaring(struct bcm43xx_wldev *dev, + int controller_index, + int for_tx, + int dma64) +{ + struct bcm43xx_dmaring *ring; + int err; + int nr_slots; + dma_addr_t dma_test; + + ring = kzalloc(sizeof(*ring), GFP_KERNEL); + if (!ring) + goto out; + + nr_slots = BCM43xx_RXRING_SLOTS; + if (for_tx) + nr_slots = BCM43xx_TXRING_SLOTS; + + ring->meta = kcalloc(nr_slots, sizeof(struct bcm43xx_dmadesc_meta), + GFP_KERNEL); + if (!ring->meta) + goto err_kfree_ring; + if (for_tx) { + ring->txhdr_cache = kcalloc(nr_slots, + sizeof(struct bcm43xx_txhdr_fw4), + GFP_KERNEL); + if (!ring->txhdr_cache) + goto err_kfree_meta; + + /* test for ability to dma to txhdr_cache */ + dma_test = dma_map_single(dev->dev->dev, + ring->txhdr_cache, sizeof(struct bcm43xx_txhdr_fw4), + DMA_TO_DEVICE); + + if (dma_mapping_error(dma_test)) { + /* ugh realloc */ + kfree(ring->txhdr_cache); + ring->txhdr_cache = kcalloc(nr_slots, + sizeof(struct bcm43xx_txhdr_fw4), + GFP_KERNEL | GFP_DMA); + if (!ring->txhdr_cache) + goto err_kfree_meta; + + dma_test = dma_map_single(dev->dev->dev, + ring->txhdr_cache, sizeof(struct bcm43xx_txhdr_fw4), + DMA_TO_DEVICE); + + if (dma_mapping_error(dma_test)) + goto err_kfree_txhdr_cache; + } + + dma_unmap_single(dev->dev->dev, + dma_test, sizeof(struct bcm43xx_txhdr_fw4), + DMA_TO_DEVICE); + } + + ring->dev = dev; + ring->nr_slots = nr_slots; + ring->mmio_base = bcm43xx_dmacontroller_base(dma64, controller_index); + ring->index = controller_index; + ring->dma64 = !!dma64; + if (dma64) + ring->ops = &dma64_ops; + else + ring->ops = &dma32_ops; + if (for_tx) { + ring->tx = 1; + ring->current_slot = -1; + } else { + if (ring->index == 0) { + ring->rx_buffersize = BCM43xx_DMA0_RX_BUFFERSIZE; + ring->frameoffset = BCM43xx_DMA0_RX_FRAMEOFFSET; + } else if (ring->index == 3) { + ring->rx_buffersize = BCM43xx_DMA3_RX_BUFFERSIZE; + ring->frameoffset = BCM43xx_DMA3_RX_FRAMEOFFSET; + } else + assert(0); + } + + err = alloc_ringmemory(ring); + if (err) + goto err_kfree_txhdr_cache; + err = dmacontroller_setup(ring); + if (err) + goto err_free_ringmemory; + +out: + return ring; + +err_free_ringmemory: + free_ringmemory(ring); +err_kfree_txhdr_cache: + kfree(ring->txhdr_cache); +err_kfree_meta: + kfree(ring->meta); +err_kfree_ring: + kfree(ring); + ring = NULL; + goto out; +} + +/* Main cleanup function. */ +static void bcm43xx_destroy_dmaring(struct bcm43xx_dmaring *ring) +{ + if (!ring) + return; + + dprintk(KERN_INFO PFX "DMA-%s 0x%04X (%s) max used slots: %d/%d\n", + (ring->dma64) ? "64" : "32", + ring->mmio_base, + (ring->tx) ? "TX" : "RX", + ring->max_used_slots, ring->nr_slots); + /* Device IRQs are disabled prior entering this function, + * so no need to take care of concurrency with rx handler stuff. + */ + dmacontroller_cleanup(ring); + free_all_descbuffers(ring); + free_ringmemory(ring); + + kfree(ring->txhdr_cache); + kfree(ring->meta); + kfree(ring); +} + +void bcm43xx_dma_free(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_dma *dma; + + if (bcm43xx_using_pio(dev)) + return; + dma = &dev->dma; + + bcm43xx_destroy_dmaring(dma->rx_ring3); + dma->rx_ring3 = NULL; + bcm43xx_destroy_dmaring(dma->rx_ring0); + dma->rx_ring0 = NULL; + + bcm43xx_destroy_dmaring(dma->tx_ring5); + dma->tx_ring5 = NULL; + bcm43xx_destroy_dmaring(dma->tx_ring4); + dma->tx_ring4 = NULL; + bcm43xx_destroy_dmaring(dma->tx_ring3); + dma->tx_ring3 = NULL; + bcm43xx_destroy_dmaring(dma->tx_ring2); + dma->tx_ring2 = NULL; + bcm43xx_destroy_dmaring(dma->tx_ring1); + dma->tx_ring1 = NULL; + bcm43xx_destroy_dmaring(dma->tx_ring0); + dma->tx_ring0 = NULL; +} + +int bcm43xx_dma_init(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_dma *dma = &dev->dma; + struct bcm43xx_dmaring *ring; + int err; + u64 dmamask; + int dma64 = 0; + + dmamask = supported_dma_mask(dev); + if (dmamask == DMA_64BIT_MASK) + dma64 = 1; + + err = ssb_dma_set_mask(dev->dev, dmamask); + if (err) { +#ifdef BCM43XX_MAC80211_PIO + printk(KERN_WARNING PFX "DMA for this device not supported. " + "Falling back to PIO\n"); + dev->__using_pio = 1; + return -EAGAIN; +#else + printk(KERN_ERR PFX "DMA for this device not supported and " + "no PIO support compiled in\n"); + return -EOPNOTSUPP; +#endif + } + + err = -ENOMEM; + /* setup TX DMA channels. */ + ring = bcm43xx_setup_dmaring(dev, 0, 1, dma64); + if (!ring) + goto out; + dma->tx_ring0 = ring; + + ring = bcm43xx_setup_dmaring(dev, 1, 1, dma64); + if (!ring) + goto err_destroy_tx0; + dma->tx_ring1 = ring; + + ring = bcm43xx_setup_dmaring(dev, 2, 1, dma64); + if (!ring) + goto err_destroy_tx1; + dma->tx_ring2 = ring; + + ring = bcm43xx_setup_dmaring(dev, 3, 1, dma64); + if (!ring) + goto err_destroy_tx2; + dma->tx_ring3 = ring; + + ring = bcm43xx_setup_dmaring(dev, 4, 1, dma64); + if (!ring) + goto err_destroy_tx3; + dma->tx_ring4 = ring; + + ring = bcm43xx_setup_dmaring(dev, 5, 1, dma64); + if (!ring) + goto err_destroy_tx4; + dma->tx_ring5 = ring; + + /* setup RX DMA channels. */ + ring = bcm43xx_setup_dmaring(dev, 0, 0, dma64); + if (!ring) + goto err_destroy_tx5; + dma->rx_ring0 = ring; + + if (dev->dev->id.revision < 5) { + ring = bcm43xx_setup_dmaring(dev, 3, 0, dma64); + if (!ring) + goto err_destroy_rx0; + dma->rx_ring3 = ring; + } + + dprintk(KERN_INFO PFX "%d-bit DMA initialized\n", + (dmamask == DMA_64BIT_MASK) ? 64 : + (dmamask == DMA_32BIT_MASK) ? 32 : 30); + err = 0; +out: + return err; + +err_destroy_rx0: + bcm43xx_destroy_dmaring(dma->rx_ring0); + dma->rx_ring0 = NULL; +err_destroy_tx5: + bcm43xx_destroy_dmaring(dma->tx_ring5); + dma->tx_ring5 = NULL; +err_destroy_tx4: + bcm43xx_destroy_dmaring(dma->tx_ring4); + dma->tx_ring4 = NULL; +err_destroy_tx3: + bcm43xx_destroy_dmaring(dma->tx_ring3); + dma->tx_ring3 = NULL; +err_destroy_tx2: + bcm43xx_destroy_dmaring(dma->tx_ring2); + dma->tx_ring2 = NULL; +err_destroy_tx1: + bcm43xx_destroy_dmaring(dma->tx_ring1); + dma->tx_ring1 = NULL; +err_destroy_tx0: + bcm43xx_destroy_dmaring(dma->tx_ring0); + dma->tx_ring0 = NULL; + goto out; +} + +/* Generate a cookie for the TX header. */ +static u16 generate_cookie(struct bcm43xx_dmaring *ring, + int slot) +{ + u16 cookie = 0x1000; + + /* Use the upper 4 bits of the cookie as + * DMA controller ID and store the slot number + * in the lower 12 bits. + * Note that the cookie must never be 0, as this + * is a special value used in RX path. + */ + switch (ring->index) { + case 0: + cookie = 0xA000; + break; + case 1: + cookie = 0xB000; + break; + case 2: + cookie = 0xC000; + break; + case 3: + cookie = 0xD000; + break; + case 4: + cookie = 0xE000; + break; + case 5: + cookie = 0xF000; + break; + } + assert(((u16)slot & 0xF000) == 0x0000); + cookie |= (u16)slot; + + return cookie; +} + +/* Inspect a cookie and find out to which controller/slot it belongs. */ +static +struct bcm43xx_dmaring * parse_cookie(struct bcm43xx_wldev *dev, + u16 cookie, int *slot) +{ + struct bcm43xx_dma *dma = &dev->dma; + struct bcm43xx_dmaring *ring = NULL; + + switch (cookie & 0xF000) { + case 0xA000: + ring = dma->tx_ring0; + break; + case 0xB000: + ring = dma->tx_ring1; + break; + case 0xC000: + ring = dma->tx_ring2; + break; + case 0xD000: + ring = dma->tx_ring3; + break; + case 0xE000: + ring = dma->tx_ring4; + break; + case 0xF000: + ring = dma->tx_ring5; + break; + default: + assert(0); + } + *slot = (cookie & 0x0FFF); + assert(ring && *slot >= 0 && *slot < ring->nr_slots); + + return ring; +} + +static int dma_tx_fragment(struct bcm43xx_dmaring *ring, + struct sk_buff *skb, + struct ieee80211_tx_control *ctl) +{ + const struct bcm43xx_dma_ops *ops = ring->ops; + u8 *header; + int slot; + int err; + struct bcm43xx_dmadesc_generic *desc; + struct bcm43xx_dmadesc_meta *meta; + struct bcm43xx_dmadesc_meta *meta_hdr; + struct sk_buff *bounce_skb; + +#define SLOTS_PER_PACKET 2 + assert(skb_shinfo(skb)->nr_frags == 0); + + /* Get a slot for the header. */ + slot = request_slot(ring); + desc = ops->idx2desc(ring, slot, &meta_hdr); + memset(meta_hdr, 0, sizeof(*meta_hdr)); + + header = &(ring->txhdr_cache[slot * sizeof(struct bcm43xx_txhdr_fw4)]); + bcm43xx_generate_txhdr(ring->dev, header, + skb->data, skb->len, ctl, + generate_cookie(ring, slot)); + + meta_hdr->dmaaddr = map_descbuffer(ring, (unsigned char *)header, + sizeof(struct bcm43xx_txhdr_fw4), 1); + if (dma_mapping_error(meta_hdr->dmaaddr)) + return -EIO; + ops->fill_descriptor(ring, desc, meta_hdr->dmaaddr, + sizeof(struct bcm43xx_txhdr_fw4), 1, 0, 0); + + /* Get a slot for the payload. */ + slot = request_slot(ring); + desc = ops->idx2desc(ring, slot, &meta); + memset(meta, 0, sizeof(*meta)); + + memcpy(&meta->txstat.control, ctl, sizeof(*ctl)); + meta->skb = skb; + meta->is_last_fragment = 1; + + meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1); + /* create a bounce buffer in zone_dma on mapping failure. */ + if (dma_mapping_error(meta->dmaaddr)) { + bounce_skb = __dev_alloc_skb(skb->len, GFP_ATOMIC | GFP_DMA); + if (!bounce_skb) { + err = -ENOMEM; + goto out_unmap_hdr; + } + + memcpy(skb_put(bounce_skb, skb->len), skb->data, skb->len); + dev_kfree_skb_any(skb); + skb = bounce_skb; + meta->skb = skb; + meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1); + if (dma_mapping_error(meta->dmaaddr)) { + err = -EIO; + goto out_free_bounce; + } + } + + ops->fill_descriptor(ring, desc, meta->dmaaddr, + skb->len, 0, 1, 1); + + /* Now transfer the whole frame. */ + wmb(); + ops->poke_tx(ring, next_slot(ring, slot)); + return 0; + +out_free_bounce: + dev_kfree_skb_any(skb); +out_unmap_hdr: + unmap_descbuffer(ring, meta_hdr->dmaaddr, + sizeof(struct bcm43xx_txhdr_fw4), 1); + return err; +} + +int bcm43xx_dma_tx(struct bcm43xx_wldev *dev, + struct sk_buff *skb, + struct ieee80211_tx_control *ctl) +{ + struct bcm43xx_dmaring *ring = dev->dma.tx_ring1; + int err = 0; + + assert(ring->tx); + if (unlikely(free_slots(ring) < SLOTS_PER_PACKET)) { + /* This should never trigger, as we call + * ieee80211_stop_queue() when it's full. + */ + printkl(KERN_ERR PFX "DMA queue overflow\n"); + return NETDEV_TX_BUSY; + } + + err = dma_tx_fragment(ring, skb, ctl); + if (unlikely(err)) { + printkl(KERN_ERR PFX "DMA tx mapping failure\n"); + return NETDEV_TX_BUSY; + } + + ring->nr_tx_packets++; + if (free_slots(ring) < SLOTS_PER_PACKET) { + /* FIXME: we currently only have one queue */ + ieee80211_stop_queue(dev->wl->hw, 0); + ring->stopped = 1; + } + + return 0; +} + +void bcm43xx_dma_handle_txstatus(struct bcm43xx_wldev *dev, + const struct bcm43xx_txstatus *status) +{ + const struct bcm43xx_dma_ops *ops; + struct bcm43xx_dmaring *ring; + struct bcm43xx_dmadesc_generic *desc; + struct bcm43xx_dmadesc_meta *meta; + int slot; + + ring = parse_cookie(dev, status->cookie, &slot); + if (unlikely(!ring)) + return; + assert(ring->tx); + ops = ring->ops; + while (1) { + assert(slot >= 0 && slot < ring->nr_slots); + desc = ops->idx2desc(ring, slot, &meta); + + if (meta->skb) + unmap_descbuffer(ring, meta->dmaaddr, meta->skb->len, 1); + else + unmap_descbuffer(ring, meta->dmaaddr, sizeof(struct bcm43xx_txhdr_fw4), 1); + + if (meta->is_last_fragment) { + assert(meta->skb); + /* Call back to inform the ieee80211 subsystem about the + * status of the transmission. + * Some fields of txstat are already filled in dma_tx(). + */ + if (status->acked) + meta->txstat.flags |= IEEE80211_TX_STATUS_ACK; + meta->txstat.retry_count = status->frame_count - 1; + ieee80211_tx_status_irqsafe(dev->wl->hw, meta->skb, &(meta->txstat)); + /* skb is freed by ieee80211_tx_status_irqsafe() */ + meta->skb = NULL; + } else { + /* No need to call free_descriptor_buffer here, as + * this is only the txhdr, which is not allocated. + */ + assert(meta->skb == NULL); + } + /* Everything belonging to the slot is unmapped + * and freed, so we can return it. + */ + return_slot(ring, slot); + + if (meta->is_last_fragment) + break; + slot = next_slot(ring, slot); + } + dev->stats.last_tx = jiffies; + if (ring->stopped) { + assert(free_slots(ring) >= SLOTS_PER_PACKET); + /* FIXME: we currently only have one queue */ + ieee80211_wake_queue(dev->wl->hw, 0); + ring->stopped = 0; + } +} + +void bcm43xx_dma_get_tx_stats(struct bcm43xx_wldev *dev, + struct ieee80211_tx_queue_stats *stats) +{ + struct bcm43xx_dma *dma = &dev->dma; + struct bcm43xx_dmaring *ring; + struct ieee80211_tx_queue_stats_data *data; + + ring = dma->tx_ring1; + data = &(stats->data[0]); + data->len = ring->used_slots / SLOTS_PER_PACKET; + data->limit = ring->nr_slots / SLOTS_PER_PACKET; + data->count = ring->nr_tx_packets; +} + +static void dma_rx(struct bcm43xx_dmaring *ring, + int *slot) +{ + const struct bcm43xx_dma_ops *ops = ring->ops; + struct bcm43xx_dmadesc_generic *desc; + struct bcm43xx_dmadesc_meta *meta; + struct bcm43xx_rxhdr_fw4 *rxhdr; + struct sk_buff *skb; + u16 len; + int err; + dma_addr_t dmaaddr; + + desc = ops->idx2desc(ring, *slot, &meta); + + sync_descbuffer_for_cpu(ring, meta->dmaaddr, ring->rx_buffersize); + skb = meta->skb; + + if (ring->index == 3) { + /* We received an xmit status. */ + struct bcm43xx_hwtxstatus *hw = (struct bcm43xx_hwtxstatus *)skb->data; + int i = 0; + + while (hw->cookie == 0) { + if (i > 100) + break; + i++; + udelay(2); + barrier(); + } + bcm43xx_handle_hwtxstatus(ring->dev, hw); + /* recycle the descriptor buffer. */ + sync_descbuffer_for_device(ring, meta->dmaaddr, ring->rx_buffersize); + + return; + } + rxhdr = (struct bcm43xx_rxhdr_fw4 *)skb->data; + len = le16_to_cpu(rxhdr->frame_len); + if (len == 0) { + int i = 0; + + do { + udelay(2); + barrier(); + len = le16_to_cpu(rxhdr->frame_len); + } while (len == 0 && i++ < 5); + if (unlikely(len == 0)) { + /* recycle the descriptor buffer. */ + sync_descbuffer_for_device(ring, meta->dmaaddr, + ring->rx_buffersize); + goto drop; + } + } + if (unlikely(len > ring->rx_buffersize)) { + /* The data did not fit into one descriptor buffer + * and is split over multiple buffers. + * This should never happen, as we try to allocate buffers + * big enough. So simply ignore this packet. + */ + int cnt = 0; + s32 tmp = len; + + while (1) { + desc = ops->idx2desc(ring, *slot, &meta); + /* recycle the descriptor buffer. */ + sync_descbuffer_for_device(ring, meta->dmaaddr, + ring->rx_buffersize); + *slot = next_slot(ring, *slot); + cnt++; + tmp -= ring->rx_buffersize; + if (tmp <= 0) + break; + } + printkl(KERN_ERR PFX "DMA RX buffer too small " + "(len: %u, buffer: %u, nr-dropped: %d)\n", + len, ring->rx_buffersize, cnt); + goto drop; + } + + dmaaddr = meta->dmaaddr; + err = setup_rx_descbuffer(ring, desc, meta, GFP_ATOMIC); + if (unlikely(err)) { + dprintkl(KERN_ERR PFX "DMA RX: setup_rx_descbuffer() failed\n"); + sync_descbuffer_for_device(ring, dmaaddr, + ring->rx_buffersize); + goto drop; + } + + unmap_descbuffer(ring, dmaaddr, ring->rx_buffersize, 0); + skb_put(skb, len + ring->frameoffset); + skb_pull(skb, ring->frameoffset); + + bcm43xx_rx(ring->dev, skb, rxhdr); +drop: + return; +} + +void bcm43xx_dma_rx(struct bcm43xx_dmaring *ring) +{ + const struct bcm43xx_dma_ops *ops = ring->ops; + int slot, current_slot; +#ifdef CONFIG_BCM43XX_MAC80211_DEBUG + int used_slots = 0; +#endif + + assert(!ring->tx); + current_slot = ops->get_current_rxslot(ring); + assert(current_slot >= 0 && current_slot < ring->nr_slots); + + slot = ring->current_slot; + for ( ; slot != current_slot; slot = next_slot(ring, slot)) { + dma_rx(ring, &slot); +#ifdef CONFIG_BCM43XX_MAC80211_DEBUG + if (++used_slots > ring->max_used_slots) + ring->max_used_slots = used_slots; +#endif + } + ops->set_current_rxslot(ring, slot); + ring->current_slot = slot; +} + +void bcm43xx_dma_tx_suspend(struct bcm43xx_dmaring *ring) +{ + assert(ring->tx); + bcm43xx_power_saving_ctl_bits(ring->dev, -1, 1); + ring->ops->tx_suspend(ring); +} + +void bcm43xx_dma_tx_resume(struct bcm43xx_dmaring *ring) +{ + assert(ring->tx); + ring->ops->tx_resume(ring); + bcm43xx_power_saving_ctl_bits(ring->dev, -1, -1); +} diff --git a/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_dma.h b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_dma.h new file mode 100644 index 0000000..94fac2a --- /dev/null +++ b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_dma.h @@ -0,0 +1,361 @@ +#ifndef BCM43xx_DMA_H_ +#define BCM43xx_DMA_H_ + +#include +#include +#include +#include +#include + +#include "bcm43xx.h" + + +/* DMA-Interrupt reasons. */ +#define BCM43xx_DMAIRQ_FATALMASK ((1 << 10) | (1 << 11) | (1 << 12) \ + | (1 << 14) | (1 << 15)) +#define BCM43xx_DMAIRQ_NONFATALMASK (1 << 13) +#define BCM43xx_DMAIRQ_RX_DONE (1 << 16) + + +/*** 32-bit DMA Engine. ***/ + +/* 32-bit DMA controller registers. */ +#define BCM43xx_DMA32_TXCTL 0x00 +#define BCM43xx_DMA32_TXENABLE 0x00000001 +#define BCM43xx_DMA32_TXSUSPEND 0x00000002 +#define BCM43xx_DMA32_TXLOOPBACK 0x00000004 +#define BCM43xx_DMA32_TXFLUSH 0x00000010 +#define BCM43xx_DMA32_TXADDREXT_MASK 0x00030000 +#define BCM43xx_DMA32_TXADDREXT_SHIFT 16 +#define BCM43xx_DMA32_TXRING 0x04 +#define BCM43xx_DMA32_TXINDEX 0x08 +#define BCM43xx_DMA32_TXSTATUS 0x0C +#define BCM43xx_DMA32_TXDPTR 0x00000FFF +#define BCM43xx_DMA32_TXSTATE 0x0000F000 +#define BCM43xx_DMA32_TXSTAT_DISABLED 0x00000000 +#define BCM43xx_DMA32_TXSTAT_ACTIVE 0x00001000 +#define BCM43xx_DMA32_TXSTAT_IDLEWAIT 0x00002000 +#define BCM43xx_DMA32_TXSTAT_STOPPED 0x00003000 +#define BCM43xx_DMA32_TXSTAT_SUSP 0x00004000 +#define BCM43xx_DMA32_TXERROR 0x000F0000 +#define BCM43xx_DMA32_TXERR_NOERR 0x00000000 +#define BCM43xx_DMA32_TXERR_PROT 0x00010000 +#define BCM43xx_DMA32_TXERR_UNDERRUN 0x00020000 +#define BCM43xx_DMA32_TXERR_BUFREAD 0x00030000 +#define BCM43xx_DMA32_TXERR_DESCREAD 0x00040000 +#define BCM43xx_DMA32_TXACTIVE 0xFFF00000 +#define BCM43xx_DMA32_RXCTL 0x10 +#define BCM43xx_DMA32_RXENABLE 0x00000001 +#define BCM43xx_DMA32_RXFROFF_MASK 0x000000FE +#define BCM43xx_DMA32_RXFROFF_SHIFT 1 +#define BCM43xx_DMA32_RXDIRECTFIFO 0x00000100 +#define BCM43xx_DMA32_RXADDREXT_MASK 0x00030000 +#define BCM43xx_DMA32_RXADDREXT_SHIFT 16 +#define BCM43xx_DMA32_RXRING 0x14 +#define BCM43xx_DMA32_RXINDEX 0x18 +#define BCM43xx_DMA32_RXSTATUS 0x1C +#define BCM43xx_DMA32_RXDPTR 0x00000FFF +#define BCM43xx_DMA32_RXSTATE 0x0000F000 +#define BCM43xx_DMA32_RXSTAT_DISABLED 0x00000000 +#define BCM43xx_DMA32_RXSTAT_ACTIVE 0x00001000 +#define BCM43xx_DMA32_RXSTAT_IDLEWAIT 0x00002000 +#define BCM43xx_DMA32_RXSTAT_STOPPED 0x00003000 +#define BCM43xx_DMA32_RXERROR 0x000F0000 +#define BCM43xx_DMA32_RXERR_NOERR 0x00000000 +#define BCM43xx_DMA32_RXERR_PROT 0x00010000 +#define BCM43xx_DMA32_RXERR_OVERFLOW 0x00020000 +#define BCM43xx_DMA32_RXERR_BUFWRITE 0x00030000 +#define BCM43xx_DMA32_RXERR_DESCREAD 0x00040000 +#define BCM43xx_DMA32_RXACTIVE 0xFFF00000 + +/* 32-bit DMA descriptor. */ +struct bcm43xx_dmadesc32 { + __le32 control; + __le32 address; +} __attribute__((__packed__)); +#define BCM43xx_DMA32_DCTL_BYTECNT 0x00001FFF +#define BCM43xx_DMA32_DCTL_ADDREXT_MASK 0x00030000 +#define BCM43xx_DMA32_DCTL_ADDREXT_SHIFT 16 +#define BCM43xx_DMA32_DCTL_DTABLEEND 0x10000000 +#define BCM43xx_DMA32_DCTL_IRQ 0x20000000 +#define BCM43xx_DMA32_DCTL_FRAMEEND 0x40000000 +#define BCM43xx_DMA32_DCTL_FRAMESTART 0x80000000 + + + +/*** 64-bit DMA Engine. ***/ + +/* 64-bit DMA controller registers. */ +#define BCM43xx_DMA64_TXCTL 0x00 +#define BCM43xx_DMA64_TXENABLE 0x00000001 +#define BCM43xx_DMA64_TXSUSPEND 0x00000002 +#define BCM43xx_DMA64_TXLOOPBACK 0x00000004 +#define BCM43xx_DMA64_TXFLUSH 0x00000010 +#define BCM43xx_DMA64_TXADDREXT_MASK 0x00030000 +#define BCM43xx_DMA64_TXADDREXT_SHIFT 16 +#define BCM43xx_DMA64_TXINDEX 0x04 +#define BCM43xx_DMA64_TXRINGLO 0x08 +#define BCM43xx_DMA64_TXRINGHI 0x0C +#define BCM43xx_DMA64_TXSTATUS 0x10 +#define BCM43xx_DMA64_TXSTATDPTR 0x00001FFF +#define BCM43xx_DMA64_TXSTAT 0xF0000000 +#define BCM43xx_DMA64_TXSTAT_DISABLED 0x00000000 +#define BCM43xx_DMA64_TXSTAT_ACTIVE 0x10000000 +#define BCM43xx_DMA64_TXSTAT_IDLEWAIT 0x20000000 +#define BCM43xx_DMA64_TXSTAT_STOPPED 0x30000000 +#define BCM43xx_DMA64_TXSTAT_SUSP 0x40000000 +#define BCM43xx_DMA64_TXERROR 0x14 +#define BCM43xx_DMA64_TXERRDPTR 0x0001FFFF +#define BCM43xx_DMA64_TXERR 0xF0000000 +#define BCM43xx_DMA64_TXERR_NOERR 0x00000000 +#define BCM43xx_DMA64_TXERR_PROT 0x10000000 +#define BCM43xx_DMA64_TXERR_UNDERRUN 0x20000000 +#define BCM43xx_DMA64_TXERR_TRANSFER 0x30000000 +#define BCM43xx_DMA64_TXERR_DESCREAD 0x40000000 +#define BCM43xx_DMA64_TXERR_CORE 0x50000000 +#define BCM43xx_DMA64_RXCTL 0x20 +#define BCM43xx_DMA64_RXENABLE 0x00000001 +#define BCM43xx_DMA64_RXFROFF_MASK 0x000000FE +#define BCM43xx_DMA64_RXFROFF_SHIFT 1 +#define BCM43xx_DMA64_RXDIRECTFIFO 0x00000100 +#define BCM43xx_DMA64_RXADDREXT_MASK 0x00030000 +#define BCM43xx_DMA64_RXADDREXT_SHIFT 16 +#define BCM43xx_DMA64_RXINDEX 0x24 +#define BCM43xx_DMA64_RXRINGLO 0x28 +#define BCM43xx_DMA64_RXRINGHI 0x2C +#define BCM43xx_DMA64_RXSTATUS 0x30 +#define BCM43xx_DMA64_RXSTATDPTR 0x00001FFF +#define BCM43xx_DMA64_RXSTAT 0xF0000000 +#define BCM43xx_DMA64_RXSTAT_DISABLED 0x00000000 +#define BCM43xx_DMA64_RXSTAT_ACTIVE 0x10000000 +#define BCM43xx_DMA64_RXSTAT_IDLEWAIT 0x20000000 +#define BCM43xx_DMA64_RXSTAT_STOPPED 0x30000000 +#define BCM43xx_DMA64_RXSTAT_SUSP 0x40000000 +#define BCM43xx_DMA64_RXERROR 0x34 +#define BCM43xx_DMA64_RXERRDPTR 0x0001FFFF +#define BCM43xx_DMA64_RXERR 0xF0000000 +#define BCM43xx_DMA64_RXERR_NOERR 0x00000000 +#define BCM43xx_DMA64_RXERR_PROT 0x10000000 +#define BCM43xx_DMA64_RXERR_UNDERRUN 0x20000000 +#define BCM43xx_DMA64_RXERR_TRANSFER 0x30000000 +#define BCM43xx_DMA64_RXERR_DESCREAD 0x40000000 +#define BCM43xx_DMA64_RXERR_CORE 0x50000000 + +/* 64-bit DMA descriptor. */ +struct bcm43xx_dmadesc64 { + __le32 control0; + __le32 control1; + __le32 address_low; + __le32 address_high; +} __attribute__((__packed__)); +#define BCM43xx_DMA64_DCTL0_DTABLEEND 0x10000000 +#define BCM43xx_DMA64_DCTL0_IRQ 0x20000000 +#define BCM43xx_DMA64_DCTL0_FRAMEEND 0x40000000 +#define BCM43xx_DMA64_DCTL0_FRAMESTART 0x80000000 +#define BCM43xx_DMA64_DCTL1_BYTECNT 0x00001FFF +#define BCM43xx_DMA64_DCTL1_ADDREXT_MASK 0x00030000 +#define BCM43xx_DMA64_DCTL1_ADDREXT_SHIFT 16 + + + +struct bcm43xx_dmadesc_generic { + union { + struct bcm43xx_dmadesc32 dma32; + struct bcm43xx_dmadesc64 dma64; + } __attribute__((__packed__)); +} __attribute__((__packed__)); + + +/* Misc DMA constants */ +#define BCM43xx_DMA_RINGMEMSIZE PAGE_SIZE +#define BCM43xx_DMA0_RX_FRAMEOFFSET 30 +#define BCM43xx_DMA3_RX_FRAMEOFFSET 0 + + +/* DMA engine tuning knobs */ +#define BCM43xx_TXRING_SLOTS 128 +#define BCM43xx_RXRING_SLOTS 64 +#define BCM43xx_DMA0_RX_BUFFERSIZE (2304 + 100) +#define BCM43xx_DMA3_RX_BUFFERSIZE 16 + + + +#ifdef CONFIG_BCM43XX_MAC80211_DMA + + +struct sk_buff; +struct bcm43xx_private; +struct bcm43xx_txstatus; + + +struct bcm43xx_dmadesc_meta { + /* The kernel DMA-able buffer. */ + struct sk_buff *skb; + /* DMA base bus-address of the descriptor buffer. */ + dma_addr_t dmaaddr; + /* ieee80211 TX status. Only used once per 802.11 frag. */ + u8 is_last_fragment; + struct ieee80211_tx_status txstat; +}; + +struct bcm43xx_dmaring; + +/* Lowlevel DMA operations that differ between 32bit and 64bit DMA. */ +struct bcm43xx_dma_ops { + struct bcm43xx_dmadesc_generic * (*idx2desc)(struct bcm43xx_dmaring *ring, + int slot, + struct bcm43xx_dmadesc_meta **meta); + void (*fill_descriptor)(struct bcm43xx_dmaring *ring, + struct bcm43xx_dmadesc_generic *desc, + dma_addr_t dmaaddr, u16 bufsize, + int start, int end, int irq); + void (*poke_tx)(struct bcm43xx_dmaring *ring, int slot); + void (*tx_suspend)(struct bcm43xx_dmaring *ring); + void (*tx_resume)(struct bcm43xx_dmaring *ring); + int (*get_current_rxslot)(struct bcm43xx_dmaring *ring); + void (*set_current_rxslot)(struct bcm43xx_dmaring *ring, int slot); +}; + +struct bcm43xx_dmaring { + /* Lowlevel DMA ops. */ + const struct bcm43xx_dma_ops *ops; + /* Kernel virtual base address of the ring memory. */ + void *descbase; + /* Meta data about all descriptors. */ + struct bcm43xx_dmadesc_meta *meta; + /* Cache of TX headers for each slot. + * This is to avoid an allocation on each TX. + * This is NULL for an RX ring. + */ + u8 *txhdr_cache; + /* (Unadjusted) DMA base bus-address of the ring memory. */ + dma_addr_t dmabase; + /* Number of descriptor slots in the ring. */ + int nr_slots; + /* Number of used descriptor slots. */ + int used_slots; + /* Currently used slot in the ring. */ + int current_slot; + /* Total number of packets sent. Statistics only. */ + unsigned int nr_tx_packets; + /* Frameoffset in octets. */ + u32 frameoffset; + /* Descriptor buffer size. */ + u16 rx_buffersize; + /* The MMIO base register of the DMA controller. */ + u16 mmio_base; + /* DMA controller index number (0-5). */ + int index; + /* Boolean. Is this a TX ring? */ + u8 tx; + /* Boolean. 64bit DMA if true, 32bit DMA otherwise. */ + u8 dma64; + /* Boolean. Is this ring stopped at ieee80211 level? */ + u8 stopped; + struct bcm43xx_wldev *dev; +#ifdef CONFIG_BCM43XX_MAC80211_DEBUG + /* Maximum number of used slots. */ + int max_used_slots; +#endif /* CONFIG_BCM43XX_MAC80211_DEBUG*/ +}; + + +static inline +u32 bcm43xx_dma_read(struct bcm43xx_dmaring *ring, + u16 offset) +{ + return bcm43xx_read32(ring->dev, ring->mmio_base + offset); +} + +static inline +void bcm43xx_dma_write(struct bcm43xx_dmaring *ring, + u16 offset, u32 value) +{ + bcm43xx_write32(ring->dev, ring->mmio_base + offset, value); +} + + +int bcm43xx_dma_init(struct bcm43xx_wldev *dev); +void bcm43xx_dma_free(struct bcm43xx_wldev *dev); + +int bcm43xx_dmacontroller_rx_reset(struct bcm43xx_wldev *dev, + u16 dmacontroller_mmio_base, + int dma64); +int bcm43xx_dmacontroller_tx_reset(struct bcm43xx_wldev *dev, + u16 dmacontroller_mmio_base, + int dma64); + +u16 bcm43xx_dmacontroller_base(int dma64bit, int dmacontroller_idx); + +void bcm43xx_dma_tx_suspend(struct bcm43xx_dmaring *ring); +void bcm43xx_dma_tx_resume(struct bcm43xx_dmaring *ring); + +void bcm43xx_dma_get_tx_stats(struct bcm43xx_wldev *dev, + struct ieee80211_tx_queue_stats *stats); + +int bcm43xx_dma_tx(struct bcm43xx_wldev *dev, + struct sk_buff *skb, + struct ieee80211_tx_control *ctl); +void bcm43xx_dma_handle_txstatus(struct bcm43xx_wldev *dev, + const struct bcm43xx_txstatus *status); + +void bcm43xx_dma_rx(struct bcm43xx_dmaring *ring); + +#else /* CONFIG_BCM43XX_MAC80211_DMA */ + + +static inline +int bcm43xx_dma_init(struct bcm43xx_wldev *dev) +{ + return 0; +} +static inline +void bcm43xx_dma_free(struct bcm43xx_wldev *dev) +{ +} +static inline +int bcm43xx_dmacontroller_rx_reset(struct bcm43xx_wldev *dev, + u16 dmacontroller_mmio_base, + int dma64) +{ + return 0; +} +static inline +int bcm43xx_dmacontroller_tx_reset(struct bcm43xx_wldev *dev, + u16 dmacontroller_mmio_base, + int dma64) +{ + return 0; +} +static inline +void bcm43xx_dma_get_tx_stats(struct bcm43xx_wldev *dev, + struct ieee80211_tx_queue_stats *stats) +{ +} +static inline +int bcm43xx_dma_tx(struct bcm43xx_wldev *dev, + struct sk_buff *skb, + struct ieee80211_tx_control *ctl) +{ + return 0; +} +static inline +void bcm43xx_dma_handle_txstatus(struct bcm43xx_wldev *dev, + const struct bcm43xx_txstatus *status) +{ +} +static inline +void bcm43xx_dma_rx(struct bcm43xx_dmaring *ring) +{ +} +static inline +void bcm43xx_dma_tx_suspend(struct bcm43xx_dmaring *ring) +{ +} +static inline +void bcm43xx_dma_tx_resume(struct bcm43xx_dmaring *ring) +{ +} + +#endif /* CONFIG_BCM43XX_MAC80211_DMA */ +#endif /* BCM43xx_DMA_H_ */ diff --git a/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_leds.c b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_leds.c new file mode 100644 index 0000000..dfa0121 --- /dev/null +++ b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_leds.c @@ -0,0 +1,300 @@ +/* + + Broadcom BCM43xx wireless driver + + Copyright (c) 2005 Martin Langer , + Stefano Brivio + Michael Buesch + Danny van Dyk + Andreas Jaggi + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "bcm43xx_leds.h" +#include "bcm43xx.h" +#include "bcm43xx_main.h" + +static void bcm43xx_led_changestate(struct bcm43xx_led *led) +{ + struct bcm43xx_wldev *dev = led->dev; + const int index = bcm43xx_led_index(led); + const u16 mask = (1 << index); + u16 ledctl; + + assert(index >= 0 && index < BCM43xx_NR_LEDS); + assert(led->blink_interval); + ledctl = bcm43xx_read16(dev, BCM43xx_MMIO_GPIO_CONTROL); + ledctl = (ledctl & mask) ? (ledctl & ~mask) : (ledctl | mask); + bcm43xx_write16(dev, BCM43xx_MMIO_GPIO_CONTROL, ledctl); +} + +static void bcm43xx_led_blink(unsigned long d) +{ + struct bcm43xx_led *led = (struct bcm43xx_led *)d; + struct bcm43xx_wldev *dev = led->dev; + unsigned long flags; + + spin_lock_irqsave(&dev->wl->leds_lock, flags); + if (led->blink_interval) { + bcm43xx_led_changestate(led); + mod_timer(&led->blink_timer, jiffies + led->blink_interval); + } + spin_unlock_irqrestore(&dev->wl->leds_lock, flags); +} + +static void bcm43xx_led_blink_start(struct bcm43xx_led *led, + unsigned long interval) +{ + if (led->blink_interval) + return; + led->blink_interval = interval; + bcm43xx_led_changestate(led); + led->blink_timer.expires = jiffies + interval; + add_timer(&led->blink_timer); +} + +static void bcm43xx_led_blink_stop(struct bcm43xx_led *led, int sync) +{ + struct bcm43xx_wldev *dev = led->dev; + const int index = bcm43xx_led_index(led); + u16 ledctl; + + if (!led->blink_interval) + return; + if (unlikely(sync)) + del_timer_sync(&led->blink_timer); + else + del_timer(&led->blink_timer); + led->blink_interval = 0; + + /* Make sure the LED is turned off. */ + assert(index >= 0 && index < BCM43xx_NR_LEDS); + ledctl = bcm43xx_read16(dev, BCM43xx_MMIO_GPIO_CONTROL); + if (led->activelow) + ledctl |= (1 << index); + else + ledctl &= ~(1 << index); + bcm43xx_write16(dev, BCM43xx_MMIO_GPIO_CONTROL, ledctl); +} + +static void bcm43xx_led_init_hardcoded(struct bcm43xx_wldev *dev, + struct bcm43xx_led *led, + int led_index) +{ + struct ssb_bus *bus = dev->dev->bus; + + /* This function is called, if the behaviour (and activelow) + * information for a LED is missing in the SPROM. + * We hardcode the behaviour values for various devices here. + * Note that the BCM43xx_LED_TEST_XXX behaviour values can + * be used to figure out which led is mapped to which index. + */ + + switch (led_index) { + case 0: + led->behaviour = BCM43xx_LED_ACTIVITY; + led->activelow = 1; + if (bus->board_vendor == PCI_VENDOR_ID_COMPAQ) + led->behaviour = BCM43xx_LED_RADIO_ALL; + break; + case 1: + led->behaviour = BCM43xx_LED_RADIO_B; + if (bus->board_vendor == PCI_VENDOR_ID_ASUSTEK) + led->behaviour = BCM43xx_LED_ASSOC; + break; + case 2: + led->behaviour = BCM43xx_LED_RADIO_A; + break; + case 3: + led->behaviour = BCM43xx_LED_OFF; + break; + default: + assert(0); + } +} + +int bcm43xx_leds_init(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_led *led; + u8 sprom[4]; + int i; + + sprom[0] = dev->dev->bus->sprom.r1.gpio0; + sprom[1] = dev->dev->bus->sprom.r1.gpio1; + sprom[2] = dev->dev->bus->sprom.r1.gpio2; + sprom[3] = dev->dev->bus->sprom.r1.gpio3; + + for (i = 0; i < BCM43xx_NR_LEDS; i++) { + led = &(dev->leds[i]); + led->dev = dev; + setup_timer(&led->blink_timer, + bcm43xx_led_blink, + (unsigned long)led); + + if (sprom[i] == 0xFF) { + bcm43xx_led_init_hardcoded(dev, led, i); + } else { + led->behaviour = sprom[i] & BCM43xx_LED_BEHAVIOUR; + led->activelow = !!(sprom[i] & BCM43xx_LED_ACTIVELOW); + } + } + + return 0; +} + +void bcm43xx_leds_exit(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_led *led; + int i; + + for (i = 0; i < BCM43xx_NR_LEDS; i++) { + led = &(dev->leds[i]); + bcm43xx_led_blink_stop(led, 1); + } + bcm43xx_leds_switch_all(dev, 0); +} + +void bcm43xx_leds_update(struct bcm43xx_wldev *dev, int activity) +{ + struct bcm43xx_led *led; + struct bcm43xx_phy *phy = &dev->phy; + const int transferring = (jiffies - dev->stats.last_tx) < BCM43xx_LED_XFER_THRES; + int i, turn_on; + unsigned long interval = 0; + u16 ledctl; + unsigned long flags; + + spin_lock_irqsave(&dev->wl->leds_lock, flags); + ledctl = bcm43xx_read16(dev, BCM43xx_MMIO_GPIO_CONTROL); + for (i = 0; i < BCM43xx_NR_LEDS; i++) { + led = &(dev->leds[i]); + + turn_on = 0; + switch (led->behaviour) { + case BCM43xx_LED_INACTIVE: + continue; + case BCM43xx_LED_OFF: + break; + case BCM43xx_LED_ON: + turn_on = 1; + break; + case BCM43xx_LED_ACTIVITY: + turn_on = activity; + break; + case BCM43xx_LED_RADIO_ALL: + turn_on = phy->radio_on && bcm43xx_is_hw_radio_enabled(dev); + break; + case BCM43xx_LED_RADIO_A: + turn_on = (phy->radio_on && bcm43xx_is_hw_radio_enabled(dev) + && phy->type == BCM43xx_PHYTYPE_A); + break; + case BCM43xx_LED_RADIO_B: + turn_on = (phy->radio_on && bcm43xx_is_hw_radio_enabled(dev) && + (phy->type == BCM43xx_PHYTYPE_B || + phy->type == BCM43xx_PHYTYPE_G)); + break; + case BCM43xx_LED_MODE_BG: + if (phy->type == BCM43xx_PHYTYPE_G && bcm43xx_is_hw_radio_enabled(dev) && + 1/*FIXME: using G rates.*/) + turn_on = 1; + break; + case BCM43xx_LED_TRANSFER: + if (transferring) + bcm43xx_led_blink_start(led, BCM43xx_LEDBLINK_MEDIUM); + else + bcm43xx_led_blink_stop(led, 0); + continue; + case BCM43xx_LED_APTRANSFER: + if (bcm43xx_is_mode(dev->wl, IEEE80211_IF_TYPE_AP)) { + if (transferring) { + interval = BCM43xx_LEDBLINK_FAST; + turn_on = 1; + } + } else { + turn_on = 1; + if (0/*TODO: not assoc*/) + interval = BCM43xx_LEDBLINK_SLOW; + else if (transferring) + interval = BCM43xx_LEDBLINK_FAST; + else + turn_on = 0; + } + if (turn_on) + bcm43xx_led_blink_start(led, interval); + else + bcm43xx_led_blink_stop(led, 0); + continue; + case BCM43xx_LED_WEIRD: + //TODO + break; + case BCM43xx_LED_ASSOC: + if (1/*dev->softmac->associated*/) + turn_on = 1; + break; +#ifdef CONFIG_BCM43XX_DEBUG + case BCM43xx_LED_TEST_BLINKSLOW: + bcm43xx_led_blink_start(led, BCM43xx_LEDBLINK_SLOW); + continue; + case BCM43xx_LED_TEST_BLINKMEDIUM: + bcm43xx_led_blink_start(led, BCM43xx_LEDBLINK_MEDIUM); + continue; + case BCM43xx_LED_TEST_BLINKFAST: + bcm43xx_led_blink_start(led, BCM43xx_LEDBLINK_FAST); + continue; +#endif /* CONFIG_BCM43XX_DEBUG */ + default: + assert(0); + }; + + if (led->activelow) + turn_on = !turn_on; + if (turn_on) + ledctl |= (1 << i); + else + ledctl &= ~(1 << i); + } + bcm43xx_write16(dev, BCM43xx_MMIO_GPIO_CONTROL, ledctl); + spin_unlock_irqrestore(&dev->wl->leds_lock, flags); +} + +void bcm43xx_leds_switch_all(struct bcm43xx_wldev *dev, int on) +{ + struct bcm43xx_led *led; + u16 ledctl; + int i; + int bit_on; + unsigned long flags; + + spin_lock_irqsave(&dev->wl->leds_lock, flags); + ledctl = bcm43xx_read16(dev, BCM43xx_MMIO_GPIO_CONTROL); + for (i = 0; i < BCM43xx_NR_LEDS; i++) { + led = &(dev->leds[i]); + if (led->behaviour == BCM43xx_LED_INACTIVE) + continue; + if (on) + bit_on = led->activelow ? 0 : 1; + else + bit_on = led->activelow ? 1 : 0; + if (bit_on) + ledctl |= (1 << i); + else + ledctl &= ~(1 << i); + } + bcm43xx_write16(dev, BCM43xx_MMIO_GPIO_CONTROL, ledctl); + spin_unlock_irqrestore(&dev->wl->leds_lock, flags); +} diff --git a/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_leds.h b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_leds.h new file mode 100644 index 0000000..ad35047 --- /dev/null +++ b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_leds.h @@ -0,0 +1,56 @@ +#ifndef BCM43xx_LEDS_H_ +#define BCM43xx_LEDS_H_ + +#include +#include + + +struct bcm43xx_led { + u8 behaviour:7; + u8 activelow:1; + + struct bcm43xx_wldev *dev; + struct timer_list blink_timer; + unsigned long blink_interval; +}; +#define bcm43xx_led_index(led) ((int)((led) - (led)->dev->leds)) + +/* Delay between state changes when blinking in jiffies */ +#define BCM43xx_LEDBLINK_SLOW (HZ / 1) +#define BCM43xx_LEDBLINK_MEDIUM (HZ / 4) +#define BCM43xx_LEDBLINK_FAST (HZ / 8) + +#define BCM43xx_LED_XFER_THRES (HZ / 100) + +#define BCM43xx_LED_BEHAVIOUR 0x7F +#define BCM43xx_LED_ACTIVELOW 0x80 +enum { /* LED behaviour values */ + BCM43xx_LED_OFF, + BCM43xx_LED_ON, + BCM43xx_LED_ACTIVITY, + BCM43xx_LED_RADIO_ALL, + BCM43xx_LED_RADIO_A, + BCM43xx_LED_RADIO_B, + BCM43xx_LED_MODE_BG, + BCM43xx_LED_TRANSFER, + BCM43xx_LED_APTRANSFER, + BCM43xx_LED_WEIRD,//FIXME + BCM43xx_LED_ASSOC, + BCM43xx_LED_INACTIVE, + + /* Behaviour values for testing. + * With these values it is easier to figure out + * the real behaviour of leds, in case the SPROM + * is missing information. + */ + BCM43xx_LED_TEST_BLINKSLOW, + BCM43xx_LED_TEST_BLINKMEDIUM, + BCM43xx_LED_TEST_BLINKFAST, +}; + +int bcm43xx_leds_init(struct bcm43xx_wldev *dev); +void bcm43xx_leds_exit(struct bcm43xx_wldev *dev); +void bcm43xx_leds_update(struct bcm43xx_wldev *dev, int activity); +void bcm43xx_leds_switch_all(struct bcm43xx_wldev *dev, int on); + +#endif /* BCM43xx_LEDS_H_ */ diff --git a/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_lo.c b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_lo.c new file mode 100644 index 0000000..d60f94e --- /dev/null +++ b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_lo.c @@ -0,0 +1,1110 @@ +/* + + Broadcom BCM43xx wireless driver + + LO (LocalOscillator) Measuring and Control routines + + Copyright (c) 2005 Martin Langer , + Copyright (c) 2005, 2006 Stefano Brivio + Copyright (c) 2005-2007 Michael Buesch + Copyright (c) 2005, 2006 Danny van Dyk + Copyright (c) 2005, 2006 Andreas Jaggi + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "bcm43xx.h" +#include "bcm43xx_lo.h" +#include "bcm43xx_phy.h" +#include "bcm43xx_main.h" + +#include + + +/* Write the LocalOscillator Control (adjust) value-pair. */ +static void bcm43xx_lo_write(struct bcm43xx_wldev *dev, + struct bcm43xx_loctl *control) +{ + struct bcm43xx_phy *phy = &dev->phy; + u16 value; + u16 reg; + + if (BCM43xx_DEBUG) { + if (unlikely(abs(control->i) > 16 || + abs(control->q) > 16)) { + printk(KERN_ERR PFX "ERROR: Invalid LO control pair " + "(I: %d, Q: %d)\n", + control->i, control->q); + dump_stack(); + return; + } + } + + value = (u8)(control->q); + value |= ((u8)(control->i)) << 8; + + reg = (phy->type == BCM43xx_PHYTYPE_B) ? 0x002F : BCM43xx_PHY_LO_CTL; + bcm43xx_phy_write(dev, reg, value); +} + +static inline +void assert_rfatt_and_bbatt(const struct bcm43xx_rfatt *rfatt, + const struct bcm43xx_bbatt *bbatt) +{ + if (BCM43xx_DEBUG) { + int err = 0; + + if (unlikely(rfatt->att >= 16)) { + dprintk(KERN_ERR PFX "ERROR: invalid rf_att: %u\n", + rfatt->att); + err = 1; + } + if (unlikely(bbatt->att >= 9)) { + dprintk(KERN_ERR PFX "ERROR: invalid bband_att: %u\n", + bbatt->att); + err = 1; + } + if (unlikely(err)) + dump_stack(); + } +} + +static +struct bcm43xx_loctl * bcm43xx_get_loctl_nopadmix(struct bcm43xx_wldev *dev, + const struct bcm43xx_rfatt *rfatt, + const struct bcm43xx_bbatt *bbatt) +{ + struct bcm43xx_phy *phy = &dev->phy; + struct bcm43xx_txpower_lo_control *lo = phy->lo_control; + + assert_rfatt_and_bbatt(rfatt, bbatt); + return &(lo->no_padmix[bbatt->att][rfatt->att]); +} + +struct bcm43xx_loctl * bcm43xx_get_loctl(struct bcm43xx_wldev *dev, + const struct bcm43xx_rfatt *rfatt, + const struct bcm43xx_bbatt *bbatt) +{ + struct bcm43xx_phy *phy = &dev->phy; + struct bcm43xx_txpower_lo_control *lo = phy->lo_control; + struct bcm43xx_loctl *ret; + + assert_rfatt_and_bbatt(rfatt, bbatt); + if (rfatt->with_padmix) + ret = &(lo->with_padmix[bbatt->att][rfatt->att]); + else + ret = &(lo->no_padmix[bbatt->att][rfatt->att]); + + return ret; +} + +/* Call a function for every possible LO control value-pair. */ +static int bcm43xx_call_for_each_loctl(struct bcm43xx_wldev *dev, + int (*func)(struct bcm43xx_wldev *, + struct bcm43xx_loctl *)) +{ + struct bcm43xx_phy *phy = &dev->phy; + struct bcm43xx_txpower_lo_control *ctl = phy->lo_control; + int i, j; + int err; + + for (i = 0; i < BCM43xx_NR_BB; i++) { + for (j = 0; j < BCM43xx_NR_RF; j++) { + err = func(dev, &(ctl->with_padmix[i][j])); + if (unlikely(err)) + return err; + } + } + for (i = 0; i < BCM43xx_NR_BB; i++) { + for (j = 0; j < BCM43xx_NR_RF; j++) { + err = func(dev, &(ctl->no_padmix[i][j])); + if (unlikely(err)) + return err; + } + } + + return 0; +} + +static u16 lo_b_r15_loop(struct bcm43xx_wldev *dev) +{ + int i; + u16 ret = 0; + + for (i = 0; i < 10; i++){ + bcm43xx_phy_write(dev, 0x0015, 0xAFA0); + udelay(1); + bcm43xx_phy_write(dev, 0x0015, 0xEFA0); + udelay(10); + bcm43xx_phy_write(dev, 0x0015, 0xFFA0); + udelay(40); + ret += bcm43xx_phy_read(dev, 0x002C); + } + + return ret; +} + +void bcm43xx_lo_b_measure(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + u16 regstack[12] = { 0 }; + u16 mls; + u16 fval; + int i, j; + + regstack[0] = bcm43xx_phy_read(dev, 0x0015); + regstack[1] = bcm43xx_radio_read16(dev, 0x0052) & 0xFFF0; + + if (phy->radio_ver == 0x2053) { + regstack[2] = bcm43xx_phy_read(dev, 0x000A); + regstack[3] = bcm43xx_phy_read(dev, 0x002A); + regstack[4] = bcm43xx_phy_read(dev, 0x0035); + regstack[5] = bcm43xx_phy_read(dev, 0x0003); + regstack[6] = bcm43xx_phy_read(dev, 0x0001); + regstack[7] = bcm43xx_phy_read(dev, 0x0030); + + regstack[8] = bcm43xx_radio_read16(dev, 0x0043); + regstack[9] = bcm43xx_radio_read16(dev, 0x007A); + regstack[10] = bcm43xx_read16(dev, 0x03EC); + regstack[11] = bcm43xx_radio_read16(dev, 0x0052) & 0x00F0; + + bcm43xx_phy_write(dev, 0x0030, 0x00FF); + bcm43xx_write16(dev, 0x03EC, 0x3F3F); + bcm43xx_phy_write(dev, 0x0035, regstack[4] & 0xFF7F); + bcm43xx_radio_write16(dev, 0x007A, regstack[9] & 0xFFF0); + } + bcm43xx_phy_write(dev, 0x0015, 0xB000); + bcm43xx_phy_write(dev, 0x002B, 0x0004); + + if (phy->radio_ver == 0x2053) { + bcm43xx_phy_write(dev, 0x002B, 0x0203); + bcm43xx_phy_write(dev, 0x002A, 0x08A3); + } + + phy->minlowsig[0] = 0xFFFF; + + for (i = 0; i < 4; i++) { + bcm43xx_radio_write16(dev, 0x0052, regstack[1] | i); + lo_b_r15_loop(dev); + } + for (i = 0; i < 10; i++) { + bcm43xx_radio_write16(dev, 0x0052, regstack[1] | i); + mls = lo_b_r15_loop(dev) / 10; + if (mls < phy->minlowsig[0]) { + phy->minlowsig[0] = mls; + phy->minlowsigpos[0] = i; + } + } + bcm43xx_radio_write16(dev, 0x0052, regstack[1] | phy->minlowsigpos[0]); + + phy->minlowsig[1] = 0xFFFF; + + for (i = -4; i < 5; i += 2) { + for (j = -4; j < 5; j += 2) { + if (j < 0) + fval = (0x0100 * i) + j + 0x0100; + else + fval = (0x0100 * i) + j; + bcm43xx_phy_write(dev, 0x002F, fval); + mls = lo_b_r15_loop(dev) / 10; + if (mls < phy->minlowsig[1]) { + phy->minlowsig[1] = mls; + phy->minlowsigpos[1] = fval; + } + } + } + phy->minlowsigpos[1] += 0x0101; + + bcm43xx_phy_write(dev, 0x002F, phy->minlowsigpos[1]); + if (phy->radio_ver == 0x2053) { + bcm43xx_phy_write(dev, 0x000A, regstack[2]); + bcm43xx_phy_write(dev, 0x002A, regstack[3]); + bcm43xx_phy_write(dev, 0x0035, regstack[4]); + bcm43xx_phy_write(dev, 0x0003, regstack[5]); + bcm43xx_phy_write(dev, 0x0001, regstack[6]); + bcm43xx_phy_write(dev, 0x0030, regstack[7]); + + bcm43xx_radio_write16(dev, 0x0043, regstack[8]); + bcm43xx_radio_write16(dev, 0x007A, regstack[9]); + + bcm43xx_radio_write16(dev, 0x0052, + (bcm43xx_radio_read16(dev, 0x0052) & 0x000F) + | regstack[11]); + + bcm43xx_write16(dev, 0x03EC, regstack[10]); + } + bcm43xx_phy_write(dev, 0x0015, regstack[0]); +} + +static u16 lo_measure_feedthrough(struct bcm43xx_wldev *dev, + u16 lna, u16 pga, u16 trsw_rx) +{ + struct bcm43xx_phy *phy = &dev->phy; + u16 rfover; + + if (phy->gmode) { + lna <<= BCM43xx_PHY_RFOVERVAL_LNA_SHIFT; + pga <<= BCM43xx_PHY_RFOVERVAL_PGA_SHIFT; + + assert((lna & ~BCM43xx_PHY_RFOVERVAL_LNA) == 0); + assert((pga & ~BCM43xx_PHY_RFOVERVAL_PGA) == 0); +/*FIXME This assertion fails assert((trsw_rx & ~(BCM43xx_PHY_RFOVERVAL_TRSWRX | + BCM43xx_PHY_RFOVERVAL_BW)) == 0); +*/ +trsw_rx &= (BCM43xx_PHY_RFOVERVAL_TRSWRX | BCM43xx_PHY_RFOVERVAL_BW); + + /* Construct the RF Override Value */ + rfover = BCM43xx_PHY_RFOVERVAL_UNK; + rfover |= pga; + rfover |= lna; + rfover |= trsw_rx; + if ((dev->dev->bus->sprom.r1.boardflags_lo & BCM43xx_BFL_EXTLNA) && + phy->rev > 6) + rfover |= BCM43xx_PHY_RFOVERVAL_EXTLNA; + + bcm43xx_phy_write(dev, BCM43xx_PHY_PGACTL, 0xE300); + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVERVAL, rfover); + udelay(10); + rfover |= BCM43xx_PHY_RFOVERVAL_BW_LBW; + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVERVAL, rfover); + udelay(10); + rfover |= BCM43xx_PHY_RFOVERVAL_BW_LPF; + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVERVAL, rfover); + udelay(10); + bcm43xx_phy_write(dev, BCM43xx_PHY_PGACTL, 0xF300); + } else { + pga |= BCM43xx_PHY_PGACTL_UNKNOWN; + bcm43xx_phy_write(dev, BCM43xx_PHY_PGACTL, pga); + udelay(10); + pga |= BCM43xx_PHY_PGACTL_LOWBANDW; + bcm43xx_phy_write(dev, BCM43xx_PHY_PGACTL, pga); + udelay(10); + pga |= BCM43xx_PHY_PGACTL_LPF; + bcm43xx_phy_write(dev, BCM43xx_PHY_PGACTL, pga); + } + udelay(21); + + return bcm43xx_phy_read(dev, BCM43xx_PHY_LO_LEAKAGE); +} + +/* TXCTL Register and Value Table. + * Returns the "TXCTL Register". + * "value" is the "TXCTL Value". + * "pad_mix_gain" is the PAD Mixer Gain. + */ +static u16 lo_txctl_register_table(struct bcm43xx_wldev *dev, + u16 *value, u16 *pad_mix_gain) +{ + struct bcm43xx_phy *phy = &dev->phy; + u16 reg, v, padmix; + + if (phy->type == BCM43xx_PHYTYPE_B) { + v = 0x30; + if (phy->radio_rev <= 5) { + reg = 0x43; + padmix = 0; + } else { + reg = 0x52; + padmix = 5; + } + } else { + if (phy->rev >= 2 && phy->radio_rev == 8) { + reg = 0x43; + v = 0x10; + padmix = 2; + } else { + reg = 0x52; + v = 0x30; + padmix = 5; + } + } + if (value) + *value = v; + if (pad_mix_gain) + *pad_mix_gain = padmix; + + return reg; +} + +static void lo_measure_txctl_values(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + struct bcm43xx_txpower_lo_control *lo = phy->lo_control; + u16 reg, mask; + u16 trsw_rx, pga; + u16 radio_pctl_reg; + + static const u8 tx_bias_values[] = { + 0x09, 0x08, 0x0A, 0x01, 0x00, + 0x02, 0x05, 0x04, 0x06, + }; + static const u8 tx_magn_values[] = { + 0x70, 0x40, + }; + + if (!has_loopback_gain(phy)) { + radio_pctl_reg = 6; + trsw_rx = 2; + pga = 0; + } else { + int lb_gain; /* Loopback gain (in dB) */ + + trsw_rx = 0; + lb_gain = phy->max_lb_gain / 2; + if (lb_gain > 10) { + radio_pctl_reg = 0; + pga = abs(10 - lb_gain) / 6; + pga = limit_value(pga, 0, 15); + } else { + int cmp_val; + int tmp; + + pga = 0; + cmp_val = 0x24; + if ((phy->rev >= 2) && + (phy->radio_ver == 0x2050) && + (phy->radio_rev == 8)) + cmp_val = 0x3C; + tmp = lb_gain; + if ((10 - lb_gain) < cmp_val) + tmp = (10 - lb_gain); + if (tmp < 0) + tmp += 6; + else + tmp += 3; + cmp_val /= 4; + tmp /= 4; + if (tmp >= cmp_val) + radio_pctl_reg = cmp_val; + else + radio_pctl_reg = tmp; + } + } + bcm43xx_radio_write16(dev, 0x43, + (bcm43xx_radio_read16(dev, 0x43) + & 0xFFF0) | radio_pctl_reg); + bcm43xx_phy_set_baseband_attenuation(dev, 2); + + reg = lo_txctl_register_table(dev, &mask, NULL); + mask = ~mask; + bcm43xx_radio_write16(dev, reg, + bcm43xx_radio_read16(dev, reg) + & mask); + + if (has_tx_magnification(phy)) { + int i, j; + int feedthrough; + int min_feedth = 0xFFFF; + u8 tx_magn, tx_bias; + + for (i = 0; i < ARRAY_SIZE(tx_magn_values); i++) { + tx_magn = tx_magn_values[i]; + bcm43xx_radio_write16(dev, 0x52, + (bcm43xx_radio_read16(dev, 0x52) + & 0xFF0F) | tx_magn); + for (j = 0; j < ARRAY_SIZE(tx_bias_values); j++) { + tx_bias = tx_bias_values[j]; + bcm43xx_radio_write16(dev, 0x52, + (bcm43xx_radio_read16(dev, 0x52) + & 0xFFF0) | tx_bias); + feedthrough = lo_measure_feedthrough(dev, 0, pga, trsw_rx); + if (feedthrough < min_feedth) { + lo->tx_bias = tx_bias; + lo->tx_magn = tx_magn; + min_feedth = feedthrough; + } + if (lo->tx_bias == 0) + break; + } + bcm43xx_radio_write16(dev, 0x52, + (bcm43xx_radio_read16(dev, 0x52) + & 0xFF00) | lo->tx_bias | lo->tx_magn); + } + } else { + lo->tx_magn = 0; /* unused */ + bcm43xx_radio_write16(dev, 0x52, + bcm43xx_radio_read16(dev, 0x52) + & 0xFFF0); /* TX bias == 0 */ + } +} + +static void lo_read_power_vector(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + struct bcm43xx_txpower_lo_control *lo = phy->lo_control; + u16 i; + u64 tmp; + u64 power_vector = 0; + int rf_offset, bb_offset; + struct bcm43xx_loctl *loctl; + + for (i = 0; i < 8; i += 2) { + tmp = bcm43xx_shm_read16(dev, BCM43xx_SHM_SHARED, + 0x310 + i); + /* Clear the top byte. We get holes in the bitmap... */ + tmp &= 0xFF; + power_vector |= (tmp << (i * 8)); + /* Clear the vector on the device. */ + bcm43xx_shm_write16(dev, BCM43xx_SHM_SHARED, + 0x310 + i, 0); + } + + if (power_vector) + lo->power_vector = power_vector; + power_vector = lo->power_vector; + + for (i = 0; i < 64; i++) { + if (power_vector & ((u64)1ULL << i)) { + /* Now figure out which bcm43xx_loctl corresponds + * to this bit. + */ + rf_offset = i / lo->rfatt_list.len; + bb_offset = i % lo->rfatt_list.len;//FIXME? + loctl = bcm43xx_get_loctl(dev, &lo->rfatt_list.list[rf_offset], + &lo->bbatt_list.list[bb_offset]); + /* And mark it as "used", as the device told us + * through the bitmap it is using it. + */ + loctl->used = 1; + } + } +} + +/* 802.11/LO/GPHY/MeasuringGains */ +static void lo_measure_gain_values(struct bcm43xx_wldev *dev, + s16 max_rx_gain, + int use_trsw_rx) +{ + struct bcm43xx_phy *phy = &dev->phy; + u16 tmp; + + if (max_rx_gain < 0) + max_rx_gain = 0; + + if (has_loopback_gain(phy)) { + int trsw_rx = 0; + int trsw_rx_gain; + + if (use_trsw_rx) { + trsw_rx_gain = phy->trsw_rx_gain / 2; + if (max_rx_gain >= trsw_rx_gain) { + trsw_rx_gain = max_rx_gain - trsw_rx_gain; + trsw_rx = 0x20; + } + } else + trsw_rx_gain = max_rx_gain; + if (trsw_rx_gain < 9) { + phy->lna_lod_gain = 0; + } else { + phy->lna_lod_gain = 1; + trsw_rx_gain -= 8; + } + trsw_rx_gain = limit_value(trsw_rx_gain, 0, 0x2D); + phy->pga_gain = trsw_rx_gain / 3; + if (phy->pga_gain >= 5) { + phy->pga_gain -= 5; + phy->lna_gain = 2; + } else + phy->lna_gain = 0; + } else { + phy->lna_gain = 0; + phy->trsw_rx_gain = 0x20; + if (max_rx_gain >= 0x14) { + phy->lna_lod_gain = 1; + phy->pga_gain = 2; + } else if (max_rx_gain >= 0x12) { + phy->lna_lod_gain = 1; + phy->pga_gain = 1; + } else if (max_rx_gain >= 0xF) { + phy->lna_lod_gain = 1; + phy->pga_gain = 0; + } else { + phy->lna_lod_gain = 0; + phy->pga_gain = 0; + } + } + + tmp = bcm43xx_radio_read16(dev, 0x7A); + if (phy->lna_lod_gain == 0) + tmp &= ~0x0008; + else + tmp |= 0x0008; + bcm43xx_radio_write16(dev, 0x7A, tmp); +} + +struct lo_g_saved_values { + u8 old_channel; + + /* Core registers */ + u16 reg_3F4; + u16 reg_3E2; + + /* PHY registers */ + u16 phy_lo_mask; + u16 phy_extg_01; + u16 phy_dacctl_hwpctl; + u16 phy_dacctl; + u16 phy_base_14; + u16 phy_hpwr_tssictl; + u16 phy_analogover; + u16 phy_analogoverval; + u16 phy_rfover; + u16 phy_rfoverval; + u16 phy_classctl; + u16 phy_base_3E; + u16 phy_crs0; + u16 phy_pgactl; + u16 phy_base_2A; + u16 phy_syncctl; + u16 phy_base_30; + u16 phy_base_06; + + /* Radio registers */ + u16 radio_43; + u16 radio_7A; + u16 radio_52; +}; + +static void lo_measure_setup(struct bcm43xx_wldev *dev, + struct lo_g_saved_values *sav) +{ + struct ssb_sprom *sprom = &dev->dev->bus->sprom; + struct bcm43xx_phy *phy = &dev->phy; + struct bcm43xx_txpower_lo_control *lo = phy->lo_control; + u16 tmp; + + if (has_hardware_pctl(phy)) { + sav->phy_lo_mask = bcm43xx_phy_read(dev, BCM43xx_PHY_LO_MASK); + sav->phy_extg_01 = bcm43xx_phy_read(dev, BCM43xx_PHY_EXTG(0x01)); + sav->phy_dacctl_hwpctl = bcm43xx_phy_read(dev, BCM43xx_PHY_DACCTL); + sav->phy_base_14 = bcm43xx_phy_read(dev, BCM43xx_PHY_BASE(0x14)); + sav->phy_hpwr_tssictl = bcm43xx_phy_read(dev, BCM43xx_PHY_HPWR_TSSICTL); + + bcm43xx_phy_write(dev, BCM43xx_PHY_HPWR_TSSICTL, + bcm43xx_phy_read(dev, BCM43xx_PHY_HPWR_TSSICTL) + | 0x100); + bcm43xx_phy_write(dev, BCM43xx_PHY_EXTG(0x01), + bcm43xx_phy_read(dev, BCM43xx_PHY_EXTG(0x01)) + | 0x40); + bcm43xx_phy_write(dev, BCM43xx_PHY_DACCTL, + bcm43xx_phy_read(dev, BCM43xx_PHY_DACCTL) + | 0x40); + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x14), + bcm43xx_phy_read(dev, BCM43xx_PHY_BASE(0x14)) + | 0x200); + } + if (phy->type == BCM43xx_PHYTYPE_B && + phy->radio_ver == 0x2050 && + phy->radio_rev < 6) { + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x16), 0x410); + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x17), 0x820); + } + if (!lo->rebuild && has_hardware_pctl(phy)) + lo_read_power_vector(dev); + if (phy->rev >= 2) { + sav->phy_analogover = bcm43xx_phy_read(dev, BCM43xx_PHY_ANALOGOVER); + sav->phy_analogoverval = bcm43xx_phy_read(dev, BCM43xx_PHY_ANALOGOVERVAL); + sav->phy_rfover = bcm43xx_phy_read(dev, BCM43xx_PHY_RFOVER); + sav->phy_rfoverval = bcm43xx_phy_read(dev, BCM43xx_PHY_RFOVERVAL); + sav->phy_classctl = bcm43xx_phy_read(dev, BCM43xx_PHY_CLASSCTL); + sav->phy_base_3E = bcm43xx_phy_read(dev, BCM43xx_PHY_BASE(0x3E)); + sav->phy_crs0 = bcm43xx_phy_read(dev, BCM43xx_PHY_CRS0); + + bcm43xx_phy_write(dev, BCM43xx_PHY_CLASSCTL, + bcm43xx_phy_read(dev, BCM43xx_PHY_CLASSCTL) + & 0xFFFC); + bcm43xx_phy_write(dev, BCM43xx_PHY_CRS0, + bcm43xx_phy_read(dev, BCM43xx_PHY_CRS0) + & 0x7FFF); + bcm43xx_phy_write(dev, BCM43xx_PHY_ANALOGOVER, + bcm43xx_phy_read(dev, BCM43xx_PHY_ANALOGOVER) + | 0x0003); + bcm43xx_phy_write(dev, BCM43xx_PHY_ANALOGOVERVAL, + bcm43xx_phy_read(dev, BCM43xx_PHY_ANALOGOVERVAL) + & 0xFFFC); + if (phy->type == BCM43xx_PHYTYPE_G) { + if ((phy->rev >= 7) && + (sprom->r1.boardflags_lo & BCM43xx_BFL_EXTLNA)) { + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVER, 0x933); + } else { + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVER, 0x133); + } + } else { + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVER, 0); + } + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x3E), 0); + } + sav->reg_3F4 = bcm43xx_read16(dev, 0x3F4); + sav->reg_3E2 = bcm43xx_read16(dev, 0x3E2); + sav->radio_43 = bcm43xx_radio_read16(dev, 0x43); + sav->radio_7A = bcm43xx_radio_read16(dev, 0x7A); + sav->phy_pgactl = bcm43xx_phy_read(dev, BCM43xx_PHY_PGACTL); + sav->phy_base_2A = bcm43xx_phy_read(dev, BCM43xx_PHY_BASE(0x2A)); + sav->phy_syncctl = bcm43xx_phy_read(dev, BCM43xx_PHY_SYNCCTL); + sav->phy_dacctl = bcm43xx_phy_read(dev, BCM43xx_PHY_DACCTL); + + if (!has_tx_magnification(phy)) { + sav->radio_52 = bcm43xx_radio_read16(dev, 0x52); + sav->radio_52 &= 0x00F0; + } + if (phy->type == BCM43xx_PHYTYPE_B) { + sav->phy_base_30 = bcm43xx_phy_read(dev, BCM43xx_PHY_BASE(0x30)); + sav->phy_base_06 = bcm43xx_phy_read(dev, BCM43xx_PHY_BASE(0x06)); + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x30), 0x00FF); + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x06), 0x3F3F); + } else { + bcm43xx_write16(dev, 0x3E2, + bcm43xx_read16(dev, 0x3E2) + | 0x8000); + } + bcm43xx_write16(dev, 0x3F4, + bcm43xx_read16(dev, 0x3F4) + & 0xF000); + + tmp = (phy->type == BCM43xx_PHYTYPE_G) ? BCM43xx_PHY_LO_MASK : BCM43xx_PHY_BASE(0x2E); + bcm43xx_phy_write(dev, tmp, 0x007F); + + tmp = sav->phy_syncctl; + bcm43xx_phy_write(dev, BCM43xx_PHY_SYNCCTL, tmp & 0xFF7F); + tmp = sav->radio_7A; + bcm43xx_radio_write16(dev, 0x007A, tmp & 0xFFF0); + + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x2A), 0x8A3); + if (phy->type == BCM43xx_PHYTYPE_G || + (phy->type == BCM43xx_PHYTYPE_B && + phy->radio_ver == 0x2050 && + phy->radio_rev >= 6)) { + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x2B), 0x1003); + } else + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x2B), 0x0802); + if (phy->rev >= 2) + bcm43xx_dummy_transmission(dev); + bcm43xx_radio_selectchannel(dev, 6, 0); + bcm43xx_radio_read16(dev, 0x51); /* dummy read */ + if (phy->type == BCM43xx_PHYTYPE_G) + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x2F), 0); + if (lo->rebuild) + lo_measure_txctl_values(dev); + if (phy->type == BCM43xx_PHYTYPE_G && phy->rev >= 3) { + bcm43xx_phy_write(dev, BCM43xx_PHY_LO_MASK, 0xC078); + } else { + if (phy->type == BCM43xx_PHYTYPE_B) + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x2E), 0x8078); + else + bcm43xx_phy_write(dev, BCM43xx_PHY_LO_MASK, 0x8078); + } +} + +static void lo_measure_restore(struct bcm43xx_wldev *dev, + struct lo_g_saved_values *sav) +{ + struct bcm43xx_phy *phy = &dev->phy; + struct bcm43xx_txpower_lo_control *lo = phy->lo_control; + u16 tmp; + + if (phy->rev >= 2) { + bcm43xx_phy_write(dev, BCM43xx_PHY_PGACTL, 0xE300); + tmp = (phy->pga_gain << 8); + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVERVAL, tmp | 0xA0); + udelay(5); + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVERVAL, tmp | 0xA2); + udelay(2); + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVERVAL, tmp | 0xA3); + } else { + tmp = (phy->pga_gain | 0xEFA0); + bcm43xx_phy_write(dev, BCM43xx_PHY_PGACTL, tmp); + } + if (has_hardware_pctl(phy)) { + bcm43xx_gphy_dc_lt_init(dev); + } else { + if (lo->rebuild) + bcm43xx_lo_adjust_to(dev, 3, 2, 0); + else + bcm43xx_lo_adjust(dev); + } + if (phy->type == BCM43xx_PHYTYPE_G) { + if (phy->rev >= 3) + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x2E), 0xC078); + else + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x2E), 0x8078); + if (phy->rev >= 2) + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x2F), 0x0202); + else + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x2F), 0x0101); + } + bcm43xx_write16(dev, 0x3F4, sav->reg_3F4); + bcm43xx_phy_write(dev, BCM43xx_PHY_PGACTL, sav->phy_pgactl); + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x2A), sav->phy_base_2A); + bcm43xx_phy_write(dev, BCM43xx_PHY_SYNCCTL, sav->phy_syncctl); + bcm43xx_phy_write(dev, BCM43xx_PHY_DACCTL, sav->phy_dacctl); + bcm43xx_radio_write16(dev, 0x43, sav->radio_43); + bcm43xx_radio_write16(dev, 0x7A, sav->radio_7A); + if (!has_tx_magnification(phy)) { + tmp = sav->radio_52; + bcm43xx_radio_write16(dev, 0x52, + (bcm43xx_radio_read16(dev, 0x52) + & 0xFF0F) | tmp); + } + bcm43xx_write16(dev, 0x3E2, sav->reg_3E2); + if (phy->type == BCM43xx_PHYTYPE_B && + phy->radio_ver == 0x2050 && + phy->radio_rev <= 5) { + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x30), sav->phy_base_30); + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x06), sav->phy_base_06); + } + if (phy->rev >= 2) { + bcm43xx_phy_write(dev, BCM43xx_PHY_ANALOGOVER, sav->phy_analogover); + bcm43xx_phy_write(dev, BCM43xx_PHY_ANALOGOVERVAL, sav->phy_analogoverval); + bcm43xx_phy_write(dev, BCM43xx_PHY_CLASSCTL, sav->phy_classctl); + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVER, sav->phy_rfover); + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVERVAL, sav->phy_rfoverval); + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x3E), sav->phy_base_3E); + bcm43xx_phy_write(dev, BCM43xx_PHY_CRS0, sav->phy_crs0); + } + if (has_hardware_pctl(phy)) { + tmp = (sav->phy_lo_mask & 0xBFFF); + bcm43xx_phy_write(dev, BCM43xx_PHY_LO_MASK, tmp); + bcm43xx_phy_write(dev, BCM43xx_PHY_EXTG(0x01), sav->phy_extg_01); + bcm43xx_phy_write(dev, BCM43xx_PHY_DACCTL, sav->phy_dacctl_hwpctl); + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x14), sav->phy_base_14); + bcm43xx_phy_write(dev, BCM43xx_PHY_HPWR_TSSICTL, sav->phy_hpwr_tssictl); + } + bcm43xx_radio_selectchannel(dev, sav->old_channel, 1); +} + +struct bcm43xx_lo_statemachine { + int current_state; + int nr_measured; + int state_val_multiplier; + u16 lowest_feedth; + struct bcm43xx_loctl min_loctl; +}; + +/* Loop over each possible value in this state. */ +static int lo_probe_possible_loctls(struct bcm43xx_wldev *dev, + struct bcm43xx_loctl *probe_loctl, + struct bcm43xx_lo_statemachine *d) +{ + struct bcm43xx_phy *phy = &dev->phy; + struct bcm43xx_txpower_lo_control *lo = phy->lo_control; + struct bcm43xx_loctl test_loctl; + struct bcm43xx_loctl orig_loctl; + struct bcm43xx_loctl prev_loctl = { + .i = -100, + .q = -100, + }; + int i; + int begin, end; + int found_lower = 0; + u16 feedth; + + static const struct bcm43xx_loctl modifiers[] = { + { .i = 1, .q = 1, }, + { .i = 1, .q = 0, }, + { .i = 1, .q = -1, }, + { .i = 0, .q = -1, }, + { .i = -1, .q = -1, }, + { .i = -1, .q = 0, }, + { .i = -1, .q = 1, }, + { .i = 0, .q = 1, }, + }; + + if (d->current_state == 0) { + begin = 1; + end = 8; + } else if (d->current_state % 2 == 0) { + begin = d->current_state - 1; + end = d->current_state + 1; + } else { + begin = d->current_state - 2; + end = d->current_state + 2; + } + if (begin < 1) + begin += 8; + if (end > 8) + end -= 8; + + memcpy(&orig_loctl, probe_loctl, sizeof(struct bcm43xx_loctl)); + i = begin; + d->current_state = i; + while (1) { + assert(i >= 1 && i <= 8); + memcpy(&test_loctl, &orig_loctl, sizeof(struct bcm43xx_loctl)); + test_loctl.i += modifiers[i - 1].i * d->state_val_multiplier; + test_loctl.q += modifiers[i - 1].q * d->state_val_multiplier; + if ((test_loctl.i != prev_loctl.i || + test_loctl.q != prev_loctl.q) && + (abs(test_loctl.i) <= 16 && + abs(test_loctl.q) <= 16)) { + bcm43xx_lo_write(dev, &test_loctl); + feedth = lo_measure_feedthrough(dev, phy->lna_gain, + phy->pga_gain, + phy->trsw_rx_gain); + if (feedth < d->lowest_feedth) { + memcpy(probe_loctl, &test_loctl, sizeof(struct bcm43xx_loctl)); + found_lower = 1; + d->lowest_feedth = feedth; + if ((d->nr_measured < 2) && + (!has_loopback_gain(phy) || lo->rebuild)) + break; + } + } + memcpy(&prev_loctl, &test_loctl, sizeof(prev_loctl)); + if (i == end) + break; + if (i == 8) + i = 1; + else + i++; + d->current_state = i; + } + + return found_lower; +} + +static void lo_probe_loctls_statemachine(struct bcm43xx_wldev *dev, + struct bcm43xx_loctl *loctl, + int *max_rx_gain) +{ + struct bcm43xx_phy *phy = &dev->phy; + struct bcm43xx_txpower_lo_control *lo = phy->lo_control; + struct bcm43xx_lo_statemachine d; + u16 feedth; + int found_lower; + struct bcm43xx_loctl probe_loctl; + int max_repeat = 1, repeat_cnt = 0; + + d.nr_measured = 0; + d.state_val_multiplier = 1; + if (has_loopback_gain(phy) && !lo->rebuild) + d.state_val_multiplier = 3; + + memcpy(&d.min_loctl, loctl, sizeof(struct bcm43xx_loctl)); + if (has_loopback_gain(phy) && lo->rebuild) + max_repeat = 4; + do { + bcm43xx_lo_write(dev, &d.min_loctl); + feedth = lo_measure_feedthrough(dev, phy->lna_gain, + phy->pga_gain, + phy->trsw_rx_gain); + if (!lo->rebuild && feedth < 0x258) { + if (feedth >= 0x12C) + *max_rx_gain += 6; + else + *max_rx_gain += 3; + feedth = lo_measure_feedthrough(dev, phy->lna_gain, + phy->pga_gain, + phy->trsw_rx_gain); + } + d.lowest_feedth = feedth; + + d.current_state = 0; + do { + assert(d.current_state >= 0 && d.current_state <= 8); + memcpy(&probe_loctl, &d.min_loctl, sizeof(struct bcm43xx_loctl)); + found_lower = lo_probe_possible_loctls(dev, &probe_loctl, &d); + if (!found_lower) + break; + if ((probe_loctl.i == d.min_loctl.i) && + (probe_loctl.q == d.min_loctl.q)) + break; + memcpy(&d.min_loctl, &probe_loctl, sizeof(struct bcm43xx_loctl)); + d.nr_measured++; + } while (d.nr_measured < 24); + memcpy(loctl, &d.min_loctl, sizeof(struct bcm43xx_loctl)); + + if (has_loopback_gain(phy)) { + if (d.lowest_feedth > 0x1194) + *max_rx_gain -= 6; + else if (d.lowest_feedth < 0x5DC) + *max_rx_gain += 3; + if (repeat_cnt == 0) { + if (d.lowest_feedth <= 0x5DC) { + d.state_val_multiplier = 1; + repeat_cnt++; + } else + d.state_val_multiplier = 2; + } else if (repeat_cnt == 2) + d.state_val_multiplier = 1; + } + lo_measure_gain_values(dev, *max_rx_gain, has_loopback_gain(phy)); + } while (++repeat_cnt < max_repeat); +} + +static void lo_measure(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + struct bcm43xx_txpower_lo_control *lo = phy->lo_control; + struct bcm43xx_loctl loctl = { + .i = 0, + .q = 0, + }; + struct bcm43xx_loctl *ploctl; + int max_rx_gain; + int rfidx, bbidx; + + /* Values from the "TXCTL Register and Value Table" */ + u16 txctl_reg; + u16 txctl_value; + u16 pad_mix_gain; + + txctl_reg = lo_txctl_register_table(dev, &txctl_value, &pad_mix_gain); + + for (rfidx = 0; rfidx < lo->rfatt_list.len; rfidx++) { + + bcm43xx_radio_write16(dev, 0x43, + (bcm43xx_radio_read16(dev, 0x43) + & 0xFFF0) | lo->rfatt_list.list[rfidx].att); + bcm43xx_radio_write16(dev, txctl_reg, + (bcm43xx_radio_read16(dev, txctl_reg) + & ~txctl_value) + | (lo->rfatt_list.list[rfidx].with_padmix ? txctl_value : 0)); + + for (bbidx = 0; bbidx < lo->bbatt_list.len; bbidx++) { + if (lo->rebuild) { + ploctl = bcm43xx_get_loctl_nopadmix(dev, + &lo->rfatt_list.list[rfidx], + &lo->bbatt_list.list[bbidx]); + } else { + ploctl = bcm43xx_get_loctl(dev, &lo->rfatt_list.list[rfidx], + &lo->bbatt_list.list[bbidx]); + if (!ploctl->used) + continue; + } + memcpy(&loctl, ploctl, sizeof(loctl)); + + max_rx_gain = lo->rfatt_list.list[rfidx].att * 2; + max_rx_gain += lo->bbatt_list.list[bbidx].att / 2; + if (lo->rfatt_list.list[rfidx].with_padmix) + max_rx_gain -= pad_mix_gain; + if (has_loopback_gain(phy)) + max_rx_gain += phy->max_lb_gain; + lo_measure_gain_values(dev, max_rx_gain, has_loopback_gain(phy)); + + bcm43xx_phy_set_baseband_attenuation(dev, lo->bbatt_list.list[bbidx].att); + lo_probe_loctls_statemachine(dev, &loctl, &max_rx_gain); + if (phy->type == BCM43xx_PHYTYPE_B) { + loctl.i++; + loctl.q++; + } + memcpy(ploctl, &loctl, sizeof(loctl)); + } + } +} + +#if BCM43xx_DEBUG +static int do_validate_loctl(struct bcm43xx_wldev *dev, + struct bcm43xx_loctl *control) +{ + const int is_initializing = (bcm43xx_status(dev) == BCM43xx_STAT_INITIALIZING); + + if (unlikely(abs(control->i) > 16 || + abs(control->q) > 16 || + (is_initializing && control->used))) { + printk(KERN_ERR PFX "ERROR: LO control pair validation failed " + "(first: %d, second: %d, used %u)\n", + control->i, control->q, control->used); + } + return 0; +} +static void validate_all_loctls(struct bcm43xx_wldev *dev) +{ + bcm43xx_call_for_each_loctl(dev, do_validate_loctl); +} +#else /* BCM43xx_DEBUG */ +static inline void validate_all_loctls(struct bcm43xx_wldev *dev) { } +#endif /* BCM43xx_DEBUG */ + +void bcm43xx_lo_g_measure(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + struct lo_g_saved_values sav; + + assert(phy->type == BCM43xx_PHYTYPE_B || + phy->type == BCM43xx_PHYTYPE_G); + + sav.old_channel = phy->channel; + lo_measure_setup(dev, &sav); + lo_measure(dev); + lo_measure_restore(dev, &sav); + + validate_all_loctls(dev); + + phy->lo_control->lo_measured = 1; + phy->lo_control->rebuild = 0; +} + +void bcm43xx_lo_adjust(struct bcm43xx_wldev *dev) +{ + bcm43xx_lo_write(dev, bcm43xx_loctl_current(dev)); +} + +static inline void fixup_rfatt_for_txctl1(struct bcm43xx_rfatt *rf, + u16 txctl1) +{ + if ((rf->att < 5) && (txctl1 & 0x0001)) + rf->att = 4; +} + +void bcm43xx_lo_adjust_to(struct bcm43xx_wldev *dev, + u16 rfatt, u16 bbatt, u16 txctl1) +{ + struct bcm43xx_rfatt rf; + struct bcm43xx_bbatt bb; + struct bcm43xx_loctl *loctl; + + memset(&rf, 0, sizeof(rf)); + memset(&bb, 0, sizeof(bb)); + rf.att = rfatt; + bb.att = bbatt; + fixup_rfatt_for_txctl1(&rf, txctl1); + loctl = bcm43xx_get_loctl(dev, &rf, &bb); + bcm43xx_lo_write(dev, loctl); +} + +struct bcm43xx_loctl * bcm43xx_loctl_current(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + struct bcm43xx_txpower_lo_control *lo = phy->lo_control; + struct bcm43xx_rfatt rf; + + memcpy(&rf, &lo->rfatt, sizeof(rf)); + fixup_rfatt_for_txctl1(&rf, phy->txctl1); + + return bcm43xx_get_loctl(dev, &rf, &lo->bbatt); +} + +static int do_mark_unused(struct bcm43xx_wldev *dev, + struct bcm43xx_loctl *control) +{ + control->used = 0; + return 0; +} + +void bcm43xx_loctl_mark_all_unused(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + struct bcm43xx_txpower_lo_control *lo = phy->lo_control; + + bcm43xx_call_for_each_loctl(dev, do_mark_unused); + lo->rebuild = 1; +} + +void bcm43xx_loctl_mark_cur_used(struct bcm43xx_wldev *dev) +{ + bcm43xx_loctl_current(dev)->used = 1; +} diff --git a/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_lo.h b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_lo.h new file mode 100644 index 0000000..3dd893c --- /dev/null +++ b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_lo.h @@ -0,0 +1,92 @@ +#ifndef BCM43xx_LO_H_ +#define BCM43xx_LO_H_ + +#include "bcm43xx_phy.h" + +struct bcm43xx_wldev; + +/* Local Oscillator control value-pair. */ +struct bcm43xx_loctl { + /* Control values. */ + s8 i; + s8 q; + /* "Used by hardware" flag. */ + u8 used; +}; + +/* TX Power LO Control Array. + * Value-pairs to adjust the LocalOscillator are stored + * in this structure. + * There are two different set of values. One for "Flag is Set" + * and one for "Flag is Unset". + * By "Flag" the flag in struct bcm43xx_rfatt is meant. + * The Value arrays are two-dimensional. The first index + * is the baseband attenuation and the second index + * is the radio attenuation. + * Use bcm43xx_get_loctl() to retrieve a value from the lists. + */ +struct bcm43xx_txpower_lo_control { +#define BCM43xx_NR_BB 9 +#define BCM43xx_NR_RF 16 + /* LO Control values, with PAD Mixer */ + struct bcm43xx_loctl with_padmix[ BCM43xx_NR_BB ][ BCM43xx_NR_RF ]; + /* LO Control values, without PAD Mixer */ + struct bcm43xx_loctl no_padmix[ BCM43xx_NR_BB ][ BCM43xx_NR_RF ]; + + /* Flag to indicate a complete rebuild of the two tables above + * to the LO measuring code. */ + u8 rebuild; + + /* Lists of valid RF and BB attenuation values for this device. */ + struct bcm43xx_rfatt_list rfatt_list; + struct bcm43xx_bbatt_list bbatt_list; + + /* Current RF and BB attenuation and LO control values. */ + struct bcm43xx_rfatt rfatt; + struct bcm43xx_bbatt bbatt; + + /* Current TX Bias value */ + u8 tx_bias; + /* Current TX Magnification Value (if used by the device) */ + u8 tx_magn; + + /* GPHY LO is measured. */ + u8 lo_measured; + + /* Saved device PowerVector */ + u64 power_vector; +}; + + +/* Measure the BPHY Local Oscillator. */ +void bcm43xx_lo_b_measure(struct bcm43xx_wldev *dev); +/* Measure the BPHY/GPHY Local Oscillator. */ +void bcm43xx_lo_g_measure(struct bcm43xx_wldev *dev); + +/* Adjust the Local Oscillator to the saved attenuation + * and txctl values. + */ +void bcm43xx_lo_adjust(struct bcm43xx_wldev *dev); +/* Adjust to specific values. */ +void bcm43xx_lo_adjust_to(struct bcm43xx_wldev *dev, + u16 rfatt, u16 bbatt, u16 txctl1); + +/* Returns the bcm43xx_loctl corresponding to the current + * attenuation values. + */ +struct bcm43xx_loctl * bcm43xx_loctl_current(struct bcm43xx_wldev *dev); +/* Mark all possible bcm43xx_loctl as "unused" */ +void bcm43xx_loctl_mark_all_unused(struct bcm43xx_wldev *dev); +/* Mark the bcm43xx_loctl corresponding to the current + * attenuation values as used. + */ +void bcm43xx_loctl_mark_cur_used(struct bcm43xx_wldev *dev); + +/* Get a reference to a LO Control value pair in the + * TX Power LO Control Array. + */ +struct bcm43xx_loctl * bcm43xx_get_loctl(struct bcm43xx_wldev *dev, + const struct bcm43xx_rfatt *rfatt, + const struct bcm43xx_bbatt *bbatt); + +#endif /* BCM43xx_LO_H_ */ diff --git a/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_main.c b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_main.c new file mode 100644 index 0000000..128294e --- /dev/null +++ b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_main.c @@ -0,0 +1,4029 @@ +/* + + Broadcom BCM43xx wireless driver + + Copyright (c) 2005 Martin Langer + Copyright (c) 2005 Stefano Brivio + Copyright (c) 2005, 2006 Michael Buesch + Copyright (c) 2005 Danny van Dyk + Copyright (c) 2005 Andreas Jaggi + + Some parts of the code in this file are derived from the ipw2200 + driver Copyright(c) 2003 - 2004 Intel Corporation. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bcm43xx.h" +#include "bcm43xx_main.h" +#include "bcm43xx_debugfs.h" +#include "bcm43xx_phy.h" +#include "bcm43xx_dma.h" +#include "bcm43xx_pio.h" +#include "bcm43xx_power.h" +#include "bcm43xx_sysfs.h" +#include "bcm43xx_xmit.h" +#include "bcm43xx_sysfs.h" +#include "bcm43xx_lo.h" +#include "bcm43xx_pcmcia.h" + + +MODULE_DESCRIPTION("Broadcom BCM43xx wireless driver"); +MODULE_AUTHOR("Martin Langer"); +MODULE_AUTHOR("Stefano Brivio"); +MODULE_AUTHOR("Michael Buesch"); +MODULE_LICENSE("GPL"); + + +extern char *nvram_get(char *name); + + +#if defined(CONFIG_BCM43XX_MAC80211_DMA) && defined(CONFIG_BCM43XX_MAC80211_PIO) +static int modparam_pio; +module_param_named(pio, modparam_pio, int, 0444); +MODULE_PARM_DESC(pio, "enable(1) / disable(0) PIO mode"); +#elif defined(CONFIG_BCM43XX_MAC80211_DMA) +# define modparam_pio 0 +#elif defined(CONFIG_BCM43XX_MAC80211_PIO) +# define modparam_pio 1 +#endif + +static int modparam_bad_frames_preempt; +module_param_named(bad_frames_preempt, modparam_bad_frames_preempt, int, 0444); +MODULE_PARM_DESC(bad_frames_preempt, "enable(1) / disable(0) Bad Frames Preemption"); + +static int modparam_short_retry = BCM43xx_DEFAULT_SHORT_RETRY_LIMIT; +module_param_named(short_retry, modparam_short_retry, int, 0444); +MODULE_PARM_DESC(short_retry, "Short-Retry-Limit (0 - 15)"); + +static int modparam_long_retry = BCM43xx_DEFAULT_LONG_RETRY_LIMIT; +module_param_named(long_retry, modparam_long_retry, int, 0444); +MODULE_PARM_DESC(long_retry, "Long-Retry-Limit (0 - 15)"); + +static int modparam_noleds; +module_param_named(noleds, modparam_noleds, int, 0444); +MODULE_PARM_DESC(noleds, "Turn off all LED activity"); + +static char modparam_fwpostfix[16]; +module_param_string(fwpostfix, modparam_fwpostfix, 16, 0444); +MODULE_PARM_DESC(fwpostfix, "Postfix for the .fw files to load."); + +static int modparam_mon_keep_bad; +module_param_named(mon_keep_bad, modparam_mon_keep_bad, int, 0444); +MODULE_PARM_DESC(mon_keep_bad, "Keep bad frames in monitor mode"); + +static int modparam_mon_keep_badplcp; +module_param_named(mon_keep_badplcp, modparam_mon_keep_bad, int, 0444); +MODULE_PARM_DESC(mon_keep_badplcp, "Keep frames with bad PLCP in monitor mode"); + + +static const struct ssb_device_id bcm43xx_ssb_tbl[] = { + SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, SSB_ANY_REV), + SSB_DEVTABLE_END +}; +MODULE_DEVICE_TABLE(ssb, bcm43xx_ssb_tbl); + + +/* Channel and ratetables are shared for all devices. + * They can't be const, because ieee80211 puts some precalculated + * data in there. This data is the same for all devices, so we don't + * get concurrency issues */ +#define RATETAB_ENT(_rateid, _flags) \ + { \ + .rate = BCM43xx_RATE_TO_BASE100KBPS(_rateid), \ + .val = (_rateid), \ + .val2 = (_rateid), \ + .flags = (_flags), \ + } +static struct ieee80211_rate __bcm43xx_ratetable[] = { + RATETAB_ENT(BCM43xx_CCK_RATE_1MB, IEEE80211_RATE_CCK), + RATETAB_ENT(BCM43xx_CCK_RATE_2MB, IEEE80211_RATE_CCK_2), + RATETAB_ENT(BCM43xx_CCK_RATE_5MB, IEEE80211_RATE_CCK_2), + RATETAB_ENT(BCM43xx_CCK_RATE_11MB, IEEE80211_RATE_CCK_2), + RATETAB_ENT(BCM43xx_OFDM_RATE_6MB, IEEE80211_RATE_OFDM), + RATETAB_ENT(BCM43xx_OFDM_RATE_9MB, IEEE80211_RATE_OFDM), + RATETAB_ENT(BCM43xx_OFDM_RATE_12MB, IEEE80211_RATE_OFDM), + RATETAB_ENT(BCM43xx_OFDM_RATE_18MB, IEEE80211_RATE_OFDM), + RATETAB_ENT(BCM43xx_OFDM_RATE_24MB, IEEE80211_RATE_OFDM), + RATETAB_ENT(BCM43xx_OFDM_RATE_36MB, IEEE80211_RATE_OFDM), + RATETAB_ENT(BCM43xx_OFDM_RATE_48MB, IEEE80211_RATE_OFDM), + RATETAB_ENT(BCM43xx_OFDM_RATE_54MB, IEEE80211_RATE_OFDM), +}; +#define bcm43xx_a_ratetable (__bcm43xx_ratetable + 4) +#define bcm43xx_a_ratetable_size 8 +#define bcm43xx_b_ratetable (__bcm43xx_ratetable + 0) +#define bcm43xx_b_ratetable_size 4 +#define bcm43xx_g_ratetable (__bcm43xx_ratetable + 0) +#define bcm43xx_g_ratetable_size 12 + +#define CHANTAB_ENT(_chanid, _freq) \ + { \ + .chan = (_chanid), \ + .freq = (_freq), \ + .val = (_chanid), \ + .flag = IEEE80211_CHAN_W_SCAN | \ + IEEE80211_CHAN_W_ACTIVE_SCAN | \ + IEEE80211_CHAN_W_IBSS, \ + .power_level = 0xFF, \ + .antenna_max = 0xFF, \ + } +static struct ieee80211_channel bcm43xx_bg_chantable[] = { + CHANTAB_ENT(1, 2412), + CHANTAB_ENT(2, 2417), + CHANTAB_ENT(3, 2422), + CHANTAB_ENT(4, 2427), + CHANTAB_ENT(5, 2432), + CHANTAB_ENT(6, 2437), + CHANTAB_ENT(7, 2442), + CHANTAB_ENT(8, 2447), + CHANTAB_ENT(9, 2452), + CHANTAB_ENT(10, 2457), + CHANTAB_ENT(11, 2462), + CHANTAB_ENT(12, 2467), + CHANTAB_ENT(13, 2472), + CHANTAB_ENT(14, 2484), +}; +#define bcm43xx_bg_chantable_size ARRAY_SIZE(bcm43xx_bg_chantable) +static struct ieee80211_channel bcm43xx_a_chantable[] = { + CHANTAB_ENT(36, 5180), + CHANTAB_ENT(40, 5200), + CHANTAB_ENT(44, 5220), + CHANTAB_ENT(48, 5240), + CHANTAB_ENT(52, 5260), + CHANTAB_ENT(56, 5280), + CHANTAB_ENT(60, 5300), + CHANTAB_ENT(64, 5320), + CHANTAB_ENT(149, 5745), + CHANTAB_ENT(153, 5765), + CHANTAB_ENT(157, 5785), + CHANTAB_ENT(161, 5805), + CHANTAB_ENT(165, 5825), +}; +#define bcm43xx_a_chantable_size ARRAY_SIZE(bcm43xx_a_chantable) + + +static void bcm43xx_wireless_core_exit(struct bcm43xx_wldev *dev); +static int bcm43xx_wireless_core_init(struct bcm43xx_wldev *dev); +static void bcm43xx_wireless_core_stop(struct bcm43xx_wldev *dev); +static int bcm43xx_wireless_core_start(struct bcm43xx_wldev *dev); + + +static void bcm43xx_ram_write(struct bcm43xx_wldev *dev, u16 offset, u32 val) +{ + u32 status; + + assert(offset % 4 == 0); + + status = bcm43xx_read32(dev, BCM43xx_MMIO_STATUS_BITFIELD); + if (status & BCM43xx_SBF_XFER_REG_BYTESWAP) + val = swab32(val); + + bcm43xx_write32(dev, BCM43xx_MMIO_RAM_CONTROL, offset); + mmiowb(); + bcm43xx_write32(dev, BCM43xx_MMIO_RAM_DATA, val); +} + +static inline +void bcm43xx_shm_control_word(struct bcm43xx_wldev *dev, + u16 routing, u16 offset) +{ + u32 control; + + /* "offset" is the WORD offset. */ + + control = routing; + control <<= 16; + control |= offset; + bcm43xx_write32(dev, BCM43xx_MMIO_SHM_CONTROL, control); +} + +u32 bcm43xx_shm_read32(struct bcm43xx_wldev *dev, + u16 routing, u16 offset) +{ + u32 ret; + + if (routing == BCM43xx_SHM_SHARED) { + assert((offset & 0x0001) == 0); + if (offset & 0x0003) { + /* Unaligned access */ + bcm43xx_shm_control_word(dev, routing, offset >> 2); + ret = bcm43xx_read16(dev, + BCM43xx_MMIO_SHM_DATA_UNALIGNED); + ret <<= 16; + bcm43xx_shm_control_word(dev, routing, (offset >> 2) + 1); + ret |= bcm43xx_read16(dev, + BCM43xx_MMIO_SHM_DATA); + + return ret; + } + offset >>= 2; + } + bcm43xx_shm_control_word(dev, routing, offset); + ret = bcm43xx_read32(dev, BCM43xx_MMIO_SHM_DATA); + + return ret; +} + +u16 bcm43xx_shm_read16(struct bcm43xx_wldev *dev, + u16 routing, u16 offset) +{ + u16 ret; + + if (routing == BCM43xx_SHM_SHARED) { + assert((offset & 0x0001) == 0); + if (offset & 0x0003) { + /* Unaligned access */ + bcm43xx_shm_control_word(dev, routing, offset >> 2); + ret = bcm43xx_read16(dev, + BCM43xx_MMIO_SHM_DATA_UNALIGNED); + + return ret; + } + offset >>= 2; + } + bcm43xx_shm_control_word(dev, routing, offset); + ret = bcm43xx_read16(dev, BCM43xx_MMIO_SHM_DATA); + + return ret; +} + +void bcm43xx_shm_write32(struct bcm43xx_wldev *dev, + u16 routing, u16 offset, + u32 value) +{ + if (routing == BCM43xx_SHM_SHARED) { + assert((offset & 0x0001) == 0); + if (offset & 0x0003) { + /* Unaligned access */ + bcm43xx_shm_control_word(dev, routing, offset >> 2); + mmiowb(); + bcm43xx_write16(dev, BCM43xx_MMIO_SHM_DATA_UNALIGNED, + (value >> 16) & 0xffff); + mmiowb(); + bcm43xx_shm_control_word(dev, routing, (offset >> 2) + 1); + mmiowb(); + bcm43xx_write16(dev, BCM43xx_MMIO_SHM_DATA, + value & 0xffff); + return; + } + offset >>= 2; + } + bcm43xx_shm_control_word(dev, routing, offset); + mmiowb(); + bcm43xx_write32(dev, BCM43xx_MMIO_SHM_DATA, value); +} + +void bcm43xx_shm_write16(struct bcm43xx_wldev *dev, + u16 routing, u16 offset, + u16 value) +{ + if (routing == BCM43xx_SHM_SHARED) { + assert((offset & 0x0001) == 0); + if (offset & 0x0003) { + /* Unaligned access */ + bcm43xx_shm_control_word(dev, routing, offset >> 2); + mmiowb(); + bcm43xx_write16(dev, BCM43xx_MMIO_SHM_DATA_UNALIGNED, + value); + return; + } + offset >>= 2; + } + bcm43xx_shm_control_word(dev, routing, offset); + mmiowb(); + bcm43xx_write16(dev, BCM43xx_MMIO_SHM_DATA, value); +} + +/* Read HostFlags */ +u32 bcm43xx_hf_read(struct bcm43xx_wldev *dev) +{ + u32 ret; + + ret = bcm43xx_shm_read16(dev, BCM43xx_SHM_SHARED, + BCM43xx_SHM_SH_HOSTFHI); + ret <<= 16; + ret |= bcm43xx_shm_read16(dev, BCM43xx_SHM_SHARED, + BCM43xx_SHM_SH_HOSTFLO); + + return ret; +} + +/* Write HostFlags */ +void bcm43xx_hf_write(struct bcm43xx_wldev *dev, u32 value) +{ + bcm43xx_shm_write16(dev, BCM43xx_SHM_SHARED, + BCM43xx_SHM_SH_HOSTFLO, + (value & 0x0000FFFF)); + bcm43xx_shm_write16(dev, BCM43xx_SHM_SHARED, + BCM43xx_SHM_SH_HOSTFHI, + ((value & 0xFFFF0000) >> 16)); +} + +void bcm43xx_tsf_read(struct bcm43xx_wldev *dev, u64 *tsf) +{ + /* We need to be careful. As we read the TSF from multiple + * registers, we should take care of register overflows. + * In theory, the whole tsf read process should be atomic. + * We try to be atomic here, by restaring the read process, + * if any of the high registers changed (overflew). + */ + if (dev->dev->id.revision >= 3) { + u32 low, high, high2; + + do { + high = bcm43xx_read32(dev, BCM43xx_MMIO_REV3PLUS_TSF_HIGH); + low = bcm43xx_read32(dev, BCM43xx_MMIO_REV3PLUS_TSF_LOW); + high2 = bcm43xx_read32(dev, BCM43xx_MMIO_REV3PLUS_TSF_HIGH); + } while (unlikely(high != high2)); + + *tsf = high; + *tsf <<= 32; + *tsf |= low; + } else { + u64 tmp; + u16 v0, v1, v2, v3; + u16 test1, test2, test3; + + do { + v3 = bcm43xx_read16(dev, BCM43xx_MMIO_TSF_3); + v2 = bcm43xx_read16(dev, BCM43xx_MMIO_TSF_2); + v1 = bcm43xx_read16(dev, BCM43xx_MMIO_TSF_1); + v0 = bcm43xx_read16(dev, BCM43xx_MMIO_TSF_0); + + test3 = bcm43xx_read16(dev, BCM43xx_MMIO_TSF_3); + test2 = bcm43xx_read16(dev, BCM43xx_MMIO_TSF_2); + test1 = bcm43xx_read16(dev, BCM43xx_MMIO_TSF_1); + } while (v3 != test3 || v2 != test2 || v1 != test1); + + *tsf = v3; + *tsf <<= 48; + tmp = v2; + tmp <<= 32; + *tsf |= tmp; + tmp = v1; + tmp <<= 16; + *tsf |= tmp; + *tsf |= v0; + } +} + +static void bcm43xx_time_lock(struct bcm43xx_wldev *dev) +{ + u32 status; + + status = bcm43xx_read32(dev, BCM43xx_MMIO_STATUS_BITFIELD); + status |= BCM43xx_SBF_TIME_UPDATE; + bcm43xx_write32(dev, BCM43xx_MMIO_STATUS_BITFIELD, status); + mmiowb(); +} + +static void bcm43xx_time_unlock(struct bcm43xx_wldev *dev) +{ + u32 status; + + status = bcm43xx_read32(dev, BCM43xx_MMIO_STATUS_BITFIELD); + status &= ~BCM43xx_SBF_TIME_UPDATE; + bcm43xx_write32(dev, BCM43xx_MMIO_STATUS_BITFIELD, status); +} + +static void bcm43xx_tsf_write_locked(struct bcm43xx_wldev *dev, u64 tsf) +{ + /* Be careful with the in-progress timer. + * First zero out the low register, so we have a full + * register-overflow duration to complete the operation. + */ + if (dev->dev->id.revision >= 3) { + u32 lo = (tsf & 0x00000000FFFFFFFFULL); + u32 hi = (tsf & 0xFFFFFFFF00000000ULL) >> 32; + + bcm43xx_write32(dev, BCM43xx_MMIO_REV3PLUS_TSF_LOW, 0); + mmiowb(); + bcm43xx_write32(dev, BCM43xx_MMIO_REV3PLUS_TSF_HIGH, hi); + mmiowb(); + bcm43xx_write32(dev, BCM43xx_MMIO_REV3PLUS_TSF_LOW, lo); + } else { + u16 v0 = (tsf & 0x000000000000FFFFULL); + u16 v1 = (tsf & 0x00000000FFFF0000ULL) >> 16; + u16 v2 = (tsf & 0x0000FFFF00000000ULL) >> 32; + u16 v3 = (tsf & 0xFFFF000000000000ULL) >> 48; + + bcm43xx_write16(dev, BCM43xx_MMIO_TSF_0, 0); + mmiowb(); + bcm43xx_write16(dev, BCM43xx_MMIO_TSF_3, v3); + mmiowb(); + bcm43xx_write16(dev, BCM43xx_MMIO_TSF_2, v2); + mmiowb(); + bcm43xx_write16(dev, BCM43xx_MMIO_TSF_1, v1); + mmiowb(); + bcm43xx_write16(dev, BCM43xx_MMIO_TSF_0, v0); + } +} + +void bcm43xx_tsf_write(struct bcm43xx_wldev *dev, u64 tsf) +{ + bcm43xx_time_lock(dev); + bcm43xx_tsf_write_locked(dev, tsf); + bcm43xx_time_unlock(dev); +} + +static void bcm43xx_measure_channel_change_time(struct bcm43xx_wldev *dev) +{ + u64 start, stop; + unsigned long flags; + u8 oldchan, testchan; + + /* We (ab)use the bcm43xx TSF timer to measure the time needed + * to switch channels. This information is handed over to + * the ieee80211 subsystem. + * Time is measured in microseconds. + */ + + spin_lock_irqsave(&dev->wl->irq_lock, flags); + oldchan = dev->phy.channel; + testchan = (oldchan == 6) ? 7 : 6; + bcm43xx_tsf_read(dev, &start); + bcm43xx_radio_selectchannel(dev, testchan, 0); + bcm43xx_tsf_read(dev, &stop); + bcm43xx_radio_selectchannel(dev, oldchan, 0); + spin_unlock_irqrestore(&dev->wl->irq_lock, flags); + + assert(stop > start); + dev->wl->hw->channel_change_time = stop - start; +} + +static +void bcm43xx_macfilter_set(struct bcm43xx_wldev *dev, + u16 offset, + const u8 *mac) +{ + u16 data; + + offset |= 0x0020; + bcm43xx_write16(dev, BCM43xx_MMIO_MACFILTER_CONTROL, offset); + + data = mac[0]; + data |= mac[1] << 8; + bcm43xx_write16(dev, BCM43xx_MMIO_MACFILTER_DATA, data); + data = mac[2]; + data |= mac[3] << 8; + bcm43xx_write16(dev, BCM43xx_MMIO_MACFILTER_DATA, data); + data = mac[4]; + data |= mac[5] << 8; + bcm43xx_write16(dev, BCM43xx_MMIO_MACFILTER_DATA, data); +} + +static void bcm43xx_macfilter_clear(struct bcm43xx_wldev *dev, + u16 offset) +{ + static const u8 zero_addr[ETH_ALEN] = { 0 }; + + bcm43xx_macfilter_set(dev, offset, zero_addr); +} + +static void bcm43xx_write_mac_bssid_templates(struct bcm43xx_wldev *dev) +{ + static const u8 zero_addr[ETH_ALEN] = { 0 }; + const u8 *mac; + const u8 *bssid; + u8 mac_bssid[ETH_ALEN * 2]; + int i; + u32 tmp; + + bssid = dev->wl->bssid; + if (!bssid) + bssid = zero_addr; + mac = dev->wl->mac_addr; + if (!mac) + mac = zero_addr; + + memcpy(mac_bssid, mac, ETH_ALEN); + memcpy(mac_bssid + ETH_ALEN, bssid, ETH_ALEN); + + /* Write our MAC address and BSSID to template ram */ + for (i = 0; i < ARRAY_SIZE(mac_bssid); i += sizeof(u32)) { + tmp = (u32)(mac_bssid[i + 0]); + tmp |= (u32)(mac_bssid[i + 1]) << 8; + tmp |= (u32)(mac_bssid[i + 2]) << 16; + tmp |= (u32)(mac_bssid[i + 3]) << 24; + bcm43xx_ram_write(dev, 0x20 + i, tmp); + } +} + +static void bcm43xx_set_slot_time(struct bcm43xx_wldev *dev, u16 slot_time) +{ + /* slot_time is in usec. */ + if (dev->phy.type != BCM43xx_PHYTYPE_G) + return; + bcm43xx_write16(dev, 0x684, 510 + slot_time); + bcm43xx_shm_write16(dev, BCM43xx_SHM_SHARED, 0x0010, slot_time); +} + +static void bcm43xx_short_slot_timing_enable(struct bcm43xx_wldev *dev) +{ + bcm43xx_set_slot_time(dev, 9); + dev->short_slot = 1; +} + +static void bcm43xx_short_slot_timing_disable(struct bcm43xx_wldev *dev) +{ + bcm43xx_set_slot_time(dev, 20); + dev->short_slot = 0; +} + +/* Enable a Generic IRQ. "mask" is the mask of which IRQs to enable. + * Returns the _previously_ enabled IRQ mask. + */ +static inline u32 bcm43xx_interrupt_enable(struct bcm43xx_wldev *dev, u32 mask) +{ + u32 old_mask; + + old_mask = bcm43xx_read32(dev, BCM43xx_MMIO_GEN_IRQ_MASK); + bcm43xx_write32(dev, BCM43xx_MMIO_GEN_IRQ_MASK, old_mask | mask); + + return old_mask; +} + +/* Disable a Generic IRQ. "mask" is the mask of which IRQs to disable. + * Returns the _previously_ enabled IRQ mask. + */ +static inline u32 bcm43xx_interrupt_disable(struct bcm43xx_wldev *dev, u32 mask) +{ + u32 old_mask; + + old_mask = bcm43xx_read32(dev, BCM43xx_MMIO_GEN_IRQ_MASK); + bcm43xx_write32(dev, BCM43xx_MMIO_GEN_IRQ_MASK, old_mask & ~mask); + + return old_mask; +} + +/* Synchronize IRQ top- and bottom-half. + * IRQs must be masked before calling this. + * This must not be called with the irq_lock held. + */ +static void bcm43xx_synchronize_irq(struct bcm43xx_wldev *dev) +{ + synchronize_irq(dev->dev->irq); + tasklet_kill(&dev->isr_tasklet); +} + +/* DummyTransmission function, as documented on + * http://bcm-specs.sipsolutions.net/DummyTransmission + */ +void bcm43xx_dummy_transmission(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + unsigned int i, max_loop; + u16 value; + u32 buffer[5] = { + 0x00000000, + 0x00D40000, + 0x00000000, + 0x01000000, + 0x00000000, + }; + + switch (phy->type) { + case BCM43xx_PHYTYPE_A: + max_loop = 0x1E; + buffer[0] = 0x000201CC; + break; + case BCM43xx_PHYTYPE_B: + case BCM43xx_PHYTYPE_G: + max_loop = 0xFA; + buffer[0] = 0x000B846E; + break; + default: + assert(0); + return; + } + + for (i = 0; i < 5; i++) + bcm43xx_ram_write(dev, i * 4, buffer[i]); + + bcm43xx_read32(dev, BCM43xx_MMIO_STATUS_BITFIELD); /* dummy read */ + + bcm43xx_write16(dev, 0x0568, 0x0000); + bcm43xx_write16(dev, 0x07C0, 0x0000); + value = ((phy->type == BCM43xx_PHYTYPE_A) ? 1 : 0); + bcm43xx_write16(dev, 0x050C, value); + bcm43xx_write16(dev, 0x0508, 0x0000); + bcm43xx_write16(dev, 0x050A, 0x0000); + bcm43xx_write16(dev, 0x054C, 0x0000); + bcm43xx_write16(dev, 0x056A, 0x0014); + bcm43xx_write16(dev, 0x0568, 0x0826); + bcm43xx_write16(dev, 0x0500, 0x0000); + bcm43xx_write16(dev, 0x0502, 0x0030); + + if (phy->radio_ver == 0x2050 && phy->radio_rev <= 0x5) + bcm43xx_radio_write16(dev, 0x0051, 0x0017); + for (i = 0x00; i < max_loop; i++) { + value = bcm43xx_read16(dev, 0x050E); + if (value & 0x0080) + break; + udelay(10); + } + for (i = 0x00; i < 0x0A; i++) { + value = bcm43xx_read16(dev, 0x050E); + if (value & 0x0400) + break; + udelay(10); + } + for (i = 0x00; i < 0x0A; i++) { + value = bcm43xx_read16(dev, 0x0690); + if (!(value & 0x0100)) + break; + udelay(10); + } + if (phy->radio_ver == 0x2050 && phy->radio_rev <= 0x5) + bcm43xx_radio_write16(dev, 0x0051, 0x0037); +} + +static void key_write(struct bcm43xx_wldev *dev, + u8 index, u8 algorithm, const u8 *key) +{ + unsigned int i; + u32 offset; + u16 value; + u16 kidx; + + /* Key index/algo block */ + kidx = bcm43xx_kidx_to_fw(dev, index); + value = ((kidx << 4) | algorithm); + bcm43xx_shm_write16(dev, BCM43xx_SHM_SHARED, + BCM43xx_SHM_SH_KEYIDXBLOCK + + (kidx * 2), value); + + /* Write the key to the Key Table Pointer offset */ + offset = dev->ktp + (index * BCM43xx_SEC_KEYSIZE); + for (i = 0; i < BCM43xx_SEC_KEYSIZE; i += 2) { + value = key[i]; + value |= (u16)(key[i + 1]) << 8; + bcm43xx_shm_write16(dev, BCM43xx_SHM_SHARED, + offset + i, value); + } +} + +static void keymac_write(struct bcm43xx_wldev *dev, + u8 index, const u8 *addr) +{ + u32 addrtmp[2]; + + assert(index >= 4 + 4); + memcpy(dev->key[index].address, addr, 6); + /* We have two default TX keys and two default RX keys. + * Physical mac 0 is mapped to physical key 8. + * So we must adjust the index here. + */ + index -= 8; + + addrtmp[0] = addr[0]; + addrtmp[0] |= ((u32)(addr[1]) << 8); + addrtmp[0] |= ((u32)(addr[2]) << 16); + addrtmp[0] |= ((u32)(addr[3]) << 24); + addrtmp[1] = addr[4]; + addrtmp[1] |= ((u32)(addr[5]) << 8); + + if (dev->dev->id.revision >= 5) { + /* Receive match transmitter address mechanism */ + bcm43xx_shm_write32(dev, BCM43xx_SHM_RCMTA, + (index * 2) + 0, addrtmp[0]); + bcm43xx_shm_write16(dev, BCM43xx_SHM_RCMTA, + (index * 2) + 1, addrtmp[1]); + } else { + /* RXE (Receive Engine) and + * PSM (Programmable State Machine) mechanism + */ + if (index < 8) { + /* TODO write to RCM 16, 19, 22 and 25 */ + TODO(); + } else { + bcm43xx_shm_write32(dev, BCM43xx_SHM_SHARED, + BCM43xx_SHM_SH_PSM + (index * 6) + 0, + addrtmp[0]); + bcm43xx_shm_write16(dev, BCM43xx_SHM_SHARED, + BCM43xx_SHM_SH_PSM + (index * 6) + 4, + addrtmp[1]); + } + } +} + +static void do_key_write(struct bcm43xx_wldev *dev, + u8 index, u8 algorithm, + const u8 *key, size_t key_len, + const u8 *mac_addr) +{ + u8 buf[BCM43xx_SEC_KEYSIZE]; + + assert(index < dev->max_nr_keys); + assert(key_len <= BCM43xx_SEC_KEYSIZE); + + memset(buf, 0, sizeof(buf)); + if (index >= 8) + keymac_write(dev, index, buf); /* First zero out mac. */ + memcpy(buf, key, key_len); + key_write(dev, index, algorithm, buf); + if (index >= 8) + keymac_write(dev, index, mac_addr); + + dev->key[index].algorithm = algorithm; +} + +static int bcm43xx_key_write(struct bcm43xx_wldev *dev, + int index, u8 algorithm, + const u8 *key, size_t key_len, + const u8 *mac_addr, + struct ieee80211_key_conf *keyconf) +{ + int i; + int sta_keys_start; + + if (key_len > BCM43xx_SEC_KEYSIZE) + return -EINVAL; + if (index < 0) { + /* Per station key with associated MAC address. + * Look if it already exists, if yes update, otherwise + * allocate a new key. + */ + if (bcm43xx_new_kidx_api(dev)) + sta_keys_start = 4; + else + sta_keys_start = 8; + for (i = sta_keys_start; i < dev->max_nr_keys; i++) { + if (compare_ether_addr(dev->key[i].address, mac_addr) == 0) { + /* found existing */ + index = i; + break; + } + } + if (index < 0) { + for (i = sta_keys_start; i < dev->max_nr_keys; i++) { + if (!dev->key[i].enabled) { + /* found empty */ + index = i; + break; + } + } + } + if (index < 0) { + dprintk(KERN_ERR PFX "Out of hw key memory\n"); + return -ENOBUFS; + } + } else + assert(index <= 3); + + do_key_write(dev, index, algorithm, key, key_len, mac_addr); + if ((index <= 3) && !bcm43xx_new_kidx_api(dev)) { + /* Default RX key */ + assert(mac_addr == NULL); + do_key_write(dev, index + 4, algorithm, key, key_len, NULL); + } + keyconf->hw_key_idx = index; + + return 0; +} + +static void bcm43xx_clear_keys(struct bcm43xx_wldev *dev) +{ + static const u8 zero[BCM43xx_SEC_KEYSIZE] = { 0 }; + unsigned int i; + + BUILD_BUG_ON(BCM43xx_SEC_KEYSIZE < ETH_ALEN); + for (i = 0; i < dev->max_nr_keys; i++) { + do_key_write(dev, i, BCM43xx_SEC_ALGO_NONE, + zero, BCM43xx_SEC_KEYSIZE, + zero); + dev->key[i].enabled = 0; + } +} + +/* Turn the Analog ON/OFF */ +static void bcm43xx_switch_analog(struct bcm43xx_wldev *dev, int on) +{ + bcm43xx_write16(dev, BCM43xx_MMIO_PHY0, on ? 0 : 0xF4); +} + +void bcm43xx_wireless_core_reset(struct bcm43xx_wldev *dev, u32 flags) +{ + u32 tmslow; + u32 macctl; + + flags |= BCM43xx_TMSLOW_PHYCLKEN; + flags |= BCM43xx_TMSLOW_PHYRESET; + ssb_device_enable(dev->dev, flags); + msleep(2); /* Wait for the PLL to turn on. */ + + /* Now take the PHY out of Reset again */ + tmslow = ssb_read32(dev->dev, SSB_TMSLOW); + tmslow |= SSB_TMSLOW_FGC; + tmslow &= ~BCM43xx_TMSLOW_PHYRESET; + ssb_write32(dev->dev, SSB_TMSLOW, tmslow); + ssb_read32(dev->dev, SSB_TMSLOW); /* flush */ + msleep(1); + tmslow &= ~SSB_TMSLOW_FGC; + ssb_write32(dev->dev, SSB_TMSLOW, tmslow); + ssb_read32(dev->dev, SSB_TMSLOW); /* flush */ + msleep(1); + + /* Turn Analog ON */ + bcm43xx_switch_analog(dev, 1); + + macctl = bcm43xx_read32(dev, BCM43xx_MMIO_MACCTL); + macctl &= ~BCM43xx_MACCTL_GMODE; + if (flags & BCM43xx_TMSLOW_GMODE) + macctl |= BCM43xx_MACCTL_GMODE; + macctl |= BCM43xx_MACCTL_IHR_ENABLED; + bcm43xx_write32(dev, BCM43xx_MMIO_MACCTL, macctl); +} + +static void handle_irq_transmit_status(struct bcm43xx_wldev *dev) +{ + u32 v0, v1; + u16 tmp; + struct bcm43xx_txstatus stat; + + while (1) { + v0 = bcm43xx_read32(dev, BCM43xx_MMIO_XMITSTAT_0); + if (!(v0 & 0x00000001)) + break; + v1 = bcm43xx_read32(dev, BCM43xx_MMIO_XMITSTAT_1); + + stat.cookie = (v0 >> 16); + stat.seq = (v1 & 0x0000FFFF); + stat.phy_stat = ((v1 & 0x00FF0000) >> 16); + tmp = (v0 & 0x0000FFFF); + stat.frame_count = ((tmp & 0xF000) >> 12); + stat.rts_count = ((tmp & 0x0F00) >> 8); + stat.supp_reason = ((tmp & 0x001C) >> 2); + stat.pm_indicated = !!(tmp & 0x0080); + stat.intermediate = !!(tmp & 0x0040); + stat.for_ampdu = !!(tmp & 0x0020); + stat.acked = !!(tmp & 0x0002); + + bcm43xx_handle_txstatus(dev, &stat); + } +} + +static void drain_txstatus_queue(struct bcm43xx_wldev *dev) +{ + u32 dummy; + + if (dev->dev->id.revision < 5) + return; + /* Read all entries from the microcode TXstatus FIFO + * and throw them away. + */ + while (1) { + dummy = bcm43xx_read32(dev, BCM43xx_MMIO_XMITSTAT_0); + if (!(dummy & 0x00000001)) + break; + dummy = bcm43xx_read32(dev, BCM43xx_MMIO_XMITSTAT_1); + } +} + +static u32 bcm43xx_jssi_read(struct bcm43xx_wldev *dev) +{ + u32 val = 0; + + val = bcm43xx_shm_read16(dev, BCM43xx_SHM_SHARED, 0x08A); + val <<= 16; + val |= bcm43xx_shm_read16(dev, BCM43xx_SHM_SHARED, 0x088); + + return val; +} + +static void bcm43xx_jssi_write(struct bcm43xx_wldev *dev, u32 jssi) +{ + bcm43xx_shm_write16(dev, BCM43xx_SHM_SHARED, 0x088, + (jssi & 0x0000FFFF)); + bcm43xx_shm_write16(dev, BCM43xx_SHM_SHARED, 0x08A, + (jssi & 0xFFFF0000) >> 16); +} + +static void bcm43xx_generate_noise_sample(struct bcm43xx_wldev *dev) +{ + bcm43xx_jssi_write(dev, 0x7F7F7F7F); + bcm43xx_write32(dev, BCM43xx_MMIO_STATUS2_BITFIELD, + bcm43xx_read32(dev, BCM43xx_MMIO_STATUS2_BITFIELD) + | (1 << 4)); + assert(dev->noisecalc.channel_at_start == dev->phy.channel); +} + +static void bcm43xx_calculate_link_quality(struct bcm43xx_wldev *dev) +{ + /* Top half of Link Quality calculation. */ + + if (dev->noisecalc.calculation_running) + return; + dev->noisecalc.channel_at_start = dev->phy.channel; + dev->noisecalc.calculation_running = 1; + dev->noisecalc.nr_samples = 0; + + bcm43xx_generate_noise_sample(dev); +} + +static void handle_irq_noise(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + u16 tmp; + u8 noise[4]; + u8 i, j; + s32 average; + + /* Bottom half of Link Quality calculation. */ + + assert(dev->noisecalc.calculation_running); + if (dev->noisecalc.channel_at_start != phy->channel) + goto drop_calculation; + *((u32 *)noise) = cpu_to_le32(bcm43xx_jssi_read(dev)); + if (noise[0] == 0x7F || noise[1] == 0x7F || + noise[2] == 0x7F || noise[3] == 0x7F) + goto generate_new; + + /* Get the noise samples. */ + assert(dev->noisecalc.nr_samples < 8); + i = dev->noisecalc.nr_samples; + noise[0] = limit_value(noise[0], 0, ARRAY_SIZE(phy->nrssi_lt) - 1); + noise[1] = limit_value(noise[1], 0, ARRAY_SIZE(phy->nrssi_lt) - 1); + noise[2] = limit_value(noise[2], 0, ARRAY_SIZE(phy->nrssi_lt) - 1); + noise[3] = limit_value(noise[3], 0, ARRAY_SIZE(phy->nrssi_lt) - 1); + dev->noisecalc.samples[i][0] = phy->nrssi_lt[noise[0]]; + dev->noisecalc.samples[i][1] = phy->nrssi_lt[noise[1]]; + dev->noisecalc.samples[i][2] = phy->nrssi_lt[noise[2]]; + dev->noisecalc.samples[i][3] = phy->nrssi_lt[noise[3]]; + dev->noisecalc.nr_samples++; + if (dev->noisecalc.nr_samples == 8) { + /* Calculate the Link Quality by the noise samples. */ + average = 0; + for (i = 0; i < 8; i++) { + for (j = 0; j < 4; j++) + average += dev->noisecalc.samples[i][j]; + } + average /= (8 * 4); + average *= 125; + average += 64; + average /= 128; + tmp = bcm43xx_shm_read16(dev, BCM43xx_SHM_SHARED, 0x40C); + tmp = (tmp / 128) & 0x1F; + if (tmp >= 8) + average += 2; + else + average -= 25; + if (tmp == 8) + average -= 72; + else + average -= 48; + + dev->stats.link_noise = average; +drop_calculation: + dev->noisecalc.calculation_running = 0; + return; + } +generate_new: + bcm43xx_generate_noise_sample(dev); +} + +static void handle_irq_tbtt_indication(struct bcm43xx_wldev *dev) +{ + if (bcm43xx_is_mode(dev->wl, IEEE80211_IF_TYPE_AP)) { + ///TODO: PS TBTT + } else { + if (1/*FIXME: the last PSpoll frame was sent successfully */) + bcm43xx_power_saving_ctl_bits(dev, -1, -1); + } + dev->reg124_set_0x4 = 0; + if (bcm43xx_is_mode(dev->wl, IEEE80211_IF_TYPE_IBSS)) + dev->reg124_set_0x4 = 1; +} + +static void handle_irq_atim_end(struct bcm43xx_wldev *dev) +{ + if (!dev->reg124_set_0x4 /*FIXME rename this variable*/) + return; + bcm43xx_write32(dev, BCM43xx_MMIO_STATUS2_BITFIELD, + bcm43xx_read32(dev, BCM43xx_MMIO_STATUS2_BITFIELD) + | 0x4); +} + +static void handle_irq_pmq(struct bcm43xx_wldev *dev) +{ + u32 tmp; + + //TODO: AP mode. + + while (1) { + tmp = bcm43xx_read32(dev, BCM43xx_MMIO_PS_STATUS); + if (!(tmp & 0x00000008)) + break; + } + /* 16bit write is odd, but correct. */ + bcm43xx_write16(dev, BCM43xx_MMIO_PS_STATUS, 0x0002); +} + +static void bcm43xx_write_template_common(struct bcm43xx_wldev *dev, + const u8* data, u16 size, + u16 ram_offset, + u16 shm_size_offset, u8 rate) +{ + u32 i, tmp; + struct bcm43xx_plcp_hdr4 plcp; + + plcp.data = 0; + bcm43xx_generate_plcp_hdr(&plcp, size + FCS_LEN, rate); + bcm43xx_ram_write(dev, ram_offset, le32_to_cpu(plcp.data)); + ram_offset += sizeof(u32); + /* The PLCP is 6 bytes long, but we only wrote 4 bytes, yet. + * So leave the first two bytes of the next write blank. + */ + tmp = (u32)(data[0]) << 16; + tmp |= (u32)(data[1]) << 24; + bcm43xx_ram_write(dev, ram_offset, tmp); + ram_offset += sizeof(u32); + for (i = 2; i < size; i += sizeof(u32)) { + tmp = (u32)(data[i + 0]); + if (i + 1 < size) + tmp |= (u32)(data[i + 1]) << 8; + if (i + 2 < size) + tmp |= (u32)(data[i + 2]) << 16; + if (i + 3 < size) + tmp |= (u32)(data[i + 3]) << 24; + bcm43xx_ram_write(dev, ram_offset + i - 2, tmp); + } + bcm43xx_shm_write16(dev, BCM43xx_SHM_SHARED, shm_size_offset, + size + sizeof(struct bcm43xx_plcp_hdr6)); +} + +static void bcm43xx_write_beacon_template(struct bcm43xx_wldev *dev, + u16 ram_offset, + u16 shm_size_offset, u8 rate) +{ + int len; + const u8 *data; + + assert(dev->cached_beacon); + len = min((size_t)dev->cached_beacon->len, + 0x200 - sizeof(struct bcm43xx_plcp_hdr6)); + data = (const u8 *)(dev->cached_beacon->data); + bcm43xx_write_template_common(dev, data, + len, ram_offset, + shm_size_offset, rate); +} + +static void bcm43xx_write_probe_resp_plcp(struct bcm43xx_wldev *dev, + u16 shm_offset, u16 size, u8 rate) +{ + struct bcm43xx_plcp_hdr4 plcp; + u32 tmp; + u16 packet_time; + + plcp.data = 0; + bcm43xx_generate_plcp_hdr(&plcp, size + FCS_LEN, rate); + /* + * 144 + 48 + 10 = preamble + PLCP + SIFS, + * taken from mac80211 timings calculation. + * + * FIXME: long preamble assumed! + * + */ + packet_time = 202 + (size + FCS_LEN) * 16 / rate; + if ((size + FCS_LEN) * 16 % rate >= rate / 2) + ++packet_time; + + /* Write PLCP in two parts and timing for packet transfer */ + tmp = le32_to_cpu(plcp.data); + bcm43xx_shm_write16(dev, BCM43xx_SHM_SHARED, shm_offset, + tmp & 0xFFFF); + bcm43xx_shm_write16(dev, BCM43xx_SHM_SHARED, shm_offset + 2, + tmp >> 16); + bcm43xx_shm_write16(dev, BCM43xx_SHM_SHARED, shm_offset + 6, + packet_time); +} + +/* Instead of using custom probe response template, this function + * just patches custom beacon template by: + * 1) Changing packet type + * 2) Patching duration field + * 3) Stripping TIM + */ +static u8 * bcm43xx_generate_probe_resp(struct bcm43xx_wldev *dev, + u16* dest_size, u8 rate) +{ + const u8 *src_data; + u8 *dest_data; + u16 src_size, elem_size, src_pos, dest_pos, tmp; + + assert(dev->cached_beacon); + src_size = dev->cached_beacon->len; + src_data = (const u8*)dev->cached_beacon->data; + + if (unlikely(src_size < 0x24)) { + dprintk(KERN_ERR PFX "bcm43xx_generate_probe_resp: " + "invalid beacon\n"); + return NULL; + } + + dest_data = kmalloc(src_size, GFP_ATOMIC); + if (unlikely(!dest_data)) + return NULL; + + /* 0x24 is offset of first variable-len Information-Element + * in beacon frame. + */ + memcpy(dest_data, src_data, 0x24); + src_pos = dest_pos = 0x24; + for ( ; src_pos < src_size - 2; src_pos += elem_size) { + elem_size = src_data[src_pos + 1] + 2; + if (src_data[src_pos] != 0x05) { /* TIM */ + memcpy(dest_data + dest_pos, src_data + src_pos, + elem_size); + dest_pos += elem_size; + } + } + *dest_size = dest_pos; + + /* Set the frame control. */ + dest_data[0] = (IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_PROBE_RESP); + dest_data[1] = 0; + + /* Set the duration field. + * + * 144 + 48 + 10 = preamble + PLCP + SIFS, + * taken from mac80211 timings calculation. + * + * FIXME: long preamble assumed! + * + */ + tmp = 202 + (14 + FCS_LEN) * 16 / rate; + if ((14 + FCS_LEN) * 16 % rate >= rate / 2) + ++tmp; + + dest_data[2] = tmp & 0xFF; + dest_data[3] = (tmp >> 8) & 0xFF; + + return dest_data; +} + +static void bcm43xx_write_probe_resp_template(struct bcm43xx_wldev *dev, + u16 ram_offset, + u16 shm_size_offset, u8 rate) +{ + u8* probe_resp_data; + u16 size; + + assert(dev->cached_beacon); + size = dev->cached_beacon->len; + probe_resp_data = bcm43xx_generate_probe_resp(dev, &size, rate); + if (unlikely(!probe_resp_data)) + return; + + /* Looks like PLCP headers plus packet timings are stored for + * all possible basic rates + */ + bcm43xx_write_probe_resp_plcp(dev, 0x31A, size, + BCM43xx_CCK_RATE_1MB); + bcm43xx_write_probe_resp_plcp(dev, 0x32C, size, + BCM43xx_CCK_RATE_2MB); + bcm43xx_write_probe_resp_plcp(dev, 0x33E, size, + BCM43xx_CCK_RATE_5MB); + bcm43xx_write_probe_resp_plcp(dev, 0x350, size, + BCM43xx_CCK_RATE_11MB); + + size = min((size_t)size, + 0x200 - sizeof(struct bcm43xx_plcp_hdr6)); + bcm43xx_write_template_common(dev, probe_resp_data, + size, ram_offset, + shm_size_offset, rate); + kfree(probe_resp_data); +} + +static int bcm43xx_refresh_cached_beacon(struct bcm43xx_wldev *dev, + struct sk_buff *beacon) +{ + if (dev->cached_beacon) + kfree_skb(dev->cached_beacon); + dev->cached_beacon = beacon; + + return 0; +} + +static void bcm43xx_update_templates(struct bcm43xx_wldev *dev) +{ + u32 status; + + assert(dev->cached_beacon); + + bcm43xx_write_beacon_template(dev, 0x68, 0x18, + BCM43xx_CCK_RATE_1MB); + bcm43xx_write_beacon_template(dev, 0x468, 0x1A, + BCM43xx_CCK_RATE_1MB); + bcm43xx_write_probe_resp_template(dev, 0x268, 0x4A, + BCM43xx_CCK_RATE_11MB); + + status = bcm43xx_read32(dev, BCM43xx_MMIO_STATUS2_BITFIELD); + status |= 0x03; + bcm43xx_write32(dev, BCM43xx_MMIO_STATUS2_BITFIELD, status); +} + +static void bcm43xx_refresh_templates(struct bcm43xx_wldev *dev, + struct sk_buff *beacon) +{ + int err; + + err = bcm43xx_refresh_cached_beacon(dev, beacon); + if (unlikely(err)) + return; + bcm43xx_update_templates(dev); +} + +static void bcm43xx_set_ssid(struct bcm43xx_wldev *dev, + const u8 *ssid, u8 ssid_len) +{ + u32 tmp; + u16 i, len; + + len = min((u16)ssid_len, (u16)0x100); + for (i = 0; i < len; i += sizeof(u32)) { + tmp = (u32)(ssid[i + 0]); + if (i + 1 < len) + tmp |= (u32)(ssid[i + 1]) << 8; + if (i + 2 < len) + tmp |= (u32)(ssid[i + 2]) << 16; + if (i + 3 < len) + tmp |= (u32)(ssid[i + 3]) << 24; + bcm43xx_shm_write32(dev, BCM43xx_SHM_SHARED, + 0x380 + i, tmp); + } + bcm43xx_shm_write16(dev, BCM43xx_SHM_SHARED, + 0x48, len); +} + +static void bcm43xx_set_beacon_int(struct bcm43xx_wldev *dev, u16 beacon_int) +{ + bcm43xx_time_lock(dev); + if (dev->dev->id.revision >= 3) { + bcm43xx_write32(dev, 0x188, (beacon_int << 16)); + } else { + bcm43xx_write16(dev, 0x606, (beacon_int >> 6)); + bcm43xx_write16(dev, 0x610, beacon_int); + } + bcm43xx_time_unlock(dev); +} + +static void handle_irq_beacon(struct bcm43xx_wldev *dev) +{ + u32 status; + + if (!bcm43xx_is_mode(dev->wl, IEEE80211_IF_TYPE_AP)) + return; + + dev->irq_savedstate &= ~BCM43xx_IRQ_BEACON; + status = bcm43xx_read32(dev, BCM43xx_MMIO_STATUS2_BITFIELD); + + if (!dev->cached_beacon || ((status & 0x1) && (status & 0x2))) { + /* ACK beacon IRQ. */ + bcm43xx_write32(dev, BCM43xx_MMIO_GEN_IRQ_REASON, + BCM43xx_IRQ_BEACON); + dev->irq_savedstate |= BCM43xx_IRQ_BEACON; + if (dev->cached_beacon) + kfree_skb(dev->cached_beacon); + dev->cached_beacon = NULL; + return; + } + if (!(status & 0x1)) { + bcm43xx_write_beacon_template(dev, 0x68, 0x18, + BCM43xx_CCK_RATE_1MB); + status |= 0x1; + bcm43xx_write32(dev, BCM43xx_MMIO_STATUS2_BITFIELD, + status); + } + if (!(status & 0x2)) { + bcm43xx_write_beacon_template(dev, 0x468, 0x1A, + BCM43xx_CCK_RATE_1MB); + status |= 0x2; + bcm43xx_write32(dev, BCM43xx_MMIO_STATUS2_BITFIELD, + status); + } +} + +static void handle_irq_ucode_debug(struct bcm43xx_wldev *dev) +{ + //TODO +} + +/* Interrupt handler bottom-half */ +static void bcm43xx_interrupt_tasklet(struct bcm43xx_wldev *dev) +{ + u32 reason; + u32 dma_reason[ARRAY_SIZE(dev->dma_reason)]; + u32 merged_dma_reason = 0; + int i, activity = 0; + unsigned long flags; + + spin_lock_irqsave(&dev->wl->irq_lock, flags); + + assert(bcm43xx_status(dev) == BCM43xx_STAT_INITIALIZED); + assert(dev->started); + + reason = dev->irq_reason; + for (i = 0; i < ARRAY_SIZE(dma_reason); i++) { + dma_reason[i] = dev->dma_reason[i]; + merged_dma_reason |= dma_reason[i]; + } + + if (unlikely(reason & BCM43xx_IRQ_MAC_TXERR)) + printkl(KERN_ERR PFX "MAC transmission error\n"); + + if (unlikely(reason & BCM43xx_IRQ_PHY_TXERR)) + printkl(KERN_ERR PFX "PHY transmission error\n"); + + if (unlikely(merged_dma_reason & (BCM43xx_DMAIRQ_FATALMASK | + BCM43xx_DMAIRQ_NONFATALMASK))) { + if (merged_dma_reason & BCM43xx_DMAIRQ_FATALMASK) { + printkl(KERN_ERR PFX "FATAL ERROR: Fatal DMA error: " + "0x%08X, 0x%08X, 0x%08X, " + "0x%08X, 0x%08X, 0x%08X\n", + dma_reason[0], dma_reason[1], + dma_reason[2], dma_reason[3], + dma_reason[4], dma_reason[5]); + bcm43xx_controller_restart(dev, "DMA error"); + mmiowb(); + spin_unlock_irqrestore(&dev->wl->irq_lock, flags); + return; + } + if (merged_dma_reason & BCM43xx_DMAIRQ_NONFATALMASK) { + printkl(KERN_ERR PFX "DMA error: " + "0x%08X, 0x%08X, 0x%08X, " + "0x%08X, 0x%08X, 0x%08X\n", + dma_reason[0], dma_reason[1], + dma_reason[2], dma_reason[3], + dma_reason[4], dma_reason[5]); + } + } + + if (unlikely(reason & BCM43xx_IRQ_UCODE_DEBUG)) + handle_irq_ucode_debug(dev); + if (reason & BCM43xx_IRQ_TBTT_INDI) + handle_irq_tbtt_indication(dev); + if (reason & BCM43xx_IRQ_ATIM_END) + handle_irq_atim_end(dev); + if (reason & BCM43xx_IRQ_BEACON) + handle_irq_beacon(dev); + if (reason & BCM43xx_IRQ_PMQ) + handle_irq_pmq(dev); + if (reason & BCM43xx_IRQ_TXFIFO_FLUSH_OK) + ;/*TODO*/ + if (reason & BCM43xx_IRQ_NOISESAMPLE_OK) + handle_irq_noise(dev); + + /* Check the DMA reason registers for received data. */ + if (dma_reason[0] & BCM43xx_DMAIRQ_RX_DONE) { + if (bcm43xx_using_pio(dev)) + bcm43xx_pio_rx(dev->pio.queue0); + else + bcm43xx_dma_rx(dev->dma.rx_ring0); + /* We intentionally don't set "activity" to 1, here. */ + } + assert(!(dma_reason[1] & BCM43xx_DMAIRQ_RX_DONE)); + assert(!(dma_reason[2] & BCM43xx_DMAIRQ_RX_DONE)); + if (dma_reason[3] & BCM43xx_DMAIRQ_RX_DONE) { + if (bcm43xx_using_pio(dev)) + bcm43xx_pio_rx(dev->pio.queue3); + else + bcm43xx_dma_rx(dev->dma.rx_ring3); + activity = 1; + } + assert(!(dma_reason[4] & BCM43xx_DMAIRQ_RX_DONE)); + assert(!(dma_reason[5] & BCM43xx_DMAIRQ_RX_DONE)); + + if (reason & BCM43xx_IRQ_TX_OK) { + handle_irq_transmit_status(dev); + activity = 1; + //TODO: In AP mode, this also causes sending of powersave responses. + } + + if (!modparam_noleds) + bcm43xx_leds_update(dev, activity); + bcm43xx_interrupt_enable(dev, dev->irq_savedstate); + mmiowb(); + spin_unlock_irqrestore(&dev->wl->irq_lock, flags); +} + +static void pio_irq_workaround(struct bcm43xx_wldev *dev, + u16 base, int queueidx) +{ + u16 rxctl; + + rxctl = bcm43xx_read16(dev, base + BCM43xx_PIO_RXCTL); + if (rxctl & BCM43xx_PIO_RXCTL_DATAAVAILABLE) + dev->dma_reason[queueidx] |= BCM43xx_DMAIRQ_RX_DONE; + else + dev->dma_reason[queueidx] &= ~BCM43xx_DMAIRQ_RX_DONE; +} + +static void bcm43xx_interrupt_ack(struct bcm43xx_wldev *dev, u32 reason) +{ + if (bcm43xx_using_pio(dev) && + (dev->dev->id.revision < 3) && + (!(reason & BCM43xx_IRQ_PIO_WORKAROUND))) { + /* Apply a PIO specific workaround to the dma_reasons */ + pio_irq_workaround(dev, BCM43xx_MMIO_PIO1_BASE, 0); + pio_irq_workaround(dev, BCM43xx_MMIO_PIO2_BASE, 1); + pio_irq_workaround(dev, BCM43xx_MMIO_PIO3_BASE, 2); + pio_irq_workaround(dev, BCM43xx_MMIO_PIO4_BASE, 3); + } + + bcm43xx_write32(dev, BCM43xx_MMIO_GEN_IRQ_REASON, reason); + + bcm43xx_write32(dev, BCM43xx_MMIO_DMA0_REASON, + dev->dma_reason[0]); + bcm43xx_write32(dev, BCM43xx_MMIO_DMA1_REASON, + dev->dma_reason[1]); + bcm43xx_write32(dev, BCM43xx_MMIO_DMA2_REASON, + dev->dma_reason[2]); + bcm43xx_write32(dev, BCM43xx_MMIO_DMA3_REASON, + dev->dma_reason[3]); + bcm43xx_write32(dev, BCM43xx_MMIO_DMA4_REASON, + dev->dma_reason[4]); + bcm43xx_write32(dev, BCM43xx_MMIO_DMA5_REASON, + dev->dma_reason[5]); +} + +/* Interrupt handler top-half */ +static irqreturn_t bcm43xx_interrupt_handler(int irq, void *dev_id) +{ + irqreturn_t ret = IRQ_HANDLED; + struct bcm43xx_wldev *dev = dev_id; + u32 reason; + + if (!dev) + return IRQ_NONE; + + spin_lock(&dev->wl->irq_lock); + + reason = bcm43xx_read32(dev, BCM43xx_MMIO_GEN_IRQ_REASON); + if (reason == 0xffffffff) { + /* irq not for us (shared irq) */ + ret = IRQ_NONE; + goto out; + } + reason &= bcm43xx_read32(dev, BCM43xx_MMIO_GEN_IRQ_MASK); + if (!reason) + goto out; + + assert(bcm43xx_status(dev) == BCM43xx_STAT_INITIALIZED); + assert(dev->started); + + dev->dma_reason[0] = bcm43xx_read32(dev, BCM43xx_MMIO_DMA0_REASON) + & 0x0001DC00; + dev->dma_reason[1] = bcm43xx_read32(dev, BCM43xx_MMIO_DMA1_REASON) + & 0x0000DC00; + dev->dma_reason[2] = bcm43xx_read32(dev, BCM43xx_MMIO_DMA2_REASON) + & 0x0000DC00; + dev->dma_reason[3] = bcm43xx_read32(dev, BCM43xx_MMIO_DMA3_REASON) + & 0x0001DC00; + dev->dma_reason[4] = bcm43xx_read32(dev, BCM43xx_MMIO_DMA4_REASON) + & 0x0000DC00; + dev->dma_reason[5] = bcm43xx_read32(dev, BCM43xx_MMIO_DMA5_REASON) + & 0x0000DC00; + + bcm43xx_interrupt_ack(dev, reason); + /* disable all IRQs. They are enabled again in the bottom half. */ + dev->irq_savedstate = bcm43xx_interrupt_disable(dev, BCM43xx_IRQ_ALL); + /* save the reason code and call our bottom half. */ + dev->irq_reason = reason; + tasklet_schedule(&dev->isr_tasklet); +out: + mmiowb(); + spin_unlock(&dev->wl->irq_lock); + + return ret; +} + +static void bcm43xx_release_firmware(struct bcm43xx_wldev *dev) +{ + release_firmware(dev->fw.ucode); + dev->fw.ucode = NULL; + release_firmware(dev->fw.pcm); + dev->fw.pcm = NULL; + release_firmware(dev->fw.initvals0); + dev->fw.initvals0 = NULL; + release_firmware(dev->fw.initvals1); + dev->fw.initvals1 = NULL; +} + +static int bcm43xx_request_firmware(struct bcm43xx_wldev *dev) +{ + u8 rev = dev->dev->id.revision; + int err = 0; + int nr; + char buf[22 + sizeof(modparam_fwpostfix) - 1] = { 0 }; + + if (!dev->fw.ucode) { + snprintf(buf, ARRAY_SIZE(buf), "bcm43xx_microcode%d%s.fw", + (rev >= 5 ? 5 : rev), + modparam_fwpostfix); + err = request_firmware(&dev->fw.ucode, buf, dev->dev->dev); + if (err) { + printk(KERN_ERR PFX + "Error: Microcode \"%s\" not available or load failed.\n", + buf); + goto error; + } + } + + if (!dev->fw.pcm) { + snprintf(buf, ARRAY_SIZE(buf), + "bcm43xx_pcm%d%s.fw", + (rev < 5 ? 4 : 5), + modparam_fwpostfix); + err = request_firmware(&dev->fw.pcm, buf, dev->dev->dev); + if (err) { + printk(KERN_ERR PFX + "Error: PCM \"%s\" not available or load failed.\n", + buf); + goto error; + } + } + + if (!dev->fw.initvals0) { + if (rev == 2 || rev == 4) { + switch (dev->phy.type) { + case BCM43xx_PHYTYPE_A: + nr = 3; + break; + case BCM43xx_PHYTYPE_B: + case BCM43xx_PHYTYPE_G: + nr = 1; + break; + default: + goto err_noinitval; + } + + } else if (rev >= 5) { + switch (dev->phy.type) { + case BCM43xx_PHYTYPE_A: + nr = 7; + break; + case BCM43xx_PHYTYPE_B: + case BCM43xx_PHYTYPE_G: + nr = 5; + break; + default: + goto err_noinitval; + } + } else + goto err_noinitval; + snprintf(buf, ARRAY_SIZE(buf), "bcm43xx_initval%02d%s.fw", + nr, modparam_fwpostfix); + + err = request_firmware(&dev->fw.initvals0, buf, dev->dev->dev); + if (err) { + printk(KERN_ERR PFX + "Error: InitVals \"%s\" not available or load failed.\n", + buf); + goto error; + } + if (dev->fw.initvals0->size % sizeof(struct bcm43xx_initval)) { + printk(KERN_ERR PFX "InitVals fileformat error.\n"); + goto error; + } + } + + if (!dev->fw.initvals1) { + if (rev >= 5) { + u32 sbtmstatehigh; + + switch (dev->phy.type) { + case BCM43xx_PHYTYPE_A: + sbtmstatehigh = ssb_read32(dev->dev, SSB_TMSHIGH); + if (sbtmstatehigh & 0x00010000) + nr = 9; + else + nr = 10; + break; + case BCM43xx_PHYTYPE_B: + case BCM43xx_PHYTYPE_G: + nr = 6; + break; + default: + goto err_noinitval; + } + snprintf(buf, ARRAY_SIZE(buf), "bcm43xx_initval%02d%s.fw", + nr, modparam_fwpostfix); + + err = request_firmware(&dev->fw.initvals1, buf, dev->dev->dev); + if (err) { + printk(KERN_ERR PFX + "Error: InitVals \"%s\" not available or load failed.\n", + buf); + goto error; + } + if (dev->fw.initvals1->size % sizeof(struct bcm43xx_initval)) { + printk(KERN_ERR PFX "InitVals fileformat error.\n"); + goto error; + } + } + } + +out: + return err; +error: + bcm43xx_release_firmware(dev); + goto out; +err_noinitval: + printk(KERN_ERR PFX "Error: No InitVals available!\n"); + err = -ENOENT; + goto error; +} + +static int bcm43xx_upload_microcode(struct bcm43xx_wldev *dev) +{ + const __be32 *data; + unsigned int i, len; + u16 fwrev, fwpatch, fwdate, fwtime; + u32 tmp; + int err = 0; + + /* Upload Microcode. */ + data = (__be32 *)(dev->fw.ucode->data); + len = dev->fw.ucode->size / sizeof(__be32); + bcm43xx_shm_control_word(dev, + BCM43xx_SHM_UCODE | BCM43xx_SHM_AUTOINC_W, + 0x0000); + for (i = 0; i < len; i++) { + bcm43xx_write32(dev, BCM43xx_MMIO_SHM_DATA, + be32_to_cpu(data[i])); + udelay(10); + } + + /* Upload PCM data. */ + data = (__be32 *)(dev->fw.pcm->data); + len = dev->fw.pcm->size / sizeof(__be32); + bcm43xx_shm_control_word(dev, BCM43xx_SHM_HW, 0x01EA); + bcm43xx_write32(dev, BCM43xx_MMIO_SHM_DATA, 0x00004000); + /* No need for autoinc bit in SHM_HW */ + bcm43xx_shm_control_word(dev, BCM43xx_SHM_HW, 0x01EB); + for (i = 0; i < len; i++) { + bcm43xx_write32(dev, BCM43xx_MMIO_SHM_DATA, + be32_to_cpu(data[i])); + udelay(10); + } + + bcm43xx_write32(dev, BCM43xx_MMIO_GEN_IRQ_REASON, BCM43xx_IRQ_ALL); + bcm43xx_write32(dev, BCM43xx_MMIO_STATUS_BITFIELD, 0x00020402); + + /* Wait for the microcode to load and respond */ + i = 0; + while (1) { + tmp = bcm43xx_read32(dev, BCM43xx_MMIO_GEN_IRQ_REASON); + if (tmp == BCM43xx_IRQ_MAC_SUSPENDED) + break; + i++; + if (i >= BCM43xx_IRQWAIT_MAX_RETRIES) { + printk(KERN_ERR PFX "Microcode not responding\n"); + err = -ENODEV; + goto out; + } + udelay(10); + } + bcm43xx_read32(dev, BCM43xx_MMIO_GEN_IRQ_REASON); /* dummy read */ + + /* Get and check the revisions. */ + fwrev = bcm43xx_shm_read16(dev, BCM43xx_SHM_SHARED, + BCM43xx_SHM_SH_UCODEREV); + fwpatch = bcm43xx_shm_read16(dev, BCM43xx_SHM_SHARED, + BCM43xx_SHM_SH_UCODEPATCH); + fwdate = bcm43xx_shm_read16(dev, BCM43xx_SHM_SHARED, + BCM43xx_SHM_SH_UCODEDATE); + fwtime = bcm43xx_shm_read16(dev, BCM43xx_SHM_SHARED, + BCM43xx_SHM_SH_UCODETIME); + + if (fwrev <= 0x128) { + printk(KERN_ERR PFX "YOUR FIRMWARE IS TOO OLD. Firmware from " + "binary drivers older than version 4.x is unsupported. " + "You must upgrade your firmware files.\n"); + bcm43xx_write32(dev, BCM43xx_MMIO_STATUS_BITFIELD, 0); + err = -EOPNOTSUPP; + goto out; + } + printk(KERN_DEBUG PFX "Loading firmware version %u.%u " + "(20%.2i-%.2i-%.2i %.2i:%.2i:%.2i)\n", + fwrev, fwpatch, + (fwdate >> 12) & 0xF, (fwdate >> 8) & 0xF, fwdate & 0xFF, + (fwtime >> 11) & 0x1F, (fwtime >> 5) & 0x3F, fwtime & 0x1F); + + dev->fw.rev = fwrev; + dev->fw.patch = fwpatch; + +out: + return err; +} + +static int bcm43xx_write_initvals(struct bcm43xx_wldev *dev, + const struct bcm43xx_initval *data, + const unsigned int len) +{ + u16 offset, size; + u32 value; + unsigned int i; + + for (i = 0; i < len; i++) { + offset = be16_to_cpu(data[i].offset); + size = be16_to_cpu(data[i].size); + value = be32_to_cpu(data[i].value); + + if (unlikely(offset >= 0x1000)) + goto err_format; + if (size == 2) { + if (unlikely(value & 0xFFFF0000)) + goto err_format; + bcm43xx_write16(dev, offset, (u16)value); + } else if (size == 4) { + bcm43xx_write32(dev, offset, value); + } else + goto err_format; + } + + return 0; + +err_format: + printk(KERN_ERR PFX "InitVals (bcm43xx_initvalXX.fw) file-format error. " + "Please fix your bcm43xx firmware files.\n"); + return -EPROTO; +} + +static int bcm43xx_upload_initvals(struct bcm43xx_wldev *dev) +{ + int err; + + err = bcm43xx_write_initvals(dev, (struct bcm43xx_initval *)dev->fw.initvals0->data, + dev->fw.initvals0->size / sizeof(struct bcm43xx_initval)); + if (err) + goto out; + if (dev->fw.initvals1) { + err = bcm43xx_write_initvals(dev, (struct bcm43xx_initval *)dev->fw.initvals1->data, + dev->fw.initvals1->size / sizeof(struct bcm43xx_initval)); + if (err) + goto out; + } +out: + return err; +} + +/* Initialize the GPIOs + * http://bcm-specs.sipsolutions.net/GPIO + */ +static int bcm43xx_gpio_init(struct bcm43xx_wldev *dev) +{ + struct ssb_bus *bus = dev->dev->bus; + struct ssb_device *gpiodev, *pcidev = NULL; + u32 mask, set; + + bcm43xx_write32(dev, BCM43xx_MMIO_STATUS_BITFIELD, + bcm43xx_read32(dev, BCM43xx_MMIO_STATUS_BITFIELD) + & 0xFFFF3FFF); + + bcm43xx_leds_switch_all(dev, 0); + bcm43xx_write16(dev, BCM43xx_MMIO_GPIO_MASK, + bcm43xx_read16(dev, BCM43xx_MMIO_GPIO_MASK) + | 0x000F); + + mask = 0x0000001F; + set = 0x0000000F; + if (dev->dev->bus->chip_id == 0x4301) { + mask |= 0x0060; + set |= 0x0060; + } + if (0 /* FIXME: conditional unknown */) { + bcm43xx_write16(dev, BCM43xx_MMIO_GPIO_MASK, + bcm43xx_read16(dev, BCM43xx_MMIO_GPIO_MASK) + | 0x0100); + mask |= 0x0180; + set |= 0x0180; + } + if (dev->dev->bus->sprom.r1.boardflags_lo & BCM43xx_BFL_PACTRL) { + bcm43xx_write16(dev, BCM43xx_MMIO_GPIO_MASK, + bcm43xx_read16(dev, BCM43xx_MMIO_GPIO_MASK) + | 0x0200); + mask |= 0x0200; + set |= 0x0200; + } + if (dev->dev->id.revision >= 2) + mask |= 0x0010; /* FIXME: This is redundant. */ + +#ifdef CONFIG_SSB_DRIVER_PCICORE + pcidev = bus->pcicore.dev; +#endif + gpiodev = bus->chipco.dev ? : pcidev; + if (!gpiodev) + return 0; + ssb_write32(gpiodev, BCM43xx_GPIO_CONTROL, + (ssb_read32(gpiodev, BCM43xx_GPIO_CONTROL) + & mask) | set); + + return 0; +} + +/* Turn off all GPIO stuff. Call this on module unload, for example. */ +static void bcm43xx_gpio_cleanup(struct bcm43xx_wldev *dev) +{ + struct ssb_bus *bus = dev->dev->bus; + struct ssb_device *gpiodev, *pcidev = NULL; + +#ifdef CONFIG_SSB_DRIVER_PCICORE + pcidev = bus->pcicore.dev; +#endif + gpiodev = bus->chipco.dev ? : pcidev; + if (!gpiodev) + return; + ssb_write32(gpiodev, BCM43xx_GPIO_CONTROL, 0); +} + +/* http://bcm-specs.sipsolutions.net/EnableMac */ +void bcm43xx_mac_enable(struct bcm43xx_wldev *dev) +{ + dev->mac_suspended--; + assert(dev->mac_suspended >= 0); + if (dev->mac_suspended == 0) { + bcm43xx_write32(dev, BCM43xx_MMIO_STATUS_BITFIELD, + bcm43xx_read32(dev, BCM43xx_MMIO_STATUS_BITFIELD) + | BCM43xx_SBF_MAC_ENABLED); + bcm43xx_write32(dev, BCM43xx_MMIO_GEN_IRQ_REASON, + BCM43xx_IRQ_MAC_SUSPENDED); + bcm43xx_read32(dev, BCM43xx_MMIO_STATUS_BITFIELD); /* dummy read */ + bcm43xx_read32(dev, BCM43xx_MMIO_GEN_IRQ_REASON); /* dummy read */ + bcm43xx_power_saving_ctl_bits(dev, -1, -1); + } +} + +/* http://bcm-specs.sipsolutions.net/SuspendMAC */ +void bcm43xx_mac_suspend(struct bcm43xx_wldev *dev) +{ + int i; + u32 tmp; + + assert(dev->mac_suspended >= 0); + if (dev->mac_suspended == 0) { + bcm43xx_power_saving_ctl_bits(dev, -1, 1); + bcm43xx_write32(dev, BCM43xx_MMIO_STATUS_BITFIELD, + bcm43xx_read32(dev, BCM43xx_MMIO_STATUS_BITFIELD) + & ~BCM43xx_SBF_MAC_ENABLED); + bcm43xx_read32(dev, BCM43xx_MMIO_GEN_IRQ_REASON); /* dummy read */ + for (i = 10000; i; i--) { + tmp = bcm43xx_read32(dev, BCM43xx_MMIO_GEN_IRQ_REASON); + if (tmp & BCM43xx_IRQ_MAC_SUSPENDED) + goto out; + udelay(1); + } + printkl(KERN_ERR PFX "MAC suspend failed\n"); + } +out: + dev->mac_suspended++; +} + +static void bcm43xx_adjust_opmode(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_wl *wl = dev->wl; + u32 ctl; + u16 cfp_pretbtt; + + ctl = bcm43xx_read32(dev, BCM43xx_MMIO_MACCTL); + /* Reset status to STA infrastructure mode. */ + ctl &= ~BCM43xx_MACCTL_AP; + ctl &= ~BCM43xx_MACCTL_KEEP_CTL; + ctl &= ~BCM43xx_MACCTL_KEEP_BADPLCP; + ctl &= ~BCM43xx_MACCTL_KEEP_BAD; + ctl &= ~BCM43xx_MACCTL_PROMISC; + ctl |= BCM43xx_MACCTL_INFRA; + + if (wl->operating) { + switch (wl->if_type) { + case IEEE80211_IF_TYPE_AP: + ctl |= BCM43xx_MACCTL_AP; + break; + case IEEE80211_IF_TYPE_IBSS: + ctl &= ~BCM43xx_MACCTL_INFRA; + break; + case IEEE80211_IF_TYPE_STA: + case IEEE80211_IF_TYPE_MNTR: + case IEEE80211_IF_TYPE_WDS: + break; + default: + assert(0); + } + } + if (wl->monitor) { + ctl |= BCM43xx_MACCTL_PROMISC; + ctl |= BCM43xx_MACCTL_KEEP_CTL; + if (modparam_mon_keep_bad) + ctl |= BCM43xx_MACCTL_KEEP_BAD; + if (modparam_mon_keep_badplcp) + ctl |= BCM43xx_MACCTL_KEEP_BADPLCP; + } + if (wl->promisc) + ctl |= BCM43xx_MACCTL_PROMISC; + +/* FIXME: Always enable promisc mode, until we get the MAC filters working correctly. */ +ctl |= BCM43xx_MACCTL_PROMISC; + + bcm43xx_write32(dev, BCM43xx_MMIO_MACCTL, ctl); + + cfp_pretbtt = 2; + if ((ctl & BCM43xx_MACCTL_INFRA) && + !(ctl & BCM43xx_MACCTL_AP)) { + if (dev->dev->bus->chip_id == 0x4306 && + dev->dev->bus->chip_rev == 3) + cfp_pretbtt = 100; + else + cfp_pretbtt = 50; + } + bcm43xx_write16(dev, 0x612, cfp_pretbtt); +} + +static void bcm43xx_rate_memory_write(struct bcm43xx_wldev *dev, + u16 rate, + int is_ofdm) +{ + u16 offset; + + if (is_ofdm) { + offset = 0x480; + offset += (bcm43xx_plcp_get_ratecode_ofdm(rate) & 0x000F) * 2; + } else { + offset = 0x4C0; + offset += (bcm43xx_plcp_get_ratecode_cck(rate) & 0x000F) * 2; + } + bcm43xx_shm_write16(dev, BCM43xx_SHM_SHARED, offset + 0x20, + bcm43xx_shm_read16(dev, BCM43xx_SHM_SHARED, offset)); +} + +static void bcm43xx_rate_memory_init(struct bcm43xx_wldev *dev) +{ + switch (dev->phy.type) { + case BCM43xx_PHYTYPE_A: + case BCM43xx_PHYTYPE_G: + bcm43xx_rate_memory_write(dev, BCM43xx_OFDM_RATE_6MB, 1); + bcm43xx_rate_memory_write(dev, BCM43xx_OFDM_RATE_12MB, 1); + bcm43xx_rate_memory_write(dev, BCM43xx_OFDM_RATE_18MB, 1); + bcm43xx_rate_memory_write(dev, BCM43xx_OFDM_RATE_24MB, 1); + bcm43xx_rate_memory_write(dev, BCM43xx_OFDM_RATE_36MB, 1); + bcm43xx_rate_memory_write(dev, BCM43xx_OFDM_RATE_48MB, 1); + bcm43xx_rate_memory_write(dev, BCM43xx_OFDM_RATE_54MB, 1); + case BCM43xx_PHYTYPE_B: + bcm43xx_rate_memory_write(dev, BCM43xx_CCK_RATE_1MB, 0); + bcm43xx_rate_memory_write(dev, BCM43xx_CCK_RATE_2MB, 0); + bcm43xx_rate_memory_write(dev, BCM43xx_CCK_RATE_5MB, 0); + bcm43xx_rate_memory_write(dev, BCM43xx_CCK_RATE_11MB, 0); + break; + default: + assert(0); + } +} + +/* Set the TX-Antenna for management frames sent by firmware. */ +static void bcm43xx_mgmtframe_txantenna(struct bcm43xx_wldev *dev, + int antenna) +{ + u16 ant = 0; + u16 tmp; + + switch (antenna) { + case BCM43xx_ANTENNA0: + ant |= BCM43xx_TX4_PHY_ANT0; + break; + case BCM43xx_ANTENNA1: + ant |= BCM43xx_TX4_PHY_ANT1; + break; + case BCM43xx_ANTENNA_AUTO: + ant |= BCM43xx_TX4_PHY_ANTLAST; + break; + default: + assert(0); + } + + /* FIXME We also need to set the other flags of the PHY control field somewhere. */ + + /* For Beacons */ + tmp = bcm43xx_shm_read16(dev, BCM43xx_SHM_SHARED, + BCM43xx_SHM_SH_BEACPHYCTL); + tmp = (tmp & ~BCM43xx_TX4_PHY_ANT) | ant; + bcm43xx_shm_write16(dev, BCM43xx_SHM_SHARED, + BCM43xx_SHM_SH_BEACPHYCTL, tmp); + /* For ACK/CTS */ + tmp = bcm43xx_shm_read16(dev, BCM43xx_SHM_SHARED, + BCM43xx_SHM_SH_ACKCTSPHYCTL); + tmp = (tmp & ~BCM43xx_TX4_PHY_ANT) | ant; + bcm43xx_shm_write16(dev, BCM43xx_SHM_SHARED, + BCM43xx_SHM_SH_ACKCTSPHYCTL, tmp); + /* For Probe Resposes */ + tmp = bcm43xx_shm_read16(dev, BCM43xx_SHM_SHARED, + BCM43xx_SHM_SH_PRPHYCTL); + tmp = (tmp & ~BCM43xx_TX4_PHY_ANT) | ant; + bcm43xx_shm_write16(dev, BCM43xx_SHM_SHARED, + BCM43xx_SHM_SH_PRPHYCTL, tmp); +} + +/* This is the opposite of bcm43xx_chip_init() */ +static void bcm43xx_chip_exit(struct bcm43xx_wldev *dev) +{ + bcm43xx_radio_turn_off(dev); + if (!modparam_noleds) + bcm43xx_leds_exit(dev); + bcm43xx_gpio_cleanup(dev); + /* firmware is released later */ +} + +/* Initialize the chip + * http://bcm-specs.sipsolutions.net/ChipInit + */ +static int bcm43xx_chip_init(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + int err, tmp; + u32 value32; + u16 value16; + + bcm43xx_write32(dev, BCM43xx_MMIO_STATUS_BITFIELD, + BCM43xx_SBF_CORE_READY + | BCM43xx_SBF_400); + + err = bcm43xx_request_firmware(dev); + if (err) + goto out; + err = bcm43xx_upload_microcode(dev); + if (err) + goto out; /* firmware is released later */ + + err = bcm43xx_gpio_init(dev); + if (err) + goto out; /* firmware is released later */ + err = bcm43xx_upload_initvals(dev); + if (err) + goto err_gpio_cleanup; + bcm43xx_radio_turn_on(dev); + dev->radio_hw_enable = bcm43xx_is_hw_radio_enabled(dev); + dprintk(KERN_INFO PFX "Radio %s by hardware\n", + (dev->radio_hw_enable == 0) ? "disabled" : "enabled"); + + bcm43xx_write16(dev, 0x03E6, 0x0000); + err = bcm43xx_phy_init(dev); + if (err) + goto err_radio_off; + + /* Select initial Interference Mitigation. */ + tmp = phy->interfmode; + phy->interfmode = BCM43xx_INTERFMODE_NONE; + bcm43xx_radio_set_interference_mitigation(dev, tmp); + + bcm43xx_set_rx_antenna(dev, BCM43xx_ANTENNA_DEFAULT); + bcm43xx_mgmtframe_txantenna(dev, BCM43xx_ANTENNA_DEFAULT); + + if (phy->type == BCM43xx_PHYTYPE_B) { + value16 = bcm43xx_read16(dev, 0x005E); + value16 |= 0x0004; + bcm43xx_write16(dev, 0x005E, value16); + } + bcm43xx_write32(dev, 0x0100, 0x01000000); + if (dev->dev->id.revision < 5) + bcm43xx_write32(dev, 0x010C, 0x01000000); + + value32 = bcm43xx_read32(dev, BCM43xx_MMIO_STATUS_BITFIELD); + value32 &= ~ BCM43xx_SBF_MODE_NOTADHOC; + bcm43xx_write32(dev, BCM43xx_MMIO_STATUS_BITFIELD, value32); + value32 = bcm43xx_read32(dev, BCM43xx_MMIO_STATUS_BITFIELD); + value32 |= BCM43xx_SBF_MODE_NOTADHOC; + bcm43xx_write32(dev, BCM43xx_MMIO_STATUS_BITFIELD, value32); + + value32 = bcm43xx_read32(dev, BCM43xx_MMIO_STATUS_BITFIELD); + value32 |= 0x100000; + bcm43xx_write32(dev, BCM43xx_MMIO_STATUS_BITFIELD, value32); + + if (bcm43xx_using_pio(dev)) { + bcm43xx_write32(dev, 0x0210, 0x00000100); + bcm43xx_write32(dev, 0x0230, 0x00000100); + bcm43xx_write32(dev, 0x0250, 0x00000100); + bcm43xx_write32(dev, 0x0270, 0x00000100); + bcm43xx_shm_write16(dev, BCM43xx_SHM_SHARED, 0x0034, 0x0000); + } + + /* Probe Response Timeout value */ + /* FIXME: Default to 0, has to be set by ioctl probably... :-/ */ + bcm43xx_shm_write16(dev, BCM43xx_SHM_SHARED, 0x0074, 0x0000); + + /* Initially set the wireless operation mode. */ + bcm43xx_adjust_opmode(dev); + + if (dev->dev->id.revision < 3) { + bcm43xx_write16(dev, 0x060E, 0x0000); + bcm43xx_write16(dev, 0x0610, 0x8000); + bcm43xx_write16(dev, 0x0604, 0x0000); + bcm43xx_write16(dev, 0x0606, 0x0200); + } else { + bcm43xx_write32(dev, 0x0188, 0x80000000); + bcm43xx_write32(dev, 0x018C, 0x02000000); + } + bcm43xx_write32(dev, BCM43xx_MMIO_GEN_IRQ_REASON, 0x00004000); + bcm43xx_write32(dev, BCM43xx_MMIO_DMA0_IRQ_MASK, 0x0001DC00); + bcm43xx_write32(dev, BCM43xx_MMIO_DMA1_IRQ_MASK, 0x0000DC00); + bcm43xx_write32(dev, BCM43xx_MMIO_DMA2_IRQ_MASK, 0x0000DC00); + bcm43xx_write32(dev, BCM43xx_MMIO_DMA3_IRQ_MASK, 0x0001DC00); + bcm43xx_write32(dev, BCM43xx_MMIO_DMA4_IRQ_MASK, 0x0000DC00); + bcm43xx_write32(dev, BCM43xx_MMIO_DMA5_IRQ_MASK, 0x0000DC00); + + value32 = ssb_read32(dev->dev, SSB_TMSLOW); + value32 |= 0x00100000; + ssb_write32(dev->dev, SSB_TMSLOW, value32); + + bcm43xx_write16(dev, BCM43xx_MMIO_POWERUP_DELAY, + dev->dev->bus->chipco.fast_pwrup_delay); + + assert(err == 0); + dprintk(KERN_INFO PFX "Chip initialized\n"); +out: + return err; + +err_radio_off: + bcm43xx_radio_turn_off(dev); +err_gpio_cleanup: + bcm43xx_gpio_cleanup(dev); + goto out; +} + +static void bcm43xx_periodic_every120sec(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + + if (phy->type != BCM43xx_PHYTYPE_G || phy->rev < 2) + return; + + bcm43xx_mac_suspend(dev); + bcm43xx_lo_g_measure(dev); + bcm43xx_mac_enable(dev); +} + +static void bcm43xx_periodic_every60sec(struct bcm43xx_wldev *dev) +{ + bcm43xx_loctl_mark_all_unused(dev); + if (dev->dev->bus->sprom.r1.boardflags_lo & BCM43xx_BFL_RSSI) { + bcm43xx_mac_suspend(dev); + bcm43xx_calc_nrssi_slope(dev); + bcm43xx_mac_enable(dev); + } +} + +static void bcm43xx_periodic_every30sec(struct bcm43xx_wldev *dev) +{ + /* Update device statistics. */ + bcm43xx_calculate_link_quality(dev); +} + +static void bcm43xx_periodic_every15sec(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + + if (phy->type == BCM43xx_PHYTYPE_G) { + //TODO: update_aci_moving_average + if (phy->aci_enable && phy->aci_wlan_automatic) { + bcm43xx_mac_suspend(dev); + if (!phy->aci_enable && 1 /*TODO: not scanning? */) { + if (0 /*TODO: bunch of conditions*/) { + bcm43xx_radio_set_interference_mitigation(dev, + BCM43xx_INTERFMODE_MANUALWLAN); + } + } else if (1/*TODO*/) { + /* + if ((aci_average > 1000) && !(bcm43xx_radio_aci_scan(dev))) { + bcm43xx_radio_set_interference_mitigation(dev, + BCM43xx_INTERFMODE_NONE); + } + */ + } + bcm43xx_mac_enable(dev); + } else if (phy->interfmode == BCM43xx_INTERFMODE_NONWLAN && + phy->rev == 1) { + //TODO: implement rev1 workaround + } + } + bcm43xx_phy_xmitpower(dev); //FIXME: unless scanning? + //TODO for APHY (temperature?) +} + +static void bcm43xx_periodic_every1sec(struct bcm43xx_wldev *dev) +{ + int radio_hw_enable; + + /* check if radio hardware enabled status changed */ + radio_hw_enable = bcm43xx_is_hw_radio_enabled(dev); + if (unlikely(dev->radio_hw_enable != radio_hw_enable)) { + dev->radio_hw_enable = radio_hw_enable; + dprintk(KERN_INFO PFX "Radio hardware status changed to %s\n", + (radio_hw_enable == 0) ? "disabled" : "enabled"); + bcm43xx_leds_update(dev, 0); + } +} + +static void do_periodic_work(struct bcm43xx_wldev *dev) +{ + unsigned int state; + + state = dev->periodic_state; + if (state % 120 == 0) + bcm43xx_periodic_every120sec(dev); + if (state % 60 == 0) + bcm43xx_periodic_every60sec(dev); + if (state % 30 == 0) + bcm43xx_periodic_every30sec(dev); + if (state % 15 == 0) + bcm43xx_periodic_every15sec(dev); + bcm43xx_periodic_every1sec(dev); + + dev->periodic_state = state + 1; + + schedule_delayed_work(&dev->periodic_work, HZ); +} + +/* Estimate a "Badness" value based on the periodic work + * state-machine state. "Badness" is worse (bigger), if the + * periodic work will take longer. + */ +static int estimate_periodic_work_badness(unsigned int state) +{ + int badness = 0; + + if (state % 120 == 0) /* every 120 sec */ + badness += 10; + if (state % 60 == 0) /* every 60 sec */ + badness += 5; + if (state % 30 == 0) /* every 30 sec */ + badness += 1; + if (state % 15 == 0) /* every 15 sec */ + badness += 1; + +#define BADNESS_LIMIT 4 + return badness; +} + +static void bcm43xx_periodic_work_handler(struct work_struct *work) +{ + struct bcm43xx_wldev *dev = + container_of(work, struct bcm43xx_wldev, periodic_work.work); + unsigned long flags; + u32 savedirqs = 0; + int badness; + + mutex_lock(&dev->wl->mutex); + badness = estimate_periodic_work_badness(dev->periodic_state); + if (badness > BADNESS_LIMIT) { + /* Periodic work will take a long time, so we want it to + * be preemtible. + */ + ieee80211_stop_queues(dev->wl->hw); + spin_lock_irqsave(&dev->wl->irq_lock, flags); + bcm43xx_mac_suspend(dev); + if (bcm43xx_using_pio(dev)) + bcm43xx_pio_freeze_txqueues(dev); + savedirqs = bcm43xx_interrupt_disable(dev, BCM43xx_IRQ_ALL); + spin_unlock_irqrestore(&dev->wl->irq_lock, flags); + bcm43xx_synchronize_irq(dev); + } else { + /* Periodic work should take short time, so we want low + * locking overhead. + */ + spin_lock_irqsave(&dev->wl->irq_lock, flags); + } + + do_periodic_work(dev); + + if (badness > BADNESS_LIMIT) { + spin_lock_irqsave(&dev->wl->irq_lock, flags); + bcm43xx_interrupt_enable(dev, savedirqs); + if (bcm43xx_using_pio(dev)) + bcm43xx_pio_thaw_txqueues(dev); + bcm43xx_mac_enable(dev); + ieee80211_start_queues(dev->wl->hw); + } + mmiowb(); + spin_unlock_irqrestore(&dev->wl->irq_lock, flags); + mutex_unlock(&dev->wl->mutex); +} + +static void bcm43xx_periodic_tasks_delete(struct bcm43xx_wldev *dev) +{ + cancel_rearming_delayed_work(&dev->periodic_work); +} + +static void bcm43xx_periodic_tasks_setup(struct bcm43xx_wldev *dev) +{ + struct delayed_work *work = &dev->periodic_work; + + assert(bcm43xx_status(dev) == BCM43xx_STAT_INITIALIZED); + dev->periodic_state = 0; + INIT_DELAYED_WORK(work, bcm43xx_periodic_work_handler); + schedule_delayed_work(work, 0); +} + +/* Validate access to the chip (SHM) */ +static int bcm43xx_validate_chipaccess(struct bcm43xx_wldev *dev) +{ + u32 value; + u32 shm_backup; + + shm_backup = bcm43xx_shm_read32(dev, BCM43xx_SHM_SHARED, 0); + bcm43xx_shm_write32(dev, BCM43xx_SHM_SHARED, 0, 0xAA5555AA); + if (bcm43xx_shm_read32(dev, BCM43xx_SHM_SHARED, 0) != 0xAA5555AA) + goto error; + bcm43xx_shm_write32(dev, BCM43xx_SHM_SHARED, 0, 0x55AAAA55); + if (bcm43xx_shm_read32(dev, BCM43xx_SHM_SHARED, 0) != 0x55AAAA55) + goto error; + bcm43xx_shm_write32(dev, BCM43xx_SHM_SHARED, 0, shm_backup); + + value = bcm43xx_read32(dev, BCM43xx_MMIO_MACCTL); + if ((value | BCM43xx_MACCTL_GMODE) != + (BCM43xx_MACCTL_GMODE | BCM43xx_MACCTL_IHR_ENABLED)) + goto error; + + value = bcm43xx_read32(dev, BCM43xx_MMIO_GEN_IRQ_REASON); + if (value) + goto error; + + return 0; +error: + printk(KERN_ERR PFX "Failed to validate the chipaccess\n"); + return -ENODEV; +} + +static void bcm43xx_security_init(struct bcm43xx_wldev *dev) +{ + dev->max_nr_keys = (dev->dev->id.revision >= 5) ? 58 : 20; + assert(dev->max_nr_keys <= ARRAY_SIZE(dev->key)); + dev->ktp = bcm43xx_shm_read16(dev, BCM43xx_SHM_SHARED, + BCM43xx_SHM_SH_KTP); + /* KTP is a word address, but we address SHM bytewise. + * So multiply by two. + */ + dev->ktp *= 2; + if (dev->dev->id.revision >= 5) { + /* Number of RCMTA address slots */ + bcm43xx_write16(dev, BCM43xx_MMIO_RCMTA_COUNT, + dev->max_nr_keys - 8); + } + bcm43xx_clear_keys(dev); +} + +static int bcm43xx_rng_read(struct hwrng *rng, u32 *data) +{ + struct bcm43xx_wl *wl = (struct bcm43xx_wl *)rng->priv; + unsigned long flags; + + /* Don't take wl->mutex here, as it could deadlock with + * hwrng internal locking. It's not needed to take + * wl->mutex here, anyway. */ + + spin_lock_irqsave(&wl->irq_lock, flags); + *data = bcm43xx_read16(wl->current_dev, BCM43xx_MMIO_RNG); + spin_unlock_irqrestore(&wl->irq_lock, flags); + + return (sizeof(u16)); +} + +static void bcm43xx_rng_exit(struct bcm43xx_wl *wl) +{ + if (wl->rng_initialized) + hwrng_unregister(&wl->rng); +} + +static int bcm43xx_rng_init(struct bcm43xx_wl *wl) +{ + int err; + + snprintf(wl->rng_name, ARRAY_SIZE(wl->rng_name), + "%s_%s", KBUILD_MODNAME, wiphy_name(wl->hw->wiphy)); + wl->rng.name = wl->rng_name; + wl->rng.data_read = bcm43xx_rng_read; + wl->rng.priv = (unsigned long)wl; + wl->rng_initialized = 1; + err = hwrng_register(&wl->rng); + if (err) { + wl->rng_initialized = 0; + printk(KERN_ERR PFX "Failed to register the random " + "number generator (%d)\n", err); + } + + return err; +} + +static int bcm43xx_tx(struct ieee80211_hw *hw, + struct sk_buff *skb, + struct ieee80211_tx_control *ctl) +{ + struct bcm43xx_wl *wl = hw_to_bcm43xx_wl(hw); + struct bcm43xx_wldev *dev = wl->current_dev; + int err = -ENODEV; + unsigned long flags; + + if (unlikely(!dev)) + goto out; + spin_lock_irqsave(&wl->irq_lock, flags); + if (likely(bcm43xx_status(dev) == BCM43xx_STAT_INITIALIZED)) { + if (bcm43xx_using_pio(dev)) + err = bcm43xx_pio_tx(dev, skb, ctl); + else + err = bcm43xx_dma_tx(dev, skb, ctl); + } + spin_unlock_irqrestore(&wl->irq_lock, flags); +out: + if (unlikely(err)) + return NETDEV_TX_BUSY; + return NETDEV_TX_OK; +} + +static int bcm43xx_conf_tx(struct ieee80211_hw *hw, + int queue, + const struct ieee80211_tx_queue_params *params) +{ + return 0; +} + +static int bcm43xx_get_tx_stats(struct ieee80211_hw *hw, + struct ieee80211_tx_queue_stats *stats) +{ + struct bcm43xx_wl *wl = hw_to_bcm43xx_wl(hw); + struct bcm43xx_wldev *dev = wl->current_dev; + unsigned long flags; + int err = -ENODEV; + + if (!dev) + goto out; + spin_lock_irqsave(&wl->irq_lock, flags); + if (likely(bcm43xx_status(dev) == BCM43xx_STAT_INITIALIZED)) { + if (bcm43xx_using_pio(dev)) + bcm43xx_pio_get_tx_stats(dev, stats); + else + bcm43xx_dma_get_tx_stats(dev, stats); + err = 0; + } + spin_unlock_irqrestore(&wl->irq_lock, flags); +out: + return err; +} + +static int bcm43xx_get_stats(struct ieee80211_hw *hw, + struct ieee80211_low_level_stats *stats) +{ + struct bcm43xx_wl *wl = hw_to_bcm43xx_wl(hw); + unsigned long flags; + + spin_lock_irqsave(&wl->irq_lock, flags); + memcpy(stats, &wl->ieee_stats, sizeof(*stats)); + spin_unlock_irqrestore(&wl->irq_lock, flags); + + return 0; +} + +static int bcm43xx_dev_reset(struct ieee80211_hw *hw) +{ + struct bcm43xx_wl *wl = hw_to_bcm43xx_wl(hw); + struct bcm43xx_wldev *dev = wl->current_dev; + unsigned long flags; + + if (!dev) + return -ENODEV; + spin_lock_irqsave(&wl->irq_lock, flags); + bcm43xx_controller_restart(dev, "Reset by ieee80211 subsystem"); + spin_unlock_irqrestore(&wl->irq_lock, flags); + + return 0; +} + +static const char * phymode_to_string(unsigned int phymode) +{ + switch (phymode) { + case BCM43xx_PHYMODE_A: + return "A"; + case BCM43xx_PHYMODE_B: + return "B"; + case BCM43xx_PHYMODE_G: + return "G"; + default: + assert(0); + } + return ""; +} + +static int find_wldev_for_phymode(struct bcm43xx_wl *wl, + unsigned int phymode, + struct bcm43xx_wldev **dev, + int *gmode) +{ + struct bcm43xx_wldev *d; + + list_for_each_entry(d, &wl->devlist, list) { + if (d->phy.possible_phymodes & phymode) { + /* Ok, this device supports the PHY-mode. + * Now figure out how the gmode bit has to be + * set to support it. */ + if (phymode == BCM43xx_PHYMODE_A) + *gmode = 0; + else + *gmode = 1; + *dev = d; + + return 0; + } + } + + return -ESRCH; +} + +static void bcm43xx_put_phy_into_reset(struct bcm43xx_wldev *dev) +{ + struct ssb_device *sdev = dev->dev; + u32 tmslow; + + tmslow = ssb_read32(sdev, SSB_TMSLOW); + tmslow &= ~BCM43xx_TMSLOW_GMODE; + tmslow |= BCM43xx_TMSLOW_PHYRESET; + tmslow |= SSB_TMSLOW_FGC; + ssb_write32(sdev, SSB_TMSLOW, tmslow); + msleep(1); + + tmslow = ssb_read32(sdev, SSB_TMSLOW); + tmslow &= ~SSB_TMSLOW_FGC; + tmslow |= BCM43xx_TMSLOW_PHYRESET; + ssb_write32(sdev, SSB_TMSLOW, tmslow); + msleep(1); +} + +static int bcm43xx_switch_phymode(struct bcm43xx_wl *wl, + unsigned int new_mode) +{ + struct bcm43xx_wldev *up_dev; + struct bcm43xx_wldev *down_dev; + int err; + int gmode = -1; + int old_was_started = 0; + int old_was_inited = 0; + + err = find_wldev_for_phymode(wl, new_mode, &up_dev, &gmode); + if (err) { + printk(KERN_INFO PFX "Could not find a device for %s-PHY mode\n", + phymode_to_string(new_mode)); + return err; + } + assert(gmode == 0 || gmode == 1); + if ((up_dev == wl->current_dev) && + (wl->current_dev->phy.gmode == gmode)) { + /* This device is already running. */ + return 0; + } + dprintk(KERN_INFO PFX "Reconfiguring PHYmode to %s-PHY\n", + phymode_to_string(new_mode)); + down_dev = wl->current_dev; + + /* Shutdown the currently running core. */ + if (down_dev->started) { + old_was_started = 1; + bcm43xx_wireless_core_stop(down_dev); + } + if (bcm43xx_status(down_dev) == BCM43xx_STAT_INITIALIZED) { + old_was_inited = 1; + bcm43xx_wireless_core_exit(down_dev); + } + + if (down_dev != up_dev) { + /* We switch to a different core, so we put PHY into + * RESET on the old core. */ + bcm43xx_put_phy_into_reset(down_dev); + } + + /* Now start the new core. */ + up_dev->phy.gmode = gmode; + if (old_was_inited) { + err = bcm43xx_wireless_core_init(up_dev); + if (err) { + printk(KERN_INFO PFX "Fatal: Could not initialize device for " + "new selected %s-PHY mode\n", + phymode_to_string(new_mode)); + return err; + } + } + if (old_was_started) { + assert(old_was_inited); + err = bcm43xx_wireless_core_start(up_dev); + if (err) { + printk(KERN_INFO PFX "Fatal: Coult not start device for " + "new selected %s-PHY mode\n", + phymode_to_string(new_mode)); + bcm43xx_wireless_core_exit(up_dev); + return err; + } + } + + wl->current_dev = up_dev; + + return 0; +} + +static int bcm43xx_antenna_from_ieee80211(u8 antenna) +{ + switch (antenna) { + case 0: /* default/diversity */ + return BCM43xx_ANTENNA_DEFAULT; + case 1: /* Antenna 0 */ + return BCM43xx_ANTENNA0; + case 2: /* Antenna 1 */ + return BCM43xx_ANTENNA1; + default: + return BCM43xx_ANTENNA_DEFAULT; + } +} + +static int bcm43xx_dev_config(struct ieee80211_hw *hw, + struct ieee80211_conf *conf) +{ + struct bcm43xx_wl *wl = hw_to_bcm43xx_wl(hw); + struct bcm43xx_wldev *dev; + struct bcm43xx_phy *phy; + unsigned long flags; + unsigned int new_phymode = 0xFFFF; + int antenna_tx; + int antenna_rx; + int err = 0; + + antenna_tx = bcm43xx_antenna_from_ieee80211(conf->antenna_sel_tx); + antenna_rx = bcm43xx_antenna_from_ieee80211(conf->antenna_sel_rx); + + mutex_lock(&wl->mutex); + + /* Switch the PHY mode (if necessary). */ + switch (conf->phymode) { + case MODE_IEEE80211A: + new_phymode = BCM43xx_PHYMODE_A; + break; + case MODE_IEEE80211B: + new_phymode = BCM43xx_PHYMODE_B; + break; + case MODE_IEEE80211G: + new_phymode = BCM43xx_PHYMODE_G; + break; + default: + assert(0); + } + err = bcm43xx_switch_phymode(wl, new_phymode); + if (err) + goto out_unlock_mutex; + dev = wl->current_dev; + phy = &dev->phy; + + spin_lock_irqsave(&wl->irq_lock, flags); + if (bcm43xx_status(dev) != BCM43xx_STAT_INITIALIZED) + goto out_unlock; + + /* Switch to the requested channel. */ + if (conf->channel_val != phy->channel) + bcm43xx_radio_selectchannel(dev, conf->channel_val, 0); + + /* Enable/Disable ShortSlot timing. */ + if (!!(conf->flags & IEEE80211_CONF_SHORT_SLOT_TIME) != dev->short_slot) { + assert(phy->type == BCM43xx_PHYTYPE_G); + if (conf->flags & IEEE80211_CONF_SHORT_SLOT_TIME) + bcm43xx_short_slot_timing_enable(dev); + else + bcm43xx_short_slot_timing_disable(dev); + } + + /* Adjust the desired TX power level. */ + if (conf->power_level != 0) { + if (conf->power_level != phy->power_level) { + phy->power_level = conf->power_level; + bcm43xx_phy_xmitpower(dev); + } + } + + /* Hide/Show the SSID (AP mode only). */ + if (conf->flags & IEEE80211_CONF_SSID_HIDDEN) { + bcm43xx_write32(dev, BCM43xx_MMIO_STATUS_BITFIELD, + bcm43xx_read32(dev, BCM43xx_MMIO_STATUS_BITFIELD) + | BCM43xx_SBF_NO_SSID_BCAST); + } else { + bcm43xx_write32(dev, BCM43xx_MMIO_STATUS_BITFIELD, + bcm43xx_read32(dev, BCM43xx_MMIO_STATUS_BITFIELD) + & ~BCM43xx_SBF_NO_SSID_BCAST); + } + + /* Antennas for RX and management frame TX. */ + bcm43xx_mgmtframe_txantenna(dev, antenna_tx); + bcm43xx_set_rx_antenna(dev, antenna_rx); + + /* Update templates for AP mode. */ + if (bcm43xx_is_mode(wl, IEEE80211_IF_TYPE_AP)) + bcm43xx_set_beacon_int(dev, conf->beacon_int); + +out_unlock: + spin_unlock_irqrestore(&wl->irq_lock, flags); +out_unlock_mutex: + mutex_unlock(&wl->mutex); + + return err; +} + +static int bcm43xx_dev_set_key(struct ieee80211_hw *hw, + set_key_cmd cmd, + u8 *addr, + struct ieee80211_key_conf *key, + int aid) +{ + struct bcm43xx_wl *wl = hw_to_bcm43xx_wl(hw); + struct bcm43xx_wldev *dev = wl->current_dev; + unsigned long flags; + u8 algorithm; + u8 index; + int err = -EINVAL; + + if (!dev) + return -ENODEV; + switch (key->alg) { + case ALG_NONE: + case ALG_NULL: + algorithm = BCM43xx_SEC_ALGO_NONE; + break; + case ALG_WEP: + if (key->keylen == 5) + algorithm = BCM43xx_SEC_ALGO_WEP40; + else + algorithm = BCM43xx_SEC_ALGO_WEP104; + break; + case ALG_TKIP: + algorithm = BCM43xx_SEC_ALGO_TKIP; + break; + case ALG_CCMP: + algorithm = BCM43xx_SEC_ALGO_AES; + break; + default: + assert(0); + goto out; + } + + index = (u8)(key->keyidx); + if (index > 3) + goto out; + + mutex_lock(&wl->mutex); + spin_lock_irqsave(&wl->irq_lock, flags); + + if (bcm43xx_status(dev) != BCM43xx_STAT_INITIALIZED) { + err = -ENODEV; + goto out_unlock; + } + + switch (cmd) { + case SET_KEY: + key->flags &= ~IEEE80211_KEY_FORCE_SW_ENCRYPT; + + if (algorithm == BCM43xx_SEC_ALGO_TKIP) { + /* FIXME: No TKIP hardware encryption for now. */ + key->flags |= IEEE80211_KEY_FORCE_SW_ENCRYPT; + } + + if (is_broadcast_ether_addr(addr)) { + /* addr is FF:FF:FF:FF:FF:FF for default keys */ + err = bcm43xx_key_write(dev, index, algorithm, + key->key, key->keylen, + NULL, key); + } else { + err = bcm43xx_key_write(dev, -1, algorithm, + key->key, key->keylen, + addr, key); + } + if (err) { + key->flags |= IEEE80211_KEY_FORCE_SW_ENCRYPT; + goto out_unlock; + } + dev->key[key->hw_key_idx].enabled = 1; + + if (algorithm == BCM43xx_SEC_ALGO_WEP40 || + algorithm == BCM43xx_SEC_ALGO_WEP104) { + bcm43xx_hf_write(dev, + bcm43xx_hf_read(dev) | + BCM43xx_HF_USEDEFKEYS); + } else { + bcm43xx_hf_write(dev, + bcm43xx_hf_read(dev) & + ~BCM43xx_HF_USEDEFKEYS); + } + break; + case DISABLE_KEY: { + static const u8 zero[BCM43xx_SEC_KEYSIZE] = { 0 }; + + algorithm = BCM43xx_SEC_ALGO_NONE; + if (is_broadcast_ether_addr(addr)) { + err = bcm43xx_key_write(dev, index, algorithm, + zero, BCM43xx_SEC_KEYSIZE, + NULL, key); + } else { + err = bcm43xx_key_write(dev, -1, algorithm, + zero, BCM43xx_SEC_KEYSIZE, + addr, key); + } + dev->key[key->hw_key_idx].enabled = 0; + break; + } + case REMOVE_ALL_KEYS: + bcm43xx_clear_keys(dev); + err = 0; + break; + default: + assert(0); + } +out_unlock: + spin_unlock_irqrestore(&wl->irq_lock, flags); + mutex_unlock(&wl->mutex); +out: + if (!err) { + dprintk(KERN_DEBUG PFX "Using %s based encryption for keyidx: %d, " + "mac: " MAC_FMT "\n", + (key->flags & IEEE80211_KEY_FORCE_SW_ENCRYPT) ? + "software" : "hardware", + key->keyidx, MAC_ARG(addr)); + } + return err; +} + +static void bcm43xx_set_multicast_list(struct ieee80211_hw *hw, + unsigned short netflags, + int mc_count) +{ + struct bcm43xx_wl *wl = hw_to_bcm43xx_wl(hw); + struct bcm43xx_wldev *dev = wl->current_dev; + unsigned long flags; + + if (!dev) + return; + spin_lock_irqsave(&wl->irq_lock, flags); + if (wl->promisc != !!(netflags & IFF_PROMISC)) { + wl->promisc = !!(netflags & IFF_PROMISC); + if (bcm43xx_status(dev) == BCM43xx_STAT_INITIALIZED) + bcm43xx_adjust_opmode(dev); + } + spin_unlock_irqrestore(&wl->irq_lock, flags); +} + +static int bcm43xx_config_interface(struct ieee80211_hw *hw, + int if_id, + struct ieee80211_if_conf *conf) +{ + struct bcm43xx_wl *wl = hw_to_bcm43xx_wl(hw); + struct bcm43xx_wldev *dev = wl->current_dev; + unsigned long flags; + + if (!dev) + return -ENODEV; + mutex_lock(&wl->mutex); + spin_lock_irqsave(&wl->irq_lock, flags); + if (conf->type != IEEE80211_IF_TYPE_MNTR) { + assert(wl->if_id == if_id); + wl->bssid = conf->bssid; + if (bcm43xx_is_mode(wl, IEEE80211_IF_TYPE_AP)) { + assert(conf->type == IEEE80211_IF_TYPE_AP); + bcm43xx_set_ssid(dev, conf->ssid, conf->ssid_len); + if (conf->beacon) + bcm43xx_refresh_templates(dev, conf->beacon); + } + } + spin_unlock_irqrestore(&wl->irq_lock, flags); + mutex_unlock(&wl->mutex); + + return 0; +} + +/* Locking: wl->mutex */ +static void bcm43xx_wireless_core_stop(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_wl *wl = dev->wl; + unsigned long flags; + + if (!dev->started) + return; + + mutex_unlock(&wl->mutex); + bcm43xx_periodic_tasks_delete(dev); + flush_scheduled_work(); + mutex_lock(&wl->mutex); + + ieee80211_stop_queues(wl->hw); + + /* Disable and sync interrupts. */ + spin_lock_irqsave(&wl->irq_lock, flags); + dev->irq_savedstate = bcm43xx_interrupt_disable(dev, BCM43xx_IRQ_ALL); + bcm43xx_read32(dev, BCM43xx_MMIO_GEN_IRQ_MASK); /* flush */ + spin_unlock_irqrestore(&wl->irq_lock, flags); + bcm43xx_synchronize_irq(dev); + + bcm43xx_mac_suspend(dev); + free_irq(dev->dev->irq, dev); + dev->started = 0; + dprintk(KERN_INFO PFX "Wireless interface stopped\n"); +} + +/* Locking: wl->mutex */ +static int bcm43xx_wireless_core_start(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_wl *wl = dev->wl; + int err; + + assert(!dev->started); + + drain_txstatus_queue(dev); + err = request_irq(dev->dev->irq, bcm43xx_interrupt_handler, + IRQF_SHARED, KBUILD_MODNAME, dev); + if (err) { + printk(KERN_ERR PFX "Cannot request IRQ-%d\n", + dev->dev->irq); + goto out; + } + dev->started = 1; + bcm43xx_interrupt_enable(dev, dev->irq_savedstate); + bcm43xx_mac_enable(dev); + + ieee80211_start_queues(wl->hw); + bcm43xx_periodic_tasks_setup(dev); + dprintk(KERN_INFO PFX "Wireless interface started\n"); +out: + return err; +} + +/* Get PHY and RADIO versioning numbers */ +static int bcm43xx_phy_versioning(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + u32 tmp; + u8 analog_type; + u8 phy_type; + u8 phy_rev; + u16 radio_manuf; + u16 radio_ver; + u16 radio_rev; + int unsupported = 0; + + /* Get PHY versioning */ + tmp = bcm43xx_read16(dev, BCM43xx_MMIO_PHY_VER); + analog_type = (tmp & BCM43xx_PHYVER_ANALOG) >> BCM43xx_PHYVER_ANALOG_SHIFT; + phy_type = (tmp & BCM43xx_PHYVER_TYPE) >> BCM43xx_PHYVER_TYPE_SHIFT; + phy_rev = (tmp & BCM43xx_PHYVER_VERSION); + switch (phy_type) { + case BCM43xx_PHYTYPE_A: + if (phy_rev >= 4) + unsupported = 1; + break; + case BCM43xx_PHYTYPE_B: + if (phy_rev != 2 && phy_rev != 4 && phy_rev != 6 && phy_rev != 7) + unsupported = 1; + break; + case BCM43xx_PHYTYPE_G: + if (phy_rev > 8) + unsupported = 1; + break; + default: + unsupported = 1; + }; + if (unsupported) { + printk(KERN_ERR PFX "FOUND UNSUPPORTED PHY " + "(Analog %u, Type %u, Revision %u)\n", + analog_type, phy_type, phy_rev); + return -EOPNOTSUPP; + } + dprintk(KERN_INFO PFX "Found PHY: Analog %u, Type %u, Revision %u\n", + analog_type, phy_type, phy_rev); + + + /* Get RADIO versioning */ + if (dev->dev->bus->chip_id == 0x4317) { + if (dev->dev->bus->chip_rev == 0) + tmp = 0x3205017F; + else if (dev->dev->bus->chip_rev == 1) + tmp = 0x4205017F; + else + tmp = 0x5205017F; + } else { + bcm43xx_write16(dev, BCM43xx_MMIO_RADIO_CONTROL, + BCM43xx_RADIOCTL_ID); + tmp = bcm43xx_read16(dev, BCM43xx_MMIO_RADIO_DATA_HIGH); + tmp <<= 16; + bcm43xx_write16(dev, BCM43xx_MMIO_RADIO_CONTROL, + BCM43xx_RADIOCTL_ID); + tmp |= bcm43xx_read16(dev, BCM43xx_MMIO_RADIO_DATA_LOW); + } + radio_manuf = (tmp & 0x00000FFF); + radio_ver = (tmp & 0x0FFFF000) >> 12; + radio_rev = (tmp & 0xF0000000) >> 28; + switch (phy_type) { + case BCM43xx_PHYTYPE_A: + if (radio_ver != 0x2060) + unsupported = 1; + if (radio_rev != 1) + unsupported = 1; + if (radio_manuf != 0x17F) + unsupported = 1; + break; + case BCM43xx_PHYTYPE_B: + if ((radio_ver & 0xFFF0) != 0x2050) + unsupported = 1; + break; + case BCM43xx_PHYTYPE_G: + if (radio_ver != 0x2050) + unsupported = 1; + break; + default: + assert(0); + } + if (unsupported) { + printk(KERN_ERR PFX "FOUND UNSUPPORTED RADIO " + "(Manuf 0x%X, Version 0x%X, Revision %u)\n", + radio_manuf, radio_ver, radio_rev); + return -EOPNOTSUPP; + } + dprintk(KERN_INFO PFX "Found Radio: Manuf 0x%X, Version 0x%X, Revision %u\n", + radio_manuf, radio_ver, radio_rev); + + + phy->radio_manuf = radio_manuf; + phy->radio_ver = radio_ver; + phy->radio_rev = radio_rev; + + phy->analog = analog_type; + phy->type = phy_type; + phy->rev = phy_rev; + + return 0; +} + +static void setup_struct_phy_for_init(struct bcm43xx_wldev *dev, + struct bcm43xx_phy *phy) +{ + struct bcm43xx_txpower_lo_control *lo; + int i; + + memset(phy->minlowsig, 0xFF, sizeof(phy->minlowsig)); + memset(phy->minlowsigpos, 0, sizeof(phy->minlowsigpos)); + + /* Flags */ + phy->locked = 0; + + phy->aci_enable = 0; + phy->aci_wlan_automatic = 0; + phy->aci_hw_rssi = 0; + + lo = phy->lo_control; + if (lo) { + memset(lo, 0, sizeof(*(phy->lo_control))); + lo->rebuild = 1; + lo->tx_bias = 0xFF; + } + phy->max_lb_gain = 0; + phy->trsw_rx_gain = 0; + + /* Set default attenuation values. */ + phy->bbatt = bcm43xx_default_baseband_attenuation(dev); + phy->rfatt = bcm43xx_default_radio_attenuation(dev); + phy->txctl1 = bcm43xx_default_txctl1(dev); + phy->txpwr_offset = 0; + + /* NRSSI */ + phy->nrssislope = 0; + for (i = 0; i < ARRAY_SIZE(phy->nrssi); i++) + phy->nrssi[i] = -1000; + for (i = 0; i < ARRAY_SIZE(phy->nrssi_lt); i++) + phy->nrssi_lt[i] = i; + + phy->lofcal = 0xFFFF; + phy->initval = 0xFFFF; + + spin_lock_init(&phy->lock); + phy->interfmode = BCM43xx_INTERFMODE_NONE; + phy->channel = 0xFF; +} + +static void setup_struct_wldev_for_init(struct bcm43xx_wldev *dev) +{ + /* Flags */ + dev->reg124_set_0x4 = 0; + + /* Stats */ + memset(&dev->stats, 0, sizeof(dev->stats)); + + setup_struct_phy_for_init(dev, &dev->phy); + + /* IRQ related flags */ + dev->irq_reason = 0; + memset(dev->dma_reason, 0, sizeof(dev->dma_reason)); + dev->irq_savedstate = BCM43xx_IRQ_MASKTEMPLATE; + + dev->mac_suspended = 1; + + /* Noise calculation context */ + memset(&dev->noisecalc, 0, sizeof(dev->noisecalc)); +} + +static void bcm43xx_bluetooth_coext_enable(struct bcm43xx_wldev *dev) +{ + struct ssb_sprom *sprom = &dev->dev->bus->sprom; + u32 hf; + + if (!(sprom->r1.boardflags_lo & BCM43xx_BFL_BTCOEXIST)) + return; + if (dev->phy.type != BCM43xx_PHYTYPE_B && !dev->phy.gmode) + return; + + hf = bcm43xx_hf_read(dev); + if (sprom->r1.boardflags_lo & BCM43xx_BFL_BTCMOD) + hf |= BCM43xx_HF_BTCOEXALT; + else + hf |= BCM43xx_HF_BTCOEX; + bcm43xx_hf_write(dev, hf); + //TODO +} + +static void bcm43xx_bluetooth_coext_disable(struct bcm43xx_wldev *dev) +{//TODO +} + +static void bcm43xx_imcfglo_timeouts_workaround(struct bcm43xx_wldev *dev) +{ +#ifdef CONFIG_SSB_DRIVER_PCICORE + struct ssb_bus *bus = dev->dev->bus; + u32 tmp; + + if (bus->pcicore.dev && + bus->pcicore.dev->id.coreid == SSB_DEV_PCI && + bus->pcicore.dev->id.revision <= 5) { + /* IMCFGLO timeouts workaround. */ + tmp = ssb_read32(dev->dev, SSB_IMCFGLO); + tmp &= ~SSB_IMCFGLO_REQTO; + tmp &= ~SSB_IMCFGLO_SERTO; + switch (bus->bustype) { + case SSB_BUSTYPE_PCI: + case SSB_BUSTYPE_PCMCIA: + tmp |= 0x32; + break; + case SSB_BUSTYPE_SSB: + tmp |= 0x53; + break; + } + ssb_write32(dev->dev, SSB_IMCFGLO, tmp); + } +#endif /* CONFIG_SSB_DRIVER_PCICORE */ +} + +/* Shutdown a wireless core */ +static void bcm43xx_wireless_core_exit(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + + if (bcm43xx_status(dev) != BCM43xx_STAT_INITIALIZED) + return; + + bcm43xx_rng_exit(dev->wl); + bcm43xx_pio_free(dev); + bcm43xx_dma_free(dev); + bcm43xx_chip_exit(dev); + bcm43xx_radio_turn_off(dev); + bcm43xx_switch_analog(dev, 0); + if (phy->dyn_tssi_tbl) + kfree(phy->tssi2dbm); + kfree(phy->lo_control); + phy->lo_control = NULL; + ssb_device_disable(dev->dev, 0); + ssb_bus_may_powerdown(dev->dev->bus); + bcm43xx_set_status(dev, BCM43xx_STAT_UNINIT); +} + +/* Initialize a wireless core */ +static int bcm43xx_wireless_core_init(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_wl *wl = dev->wl; + struct ssb_bus *bus = dev->dev->bus; + struct ssb_sprom *sprom = &bus->sprom; + struct bcm43xx_phy *phy = &dev->phy; + int err; + u32 hf, tmp; + + assert(bcm43xx_status(dev) == BCM43xx_STAT_UNINIT); + bcm43xx_set_status(dev, BCM43xx_STAT_INITIALIZING); + + err = ssb_bus_powerup(bus, 0); + if (err) + goto out; + if (!ssb_device_is_enabled(dev->dev)) { + tmp = phy->gmode ? BCM43xx_TMSLOW_GMODE : 0; + bcm43xx_wireless_core_reset(dev, tmp); + } + err = bcm43xx_phy_versioning(dev); + if (err) + goto err_busdown; + + if ((phy->type == BCM43xx_PHYTYPE_B) || (phy->type == BCM43xx_PHYTYPE_G)) { + phy->lo_control = kzalloc(sizeof(*(phy->lo_control)), GFP_KERNEL); + if (!phy->lo_control) { + err = -ENOMEM; + goto err_busdown; + } + } + setup_struct_wldev_for_init(dev); + + err = bcm43xx_phy_init_tssi2dbm_table(dev); + if (err) + goto err_kfree_lo_control; + + /* Enable IRQ routing to this device. */ + ssb_pcicore_dev_irqvecs_enable(&bus->pcicore, dev->dev); + + bcm43xx_imcfglo_timeouts_workaround(dev); + bcm43xx_bluetooth_coext_disable(dev); + bcm43xx_phy_early_init(dev); + err = bcm43xx_chip_init(dev); + if (err) + goto err_kfree_tssitbl; + bcm43xx_shm_write16(dev, BCM43xx_SHM_SHARED, + BCM43xx_SHM_SH_WLCOREREV, + dev->dev->id.revision); + hf = bcm43xx_hf_read(dev); + if (phy->type == BCM43xx_PHYTYPE_G) { + hf |= BCM43xx_HF_SYMW; + if (phy->rev == 1) + hf |= BCM43xx_HF_GDCW; + if (sprom->r1.boardflags_lo & BCM43xx_BFL_PACTRL) + hf |= BCM43xx_HF_OFDMPABOOST; + } else if (phy->type == BCM43xx_PHYTYPE_B) { + hf |= BCM43xx_HF_SYMW; + if (phy->rev >= 2 && phy->radio_ver == 0x2050) + hf &= ~BCM43xx_HF_GDCW; + } + bcm43xx_hf_write(dev, hf); + + /* Short/Long Retry Limit. + * The retry-limit is a 4-bit counter. Enforce this to avoid overflowing + * the chip-internal counter. + */ + tmp = limit_value(modparam_short_retry, 0, 0xF); + bcm43xx_shm_write16(dev, BCM43xx_SHM_SCRATCH, + BCM43xx_SHM_SC_SRLIMIT, tmp); + tmp = limit_value(modparam_long_retry, 0, 0xF); + bcm43xx_shm_write16(dev, BCM43xx_SHM_SCRATCH, + BCM43xx_SHM_SC_LRLIMIT, tmp); + + bcm43xx_shm_write16(dev, BCM43xx_SHM_SHARED, + BCM43xx_SHM_SH_SFFBLIM, 3); + bcm43xx_shm_write16(dev, BCM43xx_SHM_SHARED, + BCM43xx_SHM_SH_LFFBLIM, 2); + + bcm43xx_rate_memory_init(dev); + + /* Minimum Contention Window */ + if (phy->type == BCM43xx_PHYTYPE_B) { + bcm43xx_shm_write16(dev, BCM43xx_SHM_SCRATCH, + BCM43xx_SHM_SC_MINCONT, 0x1F); + } else { + bcm43xx_shm_write16(dev, BCM43xx_SHM_SCRATCH, + BCM43xx_SHM_SC_MINCONT, 0xF); + } + /* Maximum Contention Window */ + bcm43xx_shm_write16(dev, BCM43xx_SHM_SCRATCH, + BCM43xx_SHM_SC_MAXCONT, 0x3FF); + + bcm43xx_write_mac_bssid_templates(dev); + + do { + if (bcm43xx_using_pio(dev)) + err = bcm43xx_pio_init(dev); + else + err = bcm43xx_dma_init(dev); + } while (err == -EAGAIN); + if (err) + goto err_chip_exit; + +//FIXME +#if 1 + bcm43xx_write16(dev, 0x0612, 0x0050); + bcm43xx_shm_write16(dev, BCM43xx_SHM_SHARED, 0x0416, 0x0050); + bcm43xx_shm_write16(dev, BCM43xx_SHM_SHARED, 0x0414, 0x01F4); +#endif + + bcm43xx_bluetooth_coext_enable(dev); + + ssb_bus_powerup(bus, 1); /* Enable dynamic PCTL */ + bcm43xx_macfilter_clear(dev, BCM43xx_MACFILTER_ASSOC); + bcm43xx_macfilter_set(dev, BCM43xx_MACFILTER_SELF, + (u8 *)(wl->hw->wiphy->perm_addr)); + bcm43xx_security_init(dev); + bcm43xx_measure_channel_change_time(dev); + bcm43xx_rng_init(wl); + + bcm43xx_set_status(dev, BCM43xx_STAT_INITIALIZED); + +out: + return err; + +err_chip_exit: + bcm43xx_chip_exit(dev); +err_kfree_tssitbl: + if (phy->dyn_tssi_tbl) + kfree(phy->tssi2dbm); +err_kfree_lo_control: + kfree(phy->lo_control); + phy->lo_control = NULL; +err_busdown: + ssb_bus_may_powerdown(bus); + bcm43xx_set_status(dev, BCM43xx_STAT_UNINIT); + return err; +} + +static int bcm43xx_add_interface(struct ieee80211_hw *hw, + struct ieee80211_if_init_conf *conf) +{ + struct bcm43xx_wl *wl = hw_to_bcm43xx_wl(hw); + struct bcm43xx_wldev *dev; + unsigned long flags; + int err = -EOPNOTSUPP; + int did_init = 0; + + mutex_lock(&wl->mutex); + if ((conf->type != IEEE80211_IF_TYPE_MNTR) && + wl->operating) + goto out_mutex_unlock; + + dprintk(KERN_INFO PFX "Adding Interface type %d\n", conf->type); + + dev = wl->current_dev; + if (bcm43xx_status(dev) == BCM43xx_STAT_UNINIT) { + err = bcm43xx_wireless_core_init(dev); + if (err) + goto out_mutex_unlock; + did_init = 1; + } + if (!dev->started) { + err = bcm43xx_wireless_core_start(dev); + if (err) { + if (did_init) + bcm43xx_wireless_core_exit(dev); + goto out_mutex_unlock; + } + } + + spin_lock_irqsave(&wl->irq_lock, flags); + switch (conf->type) { + case IEEE80211_IF_TYPE_MNTR: + wl->monitor++; + break; + default: + wl->operating = 1; + wl->if_id = conf->if_id; + wl->mac_addr = conf->mac_addr; + wl->if_type = conf->type; + } + bcm43xx_adjust_opmode(dev); + spin_unlock_irqrestore(&wl->irq_lock, flags); + + err = 0; +out_mutex_unlock: + mutex_unlock(&wl->mutex); + + return err; +} + +static void bcm43xx_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_if_init_conf *conf) +{ + struct bcm43xx_wl *wl = hw_to_bcm43xx_wl(hw); + struct bcm43xx_wldev *dev; + unsigned long flags; + + dprintk(KERN_INFO PFX "Removing Interface type %d\n", conf->type); + + mutex_lock(&wl->mutex); + if (conf->type == IEEE80211_IF_TYPE_MNTR) { + wl->monitor--; + assert(wl->monitor >= 0); + } else { + assert(wl->operating); + wl->operating = 0; + } + + dev = wl->current_dev; + if (!wl->operating && wl->monitor == 0) { + if (dev->started) + bcm43xx_wireless_core_stop(dev); + bcm43xx_wireless_core_exit(dev); + } else { + spin_lock_irqsave(&wl->irq_lock, flags); + bcm43xx_adjust_opmode(dev); + spin_unlock_irqrestore(&wl->irq_lock, flags); + } + mutex_unlock(&wl->mutex); +} + + +static const struct ieee80211_ops bcm43xx_hw_ops = { + .tx = bcm43xx_tx, + .conf_tx = bcm43xx_conf_tx, + .add_interface = bcm43xx_add_interface, + .remove_interface = bcm43xx_remove_interface, + .reset = bcm43xx_dev_reset, + .config = bcm43xx_dev_config, + .config_interface = bcm43xx_config_interface, + .set_multicast_list = bcm43xx_set_multicast_list, + .set_key = bcm43xx_dev_set_key, + .get_stats = bcm43xx_get_stats, + .get_tx_stats = bcm43xx_get_tx_stats, +}; + +/* Hard-reset the chip. Do not call this directly. + * Use bcm43xx_controller_restart() + */ +static void bcm43xx_chip_reset(struct work_struct *work) +{ + struct bcm43xx_wldev *dev = + container_of(work, struct bcm43xx_wldev, restart_work); + struct bcm43xx_wl *wl = dev->wl; + int err; + int was_started = 0; + int was_inited = 0; + + mutex_lock(&wl->mutex); + + /* Bring the device down... */ + if (dev->started) { + was_started = 1; + bcm43xx_wireless_core_stop(dev); + } + if (bcm43xx_status(dev) == BCM43xx_STAT_INITIALIZED) { + was_inited = 1; + bcm43xx_wireless_core_exit(dev); + } + + /* ...and up again. */ + if (was_inited) { + err = bcm43xx_wireless_core_init(dev); + if (err) + goto out; + } + if (was_started) { + assert(was_inited); + err = bcm43xx_wireless_core_start(dev); + if (err) { + bcm43xx_wireless_core_exit(dev); + goto out; + } + } +out: + mutex_unlock(&wl->mutex); + if (err) + printk(KERN_ERR PFX "Controller restart FAILED\n"); + else + printk(KERN_INFO PFX "Controller restarted\n"); +} + +static int bcm43xx_setup_modes(struct bcm43xx_wldev *dev, + int have_aphy, + int have_bphy, + int have_gphy) +{ + struct ieee80211_hw *hw = dev->wl->hw; + struct ieee80211_hw_mode *mode; + struct bcm43xx_phy *phy = &dev->phy; + int cnt = 0; + int err; + +/*FIXME: Don't tell ieee80211 about an A-PHY, because we currently don't support A-PHY. */ +have_aphy = 0; + + phy->possible_phymodes = 0; + for ( ; 1; cnt++) { + if (have_aphy) { + assert(cnt < BCM43xx_MAX_PHYHWMODES); + mode = &phy->hwmodes[cnt]; + + mode->mode = MODE_IEEE80211A; + mode->num_channels = bcm43xx_a_chantable_size; + mode->channels = bcm43xx_a_chantable; + mode->num_rates = bcm43xx_a_ratetable_size; + mode->rates = bcm43xx_a_ratetable; + err = ieee80211_register_hwmode(hw, mode); + if (err) + return err; + + phy->possible_phymodes |= BCM43xx_PHYMODE_A; + have_aphy = 0; + continue; + } + if (have_bphy) { + assert(cnt < BCM43xx_MAX_PHYHWMODES); + mode = &phy->hwmodes[cnt]; + + mode->mode = MODE_IEEE80211B; + mode->num_channels = bcm43xx_bg_chantable_size; + mode->channels = bcm43xx_bg_chantable; + mode->num_rates = bcm43xx_b_ratetable_size; + mode->rates = bcm43xx_b_ratetable; + err = ieee80211_register_hwmode(hw, mode); + if (err) + return err; + + phy->possible_phymodes |= BCM43xx_PHYMODE_B; + have_bphy = 0; + continue; + } + if (have_gphy) { + assert(cnt < BCM43xx_MAX_PHYHWMODES); + mode = &phy->hwmodes[cnt]; + + mode->mode = MODE_IEEE80211G; + mode->num_channels = bcm43xx_bg_chantable_size; + mode->channels = bcm43xx_bg_chantable; + mode->num_rates = bcm43xx_g_ratetable_size; + mode->rates = bcm43xx_g_ratetable; + err = ieee80211_register_hwmode(hw, mode); + if (err) + return err; + + phy->possible_phymodes |= BCM43xx_PHYMODE_G; + have_gphy = 0; + continue; + } + break; + } + + return 0; +} + +static void bcm43xx_wireless_core_detach(struct bcm43xx_wldev *dev) +{ + /* We release firmware that late to not be required to re-request + * is all the time when we reinit the core. */ + bcm43xx_release_firmware(dev); +} + +static int bcm43xx_wireless_core_attach(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_wl *wl = dev->wl; + struct ssb_bus *bus = dev->dev->bus; + struct pci_dev *pdev = bus->host_pci; + int err; + int have_aphy = 0, have_bphy = 0, have_gphy = 0; + u32 tmp; + + /* Do NOT do any device initialization here. + * Do it in wireless_core_init() instead. + * This function is for gathering basic information about the HW, only. + * Also some structs may be set up here. But most likely you want to have + * that in core_init(), too. + */ + + /* Get the PHY type. */ + if (dev->dev->id.revision >= 5) { + u32 tmshigh; + + tmshigh = ssb_read32(dev->dev, SSB_TMSHIGH); + have_aphy = !!(tmshigh & BCM43xx_TMSHIGH_APHY); + have_gphy = !!(tmshigh & BCM43xx_TMSHIGH_GPHY); + if (!have_aphy && !have_gphy) + have_bphy = 1; + } else if (dev->dev->id.revision == 4) { + have_gphy = 1; + have_aphy = 1; + } else + have_bphy = 1; + + /* Initialize LEDs structs. */ + err = bcm43xx_leds_init(dev); + if (err) + goto out; + + dev->phy.gmode = (have_gphy || have_bphy); + tmp = dev->phy.gmode ? BCM43xx_TMSLOW_GMODE : 0; + bcm43xx_wireless_core_reset(dev, tmp); + + /* Check if this device supports multiband. */ + if (!pdev || + (pdev->device != 0x4312 && + pdev->device != 0x4319 && + pdev->device != 0x4324)) { + /* No multiband support. */ + u8 phytype; + + tmp = bcm43xx_read16(dev, BCM43xx_MMIO_PHY_VER); + phytype = (tmp & 0x0F00) >> 8; + have_aphy = 0; + have_bphy = 0; + have_gphy = 0; + switch (phytype) { + case BCM43xx_PHYTYPE_A: + have_aphy = 1; + break; + case BCM43xx_PHYTYPE_B: + have_bphy = 1; + break; + case BCM43xx_PHYTYPE_G: + have_gphy = 1; + break; + default: + assert(0); + } + } + dev->phy.gmode = (have_gphy || have_bphy); + tmp = dev->phy.gmode ? BCM43xx_TMSLOW_GMODE : 0; + bcm43xx_wireless_core_reset(dev, tmp); + + err = bcm43xx_validate_chipaccess(dev); + if (err) + goto err_leds_exit; + err = bcm43xx_setup_modes(dev, have_aphy, + have_bphy, have_gphy); + if (err) + goto err_leds_exit; + + /* Now set some default "current_dev" */ + if (!wl->current_dev) + wl->current_dev = dev; + INIT_WORK(&dev->restart_work, bcm43xx_chip_reset); + + bcm43xx_radio_turn_off(dev); + bcm43xx_switch_analog(dev, 0); + ssb_device_disable(dev->dev, 0); + ssb_bus_may_powerdown(bus); + +out: + return err; + +err_leds_exit: + bcm43xx_leds_exit(dev); + return err; +} + +static void bcm43xx_one_core_detach(struct ssb_device *dev) +{ + struct bcm43xx_wldev *wldev; + struct bcm43xx_wl *wl; + + wldev = ssb_get_drvdata(dev); + wl = wldev->wl; + bcm43xx_debugfs_remove_device(wldev); + bcm43xx_wireless_core_detach(wldev); + list_del(&wldev->list); + wl->nr_devs--; + ssb_set_drvdata(dev, NULL); + kfree(wldev); +} + +static int bcm43xx_one_core_attach(struct ssb_device *dev, + struct bcm43xx_wl *wl) +{ + struct bcm43xx_wldev *wldev; + struct pci_dev *pdev; + int err = -ENOMEM; + + if (!list_empty(&wl->devlist)) { + /* We are not the first core on this chip. */ + pdev = dev->bus->host_pci; + /* Only special chips support more than one wireless + * core, although some of the other chips have more than + * one wireless core as well. Check for this and + * bail out early. + */ + if (!pdev || + ((pdev->device != 0x4321) && + (pdev->device != 0x4313) && + (pdev->device != 0x431A))) { + dprintk(KERN_INFO PFX "Ignoring unconnected 802.11 core\n"); + return -ENODEV; + } + } + + wldev = kzalloc(sizeof(*wldev), GFP_KERNEL); + if (!wldev) + goto out; + + wldev->dev = dev; + wldev->wl = wl; + bcm43xx_set_status(wldev, BCM43xx_STAT_UNINIT); + wldev->bad_frames_preempt = modparam_bad_frames_preempt; + tasklet_init(&wldev->isr_tasklet, + (void (*)(unsigned long))bcm43xx_interrupt_tasklet, + (unsigned long)wldev); + if (modparam_pio) + wldev->__using_pio = 1; + INIT_LIST_HEAD(&wldev->list); + + err = bcm43xx_wireless_core_attach(wldev); + if (err) + goto err_kfree_wldev; + + list_add(&wldev->list, &wl->devlist); + wl->nr_devs++; + ssb_set_drvdata(dev, wldev); + bcm43xx_debugfs_add_device(wldev); + +out: + return err; + +err_kfree_wldev: + kfree(wldev); + return err; +} + +static void bcm43xx_sprom_fixup(struct ssb_bus *bus) +{ + /* boardflags workarounds */ + if (bus->board_vendor == SSB_BOARDVENDOR_DELL && + bus->chip_id == 0x4301 && + bus->board_rev == 0x74) + bus->sprom.r1.boardflags_lo |= BCM43xx_BFL_BTCOEXIST; + if (bus->board_vendor == PCI_VENDOR_ID_APPLE && + bus->board_type == 0x4E && + bus->board_rev > 0x40) + bus->sprom.r1.boardflags_lo |= BCM43xx_BFL_PACTRL; + + /* Convert Antennagain values to Q5.2 */ + bus->sprom.r1.antenna_gain_a <<= 2; + bus->sprom.r1.antenna_gain_bg <<= 2; +} + +static void bcm43xx_wireless_exit(struct ssb_device *dev, + struct bcm43xx_wl *wl) +{ + struct ieee80211_hw *hw = wl->hw; + + ssb_set_devtypedata(dev, NULL); + ieee80211_free_hw(hw); +} + +static int bcm43xx_wireless_init(struct ssb_device *dev) +{ + struct ssb_sprom *sprom = &dev->bus->sprom; + struct ieee80211_hw *hw; + struct bcm43xx_wl *wl; + int err = -ENOMEM; + + bcm43xx_sprom_fixup(dev->bus); + + hw = ieee80211_alloc_hw(sizeof(*wl), &bcm43xx_hw_ops); + if (!hw) { + printk(KERN_ERR PFX "Could not allocate ieee80211 device\n"); + goto out; + } + + /* fill hw info */ + hw->flags = IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE | + IEEE80211_HW_MONITOR_DURING_OPER | + IEEE80211_HW_DEVICE_HIDES_WEP | + IEEE80211_HW_WEP_INCLUDE_IV; + hw->max_signal = -110; + hw->max_rssi = BCM43xx_RX_MAX_SSI; + hw->max_noise = -110; + hw->queues = 1; + SET_IEEE80211_DEV(hw, dev->dev); + if (is_valid_ether_addr(sprom->r1.et1mac)) + SET_IEEE80211_PERM_ADDR(hw, sprom->r1.et1mac); + else + SET_IEEE80211_PERM_ADDR(hw, sprom->r1.il0mac); + + /* Get and initialize struct bcm43xx_wl */ + wl = hw_to_bcm43xx_wl(hw); + memset(wl, 0, sizeof(*wl)); + wl->hw = hw; + spin_lock_init(&wl->irq_lock); + spin_lock_init(&wl->leds_lock); + mutex_init(&wl->mutex); + INIT_LIST_HEAD(&wl->devlist); + + ssb_set_devtypedata(dev, wl); + printk(KERN_INFO PFX "Broadcom %04X WLAN found\n", dev->bus->chip_id); + err = 0; +out: + return err; +} + +static int bcm43xx_probe(struct ssb_device *dev, + const struct ssb_device_id *id) +{ + struct bcm43xx_wl *wl; + int err; + int first = 0; + + wl = ssb_get_devtypedata(dev); + if (!wl) { + /* Probing the first core. Must setup common struct bcm43xx_wl */ + first = 1; + err = bcm43xx_wireless_init(dev); + if (err) + goto out; + wl = ssb_get_devtypedata(dev); + assert(wl); + } + err = bcm43xx_one_core_attach(dev, wl); + if (err) + goto err_wireless_exit; + + if (first) { + err = ieee80211_register_hw(wl->hw); + if (err) + goto err_one_core_detach; + } + +out: + return err; + +err_one_core_detach: + bcm43xx_one_core_detach(dev); +err_wireless_exit: + if (first) + bcm43xx_wireless_exit(dev, wl); + return err; +} + +static void bcm43xx_remove(struct ssb_device *dev) +{ + struct bcm43xx_wl *wl = ssb_get_devtypedata(dev); + struct bcm43xx_wldev *wldev = ssb_get_drvdata(dev); + + assert(wl); + if (wl->current_dev == wldev) + ieee80211_unregister_hw(wl->hw); + + bcm43xx_one_core_detach(dev); + + if (list_empty(&wl->devlist)) { + /* Last core on the chip unregistered. + * We can destroy common struct bcm43xx_wl. + */ + bcm43xx_wireless_exit(dev, wl); + } +} + +/* Hard-reset the chip. + * This can be called from interrupt or process context. + * dev->irq_lock must be locked. + */ +void bcm43xx_controller_restart(struct bcm43xx_wldev *dev, const char *reason) +{ + if (bcm43xx_status(dev) != BCM43xx_STAT_INITIALIZED) + return; + printk(KERN_ERR PFX "Controller RESET (%s) ...\n", reason); + schedule_work(&dev->restart_work); +} + +#ifdef CONFIG_PM + +static int bcm43xx_suspend(struct ssb_device *dev, pm_message_t state) +{ + struct bcm43xx_wldev *wldev = ssb_get_drvdata(dev); + struct bcm43xx_wl *wl = wldev->wl; + + dprintk(KERN_INFO PFX "Suspending...\n"); + + mutex_lock(&wl->mutex); + wldev->was_started = !!wldev->started; + wldev->was_initialized = (bcm43xx_status(wldev) == BCM43xx_STAT_INITIALIZED); + if (wldev->started) + bcm43xx_wireless_core_stop(wldev); + if (bcm43xx_status(wldev) == BCM43xx_STAT_INITIALIZED) + bcm43xx_wireless_core_exit(wldev); + + mutex_unlock(&wl->mutex); + + dprintk(KERN_INFO PFX "Device suspended.\n"); + + return 0; +} + +static int bcm43xx_resume(struct ssb_device *dev) +{ + struct bcm43xx_wldev *wldev = ssb_get_drvdata(dev); + int err = 0; + + dprintk(KERN_INFO PFX "Resuming...\n"); + + if (wldev->was_initialized) { + err = bcm43xx_wireless_core_init(wldev); + if (err) { + printk(KERN_ERR PFX "Resume failed at core init\n"); + goto out; + } + } + if (wldev->was_started) { + assert(wldev->was_initialized); + err = bcm43xx_wireless_core_start(wldev); + if (err) { + printk(KERN_ERR PFX "Resume failed at core start\n"); + goto out; + } + } + + dprintk(KERN_INFO PFX "Device resumed.\n"); +out: + return err; +} + +#else /* CONFIG_PM */ +# define bcm43xx_suspend NULL +# define bcm43xx_resume NULL +#endif /* CONFIG_PM */ + +static struct ssb_driver bcm43xx_ssb_driver = { + .name = KBUILD_MODNAME, + .id_table = bcm43xx_ssb_tbl, + .probe = bcm43xx_probe, + .remove = bcm43xx_remove, + .suspend = bcm43xx_suspend, + .resume = bcm43xx_resume, +}; + +#ifdef CONFIG_BCM43XX_MAC80211_PCI +/* The PCI frontend stub */ +static const struct pci_device_id bcm43xx_pci_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4307) }, + { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4311) }, + { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4312) }, + { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4318) }, + { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4319) }, + { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4320) }, + { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4321) }, + { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4324) }, + { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4325) }, + { 0 }, +}; +MODULE_DEVICE_TABLE(pci, bcm43xx_pci_tbl); + +static struct pci_driver bcm43xx_pci_driver = { + .name = "bcm43xx-pci", + .id_table = bcm43xx_pci_tbl, +}; +#endif /* CONFIG_BCM43XX_MAC80211_PCI */ + +static int __init bcm43xx_init(void) +{ + int err; + + bcm43xx_debugfs_init(); +#ifdef CONFIG_BCM43XX_MAC80211_PCI + err = ssb_pcihost_register(&bcm43xx_pci_driver); + if (err) + goto err_dfs_exit; +#endif + err = bcm43xx_pcmcia_init(); + if (err) + goto err_pci_exit; + err = ssb_driver_register(&bcm43xx_ssb_driver); + if (err) + goto err_pcmcia_exit; + + return err; + +err_pcmcia_exit: + bcm43xx_pcmcia_exit(); +err_pci_exit: +#ifdef CONFIG_BCM43XX_MAC80211_PCI + ssb_pcihost_unregister(&bcm43xx_pci_driver); +#endif +err_dfs_exit: + bcm43xx_debugfs_exit(); + return err; +} + +static void __exit bcm43xx_exit(void) +{ + ssb_driver_unregister(&bcm43xx_ssb_driver); + bcm43xx_pcmcia_exit(); +#ifdef CONFIG_BCM43XX_MAC80211_PCI + ssb_pcihost_unregister(&bcm43xx_pci_driver); +#endif + bcm43xx_debugfs_exit(); +} + +module_init(bcm43xx_init) +module_exit(bcm43xx_exit) diff --git a/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_main.h b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_main.h new file mode 100644 index 0000000..0a39539 --- /dev/null +++ b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_main.h @@ -0,0 +1,156 @@ +/* + + Broadcom BCM43xx wireless driver + + Copyright (c) 2005 Martin Langer , + Stefano Brivio + Michael Buesch + Danny van Dyk + Andreas Jaggi + + Some parts of the code in this file are derived from the ipw2200 + driver Copyright(c) 2003 - 2004 Intel Corporation. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#ifndef BCM43xx_MAIN_H_ +#define BCM43xx_MAIN_H_ + +#include "bcm43xx.h" + + +#define P4D_BYT3S(magic, nr_bytes) u8 __p4dding##magic[nr_bytes] +#define P4D_BYTES(line, nr_bytes) P4D_BYT3S(line, nr_bytes) +/* Magic helper macro to pad structures. Ignore those above. It's magic. */ +#define PAD_BYTES(nr_bytes) P4D_BYTES( __LINE__ , (nr_bytes)) + + +/* Lightweight function to convert a frequency (in Mhz) to a channel number. */ +static inline +u8 bcm43xx_freq_to_channel_a(int freq) +{ + return ((freq - 5000) / 5); +} +static inline +u8 bcm43xx_freq_to_channel_bg(int freq) +{ + u8 channel; + + if (freq == 2484) + channel = 14; + else + channel = (freq - 2407) / 5; + + return channel; +} +static inline +u8 bcm43xx_freq_to_channel(struct bcm43xx_wldev *dev, + int freq) +{ + if (dev->phy.type == BCM43xx_PHYTYPE_A) + return bcm43xx_freq_to_channel_a(freq); + return bcm43xx_freq_to_channel_bg(freq); +} + +/* Lightweight function to convert a channel number to a frequency (in Mhz). */ +static inline +int bcm43xx_channel_to_freq_a(u8 channel) +{ + return (5000 + (5 * channel)); +} +static inline +int bcm43xx_channel_to_freq_bg(u8 channel) +{ + int freq; + + if (channel == 14) + freq = 2484; + else + freq = 2407 + (5 * channel); + + return freq; +} +static inline +int bcm43xx_channel_to_freq(struct bcm43xx_wldev *dev, + u8 channel) +{ + if (dev->phy.type == BCM43xx_PHYTYPE_A) + return bcm43xx_channel_to_freq_a(channel); + return bcm43xx_channel_to_freq_bg(channel); +} + +static inline +int bcm43xx_is_cck_rate(int rate) +{ + return (rate == BCM43xx_CCK_RATE_1MB || + rate == BCM43xx_CCK_RATE_2MB || + rate == BCM43xx_CCK_RATE_5MB || + rate == BCM43xx_CCK_RATE_11MB); +} + +static inline +int bcm43xx_is_ofdm_rate(int rate) +{ + return !bcm43xx_is_cck_rate(rate); +} + +static inline +int bcm43xx_is_hw_radio_enabled(struct bcm43xx_wldev *dev) +{ + /* function to return state of hardware enable of radio + * returns 0 if radio disabled, 1 if radio enabled + */ + struct bcm43xx_phy *phy = &dev->phy; + + if (phy->rev >= 3) + return ((bcm43xx_read32(dev, BCM43xx_MMIO_RADIO_HWENABLED_HI) + & BCM43xx_MMIO_RADIO_HWENABLED_HI_MASK) + == 0) ? 1 : 0; + else + return ((bcm43xx_read16(dev, BCM43xx_MMIO_RADIO_HWENABLED_LO) + & BCM43xx_MMIO_RADIO_HWENABLED_LO_MASK) + == 0) ? 0 : 1; +} + +void bcm43xx_tsf_read(struct bcm43xx_wldev *dev, u64 *tsf); +void bcm43xx_tsf_write(struct bcm43xx_wldev *dev, u64 tsf); + +u32 bcm43xx_shm_read32(struct bcm43xx_wldev *dev, + u16 routing, u16 offset); +u16 bcm43xx_shm_read16(struct bcm43xx_wldev *dev, + u16 routing, u16 offset); +void bcm43xx_shm_write32(struct bcm43xx_wldev *dev, + u16 routing, u16 offset, + u32 value); +void bcm43xx_shm_write16(struct bcm43xx_wldev *dev, + u16 routing, u16 offset, + u16 value); + +u32 bcm43xx_hf_read(struct bcm43xx_wldev *dev); +void bcm43xx_hf_write(struct bcm43xx_wldev *dev, u32 value); + +void bcm43xx_dummy_transmission(struct bcm43xx_wldev *dev); + +void bcm43xx_wireless_core_reset(struct bcm43xx_wldev *dev, u32 flags); + +void bcm43xx_mac_suspend(struct bcm43xx_wldev *dev); +void bcm43xx_mac_enable(struct bcm43xx_wldev *dev); + +void bcm43xx_controller_restart(struct bcm43xx_wldev *dev, const char *reason); + +#endif /* BCM43xx_MAIN_H_ */ diff --git a/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_pcmcia.c b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_pcmcia.c new file mode 100644 index 0000000..3b098e5 --- /dev/null +++ b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_pcmcia.c @@ -0,0 +1,163 @@ +/* + + Broadcom BCM43xx wireless driver + + Copyright (c) 2007 Michael Buesch + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include + +#include +#include +#include +#include +#include +#include + + +static /*const*/ struct pcmcia_device_id bcm43xx_pcmcia_tbl[] = { + PCMCIA_DEVICE_MANF_CARD(0x2D0, 0x448), + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, bcm43xx_pcmcia_tbl); + + +#ifdef CONFIG_PM +static int bcm43xx_pcmcia_suspend(struct pcmcia_device *dev) +{ + //TODO + return 0; +} + +static int bcm43xx_pcmcia_resume(struct pcmcia_device *dev) +{ + //TODO + return 0; +} +#else /* CONFIG_PM */ +# define bcm43xx_pcmcia_suspend NULL +# define bcm43xx_pcmcia_resume NULL +#endif /* CONFIG_PM */ + +static void bcm43xx_pcmcia_fill_sprom(struct ssb_sprom *sprom) +{//TODO +} + +static int __devinit bcm43xx_pcmcia_probe(struct pcmcia_device *dev) +{ + struct ssb_bus *ssb; + win_req_t win; + memreq_t mem; + tuple_t tuple; + cisparse_t parse; + int err = -ENOMEM; + int res; + unsigned char buf[64]; + + ssb = kzalloc(sizeof(*ssb), GFP_KERNEL); + if (!ssb) + goto out; + + err = -ENODEV; + tuple.DesiredTuple = CISTPL_CONFIG; + tuple.Attributes = 0; + tuple.TupleData = buf; + tuple.TupleDataMax = sizeof(buf); + tuple.TupleOffset = 0; + + res = pcmcia_get_first_tuple(dev, &tuple); + if (res != CS_SUCCESS) + goto err_kfree_ssb; + res = pcmcia_get_tuple_data(dev, &tuple); + if (res != CS_SUCCESS) + goto err_kfree_ssb; + res = pcmcia_parse_tuple(dev, &tuple, &parse); + if (res != CS_SUCCESS) + goto err_kfree_ssb; + + dev->conf.ConfigBase = parse.config.base; + dev->conf.Present = parse.config.rmask[0]; + + dev->io.BasePort2 = 0; + dev->io.NumPorts2 = 0; + dev->io.Attributes2 = 0; + + win.Attributes = WIN_MEMORY_TYPE_CM | WIN_ENABLE | WIN_USE_WAIT; + win.Base = 0; + win.Size = SSB_CORE_SIZE; + win.AccessSpeed = 1000; + res = pcmcia_request_window(&dev, &win, &dev->win); + if (res != CS_SUCCESS) + goto err_kfree_ssb; + + mem.CardOffset = 0; + mem.Page = 0; + res = pcmcia_map_mem_page(dev->win, &mem); + if (res != CS_SUCCESS) + goto err_kfree_ssb; + + res = pcmcia_request_configuration(dev, &dev->conf); + if (res != CS_SUCCESS) + goto err_disable; + + err = ssb_bus_pcmciabus_register(ssb, dev, win.Base, + bcm43xx_pcmcia_fill_sprom); + dev->priv = ssb; + +out: + return err; +err_disable: + pcmcia_disable_device(dev); +err_kfree_ssb: + kfree(ssb); + return err; +} + +static void __devexit bcm43xx_pcmcia_remove(struct pcmcia_device *dev) +{ + struct ssb_bus *ssb = dev->priv; + + ssb_bus_unregister(ssb); + pcmcia_release_window(dev->win); + pcmcia_disable_device(dev); + kfree(ssb); + dev->priv = NULL; +} + +static struct pcmcia_driver bcm43xx_pcmcia_driver = { + .owner = THIS_MODULE, + .drv = { + .name = "bcm43xx-pcmcia", + }, + .id_table = bcm43xx_pcmcia_tbl, + .probe = bcm43xx_pcmcia_probe, + .remove = bcm43xx_pcmcia_remove, + .suspend = bcm43xx_pcmcia_suspend, + .resume = bcm43xx_pcmcia_resume, +}; + +int bcm43xx_pcmcia_init(void) +{ + return pcmcia_register_driver(&bcm43xx_pcmcia_driver); +} + +void bcm43xx_pcmcia_exit(void) +{ + pcmcia_unregister_driver(&bcm43xx_pcmcia_driver); +} diff --git a/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_pcmcia.h b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_pcmcia.h new file mode 100644 index 0000000..e6d5788 --- /dev/null +++ b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_pcmcia.h @@ -0,0 +1,22 @@ +#ifndef BCM43xx_PCMCIA_H_ +#define BCM43xx_PCMCIA_H_ + +#ifdef CONFIG_BCM43XX_MAC80211_PCMCIA + +int bcm43xx_pcmcia_init(void); +void bcm43xx_pcmcia_exit(void); + +#else /* CONFIG_BCM43XX_MAC80211_PCMCIA */ + +static inline +int bcm43xx_pcmcia_init(void) +{ + return 0; +} +static inline +void bcm43xx_pcmcia_exit(void) +{ +} + +#endif /* CONFIG_BCM43XX_MAC80211_PCMCIA */ +#endif /* BCM43xx_PCMCIA_H_ */ diff --git a/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_phy.c b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_phy.c new file mode 100644 index 0000000..758f00b --- /dev/null +++ b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_phy.c @@ -0,0 +1,4286 @@ +/* + + Broadcom BCM43xx wireless driver + + Copyright (c) 2005 Martin Langer , + Copyright (c) 2005, 2006 Stefano Brivio + Copyright (c) 2005, 2006 Michael Buesch + Copyright (c) 2005, 2006 Danny van Dyk + Copyright (c) 2005, 2006 Andreas Jaggi + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include +#include + +#include "bcm43xx.h" +#include "bcm43xx_phy.h" +#include "bcm43xx_main.h" +#include "bcm43xx_tables.h" +#include "bcm43xx_power.h" +#include "bcm43xx_lo.h" + + +static const s8 bcm43xx_tssi2dbm_b_table[] = { + 0x4D, 0x4C, 0x4B, 0x4A, + 0x4A, 0x49, 0x48, 0x47, + 0x47, 0x46, 0x45, 0x45, + 0x44, 0x43, 0x42, 0x42, + 0x41, 0x40, 0x3F, 0x3E, + 0x3D, 0x3C, 0x3B, 0x3A, + 0x39, 0x38, 0x37, 0x36, + 0x35, 0x34, 0x32, 0x31, + 0x30, 0x2F, 0x2D, 0x2C, + 0x2B, 0x29, 0x28, 0x26, + 0x25, 0x23, 0x21, 0x1F, + 0x1D, 0x1A, 0x17, 0x14, + 0x10, 0x0C, 0x06, 0x00, + -7, -7, -7, -7, + -7, -7, -7, -7, + -7, -7, -7, -7, +}; + +static const s8 bcm43xx_tssi2dbm_g_table[] = { + 77, 77, 77, 76, + 76, 76, 75, 75, + 74, 74, 73, 73, + 73, 72, 72, 71, + 71, 70, 70, 69, + 68, 68, 67, 67, + 66, 65, 65, 64, + 63, 63, 62, 61, + 60, 59, 58, 57, + 56, 55, 54, 53, + 52, 50, 49, 47, + 45, 43, 40, 37, + 33, 28, 22, 14, + 5, -7, -20, -20, + -20, -20, -20, -20, + -20, -20, -20, -20, +}; + +const u8 bcm43xx_radio_channel_codes_bg[] = { + 12, 17, 22, 27, + 32, 37, 42, 47, + 52, 57, 62, 67, + 72, 84, +}; + + +static void bcm43xx_phy_initg(struct bcm43xx_wldev *dev); + +/* Reverse the bits of a 4bit value. + * Example: 1101 is flipped 1011 + */ +static u16 flip_4bit(u16 value) +{ + u16 flipped = 0x0000; + + assert((value & ~0x000F) == 0x0000); + + flipped |= (value & 0x0001) << 3; + flipped |= (value & 0x0002) << 1; + flipped |= (value & 0x0004) >> 1; + flipped |= (value & 0x0008) >> 3; + + return flipped; +} + +static void generate_rfatt_list(struct bcm43xx_wldev *dev, + struct bcm43xx_rfatt_list *list) +{ + struct bcm43xx_phy *phy = &dev->phy; + + /* APHY.rev < 5 || GPHY.rev < 6 */ + static const struct bcm43xx_rfatt rfatt_0[] = { + { .att = 3, .with_padmix = 0, }, + { .att = 1, .with_padmix = 0, }, + { .att = 5, .with_padmix = 0, }, + { .att = 7, .with_padmix = 0, }, + { .att = 9, .with_padmix = 0, }, + { .att = 2, .with_padmix = 0, }, + { .att = 0, .with_padmix = 0, }, + { .att = 4, .with_padmix = 0, }, + { .att = 6, .with_padmix = 0, }, + { .att = 8, .with_padmix = 0, }, + { .att = 1, .with_padmix = 1, }, + { .att = 2, .with_padmix = 1, }, + { .att = 3, .with_padmix = 1, }, + { .att = 4, .with_padmix = 1, }, + }; + /* Radio.rev == 8 && Radio.version == 0x2050 */ + static const struct bcm43xx_rfatt rfatt_1[] = { + { .att = 2, .with_padmix = 1, }, + { .att = 4, .with_padmix = 1, }, + { .att = 6, .with_padmix = 1, }, + { .att = 8, .with_padmix = 1, }, + { .att = 10, .with_padmix = 1, }, + { .att = 12, .with_padmix = 1, }, + { .att = 14, .with_padmix = 1, }, + }; + /* Otherwise */ + static const struct bcm43xx_rfatt rfatt_2[] = { + { .att = 0, .with_padmix = 1, }, + { .att = 2, .with_padmix = 1, }, + { .att = 4, .with_padmix = 1, }, + { .att = 6, .with_padmix = 1, }, + { .att = 8, .with_padmix = 1, }, + { .att = 9, .with_padmix = 1, }, + { .att = 9, .with_padmix = 1, }, + }; + + if ((phy->type == BCM43xx_PHYTYPE_A && phy->rev < 5) || + (phy->type == BCM43xx_PHYTYPE_G && phy->rev < 6)) { + /* Software pctl */ + list->list = rfatt_0; + list->len = ARRAY_SIZE(rfatt_0); + list->min_val = 0; + list->max_val = 9; + return; + } + if (phy->radio_ver == 0x2050 && phy->radio_rev == 8) { + /* Hardware pctl */ + list->list = rfatt_1; + list->len = ARRAY_SIZE(rfatt_1); + list->min_val = 2; + list->max_val = 14; + return; + } + /* Hardware pctl */ + list->list = rfatt_2; + list->len = ARRAY_SIZE(rfatt_2); + list->min_val = 0; + list->max_val = 9; +} + +static void generate_bbatt_list(struct bcm43xx_wldev *dev, + struct bcm43xx_bbatt_list *list) +{ + static const struct bcm43xx_bbatt bbatt_0[] = { + { .att = 0, }, + { .att = 1, }, + { .att = 2, }, + { .att = 3, }, + { .att = 4, }, + { .att = 5, }, + { .att = 6, }, + { .att = 7, }, + { .att = 8, }, + }; + + list->list = bbatt_0; + list->len = ARRAY_SIZE(bbatt_0); + list->min_val = 0; + list->max_val = 8; +} + +static void bcm43xx_shm_clear_tssi(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + + switch (phy->type) { + case BCM43xx_PHYTYPE_A: + bcm43xx_shm_write16(dev, BCM43xx_SHM_SHARED, 0x0068, 0x7F7F); + bcm43xx_shm_write16(dev, BCM43xx_SHM_SHARED, 0x006a, 0x7F7F); + break; + case BCM43xx_PHYTYPE_B: + case BCM43xx_PHYTYPE_G: + bcm43xx_shm_write16(dev, BCM43xx_SHM_SHARED, 0x0058, 0x7F7F); + bcm43xx_shm_write16(dev, BCM43xx_SHM_SHARED, 0x005a, 0x7F7F); + bcm43xx_shm_write16(dev, BCM43xx_SHM_SHARED, 0x0070, 0x7F7F); + bcm43xx_shm_write16(dev, BCM43xx_SHM_SHARED, 0x0072, 0x7F7F); + break; + } +} + +void bcm43xx_raw_phy_lock(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + + assert(irqs_disabled()); + if (bcm43xx_read32(dev, BCM43xx_MMIO_STATUS_BITFIELD) == 0) { + phy->locked = 0; + return; + } + if (dev->dev->id.revision < 3) { + bcm43xx_mac_suspend(dev); + spin_lock(&phy->lock); + } else { + if (!bcm43xx_is_mode(dev->wl, IEEE80211_IF_TYPE_AP)) + bcm43xx_power_saving_ctl_bits(dev, -1, 1); + } + phy->locked = 1; +} + +void bcm43xx_raw_phy_unlock(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + + assert(irqs_disabled()); + if (dev->dev->id.revision < 3) { + if (phy->locked) { + spin_unlock(&phy->lock); + bcm43xx_mac_enable(dev); + } + } else { + if (!bcm43xx_is_mode(dev->wl, IEEE80211_IF_TYPE_AP)) + bcm43xx_power_saving_ctl_bits(dev, -1, -1); + } + phy->locked = 0; +} + +/* Different PHYs require different register routing flags. + * This adjusts (and does sanity checks on) the routing flags. + */ +static inline u16 adjust_phyreg_for_phytype(struct bcm43xx_phy *phy, + u16 offset) +{ + if (phy->type == BCM43xx_PHYTYPE_A) { + /* OFDM registers are base-registers for the A-PHY. */ + offset &= ~BCM43xx_PHYROUTE_OFDM_GPHY; + } + if (offset & BCM43xx_PHYROUTE_EXT_GPHY) { + /* Ext-G registers are only available on G-PHYs */ + if (phy->type != BCM43xx_PHYTYPE_G) { + dprintk(KERN_ERR PFX "EXT-G PHY access at " + "0x%04X on %u type PHY\n", + offset, phy->type); + } + } + + return offset; +} + +u16 bcm43xx_phy_read(struct bcm43xx_wldev *dev, u16 offset) +{ + struct bcm43xx_phy *phy = &dev->phy; + + offset = adjust_phyreg_for_phytype(phy, offset); + bcm43xx_write16(dev, BCM43xx_MMIO_PHY_CONTROL, offset); + return bcm43xx_read16(dev, BCM43xx_MMIO_PHY_DATA); +} + +void bcm43xx_phy_write(struct bcm43xx_wldev *dev, u16 offset, u16 val) +{ + struct bcm43xx_phy *phy = &dev->phy; + + offset = adjust_phyreg_for_phytype(phy, offset); + bcm43xx_write16(dev, BCM43xx_MMIO_PHY_CONTROL, offset); + mmiowb(); + bcm43xx_write16(dev, BCM43xx_MMIO_PHY_DATA, val); +} + +/* This func is called "PHY calibrate" in the specs... */ +void bcm43xx_phy_early_init(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + struct bcm43xx_txpower_lo_control *lo = phy->lo_control; + + bcm43xx_read32(dev, BCM43xx_MMIO_STATUS_BITFIELD); /* Dummy read. */ + if (phy->type == BCM43xx_PHYTYPE_B || + phy->type == BCM43xx_PHYTYPE_G) { + generate_rfatt_list(dev, &lo->rfatt_list); + generate_bbatt_list(dev, &lo->bbatt_list); + } + if (phy->type == BCM43xx_PHYTYPE_G && phy->rev == 1) { + /* Workaround: Temporarly disable gmode through the early init + * phase, as the gmode stuff is not needed for phy rev 1 */ + phy->gmode = 0; + bcm43xx_wireless_core_reset(dev, 0); + bcm43xx_phy_initg(dev); + phy->gmode = 1; + bcm43xx_wireless_core_reset(dev, BCM43xx_TMSLOW_GMODE); + } +} + +/* GPHY_TSSI_Power_Lookup_Table_Init */ +static void bcm43xx_gphy_tssi_power_lt_init(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + int i; + u16 value; + + for (i = 0; i < 32; i++) + bcm43xx_ofdmtab_write16(dev, 0x3C20, i, phy->tssi2dbm[i]); + for (i = 32; i < 64; i++) + bcm43xx_ofdmtab_write16(dev, 0x3C00, i - 32, phy->tssi2dbm[i]); + for (i = 0; i < 64; i += 2) { + value = (u16)phy->tssi2dbm[i]; + value |= ((u16)phy->tssi2dbm[i + 1]) << 8; + bcm43xx_phy_write(dev, 0x380 + (i / 2), value); + } +} + +/* GPHY_Gain_Lookup_Table_Init */ +static void bcm43xx_gphy_gain_lt_init(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + struct bcm43xx_txpower_lo_control *lo = phy->lo_control; + u16 nr_written = 0; + u16 tmp; + u8 rf, bb; + + if (!lo->lo_measured) { + bcm43xx_phy_write(dev, 0x3FF, 0); + return; + } + + for (rf = 0; rf < lo->rfatt_list.len; rf++) { + for (bb = 0; bb < lo->bbatt_list.len; bb++) { + if (nr_written >= 0x40) + return; + tmp = lo->bbatt_list.list[bb].att; + tmp <<= 8; + if (phy->radio_rev == 8) + tmp |= 0x50; + else + tmp |= 0x40; + tmp |= lo->rfatt_list.list[rf].att; + bcm43xx_phy_write(dev, 0x3C0 + nr_written, + tmp); + nr_written++; + } + } +} + +/* GPHY_DC_Lookup_Table */ +void bcm43xx_gphy_dc_lt_init(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + struct bcm43xx_txpower_lo_control *lo = phy->lo_control; + struct bcm43xx_loctl *loctl0; + struct bcm43xx_loctl *loctl1; + int i; + int rf_offset, bb_offset; + u16 tmp; + + for (i = 0; + i < lo->rfatt_list.len + lo->bbatt_list.len; + i += 2) { + rf_offset = i / lo->rfatt_list.len; + bb_offset = i % lo->rfatt_list.len; + + loctl0 = bcm43xx_get_loctl(dev, &lo->rfatt_list.list[rf_offset], + &lo->bbatt_list.list[bb_offset]); + if (i + 1 < lo->rfatt_list.len * lo->bbatt_list.len) { + rf_offset = (i + 1) / lo->rfatt_list.len; + bb_offset = (i + 1) % lo->rfatt_list.len; + + loctl1 = bcm43xx_get_loctl(dev, &lo->rfatt_list.list[rf_offset], + &lo->bbatt_list.list[bb_offset]); + } else + loctl1 = loctl0; + + tmp = ((u16)loctl0->q & 0xF); + tmp |= ((u16)loctl0->i & 0xF) << 4; + tmp |= ((u16)loctl1->q & 0xF) << 8; + tmp |= ((u16)loctl1->i & 0xF) << 12;//FIXME? + bcm43xx_phy_write(dev, 0x3A0 + (i / 2), + tmp); + } +} + +static void hardware_pctl_init_aphy(struct bcm43xx_wldev *dev) +{ + //TODO +} + +static void hardware_pctl_init_gphy(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + + bcm43xx_phy_write(dev, 0x0036, + (bcm43xx_phy_read(dev, 0x0036) & 0xFFC0) + | (phy->tgt_idle_tssi - phy->cur_idle_tssi)); + bcm43xx_phy_write(dev, 0x0478, + (bcm43xx_phy_read(dev, 0x0478) & 0xFF00) + | (phy->tgt_idle_tssi - phy->cur_idle_tssi)); + bcm43xx_gphy_tssi_power_lt_init(dev); + bcm43xx_gphy_gain_lt_init(dev); + bcm43xx_phy_write(dev, 0x0060, + bcm43xx_phy_read(dev, 0x0060) & 0xFFBF); + bcm43xx_phy_write(dev, 0x0014, 0x0000); + + assert(phy->rev >= 6); + bcm43xx_phy_write(dev, 0x0478, + bcm43xx_phy_read(dev, 0x0478) + | 0x0800); + bcm43xx_phy_write(dev, 0x0478, + bcm43xx_phy_read(dev, 0x0478) + & 0xFEFF); + bcm43xx_phy_write(dev, 0x0801, + bcm43xx_phy_read(dev, 0x0801) + & 0xFFBF); + + bcm43xx_gphy_dc_lt_init(dev); +} + +/* HardwarePowerControl for A and G PHY. + * This does nothing, if the card does not have HW PCTL + */ +static void bcm43xx_hardware_pctl_init(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + + if (!has_hardware_pctl(phy)) + return; + if (phy->type == BCM43xx_PHYTYPE_A) { + hardware_pctl_init_aphy(dev); + return; + } + if (phy->type == BCM43xx_PHYTYPE_G) { + hardware_pctl_init_gphy(dev); + return; + } + assert(0); +} + +static void bcm43xx_hardware_pctl_early_init(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + + if (!has_hardware_pctl(phy)) { + bcm43xx_phy_write(dev, 0x047A, 0xC111); + return; + } + + bcm43xx_phy_write(dev, 0x0036, + bcm43xx_phy_read(dev, 0x0036) & 0xFEFF); + bcm43xx_phy_write(dev, 0x002F, 0x0202); + bcm43xx_phy_write(dev, 0x047C, + bcm43xx_phy_read(dev, 0x047C) | 0x0002); + bcm43xx_phy_write(dev, 0x047A, + bcm43xx_phy_read(dev, 0x047A) | 0xF000); + if (phy->radio_ver == 0x2050 && phy->radio_rev == 8) { + bcm43xx_phy_write(dev, 0x047A, + (bcm43xx_phy_read(dev, 0x047A) + & 0xFF0F) | 0x0010); + bcm43xx_phy_write(dev, 0x005D, + bcm43xx_phy_read(dev, 0x005D) + | 0x8000); + bcm43xx_phy_write(dev, 0x004E, + (bcm43xx_phy_read(dev, 0x004E) + & 0xFFC0) | 0x0010); + bcm43xx_phy_write(dev, 0x002E, 0xC07F); + bcm43xx_phy_write(dev, 0x0036, + bcm43xx_phy_read(dev, 0x0036) + | 0x0400); + } else { + bcm43xx_phy_write(dev, 0x0036, + bcm43xx_phy_read(dev, 0x0036) + | 0x0200); + bcm43xx_phy_write(dev, 0x0036, + bcm43xx_phy_read(dev, 0x0036) + | 0x0400); + bcm43xx_phy_write(dev, 0x005D, + bcm43xx_phy_read(dev, 0x005D) + & 0x7FFF); + bcm43xx_phy_write(dev, 0x004F, + bcm43xx_phy_read(dev, 0x004F) + & 0xFFFE); + bcm43xx_phy_write(dev, 0x004E, + (bcm43xx_phy_read(dev, 0x004E) + & 0xFFC0) | 0x0010); + bcm43xx_phy_write(dev, 0x002E, 0xC07F); + bcm43xx_phy_write(dev, 0x047A, + (bcm43xx_phy_read(dev, 0x047A) + & 0xFF0F) | 0x0010); + } +} + +/* Intialize B/G PHY power control + * as described in http://bcm-specs.sipsolutions.net/InitPowerControl + */ +static void bcm43xx_phy_init_pctl(struct bcm43xx_wldev *dev) +{ + struct ssb_bus *bus = dev->dev->bus; + struct bcm43xx_phy *phy = &dev->phy; + + if ((bus->board_vendor == SSB_BOARDVENDOR_BCM) && + (bus->board_type == SSB_BOARD_BU4306)) + return; + + bcm43xx_phy_write(dev, 0x0028, 0x8018); + + /* This does something with the Analog... */ + bcm43xx_write16(dev, BCM43xx_MMIO_PHY0, + bcm43xx_read16(dev, BCM43xx_MMIO_PHY0) + & 0xFFDF); + + if (phy->type == BCM43xx_PHYTYPE_G && !phy->gmode) + return; + bcm43xx_hardware_pctl_early_init(dev); + if (phy->cur_idle_tssi == 0) { + if (phy->radio_ver == 0x2050 && phy->analog == 0) { + bcm43xx_radio_write16(dev, 0x0076, + (bcm43xx_radio_read16(dev, 0x0076) + & 0x00F7) | 0x0084); + } else { + if (phy->radio_rev == 8) + bcm43xx_radio_set_txpower_bg(dev, 0xB, 0x1F, 0); + else + bcm43xx_radio_set_txpower_bg(dev, 0xB, 9, 0); + } + bcm43xx_dummy_transmission(dev); + phy->cur_idle_tssi = bcm43xx_phy_read(dev, BCM43xx_PHY_ITSSI); + if (BCM43xx_DEBUG) { + /* Current-Idle-TSSI sanity check. */ + if (abs(phy->cur_idle_tssi - phy->tgt_idle_tssi) >= 20) { + dprintk(KERN_ERR PFX "!WARNING! Idle-TSSI phy->cur_idle_tssi " + "measuring failed. (cur=%d, tgt=%d). Disabling TX power " + "adjustment.\n", phy->cur_idle_tssi, phy->tgt_idle_tssi); + phy->cur_idle_tssi = 0; + } + } + + if (phy->radio_ver == 0x2050 && phy->analog == 0) { + bcm43xx_radio_write16(dev, 0x0076, + bcm43xx_radio_read16(dev, 0x0076) + & 0xFF7B); + } else + bcm43xx_radio_set_txpower_bg(dev, -1, -1, -1); + } + bcm43xx_hardware_pctl_init(dev); + bcm43xx_shm_clear_tssi(dev); +} + +static void bcm43xx_phy_agcsetup(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + u16 offset = 0x0000; + + if (phy->rev == 1) + offset = 0x4C00; + + bcm43xx_ofdmtab_write16(dev, offset, 0, 0x00FE); + bcm43xx_ofdmtab_write16(dev, offset, 1, 0x000D); + bcm43xx_ofdmtab_write16(dev, offset, 2, 0x0013); + bcm43xx_ofdmtab_write16(dev, offset, 3, 0x0019); + + if (phy->rev == 1) { + bcm43xx_ofdmtab_write16(dev, 0x1800, 0, 0x2710); + bcm43xx_ofdmtab_write16(dev, 0x1801, 0, 0x9B83); + bcm43xx_ofdmtab_write16(dev, 0x1802, 0, 0x9B83); + bcm43xx_ofdmtab_write16(dev, 0x1803, 0, 0x0F8D); + bcm43xx_phy_write(dev, 0x0455, 0x0004); + } + + bcm43xx_phy_write(dev, 0x04A5, + (bcm43xx_phy_read(dev, 0x04A5) + & 0x00FF) | 0x5700); + bcm43xx_phy_write(dev, 0x041A, + (bcm43xx_phy_read(dev, 0x041A) + & 0xFF80) | 0x000F); + bcm43xx_phy_write(dev, 0x041A, + (bcm43xx_phy_read(dev, 0x041A) + & 0xC07F) | 0x2B80); + bcm43xx_phy_write(dev, 0x048C, + (bcm43xx_phy_read(dev, 0x048C) + & 0xF0FF) | 0x0300); + + bcm43xx_radio_write16(dev, 0x007A, + bcm43xx_radio_read16(dev, 0x007A) + | 0x0008); + + bcm43xx_phy_write(dev, 0x04A0, + (bcm43xx_phy_read(dev, 0x04A0) + & 0xFFF0) | 0x0008); + bcm43xx_phy_write(dev, 0x04A1, + (bcm43xx_phy_read(dev, 0x04A1) + & 0xF0FF) | 0x0600); + bcm43xx_phy_write(dev, 0x0412, + (bcm43xx_phy_read(dev, 0x0412) + & 0xF0FF) | 0x0700); + bcm43xx_phy_write(dev, 0x0410, + (bcm43xx_phy_read(dev, 0x0410) + & 0xF0FF) | 0x0100); + + if (phy->rev == 1) { + bcm43xx_phy_write(dev, 0x04A2, + (bcm43xx_phy_read(dev, 0x04A2) + & 0xFFF0) | 0x0007); + } + + bcm43xx_phy_write(dev, 0x0488, + (bcm43xx_phy_read(dev, 0x0488) + & 0xFF00) | 0x001C); + bcm43xx_phy_write(dev, 0x0488, + (bcm43xx_phy_read(dev, 0x0488) + & 0xC0FF) | 0x0200); + bcm43xx_phy_write(dev, 0x0496, + (bcm43xx_phy_read(dev, 0x0496) + & 0xFF00) | 0x001C); + bcm43xx_phy_write(dev, 0x0489, + (bcm43xx_phy_read(dev, 0x0489) + & 0xFF00) | 0x0020); + bcm43xx_phy_write(dev, 0x0489, + (bcm43xx_phy_read(dev, 0x0489) + & 0xC0FF) | 0x0200); + bcm43xx_phy_write(dev, 0x0482, + (bcm43xx_phy_read(dev, 0x0482) + & 0xFF00) | 0x002E); + bcm43xx_phy_write(dev, 0x0496, + (bcm43xx_phy_read(dev, 0x0496) + & 0x00FF) | 0x1A00); + bcm43xx_phy_write(dev, 0x0481, + (bcm43xx_phy_read(dev, 0x0481) + & 0xFF00) | 0x0028); + bcm43xx_phy_write(dev, 0x0481, + (bcm43xx_phy_read(dev, 0x0481) + & 0x00FF) | 0x2C00); + + if (phy->rev == 1) { + bcm43xx_phy_write(dev, 0x0430, 0x092B); + bcm43xx_phy_write(dev, 0x041B, + (bcm43xx_phy_read(dev, 0x041B) + & 0xFFE1) | 0x0002); + } else { + bcm43xx_phy_write(dev, 0x041B, + bcm43xx_phy_read(dev, 0x041B) + & 0xFFE1); + bcm43xx_phy_write(dev, 0x041F, 0x287A); + bcm43xx_phy_write(dev, 0x0420, + (bcm43xx_phy_read(dev, 0x0420) + & 0xFFF0) | 0x0004); + } + + if (phy->rev >= 6) { + bcm43xx_phy_write(dev, 0x0422, 0x287A); + bcm43xx_phy_write(dev, 0x0420, + (bcm43xx_phy_read(dev, 0x0420) + & 0x0FFF) | 0x3000); + } + + bcm43xx_phy_write(dev, 0x04A8, + (bcm43xx_phy_read(dev, 0x04A8) + & 0x8080) | 0x7874); + bcm43xx_phy_write(dev, 0x048E, 0x1C00); + + offset = 0x0800; + if (phy->rev == 1) { + offset = 0x5400; + bcm43xx_phy_write(dev, 0x04AB, + (bcm43xx_phy_read(dev, 0x04AB) + & 0xF0FF) | 0x0600); + bcm43xx_phy_write(dev, 0x048B, 0x005E); + bcm43xx_phy_write(dev, 0x048C, + (bcm43xx_phy_read(dev, 0x048C) + & 0xFF00) | 0x001E); + bcm43xx_phy_write(dev, 0x048D, 0x0002); + } + bcm43xx_ofdmtab_write16(dev, offset, 0, 0x00); + bcm43xx_ofdmtab_write16(dev, offset, 1, 0x07); + bcm43xx_ofdmtab_write16(dev, offset, 2, 0x10); + bcm43xx_ofdmtab_write16(dev, offset, 3, 0x1C); + + if (phy->rev >= 6) { + bcm43xx_phy_write(dev, 0x0426, + bcm43xx_phy_read(dev, 0x0426) + & 0xFFFC); + bcm43xx_phy_write(dev, 0x0426, + bcm43xx_phy_read(dev, 0x0426) + & 0xEFFF); + } +} + +static void bcm43xx_phy_setupg(struct bcm43xx_wldev *dev) +{ + struct ssb_bus *bus = dev->dev->bus; + struct bcm43xx_phy *phy = &dev->phy; + u16 i; + + assert(phy->type == BCM43xx_PHYTYPE_G); + if (phy->rev == 1) { + bcm43xx_phy_write(dev, 0x0406, 0x4F19); + bcm43xx_phy_write(dev, BCM43xx_PHY_G_CRS, + (bcm43xx_phy_read(dev, BCM43xx_PHY_G_CRS) & 0xFC3F) | 0x0340); + bcm43xx_phy_write(dev, 0x042C, 0x005A); + bcm43xx_phy_write(dev, 0x0427, 0x001A); + + for (i = 0; i < BCM43xx_TAB_FINEFREQG_SIZE; i++) + bcm43xx_ofdmtab_write16(dev, 0x5800, i, bcm43xx_tab_finefreqg[i]); + for (i = 0; i < BCM43xx_TAB_NOISEG1_SIZE; i++) + bcm43xx_ofdmtab_write16(dev, 0x1800, i, bcm43xx_tab_noiseg1[i]); + for (i = 0; i < BCM43xx_TAB_ROTOR_SIZE; i++) + bcm43xx_ofdmtab_write16(dev, 0x2000, i, bcm43xx_tab_rotor[i]); + } else { + /* nrssi values are signed 6-bit values. Not sure why we write 0x7654 here... */ + bcm43xx_nrssi_hw_write(dev, 0xBA98, (s16)0x7654); + + if (phy->rev == 2) { + bcm43xx_phy_write(dev, 0x04C0, 0x1861); + bcm43xx_phy_write(dev, 0x04C1, 0x0271); + } else if (phy->rev > 2) { + bcm43xx_phy_write(dev, 0x04C0, 0x0098); + bcm43xx_phy_write(dev, 0x04C1, 0x0070); + bcm43xx_phy_write(dev, 0x04C9, 0x0080); + } + bcm43xx_phy_write(dev, 0x042B, bcm43xx_phy_read(dev, 0x042B) | 0x800); + + for (i = 0; i < 64; i++) + bcm43xx_ofdmtab_write16(dev, 0x4000, i, i); + for (i = 0; i < BCM43xx_TAB_NOISEG2_SIZE; i++) + bcm43xx_ofdmtab_write16(dev, 0x1800, i, bcm43xx_tab_noiseg2[i]); + } + + if (phy->rev <= 2) + for (i = 0; i < BCM43xx_TAB_NOISESCALEG_SIZE; i++) + bcm43xx_ofdmtab_write16(dev, 0x1400, i, bcm43xx_tab_noisescaleg1[i]); + else if ((phy->rev == 7) && (bcm43xx_phy_read(dev, 0x0449) & 0x0200)) + for (i = 0; i < BCM43xx_TAB_NOISESCALEG_SIZE; i++) + bcm43xx_ofdmtab_write16(dev, 0x1400, i, bcm43xx_tab_noisescaleg3[i]); + else + for (i = 0; i < BCM43xx_TAB_NOISESCALEG_SIZE; i++) + bcm43xx_ofdmtab_write16(dev, 0x1400, i, bcm43xx_tab_noisescaleg2[i]); + + if (phy->rev == 2) + for (i = 0; i < BCM43xx_TAB_SIGMASQR_SIZE; i++) + bcm43xx_ofdmtab_write16(dev, 0x5000, i, bcm43xx_tab_sigmasqr1[i]); + else if ((phy->rev > 2) && (phy->rev <= 7)) + for (i = 0; i < BCM43xx_TAB_SIGMASQR_SIZE; i++) + bcm43xx_ofdmtab_write16(dev, 0x5000, i, bcm43xx_tab_sigmasqr2[i]); + + if (phy->rev == 1) { + for (i = 0; i < BCM43xx_TAB_RETARD_SIZE; i++) + bcm43xx_ofdmtab_write32(dev, 0x2400, i, bcm43xx_tab_retard[i]); + for (i = 0; i < 4; i++) { + bcm43xx_ofdmtab_write16(dev, 0x5404, i, 0x0020); + bcm43xx_ofdmtab_write16(dev, 0x5408, i, 0x0020); + bcm43xx_ofdmtab_write16(dev, 0x540C, i, 0x0020); + bcm43xx_ofdmtab_write16(dev, 0x5410, i, 0x0020); + } + bcm43xx_phy_agcsetup(dev); + + if ((bus->board_vendor == SSB_BOARDVENDOR_BCM) && + (bus->board_type == SSB_BOARD_BU4306) && + (bus->board_rev == 0x17)) + return; + + bcm43xx_ofdmtab_write16(dev, 0x5001, 0, 0x0002); + bcm43xx_ofdmtab_write16(dev, 0x5002, 0, 0x0001); + } else { + for (i = 0; i <= 0x2F; i++) + bcm43xx_ofdmtab_write16(dev, 0x1000, i, 0x0820); + bcm43xx_phy_agcsetup(dev); + bcm43xx_phy_read(dev, 0x0400); /* dummy read */ + bcm43xx_phy_write(dev, 0x0403, 0x1000); + bcm43xx_ofdmtab_write16(dev, 0x3C02, 0, 0x000F); + bcm43xx_ofdmtab_write16(dev, 0x3C03, 0, 0x0014); + + if ((bus->board_vendor == SSB_BOARDVENDOR_BCM) && + (bus->board_type == SSB_BOARD_BU4306) && + (bus->board_rev == 0x17)) + return; + + bcm43xx_ofdmtab_write16(dev, 0x0401, 0, 0x0002); + bcm43xx_ofdmtab_write16(dev, 0x0402, 0, 0x0001); + } +} + +/* Initialize the noisescaletable for APHY */ +static void bcm43xx_phy_init_noisescaletbl(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + int i; + + for (i = 0; i < 12; i++) { + if (phy->rev == 2) + bcm43xx_ofdmtab_write16(dev, 0x1400, i, 0x6767); + else + bcm43xx_ofdmtab_write16(dev, 0x1400, i, 0x2323); + } + if (phy->rev == 2) + bcm43xx_ofdmtab_write16(dev, 0x1400, i, 0x6700); + else + bcm43xx_ofdmtab_write16(dev, 0x1400, i, 0x2300); + for (i = 0; i < 11; i++) { + if (phy->rev == 2) + bcm43xx_ofdmtab_write16(dev, 0x1400, i, 0x6767); + else + bcm43xx_ofdmtab_write16(dev, 0x1400, i, 0x2323); + } + if (phy->rev == 2) + bcm43xx_ofdmtab_write16(dev, 0x1400, i, 0x0067); + else + bcm43xx_ofdmtab_write16(dev, 0x1400, i, 0x0023); +} + +static void bcm43xx_phy_setupa(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + u16 i; + + assert(phy->type == BCM43xx_PHYTYPE_A); + switch (phy->rev) { + case 2: + bcm43xx_phy_write(dev, 0x008E, 0x3800); + bcm43xx_phy_write(dev, 0x0035, 0x03FF); + bcm43xx_phy_write(dev, 0x0036, 0x0400); + + bcm43xx_ofdmtab_write16(dev, 0x3807, 0, 0x0051); + + bcm43xx_phy_write(dev, 0x001C, 0x0FF9); + bcm43xx_phy_write(dev, 0x0020, bcm43xx_phy_read(dev, 0x0020) & 0xFF0F); + bcm43xx_ofdmtab_write16(dev, 0x3C0C, 0, 0x07BF); + bcm43xx_radio_write16(dev, 0x0002, 0x07BF); + + bcm43xx_phy_write(dev, 0x0024, 0x4680); + bcm43xx_phy_write(dev, 0x0020, 0x0003); + bcm43xx_phy_write(dev, 0x001D, 0x0F40); + bcm43xx_phy_write(dev, 0x001F, 0x1C00); + + bcm43xx_phy_write(dev, 0x002A, + (bcm43xx_phy_read(dev, 0x002A) + & 0x00FF) | 0x0400); + bcm43xx_phy_write(dev, 0x002B, + bcm43xx_phy_read(dev, 0x002B) + & 0xFBFF); + bcm43xx_phy_write(dev, 0x008E, 0x58C1); + + bcm43xx_ofdmtab_write16(dev, 0x0803, 0, 0x000F); + bcm43xx_ofdmtab_write16(dev, 0x0804, 0, 0x001F); + bcm43xx_ofdmtab_write16(dev, 0x0805, 0, 0x002A); + bcm43xx_ofdmtab_write16(dev, 0x0805, 0, 0x0030); + bcm43xx_ofdmtab_write16(dev, 0x0807, 0, 0x003A); + + bcm43xx_ofdmtab_write16(dev, 0x0000, 0, 0x0013); + bcm43xx_ofdmtab_write16(dev, 0x0000, 1, 0x0013); + bcm43xx_ofdmtab_write16(dev, 0x0000, 2, 0x0013); + bcm43xx_ofdmtab_write16(dev, 0x0000, 3, 0x0013); + bcm43xx_ofdmtab_write16(dev, 0x0000, 4, 0x0015); + bcm43xx_ofdmtab_write16(dev, 0x0000, 5, 0x0015); + bcm43xx_ofdmtab_write16(dev, 0x0000, 6, 0x0019); + + bcm43xx_ofdmtab_write16(dev, 0x0404, 0, 0x0003); + bcm43xx_ofdmtab_write16(dev, 0x0405, 0, 0x0003); + bcm43xx_ofdmtab_write16(dev, 0x0406, 0, 0x0007); + + for (i = 0; i < 16; i++) + bcm43xx_ofdmtab_write16(dev, 0x4000, i, (0x8 + i) & 0x000F); + + bcm43xx_ofdmtab_write16(dev, 0x3003, 0, 0x1044); + bcm43xx_ofdmtab_write16(dev, 0x3004, 0, 0x7201); + bcm43xx_ofdmtab_write16(dev, 0x3006, 0, 0x0040); + bcm43xx_ofdmtab_write16(dev, 0x3001, 0, (bcm43xx_ofdmtab_read16(dev, 0x3001, 0) & 0x0010) | 0x0008); + + for (i = 0; i < BCM43xx_TAB_FINEFREQA_SIZE; i++) + bcm43xx_ofdmtab_write16(dev, 0x5800, i, bcm43xx_tab_finefreqa[i]); + for (i = 0; i < BCM43xx_TAB_NOISEA2_SIZE; i++) + bcm43xx_ofdmtab_write16(dev, 0x1800, i, bcm43xx_tab_noisea2[i]); + for (i = 0; i < BCM43xx_TAB_ROTOR_SIZE; i++) + bcm43xx_ofdmtab_write32(dev, 0x2000, i, bcm43xx_tab_rotor[i]); + bcm43xx_phy_init_noisescaletbl(dev); + for (i = 0; i < BCM43xx_TAB_RETARD_SIZE; i++) + bcm43xx_ofdmtab_write32(dev, 0x2400, i, bcm43xx_tab_retard[i]); + break; + case 3: + for (i = 0; i < 64; i++) + bcm43xx_ofdmtab_write16(dev, 0x4000, i, i); + + bcm43xx_ofdmtab_write16(dev, 0x3807, 0, 0x0051); + + bcm43xx_phy_write(dev, 0x001C, 0x0FF9); + bcm43xx_phy_write(dev, 0x0020, + bcm43xx_phy_read(dev, 0x0020) & 0xFF0F); + bcm43xx_radio_write16(dev, 0x0002, 0x07BF); + + bcm43xx_phy_write(dev, 0x0024, 0x4680); + bcm43xx_phy_write(dev, 0x0020, 0x0003); + bcm43xx_phy_write(dev, 0x001D, 0x0F40); + bcm43xx_phy_write(dev, 0x001F, 0x1C00); + bcm43xx_phy_write(dev, 0x002A, + (bcm43xx_phy_read(dev, 0x002A) + & 0x00FF) | 0x0400); + + bcm43xx_ofdmtab_write16(dev, 0x3000, 1, + (bcm43xx_ofdmtab_read16(dev, 0x3000, 1) + & 0x0010) | 0x0008); + for (i = 0; i < BCM43xx_TAB_NOISEA3_SIZE; i++) { + bcm43xx_ofdmtab_write16(dev, 0x1800, i, + bcm43xx_tab_noisea3[i]); + } + bcm43xx_phy_init_noisescaletbl(dev); + for (i = 0; i < BCM43xx_TAB_SIGMASQR_SIZE; i++) { + bcm43xx_ofdmtab_write16(dev, 0x5000, i, + bcm43xx_tab_sigmasqr1[i]); + } + + bcm43xx_phy_write(dev, 0x0003, 0x1808); + + bcm43xx_ofdmtab_write16(dev, 0x0803, 0, 0x000F); + bcm43xx_ofdmtab_write16(dev, 0x0804, 0, 0x001F); + bcm43xx_ofdmtab_write16(dev, 0x0805, 0, 0x002A); + bcm43xx_ofdmtab_write16(dev, 0x0805, 0, 0x0030); + bcm43xx_ofdmtab_write16(dev, 0x0807, 0, 0x003A); + + bcm43xx_ofdmtab_write16(dev, 0x0000, 0, 0x0013); + bcm43xx_ofdmtab_write16(dev, 0x0001, 0, 0x0013); + bcm43xx_ofdmtab_write16(dev, 0x0002, 0, 0x0013); + bcm43xx_ofdmtab_write16(dev, 0x0003, 0, 0x0013); + bcm43xx_ofdmtab_write16(dev, 0x0004, 0, 0x0015); + bcm43xx_ofdmtab_write16(dev, 0x0005, 0, 0x0015); + bcm43xx_ofdmtab_write16(dev, 0x0006, 0, 0x0019); + + bcm43xx_ofdmtab_write16(dev, 0x0404, 0, 0x0003); + bcm43xx_ofdmtab_write16(dev, 0x0405, 0, 0x0003); + bcm43xx_ofdmtab_write16(dev, 0x0406, 0, 0x0007); + + bcm43xx_ofdmtab_write16(dev, 0x3C02, 0, 0x000F); + bcm43xx_ofdmtab_write16(dev, 0x3C03, 0, 0x0014); + break; + default: + assert(0); + } +} + +/* Initialize APHY. This is also called for the GPHY in some cases. */ +static void bcm43xx_phy_inita(struct bcm43xx_wldev *dev) +{ + struct ssb_bus *bus = dev->dev->bus; + struct bcm43xx_phy *phy = &dev->phy; + u16 tval; + + if (phy->type == BCM43xx_PHYTYPE_A) { + bcm43xx_phy_setupa(dev); + } else { + bcm43xx_phy_setupg(dev); + if (phy->gmode && + (dev->dev->bus->sprom.r1.boardflags_lo & BCM43xx_BFL_PACTRL)) + bcm43xx_phy_write(dev, 0x046E, 0x03CF); + return; + } + + bcm43xx_phy_write(dev, BCM43xx_PHY_A_CRS, + (bcm43xx_phy_read(dev, BCM43xx_PHY_A_CRS) & 0xF83C) | 0x0340); + bcm43xx_phy_write(dev, 0x0034, 0x0001); + + TODO();//TODO: RSSI AGC + bcm43xx_phy_write(dev, BCM43xx_PHY_A_CRS, + bcm43xx_phy_read(dev, BCM43xx_PHY_A_CRS) | (1 << 14)); + bcm43xx_radio_init2060(dev); + + if ((bus->board_vendor == SSB_BOARDVENDOR_BCM) && + ((bus->board_type == SSB_BOARD_BU4306) || + (bus->board_type == SSB_BOARD_BU4309))) { + if (phy->lofcal == 0xFFFF) { + TODO();//TODO: LOF Cal + bcm43xx_radio_set_tx_iq(dev); + } else + bcm43xx_radio_write16(dev, 0x001E, phy->lofcal); + } + + bcm43xx_phy_write(dev, 0x007A, 0xF111); + + if (phy->cur_idle_tssi == 0) { + bcm43xx_radio_write16(dev, 0x0019, 0x0000); + bcm43xx_radio_write16(dev, 0x0017, 0x0020); + + tval = bcm43xx_ofdmtab_read16(dev, 0x3001, 0); + if (phy->rev == 1) { + bcm43xx_ofdmtab_write16(dev, 0x3001, 0, + (bcm43xx_ofdmtab_read16(dev, 0x3001, 0) & 0xFF87) + | 0x0058); + } else { + bcm43xx_ofdmtab_write16(dev, 0x3001, 0, + (bcm43xx_ofdmtab_read16(dev, 0x3001, 0) & 0xFFC3) + | 0x002C); + } + bcm43xx_dummy_transmission(dev); + phy->cur_idle_tssi = bcm43xx_phy_read(dev, BCM43xx_PHY_A_PCTL); + bcm43xx_ofdmtab_write16(dev, 0x3001, 0, tval); + + bcm43xx_radio_set_txpower_a(dev, 0x0018); + } + bcm43xx_shm_clear_tssi(dev); +} + +static void bcm43xx_phy_initb2(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + u16 offset, val; + + bcm43xx_write16(dev, 0x03EC, 0x3F22); + bcm43xx_phy_write(dev, 0x0020, 0x301C); + bcm43xx_phy_write(dev, 0x0026, 0x0000); + bcm43xx_phy_write(dev, 0x0030, 0x00C6); + bcm43xx_phy_write(dev, 0x0088, 0x3E00); + val = 0x3C3D; + for (offset = 0x0089; offset < 0x00A7; offset++) { + bcm43xx_phy_write(dev, offset, val); + val -= 0x0202; + } + bcm43xx_phy_write(dev, 0x03E4, 0x3000); + if (phy->channel == 0xFF) + bcm43xx_radio_selectchannel(dev, BCM43xx_DEFAULT_CHANNEL_BG, 0); + else + bcm43xx_radio_selectchannel(dev, phy->channel, 0); + if (phy->radio_ver != 0x2050) { + bcm43xx_radio_write16(dev, 0x0075, 0x0080); + bcm43xx_radio_write16(dev, 0x0079, 0x0081); + } + bcm43xx_radio_write16(dev, 0x0050, 0x0020); + bcm43xx_radio_write16(dev, 0x0050, 0x0023); + if (phy->radio_ver == 0x2050) { + bcm43xx_radio_write16(dev, 0x0050, 0x0020); + bcm43xx_radio_write16(dev, 0x005A, 0x0070); + bcm43xx_radio_write16(dev, 0x005B, 0x007B); + bcm43xx_radio_write16(dev, 0x005C, 0x00B0); + bcm43xx_radio_write16(dev, 0x007A, 0x000F); + bcm43xx_phy_write(dev, 0x0038, 0x0677); + bcm43xx_radio_init2050(dev); + } + bcm43xx_phy_write(dev, 0x0014, 0x0080); + bcm43xx_phy_write(dev, 0x0032, 0x00CA); + bcm43xx_phy_write(dev, 0x0032, 0x00CC); + bcm43xx_phy_write(dev, 0x0035, 0x07C2); + bcm43xx_lo_b_measure(dev); + bcm43xx_phy_write(dev, 0x0026, 0xCC00); + if (phy->radio_ver != 0x2050) + bcm43xx_phy_write(dev, 0x0026, 0xCE00); + bcm43xx_write16(dev, BCM43xx_MMIO_CHANNEL_EXT, 0x1000); + bcm43xx_phy_write(dev, 0x002A, 0x88A3); + if (phy->radio_ver != 0x2050) + bcm43xx_phy_write(dev, 0x002A, 0x88C2); + bcm43xx_radio_set_txpower_bg(dev, -1, -1, -1); + bcm43xx_phy_init_pctl(dev); +} + +static void bcm43xx_phy_initb4(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + u16 offset, val; + + bcm43xx_write16(dev, 0x03EC, 0x3F22); + bcm43xx_phy_write(dev, 0x0020, 0x301C); + bcm43xx_phy_write(dev, 0x0026, 0x0000); + bcm43xx_phy_write(dev, 0x0030, 0x00C6); + bcm43xx_phy_write(dev, 0x0088, 0x3E00); + val = 0x3C3D; + for (offset = 0x0089; offset < 0x00A7; offset++) { + bcm43xx_phy_write(dev, offset, val); + val -= 0x0202; + } + bcm43xx_phy_write(dev, 0x03E4, 0x3000); + if (phy->channel == 0xFF) + bcm43xx_radio_selectchannel(dev, BCM43xx_DEFAULT_CHANNEL_BG, 0); + else + bcm43xx_radio_selectchannel(dev, phy->channel, 0); + if (phy->radio_ver != 0x2050) { + bcm43xx_radio_write16(dev, 0x0075, 0x0080); + bcm43xx_radio_write16(dev, 0x0079, 0x0081); + } + bcm43xx_radio_write16(dev, 0x0050, 0x0020); + bcm43xx_radio_write16(dev, 0x0050, 0x0023); + if (phy->radio_ver == 0x2050) { + bcm43xx_radio_write16(dev, 0x0050, 0x0020); + bcm43xx_radio_write16(dev, 0x005A, 0x0070); + bcm43xx_radio_write16(dev, 0x005B, 0x007B); + bcm43xx_radio_write16(dev, 0x005C, 0x00B0); + bcm43xx_radio_write16(dev, 0x007A, 0x000F); + bcm43xx_phy_write(dev, 0x0038, 0x0677); + bcm43xx_radio_init2050(dev); + } + bcm43xx_phy_write(dev, 0x0014, 0x0080); + bcm43xx_phy_write(dev, 0x0032, 0x00CA); + if (phy->radio_ver == 0x2050) + bcm43xx_phy_write(dev, 0x0032, 0x00E0); + bcm43xx_phy_write(dev, 0x0035, 0x07C2); + + bcm43xx_lo_b_measure(dev); + + bcm43xx_phy_write(dev, 0x0026, 0xCC00); + if (phy->radio_ver == 0x2050) + bcm43xx_phy_write(dev, 0x0026, 0xCE00); + bcm43xx_write16(dev, BCM43xx_MMIO_CHANNEL_EXT, 0x1100); + bcm43xx_phy_write(dev, 0x002A, 0x88A3); + if (phy->radio_ver == 0x2050) + bcm43xx_phy_write(dev, 0x002A, 0x88C2); + bcm43xx_radio_set_txpower_bg(dev, 0xFFFF, 0xFFFF, 0xFFFF); + if (dev->dev->bus->sprom.r1.boardflags_lo & BCM43xx_BFL_RSSI) { + bcm43xx_calc_nrssi_slope(dev); + bcm43xx_calc_nrssi_threshold(dev); + } + bcm43xx_phy_init_pctl(dev); +} + +static void bcm43xx_phy_initb5(struct bcm43xx_wldev *dev) +{ + struct ssb_bus *bus = dev->dev->bus; + struct bcm43xx_phy *phy = &dev->phy; + u16 offset, value; + u8 old_channel; + + if (phy->analog == 1) { + bcm43xx_radio_write16(dev, 0x007A, + bcm43xx_radio_read16(dev, 0x007A) + | 0x0050); + } + if ((bus->board_vendor != SSB_BOARDVENDOR_BCM) && + (bus->board_type != SSB_BOARD_BU4306)) { + value = 0x2120; + for (offset = 0x00A8 ; offset < 0x00C7; offset++) { + bcm43xx_phy_write(dev, offset, value); + value += 0x202; + } + } + bcm43xx_phy_write(dev, 0x0035, + (bcm43xx_phy_read(dev, 0x0035) & 0xF0FF) + | 0x0700); + if (phy->radio_ver == 0x2050) + bcm43xx_phy_write(dev, 0x0038, 0x0667); + + if (phy->gmode || phy->rev >= 2) { + if (phy->radio_ver == 0x2050) { + bcm43xx_radio_write16(dev, 0x007A, + bcm43xx_radio_read16(dev, 0x007A) + | 0x0020); + bcm43xx_radio_write16(dev, 0x0051, + bcm43xx_radio_read16(dev, 0x0051) + | 0x0004); + } + bcm43xx_write16(dev, BCM43xx_MMIO_PHY_RADIO, 0x0000); + + bcm43xx_phy_write(dev, 0x0802, bcm43xx_phy_read(dev, 0x0802) | 0x0100); + bcm43xx_phy_write(dev, 0x042B, bcm43xx_phy_read(dev, 0x042B) | 0x2000); + + bcm43xx_phy_write(dev, 0x001C, 0x186A); + + bcm43xx_phy_write(dev, 0x0013, (bcm43xx_phy_read(dev, 0x0013) & 0x00FF) | 0x1900); + bcm43xx_phy_write(dev, 0x0035, (bcm43xx_phy_read(dev, 0x0035) & 0xFFC0) | 0x0064); + bcm43xx_phy_write(dev, 0x005D, (bcm43xx_phy_read(dev, 0x005D) & 0xFF80) | 0x000A); + } + + if (dev->bad_frames_preempt) { + bcm43xx_phy_write(dev, BCM43xx_PHY_RADIO_BITFIELD, + bcm43xx_phy_read(dev, BCM43xx_PHY_RADIO_BITFIELD) | (1 << 11)); + } + + if (phy->analog == 1) { + bcm43xx_phy_write(dev, 0x0026, 0xCE00); + bcm43xx_phy_write(dev, 0x0021, 0x3763); + bcm43xx_phy_write(dev, 0x0022, 0x1BC3); + bcm43xx_phy_write(dev, 0x0023, 0x06F9); + bcm43xx_phy_write(dev, 0x0024, 0x037E); + } else + bcm43xx_phy_write(dev, 0x0026, 0xCC00); + bcm43xx_phy_write(dev, 0x0030, 0x00C6); + bcm43xx_write16(dev, 0x03EC, 0x3F22); + + if (phy->analog == 1) + bcm43xx_phy_write(dev, 0x0020, 0x3E1C); + else + bcm43xx_phy_write(dev, 0x0020, 0x301C); + + if (phy->analog == 0) + bcm43xx_write16(dev, 0x03E4, 0x3000); + + old_channel = phy->channel; + /* Force to channel 7, even if not supported. */ + bcm43xx_radio_selectchannel(dev, 7, 0); + + if (phy->radio_ver != 0x2050) { + bcm43xx_radio_write16(dev, 0x0075, 0x0080); + bcm43xx_radio_write16(dev, 0x0079, 0x0081); + } + + bcm43xx_radio_write16(dev, 0x0050, 0x0020); + bcm43xx_radio_write16(dev, 0x0050, 0x0023); + + if (phy->radio_ver == 0x2050) { + bcm43xx_radio_write16(dev, 0x0050, 0x0020); + bcm43xx_radio_write16(dev, 0x005A, 0x0070); + } + + bcm43xx_radio_write16(dev, 0x005B, 0x007B); + bcm43xx_radio_write16(dev, 0x005C, 0x00B0); + + bcm43xx_radio_write16(dev, 0x007A, bcm43xx_radio_read16(dev, 0x007A) | 0x0007); + + bcm43xx_radio_selectchannel(dev, old_channel, 0); + + bcm43xx_phy_write(dev, 0x0014, 0x0080); + bcm43xx_phy_write(dev, 0x0032, 0x00CA); + bcm43xx_phy_write(dev, 0x002A, 0x88A3); + + bcm43xx_radio_set_txpower_bg(dev, -1, -1, -1); + + if (phy->radio_ver == 0x2050) + bcm43xx_radio_write16(dev, 0x005D, 0x000D); + + bcm43xx_write16(dev, 0x03E4, (bcm43xx_read16(dev, 0x03E4) & 0xFFC0) | 0x0004); +} + +static void bcm43xx_phy_initb6(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + u16 offset, val; + u8 old_channel; + + bcm43xx_phy_write(dev, 0x003E, 0x817A); + bcm43xx_radio_write16(dev, 0x007A, + (bcm43xx_radio_read16(dev, 0x007A) | 0x0058)); + if (phy->radio_rev == 4 || phy->radio_rev == 5) { + bcm43xx_radio_write16(dev, 0x51, 0x37); + bcm43xx_radio_write16(dev, 0x52, 0x70); + bcm43xx_radio_write16(dev, 0x53, 0xB3); + bcm43xx_radio_write16(dev, 0x54, 0x9B); + bcm43xx_radio_write16(dev, 0x5A, 0x88); + bcm43xx_radio_write16(dev, 0x5B, 0x88); + bcm43xx_radio_write16(dev, 0x5D, 0x88); + bcm43xx_radio_write16(dev, 0x5E, 0x88); + bcm43xx_radio_write16(dev, 0x7D, 0x88); + bcm43xx_hf_write(dev, bcm43xx_hf_read(dev) + | BCM43xx_HF_TSSIRPSMW); + } + assert(phy->radio_rev != 6 && phy->radio_rev != 7); /* We had code for these revs here...*/ + if (phy->radio_rev == 8) { + bcm43xx_radio_write16(dev, 0x51, 0); + bcm43xx_radio_write16(dev, 0x52, 0x40); + bcm43xx_radio_write16(dev, 0x53, 0xB7); + bcm43xx_radio_write16(dev, 0x54, 0x98); + bcm43xx_radio_write16(dev, 0x5A, 0x88); + bcm43xx_radio_write16(dev, 0x5B, 0x6B); + bcm43xx_radio_write16(dev, 0x5C, 0x0F); + if (dev->dev->bus->sprom.r1.boardflags_lo & BCM43xx_BFL_ALTIQ) { + bcm43xx_radio_write16(dev, 0x5D, 0xFA); + bcm43xx_radio_write16(dev, 0x5E, 0xD8); + } else { + bcm43xx_radio_write16(dev, 0x5D, 0xF5); + bcm43xx_radio_write16(dev, 0x5E, 0xB8); + } + bcm43xx_radio_write16(dev, 0x0073, 0x0003); + bcm43xx_radio_write16(dev, 0x007D, 0x00A8); + bcm43xx_radio_write16(dev, 0x007C, 0x0001); + bcm43xx_radio_write16(dev, 0x007E, 0x0008); + } + val = 0x1E1F; + for (offset = 0x0088; offset < 0x0098; offset++) { + bcm43xx_phy_write(dev, offset, val); + val -= 0x0202; + } + val = 0x3E3F; + for (offset = 0x0098; offset < 0x00A8; offset++) { + bcm43xx_phy_write(dev, offset, val); + val -= 0x0202; + } + val = 0x2120; + for (offset = 0x00A8; offset < 0x00C8; offset++) { + bcm43xx_phy_write(dev, offset, (val & 0x3F3F)); + val += 0x0202; + } + if (phy->type == BCM43xx_PHYTYPE_G) { + bcm43xx_radio_write16(dev, 0x007A, + bcm43xx_radio_read16(dev, 0x007A) | 0x0020); + bcm43xx_radio_write16(dev, 0x0051, + bcm43xx_radio_read16(dev, 0x0051) | 0x0004); + bcm43xx_phy_write(dev, 0x0802, + bcm43xx_phy_read(dev, 0x0802) | 0x0100); + bcm43xx_phy_write(dev, 0x042B, + bcm43xx_phy_read(dev, 0x042B) | 0x2000); + bcm43xx_phy_write(dev, 0x5B, 0); + bcm43xx_phy_write(dev, 0x5C, 0); + } + + old_channel = phy->channel; + if (old_channel >= 8) + bcm43xx_radio_selectchannel(dev, 1, 0); + else + bcm43xx_radio_selectchannel(dev, 13, 0); + + bcm43xx_radio_write16(dev, 0x0050, 0x0020); + bcm43xx_radio_write16(dev, 0x0050, 0x0023); + udelay(40); + if (phy->radio_rev < 6 || phy->radio_rev == 8) { + bcm43xx_radio_write16(dev, 0x7C, + (bcm43xx_radio_read16(dev, 0x7C) + | 0x0002)); + bcm43xx_radio_write16(dev, 0x50, 0x20); + } + if (phy->radio_rev <= 2) { + bcm43xx_radio_write16(dev, 0x7C, 0x20); + bcm43xx_radio_write16(dev, 0x5A, 0x70); + bcm43xx_radio_write16(dev, 0x5B, 0x7B); + bcm43xx_radio_write16(dev, 0x5C, 0xB0); + } + bcm43xx_radio_write16(dev, 0x007A, + (bcm43xx_radio_read16(dev, 0x007A) & 0x00F8) | 0x0007); + + bcm43xx_radio_selectchannel(dev, old_channel, 0); + + bcm43xx_phy_write(dev, 0x0014, 0x0200); + if (phy->radio_rev >= 6) + bcm43xx_phy_write(dev, 0x2A, 0x88C2); + else + bcm43xx_phy_write(dev, 0x2A, 0x8AC0); + bcm43xx_phy_write(dev, 0x0038, 0x0668); + bcm43xx_radio_set_txpower_bg(dev, -1, -1, -1); + if (phy->radio_rev <= 5) { + bcm43xx_phy_write(dev, 0x5D, + (bcm43xx_phy_read(dev, 0x5D) + & 0xFF80) | 0x0003); + } + if (phy->radio_rev <= 2) + bcm43xx_radio_write16(dev, 0x005D, 0x000D); + + if (phy->analog == 4) { + bcm43xx_write16(dev, 0x3E4, 9); + bcm43xx_phy_write(dev, 0x61, + bcm43xx_phy_read(dev, 0x61) + & 0x0FFF); + } else { + bcm43xx_phy_write(dev, 0x0002, + (bcm43xx_phy_read(dev, 0x0002) & 0xFFC0) + | 0x0004); + } + if (phy->type == BCM43xx_PHYTYPE_B) { + bcm43xx_write16(dev, 0x03E6, 0x8140); + bcm43xx_phy_write(dev, 0x0016, 0x0410); + bcm43xx_phy_write(dev, 0x0017, 0x0820); + bcm43xx_phy_write(dev, 0x0062, 0x0007); + bcm43xx_radio_init2050(dev); + bcm43xx_lo_g_measure(dev); + if (dev->dev->bus->sprom.r1.boardflags_lo & BCM43xx_BFL_RSSI) { + bcm43xx_calc_nrssi_slope(dev); + bcm43xx_calc_nrssi_threshold(dev); + } + bcm43xx_phy_init_pctl(dev); + } else if (phy->type == BCM43xx_PHYTYPE_G) + bcm43xx_write16(dev, 0x03E6, 0x0); +} + +static void bcm43xx_calc_loopback_gain(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + u16 backup_phy[16] = {0}; + u16 backup_radio[3]; + u16 backup_bband; + u16 i, j, loop_i_max; + u16 trsw_rx; + u16 loop1_outer_done, loop1_inner_done; + + backup_phy[0] = bcm43xx_phy_read(dev, BCM43xx_PHY_CRS0); + backup_phy[1] = bcm43xx_phy_read(dev, BCM43xx_PHY_CCKBBANDCFG); + backup_phy[2] = bcm43xx_phy_read(dev, BCM43xx_PHY_RFOVER); + backup_phy[3] = bcm43xx_phy_read(dev, BCM43xx_PHY_RFOVERVAL); + if (phy->rev != 1) { /* Not in specs, but needed to prevent PPC machine check */ + backup_phy[4] = bcm43xx_phy_read(dev, BCM43xx_PHY_ANALOGOVER); + backup_phy[5] = bcm43xx_phy_read(dev, BCM43xx_PHY_ANALOGOVERVAL); + } + backup_phy[6] = bcm43xx_phy_read(dev, BCM43xx_PHY_BASE(0x5A)); + backup_phy[7] = bcm43xx_phy_read(dev, BCM43xx_PHY_BASE(0x59)); + backup_phy[8] = bcm43xx_phy_read(dev, BCM43xx_PHY_BASE(0x58)); + backup_phy[9] = bcm43xx_phy_read(dev, BCM43xx_PHY_BASE(0x0A)); + backup_phy[10] = bcm43xx_phy_read(dev, BCM43xx_PHY_BASE(0x03)); + backup_phy[11] = bcm43xx_phy_read(dev, BCM43xx_PHY_LO_MASK); + backup_phy[12] = bcm43xx_phy_read(dev, BCM43xx_PHY_LO_CTL); + backup_phy[13] = bcm43xx_phy_read(dev, BCM43xx_PHY_BASE(0x2B)); + backup_phy[14] = bcm43xx_phy_read(dev, BCM43xx_PHY_PGACTL); + backup_phy[15] = bcm43xx_phy_read(dev, BCM43xx_PHY_LO_LEAKAGE); + backup_bband = phy->bbatt; + backup_radio[0] = bcm43xx_radio_read16(dev, 0x52); + backup_radio[1] = bcm43xx_radio_read16(dev, 0x43); + backup_radio[2] = bcm43xx_radio_read16(dev, 0x7A); + + bcm43xx_phy_write(dev, BCM43xx_PHY_CRS0, + bcm43xx_phy_read(dev, BCM43xx_PHY_CRS0) & 0x3FFF); + bcm43xx_phy_write(dev, BCM43xx_PHY_CCKBBANDCFG, + bcm43xx_phy_read(dev, BCM43xx_PHY_CCKBBANDCFG) | 0x8000); + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVER, + bcm43xx_phy_read(dev, BCM43xx_PHY_RFOVER) | 0x0002); + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVERVAL, + bcm43xx_phy_read(dev, BCM43xx_PHY_RFOVERVAL) & 0xFFFD); + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVER, + bcm43xx_phy_read(dev, BCM43xx_PHY_RFOVER) | 0x0001); + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVERVAL, + bcm43xx_phy_read(dev, BCM43xx_PHY_RFOVERVAL) & 0xFFFE); + if (phy->rev != 1) { /* Not in specs, but needed to prevent PPC machine check */ + bcm43xx_phy_write(dev, BCM43xx_PHY_ANALOGOVER, + bcm43xx_phy_read(dev, BCM43xx_PHY_ANALOGOVER) | 0x0001); + bcm43xx_phy_write(dev, BCM43xx_PHY_ANALOGOVERVAL, + bcm43xx_phy_read(dev, BCM43xx_PHY_ANALOGOVERVAL) & 0xFFFE); + bcm43xx_phy_write(dev, BCM43xx_PHY_ANALOGOVER, + bcm43xx_phy_read(dev, BCM43xx_PHY_ANALOGOVER) | 0x0002); + bcm43xx_phy_write(dev, BCM43xx_PHY_ANALOGOVERVAL, + bcm43xx_phy_read(dev, BCM43xx_PHY_ANALOGOVERVAL) & 0xFFFD); + } + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVER, + bcm43xx_phy_read(dev, BCM43xx_PHY_RFOVER) | 0x000C); + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVER, + bcm43xx_phy_read(dev, BCM43xx_PHY_RFOVERVAL) | 0x000C); + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVER, + bcm43xx_phy_read(dev, BCM43xx_PHY_RFOVER) | 0x0030); + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVERVAL, + (bcm43xx_phy_read(dev, BCM43xx_PHY_RFOVERVAL) + & 0xFFCF) | 0x10); + + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x5A), 0x0780); + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x59), 0xC810); + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x58), 0x000D); + + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x0A), + bcm43xx_phy_read(dev, BCM43xx_PHY_BASE(0x0A)) | 0x2000); + if (phy->rev != 1) { /* Not in specs, but needed to prevent PPC machine check */ + bcm43xx_phy_write(dev, BCM43xx_PHY_ANALOGOVER, + bcm43xx_phy_read(dev, BCM43xx_PHY_ANALOGOVER) | 0x0004); + bcm43xx_phy_write(dev, BCM43xx_PHY_ANALOGOVERVAL, + bcm43xx_phy_read(dev, BCM43xx_PHY_ANALOGOVERVAL) & 0xFFFB); + } + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x03), + (bcm43xx_phy_read(dev, BCM43xx_PHY_BASE(0x03)) + & 0xFF9F) | 0x40); + + if (phy->radio_rev == 8) { + bcm43xx_radio_write16(dev, 0x43, 0x000F); + } else { + bcm43xx_radio_write16(dev, 0x52, 0); + bcm43xx_radio_write16(dev, 0x43, + (bcm43xx_radio_read16(dev, 0x43) + & 0xFFF0) | 0x9); + } + bcm43xx_phy_set_baseband_attenuation(dev, 11); + + if (phy->rev >= 3) + bcm43xx_phy_write(dev, BCM43xx_PHY_LO_MASK, 0xC020); + else + bcm43xx_phy_write(dev, BCM43xx_PHY_LO_MASK, 0x8020); + bcm43xx_phy_write(dev, BCM43xx_PHY_LO_CTL, 0); + + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x2B), + (bcm43xx_phy_read(dev, BCM43xx_PHY_BASE(0x2B)) + & 0xFFC0) | 0x01); + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x2B), + (bcm43xx_phy_read(dev, BCM43xx_PHY_BASE(0x2B)) + & 0xC0FF) | 0x800); + + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVER, + bcm43xx_phy_read(dev, BCM43xx_PHY_RFOVER) | 0x0100); + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVERVAL, + bcm43xx_phy_read(dev, BCM43xx_PHY_RFOVERVAL) & 0xCFFF); + + if (dev->dev->bus->sprom.r1.boardflags_lo & BCM43xx_BFL_EXTLNA) { + if (phy->rev >= 7) { + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVER, + bcm43xx_phy_read(dev, BCM43xx_PHY_RFOVER) + | 0x0800); + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVERVAL, + bcm43xx_phy_read(dev, BCM43xx_PHY_RFOVERVAL) + | 0x8000); + } + } + bcm43xx_radio_write16(dev, 0x7A, + bcm43xx_radio_read16(dev, 0x7A) + & 0x00F7); + + j = 0; + loop_i_max = (phy->radio_rev == 8) ? 15 : 9; + for (i = 0; i < loop_i_max; i++) { + for (j = 0; j < 16; j++) { + bcm43xx_radio_write16(dev, 0x43, i); + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVERVAL, + (bcm43xx_phy_read(dev, BCM43xx_PHY_RFOVERVAL) + & 0xF0FF) | (j << 8)); + bcm43xx_phy_write(dev, BCM43xx_PHY_PGACTL, + (bcm43xx_phy_read(dev, BCM43xx_PHY_PGACTL) + & 0x0FFF) | 0xA000); + bcm43xx_phy_write(dev, BCM43xx_PHY_PGACTL, + bcm43xx_phy_read(dev, BCM43xx_PHY_PGACTL) + | 0xF000); + udelay(20); + if (bcm43xx_phy_read(dev, BCM43xx_PHY_LO_LEAKAGE) >= 0xDFC) + goto exit_loop1; + } + } +exit_loop1: + loop1_outer_done = i; + loop1_inner_done = j; + if (j >= 8) { + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVERVAL, + bcm43xx_phy_read(dev, BCM43xx_PHY_RFOVERVAL) + | 0x30); + trsw_rx = 0x1B; + for (j = j - 8; j < 16; j++) { + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVERVAL, + (bcm43xx_phy_read(dev, BCM43xx_PHY_RFOVERVAL) + & 0xF0FF) | (j << 8)); + bcm43xx_phy_write(dev, BCM43xx_PHY_PGACTL, + (bcm43xx_phy_read(dev, BCM43xx_PHY_PGACTL) + & 0x0FFF) | 0xA000); + bcm43xx_phy_write(dev, BCM43xx_PHY_PGACTL, + bcm43xx_phy_read(dev, BCM43xx_PHY_PGACTL) + | 0xF000); + udelay(20); + trsw_rx -= 3; + if (bcm43xx_phy_read(dev, BCM43xx_PHY_LO_LEAKAGE) >= 0xDFC) + goto exit_loop2; + } + } else + trsw_rx = 0x18; +exit_loop2: + + if (phy->rev != 1) { /* Not in specs, but needed to prevent PPC machine check */ + bcm43xx_phy_write(dev, BCM43xx_PHY_ANALOGOVER, backup_phy[4]); + bcm43xx_phy_write(dev, BCM43xx_PHY_ANALOGOVERVAL, backup_phy[5]); + } + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x5A), backup_phy[6]); + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x59), backup_phy[7]); + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x58), backup_phy[8]); + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x0A), backup_phy[9]); + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x03), backup_phy[10]); + bcm43xx_phy_write(dev, BCM43xx_PHY_LO_MASK, backup_phy[11]); + bcm43xx_phy_write(dev, BCM43xx_PHY_LO_CTL, backup_phy[12]); + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x2B), backup_phy[13]); + bcm43xx_phy_write(dev, BCM43xx_PHY_PGACTL, backup_phy[14]); + + bcm43xx_phy_set_baseband_attenuation(dev, backup_bband); + + bcm43xx_radio_write16(dev, 0x52, backup_radio[0]); + bcm43xx_radio_write16(dev, 0x43, backup_radio[1]); + bcm43xx_radio_write16(dev, 0x7A, backup_radio[2]); + + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVER, backup_phy[2] | 0x0003); + udelay(10); + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVER, backup_phy[2]); + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVERVAL, backup_phy[3]); + bcm43xx_phy_write(dev, BCM43xx_PHY_CRS0, backup_phy[0]); + bcm43xx_phy_write(dev, BCM43xx_PHY_CCKBBANDCFG, backup_phy[1]); + + phy->max_lb_gain = ((loop1_inner_done * 6) - (loop1_outer_done * 4)) - 11; + phy->trsw_rx_gain = trsw_rx * 2; +} + +static void bcm43xx_phy_initg(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + u16 tmp; + + if (phy->rev == 1) + bcm43xx_phy_initb5(dev); + else + bcm43xx_phy_initb6(dev); + + if (phy->rev >= 2 || phy->gmode) + bcm43xx_phy_inita(dev); + + if (phy->rev >= 2) { + bcm43xx_phy_write(dev, BCM43xx_PHY_ANALOGOVER, 0); + bcm43xx_phy_write(dev, BCM43xx_PHY_ANALOGOVERVAL, 0); + } + if (phy->rev == 2) { + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVER, 0); + bcm43xx_phy_write(dev, BCM43xx_PHY_PGACTL, 0xC0); + } + if (phy->rev > 5) { + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVER, 0x400); + bcm43xx_phy_write(dev, BCM43xx_PHY_PGACTL, 0xC0); + } + if (phy->gmode || phy->rev >= 2) { + tmp = bcm43xx_phy_read(dev, BCM43xx_PHY_VERSION_OFDM); + tmp &= BCM43xx_PHYVER_VERSION; + if (tmp == 3 || tmp == 5) { + bcm43xx_phy_write(dev, BCM43xx_PHY_OFDM(0xC2), 0x1816); + bcm43xx_phy_write(dev, BCM43xx_PHY_OFDM(0xC3), 0x8006); + } + if (tmp == 5) { + bcm43xx_phy_write(dev, BCM43xx_PHY_OFDM(0xCC), + (bcm43xx_phy_read(dev, BCM43xx_PHY_OFDM(0xCC)) + & 0x00FF) | 0x1F00); + } + } + if ((phy->rev <= 2 && phy->gmode) || phy->rev >= 2) + bcm43xx_phy_write(dev, BCM43xx_PHY_OFDM(0x7E), 0x78); + if (phy->radio_rev == 8) { + bcm43xx_phy_write(dev, BCM43xx_PHY_EXTG(0x01), + bcm43xx_phy_read(dev, BCM43xx_PHY_EXTG(0x01)) + | 0x80); + bcm43xx_phy_write(dev, BCM43xx_PHY_OFDM(0x3E), + bcm43xx_phy_read(dev, BCM43xx_PHY_OFDM(0x3E)) + | 0x4); + } + if (has_loopback_gain(phy)) + bcm43xx_calc_loopback_gain(dev); + + if (phy->radio_rev != 8) { + if (phy->initval == 0xFFFF) + phy->initval = bcm43xx_radio_init2050(dev); + else + bcm43xx_radio_write16(dev, 0x0078, phy->initval); + } + if (phy->lo_control->tx_bias == 0xFF) { + bcm43xx_lo_g_measure(dev); + } else { + if (has_tx_magnification(phy)) { + bcm43xx_radio_write16(dev, 0x52, + (bcm43xx_radio_read16(dev, 0x52) & 0xFF00) | + phy->lo_control->tx_bias | + phy->lo_control->tx_magn); + } else { + bcm43xx_radio_write16(dev, 0x52, + (bcm43xx_radio_read16(dev, 0x52) & 0xFFF0) | + phy->lo_control->tx_bias); + } + if (phy->rev >= 6) { + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x36), + (bcm43xx_phy_read(dev, BCM43xx_PHY_BASE(0x36)) + & 0x0FFF) | (phy->lo_control->tx_bias << 12)); + } + if (dev->dev->bus->sprom.r1.boardflags_lo & BCM43xx_BFL_PACTRL) + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x2E), 0x8075); + else + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x2E), 0x807F); + if (phy->rev < 2) + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x2F), 0x101); + else + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x2F), 0x202); + } + if (phy->gmode || phy->rev >= 2) { + bcm43xx_lo_adjust(dev); + bcm43xx_phy_write(dev, BCM43xx_PHY_LO_MASK, 0x8078); + } + + if (!(dev->dev->bus->sprom.r1.boardflags_lo & BCM43xx_BFL_RSSI)) { + /* The specs state to update the NRSSI LT with + * the value 0x7FFFFFFF here. I think that is some weird + * compiler optimization in the original driver. + * Essentially, what we do here is resetting all NRSSI LT + * entries to -32 (see the limit_value() in nrssi_hw_update()) + */ + bcm43xx_nrssi_hw_update(dev, 0xFFFF);//FIXME? + bcm43xx_calc_nrssi_threshold(dev); + } else if (phy->gmode || phy->rev >= 2) { + if (phy->nrssi[0] == -1000) { + assert(phy->nrssi[1] == -1000); + bcm43xx_calc_nrssi_slope(dev); + } else + bcm43xx_calc_nrssi_threshold(dev); + } + if (phy->radio_rev == 8) + bcm43xx_phy_write(dev, BCM43xx_PHY_EXTG(0x05), 0x3230); + bcm43xx_phy_init_pctl(dev); + /* FIXME: The spec says in the following if, the 0 should be replaced + 'if OFDM may not be used in the current locale' + but OFDM is legal everywhere */ + if ((dev->dev->bus->chip_id == 0x4306 && dev->dev->bus->chip_package == 2) || 0) { + bcm43xx_phy_write(dev, BCM43xx_PHY_CRS0, + bcm43xx_phy_read(dev, BCM43xx_PHY_CRS0) + & 0xBFFF); + bcm43xx_phy_write(dev, BCM43xx_PHY_OFDM(0xC3), + bcm43xx_phy_read(dev, BCM43xx_PHY_OFDM(0xC3)) + & 0x7FFF); + } +} + +/* Set the baseband attenuation value on chip. */ +void bcm43xx_phy_set_baseband_attenuation(struct bcm43xx_wldev *dev, + u16 baseband_attenuation) +{ + struct bcm43xx_phy *phy = &dev->phy; + u16 value; + + if (phy->analog == 0) { + value = (bcm43xx_read16(dev, 0x03E6) & 0xFFF0); + value |= (baseband_attenuation & 0x000F); + bcm43xx_write16(dev, 0x03E6, value); + return; + } + + if (phy->analog > 1) { + value = bcm43xx_phy_read(dev, 0x0060) & ~0x003C; + value |= (baseband_attenuation << 2) & 0x003C; + } else { + value = bcm43xx_phy_read(dev, 0x0060) & ~0x0078; + value |= (baseband_attenuation << 3) & 0x0078; + } + bcm43xx_phy_write(dev, 0x0060, value); +} + +/* http://bcm-specs.sipsolutions.net/EstimatePowerOut + * This function converts a TSSI value to dBm in Q5.2 + */ +static s8 bcm43xx_phy_estimate_power_out(struct bcm43xx_wldev *dev, s8 tssi) +{ + struct bcm43xx_phy *phy = &dev->phy; + s8 dbm = 0; + s32 tmp; + + tmp = (phy->tgt_idle_tssi - phy->cur_idle_tssi + tssi); + + switch (phy->type) { + case BCM43xx_PHYTYPE_A: + tmp += 0x80; + tmp = limit_value(tmp, 0x00, 0xFF); + dbm = phy->tssi2dbm[tmp]; + TODO(); //TODO: There's a FIXME on the specs + break; + case BCM43xx_PHYTYPE_B: + case BCM43xx_PHYTYPE_G: + tmp = limit_value(tmp, 0x00, 0x3F); + dbm = phy->tssi2dbm[tmp]; + break; + default: + assert(0); + } + + return dbm; +} + +/* http://bcm-specs.sipsolutions.net/RecalculateTransmissionPower */ +void bcm43xx_phy_xmitpower(struct bcm43xx_wldev *dev) +{ + struct ssb_bus *bus = dev->dev->bus; + struct bcm43xx_phy *phy = &dev->phy; + + if (phy->cur_idle_tssi == 0) + return; + if ((bus->board_vendor == SSB_BOARDVENDOR_BCM) && + (bus->board_type == SSB_BOARD_BU4306)) + return; + + switch (phy->type) { + case BCM43xx_PHYTYPE_A: { + + TODO(); //TODO: Nothing for A PHYs yet :-/ + + break; + } + case BCM43xx_PHYTYPE_B: + case BCM43xx_PHYTYPE_G: { + u16 tmp; + u16 txpower; + s8 v0, v1, v2, v3; + s8 average; + u8 max_pwr; + s16 desired_pwr, estimated_pwr, pwr_adjust; + s16 radio_att_delta, baseband_att_delta; + s16 radio_attenuation, baseband_attenuation; + unsigned long phylock_flags; + + tmp = bcm43xx_shm_read16(dev, BCM43xx_SHM_SHARED, 0x0058); + v0 = (s8)(tmp & 0x00FF); + v1 = (s8)((tmp & 0xFF00) >> 8); + tmp = bcm43xx_shm_read16(dev, BCM43xx_SHM_SHARED, 0x005A); + v2 = (s8)(tmp & 0x00FF); + v3 = (s8)((tmp & 0xFF00) >> 8); + tmp = 0; + + if (v0 == 0x7F || v1 == 0x7F || v2 == 0x7F || v3 == 0x7F) { + tmp = bcm43xx_shm_read16(dev, BCM43xx_SHM_SHARED, 0x0070); + v0 = (s8)(tmp & 0x00FF); + v1 = (s8)((tmp & 0xFF00) >> 8); + tmp = bcm43xx_shm_read16(dev, BCM43xx_SHM_SHARED, 0x0072); + v2 = (s8)(tmp & 0x00FF); + v3 = (s8)((tmp & 0xFF00) >> 8); + if (v0 == 0x7F || v1 == 0x7F || v2 == 0x7F || v3 == 0x7F) + return; + v0 = (v0 + 0x20) & 0x3F; + v1 = (v1 + 0x20) & 0x3F; + v2 = (v2 + 0x20) & 0x3F; + v3 = (v3 + 0x20) & 0x3F; + tmp = 1; + } + bcm43xx_shm_clear_tssi(dev); + + average = (v0 + v1 + v2 + v3 + 2) / 4; + + if (tmp && (bcm43xx_shm_read16(dev, BCM43xx_SHM_SHARED, 0x005E) & 0x8)) + average -= 13; + + estimated_pwr = bcm43xx_phy_estimate_power_out(dev, average); + + max_pwr = dev->dev->bus->sprom.r1.maxpwr_bg; + + if ((dev->dev->bus->sprom.r1.boardflags_lo & BCM43xx_BFL_PACTRL) && + (phy->type == BCM43xx_PHYTYPE_G)) + max_pwr -= 0x3; + + /*TODO: + max_pwr = min(REG - dev->dev->bus->sprom.antennagain_bgphy - 0x6, max_pwr) + where REG is the max power as per the regulatory domain + */ + + desired_pwr = phy->power_level; + /* Convert the desired_pwr to Q5.2 and limit it. */ + desired_pwr = limit_value((desired_pwr << 2), 0, max_pwr); + + pwr_adjust = desired_pwr - estimated_pwr; + radio_att_delta = -(pwr_adjust + 7) >> 3; + baseband_att_delta = -(pwr_adjust >> 1) - (4 * radio_att_delta); + if ((radio_att_delta == 0) && (baseband_att_delta == 0)) { + bcm43xx_loctl_mark_cur_used(dev); + return; + } + + /* Calculate the new attenuation values. */ + baseband_attenuation = phy->bbatt; + baseband_attenuation += baseband_att_delta; + radio_attenuation = phy->rfatt; + radio_attenuation += radio_att_delta; + + /* Get baseband and radio attenuation values into their permitted ranges. + * baseband 0-11, radio 0-9. + * Radio attenuation affects power level 4 times as much as baseband. + */ + if (radio_attenuation < 0) { + baseband_attenuation -= (4 * -radio_attenuation); + radio_attenuation = 0; + } else if (radio_attenuation > 9) { + baseband_attenuation += (4 * (radio_attenuation - 9)); + radio_attenuation = 9; + } else { + while (baseband_attenuation < 0 && radio_attenuation > 0) { + baseband_attenuation += 4; + radio_attenuation--; + } + while (baseband_attenuation > 11 && radio_attenuation < 9) { + baseband_attenuation -= 4; + radio_attenuation++; + } + } + baseband_attenuation = limit_value(baseband_attenuation, 0, 11); + + txpower = phy->txctl1; + if ((phy->radio_ver == 0x2050) && (phy->radio_rev == 2)) { + if (radio_attenuation <= 1) { + if (txpower == 0) { + txpower = 3; + radio_attenuation += 2; + baseband_attenuation += 2; + } else if (dev->dev->bus->sprom.r1.boardflags_lo & BCM43xx_BFL_PACTRL) { + baseband_attenuation += 4 * (radio_attenuation - 2); + radio_attenuation = 2; + } + } else if (radio_attenuation > 4 && txpower != 0) { + txpower = 0; + if (baseband_attenuation < 3) { + radio_attenuation -= 3; + baseband_attenuation += 2; + } else { + radio_attenuation -= 2; + baseband_attenuation -= 2; + } + } + } + phy->txctl1 = txpower; + baseband_attenuation = limit_value(baseband_attenuation, 0, 11); + radio_attenuation = limit_value(radio_attenuation, 0, 9); + + bcm43xx_phy_lock(dev, phylock_flags); + bcm43xx_radio_lock(dev); + bcm43xx_radio_set_txpower_bg(dev, baseband_attenuation, + radio_attenuation, txpower); + bcm43xx_loctl_mark_cur_used(dev); + bcm43xx_radio_unlock(dev); + bcm43xx_phy_unlock(dev, phylock_flags); + break; + } + default: + assert(0); + } +} + +static inline +s32 bcm43xx_tssi2dbm_ad(s32 num, s32 den) +{ + if (num < 0) + return num/den; + else + return (num+den/2)/den; +} + +static inline +s8 bcm43xx_tssi2dbm_entry(s8 entry [], u8 index, s16 pab0, s16 pab1, s16 pab2) +{ + s32 m1, m2, f = 256, q, delta; + s8 i = 0; + + m1 = bcm43xx_tssi2dbm_ad(16 * pab0 + index * pab1, 32); + m2 = max(bcm43xx_tssi2dbm_ad(32768 + index * pab2, 256), 1); + do { + if (i > 15) + return -EINVAL; + q = bcm43xx_tssi2dbm_ad(f * 4096 - + bcm43xx_tssi2dbm_ad(m2 * f, 16) * f, 2048); + delta = abs(q - f); + f = q; + i++; + } while (delta >= 2); + entry[index] = limit_value(bcm43xx_tssi2dbm_ad(m1 * f, 8192), -127, 128); + return 0; +} + +/* http://bcm-specs.sipsolutions.net/TSSI_to_DBM_Table */ +int bcm43xx_phy_init_tssi2dbm_table(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + s16 pab0, pab1, pab2; + u8 idx; + s8 *dyn_tssi2dbm; + + if (phy->type == BCM43xx_PHYTYPE_A) { + pab0 = (s16)(dev->dev->bus->sprom.r1.pa1b0); + pab1 = (s16)(dev->dev->bus->sprom.r1.pa1b1); + pab2 = (s16)(dev->dev->bus->sprom.r1.pa1b2); + } else { + pab0 = (s16)(dev->dev->bus->sprom.r1.pa0b0); + pab1 = (s16)(dev->dev->bus->sprom.r1.pa0b1); + pab2 = (s16)(dev->dev->bus->sprom.r1.pa0b2); + } + + if ((dev->dev->bus->chip_id == 0x4301) && (phy->radio_ver != 0x2050)) { + phy->tgt_idle_tssi = 0x34; + phy->tssi2dbm = bcm43xx_tssi2dbm_b_table; + return 0; + } + + if (pab0 != 0 && pab1 != 0 && pab2 != 0 && + pab0 != -1 && pab1 != -1 && pab2 != -1) { + /* The pabX values are set in SPROM. Use them. */ + if (phy->type == BCM43xx_PHYTYPE_A) { + if ((s8)dev->dev->bus->sprom.r1.itssi_a != 0 && + (s8)dev->dev->bus->sprom.r1.itssi_a != -1) + phy->tgt_idle_tssi = (s8)(dev->dev->bus->sprom.r1.itssi_a); + else + phy->tgt_idle_tssi = 62; + } else { + if ((s8)dev->dev->bus->sprom.r1.itssi_bg != 0 && + (s8)dev->dev->bus->sprom.r1.itssi_bg != -1) + phy->tgt_idle_tssi = (s8)(dev->dev->bus->sprom.r1.itssi_bg); + else + phy->tgt_idle_tssi = 62; + } + dyn_tssi2dbm = kmalloc(64, GFP_KERNEL); + if (dyn_tssi2dbm == NULL) { + printk(KERN_ERR PFX "Could not allocate memory" + "for tssi2dbm table\n"); + return -ENOMEM; + } + for (idx = 0; idx < 64; idx++) + if (bcm43xx_tssi2dbm_entry(dyn_tssi2dbm, idx, pab0, pab1, pab2)) { + phy->tssi2dbm = NULL; + printk(KERN_ERR PFX "Could not generate " + "tssi2dBm table\n"); + kfree(dyn_tssi2dbm); + return -ENODEV; + } + phy->tssi2dbm = dyn_tssi2dbm; + phy->dyn_tssi_tbl = 1; + } else { + /* pabX values not set in SPROM. */ + switch (phy->type) { + case BCM43xx_PHYTYPE_A: + /* APHY needs a generated table. */ + phy->tssi2dbm = NULL; + printk(KERN_ERR PFX "Could not generate tssi2dBm " + "table (wrong SPROM info)!\n"); + return -ENODEV; + case BCM43xx_PHYTYPE_B: + phy->tgt_idle_tssi = 0x34; + phy->tssi2dbm = bcm43xx_tssi2dbm_b_table; + break; + case BCM43xx_PHYTYPE_G: + phy->tgt_idle_tssi = 0x34; + phy->tssi2dbm = bcm43xx_tssi2dbm_g_table; + break; + } + } + + return 0; +} + +int bcm43xx_phy_init(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + int err = -ENODEV; + + switch (phy->type) { + case BCM43xx_PHYTYPE_A: + if (phy->rev == 2 || phy->rev == 3) { + bcm43xx_phy_inita(dev); + err = 0; + } + break; + case BCM43xx_PHYTYPE_B: + switch (phy->rev) { + case 2: + bcm43xx_phy_initb2(dev); + err = 0; + break; + case 4: + bcm43xx_phy_initb4(dev); + err = 0; + break; + case 5: + bcm43xx_phy_initb5(dev); + err = 0; + break; + case 6: + bcm43xx_phy_initb6(dev); + err = 0; + break; + } + break; + case BCM43xx_PHYTYPE_G: + bcm43xx_phy_initg(dev); + err = 0; + break; + } + if (err) + printk(KERN_WARNING PFX "Unknown PHYTYPE found!\n"); + + return err; +} + +void bcm43xx_set_rx_antenna(struct bcm43xx_wldev *dev, int antenna) +{ + struct bcm43xx_phy *phy = &dev->phy; + u32 hf; + u16 tmp; + int autodiv = 0; + + if (antenna == BCM43xx_ANTENNA_AUTO0 || + antenna == BCM43xx_ANTENNA_AUTO1) + autodiv = 1; + + hf = bcm43xx_hf_read(dev); + hf &= ~BCM43xx_HF_ANTDIVHELP; + bcm43xx_hf_write(dev, hf); + + switch (phy->type) { + case BCM43xx_PHYTYPE_A: + case BCM43xx_PHYTYPE_G: + tmp = bcm43xx_phy_read(dev, BCM43xx_PHY_BBANDCFG); + tmp &= ~BCM43xx_PHY_BBANDCFG_RXANT; + tmp |= (autodiv ? BCM43xx_ANTENNA_AUTO0 : antenna) + << BCM43xx_PHY_BBANDCFG_RXANT_SHIFT; + bcm43xx_phy_write(dev, BCM43xx_PHY_BBANDCFG, tmp); + + if (autodiv) { + tmp = bcm43xx_phy_read(dev, BCM43xx_PHY_ANTDWELL); + if (antenna == BCM43xx_ANTENNA_AUTO0) + tmp &= ~BCM43xx_PHY_ANTDWELL_AUTODIV1; + else + tmp |= BCM43xx_PHY_ANTDWELL_AUTODIV1; + bcm43xx_phy_write(dev, BCM43xx_PHY_ANTDWELL, tmp); + } + if (phy->type == BCM43xx_PHYTYPE_G) { + tmp = bcm43xx_phy_read(dev, BCM43xx_PHY_ANTWRSETT); + if (autodiv) + tmp |= BCM43xx_PHY_ANTWRSETT_ARXDIV; + else + tmp &= ~BCM43xx_PHY_ANTWRSETT_ARXDIV; + bcm43xx_phy_write(dev, BCM43xx_PHY_ANTWRSETT, tmp); + if (phy->rev >= 2) { + tmp = bcm43xx_phy_read(dev, BCM43xx_PHY_OFDM61); + tmp |= BCM43xx_PHY_OFDM61_10; + bcm43xx_phy_write(dev, BCM43xx_PHY_OFDM61, tmp); + + tmp = bcm43xx_phy_read(dev, BCM43xx_PHY_DIVSRCHGAINBACK); + tmp = (tmp & 0xFF00) | 0x15; + bcm43xx_phy_write(dev, BCM43xx_PHY_DIVSRCHGAINBACK, tmp); + + if (phy->rev == 2) { + bcm43xx_phy_write(dev, BCM43xx_PHY_ADIVRELATED, 8); + } else { + tmp = bcm43xx_phy_read(dev, BCM43xx_PHY_ADIVRELATED); + tmp = (tmp & 0xFF00) | 8; + bcm43xx_phy_write(dev, BCM43xx_PHY_ADIVRELATED, tmp); + } + } + if (phy->rev >= 6) + bcm43xx_phy_write(dev, BCM43xx_PHY_OFDM9B, 0xDC); + } else { + if (phy->rev < 3) { + tmp = bcm43xx_phy_read(dev, BCM43xx_PHY_ANTDWELL); + tmp = (tmp & 0xFF00) | 0x24; + bcm43xx_phy_write(dev, BCM43xx_PHY_ANTDWELL, tmp); + } else { + tmp = bcm43xx_phy_read(dev, BCM43xx_PHY_OFDM61); + tmp |= 0x10; + bcm43xx_phy_write(dev, BCM43xx_PHY_OFDM61, tmp); + if (phy->analog == 3) { + bcm43xx_phy_write(dev, BCM43xx_PHY_CLIPPWRDOWNT, 0x1D); + bcm43xx_phy_write(dev, BCM43xx_PHY_ADIVRELATED, 8); + } else { + bcm43xx_phy_write(dev, BCM43xx_PHY_CLIPPWRDOWNT, 0x3A); + tmp = bcm43xx_phy_read(dev, BCM43xx_PHY_ADIVRELATED); + tmp = (tmp & 0xFF00) | 8; + bcm43xx_phy_write(dev, BCM43xx_PHY_ADIVRELATED, tmp); + } + } + } + break; + case BCM43xx_PHYTYPE_B: + tmp = bcm43xx_phy_read(dev, BCM43xx_PHY_CCKBBANDCFG); + tmp &= ~BCM43xx_PHY_BBANDCFG_RXANT; + tmp |= (autodiv ? BCM43xx_ANTENNA_AUTO0 : antenna) + << BCM43xx_PHY_BBANDCFG_RXANT_SHIFT; + bcm43xx_phy_write(dev, BCM43xx_PHY_CCKBBANDCFG, tmp); + break; + default: + assert(0); + } + + hf |= BCM43xx_HF_ANTDIVHELP; + bcm43xx_hf_write(dev, hf); +} + +/* Get the freq, as it has to be written to the device. */ +static inline +u16 channel2freq_bg(u8 channel) +{ + assert(channel >= 1 && channel <= 14); + + return bcm43xx_radio_channel_codes_bg[channel - 1]; +} + +/* Get the freq, as it has to be written to the device. */ +static inline +u16 channel2freq_a(u8 channel) +{ + assert(channel <= 200); + + return (5000 + 5 * channel); +} + +void bcm43xx_radio_lock(struct bcm43xx_wldev *dev) +{ + u32 status; + + status = bcm43xx_read32(dev, BCM43xx_MMIO_STATUS_BITFIELD); + status |= BCM43xx_SBF_RADIOREG_LOCK; + bcm43xx_write32(dev, BCM43xx_MMIO_STATUS_BITFIELD, status); + mmiowb(); + udelay(10); +} + +void bcm43xx_radio_unlock(struct bcm43xx_wldev *dev) +{ + u32 status; + + bcm43xx_read16(dev, BCM43xx_MMIO_PHY_VER); /* dummy read */ + status = bcm43xx_read32(dev, BCM43xx_MMIO_STATUS_BITFIELD); + status &= ~BCM43xx_SBF_RADIOREG_LOCK; + bcm43xx_write32(dev, BCM43xx_MMIO_STATUS_BITFIELD, status); + mmiowb(); +} + +u16 bcm43xx_radio_read16(struct bcm43xx_wldev *dev, u16 offset) +{ + struct bcm43xx_phy *phy = &dev->phy; + + switch (phy->type) { + case BCM43xx_PHYTYPE_A: + offset |= 0x0040; + break; + case BCM43xx_PHYTYPE_B: + if (phy->radio_ver == 0x2053) { + if (offset < 0x70) + offset += 0x80; + else if (offset < 0x80) + offset += 0x70; + } else if (phy->radio_ver == 0x2050) { + offset |= 0x80; + } else + assert(0); + break; + case BCM43xx_PHYTYPE_G: + offset |= 0x80; + break; + } + + bcm43xx_write16(dev, BCM43xx_MMIO_RADIO_CONTROL, offset); + return bcm43xx_read16(dev, BCM43xx_MMIO_RADIO_DATA_LOW); +} + +void bcm43xx_radio_write16(struct bcm43xx_wldev *dev, u16 offset, u16 val) +{ + bcm43xx_write16(dev, BCM43xx_MMIO_RADIO_CONTROL, offset); + mmiowb(); + bcm43xx_write16(dev, BCM43xx_MMIO_RADIO_DATA_LOW, val); +} + +static void bcm43xx_set_all_gains(struct bcm43xx_wldev *dev, + s16 first, s16 second, s16 third) +{ + struct bcm43xx_phy *phy = &dev->phy; + u16 i; + u16 start = 0x08, end = 0x18; + u16 tmp; + u16 table; + + if (phy->rev <= 1) { + start = 0x10; + end = 0x20; + } + + table = BCM43xx_OFDMTAB_GAINX; + if (phy->rev <= 1) + table = BCM43xx_OFDMTAB_GAINX_R1; + for (i = 0; i < 4; i++) + bcm43xx_ofdmtab_write16(dev, table, i, first); + + for (i = start; i < end; i++) + bcm43xx_ofdmtab_write16(dev, table, i, second); + + if (third != -1) { + tmp = ((u16)third << 14) | ((u16)third << 6); + bcm43xx_phy_write(dev, 0x04A0, + (bcm43xx_phy_read(dev, 0x04A0) & 0xBFBF) | tmp); + bcm43xx_phy_write(dev, 0x04A1, + (bcm43xx_phy_read(dev, 0x04A1) & 0xBFBF) | tmp); + bcm43xx_phy_write(dev, 0x04A2, + (bcm43xx_phy_read(dev, 0x04A2) & 0xBFBF) | tmp); + } + bcm43xx_dummy_transmission(dev); +} + +static void bcm43xx_set_original_gains(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + u16 i, tmp; + u16 table; + u16 start = 0x0008, end = 0x0018; + + if (phy->rev <= 1) { + start = 0x0010; + end = 0x0020; + } + + table = BCM43xx_OFDMTAB_GAINX; + if (phy->rev <= 1) + table = BCM43xx_OFDMTAB_GAINX_R1; + for (i = 0; i < 4; i++) { + tmp = (i & 0xFFFC); + tmp |= (i & 0x0001) << 1; + tmp |= (i & 0x0002) >> 1; + + bcm43xx_ofdmtab_write16(dev, table, i, tmp); + } + + for (i = start; i < end; i++) + bcm43xx_ofdmtab_write16(dev, table, i, i - start); + + bcm43xx_phy_write(dev, 0x04A0, + (bcm43xx_phy_read(dev, 0x04A0) & 0xBFBF) | 0x4040); + bcm43xx_phy_write(dev, 0x04A1, + (bcm43xx_phy_read(dev, 0x04A1) & 0xBFBF) | 0x4040); + bcm43xx_phy_write(dev, 0x04A2, + (bcm43xx_phy_read(dev, 0x04A2) & 0xBFBF) | 0x4000); + bcm43xx_dummy_transmission(dev); +} + +/* Synthetic PU workaround */ +static void bcm43xx_synth_pu_workaround(struct bcm43xx_wldev *dev, u8 channel) +{ + struct bcm43xx_phy *phy = &dev->phy; + + if (phy->radio_ver != 0x2050 || phy->radio_rev >= 6) { + /* We do not need the workaround. */ + return; + } + + if (channel <= 10) { + bcm43xx_write16(dev, BCM43xx_MMIO_CHANNEL, + channel2freq_bg(channel + 4)); + } else { + bcm43xx_write16(dev, BCM43xx_MMIO_CHANNEL, + channel2freq_bg(1)); + } + udelay(100); + bcm43xx_write16(dev, BCM43xx_MMIO_CHANNEL, + channel2freq_bg(channel)); +} + +u8 bcm43xx_radio_aci_detect(struct bcm43xx_wldev *dev, u8 channel) +{ + struct bcm43xx_phy *phy = &dev->phy; + u8 ret = 0; + u16 saved, rssi, temp; + int i, j = 0; + + saved = bcm43xx_phy_read(dev, 0x0403); + bcm43xx_radio_selectchannel(dev, channel, 0); + bcm43xx_phy_write(dev, 0x0403, (saved & 0xFFF8) | 5); + if (phy->aci_hw_rssi) + rssi = bcm43xx_phy_read(dev, 0x048A) & 0x3F; + else + rssi = saved & 0x3F; + /* clamp temp to signed 5bit */ + if (rssi > 32) + rssi -= 64; + for (i = 0;i < 100; i++) { + temp = (bcm43xx_phy_read(dev, 0x047F) >> 8) & 0x3F; + if (temp > 32) + temp -= 64; + if (temp < rssi) + j++; + if (j >= 20) + ret = 1; + } + bcm43xx_phy_write(dev, 0x0403, saved); + + return ret; +} + +u8 bcm43xx_radio_aci_scan(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + u8 ret[13]; + unsigned int channel = phy->channel; + unsigned int i, j, start, end; + unsigned long phylock_flags; + + if (!((phy->type == BCM43xx_PHYTYPE_G) && (phy->rev > 0))) + return 0; + + bcm43xx_phy_lock(dev, phylock_flags); + bcm43xx_radio_lock(dev); + bcm43xx_phy_write(dev, 0x0802, + bcm43xx_phy_read(dev, 0x0802) & 0xFFFC); + bcm43xx_phy_write(dev, BCM43xx_PHY_G_CRS, + bcm43xx_phy_read(dev, BCM43xx_PHY_G_CRS) & 0x7FFF); + bcm43xx_set_all_gains(dev, 3, 8, 1); + + start = (channel - 5 > 0) ? channel - 5 : 1; + end = (channel + 5 < 14) ? channel + 5 : 13; + + for (i = start; i <= end; i++) { + if (abs(channel - i) > 2) + ret[i-1] = bcm43xx_radio_aci_detect(dev, i); + } + bcm43xx_radio_selectchannel(dev, channel, 0); + bcm43xx_phy_write(dev, 0x0802, + (bcm43xx_phy_read(dev, 0x0802) & 0xFFFC) | 0x0003); + bcm43xx_phy_write(dev, 0x0403, + bcm43xx_phy_read(dev, 0x0403) & 0xFFF8); + bcm43xx_phy_write(dev, BCM43xx_PHY_G_CRS, + bcm43xx_phy_read(dev, BCM43xx_PHY_G_CRS) | 0x8000); + bcm43xx_set_original_gains(dev); + for (i = 0; i < 13; i++) { + if (!ret[i]) + continue; + end = (i + 5 < 13) ? i + 5 : 13; + for (j = i; j < end; j++) + ret[j] = 1; + } + bcm43xx_radio_unlock(dev); + bcm43xx_phy_unlock(dev, phylock_flags); + + return ret[channel - 1]; +} + +/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */ +void bcm43xx_nrssi_hw_write(struct bcm43xx_wldev *dev, u16 offset, s16 val) +{ + bcm43xx_phy_write(dev, BCM43xx_PHY_NRSSILT_CTRL, offset); + mmiowb(); + bcm43xx_phy_write(dev, BCM43xx_PHY_NRSSILT_DATA, (u16)val); +} + +/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */ +s16 bcm43xx_nrssi_hw_read(struct bcm43xx_wldev *dev, u16 offset) +{ + u16 val; + + bcm43xx_phy_write(dev, BCM43xx_PHY_NRSSILT_CTRL, offset); + val = bcm43xx_phy_read(dev, BCM43xx_PHY_NRSSILT_DATA); + + return (s16)val; +} + +/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */ +void bcm43xx_nrssi_hw_update(struct bcm43xx_wldev *dev, u16 val) +{ + u16 i; + s16 tmp; + + for (i = 0; i < 64; i++) { + tmp = bcm43xx_nrssi_hw_read(dev, i); + tmp -= val; + tmp = limit_value(tmp, -32, 31); + bcm43xx_nrssi_hw_write(dev, i, tmp); + } +} + +/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */ +void bcm43xx_nrssi_mem_update(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + s16 i, delta; + s32 tmp; + + delta = 0x1F - phy->nrssi[0]; + for (i = 0; i < 64; i++) { + tmp = (i - delta) * phy->nrssislope; + tmp /= 0x10000; + tmp += 0x3A; + tmp = limit_value(tmp, 0, 0x3F); + phy->nrssi_lt[i] = tmp; + } +} + +static void bcm43xx_calc_nrssi_offset(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + u16 backup[20] = { 0 }; + s16 v47F; + u16 i; + u16 saved = 0xFFFF; + + backup[0] = bcm43xx_phy_read(dev, 0x0001); + backup[1] = bcm43xx_phy_read(dev, 0x0811); + backup[2] = bcm43xx_phy_read(dev, 0x0812); + if (phy->rev != 1) { /* Not in specs, but needed to prevent PPC machine check */ + backup[3] = bcm43xx_phy_read(dev, 0x0814); + backup[4] = bcm43xx_phy_read(dev, 0x0815); + } + backup[5] = bcm43xx_phy_read(dev, 0x005A); + backup[6] = bcm43xx_phy_read(dev, 0x0059); + backup[7] = bcm43xx_phy_read(dev, 0x0058); + backup[8] = bcm43xx_phy_read(dev, 0x000A); + backup[9] = bcm43xx_phy_read(dev, 0x0003); + backup[10] = bcm43xx_radio_read16(dev, 0x007A); + backup[11] = bcm43xx_radio_read16(dev, 0x0043); + + bcm43xx_phy_write(dev, 0x0429, + bcm43xx_phy_read(dev, 0x0429) & 0x7FFF); + bcm43xx_phy_write(dev, 0x0001, + (bcm43xx_phy_read(dev, 0x0001) & 0x3FFF) | 0x4000); + bcm43xx_phy_write(dev, 0x0811, + bcm43xx_phy_read(dev, 0x0811) | 0x000C); + bcm43xx_phy_write(dev, 0x0812, + (bcm43xx_phy_read(dev, 0x0812) & 0xFFF3) | 0x0004); + bcm43xx_phy_write(dev, 0x0802, + bcm43xx_phy_read(dev, 0x0802) & ~(0x1 | 0x2)); + if (phy->rev >= 6) { + backup[12] = bcm43xx_phy_read(dev, 0x002E); + backup[13] = bcm43xx_phy_read(dev, 0x002F); + backup[14] = bcm43xx_phy_read(dev, 0x080F); + backup[15] = bcm43xx_phy_read(dev, 0x0810); + backup[16] = bcm43xx_phy_read(dev, 0x0801); + backup[17] = bcm43xx_phy_read(dev, 0x0060); + backup[18] = bcm43xx_phy_read(dev, 0x0014); + backup[19] = bcm43xx_phy_read(dev, 0x0478); + + bcm43xx_phy_write(dev, 0x002E, 0); + bcm43xx_phy_write(dev, 0x002F, 0); + bcm43xx_phy_write(dev, 0x080F, 0); + bcm43xx_phy_write(dev, 0x0810, 0); + bcm43xx_phy_write(dev, 0x0478, + bcm43xx_phy_read(dev, 0x0478) | 0x0100); + bcm43xx_phy_write(dev, 0x0801, + bcm43xx_phy_read(dev, 0x0801) | 0x0040); + bcm43xx_phy_write(dev, 0x0060, + bcm43xx_phy_read(dev, 0x0060) | 0x0040); + bcm43xx_phy_write(dev, 0x0014, + bcm43xx_phy_read(dev, 0x0014) | 0x0200); + } + bcm43xx_radio_write16(dev, 0x007A, + bcm43xx_radio_read16(dev, 0x007A) | 0x0070); + bcm43xx_radio_write16(dev, 0x007A, + bcm43xx_radio_read16(dev, 0x007A) | 0x0080); + udelay(30); + + v47F = (s16)((bcm43xx_phy_read(dev, 0x047F) >> 8) & 0x003F); + if (v47F >= 0x20) + v47F -= 0x40; + if (v47F == 31) { + for (i = 7; i >= 4; i--) { + bcm43xx_radio_write16(dev, 0x007B, i); + udelay(20); + v47F = (s16)((bcm43xx_phy_read(dev, 0x047F) >> 8) & 0x003F); + if (v47F >= 0x20) + v47F -= 0x40; + if (v47F < 31 && saved == 0xFFFF) + saved = i; + } + if (saved == 0xFFFF) + saved = 4; + } else { + bcm43xx_radio_write16(dev, 0x007A, + bcm43xx_radio_read16(dev, 0x007A) & 0x007F); + if (phy->rev != 1) { /* Not in specs, but needed to prevent PPC machine check */ + bcm43xx_phy_write(dev, 0x0814, + bcm43xx_phy_read(dev, 0x0814) | 0x0001); + bcm43xx_phy_write(dev, 0x0815, + bcm43xx_phy_read(dev, 0x0815) & 0xFFFE); + } + bcm43xx_phy_write(dev, 0x0811, + bcm43xx_phy_read(dev, 0x0811) | 0x000C); + bcm43xx_phy_write(dev, 0x0812, + bcm43xx_phy_read(dev, 0x0812) | 0x000C); + bcm43xx_phy_write(dev, 0x0811, + bcm43xx_phy_read(dev, 0x0811) | 0x0030); + bcm43xx_phy_write(dev, 0x0812, + bcm43xx_phy_read(dev, 0x0812) | 0x0030); + bcm43xx_phy_write(dev, 0x005A, 0x0480); + bcm43xx_phy_write(dev, 0x0059, 0x0810); + bcm43xx_phy_write(dev, 0x0058, 0x000D); + if (phy->rev == 0) { + bcm43xx_phy_write(dev, 0x0003, 0x0122); + } else { + bcm43xx_phy_write(dev, 0x000A, + bcm43xx_phy_read(dev, 0x000A) + | 0x2000); + } + if (phy->rev != 1) { /* Not in specs, but needed to prevent PPC machine check */ + bcm43xx_phy_write(dev, 0x0814, + bcm43xx_phy_read(dev, 0x0814) | 0x0004); + bcm43xx_phy_write(dev, 0x0815, + bcm43xx_phy_read(dev, 0x0815) & 0xFFFB); + } + bcm43xx_phy_write(dev, 0x0003, + (bcm43xx_phy_read(dev, 0x0003) & 0xFF9F) + | 0x0040); + bcm43xx_radio_write16(dev, 0x007A, + bcm43xx_radio_read16(dev, 0x007A) | 0x000F); + bcm43xx_set_all_gains(dev, 3, 0, 1); + bcm43xx_radio_write16(dev, 0x0043, + (bcm43xx_radio_read16(dev, 0x0043) + & 0x00F0) | 0x000F); + udelay(30); + v47F = (s16)((bcm43xx_phy_read(dev, 0x047F) >> 8) & 0x003F); + if (v47F >= 0x20) + v47F -= 0x40; + if (v47F == -32) { + for (i = 0; i < 4; i++) { + bcm43xx_radio_write16(dev, 0x007B, i); + udelay(20); + v47F = (s16)((bcm43xx_phy_read(dev, 0x047F) >> 8) & 0x003F); + if (v47F >= 0x20) + v47F -= 0x40; + if (v47F > -31 && saved == 0xFFFF) + saved = i; + } + if (saved == 0xFFFF) + saved = 3; + } else + saved = 0; + } + bcm43xx_radio_write16(dev, 0x007B, saved); + + if (phy->rev >= 6) { + bcm43xx_phy_write(dev, 0x002E, backup[12]); + bcm43xx_phy_write(dev, 0x002F, backup[13]); + bcm43xx_phy_write(dev, 0x080F, backup[14]); + bcm43xx_phy_write(dev, 0x0810, backup[15]); + } + if (phy->rev != 1) { /* Not in specs, but needed to prevent PPC machine check */ + bcm43xx_phy_write(dev, 0x0814, backup[3]); + bcm43xx_phy_write(dev, 0x0815, backup[4]); + } + bcm43xx_phy_write(dev, 0x005A, backup[5]); + bcm43xx_phy_write(dev, 0x0059, backup[6]); + bcm43xx_phy_write(dev, 0x0058, backup[7]); + bcm43xx_phy_write(dev, 0x000A, backup[8]); + bcm43xx_phy_write(dev, 0x0003, backup[9]); + bcm43xx_radio_write16(dev, 0x0043, backup[11]); + bcm43xx_radio_write16(dev, 0x007A, backup[10]); + bcm43xx_phy_write(dev, 0x0802, + bcm43xx_phy_read(dev, 0x0802) | 0x1 | 0x2); + bcm43xx_phy_write(dev, 0x0429, + bcm43xx_phy_read(dev, 0x0429) | 0x8000); + bcm43xx_set_original_gains(dev); + if (phy->rev >= 6) { + bcm43xx_phy_write(dev, 0x0801, backup[16]); + bcm43xx_phy_write(dev, 0x0060, backup[17]); + bcm43xx_phy_write(dev, 0x0014, backup[18]); + bcm43xx_phy_write(dev, 0x0478, backup[19]); + } + bcm43xx_phy_write(dev, 0x0001, backup[0]); + bcm43xx_phy_write(dev, 0x0812, backup[2]); + bcm43xx_phy_write(dev, 0x0811, backup[1]); +} + +void bcm43xx_calc_nrssi_slope(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + u16 backup[18] = { 0 }; + u16 tmp; + s16 nrssi0, nrssi1; + + switch (phy->type) { + case BCM43xx_PHYTYPE_B: + backup[0] = bcm43xx_radio_read16(dev, 0x007A); + backup[1] = bcm43xx_radio_read16(dev, 0x0052); + backup[2] = bcm43xx_radio_read16(dev, 0x0043); + backup[3] = bcm43xx_phy_read(dev, 0x0030); + backup[4] = bcm43xx_phy_read(dev, 0x0026); + backup[5] = bcm43xx_phy_read(dev, 0x0015); + backup[6] = bcm43xx_phy_read(dev, 0x002A); + backup[7] = bcm43xx_phy_read(dev, 0x0020); + backup[8] = bcm43xx_phy_read(dev, 0x005A); + backup[9] = bcm43xx_phy_read(dev, 0x0059); + backup[10] = bcm43xx_phy_read(dev, 0x0058); + backup[11] = bcm43xx_read16(dev, 0x03E2); + backup[12] = bcm43xx_read16(dev, 0x03E6); + backup[13] = bcm43xx_read16(dev, BCM43xx_MMIO_CHANNEL_EXT); + + tmp = bcm43xx_radio_read16(dev, 0x007A); + tmp &= (phy->rev >= 5) ? 0x007F : 0x000F; + bcm43xx_radio_write16(dev, 0x007A, tmp); + bcm43xx_phy_write(dev, 0x0030, 0x00FF); + bcm43xx_write16(dev, 0x03EC, 0x7F7F); + bcm43xx_phy_write(dev, 0x0026, 0x0000); + bcm43xx_phy_write(dev, 0x0015, + bcm43xx_phy_read(dev, 0x0015) | 0x0020); + bcm43xx_phy_write(dev, 0x002A, 0x08A3); + bcm43xx_radio_write16(dev, 0x007A, + bcm43xx_radio_read16(dev, 0x007A) | 0x0080); + + nrssi0 = (s16)bcm43xx_phy_read(dev, 0x0027); + bcm43xx_radio_write16(dev, 0x007A, + bcm43xx_radio_read16(dev, 0x007A) & 0x007F); + if (phy->rev >= 2) { + bcm43xx_write16(dev, 0x03E6, 0x0040); + } else if (phy->rev == 0) { + bcm43xx_write16(dev, 0x03E6, 0x0122); + } else { + bcm43xx_write16(dev, BCM43xx_MMIO_CHANNEL_EXT, + bcm43xx_read16(dev, BCM43xx_MMIO_CHANNEL_EXT) & 0x2000); + } + bcm43xx_phy_write(dev, 0x0020, 0x3F3F); + bcm43xx_phy_write(dev, 0x0015, 0xF330); + bcm43xx_radio_write16(dev, 0x005A, 0x0060); + bcm43xx_radio_write16(dev, 0x0043, + bcm43xx_radio_read16(dev, 0x0043) & 0x00F0); + bcm43xx_phy_write(dev, 0x005A, 0x0480); + bcm43xx_phy_write(dev, 0x0059, 0x0810); + bcm43xx_phy_write(dev, 0x0058, 0x000D); + udelay(20); + + nrssi1 = (s16)bcm43xx_phy_read(dev, 0x0027); + bcm43xx_phy_write(dev, 0x0030, backup[3]); + bcm43xx_radio_write16(dev, 0x007A, backup[0]); + bcm43xx_write16(dev, 0x03E2, backup[11]); + bcm43xx_phy_write(dev, 0x0026, backup[4]); + bcm43xx_phy_write(dev, 0x0015, backup[5]); + bcm43xx_phy_write(dev, 0x002A, backup[6]); + bcm43xx_synth_pu_workaround(dev, phy->channel); + if (phy->rev != 0) + bcm43xx_write16(dev, 0x03F4, backup[13]); + + bcm43xx_phy_write(dev, 0x0020, backup[7]); + bcm43xx_phy_write(dev, 0x005A, backup[8]); + bcm43xx_phy_write(dev, 0x0059, backup[9]); + bcm43xx_phy_write(dev, 0x0058, backup[10]); + bcm43xx_radio_write16(dev, 0x0052, backup[1]); + bcm43xx_radio_write16(dev, 0x0043, backup[2]); + + if (nrssi0 == nrssi1) + phy->nrssislope = 0x00010000; + else + phy->nrssislope = 0x00400000 / (nrssi0 - nrssi1); + + if (nrssi0 <= -4) { + phy->nrssi[0] = nrssi0; + phy->nrssi[1] = nrssi1; + } + break; + case BCM43xx_PHYTYPE_G: + if (phy->radio_rev >= 9) + return; + if (phy->radio_rev == 8) + bcm43xx_calc_nrssi_offset(dev); + + bcm43xx_phy_write(dev, BCM43xx_PHY_G_CRS, + bcm43xx_phy_read(dev, BCM43xx_PHY_G_CRS) & 0x7FFF); + bcm43xx_phy_write(dev, 0x0802, + bcm43xx_phy_read(dev, 0x0802) & 0xFFFC); + backup[7] = bcm43xx_read16(dev, 0x03E2); + bcm43xx_write16(dev, 0x03E2, + bcm43xx_read16(dev, 0x03E2) | 0x8000); + backup[0] = bcm43xx_radio_read16(dev, 0x007A); + backup[1] = bcm43xx_radio_read16(dev, 0x0052); + backup[2] = bcm43xx_radio_read16(dev, 0x0043); + backup[3] = bcm43xx_phy_read(dev, 0x0015); + backup[4] = bcm43xx_phy_read(dev, 0x005A); + backup[5] = bcm43xx_phy_read(dev, 0x0059); + backup[6] = bcm43xx_phy_read(dev, 0x0058); + backup[8] = bcm43xx_read16(dev, 0x03E6); + backup[9] = bcm43xx_read16(dev, BCM43xx_MMIO_CHANNEL_EXT); + if (phy->rev >= 3) { + backup[10] = bcm43xx_phy_read(dev, 0x002E); + backup[11] = bcm43xx_phy_read(dev, 0x002F); + backup[12] = bcm43xx_phy_read(dev, 0x080F); + backup[13] = bcm43xx_phy_read(dev, BCM43xx_PHY_G_LO_CONTROL); + backup[14] = bcm43xx_phy_read(dev, 0x0801); + backup[15] = bcm43xx_phy_read(dev, 0x0060); + backup[16] = bcm43xx_phy_read(dev, 0x0014); + backup[17] = bcm43xx_phy_read(dev, 0x0478); + bcm43xx_phy_write(dev, 0x002E, 0); + bcm43xx_phy_write(dev, BCM43xx_PHY_G_LO_CONTROL, 0); + switch (phy->rev) { + case 4: case 6: case 7: + bcm43xx_phy_write(dev, 0x0478, + bcm43xx_phy_read(dev, 0x0478) + | 0x0100); + bcm43xx_phy_write(dev, 0x0801, + bcm43xx_phy_read(dev, 0x0801) + | 0x0040); + break; + case 3: case 5: + bcm43xx_phy_write(dev, 0x0801, + bcm43xx_phy_read(dev, 0x0801) + & 0xFFBF); + break; + } + bcm43xx_phy_write(dev, 0x0060, + bcm43xx_phy_read(dev, 0x0060) + | 0x0040); + bcm43xx_phy_write(dev, 0x0014, + bcm43xx_phy_read(dev, 0x0014) + | 0x0200); + } + bcm43xx_radio_write16(dev, 0x007A, + bcm43xx_radio_read16(dev, 0x007A) | 0x0070); + bcm43xx_set_all_gains(dev, 0, 8, 0); + bcm43xx_radio_write16(dev, 0x007A, + bcm43xx_radio_read16(dev, 0x007A) & 0x00F7); + if (phy->rev >= 2) { + bcm43xx_phy_write(dev, 0x0811, + (bcm43xx_phy_read(dev, 0x0811) & 0xFFCF) | 0x0030); + bcm43xx_phy_write(dev, 0x0812, + (bcm43xx_phy_read(dev, 0x0812) & 0xFFCF) | 0x0010); + } + bcm43xx_radio_write16(dev, 0x007A, + bcm43xx_radio_read16(dev, 0x007A) | 0x0080); + udelay(20); + + nrssi0 = (s16)((bcm43xx_phy_read(dev, 0x047F) >> 8) & 0x003F); + if (nrssi0 >= 0x0020) + nrssi0 -= 0x0040; + + bcm43xx_radio_write16(dev, 0x007A, + bcm43xx_radio_read16(dev, 0x007A) & 0x007F); + if (phy->rev >= 2) { + bcm43xx_phy_write(dev, 0x0003, + (bcm43xx_phy_read(dev, 0x0003) + & 0xFF9F) | 0x0040); + } + + bcm43xx_write16(dev, BCM43xx_MMIO_CHANNEL_EXT, + bcm43xx_read16(dev, BCM43xx_MMIO_CHANNEL_EXT) + | 0x2000); + bcm43xx_radio_write16(dev, 0x007A, + bcm43xx_radio_read16(dev, 0x007A) | 0x000F); + bcm43xx_phy_write(dev, 0x0015, 0xF330); + if (phy->rev >= 2) { + bcm43xx_phy_write(dev, 0x0812, + (bcm43xx_phy_read(dev, 0x0812) & 0xFFCF) | 0x0020); + bcm43xx_phy_write(dev, 0x0811, + (bcm43xx_phy_read(dev, 0x0811) & 0xFFCF) | 0x0020); + } + + bcm43xx_set_all_gains(dev, 3, 0, 1); + if (phy->radio_rev == 8) { + bcm43xx_radio_write16(dev, 0x0043, 0x001F); + } else { + tmp = bcm43xx_radio_read16(dev, 0x0052) & 0xFF0F; + bcm43xx_radio_write16(dev, 0x0052, tmp | 0x0060); + tmp = bcm43xx_radio_read16(dev, 0x0043) & 0xFFF0; + bcm43xx_radio_write16(dev, 0x0043, tmp | 0x0009); + } + bcm43xx_phy_write(dev, 0x005A, 0x0480); + bcm43xx_phy_write(dev, 0x0059, 0x0810); + bcm43xx_phy_write(dev, 0x0058, 0x000D); + udelay(20); + nrssi1 = (s16)((bcm43xx_phy_read(dev, 0x047F) >> 8) & 0x003F); + if (nrssi1 >= 0x0020) + nrssi1 -= 0x0040; + if (nrssi0 == nrssi1) + phy->nrssislope = 0x00010000; + else + phy->nrssislope = 0x00400000 / (nrssi0 - nrssi1); + if (nrssi0 >= -4) { + phy->nrssi[0] = nrssi1; + phy->nrssi[1] = nrssi0; + } + if (phy->rev >= 3) { + bcm43xx_phy_write(dev, 0x002E, backup[10]); + bcm43xx_phy_write(dev, 0x002F, backup[11]); + bcm43xx_phy_write(dev, 0x080F, backup[12]); + bcm43xx_phy_write(dev, BCM43xx_PHY_G_LO_CONTROL, backup[13]); + } + if (phy->rev >= 2) { + bcm43xx_phy_write(dev, 0x0812, + bcm43xx_phy_read(dev, 0x0812) & 0xFFCF); + bcm43xx_phy_write(dev, 0x0811, + bcm43xx_phy_read(dev, 0x0811) & 0xFFCF); + } + + bcm43xx_radio_write16(dev, 0x007A, backup[0]); + bcm43xx_radio_write16(dev, 0x0052, backup[1]); + bcm43xx_radio_write16(dev, 0x0043, backup[2]); + bcm43xx_write16(dev, 0x03E2, backup[7]); + bcm43xx_write16(dev, 0x03E6, backup[8]); + bcm43xx_write16(dev, BCM43xx_MMIO_CHANNEL_EXT, backup[9]); + bcm43xx_phy_write(dev, 0x0015, backup[3]); + bcm43xx_phy_write(dev, 0x005A, backup[4]); + bcm43xx_phy_write(dev, 0x0059, backup[5]); + bcm43xx_phy_write(dev, 0x0058, backup[6]); + bcm43xx_synth_pu_workaround(dev, phy->channel); + bcm43xx_phy_write(dev, 0x0802, + bcm43xx_phy_read(dev, 0x0802) | (0x0001 | 0x0002)); + bcm43xx_set_original_gains(dev); + bcm43xx_phy_write(dev, BCM43xx_PHY_G_CRS, + bcm43xx_phy_read(dev, BCM43xx_PHY_G_CRS) | 0x8000); + if (phy->rev >= 3) { + bcm43xx_phy_write(dev, 0x0801, backup[14]); + bcm43xx_phy_write(dev, 0x0060, backup[15]); + bcm43xx_phy_write(dev, 0x0014, backup[16]); + bcm43xx_phy_write(dev, 0x0478, backup[17]); + } + bcm43xx_nrssi_mem_update(dev); + bcm43xx_calc_nrssi_threshold(dev); + break; + default: + assert(0); + } +} + +void bcm43xx_calc_nrssi_threshold(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + s32 threshold; + s32 a, b; + s16 tmp16; + u16 tmp_u16; + + switch (phy->type) { + case BCM43xx_PHYTYPE_B: { + if (phy->radio_ver != 0x2050) + return; + if (!(dev->dev->bus->sprom.r1.boardflags_lo & BCM43xx_BFL_RSSI)) + return; + + if (phy->radio_rev >= 6) { + threshold = (phy->nrssi[1] - phy->nrssi[0]) * 32; + threshold += 20 * (phy->nrssi[0] + 1); + threshold /= 40; + } else + threshold = phy->nrssi[1] - 5; + + threshold = limit_value(threshold, 0, 0x3E); + bcm43xx_phy_read(dev, 0x0020); /* dummy read */ + bcm43xx_phy_write(dev, 0x0020, (((u16)threshold) << 8) | 0x001C); + + if (phy->radio_rev >= 6) { + bcm43xx_phy_write(dev, 0x0087, 0x0E0D); + bcm43xx_phy_write(dev, 0x0086, 0x0C0B); + bcm43xx_phy_write(dev, 0x0085, 0x0A09); + bcm43xx_phy_write(dev, 0x0084, 0x0808); + bcm43xx_phy_write(dev, 0x0083, 0x0808); + bcm43xx_phy_write(dev, 0x0082, 0x0604); + bcm43xx_phy_write(dev, 0x0081, 0x0302); + bcm43xx_phy_write(dev, 0x0080, 0x0100); + } + break; + } + case BCM43xx_PHYTYPE_G: + if (!phy->gmode || + !(dev->dev->bus->sprom.r1.boardflags_lo & BCM43xx_BFL_RSSI)) { + tmp16 = bcm43xx_nrssi_hw_read(dev, 0x20); + if (tmp16 >= 0x20) + tmp16 -= 0x40; + if (tmp16 < 3) { + bcm43xx_phy_write(dev, 0x048A, + (bcm43xx_phy_read(dev, 0x048A) + & 0xF000) | 0x09EB); + } else { + bcm43xx_phy_write(dev, 0x048A, + (bcm43xx_phy_read(dev, 0x048A) + & 0xF000) | 0x0AED); + } + } else { + if (phy->interfmode == BCM43xx_INTERFMODE_NONWLAN) { + a = 0xE; + b = 0xA; + } else if (!phy->aci_wlan_automatic && phy->aci_enable) { + a = 0x13; + b = 0x12; + } else { + a = 0xE; + b = 0x11; + } + + a = a * (phy->nrssi[1] - phy->nrssi[0]); + a += (phy->nrssi[0] << 6); + if (a < 32) + a += 31; + else + a += 32; + a = a >> 6; + a = limit_value(a, -31, 31); + + b = b * (phy->nrssi[1] - phy->nrssi[0]); + b += (phy->nrssi[0] << 6); + if (b < 32) + b += 31; + else + b += 32; + b = b >> 6; + b = limit_value(b, -31, 31); + + tmp_u16 = bcm43xx_phy_read(dev, 0x048A) & 0xF000; + tmp_u16 |= ((u32)b & 0x0000003F); + tmp_u16 |= (((u32)a & 0x0000003F) << 6); + bcm43xx_phy_write(dev, 0x048A, tmp_u16); + } + break; + default: + assert(0); + } +} + +/* Stack implementation to save/restore values from the + * interference mitigation code. + * It is save to restore values in random order. + */ +static void _stack_save(u32 *_stackptr, size_t *stackidx, + u8 id, u16 offset, u16 value) +{ + u32 *stackptr = &(_stackptr[*stackidx]); + + assert((offset & 0xF000) == 0x0000); + assert((id & 0xF0) == 0x00); + *stackptr = offset; + *stackptr |= ((u32)id) << 12; + *stackptr |= ((u32)value) << 16; + (*stackidx)++; + assert(*stackidx < BCM43xx_INTERFSTACK_SIZE); +} + +static u16 _stack_restore(u32 *stackptr, + u8 id, u16 offset) +{ + size_t i; + + assert((offset & 0xF000) == 0x0000); + assert((id & 0xF0) == 0x00); + for (i = 0; i < BCM43xx_INTERFSTACK_SIZE; i++, stackptr++) { + if ((*stackptr & 0x00000FFF) != offset) + continue; + if (((*stackptr & 0x0000F000) >> 12) != id) + continue; + return ((*stackptr & 0xFFFF0000) >> 16); + } + assert(0); + + return 0; +} + +#define phy_stacksave(offset) \ + do { \ + _stack_save(stack, &stackidx, 0x1, (offset), \ + bcm43xx_phy_read(dev, (offset))); \ + } while (0) +#define phy_stackrestore(offset) \ + do { \ + bcm43xx_phy_write(dev, (offset), \ + _stack_restore(stack, 0x1, \ + (offset))); \ + } while (0) +#define radio_stacksave(offset) \ + do { \ + _stack_save(stack, &stackidx, 0x2, (offset), \ + bcm43xx_radio_read16(dev, (offset))); \ + } while (0) +#define radio_stackrestore(offset) \ + do { \ + bcm43xx_radio_write16(dev, (offset), \ + _stack_restore(stack, 0x2, \ + (offset))); \ + } while (0) +#define ofdmtab_stacksave(table, offset) \ + do { \ + _stack_save(stack, &stackidx, 0x3, (offset)|(table), \ + bcm43xx_ofdmtab_read16(dev, (table), (offset))); \ + } while (0) +#define ofdmtab_stackrestore(table, offset) \ + do { \ + bcm43xx_ofdmtab_write16(dev, (table), (offset), \ + _stack_restore(stack, 0x3, \ + (offset)|(table))); \ + } while (0) + +static void +bcm43xx_radio_interference_mitigation_enable(struct bcm43xx_wldev *dev, + int mode) +{ + struct bcm43xx_phy *phy = &dev->phy; + u16 tmp, flipped; + size_t stackidx = 0; + u32 *stack = phy->interfstack; + + switch (mode) { + case BCM43xx_INTERFMODE_NONWLAN: + if (phy->rev != 1) { + bcm43xx_phy_write(dev, 0x042B, + bcm43xx_phy_read(dev, 0x042B) | 0x0800); + bcm43xx_phy_write(dev, BCM43xx_PHY_G_CRS, + bcm43xx_phy_read(dev, BCM43xx_PHY_G_CRS) & ~0x4000); + break; + } + radio_stacksave(0x0078); + tmp = (bcm43xx_radio_read16(dev, 0x0078) & 0x001E); + flipped = flip_4bit(tmp); + if (flipped < 10 && flipped >= 8) + flipped = 7; + else if (flipped >= 10) + flipped -= 3; + flipped = flip_4bit(flipped); + flipped = (flipped << 1) | 0x0020; + bcm43xx_radio_write16(dev, 0x0078, flipped); + + bcm43xx_calc_nrssi_threshold(dev); + + phy_stacksave(0x0406); + bcm43xx_phy_write(dev, 0x0406, 0x7E28); + + bcm43xx_phy_write(dev, 0x042B, + bcm43xx_phy_read(dev, 0x042B) | 0x0800); + bcm43xx_phy_write(dev, BCM43xx_PHY_RADIO_BITFIELD, + bcm43xx_phy_read(dev, BCM43xx_PHY_RADIO_BITFIELD) | 0x1000); + + phy_stacksave(0x04A0); + bcm43xx_phy_write(dev, 0x04A0, + (bcm43xx_phy_read(dev, 0x04A0) & 0xC0C0) | 0x0008); + phy_stacksave(0x04A1); + bcm43xx_phy_write(dev, 0x04A1, + (bcm43xx_phy_read(dev, 0x04A1) & 0xC0C0) | 0x0605); + phy_stacksave(0x04A2); + bcm43xx_phy_write(dev, 0x04A2, + (bcm43xx_phy_read(dev, 0x04A2) & 0xC0C0) | 0x0204); + phy_stacksave(0x04A8); + bcm43xx_phy_write(dev, 0x04A8, + (bcm43xx_phy_read(dev, 0x04A8) & 0xC0C0) | 0x0803); + phy_stacksave(0x04AB); + bcm43xx_phy_write(dev, 0x04AB, + (bcm43xx_phy_read(dev, 0x04AB) & 0xC0C0) | 0x0605); + + phy_stacksave(0x04A7); + bcm43xx_phy_write(dev, 0x04A7, 0x0002); + phy_stacksave(0x04A3); + bcm43xx_phy_write(dev, 0x04A3, 0x287A); + phy_stacksave(0x04A9); + bcm43xx_phy_write(dev, 0x04A9, 0x2027); + phy_stacksave(0x0493); + bcm43xx_phy_write(dev, 0x0493, 0x32F5); + phy_stacksave(0x04AA); + bcm43xx_phy_write(dev, 0x04AA, 0x2027); + phy_stacksave(0x04AC); + bcm43xx_phy_write(dev, 0x04AC, 0x32F5); + break; + case BCM43xx_INTERFMODE_MANUALWLAN: + if (bcm43xx_phy_read(dev, 0x0033) & 0x0800) + break; + + phy->aci_enable = 1; + + phy_stacksave(BCM43xx_PHY_RADIO_BITFIELD); + phy_stacksave(BCM43xx_PHY_G_CRS); + if (phy->rev < 2) { + phy_stacksave(0x0406); + } else { + phy_stacksave(0x04C0); + phy_stacksave(0x04C1); + } + phy_stacksave(0x0033); + phy_stacksave(0x04A7); + phy_stacksave(0x04A3); + phy_stacksave(0x04A9); + phy_stacksave(0x04AA); + phy_stacksave(0x04AC); + phy_stacksave(0x0493); + phy_stacksave(0x04A1); + phy_stacksave(0x04A0); + phy_stacksave(0x04A2); + phy_stacksave(0x048A); + phy_stacksave(0x04A8); + phy_stacksave(0x04AB); + if (phy->rev == 2) { + phy_stacksave(0x04AD); + phy_stacksave(0x04AE); + } else if (phy->rev >= 3) { + phy_stacksave(0x04AD); + phy_stacksave(0x0415); + phy_stacksave(0x0416); + phy_stacksave(0x0417); + ofdmtab_stacksave(0x1A00, 0x2); + ofdmtab_stacksave(0x1A00, 0x3); + } + phy_stacksave(0x042B); + phy_stacksave(0x048C); + + bcm43xx_phy_write(dev, BCM43xx_PHY_RADIO_BITFIELD, + bcm43xx_phy_read(dev, BCM43xx_PHY_RADIO_BITFIELD) + & ~0x1000); + bcm43xx_phy_write(dev, BCM43xx_PHY_G_CRS, + (bcm43xx_phy_read(dev, BCM43xx_PHY_G_CRS) + & 0xFFFC) | 0x0002); + + bcm43xx_phy_write(dev, 0x0033, 0x0800); + bcm43xx_phy_write(dev, 0x04A3, 0x2027); + bcm43xx_phy_write(dev, 0x04A9, 0x1CA8); + bcm43xx_phy_write(dev, 0x0493, 0x287A); + bcm43xx_phy_write(dev, 0x04AA, 0x1CA8); + bcm43xx_phy_write(dev, 0x04AC, 0x287A); + + bcm43xx_phy_write(dev, 0x04A0, + (bcm43xx_phy_read(dev, 0x04A0) + & 0xFFC0) | 0x001A); + bcm43xx_phy_write(dev, 0x04A7, 0x000D); + + if (phy->rev < 2) { + bcm43xx_phy_write(dev, 0x0406, 0xFF0D); + } else if (phy->rev == 2) { + bcm43xx_phy_write(dev, 0x04C0, 0xFFFF); + bcm43xx_phy_write(dev, 0x04C1, 0x00A9); + } else { + bcm43xx_phy_write(dev, 0x04C0, 0x00C1); + bcm43xx_phy_write(dev, 0x04C1, 0x0059); + } + + bcm43xx_phy_write(dev, 0x04A1, + (bcm43xx_phy_read(dev, 0x04A1) + & 0xC0FF) | 0x1800); + bcm43xx_phy_write(dev, 0x04A1, + (bcm43xx_phy_read(dev, 0x04A1) + & 0xFFC0) | 0x0015); + bcm43xx_phy_write(dev, 0x04A8, + (bcm43xx_phy_read(dev, 0x04A8) + & 0xCFFF) | 0x1000); + bcm43xx_phy_write(dev, 0x04A8, + (bcm43xx_phy_read(dev, 0x04A8) + & 0xF0FF) | 0x0A00); + bcm43xx_phy_write(dev, 0x04AB, + (bcm43xx_phy_read(dev, 0x04AB) + & 0xCFFF) | 0x1000); + bcm43xx_phy_write(dev, 0x04AB, + (bcm43xx_phy_read(dev, 0x04AB) + & 0xF0FF) | 0x0800); + bcm43xx_phy_write(dev, 0x04AB, + (bcm43xx_phy_read(dev, 0x04AB) + & 0xFFCF) | 0x0010); + bcm43xx_phy_write(dev, 0x04AB, + (bcm43xx_phy_read(dev, 0x04AB) + & 0xFFF0) | 0x0005); + bcm43xx_phy_write(dev, 0x04A8, + (bcm43xx_phy_read(dev, 0x04A8) + & 0xFFCF) | 0x0010); + bcm43xx_phy_write(dev, 0x04A8, + (bcm43xx_phy_read(dev, 0x04A8) + & 0xFFF0) | 0x0006); + bcm43xx_phy_write(dev, 0x04A2, + (bcm43xx_phy_read(dev, 0x04A2) + & 0xF0FF) | 0x0800); + bcm43xx_phy_write(dev, 0x04A0, + (bcm43xx_phy_read(dev, 0x04A0) + & 0xF0FF) | 0x0500); + bcm43xx_phy_write(dev, 0x04A2, + (bcm43xx_phy_read(dev, 0x04A2) + & 0xFFF0) | 0x000B); + + if (phy->rev >= 3) { + bcm43xx_phy_write(dev, 0x048A, + bcm43xx_phy_read(dev, 0x048A) + & ~0x8000); + bcm43xx_phy_write(dev, 0x0415, + (bcm43xx_phy_read(dev, 0x0415) + & 0x8000) | 0x36D8); + bcm43xx_phy_write(dev, 0x0416, + (bcm43xx_phy_read(dev, 0x0416) + & 0x8000) | 0x36D8); + bcm43xx_phy_write(dev, 0x0417, + (bcm43xx_phy_read(dev, 0x0417) + & 0xFE00) | 0x016D); + } else { + bcm43xx_phy_write(dev, 0x048A, + bcm43xx_phy_read(dev, 0x048A) + | 0x1000); + bcm43xx_phy_write(dev, 0x048A, + (bcm43xx_phy_read(dev, 0x048A) + & 0x9FFF) | 0x2000); + bcm43xx_hf_write(dev, bcm43xx_hf_read(dev) | BCM43xx_HF_ACIW); + } + if (phy->rev >= 2) { + bcm43xx_phy_write(dev, 0x042B, + bcm43xx_phy_read(dev, 0x042B) + | 0x0800); + } + bcm43xx_phy_write(dev, 0x048C, + (bcm43xx_phy_read(dev, 0x048C) + & 0xF0FF) | 0x0200); + if (phy->rev == 2) { + bcm43xx_phy_write(dev, 0x04AE, + (bcm43xx_phy_read(dev, 0x04AE) + & 0xFF00) | 0x007F); + bcm43xx_phy_write(dev, 0x04AD, + (bcm43xx_phy_read(dev, 0x04AD) + & 0x00FF) | 0x1300); + } else if (phy->rev >= 6) { + bcm43xx_ofdmtab_write16(dev, 0x1A00, 0x3, 0x007F); + bcm43xx_ofdmtab_write16(dev, 0x1A00, 0x2, 0x007F); + bcm43xx_phy_write(dev, 0x04AD, + bcm43xx_phy_read(dev, 0x04AD) + & 0x00FF); + } + bcm43xx_calc_nrssi_slope(dev); + break; + default: + assert(0); + } +} + +static void +bcm43xx_radio_interference_mitigation_disable(struct bcm43xx_wldev *dev, + int mode) +{ + struct bcm43xx_phy *phy = &dev->phy; + u32 *stack = phy->interfstack; + + switch (mode) { + case BCM43xx_INTERFMODE_NONWLAN: + if (phy->rev != 1) { + bcm43xx_phy_write(dev, 0x042B, + bcm43xx_phy_read(dev, 0x042B) & ~0x0800); + bcm43xx_phy_write(dev, BCM43xx_PHY_G_CRS, + bcm43xx_phy_read(dev, BCM43xx_PHY_G_CRS) | 0x4000); + break; + } + radio_stackrestore(0x0078); + bcm43xx_calc_nrssi_threshold(dev); + phy_stackrestore(0x0406); + bcm43xx_phy_write(dev, 0x042B, + bcm43xx_phy_read(dev, 0x042B) & ~0x0800); + if (!dev->bad_frames_preempt) { + bcm43xx_phy_write(dev, BCM43xx_PHY_RADIO_BITFIELD, + bcm43xx_phy_read(dev, BCM43xx_PHY_RADIO_BITFIELD) + & ~(1 << 11)); + } + bcm43xx_phy_write(dev, BCM43xx_PHY_G_CRS, + bcm43xx_phy_read(dev, BCM43xx_PHY_G_CRS) | 0x4000); + phy_stackrestore(0x04A0); + phy_stackrestore(0x04A1); + phy_stackrestore(0x04A2); + phy_stackrestore(0x04A8); + phy_stackrestore(0x04AB); + phy_stackrestore(0x04A7); + phy_stackrestore(0x04A3); + phy_stackrestore(0x04A9); + phy_stackrestore(0x0493); + phy_stackrestore(0x04AA); + phy_stackrestore(0x04AC); + break; + case BCM43xx_INTERFMODE_MANUALWLAN: + if (!(bcm43xx_phy_read(dev, 0x0033) & 0x0800)) + break; + + phy->aci_enable = 0; + + phy_stackrestore(BCM43xx_PHY_RADIO_BITFIELD); + phy_stackrestore(BCM43xx_PHY_G_CRS); + phy_stackrestore(0x0033); + phy_stackrestore(0x04A3); + phy_stackrestore(0x04A9); + phy_stackrestore(0x0493); + phy_stackrestore(0x04AA); + phy_stackrestore(0x04AC); + phy_stackrestore(0x04A0); + phy_stackrestore(0x04A7); + if (phy->rev >= 2) { + phy_stackrestore(0x04C0); + phy_stackrestore(0x04C1); + } else + phy_stackrestore(0x0406); + phy_stackrestore(0x04A1); + phy_stackrestore(0x04AB); + phy_stackrestore(0x04A8); + if (phy->rev == 2) { + phy_stackrestore(0x04AD); + phy_stackrestore(0x04AE); + } else if (phy->rev >= 3) { + phy_stackrestore(0x04AD); + phy_stackrestore(0x0415); + phy_stackrestore(0x0416); + phy_stackrestore(0x0417); + ofdmtab_stackrestore(0x1A00, 0x2); + ofdmtab_stackrestore(0x1A00, 0x3); + } + phy_stackrestore(0x04A2); + phy_stackrestore(0x048A); + phy_stackrestore(0x042B); + phy_stackrestore(0x048C); + bcm43xx_hf_write(dev, bcm43xx_hf_read(dev) & ~BCM43xx_HF_ACIW); + bcm43xx_calc_nrssi_slope(dev); + break; + default: + assert(0); + } +} + +#undef phy_stacksave +#undef phy_stackrestore +#undef radio_stacksave +#undef radio_stackrestore +#undef ofdmtab_stacksave +#undef ofdmtab_stackrestore + +int bcm43xx_radio_set_interference_mitigation(struct bcm43xx_wldev *dev, + int mode) +{ + struct bcm43xx_phy *phy = &dev->phy; + int currentmode; + + if ((phy->type != BCM43xx_PHYTYPE_G) || + (phy->rev == 0) || + (!phy->gmode)) + return -ENODEV; + + phy->aci_wlan_automatic = 0; + switch (mode) { + case BCM43xx_INTERFMODE_AUTOWLAN: + phy->aci_wlan_automatic = 1; + if (phy->aci_enable) + mode = BCM43xx_INTERFMODE_MANUALWLAN; + else + mode = BCM43xx_INTERFMODE_NONE; + break; + case BCM43xx_INTERFMODE_NONE: + case BCM43xx_INTERFMODE_NONWLAN: + case BCM43xx_INTERFMODE_MANUALWLAN: + break; + default: + return -EINVAL; + } + + currentmode = phy->interfmode; + if (currentmode == mode) + return 0; + if (currentmode != BCM43xx_INTERFMODE_NONE) + bcm43xx_radio_interference_mitigation_disable(dev, currentmode); + + if (mode == BCM43xx_INTERFMODE_NONE) { + phy->aci_enable = 0; + phy->aci_hw_rssi = 0; + } else + bcm43xx_radio_interference_mitigation_enable(dev, mode); + phy->interfmode = mode; + + return 0; +} + +static u16 bcm43xx_radio_core_calibration_value(struct bcm43xx_wldev *dev) +{ + u16 reg, index, ret; + + static const u8 rcc_table[] = { + 0x02, 0x03, 0x01, 0x0F, + 0x06, 0x07, 0x05, 0x0F, + 0x0A, 0x0B, 0x09, 0x0F, + 0x0E, 0x0F, 0x0D, 0x0F, + }; + + reg = bcm43xx_radio_read16(dev, 0x60); + index = (reg & 0x001E) >> 1; + ret = rcc_table[index] << 1; + ret |= (reg & 0x0001); + ret |= 0x0020; + + return ret; +} + +#define LPD(L, P, D) (((L) << 2) | ((P) << 1) | ((D) << 0)) +static u16 radio2050_rfover_val(struct bcm43xx_wldev *dev, + u16 phy_register, + unsigned int lpd) +{ + struct bcm43xx_phy *phy = &dev->phy; + struct ssb_sprom *sprom = &(dev->dev->bus->sprom); + + if (!phy->gmode) + return 0; + + if (has_loopback_gain(phy)) { + int max_lb_gain = phy->max_lb_gain; + u16 extlna; + u16 i; + + if (phy->radio_rev == 8) + max_lb_gain += 0x3E; + else + max_lb_gain += 0x26; + if (max_lb_gain >= 0x46) { + extlna = 0x3000; + max_lb_gain -= 0x46; + } else if (max_lb_gain >= 0x3A) { + extlna = 0x1000; + max_lb_gain -= 0x3A; + } else if (max_lb_gain >= 0x2E) { + extlna = 0x2000; + max_lb_gain -= 0x2E; + } else { + extlna = 0; + max_lb_gain -= 0x10; + } + + for (i = 0; i < 16; i++) { + max_lb_gain -= (i * 6); + if (max_lb_gain < 6) + break; + } + + if ((phy->rev < 7) || + !(sprom->r1.boardflags_lo & BCM43xx_BFL_EXTLNA)) { + if (phy_register == BCM43xx_PHY_RFOVER) { + return 0x1B3; + } else if (phy_register == BCM43xx_PHY_RFOVERVAL) { + extlna |= (i << 8); + switch (lpd) { + case LPD(0, 1, 1): + return 0x0F92; + case LPD(0, 0, 1): + case LPD(1, 0, 1): + return (0x0092 | extlna); + case LPD(1, 0, 0): + return (0x0093 | extlna); + } + assert(0); + } + assert(0); + } else { + if (phy_register == BCM43xx_PHY_RFOVER) { + return 0x9B3; + } else if (phy_register == BCM43xx_PHY_RFOVERVAL) { + if (extlna) + extlna |= 0x8000; + extlna |= (i << 8); + switch (lpd) { + case LPD(0, 1, 1): + return 0x8F92; + case LPD(0, 0, 1): + return (0x8092 | extlna); + case LPD(1, 0, 1): + return (0x2092 | extlna); + case LPD(1, 0, 0): + return (0x2093 | extlna); + } + assert(0); + } + assert(0); + } + } else { + if ((phy->rev < 7) || + !(sprom->r1.boardflags_lo & BCM43xx_BFL_EXTLNA)) { + if (phy_register == BCM43xx_PHY_RFOVER) { + return 0x1B3; + } else if (phy_register == BCM43xx_PHY_RFOVERVAL) { + switch (lpd) { + case LPD(0, 1, 1): + return 0x0FB2; + case LPD(0, 0, 1): + return 0x00B2; + case LPD(1, 0, 1): + return 0x30B2; + case LPD(1, 0, 0): + return 0x30B3; + } + assert(0); + } + assert(0); + } else { + if (phy_register == BCM43xx_PHY_RFOVER) { + return 0x9B3; + } else if (phy_register == BCM43xx_PHY_RFOVERVAL) { + switch (lpd) { + case LPD(0, 1, 1): + return 0x8FB2; + case LPD(0, 0, 1): + return 0x80B2; + case LPD(1, 0, 1): + return 0x20B2; + case LPD(1, 0, 0): + return 0x20B3; + } + assert(0); + } + assert(0); + } + } + return 0; +} + +struct init2050_saved_values { + /* Core registers */ + u16 reg_3EC; + u16 reg_3E6; + u16 reg_3F4; + /* Radio registers */ + u16 radio_43; + u16 radio_51; + u16 radio_52; + /* PHY registers */ + u16 phy_pgactl; + u16 phy_base_5A; + u16 phy_base_59; + u16 phy_base_58; + u16 phy_base_30; + u16 phy_rfover; + u16 phy_rfoverval; + u16 phy_analogover; + u16 phy_analogoverval; + u16 phy_crs0; + u16 phy_classctl; + u16 phy_lo_mask; + u16 phy_lo_ctl; + u16 phy_syncctl; +}; + +u16 bcm43xx_radio_init2050(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + struct init2050_saved_values sav; + u16 rcc; + u16 radio78; + u16 ret; + u16 i, j; + u32 tmp1 = 0, tmp2 = 0; + + memset(&sav, 0, sizeof(sav)); /* get rid of "may be used uninitialized..." */ + + sav.radio_43 = bcm43xx_radio_read16(dev, 0x43); + sav.radio_51 = bcm43xx_radio_read16(dev, 0x51); + sav.radio_52 = bcm43xx_radio_read16(dev, 0x52); + sav.phy_pgactl = bcm43xx_phy_read(dev, BCM43xx_PHY_PGACTL); + sav.phy_base_5A = bcm43xx_phy_read(dev, BCM43xx_PHY_BASE(0x5A)); + sav.phy_base_59 = bcm43xx_phy_read(dev, BCM43xx_PHY_BASE(0x59)); + sav.phy_base_58 = bcm43xx_phy_read(dev, BCM43xx_PHY_BASE(0x58)); + + if (phy->type == BCM43xx_PHYTYPE_B) { + sav.phy_base_30 = bcm43xx_phy_read(dev, BCM43xx_PHY_BASE(0x30)); + sav.reg_3EC = bcm43xx_read16(dev, 0x3EC); + + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x30), 0xFF); + bcm43xx_write16(dev, 0x3EC, 0x3F3F); + } else if (phy->gmode || phy->rev >= 2) { + sav.phy_rfover = bcm43xx_phy_read(dev, BCM43xx_PHY_RFOVER); + sav.phy_rfoverval = bcm43xx_phy_read(dev, BCM43xx_PHY_RFOVERVAL); + sav.phy_analogover = bcm43xx_phy_read(dev, BCM43xx_PHY_ANALOGOVER); + sav.phy_analogoverval = bcm43xx_phy_read(dev, BCM43xx_PHY_ANALOGOVERVAL); + sav.phy_crs0 = bcm43xx_phy_read(dev, BCM43xx_PHY_CRS0); + sav.phy_classctl = bcm43xx_phy_read(dev, BCM43xx_PHY_CLASSCTL); + + bcm43xx_phy_write(dev, BCM43xx_PHY_ANALOGOVER, + bcm43xx_phy_read(dev, BCM43xx_PHY_ANALOGOVER) + | 0x0003); + bcm43xx_phy_write(dev, BCM43xx_PHY_ANALOGOVERVAL, + bcm43xx_phy_read(dev, BCM43xx_PHY_ANALOGOVERVAL) + & 0xFFFC); + bcm43xx_phy_write(dev, BCM43xx_PHY_CRS0, + bcm43xx_phy_read(dev, BCM43xx_PHY_CRS0) + & 0x7FFF); + bcm43xx_phy_write(dev, BCM43xx_PHY_CLASSCTL, + bcm43xx_phy_read(dev, BCM43xx_PHY_CLASSCTL) + & 0xFFFC); + if (has_loopback_gain(phy)) { + sav.phy_lo_mask = bcm43xx_phy_read(dev, BCM43xx_PHY_LO_MASK); + sav.phy_lo_ctl = bcm43xx_phy_read(dev, BCM43xx_PHY_LO_CTL); + + if (phy->rev >= 3) + bcm43xx_phy_write(dev, BCM43xx_PHY_LO_MASK, 0xC020); + else + bcm43xx_phy_write(dev, BCM43xx_PHY_LO_MASK, 0x8020); + bcm43xx_phy_write(dev, BCM43xx_PHY_LO_CTL, 0); + } + + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVERVAL, + radio2050_rfover_val(dev, BCM43xx_PHY_RFOVERVAL, + LPD(0, 1, 1))); + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVER, + radio2050_rfover_val(dev, BCM43xx_PHY_RFOVER, 0)); + } + bcm43xx_write16(dev, 0x3E2, bcm43xx_read16(dev, 0x3E2) | 0x8000); + + sav.phy_syncctl = bcm43xx_phy_read(dev, BCM43xx_PHY_SYNCCTL); + bcm43xx_phy_write(dev, BCM43xx_PHY_SYNCCTL, + bcm43xx_phy_read(dev, BCM43xx_PHY_SYNCCTL) + & 0xFF7F); + sav.reg_3E6 = bcm43xx_read16(dev, 0x3E6); + sav.reg_3F4 = bcm43xx_read16(dev, 0x3F4); + + if (phy->analog == 0) { + bcm43xx_write16(dev, 0x03E6, 0x0122); + } else { + if (phy->analog >= 2) { + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x03), + (bcm43xx_phy_read(dev, BCM43xx_PHY_BASE(0x03)) + & 0xFFBF) | 0x40); + } + bcm43xx_write16(dev, BCM43xx_MMIO_CHANNEL_EXT, + (bcm43xx_read16(dev, BCM43xx_MMIO_CHANNEL_EXT) | 0x2000)); + } + + rcc = bcm43xx_radio_core_calibration_value(dev); + + if (phy->type == BCM43xx_PHYTYPE_B) + bcm43xx_radio_write16(dev, 0x78, 0x26); + if (phy->gmode || phy->rev >= 2) { + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVERVAL, + radio2050_rfover_val(dev, BCM43xx_PHY_RFOVERVAL, + LPD(0, 1, 1))); + } + bcm43xx_phy_write(dev, BCM43xx_PHY_PGACTL, 0xBFAF); + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x2B), 0x1403); + if (phy->gmode || phy->rev >= 2) { + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVERVAL, + radio2050_rfover_val(dev, BCM43xx_PHY_RFOVERVAL, + LPD(0, 0, 1))); + } + bcm43xx_phy_write(dev, BCM43xx_PHY_PGACTL, 0xBFA0); + bcm43xx_radio_write16(dev, 0x51, + bcm43xx_radio_read16(dev, 0x51) + | 0x0004); + if (phy->radio_rev == 8) { + bcm43xx_radio_write16(dev, 0x43, 0x1F); + } else { + bcm43xx_radio_write16(dev, 0x52, 0); + bcm43xx_radio_write16(dev, 0x43, + (bcm43xx_radio_read16(dev, 0x43) + & 0xFFF0) | 0x0009); + } + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x58), 0); + + for (i = 0; i < 16; i++) { + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x5A), 0x0480); + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x59), 0xC810); + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x58), 0x000D); + if (phy->gmode || phy->rev >= 2) { + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVERVAL, + radio2050_rfover_val(dev, BCM43xx_PHY_RFOVERVAL, + LPD(1, 0, 1))); + } + bcm43xx_phy_write(dev, BCM43xx_PHY_PGACTL, 0xAFB0); + udelay(10); + if (phy->gmode || phy->rev >= 2) { + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVERVAL, + radio2050_rfover_val(dev, BCM43xx_PHY_RFOVERVAL, + LPD(1, 0, 1))); + } + bcm43xx_phy_write(dev, BCM43xx_PHY_PGACTL, 0xEFB0); + udelay(10); + if (phy->gmode || phy->rev >= 2) { + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVERVAL, + radio2050_rfover_val(dev, BCM43xx_PHY_RFOVERVAL, + LPD(1, 0, 0))); + } + bcm43xx_phy_write(dev, BCM43xx_PHY_PGACTL, 0xFFF0); + udelay(20); + tmp1 += bcm43xx_phy_read(dev, BCM43xx_PHY_LO_LEAKAGE); + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x58), 0); + if (phy->gmode || phy->rev >= 2) { + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVERVAL, + radio2050_rfover_val(dev, BCM43xx_PHY_RFOVERVAL, + LPD(1, 0, 1))); + } + bcm43xx_phy_write(dev, BCM43xx_PHY_PGACTL, 0xAFB0); + } + udelay(10); + + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x58), 0); + tmp1++; + tmp1 >>= 9; + + for (i = 0; i < 16; i++) { + radio78 = ((flip_4bit(i) << 1) | 0x20); + bcm43xx_radio_write16(dev, 0x78, radio78); + udelay(10); + for (j = 0; j < 16; j++) { + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x5A), 0x0D80); + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x59), 0xC810); + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x58), 0x000D); + if (phy->gmode || phy->rev >= 2) { + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVERVAL, + radio2050_rfover_val(dev, BCM43xx_PHY_RFOVERVAL, + LPD(1, 0, 1))); + } + bcm43xx_phy_write(dev, BCM43xx_PHY_PGACTL, 0xAFB0); + udelay(10); + if (phy->gmode || phy->rev >= 2) { + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVERVAL, + radio2050_rfover_val(dev, BCM43xx_PHY_RFOVERVAL, + LPD(1, 0, 1))); + } + bcm43xx_phy_write(dev, BCM43xx_PHY_PGACTL, 0xEFB0); + udelay(10); + if (phy->gmode || phy->rev >= 2) { + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVERVAL, + radio2050_rfover_val(dev, BCM43xx_PHY_RFOVERVAL, + LPD(1, 0, 0))); + } + bcm43xx_phy_write(dev, BCM43xx_PHY_PGACTL, 0xFFF0); + udelay(10); + tmp2 += bcm43xx_phy_read(dev, BCM43xx_PHY_LO_LEAKAGE); + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x58), 0); + if (phy->gmode || phy->rev >= 2) { + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVERVAL, + radio2050_rfover_val(dev, BCM43xx_PHY_RFOVERVAL, + LPD(1, 0, 1))); + } + bcm43xx_phy_write(dev, BCM43xx_PHY_PGACTL, 0xAFB0); + } + tmp2++; + tmp2 >>= 8; + if (tmp1 < tmp2) + break; + } + + /* Restore the registers */ + bcm43xx_phy_write(dev, BCM43xx_PHY_PGACTL, sav.phy_pgactl); + bcm43xx_radio_write16(dev, 0x51, sav.radio_51); + bcm43xx_radio_write16(dev, 0x52, sav.radio_52); + bcm43xx_radio_write16(dev, 0x43, sav.radio_43); + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x5A), sav.phy_base_5A); + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x59), sav.phy_base_59); + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x58), sav.phy_base_58); + bcm43xx_write16(dev, 0x3E6, sav.reg_3E6); + if (phy->analog != 0) + bcm43xx_write16(dev, 0x3F4, sav.reg_3F4); + bcm43xx_phy_write(dev, BCM43xx_PHY_SYNCCTL, sav.phy_syncctl); + bcm43xx_synth_pu_workaround(dev, phy->channel); + if (phy->type == BCM43xx_PHYTYPE_B) { + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x30), sav.phy_base_30); + bcm43xx_write16(dev, 0x3EC, sav.reg_3EC); + } else if (phy->gmode) { + bcm43xx_write16(dev, BCM43xx_MMIO_PHY_RADIO, + bcm43xx_read16(dev, BCM43xx_MMIO_PHY_RADIO) + & 0x7FFF); + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVER, sav.phy_rfover); + bcm43xx_phy_write(dev, BCM43xx_PHY_RFOVERVAL, sav.phy_rfoverval); + bcm43xx_phy_write(dev, BCM43xx_PHY_ANALOGOVER, sav.phy_analogover); + bcm43xx_phy_write(dev, BCM43xx_PHY_ANALOGOVERVAL, sav.phy_analogoverval); + bcm43xx_phy_write(dev, BCM43xx_PHY_CRS0, sav.phy_crs0); + bcm43xx_phy_write(dev, BCM43xx_PHY_CLASSCTL, sav.phy_classctl); + if (has_loopback_gain(phy)) { + bcm43xx_phy_write(dev, BCM43xx_PHY_LO_MASK, sav.phy_lo_mask); + bcm43xx_phy_write(dev, BCM43xx_PHY_LO_CTL, sav.phy_lo_ctl); + } + } + if (i > 15) + ret = radio78; + else + ret = rcc; + + return ret; +} + +void bcm43xx_radio_init2060(struct bcm43xx_wldev *dev) +{ + int err; + + bcm43xx_radio_write16(dev, 0x0004, 0x00C0); + bcm43xx_radio_write16(dev, 0x0005, 0x0008); + bcm43xx_radio_write16(dev, 0x0009, 0x0040); + bcm43xx_radio_write16(dev, 0x0005, 0x00AA); + bcm43xx_radio_write16(dev, 0x0032, 0x008F); + bcm43xx_radio_write16(dev, 0x0006, 0x008F); + bcm43xx_radio_write16(dev, 0x0034, 0x008F); + bcm43xx_radio_write16(dev, 0x002C, 0x0007); + bcm43xx_radio_write16(dev, 0x0082, 0x0080); + bcm43xx_radio_write16(dev, 0x0080, 0x0000); + bcm43xx_radio_write16(dev, 0x003F, 0x00DA); + bcm43xx_radio_write16(dev, 0x0005, bcm43xx_radio_read16(dev, 0x0005) & ~0x0008); + bcm43xx_radio_write16(dev, 0x0081, bcm43xx_radio_read16(dev, 0x0081) & ~0x0010); + bcm43xx_radio_write16(dev, 0x0081, bcm43xx_radio_read16(dev, 0x0081) & ~0x0020); + bcm43xx_radio_write16(dev, 0x0081, bcm43xx_radio_read16(dev, 0x0081) & ~0x0020); + udelay(400); + + bcm43xx_radio_write16(dev, 0x0081, (bcm43xx_radio_read16(dev, 0x0081) & ~0x0020) | 0x0010); + udelay(400); + + bcm43xx_radio_write16(dev, 0x0005, (bcm43xx_radio_read16(dev, 0x0005) & ~0x0008) | 0x0008); + bcm43xx_radio_write16(dev, 0x0085, bcm43xx_radio_read16(dev, 0x0085) & ~0x0010); + bcm43xx_radio_write16(dev, 0x0005, bcm43xx_radio_read16(dev, 0x0005) & ~0x0008); + bcm43xx_radio_write16(dev, 0x0081, bcm43xx_radio_read16(dev, 0x0081) & ~0x0040); + bcm43xx_radio_write16(dev, 0x0081, (bcm43xx_radio_read16(dev, 0x0081) & ~0x0040) | 0x0040); + bcm43xx_radio_write16(dev, 0x0005, (bcm43xx_radio_read16(dev, 0x0081) & ~0x0008) | 0x0008); + bcm43xx_phy_write(dev, 0x0063, 0xDDC6); + bcm43xx_phy_write(dev, 0x0069, 0x07BE); + bcm43xx_phy_write(dev, 0x006A, 0x0000); + + err = bcm43xx_radio_selectchannel(dev, BCM43xx_DEFAULT_CHANNEL_A, 0); + assert(err == 0); + udelay(1000); +} + +static inline +u16 freq_r3A_value(u16 frequency) +{ + u16 value; + + if (frequency < 5091) + value = 0x0040; + else if (frequency < 5321) + value = 0x0000; + else if (frequency < 5806) + value = 0x0080; + else + value = 0x0040; + + return value; +} + +void bcm43xx_radio_set_tx_iq(struct bcm43xx_wldev *dev) +{ + static const u8 data_high[5] = { 0x00, 0x40, 0x80, 0x90, 0xD0 }; + static const u8 data_low[5] = { 0x00, 0x01, 0x05, 0x06, 0x0A }; + u16 tmp = bcm43xx_radio_read16(dev, 0x001E); + int i, j; + + for (i = 0; i < 5; i++) { + for (j = 0; j < 5; j++) { + if (tmp == (data_high[i] << 4 | data_low[j])) { + bcm43xx_phy_write(dev, 0x0069, (i - j) << 8 | 0x00C0); + return; + } + } + } +} + +int bcm43xx_radio_selectchannel(struct bcm43xx_wldev *dev, + u8 channel, + int synthetic_pu_workaround) +{ + struct bcm43xx_phy *phy = &dev->phy; + u16 r8, tmp; + u16 freq; + u16 channelcookie; + + /* First we set the channel radio code to prevent the + * firmware from sending ghost packets. + */ + channelcookie = channel; + if (phy->type == BCM43xx_PHYTYPE_A) + channelcookie |= 0x100; + bcm43xx_shm_write16(dev, BCM43xx_SHM_SHARED, + BCM43xx_SHM_SH_CHAN, channelcookie); + + if (phy->type == BCM43xx_PHYTYPE_A) { + if (channel > 200) + return -EINVAL; + freq = channel2freq_a(channel); + + r8 = bcm43xx_radio_read16(dev, 0x0008); + bcm43xx_write16(dev, 0x03F0, freq); + bcm43xx_radio_write16(dev, 0x0008, r8); + + TODO();//TODO: write max channel TX power? to Radio 0x2D + tmp = bcm43xx_radio_read16(dev, 0x002E); + tmp &= 0x0080; + TODO();//TODO: OR tmp with the Power out estimation for this channel? + bcm43xx_radio_write16(dev, 0x002E, tmp); + + if (freq >= 4920 && freq <= 5500) { + /* + * r8 = (((freq * 15 * 0xE1FC780F) >> 32) / 29) & 0x0F; + * = (freq * 0.025862069 + */ + r8 = 3 * freq / 116; /* is equal to r8 = freq * 0.025862 */ + } + bcm43xx_radio_write16(dev, 0x0007, (r8 << 4) | r8); + bcm43xx_radio_write16(dev, 0x0020, (r8 << 4) | r8); + bcm43xx_radio_write16(dev, 0x0021, (r8 << 4) | r8); + bcm43xx_radio_write16(dev, 0x0022, + (bcm43xx_radio_read16(dev, 0x0022) + & 0x000F) | (r8 << 4)); + bcm43xx_radio_write16(dev, 0x002A, (r8 << 4)); + bcm43xx_radio_write16(dev, 0x002B, (r8 << 4)); + bcm43xx_radio_write16(dev, 0x0008, + (bcm43xx_radio_read16(dev, 0x0008) + & 0x00F0) | (r8 << 4)); + bcm43xx_radio_write16(dev, 0x0029, + (bcm43xx_radio_read16(dev, 0x0029) + & 0xFF0F) | 0x00B0); + bcm43xx_radio_write16(dev, 0x0035, 0x00AA); + bcm43xx_radio_write16(dev, 0x0036, 0x0085); + bcm43xx_radio_write16(dev, 0x003A, + (bcm43xx_radio_read16(dev, 0x003A) + & 0xFF20) | freq_r3A_value(freq)); + bcm43xx_radio_write16(dev, 0x003D, + bcm43xx_radio_read16(dev, 0x003D) & 0x00FF); + bcm43xx_radio_write16(dev, 0x0081, + (bcm43xx_radio_read16(dev, 0x0081) + & 0xFF7F) | 0x0080); + bcm43xx_radio_write16(dev, 0x0035, + bcm43xx_radio_read16(dev, 0x0035) & 0xFFEF); + bcm43xx_radio_write16(dev, 0x0035, + (bcm43xx_radio_read16(dev, 0x0035) + & 0xFFEF) | 0x0010); + bcm43xx_radio_set_tx_iq(dev); + TODO(); //TODO: TSSI2dbm workaround + bcm43xx_phy_xmitpower(dev);//FIXME correct? + } else { + if ((channel < 1) || (channel > 14)) + return -EINVAL; + + if (synthetic_pu_workaround) + bcm43xx_synth_pu_workaround(dev, channel); + + bcm43xx_write16(dev, BCM43xx_MMIO_CHANNEL, + channel2freq_bg(channel)); + + if (channel == 14) { + if (dev->dev->bus->sprom.r1.country_code == SSB_SPROM1CCODE_JAPAN) + bcm43xx_hf_write(dev, bcm43xx_hf_read(dev) & ~BCM43xx_HF_ACPR); + else + bcm43xx_hf_write(dev, bcm43xx_hf_read(dev) | BCM43xx_HF_ACPR); + bcm43xx_write16(dev, BCM43xx_MMIO_CHANNEL_EXT, + bcm43xx_read16(dev, BCM43xx_MMIO_CHANNEL_EXT) + | (1 << 11)); + } else { + bcm43xx_write16(dev, BCM43xx_MMIO_CHANNEL_EXT, + bcm43xx_read16(dev, BCM43xx_MMIO_CHANNEL_EXT) + & 0xF7BF); + } + } + + phy->channel = channel; + //XXX: Using the longer of 2 timeouts (8000 vs 2000 usecs). Specs states + // that 2000 usecs might suffice. + udelay(8000); + + return 0; +} + +/* http://bcm-specs.sipsolutions.net/TX_Gain_Base_Band */ +static u16 bcm43xx_get_txgain_base_band(u16 txpower) +{ + u16 ret; + + assert(txpower <= 63); + + if (txpower >= 54) + ret = 2; + else if (txpower >= 49) + ret = 4; + else if (txpower >= 44) + ret = 5; + else + ret = 6; + + return ret; +} + +/* http://bcm-specs.sipsolutions.net/TX_Gain_Radio_Frequency_Power_Amplifier */ +static u16 bcm43xx_get_txgain_freq_power_amp(u16 txpower) +{ + u16 ret; + + assert(txpower <= 63); + + if (txpower >= 32) + ret = 0; + else if (txpower >= 25) + ret = 1; + else if (txpower >= 20) + ret = 2; + else if (txpower >= 12) + ret = 3; + else + ret = 4; + + return ret; +} + +/* http://bcm-specs.sipsolutions.net/TX_Gain_Digital_Analog_Converter */ +static u16 bcm43xx_get_txgain_dac(u16 txpower) +{ + u16 ret; + + assert(txpower <= 63); + + if (txpower >= 54) + ret = txpower - 53; + else if (txpower >= 49) + ret = txpower - 42; + else if (txpower >= 44) + ret = txpower - 37; + else if (txpower >= 32) + ret = txpower - 32; + else if (txpower >= 25) + ret = txpower - 20; + else if (txpower >= 20) + ret = txpower - 13; + else if (txpower >= 12) + ret = txpower - 8; + else + ret = txpower; + + return ret; +} + +void bcm43xx_radio_set_txpower_a(struct bcm43xx_wldev *dev, u16 txpower) +{ + struct bcm43xx_phy *phy = &dev->phy; + u16 pamp, base, dac, t; + + txpower = limit_value(txpower, 0, 63); + + pamp = bcm43xx_get_txgain_freq_power_amp(txpower); + pamp <<= 5; + pamp &= 0x00E0; + bcm43xx_phy_write(dev, 0x0019, pamp); + + base = bcm43xx_get_txgain_base_band(txpower); + base &= 0x000F; + bcm43xx_phy_write(dev, 0x0017, base | 0x0020); + + t = bcm43xx_ofdmtab_read16(dev, 0x3000, 1); + t &= 0x0007; + + dac = bcm43xx_get_txgain_dac(txpower); + dac <<= 3; + dac |= t; + + bcm43xx_ofdmtab_write16(dev, 0x3000, 1, dac); + + phy->txpwr_offset = txpower; + + TODO(); + //TODO: FuncPlaceholder (Adjust BB loft cancel) +} + +void bcm43xx_radio_set_txpower_bg(struct bcm43xx_wldev *dev, + s16 baseband_attenuation, + s16 radio_attenuation, + s16 _tx_magn) +{ + struct bcm43xx_phy *phy = &dev->phy; + u8 tx_bias = phy->lo_control->tx_bias; + u8 tx_magn; + + if (baseband_attenuation < 0) + baseband_attenuation = phy->bbatt; + if (radio_attenuation < 0) + radio_attenuation = phy->rfatt; + if (_tx_magn < 0) + _tx_magn = phy->lo_control->tx_magn; + tx_magn = _tx_magn; + phy->bbatt = baseband_attenuation; + phy->rfatt = radio_attenuation; + + /* Set Baseband Attenuation on device. */ + bcm43xx_phy_set_baseband_attenuation(dev, baseband_attenuation); + + /* Set Radio Attenuation on device. */ + bcm43xx_shm_write16(dev, BCM43xx_SHM_SHARED, + 0x0064, radio_attenuation); + if (phy->radio_ver == 0x2050 && phy->radio_rev == 8) { + bcm43xx_phy_write(dev, 0x0043, radio_attenuation); + } else { + bcm43xx_radio_write16(dev, 0x0043, + (bcm43xx_radio_read16(dev, 0x0043) + & 0xFFF0) | radio_attenuation); + } + + if (phy->radio_ver == 0x2050) {//FIXME: It seems like tx_magn and tx_bias are swapped in this func. + if (phy->radio_rev < 6) { + bcm43xx_radio_write16(dev, 0x0043, + (bcm43xx_radio_read16(dev, 0x0043) + & 0xFF8F) | tx_magn); + } else if (phy->radio_rev != 8) { + bcm43xx_radio_write16(dev, 0x0052, + (bcm43xx_radio_read16(dev, 0x0052) + & 0xFF8F) | tx_magn); + } else { + bcm43xx_radio_write16(dev, 0x52, + (bcm43xx_radio_read16(dev, 0x52) & 0xFF00) | + tx_magn | tx_bias); + } + } + if (phy->radio_rev != 8) { + bcm43xx_radio_write16(dev, 0x0052, + (bcm43xx_radio_read16(dev, 0x0052) + & 0xFFF0) | tx_bias); + } + if (phy->type == BCM43xx_PHYTYPE_G) + bcm43xx_lo_adjust(dev); +} + +u16 bcm43xx_default_baseband_attenuation(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + + if (phy->radio_ver == 0x2050 && phy->radio_rev < 6) + return 0; + return 2; +} + +u16 bcm43xx_default_radio_attenuation(struct bcm43xx_wldev *dev) +{ + struct ssb_bus *bus = dev->dev->bus; + struct bcm43xx_phy *phy = &dev->phy; + u16 att = 0xFFFF; + + if (phy->type == BCM43xx_PHYTYPE_A) + return 0x60; + + switch (phy->radio_ver) { + case 0x2053: + switch (phy->radio_rev) { + case 1: + att = 6; + break; + } + break; + case 0x2050: + switch (phy->radio_rev) { + case 0: + att = 5; + break; + case 1: + if (phy->type == BCM43xx_PHYTYPE_G) { + if (bus->board_vendor == SSB_BOARDVENDOR_BCM && + bus->board_type == SSB_BOARD_BCM4309G && + bus->board_rev >= 30) + att = 3; + else if (bus->board_vendor == SSB_BOARDVENDOR_BCM && + bus->board_type == SSB_BOARD_BU4306) + att = 3; + else + att = 1; + } else { + if (bus->board_vendor == SSB_BOARDVENDOR_BCM && + bus->board_type == SSB_BOARD_BCM4309G && + bus->board_rev >= 30) + att = 7; + else + att = 6; + } + break; + case 2: + if (phy->type == BCM43xx_PHYTYPE_G) { + if (bus->board_vendor == SSB_BOARDVENDOR_BCM && + bus->board_type == SSB_BOARD_BCM4309G && + bus->board_rev >= 30) + att = 3; + else if (bus->board_vendor == SSB_BOARDVENDOR_BCM && + bus->board_type == SSB_BOARD_BU4306) + att = 5; + else if (bus->chip_id == 0x4320) + att = 4; + else + att = 3; + } else + att = 6; + break; + case 3: + att = 5; + break; + case 4: + case 5: + att = 1; + break; + case 6: + case 7: + att = 5; + break; + case 8: + att = 0x1A; + break; + case 9: + default: + att = 5; + } + } + if (bus->board_vendor == SSB_BOARDVENDOR_BCM && + bus->board_type == SSB_BOARD_BCM4309G) { + if (bus->board_rev < 0x43) + att = 2; + else if (bus->board_rev < 0x51) + att = 3; + } + if (att == 0xFFFF) + att = 5; + + return att; +} + +u16 bcm43xx_default_txctl1(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + + if (phy->radio_ver != 0x2050) + return 0; + if (phy->radio_rev == 1) + return 3; + if (phy->radio_rev < 6) + return 2; + if (phy->radio_rev == 8) + return 1; + return 0; +} + +void bcm43xx_radio_turn_on(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + int err; + + if (phy->radio_on) + return; + + switch (phy->type) { + case BCM43xx_PHYTYPE_A: + bcm43xx_radio_write16(dev, 0x0004, 0x00C0); + bcm43xx_radio_write16(dev, 0x0005, 0x0008); + bcm43xx_phy_write(dev, 0x0010, bcm43xx_phy_read(dev, 0x0010) & 0xFFF7); + bcm43xx_phy_write(dev, 0x0011, bcm43xx_phy_read(dev, 0x0011) & 0xFFF7); + bcm43xx_radio_init2060(dev); + break; + case BCM43xx_PHYTYPE_B: + case BCM43xx_PHYTYPE_G: + bcm43xx_phy_write(dev, 0x0015, 0x8000); + bcm43xx_phy_write(dev, 0x0015, 0xCC00); + bcm43xx_phy_write(dev, 0x0015, (phy->gmode ? 0x00C0 : 0x0000)); + err = bcm43xx_radio_selectchannel(dev, BCM43xx_DEFAULT_CHANNEL_BG, 1); + assert(err == 0); + break; + default: + assert(0); + } + phy->radio_on = 1; + dprintk(KERN_INFO PFX "Radio turned on\n"); +} + +void bcm43xx_radio_turn_off(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + + if (phy->type == BCM43xx_PHYTYPE_A) { + bcm43xx_radio_write16(dev, 0x0004, 0x00FF); + bcm43xx_radio_write16(dev, 0x0005, 0x00FB); + bcm43xx_phy_write(dev, 0x0010, bcm43xx_phy_read(dev, 0x0010) | 0x0008); + bcm43xx_phy_write(dev, 0x0011, bcm43xx_phy_read(dev, 0x0011) | 0x0008); + } + if (phy->type == BCM43xx_PHYTYPE_G && dev->dev->id.revision >= 5) { + bcm43xx_phy_write(dev, 0x0811, bcm43xx_phy_read(dev, 0x0811) | 0x008C); + bcm43xx_phy_write(dev, 0x0812, bcm43xx_phy_read(dev, 0x0812) & 0xFF73); + } else + bcm43xx_phy_write(dev, 0x0015, 0xAA00); + phy->radio_on = 0; + dprintk(KERN_INFO PFX "Radio turned off\n"); +} diff --git a/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_phy.h b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_phy.h new file mode 100644 index 0000000..0252f65 --- /dev/null +++ b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_phy.h @@ -0,0 +1,309 @@ +#ifndef BCM43xx_PHY_H_ +#define BCM43xx_PHY_H_ + +#include + +struct bcm43xx_wldev; + + +/*** PHY Registers ***/ + +/* Routing */ +#define BCM43xx_PHYROUTE_OFDM_GPHY 0x400 +#define BCM43xx_PHYROUTE_EXT_GPHY 0x800 + +/* Base registers. */ +#define BCM43xx_PHY_BASE(reg) (reg) +/* OFDM (A) registers of a G-PHY */ +#define BCM43xx_PHY_OFDM(reg) ((reg) | BCM43xx_PHYROUTE_OFDM_GPHY) +/* Extended G-PHY registers */ +#define BCM43xx_PHY_EXTG(reg) ((reg) | BCM43xx_PHYROUTE_EXT_GPHY) + + +/* OFDM (A) PHY Registers */ +#define BCM43xx_PHY_VERSION_OFDM BCM43xx_PHY_OFDM(0x00) /* Versioning register for A-PHY */ +#define BCM43xx_PHY_BBANDCFG BCM43xx_PHY_OFDM(0x01) /* Baseband config */ +#define BCM43xx_PHY_BBANDCFG_RXANT 0x180 /* RX Antenna selection */ +#define BCM43xx_PHY_BBANDCFG_RXANT_SHIFT 7 +#define BCM43xx_PHY_PWRDOWN BCM43xx_PHY_OFDM(0x03) /* Powerdown */ +#define BCM43xx_PHY_CRSTHRES1 BCM43xx_PHY_OFDM(0x06) /* CRS Threshold 1 */ +#define BCM43xx_PHY_LNAHPFCTL BCM43xx_PHY_OFDM(0x1C) /* LNA/HPF control */ +#define BCM43xx_PHY_ADIVRELATED BCM43xx_PHY_OFDM(0x27) /* FIXME rename */ +#define BCM43xx_PHY_CRS0 BCM43xx_PHY_OFDM(0x29) +#define BCM43xx_PHY_ANTDWELL BCM43xx_PHY_OFDM(0x2B) /* Antenna dwell */ +#define BCM43xx_PHY_ANTDWELL_AUTODIV1 0x0100 /* Automatic RX diversity start antenna */ +#define BCM43xx_PHY_ENCORE BCM43xx_PHY_OFDM(0x49) /* "Encore" (RangeMax / BroadRange) */ +#define BCM43xx_PHY_ENCORE_EN 0x0200 /* Encore enable */ +#define BCM43xx_PHY_LMS BCM43xx_PHY_OFDM(0x55) +#define BCM43xx_PHY_OFDM61 BCM43xx_PHY_OFDM(0x61) /* FIXME rename */ +#define BCM43xx_PHY_OFDM61_10 0x0010 /* FIXME rename */ +#define BCM43xx_PHY_IQBAL BCM43xx_PHY_OFDM(0x69) /* I/Q balance */ +#define BCM43xx_PHY_OTABLECTL BCM43xx_PHY_OFDM(0x72) /* OFDM table control (see below) */ +#define BCM43xx_PHY_OTABLEOFF 0x03FF /* OFDM table offset (see below) */ +#define BCM43xx_PHY_OTABLENR 0xFC00 /* OFDM table number (see below) */ +#define BCM43xx_PHY_OTABLENR_SHIFT 10 +#define BCM43xx_PHY_OTABLEI BCM43xx_PHY_OFDM(0x73) /* OFDM table data I */ +#define BCM43xx_PHY_OTABLEQ BCM43xx_PHY_OFDM(0x74) /* OFDM table data Q */ +#define BCM43xx_PHY_HPWR_TSSICTL BCM43xx_PHY_OFDM(0x78) /* Hardware power TSSI control */ +#define BCM43xx_PHY_NRSSITHRES BCM43xx_PHY_OFDM(0x8A) /* NRSSI threshold */ +#define BCM43xx_PHY_ANTWRSETT BCM43xx_PHY_OFDM(0x8C) /* Antenna WR settle */ +#define BCM43xx_PHY_ANTWRSETT_ARXDIV 0x2000 /* Automatic RX diversity enabled */ +#define BCM43xx_PHY_CLIPPWRDOWNT BCM43xx_PHY_OFDM(0x93) /* Clip powerdown threshold */ +#define BCM43xx_PHY_OFDM9B BCM43xx_PHY_OFDM(0x9B) /* FIXME rename */ +#define BCM43xx_PHY_N1P1GAIN BCM43xx_PHY_OFDM(0xA0) +#define BCM43xx_PHY_P1P2GAIN BCM43xx_PHY_OFDM(0xA1) +#define BCM43xx_PHY_N1N2GAIN BCM43xx_PHY_OFDM(0xA2) +#define BCM43xx_PHY_CLIPTHRES BCM43xx_PHY_OFDM(0xA3) +#define BCM43xx_PHY_CLIPN1P2THRES BCM43xx_PHY_OFDM(0xA4) +#define BCM43xx_PHY_DIVSRCHIDX BCM43xx_PHY_OFDM(0xA8) /* Divider search gain/index */ +#define BCM43xx_PHY_CLIPP2THRES BCM43xx_PHY_OFDM(0xA9) +#define BCM43xx_PHY_CLIPP3THRES BCM43xx_PHY_OFDM(0xAA) +#define BCM43xx_PHY_DIVP1P2GAIN BCM43xx_PHY_OFDM(0xAB) +#define BCM43xx_PHY_DIVSRCHGAINBACK BCM43xx_PHY_OFDM(0xAD) /* Divider search gain back */ +#define BCM43xx_PHY_DIVSRCHGAINCHNG BCM43xx_PHY_OFDM(0xAE) /* Divider search gain change */ +#define BCM43xx_PHY_CRSTHRES1_R1 BCM43xx_PHY_OFDM(0xC0) /* CRS Threshold 1 (rev 1 only) */ +#define BCM43xx_PHY_CRSTHRES2_R1 BCM43xx_PHY_OFDM(0xC1) /* CRS Threshold 2 (rev 1 only) */ +#define BCM43xx_PHY_TSSIP_LTBASE BCM43xx_PHY_OFDM(0x380) /* TSSI power lookup table base */ +#define BCM43xx_PHY_DC_LTBASE BCM43xx_PHY_OFDM(0x3A0) /* DC lookup table base */ +#define BCM43xx_PHY_GAIN_LTBASE BCM43xx_PHY_OFDM(0x3C0) /* Gain lookup table base */ + +/* CCK (B) PHY Registers */ +#define BCM43xx_PHY_VERSION_CCK BCM43xx_PHY_BASE(0x00) /* Versioning register for B-PHY */ +#define BCM43xx_PHY_CCKBBANDCFG BCM43xx_PHY_BASE(0x01) /* Contains antenna 0/1 control bit */ +#define BCM43xx_PHY_PGACTL BCM43xx_PHY_BASE(0x15) /* PGA control */ +#define BCM43xx_PHY_PGACTL_LPF 0x1000 /* Low pass filter (?) */ +#define BCM43xx_PHY_PGACTL_LOWBANDW 0x0040 /* Low bandwidth flag */ +#define BCM43xx_PHY_PGACTL_UNKNOWN 0xEFA0 +#define BCM43xx_PHY_FBCTL1 BCM43xx_PHY_BASE(0x18) /* Frequency bandwidth control 1 */ +#define BCM43xx_PHY_ITSSI BCM43xx_PHY_BASE(0x29) /* Idle TSSI */ +#define BCM43xx_PHY_LO_LEAKAGE BCM43xx_PHY_BASE(0x2D) /* Measured LO leakage */ +#define BCM43xx_PHY_ENERGY BCM43xx_PHY_BASE(0x33) /* Energy */ +#define BCM43xx_PHY_SYNCCTL BCM43xx_PHY_BASE(0x35) +#define BCM43xx_PHY_FBCTL2 BCM43xx_PHY_BASE(0x38) /* Frequency bandwidth control 2 */ +#define BCM43xx_PHY_DACCTL BCM43xx_PHY_BASE(0x60) /* DAC control */ +#define BCM43xx_PHY_RCCALOVER BCM43xx_PHY_BASE(0x78) /* RC calibration override */ + +/* Extended G-PHY Registers */ +#define BCM43xx_PHY_CLASSCTL BCM43xx_PHY_EXTG(0x02) /* Classify control */ +#define BCM43xx_PHY_GTABCTL BCM43xx_PHY_EXTG(0x03) /* G-PHY table control (see below) */ +#define BCM43xx_PHY_GTABOFF 0x03FF /* G-PHY table offset (see below) */ +#define BCM43xx_PHY_GTABNR 0xFC00 /* G-PHY table number (see below) */ +#define BCM43xx_PHY_GTABNR_SHIFT 10 +#define BCM43xx_PHY_GTABDATA BCM43xx_PHY_EXTG(0x04) /* G-PHY table data */ +#define BCM43xx_PHY_LO_MASK BCM43xx_PHY_EXTG(0x0F) /* Local Oscillator control mask */ +#define BCM43xx_PHY_LO_CTL BCM43xx_PHY_EXTG(0x10) /* Local Oscillator control */ +#define BCM43xx_PHY_RFOVER BCM43xx_PHY_EXTG(0x11) /* RF override */ +#define BCM43xx_PHY_RFOVERVAL BCM43xx_PHY_EXTG(0x12) /* RF override value */ +#define BCM43xx_PHY_RFOVERVAL_EXTLNA 0x8000 +#define BCM43xx_PHY_RFOVERVAL_LNA 0x7000 +#define BCM43xx_PHY_RFOVERVAL_LNA_SHIFT 12 +#define BCM43xx_PHY_RFOVERVAL_PGA 0x0F00 +#define BCM43xx_PHY_RFOVERVAL_PGA_SHIFT 8 +#define BCM43xx_PHY_RFOVERVAL_UNK 0x0010 /* Unknown, always set. */ +#define BCM43xx_PHY_RFOVERVAL_TRSWRX 0x00E0 +#define BCM43xx_PHY_RFOVERVAL_BW 0x0003 /* Bandwidth flags */ +#define BCM43xx_PHY_RFOVERVAL_BW_LPF 0x0001 /* Low Pass Filter */ +#define BCM43xx_PHY_RFOVERVAL_BW_LBW 0x0002 /* Low Bandwidth (when set), high when unset */ +#define BCM43xx_PHY_ANALOGOVER BCM43xx_PHY_EXTG(0x14) /* Analog override */ +#define BCM43xx_PHY_ANALOGOVERVAL BCM43xx_PHY_EXTG(0x15) /* Analog override value */ + + + +/*** OFDM table numbers ***/ +#define BCM43xx_OFDMTAB(number, offset) (((number) << BCM43xx_PHY_OTABLENR_SHIFT) | (offset)) +#define BCM43xx_OFDMTAB_AGC1 BCM43xx_OFDMTAB(0x00, 0) +#define BCM43xx_OFDMTAB_GAIN0 BCM43xx_OFDMTAB(0x00, 0) +#define BCM43xx_OFDMTAB_GAINX BCM43xx_OFDMTAB(0x01, 0) //TODO rename +#define BCM43xx_OFDMTAB_GAIN1 BCM43xx_OFDMTAB(0x01, 4) +#define BCM43xx_OFDMTAB_AGC3 BCM43xx_OFDMTAB(0x02, 0) +#define BCM43xx_OFDMTAB_GAIN2 BCM43xx_OFDMTAB(0x02, 3) +#define BCM43xx_OFDMTAB_LNAHPFGAIN1 BCM43xx_OFDMTAB(0x03, 0) +#define BCM43xx_OFDMTAB_WRSSI BCM43xx_OFDMTAB(0x04, 0) +#define BCM43xx_OFDMTAB_LNAHPFGAIN2 BCM43xx_OFDMTAB(0x04, 0) +#define BCM43xx_OFDMTAB_NOISESCALE BCM43xx_OFDMTAB(0x05, 0) +#define BCM43xx_OFDMTAB_AGC2 BCM43xx_OFDMTAB(0x06, 0) +#define BCM43xx_OFDMTAB_ROTOR BCM43xx_OFDMTAB(0x08, 0) +#define BCM43xx_OFDMTAB_ADVRETARD BCM43xx_OFDMTAB(0x09, 0) +#define BCM43xx_OFDMTAB_DAC BCM43xx_OFDMTAB(0x0C, 0) +#define BCM43xx_OFDMTAB_DC BCM43xx_OFDMTAB(0x0E, 7) +#define BCM43xx_OFDMTAB_PWRDYN2 BCM43xx_OFDMTAB(0x0E, 12) +#define BCM43xx_OFDMTAB_LNAGAIN BCM43xx_OFDMTAB(0x0E, 13) +//TODO +#define BCM43xx_OFDMTAB_LPFGAIN BCM43xx_OFDMTAB(0x0F, 12) +#define BCM43xx_OFDMTAB_RSSI BCM43xx_OFDMTAB(0x10, 0) +//TODO +#define BCM43xx_OFDMTAB_AGC1_R1 BCM43xx_OFDMTAB(0x13, 0) +#define BCM43xx_OFDMTAB_GAINX_R1 BCM43xx_OFDMTAB(0x14, 0) //TODO rename +#define BCM43xx_OFDMTAB_MINSIGSQ BCM43xx_OFDMTAB(0x14, 1) +#define BCM43xx_OFDMTAB_AGC3_R1 BCM43xx_OFDMTAB(0x15, 0) +#define BCM43xx_OFDMTAB_WRSSI_R1 BCM43xx_OFDMTAB(0x15, 4) +#define BCM43xx_OFDMTAB_TSSI BCM43xx_OFDMTAB(0x15, 0) +#define BCM43xx_OFDMTAB_DACRFPABB BCM43xx_OFDMTAB(0x16, 0) +#define BCM43xx_OFDMTAB_DACOFF BCM43xx_OFDMTAB(0x17, 0) +#define BCM43xx_OFDMTAB_DCBIAS BCM43xx_OFDMTAB(0x18, 0) + +u16 bcm43xx_ofdmtab_read16(struct bcm43xx_wldev *dev, u16 table, u16 offset); +void bcm43xx_ofdmtab_write16(struct bcm43xx_wldev *dev, u16 table, + u16 offset, u16 value); +u32 bcm43xx_ofdmtab_read32(struct bcm43xx_wldev *dev, u16 table, u16 offset); +void bcm43xx_ofdmtab_write32(struct bcm43xx_wldev *dev, u16 table, + u16 offset, u32 value); + + +/*** G-PHY table numbers */ +#define BCM43xx_GTAB(number, offset) (((number) << BCM43xx_PHY_GTABNR_SHIFT) | (offset)) +#define BCM43xx_GTAB_NRSSI BCM43xx_GTAB(0x00, 0) +#define BCM43xx_GTAB_TRFEMW BCM43xx_GTAB(0x0C, 0x120) +#define BCM43xx_GTAB_ORIGTR BCM43xx_GTAB(0x2E, 0x298) + +u16 bcm43xx_gtab_read(struct bcm43xx_wldev *dev, u16 table, u16 offset); //TODO implement +void bcm43xx_gtab_write(struct bcm43xx_wldev *dev, u16 table, + u16 offset, u16 value); //TODO implement + + + +#define BCM43xx_DEFAULT_CHANNEL_A 36 +#define BCM43xx_DEFAULT_CHANNEL_BG 6 + +enum { + BCM43xx_ANTENNA0, /* Antenna 0 */ + BCM43xx_ANTENNA1, /* Antenna 0 */ + BCM43xx_ANTENNA_AUTO1, /* Automatic, starting with antenna 1 */ + BCM43xx_ANTENNA_AUTO0, /* Automatic, starting with antenna 0 */ + + BCM43xx_ANTENNA_AUTO = BCM43xx_ANTENNA_AUTO0, + BCM43xx_ANTENNA_DEFAULT = BCM43xx_ANTENNA_AUTO, +}; + +enum { + BCM43xx_INTERFMODE_NONE, + BCM43xx_INTERFMODE_NONWLAN, + BCM43xx_INTERFMODE_MANUALWLAN, + BCM43xx_INTERFMODE_AUTOWLAN, +}; + + +/* Masks for the different PHY versioning registers. */ +#define BCM43xx_PHYVER_ANALOG 0xF000 +#define BCM43xx_PHYVER_ANALOG_SHIFT 12 +#define BCM43xx_PHYVER_TYPE 0x0F00 +#define BCM43xx_PHYVER_TYPE_SHIFT 8 +#define BCM43xx_PHYVER_VERSION 0x00FF + + +void bcm43xx_raw_phy_lock(struct bcm43xx_wldev *dev); +#define bcm43xx_phy_lock(dev, flags) \ + do { \ + local_irq_save(flags); \ + bcm43xx_raw_phy_lock(dev); \ + } while (0) +void bcm43xx_raw_phy_unlock(struct bcm43xx_wldev *dev); +#define bcm43xx_phy_unlock(dev, flags) \ + do { \ + bcm43xx_raw_phy_unlock(dev); \ + local_irq_restore(flags); \ + } while (0) + +u16 bcm43xx_phy_read(struct bcm43xx_wldev *dev, u16 offset); +void bcm43xx_phy_write(struct bcm43xx_wldev *dev, u16 offset, u16 val); + +int bcm43xx_phy_init_tssi2dbm_table(struct bcm43xx_wldev *dev); + +void bcm43xx_phy_early_init(struct bcm43xx_wldev *dev); +int bcm43xx_phy_init(struct bcm43xx_wldev *dev); + +void bcm43xx_set_rx_antenna(struct bcm43xx_wldev *dev, int antenna); + +void bcm43xx_phy_xmitpower(struct bcm43xx_wldev *dev); +void bcm43xx_gphy_dc_lt_init(struct bcm43xx_wldev *dev); + +/* Returns the boolean whether the board has HardwarePowerControl */ +#define has_hardware_pctl(phy) \ + (((phy)->type == BCM43xx_PHYTYPE_A && (phy)->rev >= 5) || \ + ((phy)->type == BCM43xx_PHYTYPE_G && (phy)->rev >= 6)) +/* Returns the boolean whether "TX Magnification" is enabled. */ +#define has_tx_magnification(phy) \ + (((phy)->rev >= 2) && \ + ((phy)->radio_ver == 0x2050) && \ + ((phy)->radio_rev == 8)) +/* Card uses the loopback gain stuff */ +#define has_loopback_gain(phy) \ + (((phy)->rev > 1) || ((phy)->gmode)) + +/* Radio Attenuation (RF Attenuation) */ +struct bcm43xx_rfatt { + u8 att; /* Attenuation value */ + u8 with_padmix; /* Flag, PAD Mixer enabled. */ +}; +struct bcm43xx_rfatt_list { + /* Attenuation values list */ + const struct bcm43xx_rfatt *list; + u8 len; + /* Minimum/Maximum attenuation values */ + u8 min_val; + u8 max_val; +}; + +/* Baseband Attenuation */ +struct bcm43xx_bbatt { + u8 att; /* Attenuation value */ +}; +struct bcm43xx_bbatt_list { + /* Attenuation values list */ + const struct bcm43xx_bbatt *list; + u8 len; + /* Minimum/Maximum attenuation values */ + u8 min_val; + u8 max_val; +}; + +/* Write BasebandAttenuation value to the device. */ +void bcm43xx_phy_set_baseband_attenuation(struct bcm43xx_wldev *dev, + u16 baseband_attenuation); + + +extern const u8 bcm43xx_radio_channel_codes_bg[]; + +void bcm43xx_radio_lock(struct bcm43xx_wldev *dev); +void bcm43xx_radio_unlock(struct bcm43xx_wldev *dev); + +u16 bcm43xx_radio_read16(struct bcm43xx_wldev *dev, u16 offset); +void bcm43xx_radio_write16(struct bcm43xx_wldev *dev, u16 offset, u16 val); + +u16 bcm43xx_radio_init2050(struct bcm43xx_wldev *dev); +void bcm43xx_radio_init2060(struct bcm43xx_wldev *dev); + +void bcm43xx_radio_turn_on(struct bcm43xx_wldev *dev); +void bcm43xx_radio_turn_off(struct bcm43xx_wldev *dev); + +int bcm43xx_radio_selectchannel(struct bcm43xx_wldev *dev, u8 channel, + int synthetic_pu_workaround); + +void bcm43xx_radio_set_txpower_a(struct bcm43xx_wldev *dev, u16 txpower); +/* Set the txpower on device. If the values are < 0, use the saved ones. */ +void bcm43xx_radio_set_txpower_bg(struct bcm43xx_wldev *dev, + s16 baseband_attenuation, + s16 radio_attenuation, + s16 txctl1); + +u16 bcm43xx_default_baseband_attenuation(struct bcm43xx_wldev *dev); +u16 bcm43xx_default_radio_attenuation(struct bcm43xx_wldev *dev); +u16 bcm43xx_default_txctl1(struct bcm43xx_wldev *dev); + +u8 bcm43xx_radio_aci_detect(struct bcm43xx_wldev *dev, u8 channel); +u8 bcm43xx_radio_aci_scan(struct bcm43xx_wldev *dev); + +int bcm43xx_radio_set_interference_mitigation(struct bcm43xx_wldev *dev, int mode); + +void bcm43xx_calc_nrssi_slope(struct bcm43xx_wldev *dev); +void bcm43xx_calc_nrssi_threshold(struct bcm43xx_wldev *dev); +s16 bcm43xx_nrssi_hw_read(struct bcm43xx_wldev *dev, u16 offset); +void bcm43xx_nrssi_hw_write(struct bcm43xx_wldev *dev, u16 offset, s16 val); +void bcm43xx_nrssi_hw_update(struct bcm43xx_wldev *dev, u16 val); +void bcm43xx_nrssi_mem_update(struct bcm43xx_wldev *dev); + +void bcm43xx_radio_set_tx_iq(struct bcm43xx_wldev *dev); +u16 bcm43xx_radio_calibrationvalue(struct bcm43xx_wldev *dev); + + +#endif /* BCM43xx_PHY_H_ */ diff --git a/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_pio.c b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_pio.c new file mode 100644 index 0000000..3ae5ef5 --- /dev/null +++ b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_pio.c @@ -0,0 +1,671 @@ +/* + + Broadcom BCM43xx wireless driver + + PIO Transmission + + Copyright (c) 2005 Michael Buesch + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "bcm43xx.h" +#include "bcm43xx_pio.h" +#include "bcm43xx_main.h" +#include "bcm43xx_xmit.h" +#include "bcm43xx_power.h" + +#include + + +static void tx_start(struct bcm43xx_pioqueue *queue) +{ + bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL, + BCM43xx_PIO_TXCTL_INIT); +} + +static void tx_octet(struct bcm43xx_pioqueue *queue, + u8 octet) +{ + if (queue->need_workarounds) { + bcm43xx_pio_write(queue, BCM43xx_PIO_TXDATA, + octet); + bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL, + BCM43xx_PIO_TXCTL_WRITELO); + } else { + bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL, + BCM43xx_PIO_TXCTL_WRITELO); + bcm43xx_pio_write(queue, BCM43xx_PIO_TXDATA, + octet); + } +} + +static u16 tx_get_next_word(const u8 *txhdr, + const u8 *packet, + size_t txhdr_size, + unsigned int *pos) +{ + const u8 *source; + unsigned int i = *pos; + u16 ret; + + if (i < txhdr_size) { + source = txhdr; + } else { + source = packet; + i -= txhdr_size; + } + ret = le16_to_cpu( *((u16 *)(source + i)) ); + *pos += 2; + + return ret; +} + +static void tx_data(struct bcm43xx_pioqueue *queue, + u8 *txhdr, + const u8 *packet, + unsigned int octets) +{ + u16 data; + unsigned int i = 0; + + if (queue->need_workarounds) { + data = tx_get_next_word(txhdr, packet, + sizeof(struct bcm43xx_txhdr_fw4), &i); + bcm43xx_pio_write(queue, BCM43xx_PIO_TXDATA, data); + } + bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL, + BCM43xx_PIO_TXCTL_WRITELO | + BCM43xx_PIO_TXCTL_WRITEHI); + while (i < octets - 1) { + data = tx_get_next_word(txhdr, packet, + sizeof(struct bcm43xx_txhdr_fw4), &i); + bcm43xx_pio_write(queue, BCM43xx_PIO_TXDATA, data); + } + if (octets % 2) + tx_octet(queue, packet[octets - sizeof(struct bcm43xx_txhdr_fw4) - 1]); +} + +static void tx_complete(struct bcm43xx_pioqueue *queue, + struct sk_buff *skb) +{ + if (queue->need_workarounds) { + bcm43xx_pio_write(queue, BCM43xx_PIO_TXDATA, + skb->data[skb->len - 1]); + bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL, + BCM43xx_PIO_TXCTL_WRITELO | + BCM43xx_PIO_TXCTL_COMPLETE); + } else { + bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL, + BCM43xx_PIO_TXCTL_COMPLETE); + } +} + +static u16 generate_cookie(struct bcm43xx_pioqueue *queue, + struct bcm43xx_pio_txpacket *packet) +{ + u16 cookie = 0x0000; + int packetindex; + + /* We use the upper 4 bits for the PIO + * controller ID and the lower 12 bits + * for the packet index (in the cache). + */ + switch (queue->mmio_base) { + case BCM43xx_MMIO_PIO1_BASE: + break; + case BCM43xx_MMIO_PIO2_BASE: + cookie = 0x1000; + break; + case BCM43xx_MMIO_PIO3_BASE: + cookie = 0x2000; + break; + case BCM43xx_MMIO_PIO4_BASE: + cookie = 0x3000; + break; + default: + assert(0); + } + packetindex = pio_txpacket_getindex(packet); + assert(((u16)packetindex & 0xF000) == 0x0000); + cookie |= (u16)packetindex; + + return cookie; +} + +static +struct bcm43xx_pioqueue * parse_cookie(struct bcm43xx_wldev *dev, + u16 cookie, + struct bcm43xx_pio_txpacket **packet) +{ + struct bcm43xx_pio *pio = &dev->pio; + struct bcm43xx_pioqueue *queue = NULL; + int packetindex; + + switch (cookie & 0xF000) { + case 0x0000: + queue = pio->queue0; + break; + case 0x1000: + queue = pio->queue1; + break; + case 0x2000: + queue = pio->queue2; + break; + case 0x3000: + queue = pio->queue3; + break; + default: + assert(0); + } + packetindex = (cookie & 0x0FFF); + assert(packetindex >= 0 && packetindex < BCM43xx_PIO_MAXTXPACKETS); + *packet = &(queue->tx_packets_cache[packetindex]); + + return queue; +} + +union txhdr_union { + struct bcm43xx_txhdr_fw4 txhdr_fw4; +}; + +static void pio_tx_write_fragment(struct bcm43xx_pioqueue *queue, + struct sk_buff *skb, + struct bcm43xx_pio_txpacket *packet, + size_t txhdr_size) +{ + union txhdr_union txhdr_data; + u8 *txhdr = NULL; + unsigned int octets; + + txhdr = (u8 *)(&txhdr_data.txhdr_fw4); + + assert(skb_shinfo(skb)->nr_frags == 0); + bcm43xx_generate_txhdr(queue->dev, + txhdr, skb->data, skb->len, + &packet->txstat.control, + generate_cookie(queue, packet)); + + tx_start(queue); + octets = skb->len + txhdr_size; + if (queue->need_workarounds) + octets--; + tx_data(queue, txhdr, (u8 *)skb->data, octets); + tx_complete(queue, skb); +} + +static void free_txpacket(struct bcm43xx_pio_txpacket *packet, + int irq_context) +{ + struct bcm43xx_pioqueue *queue = packet->queue; + + if (packet->skb) { + if (irq_context) + dev_kfree_skb_irq(packet->skb); + else + dev_kfree_skb(packet->skb); + } + list_move(&packet->list, &queue->txfree); + queue->nr_txfree++; +} + +static int pio_tx_packet(struct bcm43xx_pio_txpacket *packet) +{ + struct bcm43xx_pioqueue *queue = packet->queue; + struct sk_buff *skb = packet->skb; + u16 octets; + + octets = (u16)skb->len + sizeof(struct bcm43xx_txhdr_fw4); + if (queue->tx_devq_size < octets) { + printkl(KERN_WARNING PFX "PIO queue too small. " + "Dropping packet.\n"); + /* Drop it silently (return success) */ + free_txpacket(packet, 1); + return 0; + } + assert(queue->tx_devq_packets <= BCM43xx_PIO_MAXTXDEVQPACKETS); + assert(queue->tx_devq_used <= queue->tx_devq_size); + /* Check if there is sufficient free space on the device + * TX queue. If not, return and let the TX tasklet + * retry later. + */ + if (queue->tx_devq_packets == BCM43xx_PIO_MAXTXDEVQPACKETS) + return -EBUSY; + if (queue->tx_devq_used + octets > queue->tx_devq_size) + return -EBUSY; + /* Now poke the device. */ + pio_tx_write_fragment(queue, skb, packet, sizeof(struct bcm43xx_txhdr_fw4)); + + /* Account for the packet size. + * (We must not overflow the device TX queue) + */ + queue->tx_devq_packets++; + queue->tx_devq_used += octets; + + /* Transmission started, everything ok, move the + * packet to the txrunning list. + */ + list_move_tail(&packet->list, &queue->txrunning); + + return 0; +} + +static void tx_tasklet(unsigned long d) +{ + struct bcm43xx_pioqueue *queue = (struct bcm43xx_pioqueue *)d; + struct bcm43xx_wldev *dev = queue->dev; + unsigned long flags; + struct bcm43xx_pio_txpacket *packet, *tmp_packet; + int err; + u16 txctl; + + spin_lock_irqsave(&dev->wl->irq_lock, flags); + if (queue->tx_frozen) + goto out_unlock; + txctl = bcm43xx_pio_read(queue, BCM43xx_PIO_TXCTL); + if (txctl & BCM43xx_PIO_TXCTL_SUSPEND) + goto out_unlock; + + list_for_each_entry_safe(packet, tmp_packet, &queue->txqueue, list) { + /* Try to transmit the packet. This can fail, if + * the device queue is full. In case of failure, the + * packet is left in the txqueue. + * If transmission succeed, the packet is moved to txrunning. + * If it is impossible to transmit the packet, it + * is dropped. + */ + err = pio_tx_packet(packet); + if (err) + break; + } +out_unlock: + spin_unlock_irqrestore(&dev->wl->irq_lock, flags); +} + +static void setup_txqueues(struct bcm43xx_pioqueue *queue) +{ + struct bcm43xx_pio_txpacket *packet; + int i; + + queue->nr_txfree = BCM43xx_PIO_MAXTXPACKETS; + for (i = 0; i < BCM43xx_PIO_MAXTXPACKETS; i++) { + packet = &(queue->tx_packets_cache[i]); + + packet->queue = queue; + INIT_LIST_HEAD(&packet->list); + + list_add(&packet->list, &queue->txfree); + } +} + +static +struct bcm43xx_pioqueue * bcm43xx_setup_pioqueue(struct bcm43xx_wldev *dev, + u16 pio_mmio_base) +{ + struct bcm43xx_pioqueue *queue; + u32 value; + u16 qsize; + + queue = kzalloc(sizeof(*queue), GFP_KERNEL); + if (!queue) + goto out; + + queue->dev = dev; + queue->mmio_base = pio_mmio_base; + queue->need_workarounds = (dev->dev->id.revision < 3); + + INIT_LIST_HEAD(&queue->txfree); + INIT_LIST_HEAD(&queue->txqueue); + INIT_LIST_HEAD(&queue->txrunning); + tasklet_init(&queue->txtask, tx_tasklet, + (unsigned long)queue); + + value = bcm43xx_read32(dev, BCM43xx_MMIO_STATUS_BITFIELD); + value &= ~BCM43xx_SBF_XFER_REG_BYTESWAP; + bcm43xx_write32(dev, BCM43xx_MMIO_STATUS_BITFIELD, value); + + qsize = bcm43xx_read16(dev, queue->mmio_base + BCM43xx_PIO_TXQBUFSIZE); + if (qsize == 0) { + printk(KERN_ERR PFX "ERROR: This card does not support PIO " + "operation mode. Please use DMA mode " + "(module parameter pio=0).\n"); + goto err_freequeue; + } + if (qsize <= BCM43xx_PIO_TXQADJUST) { + printk(KERN_ERR PFX "PIO tx device-queue too small (%u)\n", + qsize); + goto err_freequeue; + } + qsize -= BCM43xx_PIO_TXQADJUST; + queue->tx_devq_size = qsize; + + setup_txqueues(queue); + +out: + return queue; + +err_freequeue: + kfree(queue); + queue = NULL; + goto out; +} + +static void cancel_transfers(struct bcm43xx_pioqueue *queue) +{ + struct bcm43xx_pio_txpacket *packet, *tmp_packet; + + tasklet_disable(&queue->txtask); + + list_for_each_entry_safe(packet, tmp_packet, &queue->txrunning, list) + free_txpacket(packet, 0); + list_for_each_entry_safe(packet, tmp_packet, &queue->txqueue, list) + free_txpacket(packet, 0); +} + +static void bcm43xx_destroy_pioqueue(struct bcm43xx_pioqueue *queue) +{ + if (!queue) + return; + + cancel_transfers(queue); + kfree(queue); +} + +void bcm43xx_pio_free(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_pio *pio; + + if (!bcm43xx_using_pio(dev)) + return; + pio = &dev->pio; + + bcm43xx_destroy_pioqueue(pio->queue3); + pio->queue3 = NULL; + bcm43xx_destroy_pioqueue(pio->queue2); + pio->queue2 = NULL; + bcm43xx_destroy_pioqueue(pio->queue1); + pio->queue1 = NULL; + bcm43xx_destroy_pioqueue(pio->queue0); + pio->queue0 = NULL; +} + +int bcm43xx_pio_init(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_pio *pio = &dev->pio; + struct bcm43xx_pioqueue *queue; + int err = -ENOMEM; + + queue = bcm43xx_setup_pioqueue(dev, BCM43xx_MMIO_PIO1_BASE); + if (!queue) + goto out; + pio->queue0 = queue; + + queue = bcm43xx_setup_pioqueue(dev, BCM43xx_MMIO_PIO2_BASE); + if (!queue) + goto err_destroy0; + pio->queue1 = queue; + + queue = bcm43xx_setup_pioqueue(dev, BCM43xx_MMIO_PIO3_BASE); + if (!queue) + goto err_destroy1; + pio->queue2 = queue; + + queue = bcm43xx_setup_pioqueue(dev, BCM43xx_MMIO_PIO4_BASE); + if (!queue) + goto err_destroy2; + pio->queue3 = queue; + + if (dev->dev->id.revision < 3) + dev->irq_savedstate |= BCM43xx_IRQ_PIO_WORKAROUND; + + dprintk(KERN_INFO PFX "PIO initialized\n"); + err = 0; +out: + return err; + +err_destroy2: + bcm43xx_destroy_pioqueue(pio->queue2); + pio->queue2 = NULL; +err_destroy1: + bcm43xx_destroy_pioqueue(pio->queue1); + pio->queue1 = NULL; +err_destroy0: + bcm43xx_destroy_pioqueue(pio->queue0); + pio->queue0 = NULL; + goto out; +} + +int bcm43xx_pio_tx(struct bcm43xx_wldev *dev, + struct sk_buff *skb, + struct ieee80211_tx_control *ctl) +{ + struct bcm43xx_pioqueue *queue = dev->pio.queue1; + struct bcm43xx_pio_txpacket *packet; + + assert(!queue->tx_suspended); + assert(!list_empty(&queue->txfree)); + + packet = list_entry(queue->txfree.next, struct bcm43xx_pio_txpacket, list); + packet->skb = skb; + + memset(&packet->txstat, 0, sizeof(packet->txstat)); + memcpy(&packet->txstat.control, ctl, sizeof(*ctl)); + + list_move_tail(&packet->list, &queue->txqueue); + queue->nr_txfree--; + queue->nr_tx_packets++; + assert(queue->nr_txfree < BCM43xx_PIO_MAXTXPACKETS); + + tasklet_schedule(&queue->txtask); + + return 0; +} + +void bcm43xx_pio_handle_txstatus(struct bcm43xx_wldev *dev, + const struct bcm43xx_txstatus *status) +{ + struct bcm43xx_pioqueue *queue; + struct bcm43xx_pio_txpacket *packet; + + queue = parse_cookie(dev, status->cookie, &packet); + assert(queue); + + queue->tx_devq_packets--; + queue->tx_devq_used -= (packet->skb->len + sizeof(struct bcm43xx_txhdr_fw4)); + + if (status->acked) + packet->txstat.flags |= IEEE80211_TX_STATUS_ACK; + packet->txstat.retry_count = status->frame_count - 1; + ieee80211_tx_status_irqsafe(dev->wl->hw, packet->skb, + &(packet->txstat)); + packet->skb = NULL; + + free_txpacket(packet, 1); + /* If there are packets on the txqueue, poke the tasklet + * to transmit them. + */ + if (!list_empty(&queue->txqueue)) + tasklet_schedule(&queue->txtask); +} + +void bcm43xx_pio_get_tx_stats(struct bcm43xx_wldev *dev, + struct ieee80211_tx_queue_stats *stats) +{ + struct bcm43xx_pio *pio = &dev->pio; + struct bcm43xx_pioqueue *queue; + struct ieee80211_tx_queue_stats_data *data; + + queue = pio->queue1; + data = &(stats->data[0]); + data->len = BCM43xx_PIO_MAXTXPACKETS - queue->nr_txfree; + data->limit = BCM43xx_PIO_MAXTXPACKETS; + data->count = queue->nr_tx_packets; +} + +static void pio_rx_error(struct bcm43xx_pioqueue *queue, + int clear_buffers, + const char *error) +{ + int i; + + printkl("PIO RX error: %s\n", error); + bcm43xx_pio_write(queue, BCM43xx_PIO_RXCTL, + BCM43xx_PIO_RXCTL_READY); + if (clear_buffers) { + assert(queue->mmio_base == BCM43xx_MMIO_PIO1_BASE); + for (i = 0; i < 15; i++) { + /* Dummy read. */ + bcm43xx_pio_read(queue, BCM43xx_PIO_RXDATA); + } + } +} + +void bcm43xx_pio_rx(struct bcm43xx_pioqueue *queue) +{ + u16 preamble[21] = { 0 }; + struct bcm43xx_rxhdr_fw4 *rxhdr; + u16 tmp, len, macstat; + int i, preamble_readwords; + struct sk_buff *skb; + + tmp = bcm43xx_pio_read(queue, BCM43xx_PIO_RXCTL); + if (!(tmp & BCM43xx_PIO_RXCTL_DATAAVAILABLE)) + return; + bcm43xx_pio_write(queue, BCM43xx_PIO_RXCTL, + BCM43xx_PIO_RXCTL_DATAAVAILABLE); + + for (i = 0; i < 10; i++) { + tmp = bcm43xx_pio_read(queue, BCM43xx_PIO_RXCTL); + if (tmp & BCM43xx_PIO_RXCTL_READY) + goto data_ready; + udelay(10); + } + dprintkl(KERN_ERR PFX "PIO RX timed out\n"); + return; +data_ready: + + len = bcm43xx_pio_read(queue, BCM43xx_PIO_RXDATA); + if (unlikely(len > 0x700)) { + pio_rx_error(queue, 0, "len > 0x700"); + return; + } + if (unlikely(len == 0 && queue->mmio_base != BCM43xx_MMIO_PIO4_BASE)) { + pio_rx_error(queue, 0, "len == 0"); + return; + } + preamble[0] = cpu_to_le16(len); + if (queue->mmio_base == BCM43xx_MMIO_PIO4_BASE) + preamble_readwords = 14 / sizeof(u16); + else + preamble_readwords = 18 / sizeof(u16); + for (i = 0; i < preamble_readwords; i++) { + tmp = bcm43xx_pio_read(queue, BCM43xx_PIO_RXDATA); + preamble[i + 1] = cpu_to_le16(tmp); + } + rxhdr = (struct bcm43xx_rxhdr_fw4 *)preamble; + macstat = le16_to_cpu(rxhdr->mac_status); + if (macstat & BCM43xx_RX_MAC_FCSERR) { + pio_rx_error(queue, + (queue->mmio_base == BCM43xx_MMIO_PIO1_BASE), + "Frame FCS error"); + return; + } + if (queue->mmio_base == BCM43xx_MMIO_PIO4_BASE) { + /* We received an xmit status. */ + struct bcm43xx_hwtxstatus *hw; + + hw = (struct bcm43xx_hwtxstatus *)(preamble + 1); + bcm43xx_handle_hwtxstatus(queue->dev, hw); + + return; + } + + skb = dev_alloc_skb(len); + if (unlikely(!skb)) { + pio_rx_error(queue, 1, "OOM"); + return; + } + skb_put(skb, len); + for (i = 0; i < len - 1; i += 2) { + tmp = bcm43xx_pio_read(queue, BCM43xx_PIO_RXDATA); + *((u16 *)(skb->data + i)) = cpu_to_le16(tmp); + } + if (len % 2) { + tmp = bcm43xx_pio_read(queue, BCM43xx_PIO_RXDATA); + skb->data[len - 1] = (tmp & 0x00FF); +/* The specs say the following is required, but + * it is wrong and corrupts the PLCP. If we don't do + * this, the PLCP seems to be correct. So ifdef it out for now. + */ +#if 0 + if (rxflags2 & BCM43xx_RXHDR_FLAGS2_TYPE2FRAME) + skb->data[2] = (tmp & 0xFF00) >> 8; + else + skb->data[0] = (tmp & 0xFF00) >> 8; +#endif + } + bcm43xx_rx(queue->dev, skb, rxhdr); +} + +void bcm43xx_pio_tx_suspend(struct bcm43xx_pioqueue *queue) +{ + bcm43xx_power_saving_ctl_bits(queue->dev, -1, 1); + bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL, + bcm43xx_pio_read(queue, BCM43xx_PIO_TXCTL) + | BCM43xx_PIO_TXCTL_SUSPEND); +} + +void bcm43xx_pio_tx_resume(struct bcm43xx_pioqueue *queue) +{ + bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL, + bcm43xx_pio_read(queue, BCM43xx_PIO_TXCTL) + & ~BCM43xx_PIO_TXCTL_SUSPEND); + bcm43xx_power_saving_ctl_bits(queue->dev, -1, -1); + tasklet_schedule(&queue->txtask); +} + +void bcm43xx_pio_freeze_txqueues(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_pio *pio; + + assert(bcm43xx_using_pio(dev)); + pio = &dev->pio; + pio->queue0->tx_frozen = 1; + pio->queue1->tx_frozen = 1; + pio->queue2->tx_frozen = 1; + pio->queue3->tx_frozen = 1; +} + +void bcm43xx_pio_thaw_txqueues(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_pio *pio; + + assert(bcm43xx_using_pio(dev)); + pio = &dev->pio; + pio->queue0->tx_frozen = 0; + pio->queue1->tx_frozen = 0; + pio->queue2->tx_frozen = 0; + pio->queue3->tx_frozen = 0; + if (!list_empty(&pio->queue0->txqueue)) + tasklet_schedule(&pio->queue0->txtask); + if (!list_empty(&pio->queue1->txqueue)) + tasklet_schedule(&pio->queue1->txtask); + if (!list_empty(&pio->queue2->txqueue)) + tasklet_schedule(&pio->queue2->txtask); + if (!list_empty(&pio->queue3->txqueue)) + tasklet_schedule(&pio->queue3->txtask); +} diff --git a/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_pio.h b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_pio.h new file mode 100644 index 0000000..b9d919b --- /dev/null +++ b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_pio.h @@ -0,0 +1,170 @@ +#ifndef BCM43xx_PIO_H_ +#define BCM43xx_PIO_H_ + +#include "bcm43xx.h" + +#include +#include +#include + + +#define BCM43xx_PIO_TXCTL 0x00 +#define BCM43xx_PIO_TXDATA 0x02 +#define BCM43xx_PIO_TXQBUFSIZE 0x04 +#define BCM43xx_PIO_RXCTL 0x08 +#define BCM43xx_PIO_RXDATA 0x0A + +#define BCM43xx_PIO_TXCTL_WRITELO (1 << 0) +#define BCM43xx_PIO_TXCTL_WRITEHI (1 << 1) +#define BCM43xx_PIO_TXCTL_COMPLETE (1 << 2) +#define BCM43xx_PIO_TXCTL_INIT (1 << 3) +#define BCM43xx_PIO_TXCTL_SUSPEND (1 << 7) + +#define BCM43xx_PIO_RXCTL_DATAAVAILABLE (1 << 0) +#define BCM43xx_PIO_RXCTL_READY (1 << 1) + +/* PIO constants */ +#define BCM43xx_PIO_MAXTXDEVQPACKETS 31 +#define BCM43xx_PIO_TXQADJUST 80 + +/* PIO tuning knobs */ +#define BCM43xx_PIO_MAXTXPACKETS 256 + + + +#ifdef CONFIG_BCM43XX_MAC80211_PIO + + +struct bcm43xx_pioqueue; +struct bcm43xx_xmitstatus; + +struct bcm43xx_pio_txpacket { + struct bcm43xx_pioqueue *queue; + struct sk_buff *skb; + struct ieee80211_tx_status txstat; + struct list_head list; +}; + +#define pio_txpacket_getindex(packet) ((int)((packet) - (packet)->queue->tx_packets_cache)) + +struct bcm43xx_pioqueue { + struct bcm43xx_wldev *dev; + u16 mmio_base; + + u8 tx_suspended:1, + tx_frozen:1, + need_workarounds:1; /* Workarounds needed for core.rev < 3 */ + + /* Adjusted size of the device internal TX buffer. */ + u16 tx_devq_size; + /* Used octets of the device internal TX buffer. */ + u16 tx_devq_used; + /* Used packet slots in the device internal TX buffer. */ + u8 tx_devq_packets; + /* Packets from the txfree list can + * be taken on incoming TX requests. + */ + struct list_head txfree; + unsigned int nr_txfree; + /* Packets on the txqueue are queued, + * but not completely written to the chip, yet. + */ + struct list_head txqueue; + /* Packets on the txrunning queue are completely + * posted to the device. We are waiting for the txstatus. + */ + struct list_head txrunning; + /* Total number or packets sent. + * (This counter can obviously wrap). + */ + unsigned int nr_tx_packets; + struct tasklet_struct txtask; + struct bcm43xx_pio_txpacket tx_packets_cache[BCM43xx_PIO_MAXTXPACKETS]; +}; + +static inline +u16 bcm43xx_pio_read(struct bcm43xx_pioqueue *queue, + u16 offset) +{ + return bcm43xx_read16(queue->dev, queue->mmio_base + offset); +} + +static inline +void bcm43xx_pio_write(struct bcm43xx_pioqueue *queue, + u16 offset, u16 value) +{ + bcm43xx_write16(queue->dev, queue->mmio_base + offset, value); + mmiowb(); +} + + +int bcm43xx_pio_init(struct bcm43xx_wldev *dev); +void bcm43xx_pio_free(struct bcm43xx_wldev *dev); + +int bcm43xx_pio_tx(struct bcm43xx_wldev *dev, + struct sk_buff *skb, + struct ieee80211_tx_control *ctl); +void bcm43xx_pio_handle_txstatus(struct bcm43xx_wldev *dev, + const struct bcm43xx_txstatus *status); +void bcm43xx_pio_get_tx_stats(struct bcm43xx_wldev *dev, + struct ieee80211_tx_queue_stats *stats); +void bcm43xx_pio_rx(struct bcm43xx_pioqueue *queue); + +/* Suspend TX queue in hardware. */ +void bcm43xx_pio_tx_suspend(struct bcm43xx_pioqueue *queue); +void bcm43xx_pio_tx_resume(struct bcm43xx_pioqueue *queue); +/* Suspend (freeze) the TX tasklet (software level). */ +void bcm43xx_pio_freeze_txqueues(struct bcm43xx_wldev *dev); +void bcm43xx_pio_thaw_txqueues(struct bcm43xx_wldev *dev); + +#else /* CONFIG_BCM43XX_MAC80211_PIO */ + +static inline +int bcm43xx_pio_init(struct bcm43xx_wldev *dev) +{ + return 0; +} +static inline +void bcm43xx_pio_free(struct bcm43xx_wldev *dev) +{ +} +static inline +int bcm43xx_pio_tx(struct bcm43xx_wldev *dev, + struct sk_buff *skb, + struct ieee80211_tx_control *ctl) +{ + return 0; +} +static inline +void bcm43xx_pio_handle_txstatus(struct bcm43xx_wldev *dev, + const struct bcm43xx_txstatus *status) +{ +} +static inline +void bcm43xx_pio_get_tx_stats(struct bcm43xx_wldev *dev, + struct ieee80211_tx_queue_stats *stats) +{ +} +static inline +void bcm43xx_pio_rx(struct bcm43xx_pioqueue *queue) +{ +} +static inline +void bcm43xx_pio_tx_suspend(struct bcm43xx_pioqueue *queue) +{ +} +static inline +void bcm43xx_pio_tx_resume(struct bcm43xx_pioqueue *queue) +{ +} +static inline +void bcm43xx_pio_freeze_txqueues(struct bcm43xx_wldev *dev) +{ +} +static inline +void bcm43xx_pio_thaw_txqueues(struct bcm43xx_wldev *dev) +{ +} + +#endif /* CONFIG_BCM43XX_MAC80211_PIO */ +#endif /* BCM43xx_PIO_H_ */ diff --git a/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_power.c b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_power.c new file mode 100644 index 0000000..b17995a --- /dev/null +++ b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_power.c @@ -0,0 +1,82 @@ +/* + + Broadcom BCM43xx wireless driver + + Copyright (c) 2005 Martin Langer , + Stefano Brivio + Michael Buesch + Danny van Dyk + Andreas Jaggi + + Some parts of the code in this file are derived from the ipw2200 + driver Copyright(c) 2003 - 2004 Intel Corporation. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include + +#include "bcm43xx.h" +#include "bcm43xx_power.h" +#include "bcm43xx_main.h" + + +//TODO Kill this file. + +/* Set the PowerSavingControlBits. + * Bitvalues: + * 0 => unset the bit + * 1 => set the bit + * -1 => calculate the bit + */ +void bcm43xx_power_saving_ctl_bits(struct bcm43xx_wldev *dev, + int bit25, int bit26) +{ + int i; + u32 status; + +//FIXME: Force 25 to off and 26 to on for now: +bit25 = 0; +bit26 = 1; + + if (bit25 == -1) { + //TODO: If powersave is not off and FIXME is not set and we are not in adhoc + // and thus is not an AP and we are associated, set bit 25 + } + if (bit26 == -1) { + //TODO: If the device is awake or this is an AP, or we are scanning, or FIXME, + // or we are associated, or FIXME, or the latest PS-Poll packet sent was + // successful, set bit26 + } + status = bcm43xx_read32(dev, BCM43xx_MMIO_STATUS_BITFIELD); + if (bit25) + status |= BCM43xx_SBF_PS1; + else + status &= ~BCM43xx_SBF_PS1; + if (bit26) + status |= BCM43xx_SBF_PS2; + else + status &= ~BCM43xx_SBF_PS2; + bcm43xx_write32(dev, BCM43xx_MMIO_STATUS_BITFIELD, status); + if (bit26 && dev->dev->id.revision >= 5) { + for (i = 0; i < 100; i++) { + if (bcm43xx_shm_read32(dev, BCM43xx_SHM_SHARED, 0x0040) != 4) + break; + udelay(10); + } + } +} diff --git a/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_power.h b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_power.h new file mode 100644 index 0000000..720474e --- /dev/null +++ b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_power.h @@ -0,0 +1,41 @@ +/* + + Broadcom BCM43xx wireless driver + + Copyright (c) 2005 Martin Langer , + Stefano Brivio + Michael Buesch + Danny van Dyk + Andreas Jaggi + + Some parts of the code in this file are derived from the ipw2200 + driver Copyright(c) 2003 - 2004 Intel Corporation. + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#ifndef BCM43xx_POWER_H_ +#define BCM43xx_POWER_H_ + +//TODO kill this file + +struct bcm43xx_wldev; + +void bcm43xx_power_saving_ctl_bits(struct bcm43xx_wldev *dev, + int bit25, int bit26); + +#endif /* BCM43xx_POWER_H_ */ diff --git a/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_sysfs.c b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_sysfs.c new file mode 100644 index 0000000..eed688f --- /dev/null +++ b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_sysfs.c @@ -0,0 +1,232 @@ +/* + + Broadcom BCM43xx wireless driver + + SYSFS support routines + + Copyright (c) 2006 Michael Buesch + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "bcm43xx_sysfs.h" +#include "bcm43xx.h" +#include "bcm43xx_main.h" +#include "bcm43xx_phy.h" + +#include + + +#define GENERIC_FILESIZE 64 + + +static int get_integer(const char *buf, size_t count) +{ + char tmp[10 + 1] = { 0 }; + int ret = -EINVAL; + + if (count == 0) + goto out; + count = min(count, (size_t)10); + memcpy(tmp, buf, count); + ret = simple_strtol(tmp, NULL, 10); +out: + return ret; +} + +static int get_boolean(const char *buf, size_t count) +{ + if (count != 0) { + if (buf[0] == '1') + return 1; + if (buf[0] == '0') + return 0; + if (count >= 4 && memcmp(buf, "true", 4) == 0) + return 1; + if (count >= 5 && memcmp(buf, "false", 5) == 0) + return 0; + if (count >= 3 && memcmp(buf, "yes", 3) == 0) + return 1; + if (count >= 2 && memcmp(buf, "no", 2) == 0) + return 0; + if (count >= 2 && memcmp(buf, "on", 2) == 0) + return 1; + if (count >= 3 && memcmp(buf, "off", 3) == 0) + return 0; + } + return -EINVAL; +} + +static ssize_t bcm43xx_attr_interfmode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bcm43xx_wldev *wldev = dev_to_bcm43xx_wldev(dev); + ssize_t count = 0; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + mutex_lock(&wldev->wl->mutex); + + switch (wldev->phy.interfmode) { + case BCM43xx_INTERFMODE_NONE: + count = snprintf(buf, PAGE_SIZE, "0 (No Interference Mitigation)\n"); + break; + case BCM43xx_INTERFMODE_NONWLAN: + count = snprintf(buf, PAGE_SIZE, "1 (Non-WLAN Interference Mitigation)\n"); + break; + case BCM43xx_INTERFMODE_MANUALWLAN: + count = snprintf(buf, PAGE_SIZE, "2 (WLAN Interference Mitigation)\n"); + break; + default: + assert(0); + } + + mutex_unlock(&wldev->wl->mutex); + + return count; +} + +static ssize_t bcm43xx_attr_interfmode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct bcm43xx_wldev *wldev = dev_to_bcm43xx_wldev(dev); + unsigned long flags; + int err; + int mode; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + mode = get_integer(buf, count); + switch (mode) { + case 0: + mode = BCM43xx_INTERFMODE_NONE; + break; + case 1: + mode = BCM43xx_INTERFMODE_NONWLAN; + break; + case 2: + mode = BCM43xx_INTERFMODE_MANUALWLAN; + break; + case 3: + mode = BCM43xx_INTERFMODE_AUTOWLAN; + break; + default: + return -EINVAL; + } + + mutex_lock(&wldev->wl->mutex); + spin_lock_irqsave(&wldev->wl->irq_lock, flags); + + err = bcm43xx_radio_set_interference_mitigation(wldev, mode); + if (err) { + printk(KERN_ERR PFX "Interference Mitigation not " + "supported by device\n"); + } + mmiowb(); + spin_unlock_irqrestore(&wldev->wl->irq_lock, flags); + mutex_unlock(&wldev->wl->mutex); + + return err ? err : count; +} + +static DEVICE_ATTR(interference, 0644, + bcm43xx_attr_interfmode_show, + bcm43xx_attr_interfmode_store); + +static ssize_t bcm43xx_attr_preamble_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bcm43xx_wldev *wldev = dev_to_bcm43xx_wldev(dev); + ssize_t count; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + mutex_lock(&wldev->wl->mutex); + + if (wldev->short_preamble) + count = snprintf(buf, PAGE_SIZE, "1 (Short Preamble enabled)\n"); + else + count = snprintf(buf, PAGE_SIZE, "0 (Short Preamble disabled)\n"); + + mutex_unlock(&wldev->wl->mutex); + + return count; +} + +static ssize_t bcm43xx_attr_preamble_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct bcm43xx_wldev *wldev = dev_to_bcm43xx_wldev(dev); + unsigned long flags; + int value; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + value = get_boolean(buf, count); + if (value < 0) + return value; + mutex_lock(&wldev->wl->mutex); + spin_lock_irqsave(&wldev->wl->irq_lock, flags); + + wldev->short_preamble = !!value; + + spin_unlock_irqrestore(&wldev->wl->irq_lock, flags); + mutex_unlock(&wldev->wl->mutex); + + return count; +} + +static DEVICE_ATTR(shortpreamble, 0644, + bcm43xx_attr_preamble_show, + bcm43xx_attr_preamble_store); + +int bcm43xx_sysfs_register(struct bcm43xx_wldev *wldev) +{ + struct device *dev = wldev->dev->dev; + int err; + + assert(bcm43xx_status(wldev) == BCM43xx_STAT_INITIALIZED); + + err = device_create_file(dev, &dev_attr_interference); + if (err) + goto out; + err = device_create_file(dev, &dev_attr_shortpreamble); + if (err) + goto err_remove_interfmode; + +out: + return err; +err_remove_interfmode: + device_remove_file(dev, &dev_attr_interference); + goto out; +} + +void bcm43xx_sysfs_unregister(struct bcm43xx_wldev *wldev) +{ + struct device *dev = wldev->dev->dev; + + device_remove_file(dev, &dev_attr_shortpreamble); + device_remove_file(dev, &dev_attr_interference); +} diff --git a/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_sysfs.h b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_sysfs.h new file mode 100644 index 0000000..399bf26 --- /dev/null +++ b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_sysfs.h @@ -0,0 +1,9 @@ +#ifndef BCM43xx_SYSFS_H_ +#define BCM43xx_SYSFS_H_ + +struct bcm43xx_wldev; + +int bcm43xx_sysfs_register(struct bcm43xx_wldev *dev); +void bcm43xx_sysfs_unregister(struct bcm43xx_wldev *dev); + +#endif /* BCM43xx_SYSFS_H_ */ diff --git a/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_tables.c b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_tables.c new file mode 100644 index 0000000..d7a0592 --- /dev/null +++ b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_tables.c @@ -0,0 +1,376 @@ +/* + + Broadcom BCM43xx wireless driver + + Copyright (c) 2005 Martin Langer , + Copyright (c) 2005 Stefano Brivio + Copyright (c) 2006, 2006 Michael Buesch + Copyright (c) 2005 Danny van Dyk + Copyright (c) 2005 Andreas Jaggi + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "bcm43xx.h" +#include "bcm43xx_tables.h" +#include "bcm43xx_phy.h" + + +const u32 bcm43xx_tab_rotor[] = { + 0xFEB93FFD, 0xFEC63FFD, /* 0 */ + 0xFED23FFD, 0xFEDF3FFD, + 0xFEEC3FFE, 0xFEF83FFE, + 0xFF053FFE, 0xFF113FFE, + 0xFF1E3FFE, 0xFF2A3FFF, /* 8 */ + 0xFF373FFF, 0xFF443FFF, + 0xFF503FFF, 0xFF5D3FFF, + 0xFF693FFF, 0xFF763FFF, + 0xFF824000, 0xFF8F4000, /* 16 */ + 0xFF9B4000, 0xFFA84000, + 0xFFB54000, 0xFFC14000, + 0xFFCE4000, 0xFFDA4000, + 0xFFE74000, 0xFFF34000, /* 24 */ + 0x00004000, 0x000D4000, + 0x00194000, 0x00264000, + 0x00324000, 0x003F4000, + 0x004B4000, 0x00584000, /* 32 */ + 0x00654000, 0x00714000, + 0x007E4000, 0x008A3FFF, + 0x00973FFF, 0x00A33FFF, + 0x00B03FFF, 0x00BC3FFF, /* 40 */ + 0x00C93FFF, 0x00D63FFF, + 0x00E23FFE, 0x00EF3FFE, + 0x00FB3FFE, 0x01083FFE, + 0x01143FFE, 0x01213FFD, /* 48 */ + 0x012E3FFD, 0x013A3FFD, + 0x01473FFD, +}; + +const u32 bcm43xx_tab_retard[] = { + 0xDB93CB87, 0xD666CF64, /* 0 */ + 0xD1FDD358, 0xCDA6D826, + 0xCA38DD9F, 0xC729E2B4, + 0xC469E88E, 0xC26AEE2B, + 0xC0DEF46C, 0xC073FA62, /* 8 */ + 0xC01D00D5, 0xC0760743, + 0xC1560D1E, 0xC2E51369, + 0xC4ED18FF, 0xC7AC1ED7, + 0xCB2823B2, 0xCEFA28D9, /* 16 */ + 0xD2F62D3F, 0xD7BB3197, + 0xDCE53568, 0xE1FE3875, + 0xE7D13B35, 0xED663D35, + 0xF39B3EC4, 0xF98E3FA7, /* 24 */ + 0x00004000, 0x06723FA7, + 0x0C653EC4, 0x129A3D35, + 0x182F3B35, 0x1E023875, + 0x231B3568, 0x28453197, /* 32 */ + 0x2D0A2D3F, 0x310628D9, + 0x34D823B2, 0x38541ED7, + 0x3B1318FF, 0x3D1B1369, + 0x3EAA0D1E, 0x3F8A0743, /* 40 */ + 0x3FE300D5, 0x3F8DFA62, + 0x3F22F46C, 0x3D96EE2B, + 0x3B97E88E, 0x38D7E2B4, + 0x35C8DD9F, 0x325AD826, /* 48 */ + 0x2E03D358, 0x299ACF64, + 0x246DCB87, +}; + +const u16 bcm43xx_tab_finefreqa[] = { + 0x0082, 0x0082, 0x0102, 0x0182, /* 0 */ + 0x0202, 0x0282, 0x0302, 0x0382, + 0x0402, 0x0482, 0x0502, 0x0582, + 0x05E2, 0x0662, 0x06E2, 0x0762, + 0x07E2, 0x0842, 0x08C2, 0x0942, /* 16 */ + 0x09C2, 0x0A22, 0x0AA2, 0x0B02, + 0x0B82, 0x0BE2, 0x0C62, 0x0CC2, + 0x0D42, 0x0DA2, 0x0E02, 0x0E62, + 0x0EE2, 0x0F42, 0x0FA2, 0x1002, /* 32 */ + 0x1062, 0x10C2, 0x1122, 0x1182, + 0x11E2, 0x1242, 0x12A2, 0x12E2, + 0x1342, 0x13A2, 0x1402, 0x1442, + 0x14A2, 0x14E2, 0x1542, 0x1582, /* 48 */ + 0x15E2, 0x1622, 0x1662, 0x16C1, + 0x1701, 0x1741, 0x1781, 0x17E1, + 0x1821, 0x1861, 0x18A1, 0x18E1, + 0x1921, 0x1961, 0x19A1, 0x19E1, /* 64 */ + 0x1A21, 0x1A61, 0x1AA1, 0x1AC1, + 0x1B01, 0x1B41, 0x1B81, 0x1BA1, + 0x1BE1, 0x1C21, 0x1C41, 0x1C81, + 0x1CA1, 0x1CE1, 0x1D01, 0x1D41, /* 80 */ + 0x1D61, 0x1DA1, 0x1DC1, 0x1E01, + 0x1E21, 0x1E61, 0x1E81, 0x1EA1, + 0x1EE1, 0x1F01, 0x1F21, 0x1F41, + 0x1F81, 0x1FA1, 0x1FC1, 0x1FE1, /* 96 */ + 0x2001, 0x2041, 0x2061, 0x2081, + 0x20A1, 0x20C1, 0x20E1, 0x2101, + 0x2121, 0x2141, 0x2161, 0x2181, + 0x21A1, 0x21C1, 0x21E1, 0x2201, /* 112 */ + 0x2221, 0x2241, 0x2261, 0x2281, + 0x22A1, 0x22C1, 0x22C1, 0x22E1, + 0x2301, 0x2321, 0x2341, 0x2361, + 0x2361, 0x2381, 0x23A1, 0x23C1, /* 128 */ + 0x23E1, 0x23E1, 0x2401, 0x2421, + 0x2441, 0x2441, 0x2461, 0x2481, + 0x2481, 0x24A1, 0x24C1, 0x24C1, + 0x24E1, 0x2501, 0x2501, 0x2521, /* 144 */ + 0x2541, 0x2541, 0x2561, 0x2561, + 0x2581, 0x25A1, 0x25A1, 0x25C1, + 0x25C1, 0x25E1, 0x2601, 0x2601, + 0x2621, 0x2621, 0x2641, 0x2641, /* 160 */ + 0x2661, 0x2661, 0x2681, 0x2681, + 0x26A1, 0x26A1, 0x26C1, 0x26C1, + 0x26E1, 0x26E1, 0x2701, 0x2701, + 0x2721, 0x2721, 0x2740, 0x2740, /* 176 */ + 0x2760, 0x2760, 0x2780, 0x2780, + 0x2780, 0x27A0, 0x27A0, 0x27C0, + 0x27C0, 0x27E0, 0x27E0, 0x27E0, + 0x2800, 0x2800, 0x2820, 0x2820, /* 192 */ + 0x2820, 0x2840, 0x2840, 0x2840, + 0x2860, 0x2860, 0x2880, 0x2880, + 0x2880, 0x28A0, 0x28A0, 0x28A0, + 0x28C0, 0x28C0, 0x28C0, 0x28E0, /* 208 */ + 0x28E0, 0x28E0, 0x2900, 0x2900, + 0x2900, 0x2920, 0x2920, 0x2920, + 0x2940, 0x2940, 0x2940, 0x2960, + 0x2960, 0x2960, 0x2960, 0x2980, /* 224 */ + 0x2980, 0x2980, 0x29A0, 0x29A0, + 0x29A0, 0x29A0, 0x29C0, 0x29C0, + 0x29C0, 0x29E0, 0x29E0, 0x29E0, + 0x29E0, 0x2A00, 0x2A00, 0x2A00, /* 240 */ + 0x2A00, 0x2A20, 0x2A20, 0x2A20, + 0x2A20, 0x2A40, 0x2A40, 0x2A40, + 0x2A40, 0x2A60, 0x2A60, 0x2A60, +}; + +const u16 bcm43xx_tab_finefreqg[] = { + 0x0089, 0x02E9, 0x0409, 0x04E9, /* 0 */ + 0x05A9, 0x0669, 0x0709, 0x0789, + 0x0829, 0x08A9, 0x0929, 0x0989, + 0x0A09, 0x0A69, 0x0AC9, 0x0B29, + 0x0BA9, 0x0BE9, 0x0C49, 0x0CA9, /* 16 */ + 0x0D09, 0x0D69, 0x0DA9, 0x0E09, + 0x0E69, 0x0EA9, 0x0F09, 0x0F49, + 0x0FA9, 0x0FE9, 0x1029, 0x1089, + 0x10C9, 0x1109, 0x1169, 0x11A9, /* 32 */ + 0x11E9, 0x1229, 0x1289, 0x12C9, + 0x1309, 0x1349, 0x1389, 0x13C9, + 0x1409, 0x1449, 0x14A9, 0x14E9, + 0x1529, 0x1569, 0x15A9, 0x15E9, /* 48 */ + 0x1629, 0x1669, 0x16A9, 0x16E8, + 0x1728, 0x1768, 0x17A8, 0x17E8, + 0x1828, 0x1868, 0x18A8, 0x18E8, + 0x1928, 0x1968, 0x19A8, 0x19E8, /* 64 */ + 0x1A28, 0x1A68, 0x1AA8, 0x1AE8, + 0x1B28, 0x1B68, 0x1BA8, 0x1BE8, + 0x1C28, 0x1C68, 0x1CA8, 0x1CE8, + 0x1D28, 0x1D68, 0x1DC8, 0x1E08, /* 80 */ + 0x1E48, 0x1E88, 0x1EC8, 0x1F08, + 0x1F48, 0x1F88, 0x1FE8, 0x2028, + 0x2068, 0x20A8, 0x2108, 0x2148, + 0x2188, 0x21C8, 0x2228, 0x2268, /* 96 */ + 0x22C8, 0x2308, 0x2348, 0x23A8, + 0x23E8, 0x2448, 0x24A8, 0x24E8, + 0x2548, 0x25A8, 0x2608, 0x2668, + 0x26C8, 0x2728, 0x2787, 0x27E7, /* 112 */ + 0x2847, 0x28C7, 0x2947, 0x29A7, + 0x2A27, 0x2AC7, 0x2B47, 0x2BE7, + 0x2CA7, 0x2D67, 0x2E47, 0x2F67, + 0x3247, 0x3526, 0x3646, 0x3726, /* 128 */ + 0x3806, 0x38A6, 0x3946, 0x39E6, + 0x3A66, 0x3AE6, 0x3B66, 0x3BC6, + 0x3C45, 0x3CA5, 0x3D05, 0x3D85, + 0x3DE5, 0x3E45, 0x3EA5, 0x3EE5, /* 144 */ + 0x3F45, 0x3FA5, 0x4005, 0x4045, + 0x40A5, 0x40E5, 0x4145, 0x4185, + 0x41E5, 0x4225, 0x4265, 0x42C5, + 0x4305, 0x4345, 0x43A5, 0x43E5, /* 160 */ + 0x4424, 0x4464, 0x44C4, 0x4504, + 0x4544, 0x4584, 0x45C4, 0x4604, + 0x4644, 0x46A4, 0x46E4, 0x4724, + 0x4764, 0x47A4, 0x47E4, 0x4824, /* 176 */ + 0x4864, 0x48A4, 0x48E4, 0x4924, + 0x4964, 0x49A4, 0x49E4, 0x4A24, + 0x4A64, 0x4AA4, 0x4AE4, 0x4B23, + 0x4B63, 0x4BA3, 0x4BE3, 0x4C23, /* 192 */ + 0x4C63, 0x4CA3, 0x4CE3, 0x4D23, + 0x4D63, 0x4DA3, 0x4DE3, 0x4E23, + 0x4E63, 0x4EA3, 0x4EE3, 0x4F23, + 0x4F63, 0x4FC3, 0x5003, 0x5043, /* 208 */ + 0x5083, 0x50C3, 0x5103, 0x5143, + 0x5183, 0x51E2, 0x5222, 0x5262, + 0x52A2, 0x52E2, 0x5342, 0x5382, + 0x53C2, 0x5402, 0x5462, 0x54A2, /* 224 */ + 0x5502, 0x5542, 0x55A2, 0x55E2, + 0x5642, 0x5682, 0x56E2, 0x5722, + 0x5782, 0x57E1, 0x5841, 0x58A1, + 0x5901, 0x5961, 0x59C1, 0x5A21, /* 240 */ + 0x5AA1, 0x5B01, 0x5B81, 0x5BE1, + 0x5C61, 0x5D01, 0x5D80, 0x5E20, + 0x5EE0, 0x5FA0, 0x6080, 0x61C0, +}; + +const u16 bcm43xx_tab_noisea2[] = { + 0x0001, 0x0001, 0x0001, 0xFFFE, + 0xFFFE, 0x3FFF, 0x1000, 0x0393, +}; + +const u16 bcm43xx_tab_noisea3[] = { + 0x4C4C, 0x4C4C, 0x4C4C, 0x2D36, + 0x4C4C, 0x4C4C, 0x4C4C, 0x2D36, +}; + +const u16 bcm43xx_tab_noiseg1[] = { + 0x013C, 0x01F5, 0x031A, 0x0631, + 0x0001, 0x0001, 0x0001, 0x0001, +}; + +const u16 bcm43xx_tab_noiseg2[] = { + 0x5484, 0x3C40, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, +}; + +const u16 bcm43xx_tab_noisescaleg1[] = { + 0x6C77, 0x5162, 0x3B40, 0x3335, /* 0 */ + 0x2F2D, 0x2A2A, 0x2527, 0x1F21, + 0x1A1D, 0x1719, 0x1616, 0x1414, + 0x1414, 0x1400, 0x1414, 0x1614, + 0x1716, 0x1A19, 0x1F1D, 0x2521, /* 16 */ + 0x2A27, 0x2F2A, 0x332D, 0x3B35, + 0x5140, 0x6C62, 0x0077, +}; + +const u16 bcm43xx_tab_noisescaleg2[] = { + 0xD8DD, 0xCBD4, 0xBCC0, 0XB6B7, /* 0 */ + 0xB2B0, 0xADAD, 0xA7A9, 0x9FA1, + 0x969B, 0x9195, 0x8F8F, 0x8A8A, + 0x8A8A, 0x8A00, 0x8A8A, 0x8F8A, + 0x918F, 0x9695, 0x9F9B, 0xA7A1, /* 16 */ + 0xADA9, 0xB2AD, 0xB6B0, 0xBCB7, + 0xCBC0, 0xD8D4, 0x00DD, +}; + +const u16 bcm43xx_tab_noisescaleg3[] = { + 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, /* 0 */ + 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, + 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, + 0xA4A4, 0xA400, 0xA4A4, 0xA4A4, + 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, /* 16 */ + 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, + 0xA4A4, 0xA4A4, 0x00A4, +}; + +const u16 bcm43xx_tab_sigmasqr1[] = { + 0x007A, 0x0075, 0x0071, 0x006C, /* 0 */ + 0x0067, 0x0063, 0x005E, 0x0059, + 0x0054, 0x0050, 0x004B, 0x0046, + 0x0042, 0x003D, 0x003D, 0x003D, + 0x003D, 0x003D, 0x003D, 0x003D, /* 16 */ + 0x003D, 0x003D, 0x003D, 0x003D, + 0x003D, 0x003D, 0x0000, 0x003D, + 0x003D, 0x003D, 0x003D, 0x003D, + 0x003D, 0x003D, 0x003D, 0x003D, /* 32 */ + 0x003D, 0x003D, 0x003D, 0x003D, + 0x0042, 0x0046, 0x004B, 0x0050, + 0x0054, 0x0059, 0x005E, 0x0063, + 0x0067, 0x006C, 0x0071, 0x0075, /* 48 */ + 0x007A, +}; + +const u16 bcm43xx_tab_sigmasqr2[] = { + 0x00DE, 0x00DC, 0x00DA, 0x00D8, /* 0 */ + 0x00D6, 0x00D4, 0x00D2, 0x00CF, + 0x00CD, 0x00CA, 0x00C7, 0x00C4, + 0x00C1, 0x00BE, 0x00BE, 0x00BE, + 0x00BE, 0x00BE, 0x00BE, 0x00BE, /* 16 */ + 0x00BE, 0x00BE, 0x00BE, 0x00BE, + 0x00BE, 0x00BE, 0x0000, 0x00BE, + 0x00BE, 0x00BE, 0x00BE, 0x00BE, + 0x00BE, 0x00BE, 0x00BE, 0x00BE, /* 32 */ + 0x00BE, 0x00BE, 0x00BE, 0x00BE, + 0x00C1, 0x00C4, 0x00C7, 0x00CA, + 0x00CD, 0x00CF, 0x00D2, 0x00D4, + 0x00D6, 0x00D8, 0x00DA, 0x00DC, /* 48 */ + 0x00DE, +}; + + +static inline void assert_sizes(void) +{ + BUILD_BUG_ON(BCM43xx_TAB_ROTOR_SIZE != ARRAY_SIZE(bcm43xx_tab_rotor)); + BUILD_BUG_ON(BCM43xx_TAB_RETARD_SIZE != ARRAY_SIZE(bcm43xx_tab_retard)); + BUILD_BUG_ON(BCM43xx_TAB_FINEFREQA_SIZE != ARRAY_SIZE(bcm43xx_tab_finefreqa)); + BUILD_BUG_ON(BCM43xx_TAB_FINEFREQG_SIZE != ARRAY_SIZE(bcm43xx_tab_finefreqg)); + BUILD_BUG_ON(BCM43xx_TAB_NOISEA2_SIZE != ARRAY_SIZE(bcm43xx_tab_noisea2)); + BUILD_BUG_ON(BCM43xx_TAB_NOISEA3_SIZE != ARRAY_SIZE(bcm43xx_tab_noisea3)); + BUILD_BUG_ON(BCM43xx_TAB_NOISEG1_SIZE != ARRAY_SIZE(bcm43xx_tab_noiseg1)); + BUILD_BUG_ON(BCM43xx_TAB_NOISEG2_SIZE != ARRAY_SIZE(bcm43xx_tab_noiseg2)); + BUILD_BUG_ON(BCM43xx_TAB_NOISESCALEG_SIZE != ARRAY_SIZE(bcm43xx_tab_noisescaleg1)); + BUILD_BUG_ON(BCM43xx_TAB_NOISESCALEG_SIZE != ARRAY_SIZE(bcm43xx_tab_noisescaleg2)); + BUILD_BUG_ON(BCM43xx_TAB_NOISESCALEG_SIZE != ARRAY_SIZE(bcm43xx_tab_noisescaleg3)); + BUILD_BUG_ON(BCM43xx_TAB_SIGMASQR_SIZE != ARRAY_SIZE(bcm43xx_tab_sigmasqr1)); + BUILD_BUG_ON(BCM43xx_TAB_SIGMASQR_SIZE != ARRAY_SIZE(bcm43xx_tab_sigmasqr2)); +} + + +u16 bcm43xx_ofdmtab_read16(struct bcm43xx_wldev *dev, u16 table, u16 offset) +{ + assert_sizes(); + + bcm43xx_phy_write(dev, BCM43xx_PHY_OTABLECTL, table + offset); + return bcm43xx_phy_read(dev, BCM43xx_PHY_OTABLEI); +} + +void bcm43xx_ofdmtab_write16(struct bcm43xx_wldev *dev, u16 table, + u16 offset, u16 value) +{ + bcm43xx_phy_write(dev, BCM43xx_PHY_OTABLECTL, table + offset); + bcm43xx_phy_write(dev, BCM43xx_PHY_OTABLEI, value); +} + +u32 bcm43xx_ofdmtab_read32(struct bcm43xx_wldev *dev, u16 table, u16 offset) +{ + u32 ret; + + bcm43xx_phy_write(dev, BCM43xx_PHY_OTABLECTL, table + offset); + ret = bcm43xx_phy_read(dev, BCM43xx_PHY_OTABLEQ); + ret <<= 16; + ret |= bcm43xx_phy_read(dev, BCM43xx_PHY_OTABLEI); + + return ret; +} + +void bcm43xx_ofdmtab_write32(struct bcm43xx_wldev *dev, u16 table, + u16 offset, u32 value) +{ + bcm43xx_phy_write(dev, BCM43xx_PHY_OTABLECTL, table + offset); + bcm43xx_phy_write(dev, BCM43xx_PHY_OTABLEI, value); + bcm43xx_phy_write(dev, BCM43xx_PHY_OTABLEQ, (value >> 16)); +} + +u16 bcm43xx_gtab_read(struct bcm43xx_wldev *dev, u16 table, u16 offset) +{ + bcm43xx_phy_write(dev, BCM43xx_PHY_GTABCTL, table + offset); + return bcm43xx_phy_read(dev, BCM43xx_PHY_GTABDATA); +} + +void bcm43xx_gtab_write(struct bcm43xx_wldev *dev, u16 table, + u16 offset, u16 value) +{ + bcm43xx_phy_write(dev, BCM43xx_PHY_GTABCTL, table + offset); + bcm43xx_phy_write(dev, BCM43xx_PHY_GTABDATA, value); +} diff --git a/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_tables.h b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_tables.h new file mode 100644 index 0000000..e4e94c9 --- /dev/null +++ b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_tables.h @@ -0,0 +1,28 @@ +#ifndef BCM43xx_TABLES_H_ +#define BCM43xx_TABLES_H_ + +#define BCM43xx_TAB_ROTOR_SIZE 53 +extern const u32 bcm43xx_tab_rotor[]; +#define BCM43xx_TAB_RETARD_SIZE 53 +extern const u32 bcm43xx_tab_retard[]; +#define BCM43xx_TAB_FINEFREQA_SIZE 256 +extern const u16 bcm43xx_tab_finefreqa[]; +#define BCM43xx_TAB_FINEFREQG_SIZE 256 +extern const u16 bcm43xx_tab_finefreqg[]; +#define BCM43xx_TAB_NOISEA2_SIZE 8 +extern const u16 bcm43xx_tab_noisea2[]; +#define BCM43xx_TAB_NOISEA3_SIZE 8 +extern const u16 bcm43xx_tab_noisea3[]; +#define BCM43xx_TAB_NOISEG1_SIZE 8 +extern const u16 bcm43xx_tab_noiseg1[]; +#define BCM43xx_TAB_NOISEG2_SIZE 8 +extern const u16 bcm43xx_tab_noiseg2[]; +#define BCM43xx_TAB_NOISESCALEG_SIZE 27 +extern const u16 bcm43xx_tab_noisescaleg1[]; +extern const u16 bcm43xx_tab_noisescaleg2[]; +extern const u16 bcm43xx_tab_noisescaleg3[]; +#define BCM43xx_TAB_SIGMASQR_SIZE 53 +extern const u16 bcm43xx_tab_sigmasqr1[]; +extern const u16 bcm43xx_tab_sigmasqr2[]; + +#endif /* BCM43xx_TABLES_H_ */ diff --git a/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_xmit.c b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_xmit.c new file mode 100644 index 0000000..c28ab06 --- /dev/null +++ b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_xmit.c @@ -0,0 +1,603 @@ +/* + + Broadcom BCM43xx wireless driver + + Transmission (TX/RX) related functions. + + Copyright (C) 2005 Martin Langer + Copyright (C) 2005 Stefano Brivio + Copyright (C) 2005, 2006 Michael Buesch + Copyright (C) 2005 Danny van Dyk + Copyright (C) 2005 Andreas Jaggi + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "bcm43xx_xmit.h" +#include "bcm43xx_phy.h" +#include "bcm43xx_dma.h" +#include "bcm43xx_pio.h" + + +/* Extract the bitrate out of a CCK PLCP header. */ +static u8 bcm43xx_plcp_get_bitrate_cck(struct bcm43xx_plcp_hdr6 *plcp) +{ + switch (plcp->raw[0]) { + case 0x0A: + return BCM43xx_CCK_RATE_1MB; + case 0x14: + return BCM43xx_CCK_RATE_2MB; + case 0x37: + return BCM43xx_CCK_RATE_5MB; + case 0x6E: + return BCM43xx_CCK_RATE_11MB; + } + assert(0); + return 0; +} + +/* Extract the bitrate out of an OFDM PLCP header. */ +static u8 bcm43xx_plcp_get_bitrate_ofdm(struct bcm43xx_plcp_hdr6 *plcp) +{ + switch (plcp->raw[0] & 0xF) { + case 0xB: + return BCM43xx_OFDM_RATE_6MB; + case 0xF: + return BCM43xx_OFDM_RATE_9MB; + case 0xA: + return BCM43xx_OFDM_RATE_12MB; + case 0xE: + return BCM43xx_OFDM_RATE_18MB; + case 0x9: + return BCM43xx_OFDM_RATE_24MB; + case 0xD: + return BCM43xx_OFDM_RATE_36MB; + case 0x8: + return BCM43xx_OFDM_RATE_48MB; + case 0xC: + return BCM43xx_OFDM_RATE_54MB; + } + assert(0); + return 0; +} + +u8 bcm43xx_plcp_get_ratecode_cck(const u8 bitrate) +{ + switch (bitrate) { + case BCM43xx_CCK_RATE_1MB: + return 0x0A; + case BCM43xx_CCK_RATE_2MB: + return 0x14; + case BCM43xx_CCK_RATE_5MB: + return 0x37; + case BCM43xx_CCK_RATE_11MB: + return 0x6E; + } + assert(0); + return 0; +} + +u8 bcm43xx_plcp_get_ratecode_ofdm(const u8 bitrate) +{ + switch (bitrate) { + case BCM43xx_OFDM_RATE_6MB: + return 0xB; + case BCM43xx_OFDM_RATE_9MB: + return 0xF; + case BCM43xx_OFDM_RATE_12MB: + return 0xA; + case BCM43xx_OFDM_RATE_18MB: + return 0xE; + case BCM43xx_OFDM_RATE_24MB: + return 0x9; + case BCM43xx_OFDM_RATE_36MB: + return 0xD; + case BCM43xx_OFDM_RATE_48MB: + return 0x8; + case BCM43xx_OFDM_RATE_54MB: + return 0xC; + } + assert(0); + return 0; +} + +void bcm43xx_generate_plcp_hdr(struct bcm43xx_plcp_hdr4 *plcp, + const u16 octets, const u8 bitrate) +{ + __le32 *data = &(plcp->data); + __u8 *raw = plcp->raw; + + if (bcm43xx_is_ofdm_rate(bitrate)) { + *data = bcm43xx_plcp_get_ratecode_ofdm(bitrate); + assert(!(octets & 0xF000)); + *data |= (octets << 5); + *data = cpu_to_le32(*data); + } else { + u32 plen; + + plen = octets * 16 / bitrate; + if ((octets * 16 % bitrate) > 0) { + plen++; + if ((bitrate == BCM43xx_CCK_RATE_11MB) + && ((octets * 8 % 11) < 4)) { + raw[1] = 0x84; + } else + raw[1] = 0x04; + } else + raw[1] = 0x04; + *data |= cpu_to_le32(plen << 16); + raw[0] = bcm43xx_plcp_get_ratecode_cck(bitrate); + } +} + +static u8 bcm43xx_calc_fallback_rate(u8 bitrate) +{ + switch (bitrate) { + case BCM43xx_CCK_RATE_1MB: + return BCM43xx_CCK_RATE_1MB; + case BCM43xx_CCK_RATE_2MB: + return BCM43xx_CCK_RATE_1MB; + case BCM43xx_CCK_RATE_5MB: + return BCM43xx_CCK_RATE_2MB; + case BCM43xx_CCK_RATE_11MB: + return BCM43xx_CCK_RATE_5MB; + case BCM43xx_OFDM_RATE_6MB: + return BCM43xx_CCK_RATE_5MB; + case BCM43xx_OFDM_RATE_9MB: + return BCM43xx_OFDM_RATE_6MB; + case BCM43xx_OFDM_RATE_12MB: + return BCM43xx_OFDM_RATE_9MB; + case BCM43xx_OFDM_RATE_18MB: + return BCM43xx_OFDM_RATE_12MB; + case BCM43xx_OFDM_RATE_24MB: + return BCM43xx_OFDM_RATE_18MB; + case BCM43xx_OFDM_RATE_36MB: + return BCM43xx_OFDM_RATE_24MB; + case BCM43xx_OFDM_RATE_48MB: + return BCM43xx_OFDM_RATE_36MB; + case BCM43xx_OFDM_RATE_54MB: + return BCM43xx_OFDM_RATE_48MB; + } + assert(0); + return 0; +} + +static void generate_txhdr_fw4(struct bcm43xx_wldev *dev, + struct bcm43xx_txhdr_fw4 *txhdr, + const unsigned char *fragment_data, + unsigned int fragment_len, + const struct ieee80211_tx_control *txctl, + u16 cookie) +{ + const struct bcm43xx_phy *phy = &dev->phy; + const struct ieee80211_hdr *wlhdr = (const struct ieee80211_hdr *)fragment_data; + int use_encryption = ((!(txctl->flags & IEEE80211_TXCTL_DO_NOT_ENCRYPT)) && + (txctl->key_idx >= 0)); + u16 fctl = le16_to_cpu(wlhdr->frame_control); + u8 rate, rate_fb; + int rate_ofdm, rate_fb_ofdm; + unsigned int plcp_fragment_len; + u32 mac_ctl = 0; + u16 phy_ctl = 0; + u8 extra_ft = 0; + + memset(txhdr, 0, sizeof(*txhdr)); + + rate = txctl->tx_rate; + rate_ofdm = bcm43xx_is_ofdm_rate(rate); + rate_fb = (txctl->alt_retry_rate == -1) ? rate : txctl->alt_retry_rate; + rate_fb_ofdm = bcm43xx_is_ofdm_rate(rate_fb); + + if (rate_ofdm) + txhdr->phy_rate = bcm43xx_plcp_get_ratecode_ofdm(rate); + else + txhdr->phy_rate = bcm43xx_plcp_get_ratecode_cck(rate); + txhdr->mac_frame_ctl = wlhdr->frame_control; + memcpy(txhdr->tx_receiver, wlhdr->addr1, 6); + + /* Calculate duration for fallback rate */ + if ((rate_fb == rate) || + (wlhdr->duration_id & cpu_to_le16(0x8000)) || + (wlhdr->duration_id == cpu_to_le16(0))) { + /* If the fallback rate equals the normal rate or the + * dur_id field contains an AID, CFP magic or 0, + * use the original dur_id field. */ + txhdr->dur_fb = wlhdr->duration_id; + } else { + int fbrate_base100kbps = BCM43xx_RATE_TO_BASE100KBPS(rate_fb); + txhdr->dur_fb = ieee80211_generic_frame_duration(dev->wl->hw, + fragment_len, + fbrate_base100kbps); + } + + plcp_fragment_len = fragment_len + FCS_LEN; + if (use_encryption) { + u8 key_idx = (u16)(txctl->key_idx); + struct bcm43xx_key *key; + int wlhdr_len; + size_t iv_len; + + assert(key_idx < dev->max_nr_keys); + key = &(dev->key[key_idx]); + + if (key->enabled) { + /* Hardware appends ICV. */ + plcp_fragment_len += txctl->icv_len; + + key_idx = bcm43xx_kidx_to_fw(dev, key_idx); + mac_ctl |= (key_idx << BCM43xx_TX4_MAC_KEYIDX_SHIFT) & + BCM43xx_TX4_MAC_KEYIDX; + mac_ctl |= (key->algorithm << BCM43xx_TX4_MAC_KEYALG_SHIFT) & + BCM43xx_TX4_MAC_KEYALG; + wlhdr_len = ieee80211_get_hdrlen(fctl); + iv_len = min((size_t)txctl->iv_len, + ARRAY_SIZE(txhdr->iv)); + memcpy(txhdr->iv, ((u8 *)wlhdr) + wlhdr_len, iv_len); + } + } + bcm43xx_generate_plcp_hdr((struct bcm43xx_plcp_hdr4 *)(&txhdr->plcp), + plcp_fragment_len, rate); + bcm43xx_generate_plcp_hdr((struct bcm43xx_plcp_hdr4 *)(&txhdr->plcp_fb), + plcp_fragment_len, rate_fb); + + /* Extra Frame Types */ + if (rate_fb_ofdm) + extra_ft |= BCM43xx_TX4_EFT_FBOFDM; + + /* Set channel radio code. Note that the micrcode ORs 0x100 to + * this value before comparing it to the value in SHM, if this + * is a 5Ghz packet. + */ + txhdr->chan_radio_code = phy->channel; + + /* PHY TX Control word */ + if (rate_ofdm) + phy_ctl |= BCM43xx_TX4_PHY_OFDM; + if (dev->short_preamble) + phy_ctl |= BCM43xx_TX4_PHY_SHORTPRMBL; + switch (txctl->antenna_sel_tx) { + case 0: + phy_ctl |= BCM43xx_TX4_PHY_ANTLAST; + break; + case 1: + phy_ctl |= BCM43xx_TX4_PHY_ANT0; + break; + case 2: + phy_ctl |= BCM43xx_TX4_PHY_ANT1; + break; + default: + assert(0); + } + + /* MAC control */ + if (!(txctl->flags & IEEE80211_TXCTL_NO_ACK)) + mac_ctl |= BCM43xx_TX4_MAC_ACK; + if (!(((fctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL) && + ((fctl & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PSPOLL))) + mac_ctl |= BCM43xx_TX4_MAC_HWSEQ; + if (txctl->flags & IEEE80211_TXCTL_FIRST_FRAGMENT) + mac_ctl |= BCM43xx_TX4_MAC_STMSDU; + if (phy->type == BCM43xx_PHYTYPE_A) + mac_ctl |= BCM43xx_TX4_MAC_5GHZ; + + /* Generate the RTS or CTS-to-self frame */ + if ((txctl->flags & IEEE80211_TXCTL_USE_RTS_CTS) || + (txctl->flags & IEEE80211_TXCTL_USE_CTS_PROTECT)) { + unsigned int len; + struct ieee80211_hdr *hdr; + int rts_rate, rts_rate_fb; + int rts_rate_ofdm, rts_rate_fb_ofdm; + + rts_rate = txctl->rts_cts_rate; + rts_rate_ofdm = bcm43xx_is_ofdm_rate(rts_rate); + rts_rate_fb = bcm43xx_calc_fallback_rate(rts_rate); + rts_rate_fb_ofdm = bcm43xx_is_ofdm_rate(rts_rate_fb); + + if (txctl->flags & IEEE80211_TXCTL_USE_CTS_PROTECT) { + ieee80211_ctstoself_get(dev->wl->hw, + fragment_data, fragment_len, txctl, + (struct ieee80211_cts *)(txhdr->rts_frame)); + mac_ctl |= BCM43xx_TX4_MAC_SENDCTS; + len = sizeof(struct ieee80211_cts); + } else { + ieee80211_rts_get(dev->wl->hw, + fragment_data, fragment_len, txctl, + (struct ieee80211_rts *)(txhdr->rts_frame)); + mac_ctl |= BCM43xx_TX4_MAC_SENDRTS; + len = sizeof(struct ieee80211_rts); + } + len += FCS_LEN; + bcm43xx_generate_plcp_hdr((struct bcm43xx_plcp_hdr4 *)(&txhdr->rts_plcp), + len, rts_rate); + bcm43xx_generate_plcp_hdr((struct bcm43xx_plcp_hdr4 *)(&txhdr->rts_plcp_fb), + len, rts_rate_fb); + hdr = (struct ieee80211_hdr *)(&txhdr->rts_frame); + txhdr->rts_dur_fb = hdr->duration_id; + if (rts_rate_ofdm) { + extra_ft |= BCM43xx_TX4_EFT_RTSOFDM; + txhdr->phy_rate_rts = bcm43xx_plcp_get_ratecode_ofdm(rts_rate); + } else + txhdr->phy_rate_rts = bcm43xx_plcp_get_ratecode_cck(rts_rate); + if (rts_rate_fb_ofdm) + extra_ft |= BCM43xx_TX4_EFT_RTSFBOFDM; + mac_ctl |= BCM43xx_TX4_MAC_LONGFRAME; + } + + /* Magic cookie */ + txhdr->cookie = cpu_to_le16(cookie); + + /* Apply the bitfields */ + txhdr->mac_ctl = cpu_to_le32(mac_ctl); + txhdr->phy_ctl = cpu_to_le16(phy_ctl); + txhdr->extra_ft = extra_ft; +} + +void bcm43xx_generate_txhdr(struct bcm43xx_wldev *dev, + u8 *txhdr, + const unsigned char *fragment_data, + unsigned int fragment_len, + const struct ieee80211_tx_control *txctl, + u16 cookie) +{ + generate_txhdr_fw4(dev, (struct bcm43xx_txhdr_fw4 *)txhdr, + fragment_data, fragment_len, + txctl, cookie); +} + +static s8 bcm43xx_rssi_postprocess(struct bcm43xx_wldev *dev, + u8 in_rssi, int ofdm, + int adjust_2053, int adjust_2050) +{ + struct bcm43xx_phy *phy = &dev->phy; + s32 tmp; + + switch (phy->radio_ver) { + case 0x2050: + if (ofdm) { + tmp = in_rssi; + if (tmp > 127) + tmp -= 256; + tmp *= 73; + tmp /= 64; + if (adjust_2050) + tmp += 25; + else + tmp -= 3; + } else { + if (dev->dev->bus->sprom.r1.boardflags_lo & BCM43xx_BFL_RSSI) { + if (in_rssi > 63) + in_rssi = 63; + tmp = phy->nrssi_lt[in_rssi]; + tmp = 31 - tmp; + tmp *= -131; + tmp /= 128; + tmp -= 57; + } else { + tmp = in_rssi; + tmp = 31 - tmp; + tmp *= -149; + tmp /= 128; + tmp -= 68; + } + if (phy->type == BCM43xx_PHYTYPE_G && + adjust_2050) + tmp += 25; + } + break; + case 0x2060: + if (in_rssi > 127) + tmp = in_rssi - 256; + else + tmp = in_rssi; + break; + default: + tmp = in_rssi; + tmp -= 11; + tmp *= 103; + tmp /= 64; + if (adjust_2053) + tmp -= 109; + else + tmp -= 83; + } + + return (s8)tmp; +} + +//TODO +#if 0 +static s8 bcm43xx_rssinoise_postprocess(struct bcm43xx_wldev *dev, + u8 in_rssi) +{ + struct bcm43xx_phy *phy = &dev->phy; + s8 ret; + + if (phy->type == BCM43xx_PHYTYPE_A) { + //TODO: Incomplete specs. + ret = 0; + } else + ret = bcm43xx_rssi_postprocess(dev, in_rssi, 0, 1, 1); + + return ret; +} +#endif + +void bcm43xx_rx(struct bcm43xx_wldev *dev, + struct sk_buff *skb, + const void *_rxhdr) +{ + struct ieee80211_rx_status status; + struct bcm43xx_plcp_hdr6 *plcp; + struct ieee80211_hdr *wlhdr; + const struct bcm43xx_rxhdr_fw4 *rxhdr = _rxhdr; + u16 fctl; + u16 phystat0, phystat3, chanstat, mactime; + u32 macstat; + u16 chanid; + u8 jssi; + int padding; + + memset(&status, 0, sizeof(status)); + + /* Get metadata about the frame from the header. */ + phystat0 = le16_to_cpu(rxhdr->phy_status0); + phystat3 = le16_to_cpu(rxhdr->phy_status3); + jssi = rxhdr->jssi; + macstat = le32_to_cpu(rxhdr->mac_status); + mactime = le16_to_cpu(rxhdr->mac_time); + chanstat = le16_to_cpu(rxhdr->channel); + + if (macstat & BCM43xx_RX_MAC_FCSERR) + dev->wl->ieee_stats.dot11FCSErrorCount++; + + /* Skip PLCP and padding */ + padding = (macstat & BCM43xx_RX_MAC_PADDING) ? 2 : 0; + plcp = (struct bcm43xx_plcp_hdr6 *)(skb->data + padding); + skb_pull(skb, sizeof(struct bcm43xx_plcp_hdr6) + padding); + /* The skb contains the Wireless Header + payload data now */ + wlhdr = (struct ieee80211_hdr *)(skb->data); + fctl = le16_to_cpu(wlhdr->frame_control); + + skb_trim(skb, skb->len - FCS_LEN); + + if ((macstat & BCM43xx_RX_MAC_DEC) && + !(macstat & BCM43xx_RX_MAC_DECERR)) { + unsigned int keyidx; + int wlhdr_len; + int iv_len; + int icv_len; + + keyidx = ((macstat & BCM43xx_RX_MAC_KEYIDX) + >> BCM43xx_RX_MAC_KEYIDX_SHIFT); + /* We must adjust the key index here. We want the "physical" + * key index, but the ucode passed it slightly different. + */ + keyidx = bcm43xx_kidx_to_raw(dev, keyidx); + assert(keyidx < dev->max_nr_keys); + + if (dev->key[keyidx].algorithm != BCM43xx_SEC_ALGO_NONE) { + /* Remove PROTECTED flag to mark it as decrypted. */ + assert(fctl & IEEE80211_FCTL_PROTECTED); + fctl &= ~IEEE80211_FCTL_PROTECTED; + wlhdr->frame_control = cpu_to_le16(fctl); + + wlhdr_len = ieee80211_get_hdrlen(fctl); + if (skb->data[wlhdr_len + 3] & (1 << 5)) { + /* The Ext-IV Bit is set in the "KeyID" + * octet of the IV. + */ + iv_len = 8; + icv_len = 8; + } else { + iv_len = 4; + icv_len = 4; + } + + /* Remove the IV */ + memmove(skb->data + iv_len, skb->data, wlhdr_len); + skb_pull(skb, iv_len); + /* Remove the ICV */ + skb_trim(skb, skb->len - icv_len); + + status.flag |= RX_FLAG_DECRYPTED; + } + } + + status.signal = bcm43xx_rssi_postprocess(dev, jssi, + (phystat0 & BCM43xx_RX_PHYST0_OFDM), + (phystat0 & BCM43xx_RX_PHYST0_GAINCTL), + (phystat3 & BCM43xx_RX_PHYST3_TRSTATE)); + status.noise = dev->stats.link_noise; + status.ssi = jssi; + if (phystat0 & BCM43xx_RX_PHYST0_OFDM) + status.rate = bcm43xx_plcp_get_bitrate_ofdm(plcp); + else + status.rate = bcm43xx_plcp_get_bitrate_cck(plcp); + status.antenna = !!(phystat0 & BCM43xx_RX_PHYST0_ANT); + status.mactime = mactime; + + chanid = (chanstat & BCM43xx_RX_CHAN_ID) >> BCM43xx_RX_CHAN_ID_SHIFT; + switch (chanstat & BCM43xx_RX_CHAN_PHYTYPE) { + case BCM43xx_PHYTYPE_A: + status.phymode = MODE_IEEE80211A; + status.freq = chanid; + status.channel = bcm43xx_freq_to_channel_a(chanid); + break; + case BCM43xx_PHYTYPE_B: + status.phymode = MODE_IEEE80211B; + status.freq = chanid + 2400; + status.channel = bcm43xx_freq_to_channel_bg(chanid + 2400); + break; + case BCM43xx_PHYTYPE_G: + status.phymode = MODE_IEEE80211G; + status.freq = chanid + 2400; + status.channel = bcm43xx_freq_to_channel_bg(chanid + 2400); + break; + default: + assert(0); + } + + dev->stats.last_rx = jiffies; + ieee80211_rx_irqsafe(dev->wl->hw, skb, &status); +} + +void bcm43xx_handle_txstatus(struct bcm43xx_wldev *dev, + const struct bcm43xx_txstatus *status) +{ + bcm43xx_debugfs_log_txstat(dev, status); + + if (status->intermediate) + return; + if (status->for_ampdu) + return; + if (!status->acked) + dev->wl->ieee_stats.dot11ACKFailureCount++; + if (status->rts_count) { + if (status->rts_count == 0xF) //FIXME + dev->wl->ieee_stats.dot11RTSFailureCount++; + else + dev->wl->ieee_stats.dot11RTSSuccessCount++; + } + + if (bcm43xx_using_pio(dev)) + bcm43xx_pio_handle_txstatus(dev, status); + else + bcm43xx_dma_handle_txstatus(dev, status); +} + +/* Handle TX status report as received through DMA/PIO queues */ +void bcm43xx_handle_hwtxstatus(struct bcm43xx_wldev *dev, + const struct bcm43xx_hwtxstatus *hw) +{ + struct bcm43xx_txstatus status; + u8 tmp; + + status.cookie = le16_to_cpu(hw->cookie); + status.seq = le16_to_cpu(hw->seq); + status.phy_stat = hw->phy_stat; + tmp = hw->count; + status.frame_count = (tmp >> 4); + status.rts_count = (tmp & 0x0F); + tmp = hw->flags; + status.supp_reason = ((tmp & 0x1C) >> 2); + status.pm_indicated = !!(tmp & 0x80); + status.intermediate = !!(tmp & 0x40); + status.for_ampdu = !!(tmp & 0x20); + status.acked = !!(tmp & 0x02); + + bcm43xx_handle_txstatus(dev, &status); +} diff --git a/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_xmit.h b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_xmit.h new file mode 100644 index 0000000..44fa515 --- /dev/null +++ b/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_xmit.h @@ -0,0 +1,250 @@ +#ifndef BCM43xx_XMIT_H_ +#define BCM43xx_XMIT_H_ + +#include "bcm43xx_main.h" + + +#define _bcm43xx_declare_plcp_hdr(size) \ + struct bcm43xx_plcp_hdr##size { \ + union { \ + __le32 data; \ + __u8 raw[size]; \ + } __attribute__((__packed__)); \ + } __attribute__((__packed__)) + +/* struct bcm43xx_plcp_hdr4 */ +_bcm43xx_declare_plcp_hdr(4); +/* struct bcm43xx_plcp_hdr6 */ +_bcm43xx_declare_plcp_hdr(6); + +#undef _bcm43xx_declare_plcp_hdr + + +/* TX header for v4 firmware */ +struct bcm43xx_txhdr_fw4 { + __le32 mac_ctl; /* MAC TX control */ + __le16 mac_frame_ctl; /* Copy of the FrameControl field */ + __le16 tx_fes_time_norm; /* TX FES Time Normal */ + __le16 phy_ctl; /* PHY TX control */ + __le16 phy_ctl_0; /* Unused */ + __le16 phy_ctl_1; /* Unused */ + __le16 phy_ctl_rts_0; /* Unused */ + __le16 phy_ctl_rts_1; /* Unused */ + __u8 phy_rate; /* PHY rate */ + __u8 phy_rate_rts; /* PHY rate for RTS/CTS */ + __u8 extra_ft; /* Extra Frame Types */ + __u8 chan_radio_code; /* Channel Radio Code */ + __u8 iv[16]; /* Encryption IV */ + __u8 tx_receiver[6]; /* TX Frame Receiver address */ + __le16 tx_fes_time_fb; /* TX FES Time Fallback */ + struct bcm43xx_plcp_hdr6 rts_plcp_fb; /* RTS fallback PLCP */ + __le16 rts_dur_fb; /* RTS fallback duration */ + struct bcm43xx_plcp_hdr6 plcp_fb; /* Fallback PLCP */ + __le16 dur_fb; /* Fallback duration */ + __le16 mm_dur_time; /* Unused */ + __le16 mm_dur_time_fb; /* Unused */ + __le32 time_stamp; /* Timestamp */ + PAD_BYTES(2); + __le16 cookie; /* TX frame cookie */ + __le16 tx_status; /* TX status */ + struct bcm43xx_plcp_hdr6 rts_plcp; /* RTS PLCP */ + __u8 rts_frame[16]; /* The RTS frame (if used) */ + PAD_BYTES(2); + struct bcm43xx_plcp_hdr6 plcp; /* Main PLCP */ +} __attribute__((__packed__)); + +/* MAC TX control */ +#define BCM43xx_TX4_MAC_KEYIDX 0x0FF00000 /* Security key index */ +#define BCM43xx_TX4_MAC_KEYIDX_SHIFT 20 +#define BCM43xx_TX4_MAC_KEYALG 0x00070000 /* Security key algorithm */ +#define BCM43xx_TX4_MAC_KEYALG_SHIFT 16 +#define BCM43xx_TX4_MAC_LIFETIME 0x00001000 +#define BCM43xx_TX4_MAC_FRAMEBURST 0x00000800 +#define BCM43xx_TX4_MAC_SENDCTS 0x00000400 +#define BCM43xx_TX4_MAC_AMPDU 0x00000300 +#define BCM43xx_TX4_MAC_AMPDU_SHIFT 8 +#define BCM43xx_TX4_MAC_5GHZ 0x00000080 +#define BCM43xx_TX4_MAC_IGNPMQ 0x00000020 +#define BCM43xx_TX4_MAC_HWSEQ 0x00000010 /* Use Hardware Sequence Number */ +#define BCM43xx_TX4_MAC_STMSDU 0x00000008 /* Start MSDU */ +#define BCM43xx_TX4_MAC_SENDRTS 0x00000004 +#define BCM43xx_TX4_MAC_LONGFRAME 0x00000002 +#define BCM43xx_TX4_MAC_ACK 0x00000001 + +/* Extra Frame Types */ +#define BCM43xx_TX4_EFT_FBOFDM 0x0001 /* Data frame fallback rate type */ +#define BCM43xx_TX4_EFT_RTSOFDM 0x0004 /* RTS/CTS rate type */ +#define BCM43xx_TX4_EFT_RTSFBOFDM 0x0010 /* RTS/CTS fallback rate type */ + +/* PHY TX control word */ +#define BCM43xx_TX4_PHY_OFDM 0x0001 /* Data frame rate type */ +#define BCM43xx_TX4_PHY_SHORTPRMBL 0x0010 /* Use short preamble */ +#define BCM43xx_TX4_PHY_ANT 0x03C0 /* Antenna selection */ +#define BCM43xx_TX4_PHY_ANT0 0x0000 /* Use antenna 0 */ +#define BCM43xx_TX4_PHY_ANT1 0x0100 /* Use antenna 1 */ +#define BCM43xx_TX4_PHY_ANTLAST 0x0300 /* Use last used antenna */ + + + +void bcm43xx_generate_txhdr(struct bcm43xx_wldev *dev, + u8 *txhdr, + const unsigned char *fragment_data, + unsigned int fragment_len, + const struct ieee80211_tx_control *txctl, + u16 cookie); + + +/* Transmit Status */ +struct bcm43xx_txstatus { + u16 cookie; /* The cookie from the txhdr */ + u16 seq; /* Sequence number */ + u8 phy_stat; /* PHY TX status */ + u8 frame_count; /* Frame transmit count */ + u8 rts_count; /* RTS transmit count */ + u8 supp_reason; /* Suppression reason */ + /* flags */ + u8 pm_indicated; /* PM mode indicated to AP */ + u8 intermediate; /* Intermediate status notification (not final) */ + u8 for_ampdu; /* Status is for an AMPDU (afterburner) */ + u8 acked; /* Wireless ACK received */ +}; + +/* txstatus supp_reason values */ +enum { + BCM43xx_TXST_SUPP_NONE, /* Not suppressed */ + BCM43xx_TXST_SUPP_PMQ, /* Suppressed due to PMQ entry */ + BCM43xx_TXST_SUPP_FLUSH, /* Suppressed due to flush request */ + BCM43xx_TXST_SUPP_PREV, /* Previous fragment failed */ + BCM43xx_TXST_SUPP_CHAN, /* Channel mismatch */ + BCM43xx_TXST_SUPP_LIFE, /* Lifetime expired */ + BCM43xx_TXST_SUPP_UNDER, /* Buffer underflow */ + BCM43xx_TXST_SUPP_ABNACK, /* Afterburner NACK */ +}; + +/* Transmit Status as received through DMA/PIO on old chips */ +struct bcm43xx_hwtxstatus { + PAD_BYTES(4); + __le16 cookie; + u8 flags; + u8 count; + PAD_BYTES(2); + __le16 seq; + u8 phy_stat; + PAD_BYTES(1); +} __attribute__((__packed__)); + + +/* Receive header for v4 firmware. */ +struct bcm43xx_rxhdr_fw4 { + __le16 frame_len; /* Frame length */ + PAD_BYTES(2); + __le16 phy_status0; /* PHY RX Status 0 */ + __u8 jssi; /* PHY RX Status 1: JSSI */ + __u8 sig_qual; /* PHY RX Status 1: Signal Quality */ + __le16 phy_status2; /* PHY RX Status 2 */ + __le16 phy_status3; /* PHY RX Status 3 */ + __le32 mac_status; /* MAC RX status */ + __le16 mac_time; + __le16 channel; +} __attribute__((__packed__)); + + +/* PHY RX Status 0 */ +#define BCM43xx_RX_PHYST0_GAINCTL 0x4000 /* Gain Control */ +#define BCM43xx_RX_PHYST0_PLCPHCF 0x0200 +#define BCM43xx_RX_PHYST0_PLCPFV 0x0100 +#define BCM43xx_RX_PHYST0_SHORTPRMBL 0x0080 /* Received with Short Preamble */ +#define BCM43xx_RX_PHYST0_LCRS 0x0040 +#define BCM43xx_RX_PHYST0_ANT 0x0020 /* Antenna */ +#define BCM43xx_RX_PHYST0_UNSRATE 0x0010 +#define BCM43xx_RX_PHYST0_CLIP 0x000C +#define BCM43xx_RX_PHYST0_CLIP_SHIFT 2 +#define BCM43xx_RX_PHYST0_FTYPE 0x0003 /* Frame type */ +#define BCM43xx_RX_PHYST0_CCK 0x0000 /* Frame type: CCK */ +#define BCM43xx_RX_PHYST0_OFDM 0x0001 /* Frame type: OFDM */ +#define BCM43xx_RX_PHYST0_PRE_N 0x0002 /* Pre-standard N-PHY frame */ +#define BCM43xx_RX_PHYST0_STD_N 0x0003 /* Standard N-PHY frame */ + +/* PHY RX Status 2 */ +#define BCM43xx_RX_PHYST2_LNAG 0xC000 /* LNA Gain */ +#define BCM43xx_RX_PHYST2_LNAG_SHIFT 14 +#define BCM43xx_RX_PHYST2_PNAG 0x3C00 /* PNA Gain */ +#define BCM43xx_RX_PHYST2_PNAG_SHIFT 10 +#define BCM43xx_RX_PHYST2_FOFF 0x03FF /* F offset */ + +/* PHY RX Status 3 */ +#define BCM43xx_RX_PHYST3_DIGG 0x1800 /* DIG Gain */ +#define BCM43xx_RX_PHYST3_DIGG_SHIFT 11 +#define BCM43xx_RX_PHYST3_TRSTATE 0x0400 /* TR state */ + +/* MAC RX Status */ +#define BCM43xx_RX_MAC_BEACONSENT 0x00008000 /* Beacon send flag */ +#define BCM43xx_RX_MAC_KEYIDX 0x000007E0 /* Key index */ +#define BCM43xx_RX_MAC_KEYIDX_SHIFT 5 +#define BCM43xx_RX_MAC_DECERR 0x00000010 /* Decrypt error */ +#define BCM43xx_RX_MAC_DEC 0x00000008 /* Decryption attempted */ +#define BCM43xx_RX_MAC_PADDING 0x00000004 /* Pad bytes present */ +#define BCM43xx_RX_MAC_RESP 0x00000002 /* Response frame transmitted */ +#define BCM43xx_RX_MAC_FCSERR 0x00000001 /* FCS error */ + +/* RX channel */ +#define BCM43xx_RX_CHAN_GAIN 0xFC00 /* Gain */ +#define BCM43xx_RX_CHAN_GAIN_SHIFT 10 +#define BCM43xx_RX_CHAN_ID 0x03FC /* Channel ID */ +#define BCM43xx_RX_CHAN_ID_SHIFT 2 +#define BCM43xx_RX_CHAN_PHYTYPE 0x0003 /* PHY type */ + + + +u8 bcm43xx_plcp_get_ratecode_cck(const u8 bitrate); +u8 bcm43xx_plcp_get_ratecode_ofdm(const u8 bitrate); + +void bcm43xx_generate_plcp_hdr(struct bcm43xx_plcp_hdr4 *plcp, + const u16 octets, const u8 bitrate); + +void bcm43xx_rx(struct bcm43xx_wldev *dev, + struct sk_buff *skb, + const void *_rxhdr); + +void bcm43xx_handle_txstatus(struct bcm43xx_wldev *dev, + const struct bcm43xx_txstatus *status); + +void bcm43xx_handle_hwtxstatus(struct bcm43xx_wldev *dev, + const struct bcm43xx_hwtxstatus *hw); + + +/* Helper functions for converting the key-table index from "firmware-format" + * to "raw-format" and back. The firmware API changed for this at some revision. + * We need to account for that here. */ +static inline +int bcm43xx_new_kidx_api(struct bcm43xx_wldev *dev) +{ + /* FIXME: Not sure the change was at rev 351 */ + return (dev->fw.rev >= 351); +} +static inline +u8 bcm43xx_kidx_to_fw(struct bcm43xx_wldev *dev, u8 raw_kidx) +{ + u8 firmware_kidx; + if (bcm43xx_new_kidx_api(dev)) { + firmware_kidx = raw_kidx; + } else { + if (raw_kidx >= 4) /* Is per STA key? */ + firmware_kidx = raw_kidx - 4; + else + firmware_kidx = raw_kidx; /* TX default key */ + } + return firmware_kidx; +} +static inline +u8 bcm43xx_kidx_to_raw(struct bcm43xx_wldev *dev, u8 firmware_kidx) +{ + u8 raw_kidx; + if (bcm43xx_new_kidx_api(dev)) + raw_kidx = firmware_kidx; + else + raw_kidx = firmware_kidx + 4; /* RX default keys or per STA keys */ + return raw_kidx; +} + +#endif /* BCM43xx_XMIT_H_ */ diff --git a/drivers/net/wireless/mac80211/p54/Kconfig b/drivers/net/wireless/mac80211/p54/Kconfig new file mode 100644 index 0000000..8e16188 --- /dev/null +++ b/drivers/net/wireless/mac80211/p54/Kconfig @@ -0,0 +1,10 @@ +config P54_COMMON + tristate "Softmac Prism54 support" + depends on MAC80211 && WLAN_80211 && FW_LOADER && EXPERIMENTAL +config P54_USB + tristate "Prism54 USB support" + depends on P54_COMMON && USB + select CRC32 +config P54_PCI + tristate "Prism54 PCI support" + depends on P54_COMMON && PCI diff --git a/drivers/net/wireless/mac80211/p54/Makefile b/drivers/net/wireless/mac80211/p54/Makefile new file mode 100644 index 0000000..d79541a --- /dev/null +++ b/drivers/net/wireless/mac80211/p54/Makefile @@ -0,0 +1,4 @@ +obj-$(CONFIG_P54_COMMON) += prism54common.o +obj-$(CONFIG_P54_USB) += prism54usb.o +obj-$(CONFIG_P54_PCI) += prism54pci.o + diff --git a/drivers/net/wireless/mac80211/p54/net2280.h b/drivers/net/wireless/mac80211/p54/net2280.h new file mode 100644 index 0000000..120eb83 --- /dev/null +++ b/drivers/net/wireless/mac80211/p54/net2280.h @@ -0,0 +1,452 @@ +#ifndef NET2280_H +#define NET2280_H +/* + * NetChip 2280 high/full speed USB device controller. + * Unlike many such controllers, this one talks PCI. + */ + +/* + * Copyright (C) 2002 NetChip Technology, Inc. (http://www.netchip.com) + * Copyright (C) 2003 David Brownell + * + * 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 + */ + +/*-------------------------------------------------------------------------*/ + +/* NET2280 MEMORY MAPPED REGISTERS + * + * The register layout came from the chip documentation, and the bit + * number definitions were extracted from chip specification. + * + * Use the shift operator ('<<') to build bit masks, with readl/writel + * to access the registers through PCI. + */ + +/* main registers, BAR0 + 0x0000 */ +struct net2280_regs { + // offset 0x0000 + __le32 devinit; +#define LOCAL_CLOCK_FREQUENCY 8 +#define FORCE_PCI_RESET 7 +#define PCI_ID 6 +#define PCI_ENABLE 5 +#define FIFO_SOFT_RESET 4 +#define CFG_SOFT_RESET 3 +#define PCI_SOFT_RESET 2 +#define USB_SOFT_RESET 1 +#define M8051_RESET 0 + __le32 eectl; +#define EEPROM_ADDRESS_WIDTH 23 +#define EEPROM_CHIP_SELECT_ACTIVE 22 +#define EEPROM_PRESENT 21 +#define EEPROM_VALID 20 +#define EEPROM_BUSY 19 +#define EEPROM_CHIP_SELECT_ENABLE 18 +#define EEPROM_BYTE_READ_START 17 +#define EEPROM_BYTE_WRITE_START 16 +#define EEPROM_READ_DATA 8 +#define EEPROM_WRITE_DATA 0 + __le32 eeclkfreq; + u32 _unused0; + // offset 0x0010 + + __le32 pciirqenb0; /* interrupt PCI master ... */ +#define SETUP_PACKET_INTERRUPT_ENABLE 7 +#define ENDPOINT_F_INTERRUPT_ENABLE 6 +#define ENDPOINT_E_INTERRUPT_ENABLE 5 +#define ENDPOINT_D_INTERRUPT_ENABLE 4 +#define ENDPOINT_C_INTERRUPT_ENABLE 3 +#define ENDPOINT_B_INTERRUPT_ENABLE 2 +#define ENDPOINT_A_INTERRUPT_ENABLE 1 +#define ENDPOINT_0_INTERRUPT_ENABLE 0 + __le32 pciirqenb1; +#define PCI_INTERRUPT_ENABLE 31 +#define POWER_STATE_CHANGE_INTERRUPT_ENABLE 27 +#define PCI_ARBITER_TIMEOUT_INTERRUPT_ENABLE 26 +#define PCI_PARITY_ERROR_INTERRUPT_ENABLE 25 +#define PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE 20 +#define PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE 19 +#define PCI_TARGET_ABORT_ASSERTED_INTERRUPT_ENABLE 18 +#define PCI_RETRY_ABORT_INTERRUPT_ENABLE 17 +#define PCI_MASTER_CYCLE_DONE_INTERRUPT_ENABLE 16 +#define GPIO_INTERRUPT_ENABLE 13 +#define DMA_D_INTERRUPT_ENABLE 12 +#define DMA_C_INTERRUPT_ENABLE 11 +#define DMA_B_INTERRUPT_ENABLE 10 +#define DMA_A_INTERRUPT_ENABLE 9 +#define EEPROM_DONE_INTERRUPT_ENABLE 8 +#define VBUS_INTERRUPT_ENABLE 7 +#define CONTROL_STATUS_INTERRUPT_ENABLE 6 +#define ROOT_PORT_RESET_INTERRUPT_ENABLE 4 +#define SUSPEND_REQUEST_INTERRUPT_ENABLE 3 +#define SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE 2 +#define RESUME_INTERRUPT_ENABLE 1 +#define SOF_INTERRUPT_ENABLE 0 + __le32 cpu_irqenb0; /* ... or onboard 8051 */ +#define SETUP_PACKET_INTERRUPT_ENABLE 7 +#define ENDPOINT_F_INTERRUPT_ENABLE 6 +#define ENDPOINT_E_INTERRUPT_ENABLE 5 +#define ENDPOINT_D_INTERRUPT_ENABLE 4 +#define ENDPOINT_C_INTERRUPT_ENABLE 3 +#define ENDPOINT_B_INTERRUPT_ENABLE 2 +#define ENDPOINT_A_INTERRUPT_ENABLE 1 +#define ENDPOINT_0_INTERRUPT_ENABLE 0 + __le32 cpu_irqenb1; +#define CPU_INTERRUPT_ENABLE 31 +#define POWER_STATE_CHANGE_INTERRUPT_ENABLE 27 +#define PCI_ARBITER_TIMEOUT_INTERRUPT_ENABLE 26 +#define PCI_PARITY_ERROR_INTERRUPT_ENABLE 25 +#define PCI_INTA_INTERRUPT_ENABLE 24 +#define PCI_PME_INTERRUPT_ENABLE 23 +#define PCI_SERR_INTERRUPT_ENABLE 22 +#define PCI_PERR_INTERRUPT_ENABLE 21 +#define PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE 20 +#define PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE 19 +#define PCI_RETRY_ABORT_INTERRUPT_ENABLE 17 +#define PCI_MASTER_CYCLE_DONE_INTERRUPT_ENABLE 16 +#define GPIO_INTERRUPT_ENABLE 13 +#define DMA_D_INTERRUPT_ENABLE 12 +#define DMA_C_INTERRUPT_ENABLE 11 +#define DMA_B_INTERRUPT_ENABLE 10 +#define DMA_A_INTERRUPT_ENABLE 9 +#define EEPROM_DONE_INTERRUPT_ENABLE 8 +#define VBUS_INTERRUPT_ENABLE 7 +#define CONTROL_STATUS_INTERRUPT_ENABLE 6 +#define ROOT_PORT_RESET_INTERRUPT_ENABLE 4 +#define SUSPEND_REQUEST_INTERRUPT_ENABLE 3 +#define SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE 2 +#define RESUME_INTERRUPT_ENABLE 1 +#define SOF_INTERRUPT_ENABLE 0 + + // offset 0x0020 + u32 _unused1; + __le32 usbirqenb1; +#define USB_INTERRUPT_ENABLE 31 +#define POWER_STATE_CHANGE_INTERRUPT_ENABLE 27 +#define PCI_ARBITER_TIMEOUT_INTERRUPT_ENABLE 26 +#define PCI_PARITY_ERROR_INTERRUPT_ENABLE 25 +#define PCI_INTA_INTERRUPT_ENABLE 24 +#define PCI_PME_INTERRUPT_ENABLE 23 +#define PCI_SERR_INTERRUPT_ENABLE 22 +#define PCI_PERR_INTERRUPT_ENABLE 21 +#define PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE 20 +#define PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE 19 +#define PCI_RETRY_ABORT_INTERRUPT_ENABLE 17 +#define PCI_MASTER_CYCLE_DONE_INTERRUPT_ENABLE 16 +#define GPIO_INTERRUPT_ENABLE 13 +#define DMA_D_INTERRUPT_ENABLE 12 +#define DMA_C_INTERRUPT_ENABLE 11 +#define DMA_B_INTERRUPT_ENABLE 10 +#define DMA_A_INTERRUPT_ENABLE 9 +#define EEPROM_DONE_INTERRUPT_ENABLE 8 +#define VBUS_INTERRUPT_ENABLE 7 +#define CONTROL_STATUS_INTERRUPT_ENABLE 6 +#define ROOT_PORT_RESET_INTERRUPT_ENABLE 4 +#define SUSPEND_REQUEST_INTERRUPT_ENABLE 3 +#define SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE 2 +#define RESUME_INTERRUPT_ENABLE 1 +#define SOF_INTERRUPT_ENABLE 0 + __le32 irqstat0; +#define INTA_ASSERTED 12 +#define SETUP_PACKET_INTERRUPT 7 +#define ENDPOINT_F_INTERRUPT 6 +#define ENDPOINT_E_INTERRUPT 5 +#define ENDPOINT_D_INTERRUPT 4 +#define ENDPOINT_C_INTERRUPT 3 +#define ENDPOINT_B_INTERRUPT 2 +#define ENDPOINT_A_INTERRUPT 1 +#define ENDPOINT_0_INTERRUPT 0 + __le32 irqstat1; +#define POWER_STATE_CHANGE_INTERRUPT 27 +#define PCI_ARBITER_TIMEOUT_INTERRUPT 26 +#define PCI_PARITY_ERROR_INTERRUPT 25 +#define PCI_INTA_INTERRUPT 24 +#define PCI_PME_INTERRUPT 23 +#define PCI_SERR_INTERRUPT 22 +#define PCI_PERR_INTERRUPT 21 +#define PCI_MASTER_ABORT_RECEIVED_INTERRUPT 20 +#define PCI_TARGET_ABORT_RECEIVED_INTERRUPT 19 +#define PCI_RETRY_ABORT_INTERRUPT 17 +#define PCI_MASTER_CYCLE_DONE_INTERRUPT 16 +#define GPIO_INTERRUPT 13 +#define DMA_D_INTERRUPT 12 +#define DMA_C_INTERRUPT 11 +#define DMA_B_INTERRUPT 10 +#define DMA_A_INTERRUPT 9 +#define EEPROM_DONE_INTERRUPT 8 +#define VBUS_INTERRUPT 7 +#define CONTROL_STATUS_INTERRUPT 6 +#define ROOT_PORT_RESET_INTERRUPT 4 +#define SUSPEND_REQUEST_INTERRUPT 3 +#define SUSPEND_REQUEST_CHANGE_INTERRUPT 2 +#define RESUME_INTERRUPT 1 +#define SOF_INTERRUPT 0 + // offset 0x0030 + __le32 idxaddr; + __le32 idxdata; + __le32 fifoctl; +#define PCI_BASE2_RANGE 16 +#define IGNORE_FIFO_AVAILABILITY 3 +#define PCI_BASE2_SELECT 2 +#define FIFO_CONFIGURATION_SELECT 0 + u32 _unused2; + // offset 0x0040 + __le32 memaddr; +#define START 28 +#define DIRECTION 27 +#define FIFO_DIAGNOSTIC_SELECT 24 +#define MEMORY_ADDRESS 0 + __le32 memdata0; + __le32 memdata1; + u32 _unused3; + // offset 0x0050 + __le32 gpioctl; +#define GPIO3_LED_SELECT 12 +#define GPIO3_INTERRUPT_ENABLE 11 +#define GPIO2_INTERRUPT_ENABLE 10 +#define GPIO1_INTERRUPT_ENABLE 9 +#define GPIO0_INTERRUPT_ENABLE 8 +#define GPIO3_OUTPUT_ENABLE 7 +#define GPIO2_OUTPUT_ENABLE 6 +#define GPIO1_OUTPUT_ENABLE 5 +#define GPIO0_OUTPUT_ENABLE 4 +#define GPIO3_DATA 3 +#define GPIO2_DATA 2 +#define GPIO1_DATA 1 +#define GPIO0_DATA 0 + __le32 gpiostat; +#define GPIO3_INTERRUPT 3 +#define GPIO2_INTERRUPT 2 +#define GPIO1_INTERRUPT 1 +#define GPIO0_INTERRUPT 0 +} __attribute__ ((packed)); + +/* usb control, BAR0 + 0x0080 */ +struct net2280_usb_regs { + // offset 0x0080 + __le32 stdrsp; +#define STALL_UNSUPPORTED_REQUESTS 31 +#define SET_TEST_MODE 16 +#define GET_OTHER_SPEED_CONFIGURATION 15 +#define GET_DEVICE_QUALIFIER 14 +#define SET_ADDRESS 13 +#define ENDPOINT_SET_CLEAR_HALT 12 +#define DEVICE_SET_CLEAR_DEVICE_REMOTE_WAKEUP 11 +#define GET_STRING_DESCRIPTOR_2 10 +#define GET_STRING_DESCRIPTOR_1 9 +#define GET_STRING_DESCRIPTOR_0 8 +#define GET_SET_INTERFACE 6 +#define GET_SET_CONFIGURATION 5 +#define GET_CONFIGURATION_DESCRIPTOR 4 +#define GET_DEVICE_DESCRIPTOR 3 +#define GET_ENDPOINT_STATUS 2 +#define GET_INTERFACE_STATUS 1 +#define GET_DEVICE_STATUS 0 + __le32 prodvendid; +#define PRODUCT_ID 16 +#define VENDOR_ID 0 + __le32 relnum; + __le32 usbctl; +#define SERIAL_NUMBER_INDEX 16 +#define PRODUCT_ID_STRING_ENABLE 13 +#define VENDOR_ID_STRING_ENABLE 12 +#define USB_ROOT_PORT_WAKEUP_ENABLE 11 +#define VBUS_PIN 10 +#define TIMED_DISCONNECT 9 +#define SUSPEND_IMMEDIATELY 7 +#define SELF_POWERED_USB_DEVICE 6 +#define REMOTE_WAKEUP_SUPPORT 5 +#define PME_POLARITY 4 +#define USB_DETECT_ENABLE 3 +#define PME_WAKEUP_ENABLE 2 +#define DEVICE_REMOTE_WAKEUP_ENABLE 1 +#define SELF_POWERED_STATUS 0 + // offset 0x0090 + __le32 usbstat; +#define HIGH_SPEED 7 +#define FULL_SPEED 6 +#define GENERATE_RESUME 5 +#define GENERATE_DEVICE_REMOTE_WAKEUP 4 + __le32 xcvrdiag; +#define FORCE_HIGH_SPEED_MODE 31 +#define FORCE_FULL_SPEED_MODE 30 +#define USB_TEST_MODE 24 +#define LINE_STATE 16 +#define TRANSCEIVER_OPERATION_MODE 2 +#define TRANSCEIVER_SELECT 1 +#define TERMINATION_SELECT 0 + __le32 setup0123; + __le32 setup4567; + // offset 0x0090 + u32 _unused0; + __le32 ouraddr; +#define FORCE_IMMEDIATE 7 +#define OUR_USB_ADDRESS 0 + __le32 ourconfig; +} __attribute__ ((packed)); + +/* pci control, BAR0 + 0x0100 */ +struct net2280_pci_regs { + // offset 0x0100 + __le32 pcimstctl; +#define PCI_ARBITER_PARK_SELECT 13 +#define PCI_MULTI LEVEL_ARBITER 12 +#define PCI_RETRY_ABORT_ENABLE 11 +#define DMA_MEMORY_WRITE_AND_INVALIDATE_ENABLE 10 +#define DMA_READ_MULTIPLE_ENABLE 9 +#define DMA_READ_LINE_ENABLE 8 +#define PCI_MASTER_COMMAND_SELECT 6 +#define MEM_READ_OR_WRITE 0 +#define IO_READ_OR_WRITE 1 +#define CFG_READ_OR_WRITE 2 +#define PCI_MASTER_START 5 +#define PCI_MASTER_READ_WRITE 4 +#define PCI_MASTER_WRITE 0 +#define PCI_MASTER_READ 1 +#define PCI_MASTER_BYTE_WRITE_ENABLES 0 + __le32 pcimstaddr; + __le32 pcimstdata; + __le32 pcimststat; +#define PCI_ARBITER_CLEAR 2 +#define PCI_EXTERNAL_ARBITER 1 +#define PCI_HOST_MODE 0 +} __attribute__ ((packed)); + +/* dma control, BAR0 + 0x0180 ... array of four structs like this, + * for channels 0..3. see also struct net2280_dma: descriptor + * that can be loaded into some of these registers. + */ +struct net2280_dma_regs { /* [11.7] */ + // offset 0x0180, 0x01a0, 0x01c0, 0x01e0, + __le32 dmactl; +#define DMA_SCATTER_GATHER_DONE_INTERRUPT_ENABLE 25 +#define DMA_CLEAR_COUNT_ENABLE 21 +#define DESCRIPTOR_POLLING_RATE 19 +#define POLL_CONTINUOUS 0 +#define POLL_1_USEC 1 +#define POLL_100_USEC 2 +#define POLL_1_MSEC 3 +#define DMA_VALID_BIT_POLLING_ENABLE 18 +#define DMA_VALID_BIT_ENABLE 17 +#define DMA_SCATTER_GATHER_ENABLE 16 +#define DMA_OUT_AUTO_START_ENABLE 4 +#define DMA_PREEMPT_ENABLE 3 +#define DMA_FIFO_VALIDATE 2 +#define DMA_ENABLE 1 +#define DMA_ADDRESS_HOLD 0 + __le32 dmastat; +#define DMA_SCATTER_GATHER_DONE_INTERRUPT 25 +#define DMA_TRANSACTION_DONE_INTERRUPT 24 +#define DMA_ABORT 1 +#define DMA_START 0 + u32 _unused0[2]; + // offset 0x0190, 0x01b0, 0x01d0, 0x01f0, + __le32 dmacount; +#define VALID_BIT 31 +#define DMA_DIRECTION 30 +#define DMA_DONE_INTERRUPT_ENABLE 29 +#define END_OF_CHAIN 28 +#define DMA_BYTE_COUNT_MASK ((1<<24)-1) +#define DMA_BYTE_COUNT 0 + __le32 dmaaddr; + __le32 dmadesc; + u32 _unused1; +} __attribute__ ((packed)); + +/* dedicated endpoint registers, BAR0 + 0x0200 */ + +struct net2280_dep_regs { /* [11.8] */ + // offset 0x0200, 0x0210, 0x220, 0x230, 0x240 + __le32 dep_cfg; + // offset 0x0204, 0x0214, 0x224, 0x234, 0x244 + __le32 dep_rsp; + u32 _unused[2]; +} __attribute__ ((packed)); + +/* configurable endpoint registers, BAR0 + 0x0300 ... array of seven structs + * like this, for ep0 then the configurable endpoints A..F + * ep0 reserved for control; E and F have only 64 bytes of fifo + */ +struct net2280_ep_regs { /* [11.9] */ + // offset 0x0300, 0x0320, 0x0340, 0x0360, 0x0380, 0x03a0, 0x03c0 + __le32 ep_cfg; +#define ENDPOINT_BYTE_COUNT 16 +#define ENDPOINT_ENABLE 10 +#define ENDPOINT_TYPE 8 +#define ENDPOINT_DIRECTION 7 +#define ENDPOINT_NUMBER 0 + __le32 ep_rsp; +#define SET_NAK_OUT_PACKETS 15 +#define SET_EP_HIDE_STATUS_PHASE 14 +#define SET_EP_FORCE_CRC_ERROR 13 +#define SET_INTERRUPT_MODE 12 +#define SET_CONTROL_STATUS_PHASE_HANDSHAKE 11 +#define SET_NAK_OUT_PACKETS_MODE 10 +#define SET_ENDPOINT_TOGGLE 9 +#define SET_ENDPOINT_HALT 8 +#define CLEAR_NAK_OUT_PACKETS 7 +#define CLEAR_EP_HIDE_STATUS_PHASE 6 +#define CLEAR_EP_FORCE_CRC_ERROR 5 +#define CLEAR_INTERRUPT_MODE 4 +#define CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE 3 +#define CLEAR_NAK_OUT_PACKETS_MODE 2 +#define CLEAR_ENDPOINT_TOGGLE 1 +#define CLEAR_ENDPOINT_HALT 0 + __le32 ep_irqenb; +#define SHORT_PACKET_OUT_DONE_INTERRUPT_ENABLE 6 +#define SHORT_PACKET_TRANSFERRED_INTERRUPT_ENABLE 5 +#define DATA_PACKET_RECEIVED_INTERRUPT_ENABLE 3 +#define DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE 2 +#define DATA_OUT_PING_TOKEN_INTERRUPT_ENABLE 1 +#define DATA_IN_TOKEN_INTERRUPT_ENABLE 0 + __le32 ep_stat; +#define FIFO_VALID_COUNT 24 +#define HIGH_BANDWIDTH_OUT_TRANSACTION_PID 22 +#define TIMEOUT 21 +#define USB_STALL_SENT 20 +#define USB_IN_NAK_SENT 19 +#define USB_IN_ACK_RCVD 18 +#define USB_OUT_PING_NAK_SENT 17 +#define USB_OUT_ACK_SENT 16 +#define FIFO_OVERFLOW 13 +#define FIFO_UNDERFLOW 12 +#define FIFO_FULL 11 +#define FIFO_EMPTY 10 +#define FIFO_FLUSH 9 +#define SHORT_PACKET_OUT_DONE_INTERRUPT 6 +#define SHORT_PACKET_TRANSFERRED_INTERRUPT 5 +#define NAK_OUT_PACKETS 4 +#define DATA_PACKET_RECEIVED_INTERRUPT 3 +#define DATA_PACKET_TRANSMITTED_INTERRUPT 2 +#define DATA_OUT_PING_TOKEN_INTERRUPT 1 +#define DATA_IN_TOKEN_INTERRUPT 0 + // offset 0x0310, 0x0330, 0x0350, 0x0370, 0x0390, 0x03b0, 0x03d0 + __le32 ep_avail; + __le32 ep_data; + u32 _unused0[2]; +} __attribute__ ((packed)); + +struct net2280_reg_write { + __le16 port; + __le32 addr; + __le32 val; +} __attribute__ ((packed)); + +struct net2280_reg_read { + __le16 port; + __le32 addr; +} __attribute__ ((packed)); +#endif /* NET2280_H */ diff --git a/drivers/net/wireless/mac80211/p54/prism54.h b/drivers/net/wireless/mac80211/p54/prism54.h new file mode 100644 index 0000000..79641de --- /dev/null +++ b/drivers/net/wireless/mac80211/p54/prism54.h @@ -0,0 +1,77 @@ +#ifndef PRISM54_H +#define PRISM54_H + +/* + * Shared defines for all mac80211 Prism54 code + * + * Copyright (c) 2006, Michael Wu + * + * Based on the islsm (softmac prism54) driver, which is: + * Copyright 2004-2006 Jean-Baptiste Note , et al. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +enum control_frame_types { + P54_CONTROL_TYPE_FILTER_SET = 0, + P54_CONTROL_TYPE_CHANNEL_CHANGE, + P54_CONTROL_TYPE_FREQDONE, + P54_CONTROL_TYPE_DCFINIT, + P54_CONTROL_TYPE_FREEQUEUE = 7, + P54_CONTROL_TYPE_TXDONE, + P54_CONTROL_TYPE_PING, + P54_CONTROL_TYPE_STAT_READBACK, + P54_CONTROL_TYPE_BBP, + P54_CONTROL_TYPE_EEPROM_READBACK, + P54_CONTROL_TYPE_LED +}; + +struct p54_control_hdr { + __le16 magic1; + __le16 len; + __le32 req_id; + __le16 type; /* enum control_frame_types */ + u8 retry1; + u8 retry2; + u8 data[0]; +} __attribute__ ((packed)); + +#define EEPROM_READBACK_LEN (sizeof(struct p54_control_hdr) + 4 /* p54_eeprom_lm86 */) +#define MAX_RX_SIZE (IEEE80211_MAX_RTS_THRESHOLD + sizeof(struct p54_control_hdr) + 20 /* length of struct p54_rx_hdr */ + 16 ) + +#define ISL38XX_DEV_FIRMWARE_ADDR 0x20000 + +struct p54_common { + u32 rx_start; + u32 rx_end; + struct sk_buff_head tx_queue; + void (*tx)(struct ieee80211_hw *dev, struct p54_control_hdr *data, + size_t len, int free_on_tx); + int (*open)(struct ieee80211_hw *dev); + void (*stop)(struct ieee80211_hw *dev); + int mode; + u8 *mac_addr; + struct pda_iq_autocal_entry *iq_autocal; + unsigned int iq_autocal_len; + struct pda_channel_output_limit *output_limit; + unsigned int output_limit_len; + struct pda_pa_curve_data *curve_data; + __le16 rxhw; + u8 version; + unsigned int tx_hdr_len; + /* FIXME: this channels/modes/rates stuff sucks */ + struct ieee80211_channel channels[14]; + struct ieee80211_rate rates[12]; + struct ieee80211_hw_mode modes[2]; +}; + +int p54_rx(struct ieee80211_hw *dev, struct sk_buff *skb); +void p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw); +int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len); +void p54_fill_eeprom_readback(struct p54_control_hdr *hdr); +struct ieee80211_hw *p54_init_common(size_t priv_data_len); +void p54_free_common(struct ieee80211_hw *dev); + +#endif /* PRISM54_H */ diff --git a/drivers/net/wireless/mac80211/p54/prism54common.c b/drivers/net/wireless/mac80211/p54/prism54common.c new file mode 100644 index 0000000..2539464 --- /dev/null +++ b/drivers/net/wireless/mac80211/p54/prism54common.c @@ -0,0 +1,821 @@ + +/* + * Common code for mac80211 Prism54 drivers + * + * Copyright (c) 2006, Michael Wu + * + * Based on the islsm (softmac prism54) driver, which is: + * Copyright 2004-2006 Jean-Baptiste Note , et al. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include + +#include "prism54.h" +#include "prism54common.h" + +MODULE_AUTHOR("Michael Wu "); +MODULE_DESCRIPTION("Softmac Prism54 common code"); +MODULE_LICENSE("GPL"); + +void p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw) +{ + struct p54_common *priv = dev->priv; + struct bootrec *bootrec; + u32 *data = (u32 *)fw->data; + u32 *end_data = (u32 *)fw->data + (fw->size >> 2); + + if (priv->rx_start) + return; + + while (data < end_data && *data) + data++; + + while (data < end_data && !*data) + data++; + + bootrec = (struct bootrec *) data; + + while ((bootrec->data + le32_to_cpu(bootrec->len)) < end_data) { + u32 code = le32_to_cpu(bootrec->code); + switch (code) { + case BR_CODE_COMPONENT_ID: + switch (be32_to_cpu(*bootrec->data)) { + case FW_FMAC: + printk(KERN_INFO "p54: FreeMAC firmware\n"); + break; + case FW_LM20: + printk(KERN_INFO "p54: LM20 firmware\n"); + break; + case FW_LM86: + printk(KERN_INFO "p54: LM86 firmware\n"); + break; + case FW_LM87: + printk(KERN_INFO "p54: LM87 firmware - not supported yet!\n"); + break; + default: + printk(KERN_INFO "p54: unknown firmware\n"); + break; + } + break; + case BR_CODE_COMPONENT_VERSION: + break; + case BR_CODE_DESCR: + priv->rx_start = le32_to_cpu(bootrec->data[1]); + /* FIXME add sanity checking */ + priv->rx_end = le32_to_cpu(bootrec->data[2]) - 0x3500; + break; + case BR_CODE_EXPOSED_IF: + break; + case BR_CODE_DEPENDENT_IF: + break; + case BR_CODE_END_OF_BRA: + case LEGACY_BR_CODE_END_OF_BRA: + end_data = NULL; + break; + default: + break; + } + bootrec = (struct bootrec *)&bootrec->data[le32_to_cpu(bootrec->len)]; + if ((u32 *)bootrec > end_data) + break; + } +} +EXPORT_SYMBOL_GPL(p54_parse_firmware); + +static int p54_convert_rev0_to_rev1(struct ieee80211_hw *dev, + struct pda_pa_curve_data *curve_data) +{ + struct p54_common *priv = dev->priv; + struct pda_pa_curve_data_sample_rev1 *rev1; + struct pda_pa_curve_data_sample_rev0 *rev0; + size_t cd_len = sizeof(*curve_data) + + (curve_data->points_per_channel*sizeof(*rev1) + 2) * + curve_data->channels; + unsigned int i, j; + void *source, *target; + + priv->curve_data = kmalloc(cd_len, GFP_KERNEL); + if (!priv->curve_data) + return -ENOMEM; + + memcpy(priv->curve_data, curve_data, sizeof(*curve_data)); + source = curve_data->data; + target = priv->curve_data->data; + for (i = 0; i < curve_data->channels; i++) { + __le16 *freq = source; + source += sizeof(__le16); + *((__le16 *)target) = *freq; + target += sizeof(__le16); + for (j = 0; j < curve_data->points_per_channel; j++) { + rev1 = target; + rev0 = source; + + rev1->rf_power = rev0->rf_power; + rev1->pa_detector = rev0->pa_detector; + rev1->data_64qam = rev0->pcv; + /* "invent" the points for the other modulations */ +#define SUB(x,y) (u8)((x) - (y)) > (x) ? 0 : (x) - (y) + rev1->data_16qam = SUB(rev0->pcv, 12); + rev1->data_qpsk = SUB(rev1->data_16qam, 12); + rev1->data_bpsk = SUB(rev1->data_qpsk, 12); + rev1->data_barker= SUB(rev1->data_bpsk, 14); +#undef SUB + target += sizeof(*rev1); + source += sizeof(*rev0); + } + } + + return 0; +} + +int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len) +{ + struct p54_common *priv = dev->priv; + struct eeprom_pda_wrap *wrap = NULL; + struct pda_entry *entry; + int i = 0; + unsigned int data_len, entry_len; + void *tmp; + int err; + + wrap = (struct eeprom_pda_wrap *) eeprom; + entry = (void *)wrap->data + wrap->len; + i += 2; + i += le16_to_cpu(entry->len)*2; + while (i < len) { + entry_len = le16_to_cpu(entry->len); + data_len = ((entry_len - 1) << 1); + switch (le16_to_cpu(entry->code)) { + case PDR_MAC_ADDRESS: + SET_IEEE80211_PERM_ADDR(dev, entry->data); + break; + case PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS: + if (data_len < 2) { + err = -EINVAL; + goto err; + } + + if (2 + entry->data[1]*sizeof(*priv->output_limit) > data_len) { + err = -EINVAL; + goto err; + } + + priv->output_limit = kmalloc(entry->data[1] * + sizeof(*priv->output_limit), GFP_KERNEL); + + if (!priv->output_limit) { + err = -ENOMEM; + goto err; + } + + memcpy(priv->output_limit, &entry->data[2], + entry->data[1]*sizeof(*priv->output_limit)); + priv->output_limit_len = entry->data[1]; + break; + case PDR_PRISM_PA_CAL_CURVE_DATA: + if (data_len < sizeof(struct pda_pa_curve_data)) { + err = -EINVAL; + goto err; + } + + if (((struct pda_pa_curve_data *)entry->data)->cal_method_rev) { + priv->curve_data = kmalloc(data_len, GFP_KERNEL); + if (!priv->curve_data) { + err = -ENOMEM; + goto err; + } + + memcpy(priv->curve_data, entry->data, data_len); + } else { + err = p54_convert_rev0_to_rev1(dev, (struct pda_pa_curve_data *)entry->data); + if (err) + goto err; + } + + break; + case PDR_PRISM_ZIF_TX_IQ_CALIBRATION: + priv->iq_autocal = kmalloc(data_len, GFP_KERNEL); + if (!priv->iq_autocal) { + err = -ENOMEM; + goto err; + } + + memcpy(priv->iq_autocal, entry->data, data_len); + priv->iq_autocal_len = data_len / sizeof(struct pda_iq_autocal_entry); + break; + case PDR_INTERFACE_LIST: + tmp = entry->data; + while ((u8 *)tmp < entry->data + data_len) { + struct bootrec_exp_if *exp_if = tmp; + if (le16_to_cpu(exp_if->if_id) == 0xF) + priv->rxhw = exp_if->variant & cpu_to_le16(0x07); + tmp += sizeof(struct bootrec_exp_if); + } + break; + case PDR_HARDWARE_PLATFORM_COMPONENT_ID: + priv->version = *(u8 *)(entry->data + 1); + break; + case PDR_END: + i = len; + break; + } + + entry = (void *)entry + (entry_len + 1)*2; + i += 2; + i += entry_len*2; + } + + if (!priv->iq_autocal || !priv->output_limit || !priv->curve_data) { + printk(KERN_ERR "p54: not all required entries found in eeprom!\n"); + err = -EINVAL; + goto err; + } + + return 0; + + err: + if (priv->iq_autocal) { + kfree(priv->iq_autocal); + priv->iq_autocal = NULL; + } + + if (priv->output_limit) { + kfree(priv->output_limit); + priv->output_limit = NULL; + } + + if (priv->curve_data) { + kfree(priv->curve_data); + priv->curve_data = NULL; + } + + printk(KERN_ERR "p54: eeprom parse failed!\n"); + return err; +} +EXPORT_SYMBOL_GPL(p54_parse_eeprom); + +void p54_fill_eeprom_readback(struct p54_control_hdr *hdr) +{ + struct p54_eeprom_lm86 *eeprom_hdr; + + hdr->magic1 = cpu_to_le16(0x8000); + hdr->len = cpu_to_le16(sizeof(*eeprom_hdr) + 0x2000); + hdr->type = cpu_to_le16(P54_CONTROL_TYPE_EEPROM_READBACK); + hdr->retry1 = hdr->retry2 = 0; + eeprom_hdr = (struct p54_eeprom_lm86 *) hdr->data; + eeprom_hdr->offset = 0x0; + eeprom_hdr->len = cpu_to_le16(0x2000); +} +EXPORT_SYMBOL_GPL(p54_fill_eeprom_readback); + +static void p54_rx_data(struct ieee80211_hw *dev, struct sk_buff *skb) +{ + struct p54_rx_hdr *hdr = (struct p54_rx_hdr *) skb->data; + struct ieee80211_rx_status rx_status = {0}; + u16 freq = le16_to_cpu(hdr->freq); + + rx_status.ssi = hdr->rssi; /* TODO: check this */ + rx_status.rate = hdr->rate & 0x0f; + rx_status.channel = freq == 2484 ? 14 : (freq - 2407)/5; + rx_status.freq = freq; + rx_status.phymode = MODE_IEEE80211G; + + skb_pull(skb, sizeof(*hdr)); + skb_trim(skb, le16_to_cpu(hdr->len)); + + ieee80211_rx_irqsafe(dev, skb, &rx_status); +} + +static void p54_rx_frame_sent(struct ieee80211_hw *dev, struct sk_buff *skb) +{ + struct p54_common *priv = dev->priv; + struct p54_control_hdr *hdr = (struct p54_control_hdr *) skb->data; + struct p54_frame_sent_hdr *payload = (struct p54_frame_sent_hdr *) hdr->data; + struct sk_buff *entry = (struct sk_buff *) priv->tx_queue.next; + u32 addr = le32_to_cpu(hdr->req_id) - 0x70; + struct memrecord *range = NULL; + u32 freed = 0; + u32 last_addr = priv->rx_start; + + while (entry != (struct sk_buff *)&priv->tx_queue) { + range = (struct memrecord *)&entry->cb; + if (range->start_addr == addr) { + struct ieee80211_tx_status status = {{0}}; + + if (entry->next != (struct sk_buff *)&priv->tx_queue) + freed = ((struct memrecord *)&entry->next->cb)->start_addr - last_addr; + else + freed = priv->rx_end - last_addr; + + last_addr = range->end_addr; + __skb_unlink(entry, &priv->tx_queue); + if (!range->control) { + kfree_skb(entry); + break; + } + memcpy(&status.control, range->control, + sizeof(status.control)); + kfree(range->control); + if (!payload->status) + status.flags |= IEEE80211_TX_STATUS_ACK; + else + status.excessive_retries = 1; + status.retry_count = payload->retries - 1; + status.ack_signal = le16_to_cpu(payload->ack_rssi); + skb_pull(entry, sizeof(*hdr) + sizeof(struct p54_tx_control_allocdata)); + ieee80211_tx_status_irqsafe(dev, entry, &status); + break; + } else + last_addr = range->end_addr; + entry = entry->next; + } + + if (freed >= IEEE80211_MAX_RTS_THRESHOLD + 0x170 + + sizeof(struct p54_control_hdr)) + ieee80211_wake_queue(dev, 0); +} + +static void p54_rx_control(struct ieee80211_hw *dev, struct sk_buff *skb) +{ + struct p54_control_hdr *hdr = (struct p54_control_hdr *) skb->data; + + switch (le16_to_cpu(hdr->type)) { + case P54_CONTROL_TYPE_TXDONE: + p54_rx_frame_sent(dev, skb); + break; + case P54_CONTROL_TYPE_BBP: + break; + default: + printk(KERN_DEBUG "%s: not handling 0x%02x type control frame\n", + wiphy_name(dev->wiphy), le16_to_cpu(hdr->type)); + break; + } +} + +/* returns zero if skb can be reused */ +int p54_rx(struct ieee80211_hw *dev, struct sk_buff *skb) +{ + u8 type = le16_to_cpu(*((__le16 *)skb->data)) >> 8; + switch (type) { + case 0x00: + case 0x01: + p54_rx_data(dev, skb); + return -1; + case 0x4d: + /* TODO: do something better... but then again, I've never seen this happen */ + printk(KERN_ERR "%s: Received fault. Probably need to restart hardware now..\n", + wiphy_name(dev->wiphy)); + break; + case 0x80: + p54_rx_control(dev, skb); + break; + default: + printk(KERN_ERR "%s: unknown frame RXed (0x%02x)\n", + wiphy_name(dev->wiphy), type); + break; + } + return 0; +} +EXPORT_SYMBOL_GPL(p54_rx); + +/* + * So, the firmware is somewhat stupid and doesn't know what places in its + * memory incoming data should go to. By poking around in the firmware, we + * can find some unused memory to upload our packets to. However, data that we + * want the card to TX needs to stay intact until the card has told us that + * it is done with it. This function finds empty places we can upload to and + * marks allocated areas as reserved if necessary. p54_rx_frame_sent frees + * allocated areas. + */ +static void p54_assign_address(struct ieee80211_hw *dev, struct sk_buff *skb, + struct p54_control_hdr *data, u32 len, + struct ieee80211_tx_control *control) +{ + struct p54_common *priv = dev->priv; + struct sk_buff *entry = priv->tx_queue.next; + struct sk_buff *target_skb = NULL; + struct memrecord *range; + u32 last_addr = priv->rx_start; + u32 largest_hole = 0; + u32 target_addr = priv->rx_start; + unsigned long flags; + unsigned int left; + len = (len + 0x170 + 3) & ~0x3; /* 0x70 headroom, 0x100 tailroom */ + + spin_lock_irqsave(&priv->tx_queue.lock, flags); + left = skb_queue_len(&priv->tx_queue); + while (left--) { + u32 hole_size; + range = (struct memrecord *)&entry->cb; + hole_size = range->start_addr - last_addr; + if (!target_skb && hole_size >= len) { + target_skb = entry->prev; + hole_size -= len; + target_addr = last_addr; + } + largest_hole = max(largest_hole, hole_size); + last_addr = range->end_addr; + entry = entry->next; + } + if (!target_skb && priv->rx_end - last_addr >= len) { + target_skb = priv->tx_queue.prev; + largest_hole = max(largest_hole, priv->rx_end - last_addr - len); + if (!skb_queue_empty(&priv->tx_queue)) { + range = (struct memrecord *)&target_skb->cb; + target_addr = range->end_addr; + } + } else + largest_hole = max(largest_hole, priv->rx_end - last_addr); + + if (skb) { + range = (struct memrecord *)&skb->cb; + range->start_addr = target_addr; + range->end_addr = target_addr + len; + range->control = control; + __skb_queue_after(&priv->tx_queue, target_skb, skb); + if (largest_hole < IEEE80211_MAX_RTS_THRESHOLD + 0x170 + + sizeof(struct p54_control_hdr)) + ieee80211_stop_queue(dev, 0); + } + spin_unlock_irqrestore(&priv->tx_queue.lock, flags); + + data->req_id = cpu_to_le32(target_addr + 0x70); +} + +static int p54_tx(struct ieee80211_hw *dev, struct sk_buff *skb, + struct ieee80211_tx_control *control) +{ + struct p54_common *priv = dev->priv; + struct p54_control_hdr *hdr; + struct p54_tx_control_allocdata *txhdr; + struct ieee80211_tx_control *control_copy; + size_t padding, len; + u8 rate; + + padding = (unsigned long)(skb->data - (sizeof(*hdr) + sizeof(*txhdr))) & 3; + len = skb->len; + + control_copy = kmalloc(sizeof(*control), GFP_ATOMIC); + if (control_copy) + memcpy(control_copy, control, sizeof(*control)); + + txhdr = (struct p54_tx_control_allocdata *) + skb_push(skb, sizeof(*txhdr) + padding); + hdr = (struct p54_control_hdr *) skb_push(skb, sizeof(*hdr)); + + if (padding) + hdr->magic1 = cpu_to_le16(0x4010); + else + hdr->magic1 = cpu_to_le16(0x0010); + hdr->len = cpu_to_le16(len); + hdr->type = (control->flags & IEEE80211_TXCTL_NO_ACK) ? 0 : cpu_to_le16(1); + hdr->retry1 = hdr->retry2 = control->retry_limit; + p54_assign_address(dev, skb, hdr, skb->len, control_copy); + + memset(txhdr->wep_key, 0x0, 16); + txhdr->padding = 0; + txhdr->padding2 = 0; + + /* TODO: add support for alternate retry TX rates */ + rate = control->tx_rate; + if (control->flags & IEEE80211_TXCTL_USE_RTS_CTS) + rate |= 0x40; + else if (control->flags & IEEE80211_TXCTL_USE_CTS_PROTECT) + rate |= 0x20; + memset(txhdr->rateset, rate, 8); + txhdr->wep_key_present = 0; + txhdr->wep_key_len = 0; + txhdr->frame_type = cpu_to_le32(0x4); + txhdr->magic4 = 0; + txhdr->antenna = control->antenna_sel_tx; + txhdr->output_power = 0x7f; // HW Maximum + txhdr->magic5 = (control->flags & IEEE80211_TXCTL_NO_ACK) ? + 0 : cpu_to_le32(0x23); + if (padding) + txhdr->align[0] = padding; + + priv->tx(dev, hdr, skb->len, 0); + return 0; +} + +static int p54_set_filter(struct ieee80211_hw *dev, u16 filter_type, + const u8 *dst, const u8 *src, u8 antenna, + u32 magic3, u32 magic8, u32 magic9) +{ + struct p54_common *priv = dev->priv; + struct p54_control_hdr *hdr; + struct p54_tx_control_filter *filter; + + hdr = kzalloc(sizeof(*hdr) + sizeof(*filter) + + priv->tx_hdr_len, GFP_KERNEL); + if (!hdr) + return -ENOMEM; + + hdr = (void *)hdr + priv->tx_hdr_len; + + filter = (struct p54_tx_control_filter *) hdr->data; + hdr->magic1 = cpu_to_le16(0x8001); + hdr->len = cpu_to_le16(sizeof(*filter)); + p54_assign_address(dev, NULL, hdr, sizeof(*hdr) + sizeof(*filter), NULL); + hdr->type = cpu_to_le16(P54_CONTROL_TYPE_FILTER_SET); + + filter->filter_type = cpu_to_le16(filter_type); + memcpy(filter->dst, dst, ETH_ALEN); + if (!src) + memset(filter->src, ~0, ETH_ALEN); + else + memcpy(filter->src, src, ETH_ALEN); + filter->antenna = antenna; + filter->magic3 = cpu_to_le32(magic3); + filter->rx_addr = cpu_to_le32(priv->rx_end); + filter->max_rx = cpu_to_le16(0x0620); /* FIXME: for usb ver 1.. maybe */ + filter->rxhw = priv->rxhw; + filter->magic8 = cpu_to_le16(magic8); + filter->magic9 = cpu_to_le16(magic9); + + priv->tx(dev, hdr, sizeof(*hdr) + sizeof(*filter), 1); + return 0; +} + +static int p54_set_freq(struct ieee80211_hw *dev, __le16 freq) +{ + struct p54_common *priv = dev->priv; + struct p54_control_hdr *hdr; + struct p54_tx_control_channel *chan; + unsigned int i; + size_t payload_len = sizeof(*chan) + sizeof(u32)*2 + + sizeof(*chan->curve_data) * + priv->curve_data->points_per_channel; + void *entry; + + hdr = kzalloc(sizeof(*hdr) + payload_len + + priv->tx_hdr_len, GFP_KERNEL); + if (!hdr) + return -ENOMEM; + + hdr = (void *)hdr + priv->tx_hdr_len; + + chan = (struct p54_tx_control_channel *) hdr->data; + + hdr->magic1 = cpu_to_le16(0x8001); + hdr->len = cpu_to_le16(sizeof(*chan)); + hdr->type = cpu_to_le16(P54_CONTROL_TYPE_CHANNEL_CHANGE); + p54_assign_address(dev, NULL, hdr, sizeof(*hdr) + payload_len, NULL); + + chan->magic1 = cpu_to_le16(0x1); + chan->magic2 = cpu_to_le16(0x0); + + for (i = 0; i < priv->iq_autocal_len; i++) { + if (priv->iq_autocal[i].freq != freq) + continue; + + memcpy(&chan->iq_autocal, &priv->iq_autocal[i], + sizeof(*priv->iq_autocal)); + break; + } + if (i == priv->iq_autocal_len) + goto err; + + for (i = 0; i < priv->output_limit_len; i++) { + if (priv->output_limit[i].freq != freq) + continue; + + chan->val_barker = 0x38; + chan->val_bpsk = priv->output_limit[i].val_bpsk; + chan->val_qpsk = priv->output_limit[i].val_qpsk; + chan->val_16qam = priv->output_limit[i].val_16qam; + chan->val_64qam = priv->output_limit[i].val_64qam; + break; + } + if (i == priv->output_limit_len) + goto err; + + chan->pa_points_per_curve = priv->curve_data->points_per_channel; + + entry = priv->curve_data->data; + for (i = 0; i < priv->curve_data->channels; i++) { + if (*((__le16 *)entry) != freq) { + entry += sizeof(__le16); + entry += sizeof(struct pda_pa_curve_data_sample_rev1) * + chan->pa_points_per_curve; + continue; + } + + entry += sizeof(__le16); + memcpy(chan->curve_data, entry, sizeof(*chan->curve_data) * + chan->pa_points_per_curve); + break; + } + + memcpy(hdr->data + payload_len - 4, &chan->val_bpsk, 4); + + priv->tx(dev, hdr, sizeof(*hdr) + payload_len, 1); + return 0; + + err: + printk(KERN_ERR "%s: frequency change failed\n", wiphy_name(dev->wiphy)); + kfree(hdr); + return -EINVAL; +} + +static int p54_set_leds(struct ieee80211_hw *dev, int mode, int link, int act) +{ + struct p54_common *priv = dev->priv; + struct p54_control_hdr *hdr; + struct p54_tx_control_led *led; + + hdr = kzalloc(sizeof(*hdr) + sizeof(*led) + + priv->tx_hdr_len, GFP_KERNEL); + if (!hdr) + return -ENOMEM; + + hdr = (void *)hdr + priv->tx_hdr_len; + hdr->magic1 = cpu_to_le16(0x8001); + hdr->len = cpu_to_le16(sizeof(*led)); + hdr->type = cpu_to_le16(P54_CONTROL_TYPE_LED); + p54_assign_address(dev, NULL, hdr, sizeof(*hdr) + sizeof(*led), NULL); + + led = (struct p54_tx_control_led *) hdr->data; + led->mode = cpu_to_le16(mode); + led->led_permanent = cpu_to_le16(link); + led->led_temporary = cpu_to_le16(act); + led->duration = cpu_to_le16(1000); + + priv->tx(dev, hdr, sizeof(*hdr) + sizeof(*led), 1); + + return 0; +} + +static int p54_add_interface(struct ieee80211_hw *dev, + struct ieee80211_if_init_conf *conf) +{ + struct p54_common *priv = dev->priv; + int err; + + /* NOTE: using IEEE80211_IF_TYPE_MGMT to indicate no mode selected */ + if (priv->mode != IEEE80211_IF_TYPE_MGMT) + return -1; + + switch (conf->type) { + case IEEE80211_IF_TYPE_STA: + priv->mode = conf->type; + break; + default: + return -EOPNOTSUPP; + } + + priv->mac_addr = conf->mac_addr; + + err = priv->open(dev); + if (err) { + priv->mode = IEEE80211_IF_TYPE_MGMT; + skb_queue_purge(&priv->tx_queue); + return err; + } + + p54_set_filter(dev, 0, priv->mac_addr, NULL, 0, 1, 0, 0xF642); + p54_set_filter(dev, 0, priv->mac_addr, NULL, 1, 0, 0, 0xF642); + + switch (conf->type) { + case IEEE80211_IF_TYPE_STA: + p54_set_filter(dev, 1, priv->mac_addr, NULL, 0, 0x15F, 0x1F4, 0); + break; + } + + p54_set_leds(dev, 1, 0, 0); + + return 0; +} + +static void p54_remove_interface(struct ieee80211_hw *dev, + struct ieee80211_if_init_conf *conf) +{ + struct p54_common *priv = dev->priv; + struct sk_buff *skb; + while ((skb = skb_dequeue(&priv->tx_queue))) { + struct memrecord *range = (struct memrecord *)&skb->cb; + if (range->control) + kfree(range->control); + kfree_skb(skb); + } + priv->mode = IEEE80211_IF_TYPE_MGMT; + priv->stop(dev); +} + +static int p54_config(struct ieee80211_hw *dev, struct ieee80211_conf *conf) +{ + p54_set_freq(dev, cpu_to_le16(conf->freq)); + return 0; +} + +static int p54_config_interface(struct ieee80211_hw *dev, int if_id, + struct ieee80211_if_conf *conf) +{ + struct p54_common *priv = dev->priv; + + p54_set_filter(dev, 0, priv->mac_addr, conf->bssid, 0, 1, 0, 0xF642); + p54_set_filter(dev, 0, priv->mac_addr, conf->bssid, 2, 0, 0, 0); + p54_set_leds(dev, 1, !is_multicast_ether_addr(conf->bssid), 0); + return 0; +} + +static int p54_get_stats(struct ieee80211_hw *dev, + struct ieee80211_low_level_stats *stats) +{ + /* TODO */ + return 0; +} + +static int p54_get_tx_stats(struct ieee80211_hw *dev, + struct ieee80211_tx_queue_stats *stats) +{ + /* TODO.. probably should let lower level deal with this */ + return 0; +} + +static const struct ieee80211_ops p54_ops = { + .tx = p54_tx, + .add_interface = p54_add_interface, + .remove_interface = p54_remove_interface, + .config = p54_config, + .config_interface = p54_config_interface, + .get_stats = p54_get_stats, + .get_tx_stats = p54_get_tx_stats +}; + +struct ieee80211_hw *p54_init_common(size_t priv_data_len) +{ + struct ieee80211_hw *dev; + struct p54_common *priv; + int i; + + dev = ieee80211_alloc_hw(priv_data_len, &p54_ops); + if (!dev) + return NULL; + + priv = dev->priv; + priv->mode = IEEE80211_IF_TYPE_MGMT; + skb_queue_head_init(&priv->tx_queue); + memcpy(priv->channels, p54_channels, sizeof(p54_channels)); + memcpy(priv->rates, p54_rates, sizeof(p54_rates)); + priv->modes[1].mode = MODE_IEEE80211B; + priv->modes[1].num_rates = 4; + priv->modes[1].rates = priv->rates; + priv->modes[1].num_channels = ARRAY_SIZE(p54_channels); + priv->modes[1].channels = priv->channels; + priv->modes[0].mode = MODE_IEEE80211G; + priv->modes[0].num_rates = ARRAY_SIZE(p54_rates); + priv->modes[0].rates = priv->rates; + priv->modes[0].num_channels = ARRAY_SIZE(p54_channels); + priv->modes[0].channels = priv->channels; + dev->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | /* not sure */ + IEEE80211_HW_RX_INCLUDES_FCS | + IEEE80211_HW_WEP_INCLUDE_IV | + IEEE80211_HW_DATA_NULLFUNC_ACK; /* TODO: check */ + /* IEEE80211_HW_MONITOR_DURING_OPER FIXME: check */ + dev->channel_change_time = 1000; /* TODO: find actual value */ + dev->max_rssi = 100; + + dev->queues = 1; + dev->extra_tx_headroom = sizeof(struct p54_control_hdr) + 4 + + sizeof(struct p54_tx_control_allocdata); + + for (i = 0; i < 2; i++) { + if (ieee80211_register_hwmode(dev, &priv->modes[i])) { + ieee80211_free_hw(dev); + return NULL; + } + } + + return dev; +} +EXPORT_SYMBOL_GPL(p54_init_common); + +void p54_free_common(struct ieee80211_hw *dev) +{ + struct p54_common *priv = dev->priv; + kfree(priv->iq_autocal); + kfree(priv->output_limit); + kfree(priv->curve_data); +} +EXPORT_SYMBOL_GPL(p54_free_common); + +static int __init p54_init(void) +{ + return 0; +} + +static void __exit p54_exit(void) +{ +} + +module_init(p54_init); +module_exit(p54_exit); diff --git a/drivers/net/wireless/mac80211/p54/prism54common.h b/drivers/net/wireless/mac80211/p54/prism54common.h new file mode 100644 index 0000000..7427cc2 --- /dev/null +++ b/drivers/net/wireless/mac80211/p54/prism54common.h @@ -0,0 +1,328 @@ +#ifndef PRISM54COMMON_H +#define PRISM54COMMON_H + +/* + * Common code specific definitions for mac80211 Prism54 drivers + * + * Copyright (c) 2006, Michael Wu + * + * Based on the islsm (softmac prism54) driver, which is: + * Copyright 2004-2006 Jean-Baptiste Note , et al. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +struct bootrec { + __le32 code; + __le32 len; + u32 data[]; +} __attribute__((packed)); + +struct bootrec_exp_if { + __le16 role; + __le16 if_id; + __le16 variant; + __le16 btm_compat; + __le16 top_compat; +} __attribute__((packed)); + +#define BR_CODE_MIN 0x80000000 +#define BR_CODE_COMPONENT_ID 0x80000001 +#define BR_CODE_COMPONENT_VERSION 0x80000002 +#define BR_CODE_DEPENDENT_IF 0x80000003 +#define BR_CODE_EXPOSED_IF 0x80000004 +#define BR_CODE_DESCR 0x80000101 +#define BR_CODE_MAX 0x8FFFFFFF +#define BR_CODE_END_OF_BRA 0xFF0000FF +#define LEGACY_BR_CODE_END_OF_BRA 0xFFFFFFFF + +#define FW_FMAC 0x464d4143 +#define FW_LM86 0x4c4d3836 +#define FW_LM87 0x4c4d3837 +#define FW_LM20 0x4c4d3230 + +/* PDA defines are Copyright (C) 2005 Nokia Corporation (taken from islsm_pda.h) */ + +struct pda_entry { + __le16 len; /* includes both code and data */ + __le16 code; + u8 data[0]; +} __attribute__ ((packed)); + +struct eeprom_pda_wrap { + u32 magic; + u16 pad; + u16 len; + u32 arm_opcode; + u8 data[0]; +} __attribute__ ((packed)); + +struct pda_iq_autocal_entry { + __le16 freq; + __le16 iq_param[4]; +} __attribute__ ((packed)); + +struct pda_channel_output_limit { + __le16 freq; + u8 val_bpsk; + u8 val_qpsk; + u8 val_16qam; + u8 val_64qam; + u8 rate_set_mask; + u8 rate_set_size; +} __attribute__ ((packed)); + +struct pda_pa_curve_data_sample_rev0 { + u8 rf_power; + u8 pa_detector; + u8 pcv; +} __attribute__ ((packed)); + +struct pda_pa_curve_data_sample_rev1 { + u8 rf_power; + u8 pa_detector; + u8 data_barker; + u8 data_bpsk; + u8 data_qpsk; + u8 data_16qam; + u8 data_64qam; + u8 padding; +} __attribute__ ((packed)); + +struct pda_pa_curve_data { + u8 cal_method_rev; + u8 channels; + u8 points_per_channel; + u8 padding; + u8 data[0]; +} __attribute__ ((packed)); + +/* + * this defines the PDR codes used to build PDAs as defined in document + * number 553155. The current implementation mirrors version 1.1 of the + * document and lists only PDRs supported by the ARM platform. + */ + +/* common and choice range (0x0000 - 0x0fff) */ +#define PDR_END 0x0000 +#define PDR_MANUFACTURING_PART_NUMBER 0x0001 +#define PDR_PDA_VERSION 0x0002 +#define PDR_NIC_SERIAL_NUMBER 0x0003 + +#define PDR_MAC_ADDRESS 0x0101 +#define PDR_REGULATORY_DOMAIN_LIST 0x0103 +#define PDR_TEMPERATURE_TYPE 0x0107 + +#define PDR_PRISM_PCI_IDENTIFIER 0x0402 + +/* ARM range (0x1000 - 0x1fff) */ +#define PDR_COUNTRY_INFORMATION 0x1000 +#define PDR_INTERFACE_LIST 0x1001 +#define PDR_HARDWARE_PLATFORM_COMPONENT_ID 0x1002 +#define PDR_OEM_NAME 0x1003 +#define PDR_PRODUCT_NAME 0x1004 +#define PDR_UTF8_OEM_NAME 0x1005 +#define PDR_UTF8_PRODUCT_NAME 0x1006 +#define PDR_COUNTRY_LIST 0x1007 +#define PDR_DEFAULT_COUNTRY 0x1008 + +#define PDR_ANTENNA_GAIN 0x1100 + +#define PDR_PRISM_INDIGO_PA_CALIBRATION_DATA 0x1901 +#define PDR_RSSI_LINEAR_APPROXIMATION 0x1902 +#define PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS 0x1903 +#define PDR_PRISM_PA_CAL_CURVE_DATA 0x1904 +#define PDR_RSSI_LINEAR_APPROXIMATION_DUAL_BAND 0x1905 +#define PDR_PRISM_ZIF_TX_IQ_CALIBRATION 0x1906 +#define PDR_REGULATORY_POWER_LIMITS 0x1907 +#define PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED 0x1908 +#define PDR_RADIATED_TRANSMISSION_CORRECTION 0x1909 +#define PDR_PRISM_TX_IQ_CALIBRATION 0x190a + +/* reserved range (0x2000 - 0x7fff) */ + +/* customer range (0x8000 - 0xffff) */ +#define PDR_BASEBAND_REGISTERS 0x8000 +#define PDR_PER_CHANNEL_BASEBAND_REGISTERS 0x8001 + +/* stored in skb->cb */ +struct memrecord { + u32 start_addr; + u32 end_addr; + struct ieee80211_tx_control *control; +}; + +struct p54_eeprom_lm86 { + __le16 offset; + __le16 len; + u8 data[0]; +} __attribute__ ((packed)); + +struct p54_rx_hdr { + __le16 magic; + __le16 len; + __le16 freq; + u8 antenna; + u8 rate; + u8 rssi; + u8 padding; + u16 unknown2; + __le64 timestamp; + u8 data[0]; +} __attribute__ ((packed)); + +struct p54_frame_sent_hdr { + u8 status; + u8 retries; + __le16 ack_rssi; + __le16 seq; + u16 rate; +} __attribute__ ((packed)); + +struct p54_tx_control_allocdata { + u8 rateset[8]; + u16 padding; + u8 wep_key_present; + u8 wep_key_len; + u8 wep_key[16]; + __le32 frame_type; + u32 padding2; + __le16 magic4; + u8 antenna; + u8 output_power; + __le32 magic5; + u8 align[0]; +} __attribute__ ((packed)); + +struct p54_tx_control_filter { + __le16 filter_type; + u8 dst[ETH_ALEN]; + u8 src[ETH_ALEN]; + u8 antenna; + u8 debug; + __le32 magic3; + u8 rates[8]; // FIXME: what's this for? + __le32 rx_addr; + __le16 max_rx; + __le16 rxhw; + __le16 magic8; + __le16 magic9; +} __attribute__ ((packed)); + +struct p54_tx_control_channel { + __le16 magic1; + __le16 magic2; + u8 padding1[20]; + struct pda_iq_autocal_entry iq_autocal; + u8 pa_points_per_curve; + u8 val_barker; + u8 val_bpsk; + u8 val_qpsk; + u8 val_16qam; + u8 val_64qam; + struct pda_pa_curve_data_sample_rev1 curve_data[0]; + /* additional padding/data after curve_data */ +} __attribute__ ((packed)); + +struct p54_tx_control_led { + __le16 mode; + __le16 led_temporary; + __le16 led_permanent; + __le16 duration; +} __attribute__ ((packed)); + +struct p54_tx_vdcf_queues { + __le16 aifs; + __le16 cwmin; + __le16 cwmax; + __le16 txop; +} __attribute__ ((packed)); + +struct p54_tx_control_vdcf { + u8 padding; + u8 slottime; + u8 magic1; + u8 magic2; + struct p54_tx_vdcf_queues queue[8]; + u8 pad2[4]; + __le16 frameburst; +} __attribute__ ((packed)); + +static const struct ieee80211_rate p54_rates[] = { + { .rate = 10, + .val = 0, + .val2 = 0x10, + .flags = IEEE80211_RATE_CCK_2 }, + { .rate = 20, + .val = 1, + .val2 = 0x11, + .flags = IEEE80211_RATE_CCK_2 }, + { .rate = 55, + .val = 2, + .val2 = 0x12, + .flags = IEEE80211_RATE_CCK_2 }, + { .rate = 110, + .val = 3, + .val2 = 0x13, + .flags = IEEE80211_RATE_CCK_2 }, + { .rate = 60, + .val = 4, + .flags = IEEE80211_RATE_OFDM }, + { .rate = 90, + .val = 5, + .flags = IEEE80211_RATE_OFDM }, + { .rate = 120, + .val = 6, + .flags = IEEE80211_RATE_OFDM }, + { .rate = 180, + .val = 7, + .flags = IEEE80211_RATE_OFDM }, + { .rate = 240, + .val = 8, + .flags = IEEE80211_RATE_OFDM }, + { .rate = 360, + .val = 9, + .flags = IEEE80211_RATE_OFDM }, + { .rate = 480, + .val = 10, + .flags = IEEE80211_RATE_OFDM }, + { .rate = 540, + .val = 11, + .flags = IEEE80211_RATE_OFDM }, +}; + +// TODO: just generate this.. +static const struct ieee80211_channel p54_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} +}; + +#endif /* PRISM54COMMON_H */ diff --git a/drivers/net/wireless/mac80211/p54/prism54magic.h b/drivers/net/wireless/mac80211/p54/prism54magic.h new file mode 100644 index 0000000..839e9d7 --- /dev/null +++ b/drivers/net/wireless/mac80211/p54/prism54magic.h @@ -0,0 +1,77 @@ +#ifndef PRISM54MAGIC_H +#define PRISM54MAGIC_H + +/* + * Magic packets for softmac Prism54 hardware + * + * Copyright (c) 2006, Michael Wu + * + * Based on the islsm (softmac prism54) driver, which is: + * Copyright 2004-2006 Jean-Baptiste Note , et al. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* usb version 1 packet */ +static const char p54u_net2280_magic_packet[86] = { + 0x01, 0x80, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, + 0x00, 0x14, 0x0a, 0x06, + 0x02, 0x00, 0x1f, 0x00, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x1f, + 0x00, + 0xff, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x1f, 0x00, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x1f, + 0x00, + 0xff, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x1f, 0x00, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x1f, + 0x00, + 0xff, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x1f, 0x00, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x1f, + 0x00, + 0xff, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* usb version 2 packet */ +static const char p54u_3887_magic_packet[86] = { + 0x01, 0x80, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, + 0x00, 0x14, 0x0a, 0x06, + 0x02, 0x00, 0x03, 0x00, 0x07, 0x00, 0x5e, 0x00, 0x02, 0x00, 0x07, + 0x00, + 0x0f, 0x00, 0x2f, 0x00, + 0x03, 0x00, 0x0f, 0x00, 0xff, 0x03, 0x2b, 0x00, 0x07, 0x00, 0x0f, + 0x00, + 0xff, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x78, 0x05 +}; + +/* pci packet. 5a values are "don't care" values. */ +static const char p54p_magic_packet[88] = { + 0x01, 0x80, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, + 0x00, 0x14, 0x0a, 0x06, + 0x02, 0x5a, 0x03, 0x00, 0x07, 0x00, 0x2f, 0x00, 0x02, 0x5a, 0x07, + 0x00, + 0x0f, 0x00, 0x5e, 0x00, + 0x03, 0x5a, 0x0f, 0x00, 0xff, 0x03, 0x00, 0x00, 0x07, 0x5a, 0x0f, + 0x00, + 0xff, 0x03, 0x00, 0x00, + 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, + 0x5a, + 0x5a, 0x5a, 0x5a, 0x5a, + 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, + 0x5a, + 0x5a, 0x5a, 0x5a, 0x5a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +#endif /* PRISM54MAGIC_H */ diff --git a/drivers/net/wireless/mac80211/p54/prism54pci.c b/drivers/net/wireless/mac80211/p54/prism54pci.c new file mode 100644 index 0000000..0db7026 --- /dev/null +++ b/drivers/net/wireless/mac80211/p54/prism54pci.c @@ -0,0 +1,700 @@ + +/* + * Linux device driver for PCI based Prism54 + * + * Copyright (c) 2006, Michael Wu + * + * Based on the islsm (softmac prism54) driver, which is: + * Copyright 2004-2006 Jean-Baptiste Note , et al. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "prism54.h" +#include "prism54pci.h" +#include "prism54magic.h" + +MODULE_AUTHOR("Michael Wu "); +MODULE_DESCRIPTION("Prism54 PCI wireless driver"); +MODULE_LICENSE("GPL"); + +static struct pci_device_id p54p_table[] __devinitdata = { + /* Intersil PRISM Duette/Prism GT Wireless LAN adapter */ + { PCI_DEVICE(0x1260, 0x3890) }, + /* 3COM 3CRWE154G72 Wireless LAN adapter */ + { PCI_DEVICE(0x10b7, 0x6001) }, + /* Intersil PRISM Indigo Wireless LAN adapter */ + { PCI_DEVICE(0x1260, 0x3877) }, + /* Intersil PRISM Javelin/Xbow Wireless LAN adapter */ + { PCI_DEVICE(0x1260, 0x3886) }, +}; + +MODULE_DEVICE_TABLE(pci, p54p_table); + +static int p54p_upload_firmware(struct ieee80211_hw *dev) +{ + struct p54p_priv *priv = dev->priv; + const struct firmware *fw_entry = NULL; + __le32 reg; + int err; + u32 *data; + u32 remains, left, device_addr; + + P54P_WRITE(int_enable, 0); + P54P_READ(int_enable); + udelay(10); + + reg = P54P_READ(ctrl_stat); + reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); + reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RAMBOOT); + P54P_WRITE(ctrl_stat, reg); + P54P_READ(ctrl_stat); + udelay(10); + + reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET); + P54P_WRITE(ctrl_stat, reg); + wmb(); + udelay(10); + + reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); + P54P_WRITE(ctrl_stat, reg); + wmb(); + + mdelay(50); + + err = request_firmware(&fw_entry, "isl3886", &priv->pdev->dev); + if (err) { + printk(KERN_ERR "%s (prism54pci): cannot find firmware " + "(isl3886)\n", pci_name(priv->pdev)); + return err; + } + + p54_parse_firmware(dev, fw_entry); + + data = (u32 *) fw_entry->data; + remains = fw_entry->size; + device_addr = ISL38XX_DEV_FIRMWARE_ADDR; + while (remains) { + u32 i = 0; + left = min((u32)0x1000, remains); + P54P_WRITE(direct_mem_base, cpu_to_le32(device_addr)); + P54P_READ(int_enable); + + device_addr += 0x1000; + while (i < left) { + P54P_WRITE(direct_mem_win[i], *data++); + i += sizeof(u32); + } + + remains -= left; + P54P_READ(int_enable); + } + + release_firmware(fw_entry); + + reg = P54P_READ(ctrl_stat); + reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_CLKRUN); + reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); + reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RAMBOOT); + P54P_WRITE(ctrl_stat, reg); + P54P_READ(ctrl_stat); + udelay(10); + + reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET); + P54P_WRITE(ctrl_stat, reg); + wmb(); + udelay(10); + + reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); + P54P_WRITE(ctrl_stat, reg); + wmb(); + udelay(10); + + return 0; +} + +static irqreturn_t p54p_simple_interrupt(int irq, void *dev_id) +{ + struct p54p_priv *priv = (struct p54p_priv *) dev_id; + __le32 reg; + + reg = P54P_READ(int_ident); + P54P_WRITE(int_ack, reg); + + if (reg & P54P_READ(int_enable)) + complete(&priv->boot_comp); + + return IRQ_HANDLED; +} + +static int p54p_read_eeprom(struct ieee80211_hw *dev) +{ + struct p54p_priv *priv = dev->priv; + int err; + struct p54_control_hdr *hdr; + void *eeprom; + dma_addr_t rx_mapping, tx_mapping; + u16 alen; + + init_completion(&priv->boot_comp); + err = request_irq(priv->pdev->irq, &p54p_simple_interrupt, + IRQF_SHARED, "prism54pci", priv); + if (err) { + printk(KERN_ERR "%s (prism54pci): failed to register IRQ handler\n", + pci_name(priv->pdev)); + return err; + } + + eeprom = kmalloc(0x2010 + EEPROM_READBACK_LEN, GFP_KERNEL); + if (!eeprom) { + printk(KERN_ERR "%s (prism54pci): no memory for eeprom!\n", + pci_name(priv->pdev)); + err = -ENOMEM; + goto out; + } + + memset(priv->ring_control, 0, sizeof(*priv->ring_control)); + P54P_WRITE(ring_control_base, priv->ring_control_dma); + P54P_READ(ring_control_base); + udelay(10); + + P54P_WRITE(int_enable, cpu_to_le32(ISL38XX_INT_IDENT_INIT)); + P54P_READ(int_enable); + udelay(10); + + P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_RESET)); + + if (!wait_for_completion_interruptible_timeout(&priv->boot_comp, HZ)) { + printk(KERN_ERR "%s (prism54pci): Cannot boot firmware!\n", + pci_name(priv->pdev)); + err = -EINVAL; + goto out; + } + + P54P_WRITE(int_enable, cpu_to_le32(ISL38XX_INT_IDENT_UPDATE)); + P54P_READ(int_enable); + + hdr = eeprom + 0x2010; + p54_fill_eeprom_readback(hdr); + hdr->req_id = cpu_to_le32(priv->common.rx_start); + + rx_mapping = pci_map_single(priv->pdev, eeprom, + 0x2010, PCI_DMA_FROMDEVICE); + tx_mapping = pci_map_single(priv->pdev, (void *)hdr, + EEPROM_READBACK_LEN, PCI_DMA_TODEVICE); + + priv->ring_control->rx_mgmt[0].host_addr = cpu_to_le32(rx_mapping); + priv->ring_control->rx_mgmt[0].len = cpu_to_le16(0x2010); + priv->ring_control->tx_data[0].host_addr = cpu_to_le32(tx_mapping); + priv->ring_control->tx_data[0].device_addr = hdr->req_id; + priv->ring_control->tx_data[0].len = cpu_to_le16(EEPROM_READBACK_LEN); + + priv->ring_control->host_idx[2] = cpu_to_le32(1); + priv->ring_control->host_idx[1] = cpu_to_le32(1); + + wmb(); + mdelay(100); + P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_UPDATE)); + + wait_for_completion_interruptible_timeout(&priv->boot_comp, HZ); + wait_for_completion_interruptible_timeout(&priv->boot_comp, HZ); + + pci_unmap_single(priv->pdev, tx_mapping, + EEPROM_READBACK_LEN, PCI_DMA_TODEVICE); + pci_unmap_single(priv->pdev, rx_mapping, + 0x2010, PCI_DMA_FROMDEVICE); + + alen = le16_to_cpu(priv->ring_control->rx_mgmt[0].len); + if (le32_to_cpu(priv->ring_control->device_idx[2]) != 1 || + alen < 0x10) { + printk(KERN_ERR "%s (prism54pci): Cannot read eeprom!\n", + pci_name(priv->pdev)); + err = -EINVAL; + goto out; + } + + p54_parse_eeprom(dev, (u8 *)eeprom + 0x10, alen - 0x10); + + out: + kfree(eeprom); + P54P_WRITE(int_enable, 0); + P54P_READ(int_enable); + udelay(10); + free_irq(priv->pdev->irq, priv); + P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_RESET)); + return err; +} + +static void p54p_refill_rx_ring(struct ieee80211_hw *dev) +{ + struct p54p_priv *priv = dev->priv; + u32 limit, host_idx, idx; + + host_idx = le32_to_cpu(priv->ring_control->host_idx[0]); + limit = host_idx; + limit -= le32_to_cpu(priv->ring_control->device_idx[0]); + limit = ARRAY_SIZE(priv->ring_control->rx_data) - limit; + + idx = host_idx % ARRAY_SIZE(priv->ring_control->rx_data); + while (limit-- > 1) { + struct p54p_desc *desc = &priv->ring_control->rx_data[idx]; + + if (!desc->host_addr) { + struct sk_buff *skb; + dma_addr_t mapping; + skb = dev_alloc_skb(MAX_RX_SIZE); + if (!skb) + break; + + mapping = pci_map_single(priv->pdev, skb->tail, + MAX_RX_SIZE, + PCI_DMA_FROMDEVICE); + desc->host_addr = cpu_to_le32(mapping); + desc->device_addr = 0; // FIXME: necessary? + desc->len = cpu_to_le16(MAX_RX_SIZE); + desc->flags = 0; + priv->rx_buf[idx] = skb; + } + + idx++; + host_idx++; + idx %= ARRAY_SIZE(priv->ring_control->rx_data); + } + + wmb(); + priv->ring_control->host_idx[0] = cpu_to_le32(host_idx); +} + +static irqreturn_t p54p_interrupt(int irq, void *dev_id) +{ + struct ieee80211_hw *dev = dev_id; + struct p54p_priv *priv = dev->priv; + __le32 reg; + + spin_lock(&priv->lock); + reg = P54P_READ(int_ident); + if (unlikely(reg == 0xFFFFFFFF)) { + spin_unlock(&priv->lock); + return IRQ_HANDLED; + } + + P54P_WRITE(int_ack, reg); + + reg &= P54P_READ(int_enable); + + if (reg & cpu_to_le32(ISL38XX_INT_IDENT_UPDATE)) { + struct p54p_desc *desc; + u32 idx, i; + i = priv->tx_idx; + i %= ARRAY_SIZE(priv->ring_control->tx_data); + priv->tx_idx = idx = le32_to_cpu(priv->ring_control->device_idx[1]); + idx %= ARRAY_SIZE(priv->ring_control->tx_data); + + while (i != idx) { + desc = &priv->ring_control->tx_data[i]; + if (priv->tx_buf[i]) { + kfree(priv->tx_buf[i]); + priv->tx_buf[i] = NULL; + } + + pci_unmap_single(priv->pdev, le32_to_cpu(desc->host_addr), + le16_to_cpu(desc->len), PCI_DMA_TODEVICE); + + desc->host_addr = 0; + desc->device_addr = 0; + desc->len = 0; + desc->flags = 0; + + i++; + i %= ARRAY_SIZE(priv->ring_control->tx_data); + } + + i = priv->rx_idx; + i %= ARRAY_SIZE(priv->ring_control->rx_data); + priv->rx_idx = idx = le32_to_cpu(priv->ring_control->device_idx[0]); + idx %= ARRAY_SIZE(priv->ring_control->rx_data); + while (i != idx) { + u16 len; + struct sk_buff *skb; + desc = &priv->ring_control->rx_data[i]; + len = le16_to_cpu(desc->len); + skb = priv->rx_buf[i]; + + skb_put(skb, len); + + if (p54_rx(dev, skb)) { + pci_unmap_single(priv->pdev, + le32_to_cpu(desc->host_addr), + MAX_RX_SIZE, PCI_DMA_FROMDEVICE); + + priv->rx_buf[i] = NULL; + desc->host_addr = 0; + } else { + skb_trim(skb, 0); + desc->len = cpu_to_le16(MAX_RX_SIZE); + } + + i++; + i %= ARRAY_SIZE(priv->ring_control->rx_data); + } + + p54p_refill_rx_ring(dev); + + wmb(); + P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_UPDATE)); + } else if (reg & cpu_to_le32(ISL38XX_INT_IDENT_INIT)) + complete(&priv->boot_comp); + + spin_unlock(&priv->lock); + + return reg ? IRQ_HANDLED : IRQ_NONE; +} + +static void p54p_tx(struct ieee80211_hw *dev, struct p54_control_hdr *data, + size_t len, int free_on_tx) +{ + struct p54p_priv *priv = dev->priv; + unsigned long flags; + struct p54p_desc *desc; + dma_addr_t mapping; + u32 device_idx, idx, i; + + spin_lock_irqsave(&priv->lock, flags); + + device_idx = le32_to_cpu(priv->ring_control->device_idx[1]); + idx = le32_to_cpu(priv->ring_control->host_idx[1]); + i = idx % ARRAY_SIZE(priv->ring_control->tx_data); + + mapping = pci_map_single(priv->pdev, data, len, PCI_DMA_TODEVICE); + desc = &priv->ring_control->tx_data[i]; + desc->host_addr = cpu_to_le32(mapping); + desc->device_addr = data->req_id; + desc->len = cpu_to_le16(len); + desc->flags = 0; + + wmb(); + priv->ring_control->host_idx[1] = cpu_to_le32(idx + 1); + + if (free_on_tx) + priv->tx_buf[i] = data; + + spin_unlock_irqrestore(&priv->lock, flags); + + P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_UPDATE)); + P54P_READ(dev_int); + + /* FIXME: unlikely to happen because the device usually runs out of + memory before we fill the ring up, but we can make it impossible */ + if (idx - device_idx > ARRAY_SIZE(priv->ring_control->tx_data) - 2) + printk(KERN_INFO "%s: tx overflow.\n", wiphy_name(dev->wiphy)); +} + +static int p54p_open(struct ieee80211_hw *dev) +{ + struct p54p_priv *priv = dev->priv; + struct p54_control_hdr *startup_packet; + int err; + + startup_packet = kmalloc(sizeof(p54p_magic_packet), GFP_KERNEL); + if (!startup_packet) + return -ENOMEM; + + init_completion(&priv->boot_comp); + err = request_irq(priv->pdev->irq, &p54p_interrupt, + IRQF_SHARED, "prism54pci", dev); + if (err) { + printk(KERN_ERR "%s: failed to register IRQ handler\n", + wiphy_name(dev->wiphy)); + kfree(startup_packet); + return err; + } + + memset(priv->ring_control, 0, sizeof(*priv->ring_control)); + priv->rx_idx = priv->tx_idx = 0; + p54p_refill_rx_ring(dev); + + p54p_upload_firmware(dev); + + P54P_WRITE(ring_control_base, priv->ring_control_dma); + P54P_READ(ring_control_base); + wmb(); + udelay(10); + + P54P_WRITE(int_enable, cpu_to_le32(ISL38XX_INT_IDENT_INIT)); + P54P_READ(int_enable); + wmb(); + udelay(10); + + P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_RESET)); + P54P_READ(dev_int); + + if (!wait_for_completion_interruptible_timeout(&priv->boot_comp, HZ)) { + printk(KERN_ERR "%s: Cannot boot firmware!\n", + wiphy_name(dev->wiphy)); + kfree(startup_packet); + free_irq(priv->pdev->irq, dev); + return -ETIMEDOUT; + } + + P54P_WRITE(int_enable, cpu_to_le32(ISL38XX_INT_IDENT_UPDATE)); + P54P_READ(int_enable); + wmb(); + udelay(10); + + P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_UPDATE)); + P54P_READ(dev_int); + wmb(); + udelay(10); + + memcpy(startup_packet, p54p_magic_packet, sizeof(p54p_magic_packet)); + startup_packet->req_id = cpu_to_le32(priv->common.rx_start); + + p54p_tx(dev, startup_packet, sizeof(p54p_magic_packet), 1); + + return 0; +} + +static void p54p_stop(struct ieee80211_hw *dev) +{ + struct p54p_priv *priv = dev->priv; + unsigned int i; + struct p54p_desc *desc; + + P54P_WRITE(int_enable, 0); + P54P_READ(int_enable); + udelay(10); + + free_irq(priv->pdev->irq, dev); + + P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_RESET)); + + for (i = 0; i < ARRAY_SIZE(priv->rx_buf); i++) { + desc = &priv->ring_control->rx_data[i]; + if (desc->host_addr) + pci_unmap_single(priv->pdev, le32_to_cpu(desc->host_addr), + MAX_RX_SIZE, PCI_DMA_FROMDEVICE); + kfree_skb(priv->rx_buf[i]); + priv->rx_buf[i] = NULL; + } + + for (i = 0; i < ARRAY_SIZE(priv->tx_buf); i++) { + desc = &priv->ring_control->tx_data[i]; + if (desc->host_addr) + pci_unmap_single(priv->pdev, le32_to_cpu(desc->host_addr), + le16_to_cpu(desc->len), PCI_DMA_TODEVICE); + + kfree(priv->tx_buf[i]); + priv->tx_buf[i] = NULL; + } + + memset(priv->ring_control, 0, sizeof(*priv->ring_control)); +} + +static int __devinit p54p_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct p54p_priv *priv; + struct ieee80211_hw *dev; + unsigned long mem_addr, mem_len; + int err; + + err = pci_enable_device(pdev); + if (err) { + printk(KERN_ERR "%s (prism54pci): Cannot enable new PCI device\n", + pci_name(pdev)); + return err; + } + + mem_addr = pci_resource_start(pdev, 0); + mem_len = pci_resource_len(pdev, 0); + if (mem_len < sizeof(struct p54p_csr)) { + printk(KERN_ERR "%s (prism54pci): Too short PCI resources\n", + pci_name(pdev)); + pci_disable_device(pdev); + return err; + } + + err = pci_request_regions(pdev, "prism54pci"); + if (err) { + printk(KERN_ERR "%s (prism54pci): Cannot obtain PCI resources\n", + pci_name(pdev)); + return err; + } + + if (pci_set_dma_mask(pdev, DMA_32BIT_MASK) || + pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK)) { + printk(KERN_ERR "%s (prism54pci): No suitable DMA available\n", + pci_name(pdev)); + goto err_free_reg; + } + + pci_set_master(pdev); + pci_set_mwi(pdev); + + pci_write_config_byte(pdev, 0x40, 0); + pci_write_config_byte(pdev, 0x41, 0); + + dev = p54_init_common(sizeof(*priv)); + if (!dev) { + printk(KERN_ERR "%s (prism54pci): ieee80211 alloc failed\n", + pci_name(pdev)); + err = -ENOMEM; + goto err_free_reg; + } + + priv = dev->priv; + priv->pdev = pdev; + + SET_IEEE80211_DEV(dev, &pdev->dev); + pci_set_drvdata(pdev, dev); + + priv->map = ioremap(mem_addr, mem_len); + if (!priv->map) { + printk(KERN_ERR "%s (prism54pci): Cannot map device memory\n", + pci_name(pdev)); + err = -EINVAL; // TODO: use a better error code? + goto err_free_dev; + } + + priv->ring_control = pci_alloc_consistent(pdev, sizeof(*priv->ring_control), + &priv->ring_control_dma); + if (!priv->ring_control) { + printk(KERN_ERR "%s (prism54pci): Cannot allocate rings\n", + pci_name(pdev)); + err = -ENOMEM; + goto err_iounmap; + } + memset(priv->ring_control, 0, sizeof(*priv->ring_control)); + + err = p54p_upload_firmware(dev); + if (err) + goto err_free_desc; + + err = p54p_read_eeprom(dev); + if (err) + goto err_free_desc; + + priv->common.open = p54p_open; + priv->common.stop = p54p_stop; + priv->common.tx = p54p_tx; + + spin_lock_init(&priv->lock); + + err = ieee80211_register_hw(dev); + if (err) { + printk(KERN_ERR "%s (prism54pci): Cannot register netdevice\n", + pci_name(pdev)); + goto err_free_common; + } + + printk(KERN_INFO "%s: hwaddr " MAC_FMT ", isl38%02x\n", + wiphy_name(dev->wiphy), MAC_ARG(dev->wiphy->perm_addr), + priv->common.version); + + return 0; + + err_free_common: + p54_free_common(dev); + + err_free_desc: + pci_free_consistent(pdev, sizeof(*priv->ring_control), + priv->ring_control, priv->ring_control_dma); + + err_iounmap: + iounmap(priv->map); + + err_free_dev: + pci_set_drvdata(pdev, NULL); + ieee80211_free_hw(dev); + + err_free_reg: + pci_release_regions(pdev); + pci_disable_device(pdev); + return err; +} + +static void __devexit p54p_remove(struct pci_dev *pdev) +{ + struct ieee80211_hw *dev = pci_get_drvdata(pdev); + struct p54p_priv *priv; + + if (!dev) + return; + + ieee80211_unregister_hw(dev); + priv = dev->priv; + pci_free_consistent(pdev, sizeof(*priv->ring_control), + priv->ring_control, priv->ring_control_dma); + p54_free_common(dev); + iounmap(priv->map); + pci_release_regions(pdev); + pci_disable_device(pdev); + ieee80211_free_hw(dev); +} + +#ifdef CONFIG_PM +static int p54p_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct ieee80211_hw *dev = pci_get_drvdata(pdev); + struct p54p_priv *priv = dev->priv; + + if (priv->common.mode != IEEE80211_IF_TYPE_MGMT) { + ieee80211_stop_queues(dev); + p54p_stop(dev); + } + + pci_save_state(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + return 0; +} + +static int p54p_resume(struct pci_dev *pdev) +{ + struct ieee80211_hw *dev = pci_get_drvdata(pdev); + struct p54p_priv *priv = dev->priv; + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + + if (priv->common.mode != IEEE80211_IF_TYPE_MGMT) { + p54p_open(dev); + ieee80211_start_queues(dev); + } + + return 0; +} +#endif /* CONFIG_PM */ + +static struct pci_driver p54p_driver = { + .name = "prism54pci", + .id_table = p54p_table, + .probe = p54p_probe, + .remove = __devexit_p(p54p_remove), +#ifdef CONFIG_PM + .suspend = p54p_suspend, + .resume = p54p_resume, +#endif /* CONFIG_PM */ +}; + +static int __init p54p_init(void) +{ + return pci_register_driver(&p54p_driver); +} + +static void __exit p54p_exit(void) +{ + pci_unregister_driver(&p54p_driver); +} + +module_init(p54p_init); +module_exit(p54p_exit); diff --git a/drivers/net/wireless/mac80211/p54/prism54pci.h b/drivers/net/wireless/mac80211/p54/prism54pci.h new file mode 100644 index 0000000..52feb59 --- /dev/null +++ b/drivers/net/wireless/mac80211/p54/prism54pci.h @@ -0,0 +1,106 @@ +#ifndef PRISM54PCI_H +#define PRISM54PCI_H + +/* + * Defines for PCI based mac80211 Prism54 driver + * + * Copyright (c) 2006, Michael Wu + * + * Based on the islsm (softmac prism54) driver, which is: + * Copyright 2004-2006 Jean-Baptiste Note , et al. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* Device Interrupt register bits */ +#define ISL38XX_DEV_INT_RESET 0x0001 +#define ISL38XX_DEV_INT_UPDATE 0x0002 +#define ISL38XX_DEV_INT_WAKEUP 0x0008 +#define ISL38XX_DEV_INT_SLEEP 0x0010 +#define ISL38XX_DEV_INT_ABORT 0x0020 +/* these two only used in USB */ +#define ISL38XX_DEV_INT_DATA 0x0040 +#define ISL38XX_DEV_INT_MGMT 0x0080 + +#define ISL38XX_DEV_INT_PCIUART_CTS 0x4000 +#define ISL38XX_DEV_INT_PCIUART_DR 0x8000 + +/* Interrupt Identification/Acknowledge/Enable register bits */ +#define ISL38XX_INT_IDENT_UPDATE 0x0002 +#define ISL38XX_INT_IDENT_INIT 0x0004 +#define ISL38XX_INT_IDENT_WAKEUP 0x0008 +#define ISL38XX_INT_IDENT_SLEEP 0x0010 +#define ISL38XX_INT_IDENT_PCIUART_CTS 0x4000 +#define ISL38XX_INT_IDENT_PCIUART_DR 0x8000 + +/* Control/Status register bits */ +#define ISL38XX_CTRL_STAT_SLEEPMODE 0x00000200 +#define ISL38XX_CTRL_STAT_CLKRUN 0x00800000 +#define ISL38XX_CTRL_STAT_RESET 0x10000000 +#define ISL38XX_CTRL_STAT_RAMBOOT 0x20000000 +#define ISL38XX_CTRL_STAT_STARTHALTED 0x40000000 +#define ISL38XX_CTRL_STAT_HOST_OVERRIDE 0x80000000 + +struct p54p_csr { + __le32 dev_int; + u8 unused_1[12]; + __le32 int_ident; + __le32 int_ack; + __le32 int_enable; + u8 unused_2[4]; + union { + __le32 ring_control_base; + __le32 gen_purp_com[2]; + }; + u8 unused_3[8]; + __le32 direct_mem_base; + u8 unused_4[44]; + __le32 dma_addr; + __le32 dma_len; + __le32 dma_ctrl; + u8 unused_5[12]; + __le32 ctrl_stat; + u8 unused_6[1924]; + u8 cardbus_cis[0x800]; + u8 direct_mem_win[0x1000]; +} __attribute__ ((packed)); + +/* usb backend only needs the register defines above */ +#ifndef PRISM54USB_H +struct p54p_desc { + __le32 host_addr; + __le32 device_addr; + __le16 len; + __le16 flags; +} __attribute__ ((packed)); + +struct p54p_ring_control { + __le32 host_idx[4]; + __le32 device_idx[4]; + struct p54p_desc rx_data[8]; + struct p54p_desc tx_data[32]; + struct p54p_desc rx_mgmt[4]; + struct p54p_desc tx_mgmt[4]; +} __attribute__ ((packed)); + +#define P54P_READ(r) __raw_readl(&priv->map->r) +#define P54P_WRITE(r, val) __raw_writel((__force u32)(val), &priv->map->r) + +struct p54p_priv { + struct p54_common common; + struct pci_dev *pdev; + struct p54p_csr __iomem *map; + + spinlock_t lock; + struct p54p_ring_control *ring_control; + dma_addr_t ring_control_dma; + u32 rx_idx, tx_idx; + struct sk_buff *rx_buf[8]; + void *tx_buf[32]; + struct completion boot_comp; +}; + +#endif /* PRISM54USB_H */ +#endif /* PRISM54PCI_H */ diff --git a/drivers/net/wireless/mac80211/p54/prism54usb.c b/drivers/net/wireless/mac80211/p54/prism54usb.c new file mode 100644 index 0000000..d52131c --- /dev/null +++ b/drivers/net/wireless/mac80211/p54/prism54usb.c @@ -0,0 +1,946 @@ + +/* + * Linux device driver for USB based Prism54 + * + * Copyright (c) 2006, Michael Wu + * + * Based on the islsm (softmac prism54) driver, which is: + * Copyright 2004-2006 Jean-Baptiste Note , et al. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "prism54.h" +#include "prism54usb.h" +#include "prism54magic.h" + +MODULE_AUTHOR("Michael Wu "); +MODULE_DESCRIPTION("Prism54 USB wireless driver"); +MODULE_LICENSE("GPL"); + +static struct usb_device_id p54u_table[] __devinitdata = { + /* Version 1 devices (pci chip + net2280) */ + {USB_DEVICE(0x0506, 0x0a11)}, /* 3COM 3CRWE254G72 */ + {USB_DEVICE(0x0707, 0xee06)}, /* SMC 2862W-G */ + {USB_DEVICE(0x083a, 0x4501)}, /* Accton 802.11g WN4501 USB */ + {USB_DEVICE(0x083a, 0x4502)}, /* Siemens Gigaset USB Adapter */ + {USB_DEVICE(0x0846, 0x4200)}, /* Netgear WG121 */ + {USB_DEVICE(0x0846, 0x4210)}, /* Netgear WG121 the second ? */ + {USB_DEVICE(0x0846, 0x4220)}, /* Netgear WG111 */ + {USB_DEVICE(0x0cde, 0x0006)}, /* Medion 40900, Roper Europe */ + {USB_DEVICE(0x124a, 0x4023)}, /* Shuttle PN15, Airvast WM168g, IOGear GWU513 */ + {USB_DEVICE(0x1915, 0x2234)}, /* Linksys WUSB54G OEM */ + {USB_DEVICE(0x1915, 0x2235)}, /* Linksys WUSB54G Portable OEM */ + {USB_DEVICE(0x2001, 0x3701)}, /* DLink DWL-G120 Spinnaker */ + {USB_DEVICE(0x2001, 0x3703)}, /* DLink DWL-G122 */ + {USB_DEVICE(0x5041, 0x2234)}, /* Linksys WUSB54G */ + {USB_DEVICE(0x5041, 0x2235)}, /* Linksys WUSB54G Portable */ + + /* Version 2 devices (3887) */ + {USB_DEVICE(0x050d, 0x7050)}, /* Belkin F5D7050 ver 1000 */ + {USB_DEVICE(0x0572, 0x2000)}, /* Cohiba Proto board */ + {USB_DEVICE(0x0572, 0x2002)}, /* Cohiba Proto board */ + {USB_DEVICE(0x0707, 0xee13)}, /* SMC 2862W-G version 2 */ + {USB_DEVICE(0x083a, 0x4521)}, /* Siemens Gigaset USB Adapter 54 version 2 */ + {USB_DEVICE(0x0846, 0x4240)}, /* Netgear WG111 (v2) */ + {USB_DEVICE(0x0915, 0x2000)}, /* Cohiba Proto board */ + {USB_DEVICE(0x0915, 0x2002)}, /* Cohiba Proto board */ + {USB_DEVICE(0x0baf, 0x0118)}, /* U.S. Robotics U5 802.11g Adapter*/ + {USB_DEVICE(0x0bf8, 0x1009)}, /* FUJITSU E-5400 USB D1700*/ + {USB_DEVICE(0x0cde, 0x0006)}, /* Medion MD40900 */ + {USB_DEVICE(0x0cde, 0x0008)}, /* Sagem XG703A */ + {USB_DEVICE(0x0d8e, 0x3762)}, /* DLink DWL-G120 Cohiba */ + {USB_DEVICE(0x09aa, 0x1000)}, /* Spinnaker Proto board */ + {USB_DEVICE(0x1435, 0x0427)}, /* Inventel UR054G */ + {USB_DEVICE(0x2001, 0x3704)}, /* DLink DWL-G122 rev A2 */ + {USB_DEVICE(0x413c, 0x8102)}, /* Spinnaker DUT */ + {USB_DEVICE(0x413c, 0x8104)}, /* Cohiba Proto board */ + {} +}; + +MODULE_DEVICE_TABLE(usb, p54u_table); + +static void p54u_rx_cb(struct urb *urb) +{ + struct sk_buff *skb = (struct sk_buff *) urb->context; + struct p54u_rx_info *info = (struct p54u_rx_info *)skb->cb; + struct ieee80211_hw *dev = info->dev; + struct p54u_priv *priv = dev->priv; + + if (unlikely(urb->status)) { + info->urb = NULL; + usb_free_urb(urb); + return; + } + + skb_unlink(skb, &priv->rx_queue); + skb_put(skb, urb->actual_length); + if (!priv->hw_type) + skb_pull(skb, sizeof(struct net2280_tx_hdr)); + + if (p54_rx(dev, skb)) { + skb = dev_alloc_skb(MAX_RX_SIZE); + if (unlikely(!skb)) { + usb_free_urb(urb); + /* TODO check rx queue length and refill *somewhere* */ + return; + } + + info = (struct p54u_rx_info *) skb->cb; + info->urb = urb; + info->dev = dev; + urb->transfer_buffer = skb->tail; + urb->context = skb; + skb_queue_tail(&priv->rx_queue, skb); + } else { + skb_trim(skb, 0); + skb_queue_tail(&priv->rx_queue, skb); + } + + usb_submit_urb(urb, GFP_ATOMIC); +} + +static void p54u_tx_cb(struct urb *urb) +{ + usb_free_urb(urb); +} + +static void p54u_tx_free_cb(struct urb *urb) +{ + kfree(urb->transfer_buffer); + usb_free_urb(urb); +} + +static int p54u_init_urbs(struct ieee80211_hw *dev) +{ + struct p54u_priv *priv = dev->priv; + struct urb *entry; + struct sk_buff *skb; + struct p54u_rx_info *info; + + while (skb_queue_len(&priv->rx_queue) < 32) { + skb = __dev_alloc_skb(MAX_RX_SIZE, GFP_KERNEL); + if (!skb) + break; + entry = usb_alloc_urb(0, GFP_KERNEL); + if (!entry) { + kfree_skb(skb); + break; + } + usb_fill_bulk_urb(entry, priv->udev, usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), skb->tail, MAX_RX_SIZE, p54u_rx_cb, skb); + info = (struct p54u_rx_info *) skb->cb; + info->urb = entry; + info->dev = dev; + skb_queue_tail(&priv->rx_queue, skb); + usb_submit_urb(entry, GFP_KERNEL); + } + + return 0; +} + +static void p54u_free_urbs(struct ieee80211_hw *dev) +{ + struct p54u_priv *priv = dev->priv; + struct p54u_rx_info *info; + struct sk_buff *skb; + + while ((skb = skb_dequeue(&priv->rx_queue))) { + info = (struct p54u_rx_info *) skb->cb; + if (!info->urb) + continue; + + usb_kill_urb(info->urb); + kfree_skb(skb); + } +} + +static void p54u_tx_3887(struct ieee80211_hw *dev, struct p54_control_hdr *data, + size_t len, int free_on_tx) +{ + struct p54u_priv *priv = dev->priv; + struct urb *addr_urb, *data_urb; + + addr_urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!addr_urb) + return; + + data_urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!data_urb) { + usb_free_urb(addr_urb); + return; + } + + usb_fill_bulk_urb(addr_urb, priv->udev, + usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), &data->req_id, + sizeof(data->req_id), p54u_tx_cb, dev); + usb_fill_bulk_urb(data_urb, priv->udev, + usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), data, len, + free_on_tx ? p54u_tx_free_cb : p54u_tx_cb, dev); + + usb_submit_urb(addr_urb, GFP_ATOMIC); + usb_submit_urb(data_urb, GFP_ATOMIC); +} + +static void p54u_tx_net2280(struct ieee80211_hw *dev, struct p54_control_hdr *data, + size_t len, int free_on_tx) +{ + struct p54u_priv *priv = dev->priv; + struct urb *int_urb, *data_urb; + struct net2280_tx_hdr *hdr; + struct net2280_reg_write *reg; + + reg = kmalloc(sizeof(*reg), GFP_ATOMIC); + if (!reg) + return; + + int_urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!int_urb) { + kfree(reg); + return; + } + + data_urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!data_urb) { + kfree(reg); + usb_free_urb(int_urb); + return; + } + + reg->port = cpu_to_le16(NET2280_DEV_U32); + reg->addr = cpu_to_le32(P54U_DEV_BASE); + reg->val = cpu_to_le32(ISL38XX_DEV_INT_DATA); + + len += sizeof(*data); + hdr = (void *)data - sizeof(*hdr); + memset(hdr, 0, sizeof(*hdr)); + hdr->device_addr = data->req_id; + hdr->len = cpu_to_le16(len); + + usb_fill_bulk_urb(int_urb, priv->udev, + usb_sndbulkpipe(priv->udev, P54U_PIPE_DEV), reg, sizeof(*reg), + p54u_tx_free_cb, dev); + usb_submit_urb(int_urb, GFP_ATOMIC); + + usb_fill_bulk_urb(data_urb, priv->udev, + usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), hdr, len + sizeof(*hdr), + free_on_tx ? p54u_tx_free_cb : p54u_tx_cb, dev); + usb_submit_urb(data_urb, GFP_ATOMIC); +} + +static int p54u_write(struct p54u_priv *priv, + struct net2280_reg_write *buf, + enum net2280_op_type type, + __le32 addr, __le32 val) +{ + unsigned int ep; + int alen; + + if (type & 0x0800) + ep = usb_sndbulkpipe(priv->udev, P54U_PIPE_DEV); + else + ep = usb_sndbulkpipe(priv->udev, P54U_PIPE_BRG); + + buf->port = cpu_to_le16(type); + buf->addr = addr; + buf->val = val; + + return usb_bulk_msg(priv->udev, ep, buf, sizeof(*buf), &alen, 1000); +} + +static int p54u_read(struct p54u_priv *priv, void *buf, + enum net2280_op_type type, + __le32 addr, __le32 *val) +{ + struct net2280_reg_read *read = buf; + __le32 *reg = buf; + unsigned int ep; + int alen, err; + + if (type & 0x0800) + ep = P54U_PIPE_DEV; + else + ep = P54U_PIPE_BRG; + + read->port = cpu_to_le16(type); + read->addr = addr; + + err = usb_bulk_msg(priv->udev, usb_sndbulkpipe(priv->udev, ep), + read, sizeof(*read), &alen, 1000); + if (err) + return err; + + err = usb_bulk_msg(priv->udev, usb_rcvbulkpipe(priv->udev, ep), + reg, sizeof(*reg), &alen, 1000); + if (err) + return err; + + *val = *reg; + return 0; +} + +static int p54u_bulk_msg(struct p54u_priv *priv, unsigned int ep, + void *data, size_t len) +{ + int alen; + return usb_bulk_msg(priv->udev, usb_sndbulkpipe(priv->udev, ep), + data, len, &alen, 2000); +} + +static int p54u_read_eeprom(struct ieee80211_hw *dev) +{ + struct p54u_priv *priv = dev->priv; + void *buf; + struct p54_control_hdr *hdr; + int err, alen; + size_t offset = priv->hw_type ? 0x10 : 0x20; + + buf = kmalloc(0x2020, GFP_KERNEL); + if (!buf) { + printk(KERN_ERR "prism54usb: cannot allocate memory for" + "eeprom readback!\n"); + return -ENOMEM; + } + + if (priv->hw_type) { + *((u32 *) buf) = priv->common.rx_start; + err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, sizeof(u32)); + if (err) { + printk(KERN_ERR "prism54usb: addr send failed\n"); + goto fail; + } + } else { + struct net2280_reg_write *reg = buf; + reg->port = cpu_to_le16(NET2280_DEV_U32); + reg->addr = cpu_to_le32(P54U_DEV_BASE); + reg->val = cpu_to_le32(ISL38XX_DEV_INT_DATA); + err = p54u_bulk_msg(priv, P54U_PIPE_DEV, buf, sizeof(*reg)); + if (err) { + printk(KERN_ERR "prism54usb: dev_int send failed\n"); + goto fail; + } + } + + hdr = buf + priv->common.tx_hdr_len; + p54_fill_eeprom_readback(hdr); + hdr->req_id = cpu_to_le32(priv->common.rx_start); + if (priv->common.tx_hdr_len) { + struct net2280_tx_hdr *tx_hdr = buf; + tx_hdr->device_addr = hdr->req_id; + tx_hdr->len = cpu_to_le16(EEPROM_READBACK_LEN); + } + + /* we can just pretend to send 0x2000 bytes of nothing in the headers */ + err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, + EEPROM_READBACK_LEN + priv->common.tx_hdr_len); + if (err) { + printk(KERN_ERR "prism54usb: eeprom req send failed\n"); + goto fail; + } + + err = usb_bulk_msg(priv->udev, + usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), + buf, 0x2020, &alen, 1000); + if (!err && alen > offset) { + p54_parse_eeprom(dev, (u8 *)buf + offset, alen - offset); + } else { + printk(KERN_ERR "prism54usb: eeprom read failed!\n"); + err = -EINVAL; + goto fail; + } + + fail: + kfree(buf); + return err; +} + +static int p54u_upload_firmware_3887(struct ieee80211_hw *dev) +{ + static char start_string[] = "~~~~<\r"; + struct p54u_priv *priv = dev->priv; + const struct firmware *fw_entry = NULL; + int err, alen; + u8 carry = 0; + u8 *buf, *tmp, *data; + unsigned int left, remains, block_size; + struct x2_header *hdr; + unsigned long timeout; + + err = p54u_bulk_msg(priv, P54U_PIPE_DATA, start_string, 4); + if (err) { + printk(KERN_ERR "p54usb: reset failed!\n"); + return err; + } + + tmp = buf = kmalloc(P54U_FW_BLOCK, GFP_KERNEL); + if (!buf) { + printk(KERN_ERR "p54usb: cannot allocate firmware upload buffer!\n"); + return -ENOMEM; + } + + err = request_firmware(&fw_entry, "isl3887usb_bare", &priv->udev->dev); + if (err) { + printk(KERN_ERR "p54usb: cannot find firmware (isl3887usb_bare)!\n"); + return err; + } + + p54_parse_firmware(dev, fw_entry); + + left = block_size = min((size_t)P54U_FW_BLOCK, fw_entry->size); + strcpy(buf, start_string); + left -= strlen(start_string); + tmp += strlen(start_string); + + data = fw_entry->data; + remains = fw_entry->size; + + hdr = (struct x2_header *)(buf + strlen(start_string)); + memcpy(hdr->signature, X2_SIGNATURE, X2_SIGNATURE_SIZE); + hdr->fw_load_addr = cpu_to_le32(ISL38XX_DEV_FIRMWARE_ADDR); + hdr->fw_length = cpu_to_le32(fw_entry->size); + hdr->crc = cpu_to_le32(~crc32_le(~0, (void *)&hdr->fw_load_addr, + sizeof(u32)*2)); + left -= sizeof(*hdr); + tmp += sizeof(*hdr); + + while (remains) { + while (left--) { + if (carry) { + *tmp++ = carry; + carry = 0; + remains--; + continue; + } + switch (*data) { + case '~': + *tmp++ = '}'; + carry = '^'; + break; + case '}': + *tmp++ = '}'; + carry = ']'; + break; + default: + *tmp++ = *data; + remains--; + break; + } + data++; + } + + err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, block_size); + if (err) { + printk(KERN_ERR "prism54usb: firmware upload failed!\n"); + goto err_upload_failed; + } + + tmp = buf; + left = block_size = min((unsigned int)P54U_FW_BLOCK, remains); + } + + *((__le32 *)buf) = cpu_to_le32(~crc32_le(~0, fw_entry->data, fw_entry->size)); + err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, sizeof(u32)); + if (err) { + printk(KERN_ERR "prism54usb: firmware upload failed!\n"); + goto err_upload_failed; + } + + timeout = jiffies + msecs_to_jiffies(1000); + while (!(err = usb_bulk_msg(priv->udev, + usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), buf, 128, &alen, 1000))) { + if (alen > 2 && !memcmp(buf, "OK", 2)) + break; + + if (alen > 5 && !memcmp(buf, "ERROR", 5)) { + printk(KERN_INFO "prism54usb: firmware upload failed!\n"); + err = -EINVAL; + break; + } + + if (time_after(jiffies, timeout)) { + printk(KERN_ERR "prism54usb: firmware boot timed out!\n"); + err = -ETIMEDOUT; + break; + } + } + if (err) + goto err_upload_failed; + + buf[0] = 'g'; + buf[1] = '\r'; + err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, 2); + if (err) { + printk(KERN_ERR "prism54usb: firmware boot failed!\n"); + goto err_upload_failed; + } + + timeout = jiffies + msecs_to_jiffies(1000); + while (!(err = usb_bulk_msg(priv->udev, + usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), buf, 128, &alen, 1000))) { + if (alen > 0 && buf[0] == 'g') + break; + + if (time_after(jiffies, timeout)) { + err = -ETIMEDOUT; + break; + } + } + if (err) + goto err_upload_failed; + + err_upload_failed: + release_firmware(fw_entry); + kfree(buf); + return err; +} + +static int p54u_upload_firmware_net2280(struct ieee80211_hw *dev) +{ + struct p54u_priv *priv = dev->priv; + const struct firmware *fw_entry = NULL; + const struct p54p_csr *devreg = (const struct p54p_csr *) P54U_DEV_BASE; + int err, alen; + void *buf; + __le32 reg; + unsigned int remains, offset; + u8 *data; + + buf = kmalloc(512, GFP_KERNEL); + if (!buf) { + printk(KERN_ERR "p54usb: firmware buffer alloc failed!\n"); + return -ENOMEM; + } + + err = request_firmware(&fw_entry, "isl3890usb", &priv->udev->dev); + if (err) { + printk(KERN_ERR "p54usb: cannot find firmware (isl3890usb)!\n"); + kfree(buf); + return err; + } + + p54_parse_firmware(dev, fw_entry); + +#define P54U_WRITE(type, addr, data) \ + do {\ + err = p54u_write(priv, buf, type,\ + cpu_to_le32((u32)(unsigned long)addr), data);\ + if (err) \ + goto fail;\ + } while (0) + +#define P54U_READ(type, addr) \ + do {\ + err = p54u_read(priv, buf, type,\ + cpu_to_le32((u32)(unsigned long)addr), ®);\ + if (err)\ + goto fail;\ + } while (0) + + /* power down net2280 bridge */ + P54U_READ(NET2280_BRG_U32, NET2280_GPIOCTL); + reg |= cpu_to_le32(P54U_BRG_POWER_DOWN); + reg &= cpu_to_le32(~P54U_BRG_POWER_UP); + P54U_WRITE(NET2280_BRG_U32, NET2280_GPIOCTL, reg); + + mdelay(100); + + /* power up bridge */ + reg |= cpu_to_le32(P54U_BRG_POWER_UP); + reg &= cpu_to_le32(~P54U_BRG_POWER_DOWN); + P54U_WRITE(NET2280_BRG_U32, NET2280_GPIOCTL, reg); + + mdelay(100); + + P54U_WRITE(NET2280_BRG_U32, NET2280_DEVINIT, + cpu_to_le32(NET2280_CLK_30Mhz | + NET2280_PCI_ENABLE | + NET2280_PCI_SOFT_RESET)); + + mdelay(20); + + P54U_WRITE(NET2280_BRG_CFG_U16, PCI_COMMAND, + cpu_to_le32(PCI_COMMAND_MEMORY | + PCI_COMMAND_MASTER)); + + P54U_WRITE(NET2280_BRG_CFG_U32, PCI_BASE_ADDRESS_0, + cpu_to_le32(NET2280_BASE)); + + P54U_READ(NET2280_BRG_CFG_U16, PCI_STATUS); + reg |= cpu_to_le32(PCI_STATUS_REC_MASTER_ABORT); + P54U_WRITE(NET2280_BRG_CFG_U16, PCI_STATUS, reg); + + // TODO: we really need this? + P54U_READ(NET2280_BRG_U32, NET2280_RELNUM); + + P54U_WRITE(NET2280_BRG_U32, NET2280_EPA_RSP, + cpu_to_le32(NET2280_CLEAR_NAK_OUT_PACKETS_MODE)); + P54U_WRITE(NET2280_BRG_U32, NET2280_EPC_RSP, + cpu_to_le32(NET2280_CLEAR_NAK_OUT_PACKETS_MODE)); + + P54U_WRITE(NET2280_BRG_CFG_U32, PCI_BASE_ADDRESS_2, + cpu_to_le32(NET2280_BASE2)); + + /* finally done setting up the bridge */ + + P54U_WRITE(NET2280_DEV_CFG_U16, 0x10000 | PCI_COMMAND, + cpu_to_le32(PCI_COMMAND_MEMORY | + PCI_COMMAND_MASTER)); + + P54U_WRITE(NET2280_DEV_CFG_U16, 0x10000 | 0x40 /* TRDY timeout */, 0); + P54U_WRITE(NET2280_DEV_CFG_U32, 0x10000 | PCI_BASE_ADDRESS_0, + cpu_to_le32(P54U_DEV_BASE)); + + P54U_WRITE(NET2280_BRG_U32, NET2280_USBIRQENB1, 0); + P54U_WRITE(NET2280_BRG_U32, NET2280_IRQSTAT1, + cpu_to_le32(NET2280_PCI_INTA_INTERRUPT)); + + /* do romboot */ + P54U_WRITE(NET2280_DEV_U32, &devreg->int_enable, 0); + + P54U_READ(NET2280_DEV_U32, &devreg->ctrl_stat); + reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); + reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RAMBOOT); + reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_CLKRUN); + P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); + + mdelay(20); + + reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET); + P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); + + mdelay(20); + + reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); + P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); + + mdelay(100); + + P54U_READ(NET2280_DEV_U32, &devreg->int_ident); + P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg); + + /* finally, we can upload firmware now! */ + remains = fw_entry->size; + data = fw_entry->data; + offset = ISL38XX_DEV_FIRMWARE_ADDR; + + while (remains) { + unsigned int block_len = min(remains, (unsigned int)512); + memcpy(buf, data, block_len); + + err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, block_len); + if (err) { + printk(KERN_ERR "prism54usb: firmware block upload " + "failed\n"); + goto fail; + } + + P54U_WRITE(NET2280_DEV_U32, &devreg->direct_mem_base, + cpu_to_le32(0xc0000f00)); + + P54U_WRITE(NET2280_DEV_U32, + 0x0020 | (unsigned long)&devreg->direct_mem_win, 0); + P54U_WRITE(NET2280_DEV_U32, + 0x0020 | (unsigned long)&devreg->direct_mem_win, + cpu_to_le32(1)); + + P54U_WRITE(NET2280_DEV_U32, + 0x0024 | (unsigned long)&devreg->direct_mem_win, + cpu_to_le32(block_len)); + P54U_WRITE(NET2280_DEV_U32, + 0x0028 | (unsigned long)&devreg->direct_mem_win, + cpu_to_le32(offset)); + + P54U_WRITE(NET2280_DEV_U32, &devreg->dma_addr, + cpu_to_le32(NET2280_EPA_FIFO_PCI_ADDR)); + P54U_WRITE(NET2280_DEV_U32, &devreg->dma_len, + cpu_to_le32(block_len >> 2)); + P54U_WRITE(NET2280_DEV_U32, &devreg->dma_ctrl, + cpu_to_le32(ISL38XX_DMA_MASTER_CONTROL_TRIGGER)); + + mdelay(10); + + P54U_READ(NET2280_DEV_U32, + 0x002C | (unsigned long)&devreg->direct_mem_win); + if (!(reg & cpu_to_le32(ISL38XX_DMA_STATUS_DONE)) || + !(reg & cpu_to_le32(ISL38XX_DMA_STATUS_READY))) { + printk(KERN_ERR "prism54usb: firmware DMA transfer " + "failed\n"); + goto fail; + } + + P54U_WRITE(NET2280_BRG_U32, NET2280_EPA_STAT, + cpu_to_le32(NET2280_FIFO_FLUSH)); + + remains -= block_len; + data += block_len; + offset += block_len; + } + + /* do ramboot */ + P54U_READ(NET2280_DEV_U32, &devreg->ctrl_stat); + reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); + reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_CLKRUN); + reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RAMBOOT); + P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); + + mdelay(20); + + reg |= cpu_to_le32(ISL38XX_CTRL_STAT_RESET); + P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); + + reg &= cpu_to_le32(~ISL38XX_CTRL_STAT_RESET); + P54U_WRITE(NET2280_DEV_U32, &devreg->ctrl_stat, reg); + + mdelay(100); + + P54U_READ(NET2280_DEV_U32, &devreg->int_ident); + P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg); + + /* start up the firmware */ + P54U_WRITE(NET2280_DEV_U32, &devreg->int_enable, + cpu_to_le32(ISL38XX_INT_IDENT_INIT)); + + P54U_WRITE(NET2280_BRG_U32, NET2280_IRQSTAT1, + cpu_to_le32(NET2280_PCI_INTA_INTERRUPT)); + + P54U_WRITE(NET2280_BRG_U32, NET2280_USBIRQENB1, + cpu_to_le32(NET2280_PCI_INTA_INTERRUPT_ENABLE | + NET2280_USB_INTERRUPT_ENABLE)); + + P54U_WRITE(NET2280_DEV_U32, &devreg->dev_int, + cpu_to_le32(ISL38XX_DEV_INT_RESET)); + + err = usb_interrupt_msg(priv->udev, + usb_rcvbulkpipe(priv->udev, P54U_PIPE_INT), + buf, sizeof(__le32), &alen, 1000); + if (err || alen != sizeof(__le32)) + goto fail; + + P54U_READ(NET2280_DEV_U32, &devreg->int_ident); + P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg); + + if (!(reg & cpu_to_le32(ISL38XX_INT_IDENT_INIT))) + err = -EINVAL; + + P54U_WRITE(NET2280_BRG_U32, NET2280_USBIRQENB1, 0); + P54U_WRITE(NET2280_BRG_U32, NET2280_IRQSTAT1, + cpu_to_le32(NET2280_PCI_INTA_INTERRUPT)); + +#undef P54U_WRITE +#undef P54U_READ + + fail: + release_firmware(fw_entry); + kfree(buf); + return err; +} + +static int p54u_open_3887(struct ieee80211_hw *dev) +{ + struct p54u_priv *priv = dev->priv; + struct p54_control_hdr *startup_packet; + int err; + + startup_packet = kmalloc(sizeof(p54u_3887_magic_packet), GFP_KERNEL); + if (!startup_packet) { + printk(KERN_ERR "%s: cannot alloc startup packet\n", + wiphy_name(dev->wiphy)); + return -ENOMEM; + } + + err = p54u_init_urbs(dev); + if (err) { + kfree(startup_packet); + return err; + } + + memcpy(startup_packet, p54u_3887_magic_packet, + sizeof(p54u_3887_magic_packet)); + startup_packet->req_id = cpu_to_le32(priv->common.rx_start); + + p54u_tx_3887(dev, startup_packet, sizeof(p54u_3887_magic_packet), 1); + priv->common.open = p54u_init_urbs; + + return 0; +} + +static int p54u_open_net2280(struct ieee80211_hw *dev) +{ + struct p54u_priv *priv = dev->priv; + struct p54_control_hdr *startup_packet; + int err; + + startup_packet = kmalloc(sizeof(p54u_net2280_magic_packet) + + priv->common.tx_hdr_len, GFP_KERNEL); + if (!startup_packet) { + printk(KERN_ERR "%s: cannot alloc startup packet\n", + wiphy_name(dev->wiphy)); + return -ENOMEM; + } + + err = p54u_init_urbs(dev); + if (err) { + kfree(startup_packet); + return err; + } + + startup_packet = (void *)startup_packet + priv->common.tx_hdr_len; + + memcpy(startup_packet, p54u_net2280_magic_packet, + sizeof(p54u_net2280_magic_packet)); + startup_packet->req_id = cpu_to_le32(priv->common.rx_start); + + p54u_tx_net2280(dev, startup_packet, + sizeof(p54u_net2280_magic_packet), 1); + priv->common.open = p54u_init_urbs; + + return 0; +} + +static void p54u_stop(struct ieee80211_hw *dev) +{ + /* TODO: figure out how to reliably stop the 3887 and net2280 so + the hardware is still usable next time we want to start it. + until then, we just stop listening to the hardware.. */ + p54u_free_urbs(dev); + return; +} + +static int __devinit p54u_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct ieee80211_hw *dev; + struct p54u_priv *priv; + int err; + unsigned int i, recognized_pipes; + + dev = p54_init_common(sizeof(*priv)); + if (!dev) { + printk(KERN_ERR "prism54usb: ieee80211 alloc failed\n"); + return -ENOMEM; + } + + priv = dev->priv; + + SET_IEEE80211_DEV(dev, &intf->dev); + usb_set_intfdata(intf, dev); + priv->udev = udev; + + usb_get_dev(udev); + + /* really lazy and simple way of figuring out if we're a 3887 */ + /* TODO: should just stick the identification in the device table */ + i = intf->altsetting->desc.bNumEndpoints; + recognized_pipes = 0; + while (i--) { + switch (intf->altsetting->endpoint[i].desc.bEndpointAddress) { + case P54U_PIPE_DATA: + case P54U_PIPE_MGMT: + case P54U_PIPE_BRG: + case P54U_PIPE_DEV: + case P54U_PIPE_DATA | USB_DIR_IN: + case P54U_PIPE_MGMT | USB_DIR_IN: + case P54U_PIPE_BRG | USB_DIR_IN: + case P54U_PIPE_DEV | USB_DIR_IN: + case P54U_PIPE_INT | USB_DIR_IN: + recognized_pipes++; + } + } + if (recognized_pipes < P54U_PIPE_NUMBER) { + priv->hw_type = P54U_3887; + priv->common.open = p54u_open_3887; + priv->common.tx = p54u_tx_3887; + } else { + dev->extra_tx_headroom += sizeof(struct net2280_tx_hdr); + priv->common.tx_hdr_len = sizeof(struct net2280_tx_hdr); + priv->common.open = p54u_open_net2280; + priv->common.tx = p54u_tx_net2280; + } + priv->common.stop = p54u_stop; + + if (priv->hw_type) + err = p54u_upload_firmware_3887(dev); + else + err = p54u_upload_firmware_net2280(dev); + if (err) + goto err_free_dev; + + err = p54u_read_eeprom(dev); + if (err) + goto err_free_dev; + + if (!is_valid_ether_addr(dev->wiphy->perm_addr)) { + u8 perm_addr[ETH_ALEN]; + + printk(KERN_WARNING "prism54usb: Invalid hwaddr! Using randomly generated MAC addr\n"); + random_ether_addr(perm_addr); + SET_IEEE80211_PERM_ADDR(dev, perm_addr); + } + + skb_queue_head_init(&priv->rx_queue); + + err = ieee80211_register_hw(dev); + if (err) { + printk(KERN_ERR "prism54usb: Cannot register netdevice\n"); + goto err_free_dev; + } + + printk(KERN_INFO "%s: hwaddr " MAC_FMT ", isl38%02x\n", + wiphy_name(dev->wiphy), MAC_ARG(dev->wiphy->perm_addr), + priv->common.version); + + return 0; + + err_free_dev: + ieee80211_free_hw(dev); + usb_set_intfdata(intf, NULL); + usb_put_dev(udev); + return err; +} + +static void __devexit p54u_disconnect(struct usb_interface *intf) +{ + struct ieee80211_hw *dev = usb_get_intfdata(intf); + struct p54u_priv *priv; + + if (!dev) + return; + + ieee80211_unregister_hw(dev); + + priv = dev->priv; + usb_put_dev(interface_to_usbdev(intf)); + p54_free_common(dev); + ieee80211_free_hw(dev); +} + +static struct usb_driver p54u_driver = { + .name = "prism54usb", + .id_table = p54u_table, + .probe = p54u_probe, + .disconnect = p54u_disconnect, +}; + +static int __init p54u_init(void) +{ + return usb_register(&p54u_driver); +} + +static void __exit p54u_exit(void) +{ + usb_deregister(&p54u_driver); +} + +module_init(p54u_init); +module_exit(p54u_exit); diff --git a/drivers/net/wireless/mac80211/p54/prism54usb.h b/drivers/net/wireless/mac80211/p54/prism54usb.h new file mode 100644 index 0000000..5deffa8 --- /dev/null +++ b/drivers/net/wireless/mac80211/p54/prism54usb.h @@ -0,0 +1,133 @@ +#ifndef PRISM54USB_H +#define PRISM54USB_H + +/* + * Defines for USB based mac80211 Prism54 driver + * + * Copyright (c) 2006, Michael Wu + * + * Based on the islsm (softmac prism54) driver, which is: + * Copyright 2004-2006 Jean-Baptiste Note , et al. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* for isl3886 register definitions used on ver 1 devices */ +#include "prism54pci.h" +#include "net2280.h" + +/* pci */ +#define NET2280_BASE 0x10000000 +#define NET2280_BASE2 0x20000000 + +/* gpio */ +#define P54U_BRG_POWER_UP (1 << GPIO0_DATA) +#define P54U_BRG_POWER_DOWN (1 << GPIO1_DATA) + +/* devinit */ +#define NET2280_CLK_4Mhz (15 << LOCAL_CLOCK_FREQUENCY) +#define NET2280_CLK_30Mhz (2 << LOCAL_CLOCK_FREQUENCY) +#define NET2280_CLK_60Mhz (1 << LOCAL_CLOCK_FREQUENCY) +#define NET2280_CLK_STOP (0 << LOCAL_CLOCK_FREQUENCY) +#define NET2280_PCI_ENABLE (1 << PCI_ENABLE) +#define NET2280_PCI_SOFT_RESET (1 << PCI_SOFT_RESET) + +/* endpoints */ +#define NET2280_CLEAR_NAK_OUT_PACKETS_MODE (1 << CLEAR_NAK_OUT_PACKETS_MODE) +#define NET2280_FIFO_FLUSH (1 << FIFO_FLUSH) + +/* irq */ +#define NET2280_USB_INTERRUPT_ENABLE (1 << USB_INTERRUPT_ENABLE) +#define NET2280_PCI_INTA_INTERRUPT (1 << PCI_INTA_INTERRUPT) +#define NET2280_PCI_INTA_INTERRUPT_ENABLE (1 << PCI_INTA_INTERRUPT_ENABLE) + +/* registers */ +#define NET2280_DEVINIT 0x00 +#define NET2280_USBIRQENB1 0x24 +#define NET2280_IRQSTAT1 0x2c +#define NET2280_FIFOCTL 0x38 +#define NET2280_GPIOCTL 0x50 +#define NET2280_RELNUM 0x88 +#define NET2280_EPA_RSP 0x324 +#define NET2280_EPA_STAT 0x32c +#define NET2280_EPB_STAT 0x34c +#define NET2280_EPC_RSP 0x364 +#define NET2280_EPC_STAT 0x36c +#define NET2280_EPD_STAT 0x38c + +#define NET2280_EPA_CFG 0x320 +#define NET2280_EPB_CFG 0x340 +#define NET2280_EPC_CFG 0x360 +#define NET2280_EPD_CFG 0x380 +#define NET2280_EPE_CFG 0x3A0 +#define NET2280_EPF_CFG 0x3C0 +#define P54U_DEV_BASE 0x40000000 + +struct net2280_tx_hdr { + __le32 device_addr; + __le16 len; + __le16 follower; /* ? */ + u8 padding[8]; +} __attribute__((packed)); + +/* Some flags for the isl hardware registers controlling DMA inside the + * chip */ +#define ISL38XX_DMA_STATUS_DONE 0x00000001 +#define ISL38XX_DMA_STATUS_READY 0x00000002 +#define NET2280_EPA_FIFO_PCI_ADDR 0x20000000 +#define ISL38XX_DMA_MASTER_CONTROL_TRIGGER 0x00000004 + +enum net2280_op_type { + NET2280_BRG_U32 = 0x001F, + NET2280_BRG_CFG_U32 = 0x000F, + NET2280_BRG_CFG_U16 = 0x0003, + NET2280_DEV_U32 = 0x080F, + NET2280_DEV_CFG_U32 = 0x088F, + NET2280_DEV_CFG_U16 = 0x0883 +}; + +#define P54U_FW_BLOCK 2048 + +#define X2_SIGNATURE "x2 " +#define X2_SIGNATURE_SIZE 4 + +struct x2_header { + u8 signature[X2_SIGNATURE_SIZE]; + __le32 fw_load_addr; + __le32 fw_length; + __le32 crc; +} __attribute__((packed)); + +/* pipes 3 and 4 are not used by the driver */ +#define P54U_PIPE_NUMBER 9 + +enum p54u_pipe_addr { + P54U_PIPE_DATA = 0x01, + P54U_PIPE_MGMT = 0x02, + P54U_PIPE_3 = 0x03, + P54U_PIPE_4 = 0x04, + P54U_PIPE_BRG = 0x0d, + P54U_PIPE_DEV = 0x0e, + P54U_PIPE_INT = 0x0f +}; + +struct p54u_rx_info { + struct urb *urb; + struct ieee80211_hw *dev; +}; + +struct p54u_priv { + struct p54_common common; + struct usb_device *udev; + enum { + P54U_NET2280 = 0, + P54U_3887 + } hw_type; + + spinlock_t lock; + struct sk_buff_head rx_queue; +}; + +#endif /* PRISM54USB_H */ diff --git a/drivers/net/wireless/mac80211/rt2x00/Kconfig b/drivers/net/wireless/mac80211/rt2x00/Kconfig new file mode 100644 index 0000000..af67572 --- /dev/null +++ b/drivers/net/wireless/mac80211/rt2x00/Kconfig @@ -0,0 +1,99 @@ +config RT2X00 + tristate "Ralink driver support" + depends on MAC80211 && WLAN_80211 && EXPERIMENTAL + ---help--- + This will enable the experimental support for the Ralink drivers, + developed in the rt2x00 project . + + These drivers will make use of the Devicescape ieee80211 stack. + + When building one of the individual drivers, the rt2x00 library + will also be created. That library (when the driver is built as + a module) will be called "rt2x00lib.ko". + +config RT2X00_LIB + tristate + depends on RT2X00 + +config RT2X00_LIB_PCI + tristate + depends on RT2X00 + +config RT2X00_LIB_USB + tristate + depends on RT2X00 + +config RT2X00_LIB_FIRMWARE + boolean + depends on RT2X00 + +config RT2400PCI + tristate "Ralink rt2400 pci/pcmcia support" + depends on RT2X00 && PCI + select RT2X00_LIB + select RT2X00_LIB_PCI + select EEPROM_93CX6 + ---help--- + This is an experimental driver for the Ralink rt2400 wireless chip. + + When compiled as a module, this driver will be called "rt2400pci.ko". + +config RT2500PCI + tristate "Ralink rt2500 pci/pcmcia support" + depends on RT2X00 && PCI + select RT2X00_LIB + select RT2X00_LIB_PCI + select EEPROM_93CX6 + ---help--- + This is an experimental driver for the Ralink rt2500 wireless chip. + + When compiled as a module, this driver will be called "rt2500pci.ko". + +config RT61PCI + tristate "Ralink rt61 pci/pcmcia support" + depends on RT2X00 && FW_LOADER && PCI + select RT2X00_LIB + select RT2X00_LIB_PCI + select RT2X00_LIB_FIRMWARE + select CRC_ITU_T + select EEPROM_93CX6 + ---help--- + This is an experimental driver for the Ralink rt61 wireless chip. + + When compiled as a module, this driver will be called "rt61pci.ko". + +config RT2500USB + tristate "Ralink rt2500 usb support" + depends on RT2X00 && USB + select RT2X00_LIB + select RT2X00_LIB_USB + ---help--- + This is an experimental driver for the Ralink rt2500 wireless chip. + + When compiled as a module, this driver will be called "rt2500usb.ko". + +config RT73USB + tristate "Ralink rt73 usb support" + depends on RT2X00 && FW_LOADER && USB + select RT2X00_LIB + select RT2X00_LIB_USB + select RT2X00_LIB_FIRMWARE + select CRC_ITU_T + ---help--- + This is an experimental driver for the Ralink rt73 wireless chip. + + When compiled as a module, this driver will be called "rt73usb.ko". + +config RT2X00_DEBUGFS + bool "Ralink debugfs support" + depends on RT2X00 && RT2X00_LIB && MAC80211_DEBUGFS + ---help--- + Enable creation of debugfs files for the rt2x00 drivers. + These debugfs files support both reading and writing of the + most important register types of the rt2x00 devices. + +config RT2X00_DEBUG + bool "Ralink debug output" + depends on RT2X00 && RT2X00_LIB + ---help--- + Enable debugging output for all rt2x00 modules diff --git a/drivers/net/wireless/mac80211/rt2x00/Makefile b/drivers/net/wireless/mac80211/rt2x00/Makefile new file mode 100644 index 0000000..01d045c --- /dev/null +++ b/drivers/net/wireless/mac80211/rt2x00/Makefile @@ -0,0 +1,14 @@ +rt2x00lib-objs := rt2x00dev.o rt2x00mac.o + +ifeq ($(CONFIG_RT2X00_DEBUGFS),y) + rt2x00lib-objs += rt2x00debug.o +endif + +obj-$(CONFIG_RT2X00_LIB) += rt2x00lib.o +obj-$(CONFIG_RT2X00_LIB_PCI) += rt2x00pci.o +obj-$(CONFIG_RT2X00_LIB_USB) += rt2x00usb.o +obj-$(CONFIG_RT2400PCI) += rt2400pci.o +obj-$(CONFIG_RT2500PCI) += rt2500pci.o +obj-$(CONFIG_RT61PCI) += rt61pci.o +obj-$(CONFIG_RT2500USB) += rt2500usb.o +obj-$(CONFIG_RT73USB) += rt73usb.o diff --git a/drivers/net/wireless/mac80211/rt2x00/rt2400pci.c b/drivers/net/wireless/mac80211/rt2x00/rt2400pci.c new file mode 100644 index 0000000..4d6b90c --- /dev/null +++ b/drivers/net/wireless/mac80211/rt2x00/rt2400pci.c @@ -0,0 +1,1769 @@ +/* + Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + + + 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. + */ + +/* + Module: rt2400pci + Abstract: rt2400pci device specific routines. + Supported chipsets: RT2460. + */ + +/* + * Set enviroment defines for rt2x00.h + */ +#define DRV_NAME "rt2400pci" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "rt2x00.h" +#include "rt2x00pci.h" +#include "rt2400pci.h" + +/* + * Register access. + * All access to the CSR registers will go through the methods + * rt2x00_register_read and rt2x00_register_write. + * BBP and RF register require indirect register access, + * and use the CSR registers BBPCSR and RFCSR to achieve this. + * These indirect registers work with busy bits, + * and we will try maximal REGISTER_BUSY_COUNT times to access + * the register while taking a REGISTER_BUSY_DELAY us delay + * between each attampt. When the busy bit is still set at that time, + * the access attempt is considered to have failed, + * and we will print an error. + */ +static inline void rt2x00_register_read( + const struct rt2x00_dev *rt2x00dev, + const unsigned long offset, u32 *value) +{ + *value = readl(rt2x00dev->csr_addr + offset); +} + +static inline void rt2x00_register_multiread( + const struct rt2x00_dev *rt2x00dev, + const unsigned long offset, void *value, const u16 length) +{ + memcpy_fromio(value, rt2x00dev->csr_addr + offset, length); +} + +static inline void rt2x00_register_write( + const struct rt2x00_dev *rt2x00dev, + const unsigned long offset, u32 value) +{ + writel(value, rt2x00dev->csr_addr + offset); +} + +static inline void rt2x00_register_multiwrite( + const struct rt2x00_dev *rt2x00dev, + const unsigned long offset, void *value, const u16 length) +{ + memcpy_toio(rt2x00dev->csr_addr + offset, value, length); +} + +static u32 rt2x00_bbp_check(const struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + unsigned int i; + + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2x00_register_read(rt2x00dev, BBPCSR, ®); + if (!rt2x00_get_field32(reg, BBPCSR_BUSY)) + return reg; + udelay(REGISTER_BUSY_DELAY); + } + + return 0xffff; +} + +static void rt2x00_bbp_write(const struct rt2x00_dev *rt2x00dev, + const u8 reg_id, const u8 value) +{ + u32 reg; + + /* + * Wait until the BBP becomes ready. + */ + if (rt2x00_bbp_check(rt2x00dev) == 0xffff) { + ERROR("BBPCSR register busy. Write failed.\n"); + return; + } + + /* + * Write the data into the BBP. + */ + reg = 0; + rt2x00_set_field32(®, BBPCSR_VALUE, value); + rt2x00_set_field32(®, BBPCSR_REGNUM, reg_id); + rt2x00_set_field32(®, BBPCSR_BUSY, 1); + rt2x00_set_field32(®, BBPCSR_WRITE_CONTROL, 1); + + rt2x00_register_write(rt2x00dev, BBPCSR, reg); +} + +static void rt2x00_bbp_read(const struct rt2x00_dev *rt2x00dev, + const u8 reg_id, u8 *value) +{ + u32 reg; + + /* + * Wait until the BBP becomes ready. + */ + if (rt2x00_bbp_check(rt2x00dev) == 0xffff) { + ERROR("BBPCSR register busy. Read failed.\n"); + return; + } + + /* + * Write the request into the BBP. + */ + reg = 0; + rt2x00_set_field32(®, BBPCSR_REGNUM, reg_id); + rt2x00_set_field32(®, BBPCSR_BUSY, 1); + rt2x00_set_field32(®, BBPCSR_WRITE_CONTROL, 0); + + rt2x00_register_write(rt2x00dev, BBPCSR, reg); + + /* + * Wait until the BBP becomes ready. + */ + reg = rt2x00_bbp_check(rt2x00dev); + if (reg == 0xffff) + ERROR("BBPCSR register busy. Read failed.\n"); + + *value = rt2x00_get_field32(reg, BBPCSR_VALUE); +} + +static void rt2x00_rf_write(const struct rt2x00_dev *rt2x00dev, + const u32 value) +{ + u32 reg; + unsigned int i; + + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2x00_register_read(rt2x00dev, RFCSR, ®); + if (!rt2x00_get_field32(reg, RFCSR_BUSY)) + goto rf_write; + udelay(REGISTER_BUSY_DELAY); + } + + ERROR("RFCSR register busy. Write failed.\n"); + return; + +rf_write: + reg = 0; + rt2x00_set_field32(®, RFCSR_VALUE, value); + rt2x00_set_field32(®, RFCSR_NUMBER_OF_BITS, 20); + rt2x00_set_field32(®, RFCSR_IF_SELECT, 0); + rt2x00_set_field32(®, RFCSR_BUSY, 1); + + rt2x00_register_write(rt2x00dev, RFCSR, reg); +} + +static void rt2400pci_eepromregister_read(struct eeprom_93cx6 *eeprom) +{ + struct rt2x00_dev *rt2x00dev = eeprom->data; + u32 reg; + + rt2x00_register_read(rt2x00dev, CSR21, ®); + + eeprom->reg_data_in = !!rt2x00_get_field32(reg, + CSR21_EEPROM_DATA_IN); + eeprom->reg_data_out = !!rt2x00_get_field32(reg, + CSR21_EEPROM_DATA_OUT); + eeprom->reg_data_clock = !!rt2x00_get_field32(reg, + CSR21_EEPROM_DATA_CLOCK); + eeprom->reg_chip_select = !!rt2x00_get_field32(reg, + CSR21_EEPROM_CHIP_SELECT); +} + +static void rt2400pci_eepromregister_write(struct eeprom_93cx6 *eeprom) +{ + struct rt2x00_dev *rt2x00dev = eeprom->data; + u32 reg = 0; + + rt2x00_set_field32(®, CSR21_EEPROM_DATA_IN, + !!eeprom->reg_data_in); + rt2x00_set_field32(®, CSR21_EEPROM_DATA_OUT, + !!eeprom->reg_data_out); + rt2x00_set_field32(®, CSR21_EEPROM_DATA_CLOCK, + !!eeprom->reg_data_clock); + rt2x00_set_field32(®, CSR21_EEPROM_CHIP_SELECT, + !!eeprom->reg_chip_select); + + rt2x00_register_write(rt2x00dev, CSR21, reg); +} + +#ifdef CONFIG_RT2X00_DEBUGFS +#define CSR_OFFSET(__word) ( CSR_REG_BASE + ((__word) * sizeof(u32)) ) + +static void rt2400pci_read_csr(struct rt2x00_dev *rt2x00dev, + const unsigned long word, void *data) +{ + rt2x00_register_read(rt2x00dev, CSR_OFFSET(word), data); +} + +static void rt2400pci_write_csr(struct rt2x00_dev *rt2x00dev, + const unsigned long word, void *data) +{ + rt2x00_register_write(rt2x00dev, CSR_OFFSET(word), *((u32*)data)); +} + +static void rt2400pci_read_eeprom(struct rt2x00_dev *rt2x00dev, + const unsigned long word, void *data) +{ + rt2x00_eeprom_read(rt2x00dev, word, data); +} + +static void rt2400pci_write_eeprom(struct rt2x00_dev *rt2x00dev, + const unsigned long word, void *data) +{ + rt2x00_eeprom_write(rt2x00dev, word, *((u16*)data)); +} + +static void rt2400pci_read_bbp(struct rt2x00_dev *rt2x00dev, + const unsigned long word, void *data) +{ + rt2x00_bbp_read(rt2x00dev, word, data); +} + +static void rt2400pci_write_bbp(struct rt2x00_dev *rt2x00dev, + const unsigned long word, void *data) +{ + rt2x00_bbp_write(rt2x00dev, word, *((u8*)data)); +} + +static const struct rt2x00debug rt2400pci_rt2x00debug = { + .owner = THIS_MODULE, + .mod_name = DRV_NAME, + .mod_version = DRV_VERSION, + .reg_csr = { + .read = rt2400pci_read_csr, + .write = rt2400pci_write_csr, + .word_size = sizeof(u32), + .word_count = CSR_REG_SIZE / sizeof(u32), + }, + .reg_eeprom = { + .read = rt2400pci_read_eeprom, + .write = rt2400pci_write_eeprom, + .word_size = sizeof(u16), + .word_count = EEPROM_SIZE / sizeof(u16), + }, + .reg_bbp = { + .read = rt2400pci_read_bbp, + .write = rt2400pci_write_bbp, + .word_size = sizeof(u8), + .word_count = BBP_SIZE / sizeof(u8), + }, +}; +#endif /* CONFIG_RT2X00_DEBUGFS */ + +/* + * Configuration handlers. + */ +static void rt2400pci_config_bssid(struct rt2x00_dev *rt2x00dev, u8 *bssid) +{ + u32 reg[2]; + + memset(®, 0, sizeof(reg)); + memcpy(®, bssid, ETH_ALEN); + + /* + * The BSSID is passed to us as an array of bytes, + * that array is little endian, so no need for byte ordering. + */ + rt2x00_register_multiwrite(rt2x00dev, CSR5, ®, sizeof(reg)); +} + +static void rt2400pci_config_promisc(struct rt2x00_dev *rt2x00dev, int promisc) +{ + u32 reg; + + rt2x00_register_read(rt2x00dev, RXCSR0, ®); + + if (promisc) { + rt2x00_set_field32(®, RXCSR0_DROP_NOT_TO_ME, 0); + SET_FLAG(rt2x00dev, INTERFACE_ENABLED_PROMISC); + } else { + rt2x00_set_field32(®, RXCSR0_DROP_NOT_TO_ME, 1); + CLEAR_FLAG(rt2x00dev, INTERFACE_ENABLED_PROMISC); + } + + rt2x00_register_write(rt2x00dev, RXCSR0, reg); +} + +static void rt2400pci_config_type(struct rt2x00_dev *rt2x00dev, int type) +{ + u32 reg; + + /* + * Only continue when there is something to be done. + */ + if (!(GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED) ^ + GET_FLAG(rt2x00dev, INTERFACE_ENABLED)) && + !(GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED_MONITOR) ^ + GET_FLAG(rt2x00dev, INTERFACE_ENABLED_MONITOR))) + return; + + rt2x00_register_write(rt2x00dev, CSR14, 0); + + /* + * Apply hardware packet filter. + */ + rt2x00_register_read(rt2x00dev, RXCSR0, ®); + + if (!GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED_MONITOR) && + (type == IEEE80211_IF_TYPE_IBSS || type == IEEE80211_IF_TYPE_STA)) + rt2x00_set_field32(®, RXCSR0_DROP_TODS, 1); + else + rt2x00_set_field32(®, RXCSR0_DROP_TODS, 0); + + rt2x00_set_field32(®, RXCSR0_DROP_CRC, 1); + if (GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED_MONITOR)) { + rt2x00_set_field32(®, RXCSR0_DROP_PHYSICAL, 0); + rt2x00_set_field32(®, RXCSR0_DROP_CONTROL, 0); + rt2x00_set_field32(®, RXCSR0_DROP_VERSION_ERROR, 0); + } else { + rt2x00_set_field32(®, RXCSR0_DROP_PHYSICAL, 1); + rt2x00_set_field32(®, RXCSR0_DROP_CONTROL, 1); + rt2x00_set_field32(®, RXCSR0_DROP_VERSION_ERROR, 1); + } + + rt2x00_register_write(rt2x00dev, RXCSR0, reg); + + /* + * Enable promisc mode when in monitor mode. + */ + if (GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED_MONITOR)) + rt2400pci_config_promisc(rt2x00dev, 1); + + /* + * Enable beacon config + */ + rt2x00_register_read(rt2x00dev, BCNCSR1, ®); + rt2x00_set_field32(®, BCNCSR1_PRELOAD, + PREAMBLE + get_duration(IEEE80211_HEADER, 2)); + rt2x00_register_write(rt2x00dev, BCNCSR1, reg); + + /* + * Enable synchronisation. + */ + rt2x00_register_read(rt2x00dev, CSR12, ®); + rt2x00_set_field32(®, CSR12_BEACON_INTERVAL, 100 * 16); + rt2x00_set_field32(®, CSR12_CFP_MAX_DURATION, 100 * 16); + rt2x00_register_write(rt2x00dev, CSR12, reg); + + rt2x00_register_read(rt2x00dev, CSR14, ®); + if (GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED)) { + rt2x00_set_field32(®, CSR14_TSF_COUNT, 1); + rt2x00_set_field32(®, CSR14_TBCN, 1); + } + + rt2x00_set_field32(®, CSR14_BEACON_GEN, 0); + if (type == IEEE80211_IF_TYPE_IBSS || type == IEEE80211_IF_TYPE_AP) + rt2x00_set_field32(®, CSR14_TSF_SYNC, 2); + else if (type == IEEE80211_IF_TYPE_STA) + rt2x00_set_field32(®, CSR14_TSF_SYNC, 1); + else if (GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED_MONITOR) && + !GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED)) + rt2x00_set_field32(®, CSR14_TSF_SYNC, 0); + + rt2x00_register_write(rt2x00dev, CSR14, reg); + + /* + * Change flags of enabled interfaces. + */ + if (type != IEEE80211_IF_TYPE_MNTR) { + if (GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED)) + SET_FLAG(rt2x00dev, INTERFACE_ENABLED); + else + CLEAR_FLAG(rt2x00dev, INTERFACE_ENABLED); + } else { + if (GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED_MONITOR)) + SET_FLAG(rt2x00dev, INTERFACE_ENABLED_MONITOR); + else + CLEAR_FLAG(rt2x00dev, INTERFACE_ENABLED_MONITOR); + } +} + +static void rt2400pci_config_channel(struct rt2x00_dev *rt2x00dev, + int value, int channel, int freq, int txpower) +{ + u32 rf1 = rt2x00dev->rf1; + u32 rf2 = value; + u32 rf3 = rt2x00dev->rf3; + + /* + * Only continue when there is something to be done. + */ + if (channel == rt2x00dev->rx_status.channel) + return; + + /* + * Switch on tuning bits. + */ + rt2x00_set_field32(&rf1, RF1_TUNER, 1); + rt2x00_set_field32(&rf3, RF3_TUNER, 1); + + INFO("Switching channel. RF1: 0x%08x, RF2: 0x%08x, RF3: 0x%08x.\n", + rf1, rf2, rf3); + + rt2x00_rf_write(rt2x00dev, rf1); + rt2x00_rf_write(rt2x00dev, rf2); + rt2x00_rf_write(rt2x00dev, rf3); + + /* + * RF2420 chipset don't need any additional actions. + */ + if (rt2x00_rf(&rt2x00dev->chip, RF2420)) + return; + + /* + * For the RT2421 chipsets we need to write an invalid + * reference clock rate to activate auto_tune. + * After that we set the value back to the correct channel. + */ + rt2x00_rf_write(rt2x00dev, rf1); + rt2x00_rf_write(rt2x00dev, 0x000c2a32); + rt2x00_rf_write(rt2x00dev, rf3); + + msleep(1); + + rt2x00_rf_write(rt2x00dev, rf1); + rt2x00_rf_write(rt2x00dev, rf2); + rt2x00_rf_write(rt2x00dev, rf3); + + msleep(1); + + /* + * Switch off tuning bits. + */ + rt2x00_set_field32(&rf1, RF1_TUNER, 0); + rt2x00_set_field32(&rf3, RF3_TUNER, 0); + + rt2x00_rf_write(rt2x00dev, rf1); + rt2x00_rf_write(rt2x00dev, rf3); + + /* + * Update active info for RX. + */ + rt2x00dev->rx_status.freq = freq; + rt2x00dev->rx_status.channel = channel; + + /* + * Update rf fields + */ + rt2x00dev->rf1 = rf1; + rt2x00dev->rf2 = rf2; + rt2x00dev->rf3 = rf3; + + /* + * Clear false CRC during channel switch. + */ + rt2x00_register_read(rt2x00dev, CNT0, &rf1); +} + +static void rt2400pci_config_txpower(struct rt2x00_dev *rt2x00dev, int txpower) +{ + txpower = TXPOWER_TO_DEV(txpower); + + /* + * Only continue when there is something to be done. + */ + if (txpower == rt2x00dev->tx_power) + return; + + rt2x00_bbp_write(rt2x00dev, 3, txpower); + + rt2x00dev->tx_power = txpower; +} + +static void rt2400pci_config_antenna(struct rt2x00_dev *rt2x00dev, + int antenna_tx, int antenna_rx) +{ + u8 reg_rx; + u8 reg_tx; + + /* + * Only continue when there is something to be done. + */ + if (rt2x00dev->rx_status.antenna == antenna_rx) + return; + + rt2x00_bbp_read(rt2x00dev, 4, ®_rx); + rt2x00_bbp_read(rt2x00dev, 1, ®_tx); + + /* + * Clear current config antenna bits. + */ + reg_rx &= ~0x06; + reg_tx &= ~0x03; + + /* + * Configure the TX antenna. + */ + if (antenna_tx == 0) /* Diversity. */ + reg_tx |= 0x01; + else if (antenna_tx == 1) /* TX: Antenna A */ + reg_tx |= 0x00; + else if (antenna_tx == 2) /* TX: Antenna B */ + reg_tx |= 0x02; + + /* + * Configure the RX antenna. + */ + if (antenna_rx == 0) /* Diversity. */ + reg_rx |= 0x02; + else if (antenna_rx == 1) /* RX: Antenna A */ + reg_rx |= 0x00; + else if (antenna_rx == 2) /* RX: Antenna B */ + reg_rx |= 0x04; + + rt2x00_bbp_write(rt2x00dev, 4, reg_rx); + rt2x00_bbp_write(rt2x00dev, 1, reg_tx); + + /* + * Update active info for RX. + */ + rt2x00dev->rx_status.antenna = antenna_rx; +} + +static void rt2400pci_config_cw(struct rt2x00_dev *rt2x00dev, + struct ieee80211_tx_queue_params *params) +{ + u32 reg; + + rt2x00_register_read(rt2x00dev, CSR11, ®); + rt2x00_set_field32(®, CSR11_CWMIN, params->cw_min); + rt2x00_set_field32(®, CSR11_CWMAX, params->cw_max); + rt2x00_register_write(rt2x00dev, CSR11, reg); +} + +static void rt2400pci_config_duration(struct rt2x00_dev *rt2x00dev, + int short_slot_time) +{ + u32 reg; + + rt2x00_register_read(rt2x00dev, CSR11, ®); + rt2x00_set_field32(®, CSR11_SLOT_TIME, + short_slot_time ? SHORT_SLOT_TIME : SLOT_TIME); + rt2x00_register_write(rt2x00dev, CSR11, reg); + + rt2x00_register_read(rt2x00dev, CSR18, ®); + rt2x00_set_field32(®, CSR18_SIFS, SIFS); + rt2x00_set_field32(®, CSR18_PIFS, + short_slot_time ? SHORT_PIFS : PIFS); + rt2x00_register_write(rt2x00dev, CSR18, reg); + + rt2x00_register_read(rt2x00dev, CSR19, ®); + rt2x00_set_field32(®, CSR19_DIFS, + short_slot_time ? SHORT_DIFS : DIFS); + rt2x00_set_field32(®, CSR19_EIFS, EIFS); + rt2x00_register_write(rt2x00dev, CSR19, reg); + + rt2x00_register_read(rt2x00dev, TXCSR1, ®); + rt2x00_set_field32(®, TXCSR1_TSF_OFFSET, IEEE80211_HEADER); + rt2x00_set_field32(®, TXCSR1_AUTORESPONDER, 1); + rt2x00_register_write(rt2x00dev, TXCSR1, reg); +} + +static void rt2400pci_config_rate(struct rt2x00_dev *rt2x00dev, const int rate) +{ + struct ieee80211_conf *conf = &rt2x00dev->hw->conf; + u32 reg; + u32 value; + u32 preamble; + + preamble = DEVICE_GET_RATE_FIELD(rate, PREAMBLE) + ? SHORT_PREAMBLE : PREAMBLE; + + reg = DEVICE_GET_RATE_FIELD(rate, RATEMASK) & DEV_BASIC_RATE; + rt2x00_register_write(rt2x00dev, ARCSR1, reg); + + rt2x00_register_read(rt2x00dev, TXCSR1, ®); + value = ((conf->flags & IEEE80211_CONF_SHORT_SLOT_TIME) ? + SHORT_DIFS : DIFS) + + PLCP + preamble + get_duration(ACK_SIZE, 10); + rt2x00_set_field32(®, TXCSR1_ACK_TIMEOUT, value); + value = SIFS + PLCP + preamble + get_duration(ACK_SIZE, 10); + rt2x00_set_field32(®, TXCSR1_ACK_CONSUME_TIME, value); + rt2x00_register_write(rt2x00dev, TXCSR1, reg); + + preamble = DEVICE_GET_RATE_FIELD(rate, PREAMBLE) ? 0x08 : 0x00; + + rt2x00_register_write(rt2x00dev, ARCSR2, 0x00700400 | preamble); + rt2x00_register_write(rt2x00dev, ARCSR3, 0x00380401 | preamble); + rt2x00_register_write(rt2x00dev, ARCSR4, 0x00150402 | preamble); + rt2x00_register_write(rt2x00dev, ARCSR5, 0x000b8403 | preamble); +} + +static void rt2400pci_config_phymode(struct rt2x00_dev *rt2x00dev, + const int phymode) +{ + struct ieee80211_hw_mode *mode; + struct ieee80211_rate *rate; + + /* + * Only continue when there is something to be done. + */ + if (rt2x00dev->rx_status.phymode == phymode) + return; + + rt2x00dev->curr_hwmode = HWMODE_B; + + mode = &rt2x00dev->hwmodes[rt2x00dev->curr_hwmode]; + rate = &mode->rates[mode->num_rates - 1]; + + rt2400pci_config_rate(rt2x00dev, rate->val2); + + /* + * Update physical mode for rx ring. + */ + rt2x00dev->rx_status.phymode = phymode; +} + +static void rt2400pci_config_mac_addr(struct rt2x00_dev *rt2x00dev, u8 *addr) +{ + u32 reg[2]; + + memset(®, 0, sizeof(reg)); + memcpy(®, addr, ETH_ALEN); + + /* + * The MAC address is passed to us as an array of bytes, + * that array is little endian, so no need for byte ordering. + */ + rt2x00_register_multiwrite(rt2x00dev, CSR3, ®, sizeof(reg)); +} + +/* + * Link tuning + */ +static void rt2400pci_link_tuner(struct work_struct *work) +{ + struct rt2x00_dev *rt2x00dev = + container_of(work, struct rt2x00_dev, link.work.work); + u8 reg; + char false_cca_delta; + + /* + * Don't perform any tuning when it is disabled + * in the EEPROM. + */ + if (GET_FLAG(rt2x00dev, CONFIG_DISABLE_LINK_TUNING)) + return; + + /* + * Read false CCA counter. + */ + rt2x00_bbp_read(rt2x00dev, 39, ®); + + /* + * Determine difference with previous CCA counter. + */ + false_cca_delta = reg - rt2x00dev->false_cca; + rt2x00dev->false_cca = reg; + + /* + * Check if the difference is higher than the + * threshold and if so, tune the link. + */ + if (false_cca_delta >= 8) { + /* + * Read and update RX AGC VGC. + */ + rt2x00_bbp_read(rt2x00dev, 13, ®); + reg += 2; + if (reg < 0x20) + rt2x00_bbp_write(rt2x00dev, 13, reg); + } + + queue_delayed_work(rt2x00dev->workqueue, &rt2x00dev->link.work, + LINK_TUNE_INTERVAL); +} + +/* + * LED functions. + */ +static void rt2400pci_enable_led(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + rt2x00_register_read(rt2x00dev, LEDCSR, ®); + + rt2x00_set_field32(®, LEDCSR_ON_PERIOD, 70); + rt2x00_set_field32(®, LEDCSR_OFF_PERIOD, 30); + + if (rt2x00dev->led_mode == LED_MODE_TXRX_ACTIVITY) { + rt2x00_set_field32(®, LEDCSR_LINK, 1); + rt2x00_set_field32(®, LEDCSR_ACTIVITY, 0); + } else if (rt2x00dev->led_mode == LED_MODE_ASUS) { + rt2x00_set_field32(®, LEDCSR_LINK, 0); + rt2x00_set_field32(®, LEDCSR_ACTIVITY, 1); + } else { + rt2x00_set_field32(®, LEDCSR_LINK, 1); + rt2x00_set_field32(®, LEDCSR_ACTIVITY, 1); + } + + rt2x00_register_write(rt2x00dev, LEDCSR, reg); +} + +static void rt2400pci_disable_led(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + rt2x00_register_read(rt2x00dev, LEDCSR, ®); + rt2x00_set_field32(®, LEDCSR_LINK, 0); + rt2x00_set_field32(®, LEDCSR_ACTIVITY, 0); + rt2x00_register_write(rt2x00dev, LEDCSR, reg); +} + +static void rt2400pci_activity_led(struct rt2x00_dev *rt2x00dev, char activity) +{ + u32 reg; + + if (rt2x00dev->led_mode != LED_MODE_TXRX_ACTIVITY) + return; + + rt2x00_register_read(rt2x00dev, LEDCSR, ®); + rt2x00_set_field32(®, LEDCSR_ACTIVITY, activity); + rt2x00_register_write(rt2x00dev, LEDCSR, reg); +} + +/* + * Device state switch. + * This will put the device to sleep, or awake it. + */ +static int rt2400pci_set_state(struct rt2x00_dev *rt2x00dev, + enum dev_state state) +{ + u32 reg; + unsigned int i; + char put_to_sleep; + char bbp_state; + char rf_state; + + put_to_sleep = (state != STATE_AWAKE); + + rt2x00_register_read(rt2x00dev, PWRCSR1, ®); + rt2x00_set_field32(®, PWRCSR1_SET_STATE, 1); + rt2x00_set_field32(®, PWRCSR1_BBP_DESIRE_STATE, state); + rt2x00_set_field32(®, PWRCSR1_RF_DESIRE_STATE, state); + rt2x00_set_field32(®, PWRCSR1_PUT_TO_SLEEP, put_to_sleep); + rt2x00_register_write(rt2x00dev, PWRCSR1, reg); + + /* + * Device is not guaranteed to be in the requested state yet. + * We must wait until the register indicates that the + * device has entered the correct state. + */ + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2x00_register_read(rt2x00dev, PWRCSR1, ®); + bbp_state = rt2x00_get_field32(reg, PWRCSR1_BBP_CURR_STATE); + rf_state = rt2x00_get_field32(reg, PWRCSR1_RF_CURR_STATE); + if (bbp_state == state && rf_state == state) + return 0; + msleep(10); + } + + NOTICE("Device failed to enter state %d, " + "current device state: bbp %d and rf %d.\n", + state, bbp_state, rf_state); + + return -EBUSY; +} + +/* + * Initialization functions. + */ +static void rt2400pci_init_rxring(struct rt2x00_dev *rt2x00dev, + enum ring_index ring_type) +{ + struct data_ring *ring = &rt2x00dev->ring[ring_type]; + struct data_desc *rxd; + unsigned int i; + u32 word; + + memset(ring->data_addr, 0x00, rt2x00_get_ring_size(ring)); + + ring->type = ring_type; + + for (i = 0; i < ring->stats.limit; i++) { + rxd = ring->entry[i].priv; + + rt2x00_desc_read(rxd, 2, &word); + rt2x00_set_field32(&word, RXD_W2_BUFFER_LENGTH, + ring->data_size); + rt2x00_desc_write(rxd, 2, word); + + rt2x00_desc_read(rxd, 1, &word); + rt2x00_set_field32(&word, RXD_W1_BUFFER_ADDRESS, + ring->entry[i].data_dma); + rt2x00_desc_write(rxd, 1, word); + + rt2x00_desc_read(rxd, 0, &word); + rt2x00_set_field32(&word, RXD_W0_OWNER_NIC, 1); + rt2x00_desc_write(rxd, 0, word); + } + + rt2x00_ring_index_clear(ring); +} + +static void rt2400pci_init_txring(struct rt2x00_dev *rt2x00dev, + enum ring_index ring_type) +{ + struct data_ring *ring = &rt2x00dev->ring[ring_type]; + struct data_desc *txd; + unsigned int i; + u32 word; + + memset(ring->data_addr, 0x00, rt2x00_get_ring_size(ring)); + + ring->type = ring_type; + + for (i = 0; i < ring->stats.limit; i++) { + txd = ring->entry[i].priv; + + rt2x00_desc_read(txd, 1, &word); + rt2x00_set_field32(&word, TXD_W1_BUFFER_ADDRESS, + ring->entry[i].data_dma); + rt2x00_desc_write(txd, 1, word); + + rt2x00_desc_read(txd, 2, &word); + rt2x00_set_field32(&word, TXD_W2_BUFFER_LENGTH, + ring->data_size); + rt2x00_desc_write(txd, 2, word); + + rt2x00_desc_read(txd, 0, &word); + rt2x00_set_field32(&word, TXD_W0_VALID, 0); + rt2x00_set_field32(&word, TXD_W0_OWNER_NIC, 0); + rt2x00_desc_write(txd, 0, word); + } + + rt2x00_ring_index_clear(ring); +} + +static int rt2400pci_init_rings(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + /* + * Initialize rings. + */ + rt2400pci_init_rxring(rt2x00dev, RING_RX); + rt2400pci_init_txring(rt2x00dev, RING_TX); + rt2400pci_init_txring(rt2x00dev, RING_ATIM); + rt2400pci_init_txring(rt2x00dev, RING_PRIO); + rt2400pci_init_txring(rt2x00dev, RING_BEACON); + + /* + * Initialize registers. + */ + reg = 0; + rt2x00_set_field32(®, TXCSR2_TXD_SIZE, + rt2x00dev->ring[RING_TX].desc_size); + rt2x00_set_field32(®, TXCSR2_NUM_TXD, + rt2x00dev->ring[RING_TX].stats.limit); + rt2x00_set_field32(®, TXCSR2_NUM_ATIM, + rt2x00dev->ring[RING_ATIM].stats.limit); + rt2x00_set_field32(®, TXCSR2_NUM_PRIO, + rt2x00dev->ring[RING_PRIO].stats.limit); + rt2x00_register_write(rt2x00dev, TXCSR2, reg); + + reg = 0; + rt2x00_set_field32(®, TXCSR3_TX_RING_REGISTER, + rt2x00dev->ring[RING_TX].data_dma); + rt2x00_register_write(rt2x00dev, TXCSR3, reg); + + reg = 0; + rt2x00_set_field32(®, TXCSR5_PRIO_RING_REGISTER, + rt2x00dev->ring[RING_PRIO].data_dma); + rt2x00_register_write(rt2x00dev, TXCSR5, reg); + + reg = 0; + rt2x00_set_field32(®, TXCSR4_ATIM_RING_REGISTER, + rt2x00dev->ring[RING_ATIM].data_dma); + rt2x00_register_write(rt2x00dev, TXCSR4, reg); + + reg = 0; + rt2x00_set_field32(®, TXCSR6_BEACON_RING_REGISTER, + rt2x00dev->ring[RING_BEACON].data_dma); + rt2x00_register_write(rt2x00dev, TXCSR6, reg); + + reg = 0; + rt2x00_set_field32(®, RXCSR1_RXD_SIZE, + rt2x00dev->ring[RING_RX].desc_size); + rt2x00_set_field32(®, RXCSR1_NUM_RXD, + rt2x00dev->ring[RING_RX].stats.limit); + rt2x00_register_write(rt2x00dev, RXCSR1, reg); + + reg = 0; + rt2x00_set_field32(®, RXCSR2_RX_RING_REGISTER, + rt2x00dev->ring[RING_RX].data_dma); + rt2x00_register_write(rt2x00dev, RXCSR2, reg); + + return 0; +} + +static int rt2400pci_init_registers(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + if (rt2400pci_set_state(rt2x00dev, STATE_AWAKE)) + return -EBUSY; + + rt2x00_register_write(rt2x00dev, PWRCSR0, 0x3f3b3100); + + rt2x00_register_write(rt2x00dev, PSCSR0, 0x00020002); + rt2x00_register_write(rt2x00dev, PSCSR1, 0x00000002); + rt2x00_register_write(rt2x00dev, PSCSR2, 0x00020002); + rt2x00_register_write(rt2x00dev, PSCSR3, 0x00000002); + + rt2x00_register_read(rt2x00dev, TIMECSR, ®); + rt2x00_set_field32(®, TIMECSR_US_COUNT, 33); + rt2x00_set_field32(®, TIMECSR_US_64_COUNT, 63); + rt2x00_set_field32(®, TIMECSR_BEACON_EXPECT, 0); + rt2x00_register_write(rt2x00dev, TIMECSR, reg); + + rt2x00_register_read(rt2x00dev, CSR9, ®); + rt2x00_set_field32(®, CSR9_MAX_FRAME_UNIT, + (rt2x00dev->ring[RING_RX].data_size / 128)); + rt2x00_register_write(rt2x00dev, CSR9, reg); + + rt2x00_register_write(rt2x00dev, CNT3, 0x3f080000); + + rt2x00_register_write(rt2x00dev, MACCSR0, 0x00217223); + rt2x00_register_write(rt2x00dev, MACCSR1, 0x00235518); + + rt2x00_register_read(rt2x00dev, MACCSR2, ®); + rt2x00_set_field32(®, MACCSR2_DELAY, 64); + rt2x00_register_write(rt2x00dev, MACCSR2, reg); + + rt2x00_register_read(rt2x00dev, RXCSR3, ®); + /* + * Tx power. + */ + rt2x00_set_field32(®, RXCSR3_BBP_ID0, 3); + rt2x00_set_field32(®, RXCSR3_BBP_ID0_VALID, 1); + /* + * Signal. + */ + rt2x00_set_field32(®, RXCSR3_BBP_ID1, 32); + rt2x00_set_field32(®, RXCSR3_BBP_ID1_VALID, 1); + /* + * Rssi. + */ + rt2x00_set_field32(®, RXCSR3_BBP_ID2, 36); + rt2x00_set_field32(®, RXCSR3_BBP_ID2_VALID, 1); + rt2x00_register_write(rt2x00dev, RXCSR3, reg); + + rt2x00_register_read(rt2x00dev, RALINKCSR, ®); + rt2x00_set_field32(®, RALINKCSR_AR_BBP_DATA0, 17); + rt2x00_set_field32(®, RALINKCSR_AR_BBP_ID0, 154); + rt2x00_set_field32(®, RALINKCSR_AR_BBP_DATA1, 0); + rt2x00_set_field32(®, RALINKCSR_AR_BBP_ID1, 154); + rt2x00_register_write(rt2x00dev, RALINKCSR, reg); + + rt2x00_register_read(rt2x00dev, CSR1, ®); + rt2x00_set_field32(®, CSR1_SOFT_RESET, 1); + rt2x00_set_field32(®, CSR1_BBP_RESET, 0); + rt2x00_set_field32(®, CSR1_HOST_READY, 0); + rt2x00_register_write(rt2x00dev, CSR1, reg); + + rt2x00_register_read(rt2x00dev, CSR1, ®); + rt2x00_set_field32(®, CSR1_SOFT_RESET, 0); + rt2x00_set_field32(®, CSR1_HOST_READY, 1); + rt2x00_register_write(rt2x00dev, CSR1, reg); + + /* + * We must clear the FCS and FIFO error count. + * These registers are cleared on read, + * so we may pass a useless variable to store the value. + */ + rt2x00_register_read(rt2x00dev, CNT0, ®); + rt2x00_register_read(rt2x00dev, CNT4, ®); + + return 0; +} + +static int rt2400pci_init_bbp(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + u16 eeprom; + u8 reg_id; + u8 value; + + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2x00_bbp_read(rt2x00dev, 0, &value); + if ((value != 0xff) && (value != 0x00)) + goto continue_csr_init; + NOTICE("Waiting for BBP register.\n"); + udelay(REGISTER_BUSY_DELAY); + } + + ERROR("BBP register access failed, aborting.\n"); + return -EACCES; + +continue_csr_init: + rt2x00_bbp_write(rt2x00dev, 1, 0x00); + rt2x00_bbp_write(rt2x00dev, 3, 0x27); + rt2x00_bbp_write(rt2x00dev, 4, 0x08); + rt2x00_bbp_write(rt2x00dev, 10, 0x0f); + rt2x00_bbp_write(rt2x00dev, 13, 0x08); + rt2x00_bbp_write(rt2x00dev, 15, 0x72); + rt2x00_bbp_write(rt2x00dev, 16, 0x74); + rt2x00_bbp_write(rt2x00dev, 17, 0x20); + rt2x00_bbp_write(rt2x00dev, 18, 0x72); + rt2x00_bbp_write(rt2x00dev, 19, 0x0b); + rt2x00_bbp_write(rt2x00dev, 20, 0x00); + rt2x00_bbp_write(rt2x00dev, 28, 0x11); + rt2x00_bbp_write(rt2x00dev, 29, 0x04); + rt2x00_bbp_write(rt2x00dev, 30, 0x21); + rt2x00_bbp_write(rt2x00dev, 31, 0x00); + + DEBUG("Start initialization from EEPROM...\n"); + for (i = 0; i < EEPROM_BBP_SIZE; i++) { + rt2x00_eeprom_read(rt2x00dev, EEPROM_BBP_START + i, &eeprom); + + if (eeprom != 0xffff && eeprom != 0x0000) { + reg_id = rt2x00_get_field16(eeprom, EEPROM_BBP_REG_ID); + value = rt2x00_get_field16(eeprom, EEPROM_BBP_VALUE); + DEBUG("BBP: 0x%02x, value: 0x%02x.\n", reg_id, value); + rt2x00_bbp_write(rt2x00dev, reg_id, value); + } + } + DEBUG("...End initialization from EEPROM.\n"); + + return 0; +} + +/* + * Radio control functions. + */ +static void rt2400pci_toggle_rx(struct rt2x00_dev *rt2x00dev, int enable) +{ + u32 reg; + + rt2x00_register_read(rt2x00dev, RXCSR0, ®); + rt2x00_set_field32(®, RXCSR0_DISABLE_RX, !enable); + rt2x00_register_write(rt2x00dev, RXCSR0, reg); +} + +static int rt2400pci_enable_radio(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + /* + * Initialize all registers. + */ + if (rt2400pci_init_rings(rt2x00dev) || + rt2400pci_init_registers(rt2x00dev) || + rt2400pci_init_bbp(rt2x00dev)) { + ERROR("Register initialization failed.\n"); + return -EIO; + } + + /* + * Clear interrupts. + */ + rt2x00_register_read(rt2x00dev, CSR7, ®); + rt2x00_register_write(rt2x00dev, CSR7, reg); + + /* + * Enable interrupts. + */ + rt2x00_register_read(rt2x00dev, CSR8, ®); + rt2x00_set_field32(®, CSR8_TBCN_EXPIRE, 0); + rt2x00_set_field32(®, CSR8_TXDONE_TXRING, 0); + rt2x00_set_field32(®, CSR8_TXDONE_ATIMRING, 0); + rt2x00_set_field32(®, CSR8_TXDONE_PRIORING, 0); + rt2x00_set_field32(®, CSR8_RXDONE, 0); + rt2x00_register_write(rt2x00dev, CSR8, reg); + + /* + * Enable LED + */ + rt2400pci_enable_led(rt2x00dev); + + return 0; +} + +static void rt2400pci_disable_radio(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + /* + * Disable LED + */ + rt2400pci_disable_led(rt2x00dev); + + rt2x00_register_write(rt2x00dev, PWRCSR0, 0); + + /* + * Disable synchronisation. + */ + rt2x00_register_write(rt2x00dev, CSR14, 0); + + /* + * Cancel RX and TX. + */ + rt2x00_register_read(rt2x00dev, TXCSR0, ®); + rt2x00_set_field32(®, TXCSR0_ABORT, 1); + rt2x00_register_write(rt2x00dev, TXCSR0, reg); + + /* + * Disable interrupts. + */ + rt2x00_register_read(rt2x00dev, CSR8, ®); + rt2x00_set_field32(®, CSR8_TBCN_EXPIRE, 1); + rt2x00_set_field32(®, CSR8_TXDONE_TXRING, 1); + rt2x00_set_field32(®, CSR8_TXDONE_ATIMRING, 1); + rt2x00_set_field32(®, CSR8_TXDONE_PRIORING, 1); + rt2x00_set_field32(®, CSR8_RXDONE, 1); + rt2x00_register_write(rt2x00dev, CSR8, reg); +} + +/* + * TX descriptor initialization + */ +static void rt2400pci_write_tx_desc(struct rt2x00_dev *rt2x00dev, + struct data_entry *entry, struct data_desc *txd, + struct data_entry_desc *desc, struct ieee80211_hdr *ieee80211hdr, + unsigned int length, struct ieee80211_tx_control *control) +{ + u32 word; + + /* + * Create the signal and service values, the values should + * be stored as if it was a BBP register with the busy bit + * and register number. + */ + desc->signal |= 0x8500; + desc->service |= 0x8600; + desc->length_high |= 0x8700; + desc->length_low |= 0x8800; + + /* + * Start writing the descriptor words. + */ + rt2x00_desc_read(txd, 2, &word); + rt2x00_set_field32(&word, TXD_W2_DATABYTE_COUNT, length); + rt2x00_desc_write(txd, 2, word); + + rt2x00_desc_read(txd, 3, &word); + rt2x00_set_field32(&word, TXD_W3_PLCP_SIGNAL, desc->signal); + rt2x00_set_field32(&word, TXD_W3_PLCP_SERVICE, desc->service); + rt2x00_desc_write(txd, 3, word); + + rt2x00_desc_read(txd, 4, &word); + rt2x00_set_field32(&word, TXD_W4_PLCP_LENGTH_LOW, desc->length_low); + rt2x00_set_field32(&word, TXD_W4_PLCP_LENGTH_HIGH, desc->length_high); + rt2x00_desc_write(txd, 4, word); + + rt2x00_desc_read(txd, 0, &word); + rt2x00_set_field32(&word, TXD_W0_OWNER_NIC, 1); + rt2x00_set_field32(&word, TXD_W0_VALID, 1); + rt2x00_set_field32(&word, TXD_W0_MORE_FRAG, + !!(entry->reg & ENTRY_TXD_MORE_FRAG)); + rt2x00_set_field32(&word, TXD_W0_ACK, + !!(entry->reg & ENTRY_TXD_REQ_ACK)); + rt2x00_set_field32(&word, TXD_W0_TIMESTAMP, + !!(entry->reg & ENTRY_TXD_REQ_TIMESTAMP)); + rt2x00_set_field32(&word, TXD_W0_RTS, + !!(entry->reg & ENTRY_TXD_RTS_FRAME)); + rt2x00_set_field32(&word, TXD_W0_IFS, desc->ifs); + rt2x00_set_field32(&word, TXD_W0_RETRY_MODE, 0); + rt2x00_desc_write(txd, 0, word); +} + +/* + * TX data initialization + */ +static void rt2400pci_kick_tx_queue(struct rt2x00_dev *rt2x00dev, int queue) +{ + u32 reg; + + rt2x00_register_read(rt2x00dev, TXCSR0, ®); + if (queue == IEEE80211_TX_QUEUE_DATA0) + rt2x00_set_field32(®, TXCSR0_KICK_PRIO, 1); + else if (queue == IEEE80211_TX_QUEUE_DATA1) + rt2x00_set_field32(®, TXCSR0_KICK_TX, 1); + else if (queue == IEEE80211_TX_QUEUE_AFTER_BEACON) + rt2x00_set_field32(®, TXCSR0_KICK_ATIM, 1); + rt2x00_register_write(rt2x00dev, TXCSR0, reg); +} + +static void rt2400pci_kick_beacon_gen(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + rt2x00_register_read(rt2x00dev, CSR14, ®); + if (!rt2x00_get_field32(reg, CSR14_BEACON_GEN)) { + rt2x00_set_field32(®, CSR14_BEACON_GEN, 1); + rt2x00_register_write(rt2x00dev, CSR14, reg); + } +} + +/* + * Interrupt functions. + */ +static void rt2400pci_rxdone(struct rt2x00_dev *rt2x00dev, int queue) +{ + struct data_ring *ring = &rt2x00dev->ring[queue]; + struct data_entry *entry; + struct data_desc *rxd; + u32 word0; + u32 word2; + int signal; + int rssi; + u16 size; + + while (1) { + entry = rt2x00_get_data_entry(ring); + rxd = entry->priv; + rt2x00_desc_read(rxd, 0, &word0); + rt2x00_desc_read(rxd, 2, &word2); + + if (rt2x00_get_field32(word0, RXD_W0_OWNER_NIC)) + break; + + /* + * TODO: Don't we need to keep statistics + * updated about events like CRC and physical errors? + */ + if (rt2x00_get_field32(word0, RXD_W0_CRC) || + rt2x00_get_field32(word0, RXD_W0_PHYSICAL_ERROR)) + goto skip_entry; + + /* + * Obtain the status about this packet. + */ + size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT); + signal = rt2x00_get_field32(word2, RXD_W2_SIGNAL); + rssi = rt2x00_get_field32(word2, RXD_W2_RSSI); + + /* + * Send the packet to upper layer. + */ + rt2x00lib_rxdone(entry, entry->data_addr, size, + signal, rssi, 0); + +skip_entry: + if (GET_FLAG(ring->rt2x00dev, DEVICE_ENABLED_RADIO)) { + rt2x00_set_field32(&word0, RXD_W0_OWNER_NIC, 1); + rt2x00_desc_write(rxd, 0, word0); + } + + rt2x00_ring_index_inc(ring); + } + + /* + * Update LED. + */ + rt2400pci_activity_led(rt2x00dev, 0); +} + +static void rt2400pci_txdone(struct rt2x00_dev *rt2x00dev, const int queue) +{ + struct data_ring *ring = &rt2x00dev->ring[queue]; + struct data_entry *entry; + struct data_desc *txd; + u32 word; + int tx_status; + int retry; + + while (!rt2x00_ring_empty(ring)) { + entry = rt2x00_get_data_entry_done(ring); + txd = entry->priv; + rt2x00_desc_read(txd, 0, &word); + + if (rt2x00_get_field32(word, TXD_W0_OWNER_NIC) || + !rt2x00_get_field32(word, TXD_W0_VALID)) + break; + + /* + * Obtain the status about this packet. + */ + tx_status = rt2x00_get_field32(word, TXD_W0_RESULT); + retry = rt2x00_get_field32(word, TXD_W0_RETRY_COUNT); + + rt2x00lib_txdone(entry, tx_status, retry); + + /* + * Make this entry available for reuse. + */ + entry->reg = 0; + rt2x00_set_field32(&word, TXD_W0_VALID, 0); + rt2x00_desc_write(txd, 0, word); + rt2x00_ring_index_done_inc(ring); + } + + /* + * If the data ring was full before the txdone handler + * we must make sure the packet queue in the mac80211 stack + * is reenabled when the txdone handler has finished. + */ + entry = ring->entry; + if (!rt2x00_ring_full(ring)) + ieee80211_wake_queue(rt2x00dev->hw, + entry->tx_status.control.queue); +} + +static irqreturn_t rt2400pci_interrupt(int irq, void *dev_instance) +{ + struct rt2x00_dev *rt2x00dev = dev_instance; + u32 reg; + + /* + * Get the interrupt sources & saved to local variable. + * Write register value back to clear pending interrupts. + */ + rt2x00_register_read(rt2x00dev, CSR7, ®); + rt2x00_register_write(rt2x00dev, CSR7, reg); + + if (!reg) + return IRQ_NONE; + + if (!GET_FLAG(rt2x00dev, DEVICE_ENABLED_RADIO)) + return IRQ_HANDLED; + + /* + * Handle interrupts, walk through all bits + * and run the tasks, the bits are checked in order of + * priority. + */ + + /* + * 1 - Beacon timer expired interrupt. + */ + if (rt2x00_get_field32(reg, CSR7_TBCN_EXPIRE)) + rt2x00pci_beacondone(rt2x00dev, RING_BEACON); + + /* + * 2 - Rx ring done interrupt. + * Enable the TXRX activity led. + */ + if (rt2x00_get_field32(reg, CSR7_RXDONE)) { + rt2400pci_rxdone(rt2x00dev, RING_RX); + rt2400pci_activity_led(rt2x00dev, 1); + } + + /* + * 3 - Atim ring transmit done interrupt. + */ + if (rt2x00_get_field32(reg, CSR7_TXDONE_ATIMRING)) + rt2400pci_txdone(rt2x00dev, RING_ATIM); + + /* + * 4 - Priority ring transmit done interrupt. + */ + if (rt2x00_get_field32(reg, CSR7_TXDONE_PRIORING)) + rt2400pci_txdone(rt2x00dev, RING_PRIO); + + /* + * 5 - Tx ring transmit done interrupt. + */ + if (rt2x00_get_field32(reg, CSR7_TXDONE_TXRING)) + rt2400pci_txdone(rt2x00dev, RING_TX); + + return IRQ_HANDLED; +} + +/* + * Device initialization functions. + */ +static int rt2400pci_alloc_eeprom(struct rt2x00_dev *rt2x00dev) +{ + struct eeprom_93cx6 eeprom; + u32 reg; + + /* + * Allocate the eeprom memory, check the eeprom width + * and copy the entire eeprom into this allocated memory. + */ + rt2x00dev->eeprom = kzalloc(EEPROM_SIZE, GFP_KERNEL); + if (!rt2x00dev->eeprom) + return -ENOMEM; + + rt2x00_register_read(rt2x00dev, CSR21, ®); + + eeprom.data = rt2x00dev; + eeprom.register_read = rt2400pci_eepromregister_read; + eeprom.register_write = rt2400pci_eepromregister_write; + eeprom.width = rt2x00_get_field32(reg, CSR21_TYPE_93C46) ? + PCI_EEPROM_WIDTH_93C46 : PCI_EEPROM_WIDTH_93C66; + eeprom.reg_data_in = 0; + eeprom.reg_data_out = 0; + eeprom.reg_data_clock = 0; + eeprom.reg_chip_select = 0; + + eeprom_93cx6_multiread(&eeprom, EEPROM_BASE, rt2x00dev->eeprom, + EEPROM_SIZE / sizeof(u16)); + + return 0; +} + +static int rt2400pci_init_eeprom(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + u16 value; + u16 eeprom; + + /* + * Read EEPROM word for configuration. + */ + rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &eeprom); + + /* + * Identify RF chipset. + */ + value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE); + rt2x00_register_read(rt2x00dev, CSR0, ®); + rt2x00_set_chip(&rt2x00dev->chip, RT2460, value, reg); + + if (!rt2x00_rf(&rt2x00dev->chip, RF2420) && + !rt2x00_rf(&rt2x00dev->chip, RF2421)) { + ERROR("Invalid RF chipset detected.\n"); + return -ENODEV; + } + + /* + * Identify default antenna configuration. + */ + rt2x00dev->hw->conf.antenna_sel_tx = rt2x00_get_field16(eeprom, + EEPROM_ANTENNA_TX_DEFAULT); + rt2x00dev->hw->conf.antenna_sel_rx = rt2x00_get_field16(eeprom, + EEPROM_ANTENNA_RX_DEFAULT); + + /* + * Store led mode, for correct led behaviour. + */ + rt2x00dev->led_mode = rt2x00_get_field16(eeprom, + EEPROM_ANTENNA_LED_MODE); + + /* + * Detect if this device has an hardware controlled radio. + */ + if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO)) + SET_FLAG(rt2x00dev, DEVICE_SUPPORT_HW_BUTTON); + + /* + * Check if the BBP tuning should be enabled. + */ + if (!rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RX_AGCVGC_TUNING)) + SET_FLAG(rt2x00dev, CONFIG_DISABLE_LINK_TUNING); + + return 0; +} + +/* + * RF value list for RF2420 & RF2421 + * Supports: 2.4 GHz + */ +static const u32 rf_vals_bg[] = { + 0x000c1fda, 0x000c1fee, 0x000c2002, 0x000c2016, 0x000c202a, + 0x000c203e, 0x000c2052, 0x000c2066, 0x000c207a, 0x000c208e, + 0x000c20a2, 0x000c20b6, 0x000c20ca, 0x000c20fa +}; + +static void rt2400pci_init_hw_mode(struct rt2x00_dev *rt2x00dev) +{ + struct hw_mode_spec *spec = &rt2x00dev->spec; + u8 *txpower; + unsigned int i; + + if (GET_FLAG(rt2x00dev, DEVICE_INITIALIZED_HW)) + return; + + /* + * Initialize all hw fields. + */ + rt2x00dev->hw->flags = IEEE80211_HW_HOST_GEN_BEACON | + IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | + IEEE80211_HW_WEP_INCLUDE_IV | + IEEE80211_HW_DATA_NULLFUNC_ACK | + IEEE80211_HW_NO_TKIP_WMM_HWACCEL | + IEEE80211_HW_MONITOR_DURING_OPER; + rt2x00dev->hw->extra_tx_headroom = 0; + rt2x00dev->hw->max_rssi = MAX_RX_SSI; + rt2x00dev->hw->max_noise = MAX_RX_NOISE; + rt2x00dev->hw->queues = RING_NUM_TX; + + /* + * This device supports ATIM + */ + SET_FLAG(rt2x00dev, DEVICE_SUPPORT_ATIM); + + /* + * Set device specific, but channel independent RF values. + */ + rt2x00dev->rf1 = 0x00022058; + if (rt2x00_rf(&rt2x00dev->chip, RF2420)) + rt2x00dev->rf3 = 0x00000111; + else + rt2x00dev->rf3 = 0x00000101; + + /* + * Convert tx_power array in eeprom. + */ + txpower = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_START); + for (i = 0; i < 14; i++) + txpower[i] = TXPOWER_FROM_DEV(txpower[i]); + + /* + * Initialize hw_mode information. + */ + spec->mac_addr = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); + spec->num_modes = 1; + spec->num_rates = 4; + spec->num_channels = 14; + spec->tx_power_a = NULL; + spec->tx_power_bg = txpower; + spec->tx_power_default = DEFAULT_TXPOWER; + spec->chan_val_a = NULL; + spec->chan_val_bg = rf_vals_bg; +} + +static int rt2400pci_init_hw(struct rt2x00_dev *rt2x00dev) +{ + int retval; + + rt2x00dev->rxd_size = RXD_DESC_SIZE; + rt2x00dev->txd_size = TXD_DESC_SIZE; + + /* + * Allocate eeprom data. + */ + retval = rt2400pci_alloc_eeprom(rt2x00dev); + if (retval) + return retval; + + retval = rt2400pci_init_eeprom(rt2x00dev); + if (retval) + return retval; + + /* + * Initialize hw specifications. + */ + rt2400pci_init_hw_mode(rt2x00dev); + + return 0; +} + +/* + * IEEE80211 stack callback functions. + */ +static int rt2400pci_get_stats(struct ieee80211_hw *hw, + struct ieee80211_low_level_stats *stats) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + u32 reg; + + /* + * Update FCS error count from register. + * The dot11ACKFailureCount, dot11RTSFailureCount and + * dot11RTSSuccessCount are updated in interrupt time. + */ + rt2x00_register_read(rt2x00dev, CNT0, ®); + rt2x00dev->low_level_stats.dot11FCSErrorCount += + rt2x00_get_field32(reg, CNT0_FCS_ERROR); + + memcpy(stats, &rt2x00dev->low_level_stats, sizeof(*stats)); + + return 0; +} + +static int rt2400pci_set_retry_limit(struct ieee80211_hw *hw, + u32 short_retry, u32 long_retry) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + u32 reg; + + rt2x00_register_read(rt2x00dev, CSR11, ®); + rt2x00_set_field32(®, CSR11_LONG_RETRY, long_retry); + rt2x00_set_field32(®, CSR11_SHORT_RETRY, short_retry); + rt2x00_register_write(rt2x00dev, CSR11, reg); + + return 0; +} + +static int rt2400pci_conf_tx(struct ieee80211_hw *hw, + int queue, const struct ieee80211_tx_queue_params *params) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct data_ring *ring = &rt2x00dev->ring[RING_PRIO]; + + /* + * We don't support variating cw_min and cw_max variables + * per queue. So by default we only configure the TX queue, + * and ignore all other configurations. + */ + if (queue != IEEE80211_TX_QUEUE_DATA0) + return -EINVAL; + + if (rt2x00lib_conf_tx(hw, queue, params)) + return -EINVAL; + + /* + * Write configuration to register. + */ + rt2400pci_config_cw(rt2x00dev, &ring->tx_params); + + return 0; +} + +static u64 rt2400pci_get_tsf(struct ieee80211_hw *hw) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + u64 tsf; + u32 reg; + + rt2x00_register_read(rt2x00dev, CSR17, ®); + tsf = (u64)rt2x00_get_field32(reg, CSR17_HIGH_TSFTIMER) << 32; + rt2x00_register_read(rt2x00dev, CSR16, ®); + tsf |= rt2x00_get_field32(reg, CSR16_LOW_TSFTIMER); + + return tsf; +} + +static void rt2400pci_reset_tsf(struct ieee80211_hw *hw) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + + rt2x00_register_write(rt2x00dev, CSR16, 0); + rt2x00_register_write(rt2x00dev, CSR17, 0); +} + +static int rt2400pci_tx_last_beacon(struct ieee80211_hw *hw) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + u32 reg; + + rt2x00_register_read(rt2x00dev, CSR15, ®); + return rt2x00_get_field32(reg, CSR15_BEACON_SENT); +} + +static const struct ieee80211_ops rt2400pci_mac80211_ops = { + .tx = rt2x00lib_tx, + .reset = rt2x00lib_reset, + .add_interface = rt2x00lib_add_interface, + .remove_interface = rt2x00lib_remove_interface, + .config = rt2x00lib_config, + .config_interface = rt2x00lib_config_interface, + .set_multicast_list = rt2x00lib_set_multicast_list, + .get_stats = rt2400pci_get_stats, + .set_retry_limit = rt2400pci_set_retry_limit, + .conf_tx = rt2400pci_conf_tx, + .get_tx_stats = rt2x00lib_get_tx_stats, + .get_tsf = rt2400pci_get_tsf, + .reset_tsf = rt2400pci_reset_tsf, + .beacon_update = rt2x00pci_beacon_update, + .tx_last_beacon = rt2400pci_tx_last_beacon, +}; + +static const struct rt2x00lib_ops rt2400pci_rt2x00_ops = { + .irq_handler = rt2400pci_interrupt, + .link_tuner = rt2400pci_link_tuner, + .init_hw = rt2400pci_init_hw, + .initialize = rt2x00pci_initialize, + .uninitialize = rt2x00pci_uninitialize, + .enable_radio = rt2400pci_enable_radio, + .disable_radio = rt2400pci_disable_radio, + .set_state = rt2400pci_set_state, + .toggle_rx = rt2400pci_toggle_rx, + .write_tx_desc = rt2400pci_write_tx_desc, + .write_tx_data = rt2x00pci_write_tx_data, + .kick_tx_queue = rt2400pci_kick_tx_queue, + .kick_beacon_gen = rt2400pci_kick_beacon_gen, + .config_type = rt2400pci_config_type, + .config_phymode = rt2400pci_config_phymode, + .config_channel = rt2400pci_config_channel, + .config_mac_addr = rt2400pci_config_mac_addr, + .config_bssid = rt2400pci_config_bssid, + .config_promisc = rt2400pci_config_promisc, + .config_txpower = rt2400pci_config_txpower, + .config_antenna = rt2400pci_config_antenna, + .config_duration = rt2400pci_config_duration, +}; + +static const struct rt2x00_ops rt2400pci_ops = { + .lib = &rt2400pci_rt2x00_ops, + .hw = &rt2400pci_mac80211_ops, +#ifdef CONFIG_RT2X00_DEBUGFS + .debugfs = &rt2400pci_rt2x00debug, +#endif /* CONFIG_RT2X00_DEBUGFS */ +}; + +/* + * RT2400pci module information. + */ +static char version[] = + DRV_NAME " - " DRV_VERSION " (" DRV_RELDATE ") by " DRV_PROJECT; + +static struct pci_device_id rt2400pci_device_table[] = { + { PCI_DEVICE(0x1814, 0x0101), PCI_DEVICE_DATA(&rt2400pci_ops) }, + { 0, } +}; + +MODULE_AUTHOR(DRV_PROJECT); +MODULE_VERSION(DRV_VERSION); +MODULE_DESCRIPTION("Ralink RT2400 PCI & PCMCIA Wireless LAN driver."); +MODULE_SUPPORTED_DEVICE("Ralink RT2460 PCI & PCMCIA chipset based cards"); +MODULE_DEVICE_TABLE(pci, rt2400pci_device_table); +MODULE_LICENSE("GPL"); + +#ifdef CONFIG_RT2X00_DEBUG +module_param_named(debug, rt2x00_debug_level, bool, S_IWUSR | S_IRUGO); +MODULE_PARM_DESC(debug, "Set this parameter to 1 to enable debug output."); +#endif /* CONFIG_RT2X00_DEBUG */ + +static struct pci_driver rt2400pci_driver = { + .name = DRV_NAME, + .id_table = rt2400pci_device_table, + .probe = rt2x00pci_probe, + .remove = __devexit_p(rt2x00pci_remove), +#ifdef CONFIG_PM + .suspend = rt2x00pci_suspend, + .resume = rt2x00pci_resume, +#endif /* CONFIG_PM */ +}; + +static int __init rt2400pci_init(void) +{ + printk(KERN_INFO "Loading module: %s.\n", version); + return pci_register_driver(&rt2400pci_driver); +} + +static void __exit rt2400pci_exit(void) +{ + printk(KERN_INFO "Unloading module: %s.\n", version); + pci_unregister_driver(&rt2400pci_driver); +} + +module_init(rt2400pci_init); +module_exit(rt2400pci_exit); diff --git a/drivers/net/wireless/mac80211/rt2x00/rt2400pci.h b/drivers/net/wireless/mac80211/rt2x00/rt2400pci.h new file mode 100644 index 0000000..1f28e1a --- /dev/null +++ b/drivers/net/wireless/mac80211/rt2x00/rt2400pci.h @@ -0,0 +1,918 @@ +/* + Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + + + 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. + */ + +/* + Module: rt2400pci + Abstract: Data structures and registers for the rt2400pci module. + Supported chipsets: RT2460. + */ + +#ifndef RT2400PCI_H +#define RT2400PCI_H + +/* + * RF chip defines. + */ +#define RF2420 0x0000 +#define RF2421 0x0001 + +/* + * Max RSSI value, required for RSSI <-> dBm conversion. + */ +#define MAX_RX_SSI 100 +#define MAX_RX_NOISE -110 + +/* + * Register layout information. + */ +#define CSR_REG_BASE 0x0000 +#define CSR_REG_SIZE 0x014c +#define EEPROM_BASE 0x0000 +#define EEPROM_SIZE 0x0100 +#define BBP_SIZE 0x0020 + +/* + * Control/Status Registers(CSR). + * Some values are set in TU, whereas 1 TU == 1024 us. + */ + +/* + * CSR0: ASIC revision number. + */ +#define CSR0 0x0000 + +/* + * CSR1: System control register. + * SOFT_RESET: Software reset, 1: reset, 0: normal. + * BBP_RESET: Hardware reset, 1: reset, 0, release. + * HOST_READY: Host ready after initialization. + */ +#define CSR1 0x0004 +#define CSR1_SOFT_RESET FIELD32(0x00000001) +#define CSR1_BBP_RESET FIELD32(0x00000002) +#define CSR1_HOST_READY FIELD32(0x00000004) + +/* + * CSR2: System admin status register (invalid). + */ +#define CSR2 0x0008 + +/* + * CSR3: STA MAC address register 0. + */ +#define CSR3 0x000c +#define CSR3_BYTE0 FIELD32(0x000000ff) +#define CSR3_BYTE1 FIELD32(0x0000ff00) +#define CSR3_BYTE2 FIELD32(0x00ff0000) +#define CSR3_BYTE3 FIELD32(0xff000000) + +/* + * CSR4: STA MAC address register 1. + */ +#define CSR4 0x0010 +#define CSR4_BYTE4 FIELD32(0x000000ff) +#define CSR4_BYTE5 FIELD32(0x0000ff00) + +/* + * CSR5: BSSID register 0. + */ +#define CSR5 0x0014 +#define CSR5_BYTE0 FIELD32(0x000000ff) +#define CSR5_BYTE1 FIELD32(0x0000ff00) +#define CSR5_BYTE2 FIELD32(0x00ff0000) +#define CSR5_BYTE3 FIELD32(0xff000000) + +/* + * CSR6: BSSID register 1. + */ +#define CSR6 0x0018 +#define CSR6_BYTE4 FIELD32(0x000000ff) +#define CSR6_BYTE5 FIELD32(0x0000ff00) + +/* + * CSR7: Interrupt source register. + * Write 1 to clear interrupt. + * TBCN_EXPIRE: Beacon timer expired interrupt. + * TWAKE_EXPIRE: Wakeup timer expired interrupt. + * TATIMW_EXPIRE: Timer of atim window expired interrupt. + * TXDONE_TXRING: Tx ring transmit done interrupt. + * TXDONE_ATIMRING: Atim ring transmit done interrupt. + * TXDONE_PRIORING: Priority ring transmit done interrupt. + * RXDONE: Receive done interrupt. + */ +#define CSR7 0x001c +#define CSR7_TBCN_EXPIRE FIELD32(0x00000001) +#define CSR7_TWAKE_EXPIRE FIELD32(0x00000002) +#define CSR7_TATIMW_EXPIRE FIELD32(0x00000004) +#define CSR7_TXDONE_TXRING FIELD32(0x00000008) +#define CSR7_TXDONE_ATIMRING FIELD32(0x00000010) +#define CSR7_TXDONE_PRIORING FIELD32(0x00000020) +#define CSR7_RXDONE FIELD32(0x00000040) + +/* + * CSR8: Interrupt mask register. + * Write 1 to mask interrupt. + * TBCN_EXPIRE: Beacon timer expired interrupt. + * TWAKE_EXPIRE: Wakeup timer expired interrupt. + * TATIMW_EXPIRE: Timer of atim window expired interrupt. + * TXDONE_TXRING: Tx ring transmit done interrupt. + * TXDONE_ATIMRING: Atim ring transmit done interrupt. + * TXDONE_PRIORING: Priority ring transmit done interrupt. + * RXDONE: Receive done interrupt. + */ +#define CSR8 0x0020 +#define CSR8_TBCN_EXPIRE FIELD32(0x00000001) +#define CSR8_TWAKE_EXPIRE FIELD32(0x00000002) +#define CSR8_TATIMW_EXPIRE FIELD32(0x00000004) +#define CSR8_TXDONE_TXRING FIELD32(0x00000008) +#define CSR8_TXDONE_ATIMRING FIELD32(0x00000010) +#define CSR8_TXDONE_PRIORING FIELD32(0x00000020) +#define CSR8_RXDONE FIELD32(0x00000040) + +/* + * CSR9: Maximum frame length register. + * MAX_FRAME_UNIT: Maximum frame length in 128b unit, default: 12. + */ +#define CSR9 0x0024 +#define CSR9_MAX_FRAME_UNIT FIELD32(0x00000f80) + +/* + * CSR11: Back-off control register. + * CWMIN: CWmin. Default cwmin is 31 (2^5 - 1). + * CWMAX: CWmax. Default cwmax is 1023 (2^10 - 1). + * SLOT_TIME: Slot time, default is 20us for 802.11b. + * LONG_RETRY: Long retry count. + * SHORT_RETRY: Short retry count. + */ +#define CSR11 0x002c +#define CSR11_CWMIN FIELD32(0x0000000f) +#define CSR11_CWMAX FIELD32(0x000000f0) +#define CSR11_SLOT_TIME FIELD32(0x00001f00) +#define CSR11_LONG_RETRY FIELD32(0x00ff0000) +#define CSR11_SHORT_RETRY FIELD32(0xff000000) + +/* + * CSR12: Synchronization configuration register 0. + * All units in 1/16 TU. + * BEACON_INTERVAL: Beacon interval, default is 100 TU. + * CFPMAX_DURATION: Cfp maximum duration, default is 100 TU. + */ +#define CSR12 0x0030 +#define CSR12_BEACON_INTERVAL FIELD32(0x0000ffff) +#define CSR12_CFP_MAX_DURATION FIELD32(0xffff0000) + +/* + * CSR13: Synchronization configuration register 1. + * All units in 1/16 TU. + * ATIMW_DURATION: Atim window duration. + * CFP_PERIOD: Cfp period, default is 0 TU. + */ +#define CSR13 0x0034 +#define CSR13_ATIMW_DURATION FIELD32(0x0000ffff) +#define CSR13_CFP_PERIOD FIELD32(0x00ff0000) + +/* + * CSR14: Synchronization control register. + * TSF_COUNT: Enable tsf auto counting. + * TSF_SYNC: Tsf sync, 0: disable, 1: infra, 2: ad-hoc/master mode. + * TBCN: Enable tbcn with reload value. + * TCFP: Enable tcfp & cfp / cp switching. + * TATIMW: Enable tatimw & atim window switching. + * BEACON_GEN: Enable beacon generator. + * CFP_COUNT_PRELOAD: Cfp count preload value. + * TBCM_PRELOAD: Tbcn preload value in units of 64us. + */ +#define CSR14 0x0038 +#define CSR14_TSF_COUNT FIELD32(0x00000001) +#define CSR14_TSF_SYNC FIELD32(0x00000006) +#define CSR14_TBCN FIELD32(0x00000008) +#define CSR14_TCFP FIELD32(0x00000010) +#define CSR14_TATIMW FIELD32(0x00000020) +#define CSR14_BEACON_GEN FIELD32(0x00000040) +#define CSR14_CFP_COUNT_PRELOAD FIELD32(0x0000ff00) +#define CSR14_TBCM_PRELOAD FIELD32(0xffff0000) + +/* + * CSR15: Synchronization status register. + * CFP: ASIC is in contention-free period. + * ATIMW: ASIC is in ATIM window. + * BEACON_SENT: Beacon is send. + */ +#define CSR15 0x003c +#define CSR15_CFP FIELD32(0x00000001) +#define CSR15_ATIMW FIELD32(0x00000002) +#define CSR15_BEACON_SENT FIELD32(0x00000004) + +/* + * CSR16: TSF timer register 0. + */ +#define CSR16 0x0040 +#define CSR16_LOW_TSFTIMER FIELD32(0xffffffff) + +/* + * CSR17: TSF timer register 1. + */ +#define CSR17 0x0044 +#define CSR17_HIGH_TSFTIMER FIELD32(0xffffffff) + +/* + * CSR18: IFS timer register 0. + * SIFS: Sifs, default is 10 us. + * PIFS: Pifs, default is 30 us. + */ +#define CSR18 0x0048 +#define CSR18_SIFS FIELD32(0x0000ffff) +#define CSR18_PIFS FIELD32(0xffff0000) + +/* + * CSR19: IFS timer register 1. + * DIFS: Difs, default is 50 us. + * EIFS: Eifs, default is 364 us. + */ +#define CSR19 0x004c +#define CSR19_DIFS FIELD32(0x0000ffff) +#define CSR19_EIFS FIELD32(0xffff0000) + +/* + * CSR20: Wakeup timer register. + * DELAY_AFTER_TBCN: Delay after tbcn expired in units of 1/16 TU. + * TBCN_BEFORE_WAKEUP: Number of beacon before wakeup. + * AUTOWAKE: Enable auto wakeup / sleep mechanism. + */ +#define CSR20 0x0050 +#define CSR20_DELAY_AFTER_TBCN FIELD32(0x0000ffff) +#define CSR20_TBCN_BEFORE_WAKEUP FIELD32(0x00ff0000) +#define CSR20_AUTOWAKE FIELD32(0x01000000) + +/* + * CSR21: EEPROM control register. + * RELOAD: Write 1 to reload eeprom content. + * TYPE_93C46: 1: 93c46, 0:93c66. + */ +#define CSR21 0x0054 +#define CSR21_RELOAD FIELD32(0x00000001) +#define CSR21_EEPROM_DATA_CLOCK FIELD32(0x00000002) +#define CSR21_EEPROM_CHIP_SELECT FIELD32(0x00000004) +#define CSR21_EEPROM_DATA_IN FIELD32(0x00000008) +#define CSR21_EEPROM_DATA_OUT FIELD32(0x00000010) +#define CSR21_TYPE_93C46 FIELD32(0x00000020) + +/* + * CSR22: CFP control register. + * CFP_DURATION_REMAIN: Cfp duration remain, in units of TU. + * RELOAD_CFP_DURATION: Write 1 to reload cfp duration remain. + */ +#define CSR22 0x0058 +#define CSR22_CFP_DURATION_REMAIN FIELD32(0x0000ffff) +#define CSR22_RELOAD_CFP_DURATION FIELD32(0x00010000) + +/* + * Transmit related CSRs. + * Some values are set in TU, whereas 1 TU == 1024 us. + */ + +/* + * TXCSR0: TX Control Register. + * KICK_TX: Kick tx ring. + * KICK_ATIM: Kick atim ring. + * KICK_PRIO: Kick priority ring. + * ABORT: Abort all transmit related ring operation. + */ +#define TXCSR0 0x0060 +#define TXCSR0_KICK_TX FIELD32(0x00000001) +#define TXCSR0_KICK_ATIM FIELD32(0x00000002) +#define TXCSR0_KICK_PRIO FIELD32(0x00000004) +#define TXCSR0_ABORT FIELD32(0x00000008) + +/* + * TXCSR1: TX Configuration Register. + * ACK_TIMEOUT: Ack timeout, default = sifs + 2*slottime + acktime @ 1mbps. + * ACK_CONSUME_TIME: Ack consume time, default = sifs + acktime @ 1mbps. + * TSF_OFFSET: Insert tsf offset. + * AUTORESPONDER: Enable auto responder which include ack & cts. + */ +#define TXCSR1 0x0064 +#define TXCSR1_ACK_TIMEOUT FIELD32(0x000001ff) +#define TXCSR1_ACK_CONSUME_TIME FIELD32(0x0003fe00) +#define TXCSR1_TSF_OFFSET FIELD32(0x00fc0000) +#define TXCSR1_AUTORESPONDER FIELD32(0x01000000) + +/* + * TXCSR2: Tx descriptor configuration register. + * TXD_SIZE: Tx descriptor size, default is 48. + * NUM_TXD: Number of tx entries in ring. + * NUM_ATIM: Number of atim entries in ring. + * NUM_PRIO: Number of priority entries in ring. + */ +#define TXCSR2 0x0068 +#define TXCSR2_TXD_SIZE FIELD32(0x000000ff) +#define TXCSR2_NUM_TXD FIELD32(0x0000ff00) +#define TXCSR2_NUM_ATIM FIELD32(0x00ff0000) +#define TXCSR2_NUM_PRIO FIELD32(0xff000000) + +/* + * TXCSR3: TX Ring Base address register. + */ +#define TXCSR3 0x006c +#define TXCSR3_TX_RING_REGISTER FIELD32(0xffffffff) + +/* + * TXCSR4: TX Atim Ring Base address register. + */ +#define TXCSR4 0x0070 +#define TXCSR4_ATIM_RING_REGISTER FIELD32(0xffffffff) + +/* + * TXCSR5: TX Prio Ring Base address register. + */ +#define TXCSR5 0x0074 +#define TXCSR5_PRIO_RING_REGISTER FIELD32(0xffffffff) + +/* + * TXCSR6: Beacon Base address register. + */ +#define TXCSR6 0x0078 +#define TXCSR6_BEACON_RING_REGISTER FIELD32(0xffffffff) + +/* + * TXCSR7: Auto responder control register. + * AR_POWERMANAGEMENT: Auto responder power management bit. + */ +#define TXCSR7 0x007c +#define TXCSR7_AR_POWERMANAGEMENT FIELD32(0x00000001) + +/* + * Receive related CSRs. + * Some values are set in TU, whereas 1 TU == 1024 us. + */ + +/* + * RXCSR0: RX Control Register. + * DISABLE_RX: Disable rx engine. + * DROP_CRC: Drop crc error. + * DROP_PHYSICAL: Drop physical error. + * DROP_CONTROL: Drop control frame. + * DROP_NOT_TO_ME: Drop not to me unicast frame. + * DROP_TODS: Drop frame tods bit is true. + * DROP_VERSION_ERROR: Drop version error frame. + * PASS_CRC: Pass all packets with crc attached. + */ +#define RXCSR0 0x0080 +#define RXCSR0_DISABLE_RX FIELD32(0x00000001) +#define RXCSR0_DROP_CRC FIELD32(0x00000002) +#define RXCSR0_DROP_PHYSICAL FIELD32(0x00000004) +#define RXCSR0_DROP_CONTROL FIELD32(0x00000008) +#define RXCSR0_DROP_NOT_TO_ME FIELD32(0x00000010) +#define RXCSR0_DROP_TODS FIELD32(0x00000020) +#define RXCSR0_DROP_VERSION_ERROR FIELD32(0x00000040) +#define RXCSR0_PASS_CRC FIELD32(0x00000080) + +/* + * RXCSR1: RX descriptor configuration register. + * RXD_SIZE: Rx descriptor size, default is 32b. + * NUM_RXD: Number of rx entries in ring. + */ +#define RXCSR1 0x0084 +#define RXCSR1_RXD_SIZE FIELD32(0x000000ff) +#define RXCSR1_NUM_RXD FIELD32(0x0000ff00) + +/* + * RXCSR2: RX Ring base address register. + */ +#define RXCSR2 0x0088 +#define RXCSR2_RX_RING_REGISTER FIELD32(0xffffffff) + +/* + * RXCSR3: BBP ID register for Rx operation. + * BBP_ID#: BBP register # id. + * BBP_ID#_VALID: BBP register # id is valid or not. + */ +#define RXCSR3 0x0090 +#define RXCSR3_BBP_ID0 FIELD32(0x0000007f) +#define RXCSR3_BBP_ID0_VALID FIELD32(0x00000080) +#define RXCSR3_BBP_ID1 FIELD32(0x00007f00) +#define RXCSR3_BBP_ID1_VALID FIELD32(0x00008000) +#define RXCSR3_BBP_ID2 FIELD32(0x007f0000) +#define RXCSR3_BBP_ID2_VALID FIELD32(0x00800000) +#define RXCSR3_BBP_ID3 FIELD32(0x7f000000) +#define RXCSR3_BBP_ID3_VALID FIELD32(0x80000000) + +/* + * RXCSR4: BBP ID register for Rx operation. + * BBP_ID#: BBP register # id. + * BBP_ID#_VALID: BBP register # id is valid or not. + */ +#define RXCSR4 0x0094 +#define RXCSR4_BBP_ID4 FIELD32(0x0000007f) +#define RXCSR4_BBP_ID4_VALID FIELD32(0x00000080) +#define RXCSR4_BBP_ID5 FIELD32(0x00007f00) +#define RXCSR4_BBP_ID5_VALID FIELD32(0x00008000) + +/* + * ARCSR0: Auto Responder PLCP config register 0. + * ARCSR0_AR_BBP_DATA#: Auto responder BBP register # data. + * ARCSR0_AR_BBP_ID#: Auto responder BBP register # Id. + */ +#define ARCSR0 0x0098 +#define ARCSR0_AR_BBP_DATA0 FIELD32(0x000000ff) +#define ARCSR0_AR_BBP_ID0 FIELD32(0x0000ff00) +#define ARCSR0_AR_BBP_DATA1 FIELD32(0x00ff0000) +#define ARCSR0_AR_BBP_ID1 FIELD32(0xff000000) + +/* + * ARCSR1: Auto Responder PLCP config register 1. + * ARCSR0_AR_BBP_DATA#: Auto responder BBP register # data. + * ARCSR0_AR_BBP_ID#: Auto responder BBP register # Id. + */ +#define ARCSR1 0x009c +#define ARCSR1_AR_BBP_DATA2 FIELD32(0x000000ff) +#define ARCSR1_AR_BBP_ID2 FIELD32(0x0000ff00) +#define ARCSR1_AR_BBP_DATA3 FIELD32(0x00ff0000) +#define ARCSR1_AR_BBP_ID3 FIELD32(0xff000000) + +/* + * Miscellaneous Registers. + * Some values are set in TU, whereas 1 TU == 1024 us. + */ + +/* + * PCICSR: PCI control register. + * BIG_ENDIAN: 1: big endian, 0: little endian. + * RX_TRESHOLD: Rx threshold in dw to start pci access + * 0: 16dw (default), 1: 8dw, 2: 4dw, 3: 32dw. + * TX_TRESHOLD: Tx threshold in dw to start pci access + * 0: 0dw (default), 1: 1dw, 2: 4dw, 3: forward. + * BURST_LENTH: Pci burst length 0: 4dw (default, 1: 8dw, 2: 16dw, 3:32dw. + * ENABLE_CLK: Enable clk_run, pci clock can't going down to non-operational. + */ +#define PCICSR 0x008c +#define PCICSR_BIG_ENDIAN FIELD32(0x00000001) +#define PCICSR_RX_TRESHOLD FIELD32(0x00000006) +#define PCICSR_TX_TRESHOLD FIELD32(0x00000018) +#define PCICSR_BURST_LENTH FIELD32(0x00000060) +#define PCICSR_ENABLE_CLK FIELD32(0x00000080) + +/* + * CNT0: FCS error count. + * FCS_ERROR: FCS error count, cleared when read. + */ +#define CNT0 0x00a0 +#define CNT0_FCS_ERROR FIELD32(0x0000ffff) + +/* + * Statistic Register. + * CNT1: PLCP error count. + * CNT2: Long error count. + * CNT3: CCA false alarm count. + * CNT4: Rx FIFO overflow count. + * CNT5: Tx FIFO underrun count. + */ +#define TIMECSR2 0x00a8 +#define CNT1 0x00ac +#define CNT2 0x00b0 +#define TIMECSR3 0x00b4 +#define CNT3 0x00b8 +#define CNT4 0x00bc +#define CNT5 0x00c0 + +/* + * Baseband Control Register. + */ + +/* + * PWRCSR0: Power mode configuration register. + */ +#define PWRCSR0 0x00c4 + +/* + * Power state transition time registers. + */ +#define PSCSR0 0x00c8 +#define PSCSR1 0x00cc +#define PSCSR2 0x00d0 +#define PSCSR3 0x00d4 + +/* + * PWRCSR1: Manual power control / status register. + * Allowed state: 0 deep_sleep, 1: sleep, 2: standby, 3: awake. + * SET_STATE: Set state. Write 1 to trigger, self cleared. + * BBP_DESIRE_STATE: BBP desired state. + * RF_DESIRE_STATE: RF desired state. + * BBP_CURR_STATE: BBP current state. + * RF_CURR_STATE: RF current state. + * PUT_TO_SLEEP: Put to sleep. Write 1 to trigger, self cleared. + */ +#define PWRCSR1 0x00d8 +#define PWRCSR1_SET_STATE FIELD32(0x00000001) +#define PWRCSR1_BBP_DESIRE_STATE FIELD32(0x00000006) +#define PWRCSR1_RF_DESIRE_STATE FIELD32(0x00000018) +#define PWRCSR1_BBP_CURR_STATE FIELD32(0x00000060) +#define PWRCSR1_RF_CURR_STATE FIELD32(0x00000180) +#define PWRCSR1_PUT_TO_SLEEP FIELD32(0x00000200) + +/* + * TIMECSR: Timer control register. + * US_COUNT: 1 us timer count in units of clock cycles. + * US_64_COUNT: 64 us timer count in units of 1 us timer. + * BEACON_EXPECT: Beacon expect window. + */ +#define TIMECSR 0x00dc +#define TIMECSR_US_COUNT FIELD32(0x000000ff) +#define TIMECSR_US_64_COUNT FIELD32(0x0000ff00) +#define TIMECSR_BEACON_EXPECT FIELD32(0x00070000) + +/* + * MACCSR0: MAC configuration register 0. + */ +#define MACCSR0 0x00e0 + + +/* + * MACCSR1: MAC configuration register 1. + * KICK_RX: Kick one-shot rx in one-shot rx mode. + * ONESHOT_RXMODE: Enable one-shot rx mode for debugging. + * BBPRX_RESET_MODE: Ralink bbp rx reset mode. + * AUTO_TXBBP: Auto tx logic access bbp control register. + * AUTO_RXBBP: Auto rx logic access bbp control register. + * LOOPBACK: Loopback mode. 0: normal, 1: internal, 2: external, 3:rsvd. + * INTERSIL_IF: Intersil if calibration pin. + */ +#define MACCSR1 0x00e4 +#define MACCSR1_KICK_RX FIELD32(0x00000001) +#define MACCSR1_ONESHOT_RXMODE FIELD32(0x00000002) +#define MACCSR1_BBPRX_RESET_MODE FIELD32(0x00000004) +#define MACCSR1_AUTO_TXBBP FIELD32(0x00000008) +#define MACCSR1_AUTO_RXBBP FIELD32(0x00000010) +#define MACCSR1_LOOPBACK FIELD32(0x00000060) +#define MACCSR1_INTERSIL_IF FIELD32(0x00000080) + +/* + * RALINKCSR: Ralink Rx auto-reset BBCR. + * AR_BBP_DATA#: Auto reset BBP register # data. + * AR_BBP_ID#: Auto reset BBP register # id. + */ +#define RALINKCSR 0x00e8 +#define RALINKCSR_AR_BBP_DATA0 FIELD32(0x000000ff) +#define RALINKCSR_AR_BBP_ID0 FIELD32(0x0000ff00) +#define RALINKCSR_AR_BBP_DATA1 FIELD32(0x00ff0000) +#define RALINKCSR_AR_BBP_ID1 FIELD32(0xff000000) + +/* + * BCNCSR: Beacon interval control register. + * CHANGE: Write one to change beacon interval. + * DELTATIME: The delta time value. + * NUM_BEACON: Number of beacon according to mode. + * MODE: Please refer to asic specs. + * PLUS: Plus or minus delta time value. + */ +#define BCNCSR 0x00ec +#define BCNCSR_CHANGE FIELD32(0x00000001) +#define BCNCSR_DELTATIME FIELD32(0x0000001e) +#define BCNCSR_NUM_BEACON FIELD32(0x00001fe0) +#define BCNCSR_MODE FIELD32(0x00006000) +#define BCNCSR_PLUS FIELD32(0x00008000) + +/* + * BBP / RF / IF Control Register. + */ + +/* + * BBPCSR: BBP serial control register. + * VALUE: Register value to program into BBP. + * REGNUM: Selected BBP register. + * BUSY: 1: asic is busy execute BBP programming. + * WRITE_CONTROL: 1: write BBP, 0: read BBP. + */ +#define BBPCSR 0x00f0 +#define BBPCSR_VALUE FIELD32(0x000000ff) +#define BBPCSR_REGNUM FIELD32(0x00007f00) +#define BBPCSR_BUSY FIELD32(0x00008000) +#define BBPCSR_WRITE_CONTROL FIELD32(0x00010000) + +/* + * RFCSR: RF serial control register. + * VALUE: Register value + id to program into rf/if. + * NUMBER_OF_BITS: Number of bits used in value (i:20, rfmd:22). + * IF_SELECT: Chip to program: 0: rf, 1: if. + * PLL_LD: Rf pll_ld status. + * BUSY: 1: asic is busy execute rf programming. + */ +#define RFCSR 0x00f4 +#define RFCSR_VALUE FIELD32(0x00ffffff) +#define RFCSR_NUMBER_OF_BITS FIELD32(0x1f000000) +#define RFCSR_IF_SELECT FIELD32(0x20000000) +#define RFCSR_PLL_LD FIELD32(0x40000000) +#define RFCSR_BUSY FIELD32(0x80000000) + +/* + * LEDCSR: LED control register. + * ON_PERIOD: On period, default 70ms. + * OFF_PERIOD: Off period, default 30ms. + * LINK: 0: linkoff, 1: linkup. + * ACTIVITY: 0: idle, 1: active. + */ +#define LEDCSR 0x00f8 +#define LEDCSR_ON_PERIOD FIELD32(0x000000ff) +#define LEDCSR_OFF_PERIOD FIELD32(0x0000ff00) +#define LEDCSR_LINK FIELD32(0x00010000) +#define LEDCSR_ACTIVITY FIELD32(0x00020000) + +/* + * ASIC pointer information. + * RXPTR: Current RX ring address. + * TXPTR: Current Tx ring address. + * PRIPTR: Current Priority ring address. + * ATIMPTR: Current ATIM ring address. + */ +#define RXPTR 0x0100 +#define TXPTR 0x0104 +#define PRIPTR 0x0108 +#define ATIMPTR 0x010c + +/* + * GPIO and others. + */ + +/* + * GPIOCSR: GPIO control register. + */ +#define GPIOCSR 0x0120 +#define GPIOCSR_BIT0 FIELD32(0x00000001) +#define GPIOCSR_BIT1 FIELD32(0x00000002) +#define GPIOCSR_BIT2 FIELD32(0x00000004) +#define GPIOCSR_BIT3 FIELD32(0x00000008) +#define GPIOCSR_BIT4 FIELD32(0x00000010) +#define GPIOCSR_BIT5 FIELD32(0x00000020) +#define GPIOCSR_BIT6 FIELD32(0x00000040) +#define GPIOCSR_BIT7 FIELD32(0x00000080) + +/* + * BBPPCSR: BBP Pin control register. + */ +#define BBPPCSR 0x0124 + +/* + * BCNCSR1: Tx BEACON offset time control register. + * PRELOAD: Beacon timer offset in units of usec. + */ +#define BCNCSR1 0x0130 +#define BCNCSR1_PRELOAD FIELD32(0x0000ffff) + +/* + * MACCSR2: TX_PE to RX_PE turn-around time control register + * DELAY: RX_PE low width, in units of pci clock cycle. + */ +#define MACCSR2 0x0134 +#define MACCSR2_DELAY FIELD32(0x000000ff) + +/* + * ACK/CTS PLCP registers. + * ARCSR2: 1 Mbps ACK/CTS PLCP. + * ARCSR3: 2 Mbps ACK/CTS PLCP. + * ARCSR4: 5.5 Mbps ACK/CTS PLCP. + * ARCSR5: 11 Mbps ACK/CTS PLCP. + */ +#define ARCSR2 0x013c +#define ARCSR3 0x0140 +#define ARCSR4 0x0144 +#define ARCSR5 0x0148 + +/* + * RF registers + */ +#define RF1_TUNER FIELD32(0x00020000) +#define RF3_TUNER FIELD32(0x00000100) +#define RF3_TXPOWER FIELD32(0x00003e00) + +/* + * EEPROM content. + * The wordsize of the EEPROM is 16 bits. + */ + +/* + * HW MAC address. + */ +#define EEPROM_MAC_ADDR_0 0x0002 +#define EEPROM_MAC_ADDR_BYTE0 FIELD16(0x00ff) +#define EEPROM_MAC_ADDR_BYTE1 FIELD16(0xff00) +#define EEPROM_MAC_ADDR1 0x0003 +#define EEPROM_MAC_ADDR_BYTE2 FIELD16(0x00ff) +#define EEPROM_MAC_ADDR_BYTE3 FIELD16(0xff00) +#define EEPROM_MAC_ADDR_2 0x0004 +#define EEPROM_MAC_ADDR_BYTE4 FIELD16(0x00ff) +#define EEPROM_MAC_ADDR_BYTE5 FIELD16(0xff00) + +/* + * EEPROM antenna. + * ANTENNA_NUM: Number of antenna's. + * TX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. + * RX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. + * RF_TYPE: Rf_type of this adapter. + * LED_MODE: 0: default, 1: TX/RX activity,2: Single (ignore link), 3: rsvd. + * RX_AGCVGC: 0: disable, 1:enable BBP R13 tuning. + * HARDWARE_RADIO: 1: Hardware controlled radio. Read GPIO0. + */ +#define EEPROM_ANTENNA 0x0b +#define EEPROM_ANTENNA_NUM FIELD16(0x0003) +#define EEPROM_ANTENNA_TX_DEFAULT FIELD16(0x000c) +#define EEPROM_ANTENNA_RX_DEFAULT FIELD16(0x0030) +#define EEPROM_ANTENNA_RF_TYPE FIELD16(0x0040) +#define EEPROM_ANTENNA_LED_MODE FIELD16(0x0180) +#define EEPROM_ANTENNA_RX_AGCVGC_TUNING FIELD16(0x0200) +#define EEPROM_ANTENNA_HARDWARE_RADIO FIELD16(0x0400) + +/* + * EEPROM BBP. + */ +#define EEPROM_BBP_START 0x0c +#define EEPROM_BBP_SIZE 7 +#define EEPROM_BBP_VALUE FIELD16(0x00ff) +#define EEPROM_BBP_REG_ID FIELD16(0xff00) + +/* + * EEPROM TXPOWER + */ +#define EEPROM_TXPOWER_START 0x13 +#define EEPROM_TXPOWER_SIZE 7 +#define EEPROM_TXPOWER_1 FIELD16(0x00ff) +#define EEPROM_TXPOWER_2 FIELD16(0xff00) + +/* + * DMA descriptor defines. + */ +#define TXD_DESC_SIZE ( 8 * sizeof(struct data_desc) ) +#define RXD_DESC_SIZE ( 8 * sizeof(struct data_desc) ) + +/* + * TX descriptor format for TX, PRIO, ATIM and Beacon Ring. + */ + +/* + * Word0 + */ +#define TXD_W0_OWNER_NIC FIELD32(0x00000001) +#define TXD_W0_VALID FIELD32(0x00000002) +#define TXD_W0_RESULT FIELD32(0x0000001c) +#define TXD_W0_RETRY_COUNT FIELD32(0x000000e0) +#define TXD_W0_MORE_FRAG FIELD32(0x00000100) +#define TXD_W0_ACK FIELD32(0x00000200) +#define TXD_W0_TIMESTAMP FIELD32(0x00000400) +#define TXD_W0_RTS FIELD32(0x00000800) +#define TXD_W0_IFS FIELD32(0x00006000) +#define TXD_W0_RETRY_MODE FIELD32(0x00008000) +#define TXD_W0_AGC FIELD32(0x00ff0000) +#define TXD_W0_R2 FIELD32(0xff000000) + +/* + * Word1 + */ +#define TXD_W1_BUFFER_ADDRESS FIELD32(0xffffffff) + +/* + * Word2 + */ +#define TXD_W2_BUFFER_LENGTH FIELD32(0x0000ffff) +#define TXD_W2_DATABYTE_COUNT FIELD32(0xffff0000) + +/* + * Word3 & 4: PLCP information + */ +#define TXD_W3_PLCP_SIGNAL FIELD32(0x0000ffff) +#define TXD_W3_PLCP_SERVICE FIELD32(0xffff0000) +#define TXD_W4_PLCP_LENGTH_LOW FIELD32(0x0000ffff) +#define TXD_W4_PLCP_LENGTH_HIGH FIELD32(0xffff0000) + +/* + * Word5 + */ +#define TXD_W5_BBCR4 FIELD32(0x0000ffff) +#define TXD_W5_AGC_REG FIELD32(0x007f0000) +#define TXD_W5_AGC_REG_VALID FIELD32(0x00800000) +#define TXD_W5_XXX_REG FIELD32(0x7f000000) +#define TXD_W5_XXX_REG_VALID FIELD32(0x80000000) + +/* + * Word6 + */ +#define TXD_W6_SK_BUFF FIELD32(0xffffffff) + +/* + * Word7 + */ +#define TXD_W7_RESERVED FIELD32(0xffffffff) + +/* + * RX descriptor format for RX Ring. + */ + +/* + * Word0 + */ +#define RXD_W0_OWNER_NIC FIELD32(0x00000001) +#define RXD_W0_UNICAST_TO_ME FIELD32(0x00000002) +#define RXD_W0_MULTICAST FIELD32(0x00000004) +#define RXD_W0_BROADCAST FIELD32(0x00000008) +#define RXD_W0_MY_BSS FIELD32(0x00000010) +#define RXD_W0_CRC FIELD32(0x00000020) +#define RXD_W0_PHYSICAL_ERROR FIELD32(0x00000080) +#define RXD_W0_DATABYTE_COUNT FIELD32(0xffff0000) + +/* + * Word1 + */ +#define RXD_W1_BUFFER_ADDRESS FIELD32(0xffffffff) + +/* + * Word2 + */ +#define RXD_W2_BUFFER_LENGTH FIELD32(0x0000ffff) +#define RXD_W2_SIGNAL FIELD32(0x00ff0000) +#define RXD_W2_RSSI FIELD32(0xff000000) + +/* + * Word3 + */ +#define RXD_W3_BBR2 FIELD32(0x000000ff) +#define RXD_W3_BBR3 FIELD32(0x0000ff00) +#define RXD_W3_BBR4 FIELD32(0x00ff0000) +#define RXD_W3_BBR5 FIELD32(0xff000000) + +/* + * Word4 + */ +#define RXD_W4_RX_END_TIME FIELD32(0xffffffff) + +/* + * Word5 & 6 & 7: Reserved + */ +#define RXD_W5_RESERVED FIELD32(0xffffffff) +#define RXD_W6_RESERVED FIELD32(0xffffffff) +#define RXD_W7_RESERVED FIELD32(0xffffffff) + +/* + * TX ring index number for rt2x00_dev structure. + */ +enum ring_index { + RING_PRIO = 0, + RING_TX = 1, + RING_ATIM = 2, + RING_BEACON = 3, + RING_RX = 4, + RING_NUM = 5, + RING_NUM_TX = 2, +}; + +/* + * Macro's for converting txpower from EEPROM to dscape value + * and from dscape value to register value. + * NOTE: Logics in rt2400pci for txpower are reversed + * compared to the other rt2x00 drivers. A higher txpower + * value means that the txpower must be lowered. This is + * important when converting the value coming from the + * dscape stack to the rt2400 acceptable value. + */ +#define MIN_TXPOWER 31 +#define MAX_TXPOWER 62 +#define DEFAULT_TXPOWER 39 + +#define TXPOWER_FROM_DEV(__txpower) \ + ({ \ + ((__txpower) > MAX_TXPOWER) ? DEFAULT_TXPOWER - MIN_TXPOWER : \ + ((__txpower) < MIN_TXPOWER) ? DEFAULT_TXPOWER - MIN_TXPOWER : \ + (((__txpower) - MAX_TXPOWER) + MIN_TXPOWER); \ + }) + +#define TXPOWER_TO_DEV(__txpower) \ + ({ \ + (__txpower) += MIN_TXPOWER; \ + ((__txpower) <= MIN_TXPOWER) ? MAX_TXPOWER : \ + (((__txpower) >= MAX_TXPOWER) ? MIN_TXPOWER : \ + (MAX_TXPOWER - ((__txpower) - MIN_TXPOWER))); \ + }) + +/* + * LED control functions. + */ +static void rt2400pci_enable_led(struct rt2x00_dev *rt2x00dev); +static void rt2400pci_disable_led(struct rt2x00_dev *rt2x00dev); +static void rt2400pci_activity_led(struct rt2x00_dev *rt2x00dev, + char activity); + +#endif /* RT2400PCI_H */ diff --git a/drivers/net/wireless/mac80211/rt2x00/rt2500pci.c b/drivers/net/wireless/mac80211/rt2x00/rt2500pci.c new file mode 100644 index 0000000..e202682 --- /dev/null +++ b/drivers/net/wireless/mac80211/rt2x00/rt2500pci.c @@ -0,0 +1,1951 @@ +/* + Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + + + 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. + */ + +/* + Module: rt2500pci + Abstract: rt2500pci device specific routines. + Supported chipsets: RT2560. + */ + +/* + * Set enviroment defines for rt2x00.h + */ +#define DRV_NAME "rt2500pci" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "rt2x00.h" +#include "rt2x00pci.h" +#include "rt2500pci.h" + +/* + * Register access. + * All access to the CSR registers will go through the methods + * rt2x00_register_read and rt2x00_register_write. + * BBP and RF register require indirect register access, + * and use the CSR registers BBPCSR and RFCSR to achieve this. + * These indirect registers work with busy bits, + * and we will try maximal REGISTER_BUSY_COUNT times to access + * the register while taking a REGISTER_BUSY_DELAY us delay + * between each attampt. When the busy bit is still set at that time, + * the access attempt is considered to have failed, + * and we will print an error. + */ +static inline void rt2x00_register_read( + const struct rt2x00_dev *rt2x00dev, + const unsigned long offset, u32 *value) +{ + *value = readl(rt2x00dev->csr_addr + offset); +} + +static inline void rt2x00_register_multiread( + const struct rt2x00_dev *rt2x00dev, + const unsigned long offset, void *value, const u16 length) +{ + memcpy_fromio(value, rt2x00dev->csr_addr + offset, length); +} + +static inline void rt2x00_register_write( + const struct rt2x00_dev *rt2x00dev, + const unsigned long offset, u32 value) +{ + writel(value, rt2x00dev->csr_addr + offset); +} + +static inline void rt2x00_register_multiwrite( + const struct rt2x00_dev *rt2x00dev, + const unsigned long offset, void *value, const u16 length) +{ + memcpy_toio(rt2x00dev->csr_addr + offset, value, length); +} + +static u32 rt2x00_bbp_check(const struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + unsigned int i; + + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2x00_register_read(rt2x00dev, BBPCSR, ®); + if (!rt2x00_get_field32(reg, BBPCSR_BUSY)) + return reg; + udelay(REGISTER_BUSY_DELAY); + } + + return 0xffff; +} + +static void rt2x00_bbp_write(const struct rt2x00_dev *rt2x00dev, + const u8 reg_id, const u8 value) +{ + u32 reg; + + /* + * Wait until the BBP becomes ready. + */ + if (rt2x00_bbp_check(rt2x00dev) == 0xffff) { + ERROR("BBPCSR register busy. Write failed.\n"); + return; + } + + /* + * Write the data into the BBP. + */ + reg = 0; + rt2x00_set_field32(®, BBPCSR_VALUE, value); + rt2x00_set_field32(®, BBPCSR_REGNUM, reg_id); + rt2x00_set_field32(®, BBPCSR_BUSY, 1); + rt2x00_set_field32(®, BBPCSR_WRITE_CONTROL, 1); + + rt2x00_register_write(rt2x00dev, BBPCSR, reg); +} + +static void rt2x00_bbp_read(const struct rt2x00_dev *rt2x00dev, + const u8 reg_id, u8 *value) +{ + u32 reg; + + /* + * Wait until the BBP becomes ready. + */ + if (rt2x00_bbp_check(rt2x00dev) == 0xffff) { + ERROR("BBPCSR register busy. Read failed.\n"); + return; + } + + /* + * Write the request into the BBP. + */ + reg = 0; + rt2x00_set_field32(®, BBPCSR_REGNUM, reg_id); + rt2x00_set_field32(®, BBPCSR_BUSY, 1); + rt2x00_set_field32(®, BBPCSR_WRITE_CONTROL, 0); + + rt2x00_register_write(rt2x00dev, BBPCSR, reg); + + /* + * Wait until the BBP becomes ready. + */ + reg = rt2x00_bbp_check(rt2x00dev); + if (reg == 0xffff) + ERROR("BBPCSR register busy. Read failed.\n"); + + *value = rt2x00_get_field32(reg, BBPCSR_VALUE); +} + +static void rt2x00_rf_write(const struct rt2x00_dev *rt2x00dev, + const u32 value) +{ + u32 reg; + unsigned int i; + + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2x00_register_read(rt2x00dev, RFCSR, ®); + if (!rt2x00_get_field32(reg, RFCSR_BUSY)) + goto rf_write; + udelay(REGISTER_BUSY_DELAY); + } + + ERROR("RFCSR register busy. Write failed.\n"); + return; + +rf_write: + reg = 0; + rt2x00_set_field32(®, RFCSR_VALUE, value); + rt2x00_set_field32(®, RFCSR_NUMBER_OF_BITS, 20); + rt2x00_set_field32(®, RFCSR_IF_SELECT, 0); + rt2x00_set_field32(®, RFCSR_BUSY, 1); + + rt2x00_register_write(rt2x00dev, RFCSR, reg); +} + +static void rt2500pci_eepromregister_read(struct eeprom_93cx6 *eeprom) +{ + struct rt2x00_dev *rt2x00dev = eeprom->data; + u32 reg; + + rt2x00_register_read(rt2x00dev, CSR21, ®); + + eeprom->reg_data_in = !!rt2x00_get_field32(reg, + CSR21_EEPROM_DATA_IN); + eeprom->reg_data_out = !!rt2x00_get_field32(reg, + CSR21_EEPROM_DATA_OUT); + eeprom->reg_data_clock = !!rt2x00_get_field32(reg, + CSR21_EEPROM_DATA_CLOCK); + eeprom->reg_chip_select = !!rt2x00_get_field32(reg, + CSR21_EEPROM_CHIP_SELECT); +} + +static void rt2500pci_eepromregister_write(struct eeprom_93cx6 *eeprom) +{ + struct rt2x00_dev *rt2x00dev = eeprom->data; + u32 reg = 0; + + rt2x00_set_field32(®, CSR21_EEPROM_DATA_IN, + !!eeprom->reg_data_in); + rt2x00_set_field32(®, CSR21_EEPROM_DATA_OUT, + !!eeprom->reg_data_out); + rt2x00_set_field32(®, CSR21_EEPROM_DATA_CLOCK, + !!eeprom->reg_data_clock); + rt2x00_set_field32(®, CSR21_EEPROM_CHIP_SELECT, + !!eeprom->reg_chip_select); + + rt2x00_register_write(rt2x00dev, CSR21, reg); +} + +#ifdef CONFIG_RT2X00_DEBUGFS +#define CSR_OFFSET(__word) ( CSR_REG_BASE + ((__word) * sizeof(u32)) ) + +static void rt2500pci_read_csr(struct rt2x00_dev *rt2x00dev, + const unsigned long word, void *data) +{ + rt2x00_register_read(rt2x00dev, CSR_OFFSET(word), data); +} + +static void rt2500pci_write_csr(struct rt2x00_dev *rt2x00dev, + const unsigned long word, void *data) +{ + rt2x00_register_write(rt2x00dev, CSR_OFFSET(word), *((u32*)data)); +} + +static void rt2500pci_read_eeprom(struct rt2x00_dev *rt2x00dev, + const unsigned long word, void *data) +{ + rt2x00_eeprom_read(rt2x00dev, word, data); +} + +static void rt2500pci_write_eeprom(struct rt2x00_dev *rt2x00dev, + const unsigned long word, void *data) +{ + rt2x00_eeprom_write(rt2x00dev, word, *((u16*)data)); +} + +static void rt2500pci_read_bbp(struct rt2x00_dev *rt2x00dev, + const unsigned long word, void *data) +{ + rt2x00_bbp_read(rt2x00dev, word, data); +} + +static void rt2500pci_write_bbp(struct rt2x00_dev *rt2x00dev, + const unsigned long word, void *data) +{ + rt2x00_bbp_write(rt2x00dev, word, *((u8*)data)); +} + +static const struct rt2x00debug rt2500pci_rt2x00debug = { + .owner = THIS_MODULE, + .mod_name = DRV_NAME, + .mod_version = DRV_VERSION, + .reg_csr = { + .read = rt2500pci_read_csr, + .write = rt2500pci_write_csr, + .word_size = sizeof(u32), + .word_count = CSR_REG_SIZE / sizeof(u32), + }, + .reg_eeprom = { + .read = rt2500pci_read_eeprom, + .write = rt2500pci_write_eeprom, + .word_size = sizeof(u16), + .word_count = EEPROM_SIZE / sizeof(u16), + }, + .reg_bbp = { + .read = rt2500pci_read_bbp, + .write = rt2500pci_write_bbp, + .word_size = sizeof(u8), + .word_count = BBP_SIZE / sizeof(u8), + }, +}; +#endif /* CONFIG_RT2X00_DEBUGFS */ + +/* + * Configuration handlers. + */ +static void rt2500pci_config_bssid(struct rt2x00_dev *rt2x00dev, u8 *bssid) +{ + u32 reg[2]; + + memset(®, 0, sizeof(reg)); + memcpy(®, bssid, ETH_ALEN); + + /* + * The BSSID is passed to us as an array of bytes, + * that array is little endian, so no need for byte ordering. + */ + rt2x00_register_multiwrite(rt2x00dev, CSR5, ®, sizeof(reg)); +} + +static void rt2500pci_config_promisc(struct rt2x00_dev *rt2x00dev, int promisc) +{ + u32 reg; + + rt2x00_register_read(rt2x00dev, RXCSR0, ®); + + if (promisc) { + rt2x00_set_field32(®, RXCSR0_DROP_NOT_TO_ME, 0); + SET_FLAG(rt2x00dev, INTERFACE_ENABLED_PROMISC); + } else { + rt2x00_set_field32(®, RXCSR0_DROP_NOT_TO_ME, 1); + CLEAR_FLAG(rt2x00dev, INTERFACE_ENABLED_PROMISC); + } + + rt2x00_register_write(rt2x00dev, RXCSR0, reg); +} + +static void rt2500pci_config_type(struct rt2x00_dev *rt2x00dev, int type) +{ + u32 reg; + + /* + * Only continue when there is something to be done. + */ + if (!(GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED) ^ + GET_FLAG(rt2x00dev, INTERFACE_ENABLED)) && + !(GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED_MONITOR) ^ + GET_FLAG(rt2x00dev, INTERFACE_ENABLED_MONITOR))) + return; + + rt2x00_register_write(rt2x00dev, CSR14, 0); + + /* + * Apply hardware packet filter. + */ + rt2x00_register_read(rt2x00dev, RXCSR0, ®); + + if (!GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED_MONITOR) && + (type == IEEE80211_IF_TYPE_IBSS || type == IEEE80211_IF_TYPE_STA)) + rt2x00_set_field32(®, RXCSR0_DROP_TODS, 1); + else + rt2x00_set_field32(®, RXCSR0_DROP_TODS, 0); + + rt2x00_set_field32(®, RXCSR0_DROP_CRC, 1); + if (GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED_MONITOR)) { + rt2x00_set_field32(®, RXCSR0_DROP_PHYSICAL, 0); + rt2x00_set_field32(®, RXCSR0_DROP_CONTROL, 0); + rt2x00_set_field32(®, RXCSR0_DROP_VERSION_ERROR, 0); + } else { + rt2x00_set_field32(®, RXCSR0_DROP_PHYSICAL, 1); + rt2x00_set_field32(®, RXCSR0_DROP_CONTROL, 1); + rt2x00_set_field32(®, RXCSR0_DROP_VERSION_ERROR, 1); + } + + rt2x00_set_field32(®, RXCSR0_DROP_MCAST, 0); + rt2x00_set_field32(®, RXCSR0_DROP_BCAST, 0); + + rt2x00_register_write(rt2x00dev, RXCSR0, reg); + + /* + * Enable promisc mode when in monitor mode. + */ + if (GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED_MONITOR)) + rt2500pci_config_promisc(rt2x00dev, 1); + + /* + * Enable beacon config + */ + rt2x00_register_read(rt2x00dev, BCNCSR1, ®); + rt2x00_set_field32(®, BCNCSR1_PRELOAD, + PREAMBLE + get_duration(IEEE80211_HEADER, 2)); + rt2x00_set_field32(®, BCNCSR1_BEACON_CWMIN, + rt2x00dev->ring[RING_BEACON].tx_params.cw_min); + rt2x00_register_write(rt2x00dev, BCNCSR1, reg); + + /* + * Enable synchronisation. + */ + rt2x00_register_read(rt2x00dev, CSR12, ®); + rt2x00_set_field32(®, CSR12_BEACON_INTERVAL, 100 * 16); + rt2x00_set_field32(®, CSR12_CFPMAX_DURATION, 100 * 16); + rt2x00_register_write(rt2x00dev, CSR12, reg); + + rt2x00_register_read(rt2x00dev, CSR14, ®); + if (GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED)) { + rt2x00_set_field32(®, CSR14_TSF_COUNT, 1); + rt2x00_set_field32(®, CSR14_TBCN, 1); + } + + rt2x00_set_field32(®, CSR14_BEACON_GEN, 0); + if (type == IEEE80211_IF_TYPE_IBSS || type == IEEE80211_IF_TYPE_AP) + rt2x00_set_field32(®, CSR14_TSF_SYNC, 2); + else if (type == IEEE80211_IF_TYPE_STA) + rt2x00_set_field32(®, CSR14_TSF_SYNC, 1); + else if (GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED_MONITOR) && + !GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED)) + rt2x00_set_field32(®, CSR14_TSF_SYNC, 0); + + rt2x00_register_write(rt2x00dev, CSR14, reg); + + /* + * Change flags of enabled interfaces. + */ + if (type != IEEE80211_IF_TYPE_MNTR) { + if (GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED)) + SET_FLAG(rt2x00dev, INTERFACE_ENABLED); + else + CLEAR_FLAG(rt2x00dev, INTERFACE_ENABLED); + } else { + if (GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED_MONITOR)) + SET_FLAG(rt2x00dev, INTERFACE_ENABLED_MONITOR); + else + CLEAR_FLAG(rt2x00dev, INTERFACE_ENABLED_MONITOR); + } +} + +static void rt2500pci_config_channel(struct rt2x00_dev *rt2x00dev, + int value, int channel, int freq, int txpower) +{ + u32 rf1 = rt2x00dev->rf1; + u32 rf2 = value; + u32 rf3 = rt2x00dev->rf3; + u32 rf4 = rt2x00dev->rf4; + + /* + * Only continue when there is something to be done. + */ + if (channel == rt2x00dev->rx_status.channel) + return; + + if (txpower == 0xff) + txpower = rt2x00dev->tx_power; + else + txpower = TXPOWER_TO_DEV(txpower); + + if (rt2x00_rf(&rt2x00dev->chip, RF2525) || + rt2x00_rf(&rt2x00dev->chip, RF2525E)) + rf2 |= 0x00080000; + + if (rt2x00_rf(&rt2x00dev->chip, RF2525E) && channel == 14) + rf4 |= 0x00000010; + + if (rt2x00_rf(&rt2x00dev->chip, RF5222)) { + if (channel < 14) { + rf1 = 0x00022020; + rf4 = 0x00000a0b; + } else if (channel == 14) { + rf1 = 0x00022010; + rf4 = 0x00000a1b; + } else if (channel < 64) { + rf1 = 0x00022010; + rf4 = 0x00000a1f; + } else if (channel < 140) { + rf1 = 0x00022010; + rf4 = 0x00000a0f; + } else if (channel < 161) { + rf1 = 0x00022020; + rf4 = 0x00000a07; + } + } + + /* + * Set TXpower. + */ + rt2x00_set_field32(&rf3, RF3_TXPOWER, txpower); + + /* + * Switch on tuning bits. + * For RT2523 devices we do not need to update the R1 register. + */ + if (!rt2x00_rf(&rt2x00dev->chip, RF2523)) + rt2x00_set_field32(&rf1, RF1_TUNER, 1); + rt2x00_set_field32(&rf3, RF3_TUNER, 1); + + INFO("Switching channel. RF1: 0x%08x, RF2: 0x%08x, RF3: 0x%08x, " + "RF4: 0x%08x.\n", rf1, rf2, rf3, rf4); + + /* + * For RT2525 we should first set the channel to half band higher. + */ + if (rt2x00_rf(&rt2x00dev->chip, RF2525)) { + static const u32 vals[] = { + 0x00080cbe, 0x00080d02, 0x00080d06, 0x00080d0a, + 0x00080d0e, 0x00080d12, 0x00080d16, 0x00080d1a, + 0x00080d1e, 0x00080d22, 0x00080d26, 0x00080d2a, + 0x00080d2e, 0x00080d3a + }; + + rt2x00_rf_write(rt2x00dev, rf1); + rt2x00_rf_write(rt2x00dev, vals[channel - 1]); + rt2x00_rf_write(rt2x00dev, rf3); + if (rf4) + rt2x00_rf_write(rt2x00dev, rf4); + } + + rt2x00_rf_write(rt2x00dev, rf1); + rt2x00_rf_write(rt2x00dev, rf2); + rt2x00_rf_write(rt2x00dev, rf3); + if (rf4) + rt2x00_rf_write(rt2x00dev, rf4); + + /* + * Channel 14 requires the Japan filter bit to be set. + */ + rt2x00_bbp_write(rt2x00dev, 70, (channel == 14) ? 0x4e : 0x46); + + msleep(1); + + /* + * Switch off tuning bits. + * For RT2523 devices we do not need to update the R1 register. + */ + rt2x00_set_field32(&rf1, RF1_TUNER, 0); + rt2x00_set_field32(&rf3, RF3_TUNER, 0); + + + if (!rt2x00_rf(&rt2x00dev->chip, RF2523)) + rt2x00_rf_write(rt2x00dev, rf1); + + rt2x00_rf_write(rt2x00dev, rf3); + + /* + * Update active info for RX. + */ + rt2x00dev->rx_status.freq = freq; + rt2x00dev->rx_status.channel = channel; + + /* + * Update rf fields + */ + rt2x00dev->rf1 = rf1; + rt2x00dev->rf2 = rf2; + rt2x00dev->rf3 = rf3; + rt2x00dev->rf4 = rf4; + + rt2x00dev->tx_power = txpower; + + /* + * Clear false CRC during channel switch. + */ + rt2x00_register_read(rt2x00dev, CNT0, &rf1); +} + +static void rt2500pci_config_txpower(struct rt2x00_dev *rt2x00dev, int txpower) +{ + txpower = TXPOWER_TO_DEV(txpower); + + /* + * Only continue when there is something to be done. + */ + if (txpower == rt2x00dev->tx_power) + return; + + rt2x00_set_field32(&rt2x00dev->rf3, RF3_TXPOWER, txpower); + rt2x00_rf_write(rt2x00dev, rt2x00dev->rf3); + + rt2x00dev->tx_power = txpower; +} + +static void rt2500pci_config_antenna(struct rt2x00_dev *rt2x00dev, + int antenna_tx, int antenna_rx) +{ + u32 reg; + u8 reg_rx; + u8 reg_tx; + + /* + * Only continue when there is something to be done. + */ + if (rt2x00dev->rx_status.antenna == antenna_rx) + return; + + rt2x00_register_read(rt2x00dev, BBPCSR1, ®); + rt2x00_bbp_read(rt2x00dev, 14, ®_rx); + rt2x00_bbp_read(rt2x00dev, 2, ®_tx); + + /* + * Clear current config antenna bits. + */ + reg_rx &= ~0x03; + reg_tx &= ~0x03; + + /* + * Configure the TX antenna. + */ + if (antenna_tx == 0) { /* Diversity. */ + reg_tx |= 0x02; + rt2x00_set_field32(®, BBPCSR1_CCK, 2); + rt2x00_set_field32(®, BBPCSR1_OFDM, 2); + } else if (antenna_tx == 1) { /* TX: Antenna A */ + reg_tx |= 0x00; + rt2x00_set_field32(®, BBPCSR1_CCK, 0); + rt2x00_set_field32(®, BBPCSR1_OFDM, 0); + } else if (antenna_tx == 2) { /* TX: Antenna B */ + reg_tx |= 0x02; + rt2x00_set_field32(®, BBPCSR1_CCK, 2); + rt2x00_set_field32(®, BBPCSR1_OFDM, 2); + } + + /* + * Configure the RX antenna. + */ + if (antenna_rx == 0) /* Diversity. */ + reg_rx |= 0x02; + else if (antenna_rx == 1) /* RX: Antenna A */ + reg_rx |= 0x00; + else if (antenna_rx == 2) /* RX: Antenna B */ + reg_rx |= 0x02; + + /* + * RT2525E and RT5222 need to flip TX I/Q + */ + if (rt2x00_rf(&rt2x00dev->chip, RF2525E) || + rt2x00_rf(&rt2x00dev->chip, RF5222)) { + reg_tx |= 0x04; + rt2x00_set_field32(®, BBPCSR1_CCK_FLIP, 1); + rt2x00_set_field32(®, BBPCSR1_OFDM_FLIP, 1); + + /* + * RT2525E does not need RX I/Q Flip. + */ + if (rt2x00_rf(&rt2x00dev->chip, RF2525E)) + reg_rx &= ~0x04; + } else { + rt2x00_set_field32(®, BBPCSR1_CCK_FLIP, 0); + rt2x00_set_field32(®, BBPCSR1_OFDM_FLIP, 0); + } + + rt2x00_register_write(rt2x00dev, BBPCSR1, reg); + rt2x00_bbp_write(rt2x00dev, 14, reg_rx); + rt2x00_bbp_write(rt2x00dev, 2, reg_tx); + + /* + * Update active info for RX. + */ + rt2x00dev->rx_status.antenna = antenna_rx; +} + +static void rt2500pci_config_duration(struct rt2x00_dev *rt2x00dev, + int short_slot_time) +{ + u32 reg; + + rt2x00_register_read(rt2x00dev, CSR11, ®); + rt2x00_set_field32(®, CSR11_SLOT_TIME, + short_slot_time ? SHORT_SLOT_TIME : SLOT_TIME); + rt2x00_register_write(rt2x00dev, CSR11, reg); + + rt2x00_register_read(rt2x00dev, CSR18, ®); + rt2x00_set_field32(®, CSR18_SIFS, SIFS); + rt2x00_set_field32(®, CSR18_PIFS, + short_slot_time ? SHORT_PIFS : PIFS); + rt2x00_register_write(rt2x00dev, CSR18, reg); + + rt2x00_register_read(rt2x00dev, CSR19, ®); + rt2x00_set_field32(®, CSR19_DIFS, + short_slot_time ? SHORT_DIFS : DIFS); + rt2x00_set_field32(®, CSR19_EIFS, EIFS); + rt2x00_register_write(rt2x00dev, CSR19, reg); + + rt2x00_register_read(rt2x00dev, TXCSR1, ®); + rt2x00_set_field32(®, TXCSR1_TSF_OFFSET, IEEE80211_HEADER); + rt2x00_set_field32(®, TXCSR1_AUTORESPONDER, 1); + rt2x00_register_write(rt2x00dev, TXCSR1, reg); +} + +static void rt2500pci_config_rate(struct rt2x00_dev *rt2x00dev, const int rate) +{ + struct ieee80211_conf *conf = &rt2x00dev->hw->conf; + u32 reg; + u32 value; + u32 preamble; + + preamble = DEVICE_GET_RATE_FIELD(rate, PREAMBLE) + ? SHORT_PREAMBLE : PREAMBLE; + + reg = DEVICE_GET_RATE_FIELD(rate, RATEMASK) & DEV_BASIC_RATE; + rt2x00_register_write(rt2x00dev, ARCSR1, reg); + + rt2x00_register_read(rt2x00dev, TXCSR1, ®); + value = ((conf->flags & IEEE80211_CONF_SHORT_SLOT_TIME) ? + SHORT_DIFS : DIFS) + + PLCP + preamble + get_duration(ACK_SIZE, 10); + rt2x00_set_field32(®, TXCSR1_ACK_TIMEOUT, value); + value = SIFS + PLCP + preamble + get_duration(ACK_SIZE, 10); + rt2x00_set_field32(®, TXCSR1_ACK_CONSUME_TIME, value); + rt2x00_register_write(rt2x00dev, TXCSR1, reg); + + preamble = DEVICE_GET_RATE_FIELD(rate, PREAMBLE) ? 0x08 : 0x00; + + rt2x00_register_write(rt2x00dev, ARCSR2, 0x00700400 | preamble); + rt2x00_register_write(rt2x00dev, ARCSR3, 0x00380401 | preamble); + rt2x00_register_write(rt2x00dev, ARCSR4, 0x00150402 | preamble); + rt2x00_register_write(rt2x00dev, ARCSR5, 0x000b8403 | preamble); +} + +static void rt2500pci_config_phymode(struct rt2x00_dev *rt2x00dev, + const int phymode) +{ + struct ieee80211_hw_mode *mode; + struct ieee80211_rate *rate; + + /* + * Only continue when there is something to be done. + */ + if (rt2x00dev->rx_status.phymode == phymode) + return; + + if (phymode == MODE_IEEE80211A) + rt2x00dev->curr_hwmode = HWMODE_A; + else if (phymode == MODE_IEEE80211B) + rt2x00dev->curr_hwmode = HWMODE_B; + else + rt2x00dev->curr_hwmode = HWMODE_G; + + mode = &rt2x00dev->hwmodes[rt2x00dev->curr_hwmode]; + rate = &mode->rates[mode->num_rates - 1]; + + rt2500pci_config_rate(rt2x00dev, rate->val2); + + /* + * Update physical mode for rx ring. + */ + rt2x00dev->rx_status.phymode = phymode; +} + +static void rt2500pci_config_mac_addr(struct rt2x00_dev *rt2x00dev, u8 *addr) +{ + u32 reg[2]; + + memset(®, 0, sizeof(reg)); + memcpy(®, addr, ETH_ALEN); + + /* + * The MAC address is passed to us as an array of bytes, + * that array is little endian, so no need for byte ordering. + */ + rt2x00_register_multiwrite(rt2x00dev, CSR3, ®, sizeof(reg)); +} + +/* + * Link tuning + */ +static void rt2500pci_link_tuner(struct work_struct *work) +{ + struct rt2x00_dev *rt2x00dev = + container_of(work, struct rt2x00_dev, link.work.work); + u32 reg; + u32 rssi; + u8 reg_r17; + + /* + * Don't perform any tuning when it is disabled + * in the EEPROM. + */ + if (GET_FLAG(rt2x00dev, CONFIG_DISABLE_LINK_TUNING)) + return; + + /* + * Retrieve link quality. + */ + rssi = rt2x00_get_link_rssi(&rt2x00dev->link); + if (!rssi) + goto exit; + + rt2x00_register_read(rt2x00dev, CSR0, ®); + rt2x00_bbp_read(rt2x00dev, 17, ®_r17); + + if (reg < RT2560_VERSION_D) + goto dynamic_cca_tune; + + if (rssi < 40) { + if (reg_r17 >= 0x41) + rt2x00_bbp_write(rt2x00dev, 17, reg_r17); + goto exit; + } else if (rssi >= 62) { + if (reg_r17 != 0x50) + rt2x00_bbp_write(rt2x00dev, 17, 0x50); + goto exit; + } else if (reg_r17 >= 0x41) { + rt2x00_bbp_write(rt2x00dev, 17, reg_r17); + goto exit; + } + +dynamic_cca_tune: + rt2x00_register_read(rt2x00dev, CNT3, ®); + + reg = rt2x00_get_field32(reg, CNT3_FALSE_CCA); + + if (reg > 512 && reg_r17 < 0x40) + rt2x00_bbp_write(rt2x00dev, 17, ++reg_r17); + else if (reg < 100 && reg_r17 > 0x32) + rt2x00_bbp_write(rt2x00dev, 17, --reg_r17); + +exit: + /* + * Update noise statistics. + */ + if (reg_r17) + rt2x00_update_link_noise(&rt2x00dev->link, reg_r17); + + queue_delayed_work(rt2x00dev->workqueue, &rt2x00dev->link.work, + LINK_TUNE_INTERVAL); +} + +/* + * LED functions. + */ +static void rt2500pci_enable_led(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + rt2x00_register_read(rt2x00dev, LEDCSR, ®); + + rt2x00_set_field32(®, LEDCSR_ON_PERIOD, 70); + rt2x00_set_field32(®, LEDCSR_OFF_PERIOD, 30); + + if (rt2x00dev->led_mode == LED_MODE_TXRX_ACTIVITY) { + rt2x00_set_field32(®, LEDCSR_LINK, 1); + rt2x00_set_field32(®, LEDCSR_ACTIVITY, 0); + } else if (rt2x00dev->led_mode == LED_MODE_ASUS) { + rt2x00_set_field32(®, LEDCSR_LINK, 0); + rt2x00_set_field32(®, LEDCSR_ACTIVITY, 1); + } else { + rt2x00_set_field32(®, LEDCSR_LINK, 1); + rt2x00_set_field32(®, LEDCSR_ACTIVITY, 1); + } + + rt2x00_register_write(rt2x00dev, LEDCSR, reg); +} + +static void rt2500pci_disable_led(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + rt2x00_register_read(rt2x00dev, LEDCSR, ®); + rt2x00_set_field32(®, LEDCSR_LINK, 0); + rt2x00_set_field32(®, LEDCSR_ACTIVITY, 0); + rt2x00_register_write(rt2x00dev, LEDCSR, reg); +} + +static void rt2500pci_activity_led(struct rt2x00_dev *rt2x00dev, char activity) +{ + u32 reg; + + if (rt2x00dev->led_mode != LED_MODE_TXRX_ACTIVITY) + return; + + rt2x00_register_read(rt2x00dev, LEDCSR, ®); + rt2x00_set_field32(®, LEDCSR_ACTIVITY, activity); + rt2x00_register_write(rt2x00dev, LEDCSR, reg); +} + +/* + * Device state switch. + * This will put the device to sleep, or awake it. + */ +static int rt2500pci_set_state(struct rt2x00_dev *rt2x00dev, + enum dev_state state) +{ + u32 reg; + unsigned int i; + char put_to_sleep; + char bbp_state; + char rf_state; + + put_to_sleep = (state != STATE_AWAKE); + + rt2x00_register_read(rt2x00dev, PWRCSR1, ®); + rt2x00_set_field32(®, PWRCSR1_SET_STATE, 1); + rt2x00_set_field32(®, PWRCSR1_BBP_DESIRE_STATE, state); + rt2x00_set_field32(®, PWRCSR1_RF_DESIRE_STATE, state); + rt2x00_set_field32(®, PWRCSR1_PUT_TO_SLEEP, put_to_sleep); + rt2x00_register_write(rt2x00dev, PWRCSR1, reg); + + /* + * Device is not guaranteed to be in the requested state yet. + * We must wait until the register indicates that the + * device has entered the correct state. + */ + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2x00_register_read(rt2x00dev, PWRCSR1, ®); + bbp_state = rt2x00_get_field32(reg, PWRCSR1_BBP_CURR_STATE); + rf_state = rt2x00_get_field32(reg, PWRCSR1_RF_CURR_STATE); + if (bbp_state == state && rf_state == state) + return 0; + msleep(10); + } + + NOTICE("Device failed to enter state %d, " + "current device state: bbp %d and rf %d.\n", + state, bbp_state, rf_state); + + return -EBUSY; +} + +/* + * Initialization functions. + */ +static void rt2500pci_init_rxring(struct rt2x00_dev *rt2x00dev, + enum ring_index ring_type) +{ + struct data_ring *ring = &rt2x00dev->ring[ring_type]; + struct data_desc *rxd; + unsigned int i; + u32 word; + + memset(ring->data_addr, 0x00, rt2x00_get_ring_size(ring)); + + ring->type = ring_type; + + for (i = 0; i < ring->stats.limit; i++) { + rxd = ring->entry[i].priv; + + rt2x00_desc_read(rxd, 1, &word); + rt2x00_set_field32(&word, RXD_W1_BUFFER_ADDRESS, + ring->entry[i].data_dma); + rt2x00_desc_write(rxd, 1, word); + + rt2x00_desc_read(rxd, 0, &word); + rt2x00_set_field32(&word, RXD_W0_OWNER_NIC, 1); + rt2x00_desc_write(rxd, 0, word); + } + + rt2x00_ring_index_clear(ring); +} + +static void rt2500pci_init_txring(struct rt2x00_dev *rt2x00dev, + enum ring_index ring_type) +{ + struct data_ring *ring = &rt2x00dev->ring[ring_type]; + struct data_desc *txd; + unsigned int i; + u32 word; + + memset(ring->data_addr, 0x00, rt2x00_get_ring_size(ring)); + + ring->type = ring_type; + + for (i = 0; i < ring->stats.limit; i++) { + txd = ring->entry[i].priv; + + rt2x00_desc_read(txd, 1, &word); + rt2x00_set_field32(&word, TXD_W1_BUFFER_ADDRESS, + ring->entry[i].data_dma); + rt2x00_desc_write(txd, 1, word); + + rt2x00_desc_read(txd, 0, &word); + rt2x00_set_field32(&word, TXD_W0_VALID, 0); + rt2x00_set_field32(&word, TXD_W0_OWNER_NIC, 0); + rt2x00_desc_write(txd, 0, word); + } + + rt2x00_ring_index_clear(ring); +} + +static int rt2500pci_init_rings(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + /* + * Initialize rings. + */ + rt2500pci_init_rxring(rt2x00dev, RING_RX); + rt2500pci_init_txring(rt2x00dev, RING_TX); + rt2500pci_init_txring(rt2x00dev, RING_ATIM); + rt2500pci_init_txring(rt2x00dev, RING_PRIO); + rt2500pci_init_txring(rt2x00dev, RING_BEACON); + + /* + * Initialize registers. + */ + reg = 0; + rt2x00_set_field32(®, TXCSR2_TXD_SIZE, + rt2x00dev->ring[RING_TX].desc_size); + rt2x00_set_field32(®, TXCSR2_NUM_TXD, + rt2x00dev->ring[RING_TX].stats.limit); + rt2x00_set_field32(®, TXCSR2_NUM_ATIM, + rt2x00dev->ring[RING_ATIM].stats.limit); + rt2x00_set_field32(®, TXCSR2_NUM_PRIO, + rt2x00dev->ring[RING_PRIO].stats.limit); + rt2x00_register_write(rt2x00dev, TXCSR2, reg); + + reg = 0; + rt2x00_set_field32(®, TXCSR3_TX_RING_REGISTER, + rt2x00dev->ring[RING_TX].data_dma); + rt2x00_register_write(rt2x00dev, TXCSR3, reg); + + reg = 0; + rt2x00_set_field32(®, TXCSR5_PRIO_RING_REGISTER, + rt2x00dev->ring[RING_PRIO].data_dma); + rt2x00_register_write(rt2x00dev, TXCSR5, reg); + + reg = 0; + rt2x00_set_field32(®, TXCSR4_ATIM_RING_REGISTER, + rt2x00dev->ring[RING_ATIM].data_dma); + rt2x00_register_write(rt2x00dev, TXCSR4, reg); + + reg = 0; + rt2x00_set_field32(®, TXCSR6_BEACON_RING_REGISTER, + rt2x00dev->ring[RING_BEACON].data_dma); + rt2x00_register_write(rt2x00dev, TXCSR6, reg); + + reg = 0; + rt2x00_set_field32(®, RXCSR1_RXD_SIZE, + rt2x00dev->ring[RING_RX].desc_size); + rt2x00_set_field32(®, RXCSR1_NUM_RXD, + rt2x00dev->ring[RING_RX].stats.limit); + rt2x00_register_write(rt2x00dev, RXCSR1, reg); + + reg = 0; + rt2x00_set_field32(®, RXCSR2_RX_RING_REGISTER, + rt2x00dev->ring[RING_RX].data_dma); + rt2x00_register_write(rt2x00dev, RXCSR2, reg); + + return 0; +} + +static int rt2500pci_init_registers(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + if (rt2500pci_set_state(rt2x00dev, STATE_AWAKE)) + return -EBUSY; + + rt2x00_register_write(rt2x00dev, PWRCSR0, 0x3f3b3100); + rt2x00_register_write(rt2x00dev, PCICSR, 0x000003b8); + + rt2x00_register_write(rt2x00dev, PSCSR0, 0x00020002); + rt2x00_register_write(rt2x00dev, PSCSR1, 0x00000002); + rt2x00_register_write(rt2x00dev, PSCSR2, 0x00020002); + rt2x00_register_write(rt2x00dev, PSCSR3, 0x00000002); + + rt2x00_register_read(rt2x00dev, TIMECSR, ®); + rt2x00_set_field32(®, TIMECSR_US_COUNT, 33); + rt2x00_set_field32(®, TIMECSR_US_64_COUNT, 63); + rt2x00_set_field32(®, TIMECSR_BEACON_EXPECT, 0); + rt2x00_register_write(rt2x00dev, TIMECSR, reg); + + rt2x00_register_read(rt2x00dev, CSR9, ®); + rt2x00_set_field32(®, CSR9_MAX_FRAME_UNIT, + (rt2x00dev->ring[RING_RX].data_size / 128)); + rt2x00_register_write(rt2x00dev, CSR9, reg); + + rt2x00_register_write(rt2x00dev, CNT3, 0); + + rt2x00_register_write(rt2x00dev, GPIOCSR, 0x0000ff00); + rt2x00_register_write(rt2x00dev, TESTCSR, 0x000000f0); + + rt2x00_register_write(rt2x00dev, MACCSR0, 0x00213223); + rt2x00_register_write(rt2x00dev, MACCSR1, 0x00235518); + + rt2x00_register_read(rt2x00dev, MACCSR2, ®); + rt2x00_set_field32(®, MACCSR2_DELAY, 64); + rt2x00_register_write(rt2x00dev, MACCSR2, reg); + + /* + * Always use CWmin and CWmax set in descriptor. + */ + rt2x00_register_read(rt2x00dev, CSR11, ®); + rt2x00_set_field32(®, CSR11_CW_SELECT, 0); + rt2x00_register_write(rt2x00dev, CSR11, reg); + + rt2x00_register_read(rt2x00dev, RXCSR3, ®); + /* + * Signal. + */ + rt2x00_set_field32(®, RXCSR3_BBP_ID0, 47); + rt2x00_set_field32(®, RXCSR3_BBP_ID0_VALID, 1); + /* + * Rssi. + */ + rt2x00_set_field32(®, RXCSR3_BBP_ID1, 51); + rt2x00_set_field32(®, RXCSR3_BBP_ID1_VALID, 1); + /* + * OFDM Rate. + */ + rt2x00_set_field32(®, RXCSR3_BBP_ID2, 42); + rt2x00_set_field32(®, RXCSR3_BBP_ID2_VALID, 1); + /* + * OFDM. + */ + rt2x00_set_field32(®, RXCSR3_BBP_ID3, 51); + rt2x00_set_field32(®, RXCSR3_BBP_ID3_VALID, 1); + rt2x00_register_write(rt2x00dev, RXCSR3, reg); + + rt2x00_register_read(rt2x00dev, RALINKCSR, ®); + rt2x00_set_field32(®, RALINKCSR_AR_BBP_DATA0, 17); + rt2x00_set_field32(®, RALINKCSR_AR_BBP_ID0, 26); + rt2x00_set_field32(®, RALINKCSR_AR_BBP_VALID0, 1); + rt2x00_set_field32(®, RALINKCSR_AR_BBP_DATA1, 0); + rt2x00_set_field32(®, RALINKCSR_AR_BBP_ID1, 26); + rt2x00_set_field32(®, RALINKCSR_AR_BBP_VALID1, 1); + rt2x00_register_write(rt2x00dev, RALINKCSR, reg); + + rt2x00_register_write(rt2x00dev, BBPCSR1, 0x82188200); + + rt2x00_register_write(rt2x00dev, TXACKCSR0, 0x00000020); + + rt2x00_register_write(rt2x00dev, ARTCSR0, 0x7038140a); + rt2x00_register_write(rt2x00dev, ARTCSR1, 0x1d21252d); + rt2x00_register_write(rt2x00dev, ARTCSR2, 0x1919191d); + + rt2x00_register_read(rt2x00dev, CSR1, ®); + rt2x00_set_field32(®, CSR1_SOFT_RESET, 1); + rt2x00_set_field32(®, CSR1_BBP_RESET, 0); + rt2x00_set_field32(®, CSR1_HOST_READY, 0); + rt2x00_register_write(rt2x00dev, CSR1, reg); + + rt2x00_register_read(rt2x00dev, CSR1, ®); + rt2x00_set_field32(®, CSR1_SOFT_RESET, 0); + rt2x00_set_field32(®, CSR1_HOST_READY, 1); + rt2x00_register_write(rt2x00dev, CSR1, reg); + + /* + * We must clear the FCS and FIFO error count. + * These registers are cleared on read, + * so we may pass a useless variable to store the value. + */ + rt2x00_register_read(rt2x00dev, CNT0, ®); + rt2x00_register_read(rt2x00dev, CNT4, ®); + + return 0; +} + +static int rt2500pci_init_bbp(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + u16 eeprom; + u8 reg_id; + u8 value; + + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2x00_bbp_read(rt2x00dev, 0, &value); + if ((value != 0xff) && (value != 0x00)) + goto continue_csr_init; + NOTICE("Waiting for BBP register.\n"); + udelay(REGISTER_BUSY_DELAY); + } + + ERROR("BBP register access failed, aborting.\n"); + return -EACCES; + +continue_csr_init: + rt2x00_bbp_write(rt2x00dev, 3, 0x02); + rt2x00_bbp_write(rt2x00dev, 4, 0x19); + rt2x00_bbp_write(rt2x00dev, 14, 0x1c); + rt2x00_bbp_write(rt2x00dev, 15, 0x30); + rt2x00_bbp_write(rt2x00dev, 16, 0xac); + rt2x00_bbp_write(rt2x00dev, 17, 0x48); + rt2x00_bbp_write(rt2x00dev, 18, 0x18); + rt2x00_bbp_write(rt2x00dev, 19, 0xff); + rt2x00_bbp_write(rt2x00dev, 20, 0x1e); + rt2x00_bbp_write(rt2x00dev, 21, 0x08); + rt2x00_bbp_write(rt2x00dev, 22, 0x08); + rt2x00_bbp_write(rt2x00dev, 23, 0x08); + rt2x00_bbp_write(rt2x00dev, 24, 0x70); + rt2x00_bbp_write(rt2x00dev, 25, 0x40); + rt2x00_bbp_write(rt2x00dev, 26, 0x08); + rt2x00_bbp_write(rt2x00dev, 27, 0x23); + rt2x00_bbp_write(rt2x00dev, 30, 0x10); + rt2x00_bbp_write(rt2x00dev, 31, 0x2b); + rt2x00_bbp_write(rt2x00dev, 32, 0xb9); + rt2x00_bbp_write(rt2x00dev, 34, 0x12); + rt2x00_bbp_write(rt2x00dev, 35, 0x50); + rt2x00_bbp_write(rt2x00dev, 39, 0xc4); + rt2x00_bbp_write(rt2x00dev, 40, 0x02); + rt2x00_bbp_write(rt2x00dev, 41, 0x60); + rt2x00_bbp_write(rt2x00dev, 53, 0x10); + rt2x00_bbp_write(rt2x00dev, 54, 0x18); + rt2x00_bbp_write(rt2x00dev, 56, 0x08); + rt2x00_bbp_write(rt2x00dev, 57, 0x10); + rt2x00_bbp_write(rt2x00dev, 58, 0x08); + rt2x00_bbp_write(rt2x00dev, 61, 0x6d); + rt2x00_bbp_write(rt2x00dev, 62, 0x10); + + DEBUG("Start initialization from EEPROM...\n"); + for (i = 0; i < EEPROM_BBP_SIZE; i++) { + rt2x00_eeprom_read(rt2x00dev, EEPROM_BBP_START + i, &eeprom); + + if (eeprom != 0xffff && eeprom != 0x0000) { + reg_id = rt2x00_get_field16(eeprom, EEPROM_BBP_REG_ID); + value = rt2x00_get_field16(eeprom, EEPROM_BBP_VALUE); + DEBUG("BBP: 0x%02x, value: 0x%02x.\n", reg_id, value); + rt2x00_bbp_write(rt2x00dev, reg_id, value); + } + } + DEBUG("...End initialization from EEPROM.\n"); + + return 0; +} + +/* + * Radio control functions. + */ +static void rt2500pci_toggle_rx(struct rt2x00_dev *rt2x00dev, int enable) +{ + u32 reg; + + rt2x00_register_read(rt2x00dev, RXCSR0, ®); + rt2x00_set_field32(®, RXCSR0_DISABLE_RX, !enable); + rt2x00_register_write(rt2x00dev, RXCSR0, reg); +} + +static int rt2500pci_enable_radio(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + /* + * Initialize all registers. + */ + if (rt2500pci_init_rings(rt2x00dev) || + rt2500pci_init_registers(rt2x00dev) || + rt2500pci_init_bbp(rt2x00dev)) { + ERROR("Register initialization failed.\n"); + return -EIO; + } + + /* + * Clear interrupts. + */ + rt2x00_register_read(rt2x00dev, CSR7, ®); + rt2x00_register_write(rt2x00dev, CSR7, reg); + + /* + * Enable interrupts. + */ + rt2x00_register_read(rt2x00dev, CSR8, ®); + rt2x00_set_field32(®, CSR8_TBCN_EXPIRE, 0); + rt2x00_set_field32(®, CSR8_TXDONE_TXRING, 0); + rt2x00_set_field32(®, CSR8_TXDONE_ATIMRING, 0); + rt2x00_set_field32(®, CSR8_TXDONE_PRIORING, 0); + rt2x00_set_field32(®, CSR8_RXDONE, 0); + rt2x00_register_write(rt2x00dev, CSR8, reg); + + /* + * Enable LED + */ + rt2500pci_enable_led(rt2x00dev); + + return 0; +} + +static void rt2500pci_disable_radio(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + /* + * Disable LED + */ + rt2500pci_disable_led(rt2x00dev); + + rt2x00_register_write(rt2x00dev, PWRCSR0, 0); + + /* + * Disable synchronisation. + */ + rt2x00_register_write(rt2x00dev, CSR14, 0); + + /* + * Cancel RX and TX. + */ + rt2x00_register_read(rt2x00dev, TXCSR0, ®); + rt2x00_set_field32(®, TXCSR0_ABORT, 1); + rt2x00_register_write(rt2x00dev, TXCSR0, reg); + + /* + * Disable interrupts. + */ + rt2x00_register_read(rt2x00dev, CSR8, ®); + rt2x00_set_field32(®, CSR8_TBCN_EXPIRE, 1); + rt2x00_set_field32(®, CSR8_TXDONE_TXRING, 1); + rt2x00_set_field32(®, CSR8_TXDONE_ATIMRING, 1); + rt2x00_set_field32(®, CSR8_TXDONE_PRIORING, 1); + rt2x00_set_field32(®, CSR8_RXDONE, 1); + rt2x00_register_write(rt2x00dev, CSR8, reg); +} + +/* + * TX descriptor initialization + */ +static void rt2500pci_write_tx_desc(struct rt2x00_dev *rt2x00dev, + struct data_entry *entry, struct data_desc *txd, + struct data_entry_desc *desc, struct ieee80211_hdr *ieee80211hdr, + unsigned int length, struct ieee80211_tx_control *control) +{ + u32 word; + + /* + * Start writing the descriptor words. + */ + rt2x00_desc_read(txd, 2, &word); + rt2x00_set_field32(&word, TXD_W2_IV_OFFSET, IEEE80211_HEADER); + rt2x00_set_field32(&word, TXD_W2_AIFS, entry->ring->tx_params.aifs); + rt2x00_set_field32(&word, TXD_W2_CWMIN, entry->ring->tx_params.cw_min); + rt2x00_set_field32(&word, TXD_W2_CWMAX, entry->ring->tx_params.cw_max); + rt2x00_desc_write(txd, 2, word); + + rt2x00_desc_read(txd, 3, &word); + rt2x00_set_field32(&word, TXD_W3_PLCP_SIGNAL, desc->signal); + rt2x00_set_field32(&word, TXD_W3_PLCP_SERVICE, desc->service); + rt2x00_set_field32(&word, TXD_W3_PLCP_LENGTH_LOW, desc->length_low); + rt2x00_set_field32(&word, TXD_W3_PLCP_LENGTH_HIGH, desc->length_high); + rt2x00_desc_write(txd, 3, word); + + rt2x00_desc_read(txd, 10, &word); + rt2x00_set_field32(&word, TXD_W10_RTS, + !!(entry->reg & ENTRY_TXD_RTS_FRAME)); + rt2x00_desc_write(txd, 10, word); + + rt2x00_desc_read(txd, 0, &word); + rt2x00_set_field32(&word, TXD_W0_OWNER_NIC, 1); + rt2x00_set_field32(&word, TXD_W0_VALID, 1); + rt2x00_set_field32(&word, TXD_W0_MORE_FRAG, + !!(entry->reg & ENTRY_TXD_MORE_FRAG)); + rt2x00_set_field32(&word, TXD_W0_ACK, + !!(entry->reg & ENTRY_TXD_REQ_ACK)); + rt2x00_set_field32(&word, TXD_W0_TIMESTAMP, + !!(entry->reg & ENTRY_TXD_REQ_TIMESTAMP)); + rt2x00_set_field32(&word, TXD_W0_OFDM, + !!(entry->reg & ENTRY_TXD_OFDM_RATE)); + rt2x00_set_field32(&word, TXD_W0_CIPHER_OWNER, 1); + rt2x00_set_field32(&word, TXD_W0_IFS, desc->ifs); + rt2x00_set_field32(&word, TXD_W0_RETRY_MODE, 0); + rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, length); + rt2x00_set_field32(&word, TXD_W0_CIPHER_ALG, CIPHER_NONE); + rt2x00_desc_write(txd, 0, word); +} + +/* + * TX data initialization + */ +static void rt2500pci_kick_tx_queue(struct rt2x00_dev *rt2x00dev, int queue) +{ + u32 reg; + + rt2x00_register_read(rt2x00dev, TXCSR0, ®); + if (queue == IEEE80211_TX_QUEUE_DATA0) + rt2x00_set_field32(®, TXCSR0_KICK_PRIO, 1); + else if (queue == IEEE80211_TX_QUEUE_DATA1) + rt2x00_set_field32(®, TXCSR0_KICK_TX, 1); + else if (queue == IEEE80211_TX_QUEUE_AFTER_BEACON) + rt2x00_set_field32(®, TXCSR0_KICK_ATIM, 1); + rt2x00_register_write(rt2x00dev, TXCSR0, reg); +} + +static void rt2500pci_kick_beacon_gen(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + rt2x00_register_read(rt2x00dev, CSR14, ®); + if (!rt2x00_get_field32(reg, CSR14_BEACON_GEN)) { + rt2x00_set_field32(®, CSR14_BEACON_GEN, 1); + rt2x00_register_write(rt2x00dev, CSR14, reg); + } +} + +/* + * Interrupt functions. + */ +static void rt2500pci_rxdone(struct rt2x00_dev *rt2x00dev, int queue) +{ + struct data_ring *ring = &rt2x00dev->ring[queue]; + struct data_entry *entry; + struct data_desc *rxd; + u32 word0; + u32 word2; + int signal; + int rssi; + int ofdm; + u16 size; + + while (1) { + entry = rt2x00_get_data_entry(ring); + rxd = entry->priv; + rt2x00_desc_read(rxd, 0, &word0); + rt2x00_desc_read(rxd, 2, &word2); + + if (rt2x00_get_field32(word0, RXD_W0_OWNER_NIC)) + break; + + /* + * TODO: Don't we need to keep statistics + * updated about events like CRC and physical errors? + */ + if (rt2x00_get_field32(word0, RXD_W0_CRC) || + rt2x00_get_field32(word0, RXD_W0_PHYSICAL_ERROR)) + goto skip_entry; + + /* + * Obtain the status about this packet. + */ + size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT); + signal = rt2x00_get_field32(word2, RXD_W2_SIGNAL); + rssi = rt2x00_get_field32(word2, RXD_W2_RSSI); + ofdm = rt2x00_get_field32(word0, RXD_W0_OFDM); + + /* + * Send the packet to upper layer. + */ + rt2x00lib_rxdone(entry, entry->data_addr, size, + signal, rssi, ofdm); + +skip_entry: + if (GET_FLAG(ring->rt2x00dev, DEVICE_ENABLED_RADIO)) { + rt2x00_set_field32(&word0, RXD_W0_OWNER_NIC, 1); + rt2x00_desc_write(rxd, 0, word0); + } + + rt2x00_ring_index_inc(ring); + } + + /* + * Update LED. + */ + rt2500pci_activity_led(rt2x00dev, 0); +} + +static void rt2500pci_txdone(struct rt2x00_dev *rt2x00dev, const int queue) +{ + struct data_ring *ring = &rt2x00dev->ring[queue]; + struct data_entry *entry; + struct data_desc *txd; + u32 word; + int tx_status; + int retry; + + while (!rt2x00_ring_empty(ring)) { + entry = rt2x00_get_data_entry_done(ring); + txd = entry->priv; + rt2x00_desc_read(txd, 0, &word); + + if (rt2x00_get_field32(word, TXD_W0_OWNER_NIC) || + !rt2x00_get_field32(word, TXD_W0_VALID)) + break; + + /* + * Obtain the status about this packet. + */ + tx_status = rt2x00_get_field32(word, TXD_W0_RESULT); + retry = rt2x00_get_field32(word, TXD_W0_RETRY_COUNT); + + rt2x00lib_txdone(entry, tx_status, retry); + + /* + * Make this entry available for reuse. + */ + entry->reg = 0; + rt2x00_set_field32(&word, TXD_W0_VALID, 0); + rt2x00_desc_write(txd, 0, word); + rt2x00_ring_index_done_inc(ring); + } + + /* + * If the data ring was full before the txdone handler + * we must make sure the packet queue in the mac80211 stack + * is reenabled when the txdone handler has finished. + */ + entry = ring->entry; + if (!rt2x00_ring_full(ring)) + ieee80211_wake_queue(rt2x00dev->hw, + entry->tx_status.control.queue); +} + +static irqreturn_t rt2500pci_interrupt(int irq, void *dev_instance) +{ + struct rt2x00_dev *rt2x00dev = dev_instance; + u32 reg; + + /* + * Get the interrupt sources & saved to local variable. + * Write register value back to clear pending interrupts. + */ + rt2x00_register_read(rt2x00dev, CSR7, ®); + rt2x00_register_write(rt2x00dev, CSR7, reg); + + if (!reg) + return IRQ_NONE; + + if (!GET_FLAG(rt2x00dev, DEVICE_ENABLED_RADIO)) + return IRQ_HANDLED; + + /* + * Handle interrupts, walk through all bits + * and run the tasks, the bits are checked in order of + * priority. + */ + + /* + * 1 - Beacon timer expired interrupt. + */ + if (rt2x00_get_field32(reg, CSR7_TBCN_EXPIRE)) + rt2x00pci_beacondone(rt2x00dev, RING_BEACON); + + /* + * 2 - Rx ring done interrupt. + * Enable the TXRX activity led. + */ + if (rt2x00_get_field32(reg, CSR7_RXDONE)) { + rt2500pci_rxdone(rt2x00dev, RING_RX); + rt2500pci_activity_led(rt2x00dev, 1); + } + + /* + * 3 - Atim ring transmit done interrupt. + */ + if (rt2x00_get_field32(reg, CSR7_TXDONE_ATIMRING)) + rt2500pci_txdone(rt2x00dev, RING_ATIM); + + /* + * 4 - Priority ring transmit done interrupt. + */ + if (rt2x00_get_field32(reg, CSR7_TXDONE_PRIORING)) + rt2500pci_txdone(rt2x00dev, RING_PRIO); + + /* + * 5 - Tx ring transmit done interrupt. + */ + if (rt2x00_get_field32(reg, CSR7_TXDONE_TXRING)) + rt2500pci_txdone(rt2x00dev, RING_TX); + + return IRQ_HANDLED; +} + +/* + * Device initialization functions. + */ +static int rt2500pci_alloc_eeprom(struct rt2x00_dev *rt2x00dev) +{ + struct eeprom_93cx6 eeprom; + u32 reg; + + /* + * Allocate the eeprom memory, check the eeprom width + * and copy the entire eeprom into this allocated memory. + */ + rt2x00dev->eeprom = kzalloc(EEPROM_SIZE, GFP_KERNEL); + if (!rt2x00dev->eeprom) + return -ENOMEM; + + rt2x00_register_read(rt2x00dev, CSR21, ®); + + eeprom.data = rt2x00dev; + eeprom.register_read = rt2500pci_eepromregister_read; + eeprom.register_write = rt2500pci_eepromregister_write; + eeprom.width = rt2x00_get_field32(reg, CSR21_TYPE_93C46) ? + PCI_EEPROM_WIDTH_93C46 : PCI_EEPROM_WIDTH_93C66; + eeprom.reg_data_in = 0; + eeprom.reg_data_out = 0; + eeprom.reg_data_clock = 0; + eeprom.reg_chip_select = 0; + + eeprom_93cx6_multiread(&eeprom, EEPROM_BASE, rt2x00dev->eeprom, + EEPROM_SIZE / sizeof(u16)); + + return 0; +} + +static int rt2500pci_init_eeprom(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + u16 value; + u16 eeprom; + + /* + * Read EEPROM word for configuration. + */ + rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &eeprom); + + /* + * Identify RF chipset. + */ + value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE); + rt2x00_register_read(rt2x00dev, CSR0, ®); + rt2x00_set_chip(&rt2x00dev->chip, RT2560, value, reg); + + if (!rt2x00_rf(&rt2x00dev->chip, RF2522) && + !rt2x00_rf(&rt2x00dev->chip, RF2523) && + !rt2x00_rf(&rt2x00dev->chip, RF2524) && + !rt2x00_rf(&rt2x00dev->chip, RF2525) && + !rt2x00_rf(&rt2x00dev->chip, RF2525E) && + !rt2x00_rf(&rt2x00dev->chip, RF5222)) { + ERROR("Invalid RF chipset detected.\n"); + return -ENODEV; + } + + /* + * Identify default antenna configuration. + */ + rt2x00dev->hw->conf.antenna_sel_tx = rt2x00_get_field16(eeprom, + EEPROM_ANTENNA_TX_DEFAULT); + rt2x00dev->hw->conf.antenna_sel_rx = rt2x00_get_field16(eeprom, + EEPROM_ANTENNA_RX_DEFAULT); + + /* + * Store led mode, for correct led behaviour. + */ + rt2x00dev->led_mode = rt2x00_get_field16(eeprom, + EEPROM_ANTENNA_LED_MODE); + + /* + * Detect if this device has an hardware controlled radio. + */ + if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO)) + SET_FLAG(rt2x00dev, DEVICE_SUPPORT_HW_BUTTON); + + /* + * Check if the BBP tuning should be enabled. + */ + rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &eeprom); + if (eeprom == 0xffff) + eeprom = 0; + if (rt2x00_get_field16(eeprom, EEPROM_NIC_DYN_BBP_TUNE)) + SET_FLAG(rt2x00dev, CONFIG_DISABLE_LINK_TUNING); + + /* + * Read the RSSI <-> dBm offset information. + */ + rt2x00_eeprom_read(rt2x00dev, EEPROM_CALIBRATE_OFFSET, &eeprom); + rt2x00dev->hw->max_rssi = + rt2x00_get_field16(eeprom, EEPROM_CALIBRATE_OFFSET_RSSI); + if (rt2x00dev->hw->max_rssi < 0 || rt2x00dev->hw->max_rssi == (s8)0xff) + rt2x00dev->hw->max_rssi = MAX_RX_SSI; + + return 0; +} + +static const struct { + unsigned int chip; + u32 val[3]; +} rf_vals[] = { + { RF2522, { 0x00002050, 0x00000101, 0x00000000 } }, + { RF2523, { 0x00022010, 0x000e0111, 0x00000a1b } }, + { RF2524, { 0x00032020, 0x00000101, 0x00000a1b } }, + { RF2525, { 0x00022020, 0x00060111, 0x00000a1b } }, + { RF2525E, { 0x00022020, 0x00060111, 0x00000a0b } }, + { RF5222, { 0x00000000, 0x00000101, 0x00000000 } }, +}; + +/* + * RF value list for RF2522 + * Supports: 2.4 GHz + */ +static const u32 rf_vals_bg_2522[] = { + 0x000c1fda, 0x000c1fee, 0x000c2002, 0x000c2016, 0x000c202a, + 0x000c203e, 0x000c2052, 0x000c2066, 0x000c207a, 0x000c208e, + 0x000c20a2, 0x000c20b6, 0x000c20ca, 0x000c20fa +}; + +/* + * RF value list for RF2523, RF2524 & RF2525 + * Supports: 2.4 GHz + */ +static const u32 rf_vals_bg_252x[] = { + 0x00000c9e, 0x00000ca2, 0x00000ca6, 0x00000caa, 0x00000cae, + 0x00000cb2, 0x00000cb6, 0x00000cba, 0x00000cbe, 0x00000d02, + 0x00000d06, 0x00000d0a, 0x00000d0e, 0x00000d1a +}; + +/* + * RF value list for RF2525E & RF5222 + * Supports: 2.4 GHz + */ +static const u32 rf_vals_bg_5x[] = { + 0x00001136, 0x0000113a, 0x0000113e, 0x00001182, 0x00001186, + 0x0000118a, 0x0000118e, 0x00001192, 0x00001196, 0x0000119a, + 0x0000119e, 0x000011a2, 0x000011a6, 0x000011ae +}; + +/* + * RF value list for RF5222 (supplement to rf_vals_bg_5x) + * Supports: 5.2 GHz + */ +static const u32 rf_vals_a_5x[] = { + 0x00018896, 0x0001889a, 0x0001889e, 0x000188a2, 0x000188a6, + 0x000188aa, 0x000188ae, 0x000188b2, 0x00008802, 0x00008806, + 0x0000880a, 0x0000880e, 0x00008812, 0x00008816, 0x0000881a, + 0x0000881e, 0x00008822, 0x00008826, 0x0000882a, 0x000090a6, + 0x000090ae, 0x000090b6, 0x000090be +}; + +static void rt2500pci_init_hw_mode(struct rt2x00_dev *rt2x00dev) +{ + struct hw_mode_spec *spec = &rt2x00dev->spec; + u8 *txpower; + unsigned int i; + + if (GET_FLAG(rt2x00dev, DEVICE_INITIALIZED_HW)) + return; + + /* + * Initialize all hw fields. + */ + rt2x00dev->hw->flags = IEEE80211_HW_HOST_GEN_BEACON | + IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | + IEEE80211_HW_WEP_INCLUDE_IV | + IEEE80211_HW_DATA_NULLFUNC_ACK | + IEEE80211_HW_NO_TKIP_WMM_HWACCEL | + IEEE80211_HW_MONITOR_DURING_OPER; + rt2x00dev->hw->extra_tx_headroom = 0; + rt2x00dev->hw->max_rssi = MAX_RX_SSI; + rt2x00dev->hw->max_noise = MAX_RX_NOISE; + rt2x00dev->hw->queues = RING_NUM_TX; + + /* + * This device supports ATIM + */ + SET_FLAG(rt2x00dev, DEVICE_SUPPORT_ATIM); + + /* + * Set device specific, but channel independent RF values. + */ + for (i = 0; i < ARRAY_SIZE(rf_vals); i++) { + if (rt2x00_rf(&rt2x00dev->chip, rf_vals[i].chip)) { + rt2x00dev->rf1 = rf_vals[i].val[0]; + rt2x00dev->rf3 = rf_vals[i].val[1]; + rt2x00dev->rf4 = rf_vals[i].val[2]; + } + } + + /* + * Convert tx_power array in eeprom. + */ + txpower = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_START); + for (i = 0; i < 14; i++) + txpower[i] = TXPOWER_FROM_DEV(txpower[i]); + + /* + * Initialize hw_mode information. + */ + spec->mac_addr = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); + spec->num_modes = 2; + spec->num_rates = 12; + spec->num_channels = 14; + spec->tx_power_a = NULL; + spec->tx_power_bg = txpower; + spec->tx_power_default = DEFAULT_TXPOWER; + spec->chan_val_a = NULL; + + if (rt2x00_rf(&rt2x00dev->chip, RF2522)) + spec->chan_val_bg = rf_vals_bg_2522; + else if (rt2x00_rf(&rt2x00dev->chip, RF2523) || + rt2x00_rf(&rt2x00dev->chip, RF2524) || + rt2x00_rf(&rt2x00dev->chip, RF2525)) + spec->chan_val_bg = rf_vals_bg_252x; + else if (rt2x00_rf(&rt2x00dev->chip, RF2525E) || + rt2x00_rf(&rt2x00dev->chip, RF5222)) + spec->chan_val_bg = rf_vals_bg_5x; + + if (rt2x00_rf(&rt2x00dev->chip, RF5222)) { + spec->num_modes = 3; + spec->num_channels += 23; + spec->chan_val_a = rf_vals_a_5x; + } +} + +static int rt2500pci_init_hw(struct rt2x00_dev *rt2x00dev) +{ + int retval; + + rt2x00dev->rxd_size = RXD_DESC_SIZE; + rt2x00dev->txd_size = TXD_DESC_SIZE; + + /* + * Allocate eeprom data. + */ + retval = rt2500pci_alloc_eeprom(rt2x00dev); + if (retval) + return retval; + + retval = rt2500pci_init_eeprom(rt2x00dev); + if (retval) + return retval; + + /* + * Initialize hw specifications. + */ + rt2500pci_init_hw_mode(rt2x00dev); + + return 0; +} + +/* + * IEEE80211 stack callback functions. + */ +static int rt2500pci_get_stats(struct ieee80211_hw *hw, + struct ieee80211_low_level_stats *stats) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + u32 reg; + + /* + * Update FCS error count from register. + * The dot11ACKFailureCount, dot11RTSFailureCount and + * dot11RTSSuccessCount are updated in interrupt time. + */ + rt2x00_register_read(rt2x00dev, CNT0, ®); + rt2x00dev->low_level_stats.dot11FCSErrorCount += + rt2x00_get_field32(reg, CNT0_FCS_ERROR); + + memcpy(stats, &rt2x00dev->low_level_stats, sizeof(*stats)); + + return 0; +} + +static int rt2500pci_set_retry_limit(struct ieee80211_hw *hw, + u32 short_retry, u32 long_retry) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + u32 reg; + + rt2x00_register_read(rt2x00dev, CSR11, ®); + rt2x00_set_field32(®, CSR11_LONG_RETRY, long_retry); + rt2x00_set_field32(®, CSR11_SHORT_RETRY, short_retry); + rt2x00_register_write(rt2x00dev, CSR11, reg); + + return 0; +} + +static u64 rt2500pci_get_tsf(struct ieee80211_hw *hw) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + u64 tsf; + u32 reg; + + rt2x00_register_read(rt2x00dev, CSR17, ®); + tsf = (u64)rt2x00_get_field32(reg, CSR17_HIGH_TSFTIMER) << 32; + rt2x00_register_read(rt2x00dev, CSR16, ®); + tsf |= rt2x00_get_field32(reg, CSR16_LOW_TSFTIMER); + + return tsf; +} + +static void rt2500pci_reset_tsf(struct ieee80211_hw *hw) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + + rt2x00_register_write(rt2x00dev, CSR16, 0); + rt2x00_register_write(rt2x00dev, CSR17, 0); +} + +static int rt2500pci_tx_last_beacon(struct ieee80211_hw *hw) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + u32 reg; + + rt2x00_register_read(rt2x00dev, CSR15, ®); + return rt2x00_get_field32(reg, CSR15_BEACON_SENT); +} + +static const struct ieee80211_ops rt2500pci_mac80211_ops = { + .tx = rt2x00lib_tx, + .reset = rt2x00lib_reset, + .add_interface = rt2x00lib_add_interface, + .remove_interface = rt2x00lib_remove_interface, + .config = rt2x00lib_config, + .config_interface = rt2x00lib_config_interface, + .set_multicast_list = rt2x00lib_set_multicast_list, + .get_stats = rt2500pci_get_stats, + .set_retry_limit = rt2500pci_set_retry_limit, + .conf_tx = rt2x00lib_conf_tx, + .get_tx_stats = rt2x00lib_get_tx_stats, + .get_tsf = rt2500pci_get_tsf, + .reset_tsf = rt2500pci_reset_tsf, + .beacon_update = rt2x00pci_beacon_update, + .tx_last_beacon = rt2500pci_tx_last_beacon, +}; + +static const struct rt2x00lib_ops rt2500pci_rt2x00_ops = { + .irq_handler = rt2500pci_interrupt, + .link_tuner = rt2500pci_link_tuner, + .init_hw = rt2500pci_init_hw, + .initialize = rt2x00pci_initialize, + .uninitialize = rt2x00pci_uninitialize, + .enable_radio = rt2500pci_enable_radio, + .disable_radio = rt2500pci_disable_radio, + .set_state = rt2500pci_set_state, + .toggle_rx = rt2500pci_toggle_rx, + .write_tx_desc = rt2500pci_write_tx_desc, + .write_tx_data = rt2x00pci_write_tx_data, + .kick_tx_queue = rt2500pci_kick_tx_queue, + .kick_beacon_gen = rt2500pci_kick_beacon_gen, + .config_type = rt2500pci_config_type, + .config_phymode = rt2500pci_config_phymode, + .config_channel = rt2500pci_config_channel, + .config_mac_addr = rt2500pci_config_mac_addr, + .config_bssid = rt2500pci_config_bssid, + .config_promisc = rt2500pci_config_promisc, + .config_txpower = rt2500pci_config_txpower, + .config_antenna = rt2500pci_config_antenna, + .config_duration = rt2500pci_config_duration, +}; + +static const struct rt2x00_ops rt2500pci_ops = { + .lib = &rt2500pci_rt2x00_ops, + .hw = &rt2500pci_mac80211_ops, +#ifdef CONFIG_RT2X00_DEBUGFS + .debugfs = &rt2500pci_rt2x00debug, +#endif /* CONFIG_RT2X00_DEBUGFS */ +}; + +/* + * RT2500pci module information. + */ +static char version[] = + DRV_NAME " - " DRV_VERSION " (" DRV_RELDATE ") by " DRV_PROJECT; + +static struct pci_device_id rt2500pci_device_table[] = { + { PCI_DEVICE(0x1814, 0x0201), PCI_DEVICE_DATA(&rt2500pci_ops) }, + { 0, } +}; + +MODULE_AUTHOR(DRV_PROJECT); +MODULE_VERSION(DRV_VERSION); +MODULE_DESCRIPTION("Ralink RT2500 PCI & PCMCIA Wireless LAN driver."); +MODULE_SUPPORTED_DEVICE("Ralink RT2560 PCI & PCMCIA chipset based cards"); +MODULE_DEVICE_TABLE(pci, rt2500pci_device_table); +MODULE_LICENSE("GPL"); + +#ifdef CONFIG_RT2X00_DEBUG +module_param_named(debug, rt2x00_debug_level, bool, S_IWUSR | S_IRUGO); +MODULE_PARM_DESC(debug, "Set this parameter to 1 to enable debug output."); +#endif /* CONFIG_RT2X00_DEBUG */ + +static struct pci_driver rt2500pci_driver = { + .name = DRV_NAME, + .id_table = rt2500pci_device_table, + .probe = rt2x00pci_probe, + .remove = __devexit_p(rt2x00pci_remove), +#ifdef CONFIG_PM + .suspend = rt2x00pci_suspend, + .resume = rt2x00pci_resume, +#endif /* CONFIG_PM */ +}; + +static int __init rt2500pci_init(void) +{ + printk(KERN_INFO "Loading module: %s.\n", version); + return pci_register_driver(&rt2500pci_driver); +} + +static void __exit rt2500pci_exit(void) +{ + printk(KERN_INFO "Unloading module: %s.\n", version); + pci_unregister_driver(&rt2500pci_driver); +} + +module_init(rt2500pci_init); +module_exit(rt2500pci_exit); diff --git a/drivers/net/wireless/mac80211/rt2x00/rt2500pci.h b/drivers/net/wireless/mac80211/rt2x00/rt2500pci.h new file mode 100644 index 0000000..a4f8dd7 --- /dev/null +++ b/drivers/net/wireless/mac80211/rt2x00/rt2500pci.h @@ -0,0 +1,1185 @@ +/* + Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + + + 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. + */ + +/* + Module: rt2500pci + Abstract: Data structures and registers for the rt2500pci module. + Supported chipsets: RT2560. + */ + +#ifndef RT2500PCI_H +#define RT2500PCI_H + +/* + * RF chip defines. + */ +#define RF2522 0x0000 +#define RF2523 0x0001 +#define RF2524 0x0002 +#define RF2525 0x0003 +#define RF2525E 0x0004 +#define RF5222 0x0010 + +/* + * RT2560 version + */ +#define RT2560_VERSION_B 2 +#define RT2560_VERSION_C 3 +#define RT2560_VERSION_D 4 + +/* + * Max RSSI value, required for RSSI <-> dBm conversion. + */ +#define MAX_RX_SSI 120 +#define MAX_RX_NOISE -110 + +/* + * Register layout information. + */ +#define CSR_REG_BASE 0x0000 +#define CSR_REG_SIZE 0x0174 +#define EEPROM_BASE 0x0000 +#define EEPROM_SIZE 0x0200 +#define BBP_SIZE 0x0040 + +/* + * Control/Status Registers(CSR). + * Some values are set in TU, whereas 1 TU == 1024 us. + */ + +/* + * CSR0: ASIC revision number. + */ +#define CSR0 0x0000 + +/* + * CSR1: System control register. + * SOFT_RESET: Software reset, 1: reset, 0: normal. + * BBP_RESET: Hardware reset, 1: reset, 0, release. + * HOST_READY: Host ready after initialization. + */ +#define CSR1 0x0004 +#define CSR1_SOFT_RESET FIELD32(0x00000001) +#define CSR1_BBP_RESET FIELD32(0x00000002) +#define CSR1_HOST_READY FIELD32(0x00000004) + +/* + * CSR2: System admin status register (invalid). + */ +#define CSR2 0x0008 + +/* + * CSR3: STA MAC address register 0. + */ +#define CSR3 0x000c +#define CSR3_BYTE0 FIELD32(0x000000ff) +#define CSR3_BYTE1 FIELD32(0x0000ff00) +#define CSR3_BYTE2 FIELD32(0x00ff0000) +#define CSR3_BYTE3 FIELD32(0xff000000) + +/* + * CSR4: STA MAC address register 1. + */ +#define CSR4 0x0010 +#define CSR4_BYTE4 FIELD32(0x000000ff) +#define CSR4_BYTE5 FIELD32(0x0000ff00) + +/* + * CSR5: BSSID register 0. + */ +#define CSR5 0x0014 +#define CSR5_BYTE0 FIELD32(0x000000ff) +#define CSR5_BYTE1 FIELD32(0x0000ff00) +#define CSR5_BYTE2 FIELD32(0x00ff0000) +#define CSR5_BYTE3 FIELD32(0xff000000) + +/* + * CSR6: BSSID register 1. + */ +#define CSR6 0x0018 +#define CSR6_BYTE4 FIELD32(0x000000ff) +#define CSR6_BYTE5 FIELD32(0x0000ff00) + +/* + * CSR7: Interrupt source register. + * Write 1 to clear. + * TBCN_EXPIRE: Beacon timer expired interrupt. + * TWAKE_EXPIRE: Wakeup timer expired interrupt. + * TATIMW_EXPIRE: Timer of atim window expired interrupt. + * TXDONE_TXRING: Tx ring transmit done interrupt. + * TXDONE_ATIMRING: Atim ring transmit done interrupt. + * TXDONE_PRIORING: Priority ring transmit done interrupt. + * RXDONE: Receive done interrupt. + * DECRYPTION_DONE: Decryption done interrupt. + * ENCRYPTION_DONE: Encryption done interrupt. + * UART1_TX_TRESHOLD: UART1 TX reaches threshold. + * UART1_RX_TRESHOLD: UART1 RX reaches threshold. + * UART1_IDLE_TRESHOLD: UART1 IDLE over threshold. + * UART1_TX_BUFF_ERROR: UART1 TX buffer error. + * UART1_RX_BUFF_ERROR: UART1 RX buffer error. + * UART2_TX_TRESHOLD: UART2 TX reaches threshold. + * UART2_RX_TRESHOLD: UART2 RX reaches threshold. + * UART2_IDLE_TRESHOLD: UART2 IDLE over threshold. + * UART2_TX_BUFF_ERROR: UART2 TX buffer error. + * UART2_RX_BUFF_ERROR: UART2 RX buffer error. + * TIMER_CSR3_EXPIRE: TIMECSR3 timer expired (802.1H quiet period). + + */ +#define CSR7 0x001c +#define CSR7_TBCN_EXPIRE FIELD32(0x00000001) +#define CSR7_TWAKE_EXPIRE FIELD32(0x00000002) +#define CSR7_TATIMW_EXPIRE FIELD32(0x00000004) +#define CSR7_TXDONE_TXRING FIELD32(0x00000008) +#define CSR7_TXDONE_ATIMRING FIELD32(0x00000010) +#define CSR7_TXDONE_PRIORING FIELD32(0x00000020) +#define CSR7_RXDONE FIELD32(0x00000040) +#define CSR7_DECRYPTION_DONE FIELD32(0x00000080) +#define CSR7_ENCRYPTION_DONE FIELD32(0x00000100) +#define CSR7_UART1_TX_TRESHOLD FIELD32(0x00000200) +#define CSR7_UART1_RX_TRESHOLD FIELD32(0x00000400) +#define CSR7_UART1_IDLE_TRESHOLD FIELD32(0x00000800) +#define CSR7_UART1_TX_BUFF_ERROR FIELD32(0x00001000) +#define CSR7_UART1_RX_BUFF_ERROR FIELD32(0x00002000) +#define CSR7_UART2_TX_TRESHOLD FIELD32(0x00004000) +#define CSR7_UART2_RX_TRESHOLD FIELD32(0x00008000) +#define CSR7_UART2_IDLE_TRESHOLD FIELD32(0x00010000) +#define CSR7_UART2_TX_BUFF_ERROR FIELD32(0x00020000) +#define CSR7_UART2_RX_BUFF_ERROR FIELD32(0x00040000) +#define CSR7_TIMER_CSR3_EXPIRE FIELD32(0x00080000) + +/* + * CSR8: Interrupt mask register. + * Write 1 to mask interrupt. + * TBCN_EXPIRE: Beacon timer expired interrupt. + * TWAKE_EXPIRE: Wakeup timer expired interrupt. + * TATIMW_EXPIRE: Timer of atim window expired interrupt. + * TXDONE_TXRING: Tx ring transmit done interrupt. + * TXDONE_ATIMRING: Atim ring transmit done interrupt. + * TXDONE_PRIORING: Priority ring transmit done interrupt. + * RXDONE: Receive done interrupt. + * DECRYPTION_DONE: Decryption done interrupt. + * ENCRYPTION_DONE: Encryption done interrupt. + * UART1_TX_TRESHOLD: UART1 TX reaches threshold. + * UART1_RX_TRESHOLD: UART1 RX reaches threshold. + * UART1_IDLE_TRESHOLD: UART1 IDLE over threshold. + * UART1_TX_BUFF_ERROR: UART1 TX buffer error. + * UART1_RX_BUFF_ERROR: UART1 RX buffer error. + * UART2_TX_TRESHOLD: UART2 TX reaches threshold. + * UART2_RX_TRESHOLD: UART2 RX reaches threshold. + * UART2_IDLE_TRESHOLD: UART2 IDLE over threshold. + * UART2_TX_BUFF_ERROR: UART2 TX buffer error. + * UART2_RX_BUFF_ERROR: UART2 RX buffer error. + * TIMER_CSR3_EXPIRE: TIMECSR3 timer expired (802.1H quiet period). + */ +#define CSR8 0x0020 +#define CSR8_TBCN_EXPIRE FIELD32(0x00000001) +#define CSR8_TWAKE_EXPIRE FIELD32(0x00000002) +#define CSR8_TATIMW_EXPIRE FIELD32(0x00000004) +#define CSR8_TXDONE_TXRING FIELD32(0x00000008) +#define CSR8_TXDONE_ATIMRING FIELD32(0x00000010) +#define CSR8_TXDONE_PRIORING FIELD32(0x00000020) +#define CSR8_RXDONE FIELD32(0x00000040) +#define CSR8_DECRYPTION_DONE FIELD32(0x00000080) +#define CSR8_ENCRYPTION_DONE FIELD32(0x00000100) +#define CSR8_UART1_TX_TRESHOLD FIELD32(0x00000200) +#define CSR8_UART1_RX_TRESHOLD FIELD32(0x00000400) +#define CSR8_UART1_IDLE_TRESHOLD FIELD32(0x00000800) +#define CSR8_UART1_TX_BUFF_ERROR FIELD32(0x00001000) +#define CSR8_UART1_RX_BUFF_ERROR FIELD32(0x00002000) +#define CSR8_UART2_TX_TRESHOLD FIELD32(0x00004000) +#define CSR8_UART2_RX_TRESHOLD FIELD32(0x00008000) +#define CSR8_UART2_IDLE_TRESHOLD FIELD32(0x00010000) +#define CSR8_UART2_TX_BUFF_ERROR FIELD32(0x00020000) +#define CSR8_UART2_RX_BUFF_ERROR FIELD32(0x00040000) +#define CSR8_TIMER_CSR3_EXPIRE FIELD32(0x00080000) + +/* + * CSR9: Maximum frame length register. + * MAX_FRAME_UNIT: Maximum frame length in 128b unit, default: 12. + */ +#define CSR9 0x0024 +#define CSR9_MAX_FRAME_UNIT FIELD32(0x00000f80) + +/* + * SECCSR0: WEP control register. + * KICK_DECRYPT: Kick decryption engine, self-clear. + * ONE_SHOT: 0: ring mode, 1: One shot only mode. + * DESC_ADDRESS: Descriptor physical address of frame. + */ +#define SECCSR0 0x0028 +#define SECCSR0_KICK_DECRYPT FIELD32(0x00000001) +#define SECCSR0_ONE_SHOT FIELD32(0x00000002) +#define SECCSR0_DESC_ADDRESS FIELD32(0xfffffffc) + +/* + * CSR11: Back-off control register. + * CWMIN: CWmin. Default cwmin is 31 (2^5 - 1). + * CWMAX: CWmax. Default cwmax is 1023 (2^10 - 1). + * SLOT_TIME: Slot time, default is 20us for 802.11b + * CW_SELECT: CWmin/CWmax selection, 1: Register, 0: TXD. + * LONG_RETRY: Long retry count. + * SHORT_RETRY: Short retry count. + */ +#define CSR11 0x002c +#define CSR11_CWMIN FIELD32(0x0000000f) +#define CSR11_CWMAX FIELD32(0x000000f0) +#define CSR11_SLOT_TIME FIELD32(0x00001f00) +#define CSR11_CW_SELECT FIELD32(0x00002000) +#define CSR11_LONG_RETRY FIELD32(0x00ff0000) +#define CSR11_SHORT_RETRY FIELD32(0xff000000) + +/* + * CSR12: Synchronization configuration register 0. + * All units in 1/16 TU. + * BEACON_INTERVAL: Beacon interval, default is 100 TU. + * CFPMAX_DURATION: Cfp maximum duration, default is 100 TU. + */ +#define CSR12 0x0030 +#define CSR12_BEACON_INTERVAL FIELD32(0x0000ffff) +#define CSR12_CFPMAX_DURATION FIELD32(0xffff0000) + +/* + * CSR13: Synchronization configuration register 1. + * All units in 1/16 TU. + * ATIMW_DURATION: Atim window duration. + * CFP_PERIOD: Cfp period, default is 0 TU. + */ +#define CSR13 0x0034 +#define CSR13_ATIMW_DURATION FIELD32(0x0000ffff) +#define CSR13_CFP_PERIOD FIELD32(0x00ff0000) + +/* + * CSR14: Synchronization control register. + * TSF_COUNT: Enable tsf auto counting. + * TSF_SYNC: Tsf sync, 0: disable, 1: infra, 2: ad-hoc/master mode. + * TBCN: Enable tbcn with reload value. + * TCFP: Enable tcfp & cfp / cp switching. + * TATIMW: Enable tatimw & atim window switching. + * BEACON_GEN: Enable beacon generator. + * CFP_COUNT_PRELOAD: Cfp count preload value. + * TBCM_PRELOAD: Tbcn preload value in units of 64us. + */ +#define CSR14 0x0038 +#define CSR14_TSF_COUNT FIELD32(0x00000001) +#define CSR14_TSF_SYNC FIELD32(0x00000006) +#define CSR14_TBCN FIELD32(0x00000008) +#define CSR14_TCFP FIELD32(0x00000010) +#define CSR14_TATIMW FIELD32(0x00000020) +#define CSR14_BEACON_GEN FIELD32(0x00000040) +#define CSR14_CFP_COUNT_PRELOAD FIELD32(0x0000ff00) +#define CSR14_TBCM_PRELOAD FIELD32(0xffff0000) + +/* + * CSR15: Synchronization status register. + * CFP: ASIC is in contention-free period. + * ATIMW: ASIC is in ATIM window. + * BEACON_SENT: Beacon is send. + */ +#define CSR15 0x003c +#define CSR15_CFP FIELD32(0x00000001) +#define CSR15_ATIMW FIELD32(0x00000002) +#define CSR15_BEACON_SENT FIELD32(0x00000004) + +/* + * CSR16: TSF timer register 0. + */ +#define CSR16 0x0040 +#define CSR16_LOW_TSFTIMER FIELD32(0xffffffff) + +/* + * CSR17: TSF timer register 1. + */ +#define CSR17 0x0044 +#define CSR17_HIGH_TSFTIMER FIELD32(0xffffffff) + +/* + * CSR18: IFS timer register 0. + * SIFS: Sifs, default is 10 us. + * PIFS: Pifs, default is 30 us. + */ +#define CSR18 0x0048 +#define CSR18_SIFS FIELD32(0x000001ff) +#define CSR18_PIFS FIELD32(0x001f0000) + +/* + * CSR19: IFS timer register 1. + * DIFS: Difs, default is 50 us. + * EIFS: Eifs, default is 364 us. + */ +#define CSR19 0x004c +#define CSR19_DIFS FIELD32(0x0000ffff) +#define CSR19_EIFS FIELD32(0xffff0000) + +/* + * CSR20: Wakeup timer register. + * DELAY_AFTER_TBCN: Delay after tbcn expired in units of 1/16 TU. + * TBCN_BEFORE_WAKEUP: Number of beacon before wakeup. + * AUTOWAKE: Enable auto wakeup / sleep mechanism. + */ +#define CSR20 0x0050 +#define CSR20_DELAY_AFTER_TBCN FIELD32(0x0000ffff) +#define CSR20_TBCN_BEFORE_WAKEUP FIELD32(0x00ff0000) +#define CSR20_AUTOWAKE FIELD32(0x01000000) + +/* + * CSR21: EEPROM control register. + * RELOAD: Write 1 to reload eeprom content. + * TYPE_93C46: 1: 93c46, 0:93c66. + */ +#define CSR21 0x0054 +#define CSR21_RELOAD FIELD32(0x00000001) +#define CSR21_EEPROM_DATA_CLOCK FIELD32(0x00000002) +#define CSR21_EEPROM_CHIP_SELECT FIELD32(0x00000004) +#define CSR21_EEPROM_DATA_IN FIELD32(0x00000008) +#define CSR21_EEPROM_DATA_OUT FIELD32(0x00000010) +#define CSR21_TYPE_93C46 FIELD32(0x00000020) + +/* + * CSR22: CFP control register. + * CFP_DURATION_REMAIN: Cfp duration remain, in units of TU. + * RELOAD_CFP_DURATION: Write 1 to reload cfp duration remain. + */ +#define CSR22 0x0058 +#define CSR22_CFP_DURATION_REMAIN FIELD32(0x0000ffff) +#define CSR22_RELOAD_CFP_DURATION FIELD32(0x00010000) + +/* + * Transmit related CSRs. + * Some values are set in TU, whereas 1 TU == 1024 us. + */ + +/* + * TXCSR0: TX Control Register. + * KICK_TX: Kick tx ring. + * KICK_ATIM: Kick atim ring. + * KICK_PRIO: Kick priority ring. + * ABORT: Abort all transmit related ring operation. + */ +#define TXCSR0 0x0060 +#define TXCSR0_KICK_TX FIELD32(0x00000001) +#define TXCSR0_KICK_ATIM FIELD32(0x00000002) +#define TXCSR0_KICK_PRIO FIELD32(0x00000004) +#define TXCSR0_ABORT FIELD32(0x00000008) + +/* + * TXCSR1: TX Configuration Register. + * ACK_TIMEOUT: Ack timeout, default = sifs + 2*slottime + acktime @ 1mbps. + * ACK_CONSUME_TIME: Ack consume time, default = sifs + acktime @ 1mbps. + * TSF_OFFSET: Insert tsf offset. + * AUTORESPONDER: Enable auto responder which include ack & cts. + */ +#define TXCSR1 0x0064 +#define TXCSR1_ACK_TIMEOUT FIELD32(0x000001ff) +#define TXCSR1_ACK_CONSUME_TIME FIELD32(0x0003fe00) +#define TXCSR1_TSF_OFFSET FIELD32(0x00fc0000) +#define TXCSR1_AUTORESPONDER FIELD32(0x01000000) + +/* + * TXCSR2: Tx descriptor configuration register. + * TXD_SIZE: Tx descriptor size, default is 48. + * NUM_TXD: Number of tx entries in ring. + * NUM_ATIM: Number of atim entries in ring. + * NUM_PRIO: Number of priority entries in ring. + */ +#define TXCSR2 0x0068 +#define TXCSR2_TXD_SIZE FIELD32(0x000000ff) +#define TXCSR2_NUM_TXD FIELD32(0x0000ff00) +#define TXCSR2_NUM_ATIM FIELD32(0x00ff0000) +#define TXCSR2_NUM_PRIO FIELD32(0xff000000) + +/* + * TXCSR3: TX Ring Base address register. + */ +#define TXCSR3 0x006c +#define TXCSR3_TX_RING_REGISTER FIELD32(0xffffffff) + +/* + * TXCSR4: TX Atim Ring Base address register. + */ +#define TXCSR4 0x0070 +#define TXCSR4_ATIM_RING_REGISTER FIELD32(0xffffffff) + +/* + * TXCSR5: TX Prio Ring Base address register. + */ +#define TXCSR5 0x0074 +#define TXCSR5_PRIO_RING_REGISTER FIELD32(0xffffffff) + +/* + * TXCSR6: Beacon Base address register. + */ +#define TXCSR6 0x0078 +#define TXCSR6_BEACON_RING_REGISTER FIELD32(0xffffffff) + +/* + * TXCSR7: Auto responder control register. + * AR_POWERMANAGEMENT: Auto responder power management bit. + */ +#define TXCSR7 0x007c +#define TXCSR7_AR_POWERMANAGEMENT FIELD32(0x00000001) + +/* + * TXCSR8: CCK Tx BBP register. + * CCK_SIGNAL: BBP rate field address for CCK. + * CCK_SERVICE: BBP service field address for CCK. + * CCK_LENGTH_LOW: BBP length low byte address for CCK. + * CCK_LENGTH_HIGH: BBP length high byte address for CCK. + */ +#define TXCSR8 0x0098 +#define TXCSR8_CCK_SIGNAL FIELD32(0x000000ff) +#define TXCSR8_CCK_SERVICE FIELD32(0x0000ff00) +#define TXCSR8_CCK_LENGTH_LOW FIELD32(0x00ff0000) +#define TXCSR8_CCK_LENGTH_HIGH FIELD32(0xff000000) + +/* + * TXCSR9: OFDM TX BBP registers + * OFDM_SIGNAL: BBP rate field address for OFDM. + * OFDM_SERVICE: BBP service field address for OFDM. + * OFDM_LENGTH_LOW: BBP length low byte address for OFDM. + * OFDM_LENGTH_HIGH: BBP length high byte address for OFDM. + */ +#define TXCSR9 0x0094 +#define TXCSR9_OFDM_RATE FIELD32(0x000000ff) +#define TXCSR9_OFDM_SERVICE FIELD32(0x0000ff00) +#define TXCSR9_OFDM_LENGTH_LOW FIELD32(0x00ff0000) +#define TXCSR9_OFDM_LENGTH_HIGH FIELD32(0xff000000) + +/* + * Receive related CSRs. + * Some values are set in TU, whereas 1 TU == 1024 us. + */ + +/* + * RXCSR0: RX Control Register. + * DISABLE_RX: Disable rx engine. + * DROP_CRC: Drop crc error. + * DROP_PHYSICAL: Drop physical error. + * DROP_CONTROL: Drop control frame. + * DROP_NOT_TO_ME: Drop not to me unicast frame. + * DROP_TODS: Drop frame tods bit is true. + * DROP_VERSION_ERROR: Drop version error frame. + * PASS_CRC: Pass all packets with crc attached. + * PASS_CRC: Pass all packets with crc attached. + * PASS_PLCP: Pass all packets with 4 bytes PLCP attached. + * DROP_MCAST: Drop multicast frames. + * DROP_BCAST: Drop broadcast frames. + * ENABLE_QOS: Accept QOS data frame and parse QOS field. + */ +#define RXCSR0 0x0080 +#define RXCSR0_DISABLE_RX FIELD32(0x00000001) +#define RXCSR0_DROP_CRC FIELD32(0x00000002) +#define RXCSR0_DROP_PHYSICAL FIELD32(0x00000004) +#define RXCSR0_DROP_CONTROL FIELD32(0x00000008) +#define RXCSR0_DROP_NOT_TO_ME FIELD32(0x00000010) +#define RXCSR0_DROP_TODS FIELD32(0x00000020) +#define RXCSR0_DROP_VERSION_ERROR FIELD32(0x00000040) +#define RXCSR0_PASS_CRC FIELD32(0x00000080) +#define RXCSR0_PASS_PLCP FIELD32(0x00000100) +#define RXCSR0_DROP_MCAST FIELD32(0x00000200) +#define RXCSR0_DROP_BCAST FIELD32(0x00000400) +#define RXCSR0_ENABLE_QOS FIELD32(0x00000800) + +/* + * RXCSR1: RX descriptor configuration register. + * RXD_SIZE: Rx descriptor size, default is 32b. + * NUM_RXD: Number of rx entries in ring. + */ +#define RXCSR1 0x0084 +#define RXCSR1_RXD_SIZE FIELD32(0x000000ff) +#define RXCSR1_NUM_RXD FIELD32(0x0000ff00) + +/* + * RXCSR2: RX Ring base address register. + */ +#define RXCSR2 0x0088 +#define RXCSR2_RX_RING_REGISTER FIELD32(0xffffffff) + +/* + * RXCSR3: BBP ID register for Rx operation. + * BBP_ID#: BBP register # id. + * BBP_ID#_VALID: BBP register # id is valid or not. + */ +#define RXCSR3 0x0090 +#define RXCSR3_BBP_ID0 FIELD32(0x0000007f) +#define RXCSR3_BBP_ID0_VALID FIELD32(0x00000080) +#define RXCSR3_BBP_ID1 FIELD32(0x00007f00) +#define RXCSR3_BBP_ID1_VALID FIELD32(0x00008000) +#define RXCSR3_BBP_ID2 FIELD32(0x007f0000) +#define RXCSR3_BBP_ID2_VALID FIELD32(0x00800000) +#define RXCSR3_BBP_ID3 FIELD32(0x7f000000) +#define RXCSR3_BBP_ID3_VALID FIELD32(0x80000000) + +/* + * ARCSR1: Auto Responder PLCP config register 1. + * AR_BBP_DATA#: Auto responder BBP register # data. + * AR_BBP_ID#: Auto responder BBP register # Id. + */ +#define ARCSR1 0x009c +#define ARCSR1_AR_BBP_DATA2 FIELD32(0x000000ff) +#define ARCSR1_AR_BBP_ID2 FIELD32(0x0000ff00) +#define ARCSR1_AR_BBP_DATA3 FIELD32(0x00ff0000) +#define ARCSR1_AR_BBP_ID3 FIELD32(0xff000000) + +/* + * Miscellaneous Registers. + * Some values are set in TU, whereas 1 TU == 1024 us. + + */ + +/* + * PCICSR: PCI control register. + * BIG_ENDIAN: 1: big endian, 0: little endian. + * RX_TRESHOLD: Rx threshold in dw to start pci access + * 0: 16dw (default), 1: 8dw, 2: 4dw, 3: 32dw. + * TX_TRESHOLD: Tx threshold in dw to start pci access + * 0: 0dw (default), 1: 1dw, 2: 4dw, 3: forward. + * BURST_LENTH: Pci burst length 0: 4dw (default, 1: 8dw, 2: 16dw, 3:32dw. + * ENABLE_CLK: Enable clk_run, pci clock can't going down to non-operational. + * READ_MULTIPLE: Enable memory read multiple. + * WRITE_INVALID: Enable memory write & invalid. + */ +#define PCICSR 0x008c +#define PCICSR_BIG_ENDIAN FIELD32(0x00000001) +#define PCICSR_RX_TRESHOLD FIELD32(0x00000006) +#define PCICSR_TX_TRESHOLD FIELD32(0x00000018) +#define PCICSR_BURST_LENTH FIELD32(0x00000060) +#define PCICSR_ENABLE_CLK FIELD32(0x00000080) +#define PCICSR_READ_MULTIPLE FIELD32(0x00000100) +#define PCICSR_WRITE_INVALID FIELD32(0x00000200) + +/* + * CNT0: FCS error count. + * FCS_ERROR: FCS error count, cleared when read. + */ +#define CNT0 0x00a0 +#define CNT0_FCS_ERROR FIELD32(0x0000ffff) + +/* + * Statistic Register. + * CNT1: PLCP error count. + * CNT2: Long error count. + */ +#define TIMECSR2 0x00a8 +#define CNT1 0x00ac +#define CNT2 0x00b0 +#define TIMECSR3 0x00b4 + +/* + * CNT3: CCA false alarm count. + */ +#define CNT3 0x00b8 +#define CNT3_FALSE_CCA FIELD32(0x0000ffff) + +/* + * Statistic Register. + * CNT4: Rx FIFO overflow count. + * CNT5: Tx FIFO underrun count. + */ +#define CNT4 0x00bc +#define CNT5 0x00c0 + +/* + * Baseband Control Register. + */ + +/* + * PWRCSR0: Power mode configuration register. + */ +#define PWRCSR0 0x00c4 + +/* + * Power state transition time registers. + */ +#define PSCSR0 0x00c8 +#define PSCSR1 0x00cc +#define PSCSR2 0x00d0 +#define PSCSR3 0x00d4 + +/* + * PWRCSR1: Manual power control / status register. + * Allowed state: 0 deep_sleep, 1: sleep, 2: standby, 3: awake. + * SET_STATE: Set state. Write 1 to trigger, self cleared. + * BBP_DESIRE_STATE: BBP desired state. + * RF_DESIRE_STATE: RF desired state. + * BBP_CURR_STATE: BBP current state. + * RF_CURR_STATE: RF current state. + * PUT_TO_SLEEP: Put to sleep. Write 1 to trigger, self cleared. + */ +#define PWRCSR1 0x00d8 +#define PWRCSR1_SET_STATE FIELD32(0x00000001) +#define PWRCSR1_BBP_DESIRE_STATE FIELD32(0x00000006) +#define PWRCSR1_RF_DESIRE_STATE FIELD32(0x00000018) +#define PWRCSR1_BBP_CURR_STATE FIELD32(0x00000060) +#define PWRCSR1_RF_CURR_STATE FIELD32(0x00000180) +#define PWRCSR1_PUT_TO_SLEEP FIELD32(0x00000200) + +/* + * TIMECSR: Timer control register. + * US_COUNT: 1 us timer count in units of clock cycles. + * US_64_COUNT: 64 us timer count in units of 1 us timer. + * BEACON_EXPECT: Beacon expect window. + */ +#define TIMECSR 0x00dc +#define TIMECSR_US_COUNT FIELD32(0x000000ff) +#define TIMECSR_US_64_COUNT FIELD32(0x0000ff00) +#define TIMECSR_BEACON_EXPECT FIELD32(0x00070000) + +/* + * MACCSR0: MAC configuration register 0. + */ +#define MACCSR0 0x00e0 + +/* + * MACCSR1: MAC configuration register 1. + * KICK_RX: Kick one-shot rx in one-shot rx mode. + * ONESHOT_RXMODE: Enable one-shot rx mode for debugging. + * BBPRX_RESET_MODE: Ralink bbp rx reset mode. + * AUTO_TXBBP: Auto tx logic access bbp control register. + * AUTO_RXBBP: Auto rx logic access bbp control register. + * LOOPBACK: Loopback mode. 0: normal, 1: internal, 2: external, 3:rsvd. + * INTERSIL_IF: Intersil if calibration pin. + */ +#define MACCSR1 0x00e4 +#define MACCSR1_KICK_RX FIELD32(0x00000001) +#define MACCSR1_ONESHOT_RXMODE FIELD32(0x00000002) +#define MACCSR1_BBPRX_RESET_MODE FIELD32(0x00000004) +#define MACCSR1_AUTO_TXBBP FIELD32(0x00000008) +#define MACCSR1_AUTO_RXBBP FIELD32(0x00000010) +#define MACCSR1_LOOPBACK FIELD32(0x00000060) +#define MACCSR1_INTERSIL_IF FIELD32(0x00000080) + +/* + * RALINKCSR: Ralink Rx auto-reset BBCR. + * AR_BBP_DATA#: Auto reset BBP register # data. + * AR_BBP_ID#: Auto reset BBP register # id. + */ +#define RALINKCSR 0x00e8 +#define RALINKCSR_AR_BBP_DATA0 FIELD32(0x000000ff) +#define RALINKCSR_AR_BBP_ID0 FIELD32(0x00007f00) +#define RALINKCSR_AR_BBP_VALID0 FIELD32(0x00008000) +#define RALINKCSR_AR_BBP_DATA1 FIELD32(0x00ff0000) +#define RALINKCSR_AR_BBP_ID1 FIELD32(0x7f000000) +#define RALINKCSR_AR_BBP_VALID1 FIELD32(0x80000000) + +/* + * BCNCSR: Beacon interval control register. + * CHANGE: Write one to change beacon interval. + * DELTATIME: The delta time value. + * NUM_BEACON: Number of beacon according to mode. + * MODE: Please refer to asic specs. + * PLUS: Plus or minus delta time value. + */ +#define BCNCSR 0x00ec +#define BCNCSR_CHANGE FIELD32(0x00000001) +#define BCNCSR_DELTATIME FIELD32(0x0000001e) +#define BCNCSR_NUM_BEACON FIELD32(0x00001fe0) +#define BCNCSR_MODE FIELD32(0x00006000) +#define BCNCSR_PLUS FIELD32(0x00008000) + +/* + * BBP / RF / IF Control Register. + */ + +/* + * BBPCSR: BBP serial control register. + * VALUE: Register value to program into BBP. + * REGNUM: Selected BBP register. + * BUSY: 1: asic is busy execute BBP programming. + * WRITE_CONTROL: 1: write BBP, 0: read BBP. + */ +#define BBPCSR 0x00f0 +#define BBPCSR_VALUE FIELD32(0x000000ff) +#define BBPCSR_REGNUM FIELD32(0x00007f00) +#define BBPCSR_BUSY FIELD32(0x00008000) +#define BBPCSR_WRITE_CONTROL FIELD32(0x00010000) + +/* + * RFCSR: RF serial control register. + * VALUE: Register value + id to program into rf/if. + * NUMBER_OF_BITS: Number of bits used in value (i:20, rfmd:22). + * IF_SELECT: Chip to program: 0: rf, 1: if. + * PLL_LD: Rf pll_ld status. + * BUSY: 1: asic is busy execute rf programming. + */ +#define RFCSR 0x00f4 +#define RFCSR_VALUE FIELD32(0x00ffffff) +#define RFCSR_NUMBER_OF_BITS FIELD32(0x1f000000) +#define RFCSR_IF_SELECT FIELD32(0x20000000) +#define RFCSR_PLL_LD FIELD32(0x40000000) +#define RFCSR_BUSY FIELD32(0x80000000) + +/* + * LEDCSR: LED control register. + * ON_PERIOD: On period, default 70ms. + * OFF_PERIOD: Off period, default 30ms. + * LINK: 0: linkoff, 1: linkup. + * ACTIVITY: 0: idle, 1: active. + * LINK_POLARITY: 0: active low, 1: active high. + * ACTIVITY_POLARITY: 0: active low, 1: active high. + * LED_DEFAULT: LED state for "enable" 0: ON, 1: OFF. + */ +#define LEDCSR 0x00f8 +#define LEDCSR_ON_PERIOD FIELD32(0x000000ff) +#define LEDCSR_OFF_PERIOD FIELD32(0x0000ff00) +#define LEDCSR_LINK FIELD32(0x00010000) +#define LEDCSR_ACTIVITY FIELD32(0x00020000) +#define LEDCSR_LINK_POLARITY FIELD32(0x00040000) +#define LEDCSR_ACTIVITY_POLARITY FIELD32(0x00080000) +#define LEDCSR_LED_DEFAULT FIELD32(0x00100000) + +/* + * AES control register. + */ +#define SECCSR3 0x00fc + +/* + * ASIC pointer information. + * RXPTR: Current RX ring address. + * TXPTR: Current Tx ring address. + * PRIPTR: Current Priority ring address. + * ATIMPTR: Current ATIM ring address. + */ +#define RXPTR 0x0100 +#define TXPTR 0x0104 +#define PRIPTR 0x0108 +#define ATIMPTR 0x010c + +/* + * TXACKCSR0: TX ACK timeout. + */ +#define TXACKCSR0 0x0110 + +/* + * ACK timeout count registers. + * ACKCNT0: TX ACK timeout count. + * ACKCNT1: RX ACK timeout count. + */ +#define ACKCNT0 0x0114 +#define ACKCNT1 0x0118 + +/* + * GPIO and others. + */ + +/* + * GPIOCSR: GPIO control register. + */ +#define GPIOCSR 0x0120 +#define GPIOCSR_BIT0 FIELD32(0x00000001) +#define GPIOCSR_BIT1 FIELD32(0x00000002) +#define GPIOCSR_BIT2 FIELD32(0x00000004) +#define GPIOCSR_BIT3 FIELD32(0x00000008) +#define GPIOCSR_BIT4 FIELD32(0x00000010) +#define GPIOCSR_BIT5 FIELD32(0x00000020) +#define GPIOCSR_BIT6 FIELD32(0x00000040) +#define GPIOCSR_BIT7 FIELD32(0x00000080) +#define GPIOCSR_DIR0 FIELD32(0x00000100) +#define GPIOCSR_DIR1 FIELD32(0x00000200) +#define GPIOCSR_DIR2 FIELD32(0x00000400) +#define GPIOCSR_DIR3 FIELD32(0x00000800) +#define GPIOCSR_DIR4 FIELD32(0x00001000) +#define GPIOCSR_DIR5 FIELD32(0x00002000) +#define GPIOCSR_DIR6 FIELD32(0x00004000) +#define GPIOCSR_DIR7 FIELD32(0x00008000) + +/* + * FIFO pointer registers. + * FIFOCSR0: TX FIFO pointer. + * FIFOCSR1: RX FIFO pointer. + */ +#define FIFOCSR0 0x0128 +#define FIFOCSR1 0x012c + +/* + * BCNCSR1: Tx BEACON offset time control register. + * PRELOAD: Beacon timer offset in units of usec. + * BEACON_CWMIN: 2^CwMin. + */ +#define BCNCSR1 0x0130 +#define BCNCSR1_PRELOAD FIELD32(0x0000ffff) +#define BCNCSR1_BEACON_CWMIN FIELD32(0x000f0000) + +/* + * MACCSR2: TX_PE to RX_PE turn-around time control register + * DELAY: RX_PE low width, in units of pci clock cycle. + */ +#define MACCSR2 0x0134 +#define MACCSR2_DELAY FIELD32(0x000000ff) + +/* + * TESTCSR: TEST mode selection register. + */ +#define TESTCSR 0x0138 + +/* + * ACK/CTS PLCP registers. + * ARCSR2: 1 Mbps ACK/CTS PLCP. + * ARCSR3: 2 Mbps ACK/CTS PLCP. + * ARCSR4: 5.5 Mbps ACK/CTS PLCP. + * ARCSR5: 11 Mbps ACK/CTS PLCP. + */ +#define ARCSR2 0x013c +#define ARCSR3 0x0140 +#define ARCSR4 0x0144 +#define ARCSR5 0x0148 + +/* + * ACK/CTS payload consumed time registers. + * ARTCSR0: CCK ACK/CTS payload consumed time for 1/2/5.5/11 mbps. + * ARTCSR1: OFDM ACK/CTS payload consumed time for 6/9/12/18 mbps. + * ARTCSR2: OFDM ACK/CTS payload consumed time for 24/36/48/54 mbps. + */ +#define ARTCSR0 0x014c +#define ARTCSR1 0x0150 +#define ARTCSR2 0x0154 + +/* + * SECCSR1_RT2509: WEP control register. + * KICK_ENCRYPT: Kick encryption engine, self-clear. + * ONE_SHOT: 0: ring mode, 1: One shot only mode. + * DESC_ADDRESS: Descriptor physical address of frame. + */ +#define SECCSR1 0x0158 +#define SECCSR1_KICK_ENCRYPT FIELD32(0x00000001) +#define SECCSR1_ONE_SHOT FIELD32(0x00000002) +#define SECCSR1_DESC_ADDRESS FIELD32(0xfffffffc) + +/* + * BBPCSR1: BBP TX configuration. + */ +#define BBPCSR1 0x015c +#define BBPCSR1_CCK FIELD32(0x00000003) +#define BBPCSR1_CCK_FLIP FIELD32(0x00000004) +#define BBPCSR1_OFDM FIELD32(0x00030000) +#define BBPCSR1_OFDM_FLIP FIELD32(0x00040000) + +/* + * Dual band configuration registers. + * DBANDCSR0: Dual band configuration register 0. + * DBANDCSR1: Dual band configuration register 1. + */ +#define DBANDCSR0 0x0160 +#define DBANDCSR1 0x0164 + +/* + * BBPPCSR: BBP Pin control register. + */ +#define BBPPCSR 0x0168 + +/* + * MAC special debug mode selection registers. + * DBGSEL0: MAC special debug mode selection register 0. + * DBGSEL1: MAC special debug mode selection register 1. + */ +#define DBGSEL0 0x016c +#define DBGSEL1 0x0170 + +/* + * BISTCSR: BBP BIST register. + */ +#define BISTCSR 0x0174 + +/* + * Multicast filter registers. + * MCAST0: Multicast filter register 0. + * MCAST1: Multicast filter register 1. + */ +#define MCAST0 0x0178 +#define MCAST1 0x017c + +/* + * UART registers. + * UARTCSR0: UART1 TX register. + * UARTCSR1: UART1 RX register. + * UARTCSR3: UART1 frame control register. + * UARTCSR4: UART1 buffer control register. + * UART2CSR0: UART2 TX register. + * UART2CSR1: UART2 RX register. + * UART2CSR3: UART2 frame control register. + * UART2CSR4: UART2 buffer control register. + */ +#define UARTCSR0 0x0180 +#define UARTCSR1 0x0184 +#define UARTCSR3 0x0188 +#define UARTCSR4 0x018c +#define UART2CSR0 0x0190 +#define UART2CSR1 0x0194 +#define UART2CSR3 0x0198 +#define UART2CSR4 0x019c + +/* + * RF registers + */ +#define RF1_TUNER FIELD32(0x00020000) +#define RF3_TUNER FIELD32(0x00000100) +#define RF3_TXPOWER FIELD32(0x00003e00) + +/* + * EEPROM content. + * The wordsize of the EEPROM is 16 bits. + */ + +/* + * HW MAC address. + */ +#define EEPROM_MAC_ADDR_0 0x0002 +#define EEPROM_MAC_ADDR_BYTE0 FIELD16(0x00ff) +#define EEPROM_MAC_ADDR_BYTE1 FIELD16(0xff00) +#define EEPROM_MAC_ADDR1 0x0003 +#define EEPROM_MAC_ADDR_BYTE2 FIELD16(0x00ff) +#define EEPROM_MAC_ADDR_BYTE3 FIELD16(0xff00) +#define EEPROM_MAC_ADDR_2 0x0004 +#define EEPROM_MAC_ADDR_BYTE4 FIELD16(0x00ff) +#define EEPROM_MAC_ADDR_BYTE5 FIELD16(0xff00) + +/* + * EEPROM antenna. + * ANTENNA_NUM: Number of antenna's. + * TX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. + * RX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. + * LED_MODE: 0: default, 1: TX/RX activity,2: Single (ignore link), 3: rsvd. + * DYN_TXAGC: Dynamic TX AGC control. + * HARDWARE_RADIO: 1: Hardware controlled radio. Read GPIO0. + * RF_TYPE: Rf_type of this adapter. + */ +#define EEPROM_ANTENNA 0x10 +#define EEPROM_ANTENNA_NUM FIELD16(0x0003) +#define EEPROM_ANTENNA_TX_DEFAULT FIELD16(0x000c) +#define EEPROM_ANTENNA_RX_DEFAULT FIELD16(0x0030) +#define EEPROM_ANTENNA_LED_MODE FIELD16(0x01c0) +#define EEPROM_ANTENNA_DYN_TXAGC FIELD16(0x0200) +#define EEPROM_ANTENNA_HARDWARE_RADIO FIELD16(0x0400) +#define EEPROM_ANTENNA_RF_TYPE FIELD16(0xf800) + +/* + * EEPROM NIC config. + * CARDBUS_ACCEL: 0: enable, 1: disable. + * DYN_BBP_TUNE: 0: enable, 1: disable. + * CCK_TX_POWER: CCK TX power compensation. + */ +#define EEPROM_NIC 0x11 +#define EEPROM_NIC_CARDBUS_ACCEL FIELD16(0x0001) +#define EEPROM_NIC_DYN_BBP_TUNE FIELD16(0x0002) +#define EEPROM_NIC_CCK_TX_POWER FIELD16(0x000c) + +/* + * EEPROM geography. + * GEO: Default geography setting for device. + */ +#define EEPROM_GEOGRAPHY 0x12 +#define EEPROM_GEOGRAPHY_GEO FIELD16(0x0f00) + +/* + * EEPROM BBP. + */ +#define EEPROM_BBP_START 0x13 +#define EEPROM_BBP_SIZE 16 +#define EEPROM_BBP_VALUE FIELD16(0x00ff) +#define EEPROM_BBP_REG_ID FIELD16(0xff00) + +/* + * EEPROM TXPOWER + */ +#define EEPROM_TXPOWER_START 0x23 +#define EEPROM_TXPOWER_SIZE 7 +#define EEPROM_TXPOWER_1 FIELD16(0x00ff) +#define EEPROM_TXPOWER_2 FIELD16(0xff00) + +/* + * RSSI <-> dBm offset calibration + */ +#define EEPROM_CALIBRATE_OFFSET 0x3e +#define EEPROM_CALIBRATE_OFFSET_RSSI FIELD16(0x00ff) + +/* + * DMA descriptor defines. + */ +#define TXD_DESC_SIZE ( 11 * sizeof(struct data_desc) ) +#define RXD_DESC_SIZE ( 11 * sizeof(struct data_desc) ) + +/* + * TX descriptor format for TX, PRIO, ATIM and Beacon Ring. + */ + +/* + * Word0 + */ +#define TXD_W0_OWNER_NIC FIELD32(0x00000001) +#define TXD_W0_VALID FIELD32(0x00000002) +#define TXD_W0_RESULT FIELD32(0x0000001c) +#define TXD_W0_RETRY_COUNT FIELD32(0x000000e0) +#define TXD_W0_MORE_FRAG FIELD32(0x00000100) +#define TXD_W0_ACK FIELD32(0x00000200) +#define TXD_W0_TIMESTAMP FIELD32(0x00000400) +#define TXD_W0_OFDM FIELD32(0x00000800) +#define TXD_W0_CIPHER_OWNER FIELD32(0x00001000) +#define TXD_W0_IFS FIELD32(0x00006000) +#define TXD_W0_RETRY_MODE FIELD32(0x00008000) +#define TXD_W0_DATABYTE_COUNT FIELD32(0x0fff0000) +#define TXD_W0_CIPHER_ALG FIELD32(0xe0000000) + +/* + * Word1 + */ +#define TXD_W1_BUFFER_ADDRESS FIELD32(0xffffffff) + +/* + * Word2 + */ +#define TXD_W2_IV_OFFSET FIELD32(0x0000003f) +#define TXD_W2_AIFS FIELD32(0x000000c0) +#define TXD_W2_CWMIN FIELD32(0x00000f00) +#define TXD_W2_CWMAX FIELD32(0x0000f000) + +/* + * Word3: PLCP information + */ +#define TXD_W3_PLCP_SIGNAL FIELD32(0x000000ff) +#define TXD_W3_PLCP_SERVICE FIELD32(0x0000ff00) +#define TXD_W3_PLCP_LENGTH_LOW FIELD32(0x00ff0000) +#define TXD_W3_PLCP_LENGTH_HIGH FIELD32(0xff000000) + +/* + * Word4 + */ +#define TXD_W4_IV FIELD32(0xffffffff) + +/* + * Word5 + */ +#define TXD_W5_EIV FIELD32(0xffffffff) + +/* + * Word6-9: Key + */ +#define TXD_W6_KEY FIELD32(0xffffffff) +#define TXD_W7_KEY FIELD32(0xffffffff) +#define TXD_W8_KEY FIELD32(0xffffffff) +#define TXD_W9_KEY FIELD32(0xffffffff) + +/* + * Word10 + */ +#define TXD_W10_RTS FIELD32(0x00000001) +#define TXD_W10_TX_RATE FIELD32(0x000000fe) + +/* + * RX descriptor format for RX Ring. + */ + +/* + * Word0 + */ +#define RXD_W0_OWNER_NIC FIELD32(0x00000001) +#define RXD_W0_UNICAST_TO_ME FIELD32(0x00000002) +#define RXD_W0_MULTICAST FIELD32(0x00000004) +#define RXD_W0_BROADCAST FIELD32(0x00000008) +#define RXD_W0_MY_BSS FIELD32(0x00000010) +#define RXD_W0_CRC FIELD32(0x00000020) +#define RXD_W0_OFDM FIELD32(0x00000040) +#define RXD_W0_PHYSICAL_ERROR FIELD32(0x00000080) +#define RXD_W0_CIPHER_OWNER FIELD32(0x00000100) +#define RXD_W0_ICV_ERROR FIELD32(0x00000200) +#define RXD_W0_IV_OFFSET FIELD32(0x0000fc00) +#define RXD_W0_DATABYTE_COUNT FIELD32(0x0fff0000) +#define RXD_W0_CIPHER_ALG FIELD32(0xe0000000) + +/* + * Word1 + */ +#define RXD_W1_BUFFER_ADDRESS FIELD32(0xffffffff) + +/* + * Word2 + */ +#define RXD_W2_SIGNAL FIELD32(0x000000ff) +#define RXD_W2_RSSI FIELD32(0x0000ff00) +#define RXD_W2_TA FIELD32(0xffff0000) + +/* + * Word3 + */ +#define RXD_W3_TA FIELD32(0xffffffff) + +/* + * Word4 + */ +#define RXD_W4_IV FIELD32(0xffffffff) + +/* + * Word5 + */ +#define RXD_W5_EIV FIELD32(0xffffffff) + +/* + * Word6-9: Key + */ +#define RXD_W6_KEY FIELD32(0xffffffff) +#define RXD_W7_KEY FIELD32(0xffffffff) +#define RXD_W8_KEY FIELD32(0xffffffff) +#define RXD_W9_KEY FIELD32(0xffffffff) + +/* + * Word10 + */ +#define RXD_W10_DROP FIELD32(0x00000001) + +/* + * TX ring index number for rt2x00_dev structure. + */ +enum ring_index { + RING_PRIO = 0, + RING_TX = 1, + RING_ATIM = 2, + RING_BEACON = 3, + RING_RX = 4, + RING_NUM = 5, + RING_NUM_TX = 2, +}; + +/* + * Macro's for converting txpower from EEPROM to dscape value + * and from dscape value to register value. + */ +#define MIN_TXPOWER 0 +#define MAX_TXPOWER 31 +#define DEFAULT_TXPOWER 24 + +#define TXPOWER_FROM_DEV(__txpower) \ + ({ \ + ((__txpower) > MAX_TXPOWER) ? DEFAULT_TXPOWER : (__txpower); \ + }) + +#define TXPOWER_TO_DEV(__txpower) \ + ({ \ + ((__txpower) <= MIN_TXPOWER) ? MIN_TXPOWER : \ + (((__txpower) >= MAX_TXPOWER) ? MAX_TXPOWER : \ + (__txpower)); \ + }) + +/* + * LED control functions. + */ +static void rt2500pci_enable_led(struct rt2x00_dev *rt2x00dev); +static void rt2500pci_disable_led(struct rt2x00_dev *rt2x00dev); +static void rt2500pci_activity_led(struct rt2x00_dev *rt2x00dev, + char activity); + +#endif /* RT2500PCI_H */ diff --git a/drivers/net/wireless/mac80211/rt2x00/rt2500usb.c b/drivers/net/wireless/mac80211/rt2x00/rt2500usb.c new file mode 100644 index 0000000..ffc71fd --- /dev/null +++ b/drivers/net/wireless/mac80211/rt2x00/rt2500usb.c @@ -0,0 +1,1729 @@ +/* + Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + + + 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. + */ + +/* + Module: rt2500usb + Abstract: rt2500usb device specific routines. + Supported chipsets: RT2570. + */ + +/* + * Set enviroment defines for rt2x00.h + */ +#define DRV_NAME "rt2500usb" + +#include +#include +#include +#include +#include +#include +#include + +#include "rt2x00.h" +#include "rt2x00usb.h" +#include "rt2500usb.h" + +/* + * Register access. + * All access to the CSR registers will go through the methods + * rt2x00_register_read and rt2x00_register_write. + * BBP and RF register require indirect register access, + * and use the CSR registers BBPCSR and RFCSR to achieve this. + * These indirect registers work with busy bits, + * and we will try maximal REGISTER_BUSY_COUNT times to access + * the register while taking a REGISTER_BUSY_DELAY us delay + * between each attampt. When the busy bit is still set at that time, + * the access attempt is considered to have failed, + * and we will print an error. + */ +static int rt2x00_vendor_request(const struct rt2x00_dev *rt2x00dev, + const u8 request, const u8 type, const u16 offset, + u32 value, void *buffer, const u16 buffer_length, const u16 timeout) +{ + struct usb_device *usb_dev = interface_to_usbdev( + rt2x00dev_usb(rt2x00dev)); + int status; + unsigned int i; + + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + status = usb_control_msg( + usb_dev, + (type == USB_VENDOR_REQUEST_IN) ? + usb_rcvctrlpipe(usb_dev, 0) : + usb_sndctrlpipe(usb_dev, 0), + request, type, value, offset, buffer, buffer_length, + timeout); + if (status >= 0) + return 0; + } + + ERROR("vendor request error. Request 0x%02x failed " + "for offset 0x%04x with error %d.\n", request, offset, status); + + return status; +} + +static inline void rt2x00_register_read( + const struct rt2x00_dev *rt2x00dev, + const u16 offset, u16 *value) +{ + __le16 reg; + rt2x00_vendor_request( + rt2x00dev, USB_MULTI_READ, USB_VENDOR_REQUEST_IN, + offset, 0x00, ®, sizeof(u16), REGISTER_TIMEOUT); + *value = le16_to_cpu(reg); +} + +static inline void rt2x00_register_multiread( + const struct rt2x00_dev *rt2x00dev, + const u16 offset, void *value, const u16 length) +{ + rt2x00_vendor_request( + rt2x00dev, USB_MULTI_READ, USB_VENDOR_REQUEST_IN, + offset, 0x00, value, length, + REGISTER_TIMEOUT * (length / sizeof(u16))); +} + +static inline void rt2x00_register_write( + const struct rt2x00_dev *rt2x00dev, + const u16 offset, u16 value) +{ + __le16 reg = cpu_to_le16(value); + rt2x00_vendor_request( + rt2x00dev, USB_MULTI_WRITE, USB_VENDOR_REQUEST_OUT, + offset, 0x00, ®, sizeof(u16), REGISTER_TIMEOUT); +} + +static inline void rt2x00_register_multiwrite( + const struct rt2x00_dev *rt2x00dev, + const u16 offset, void *value, const u16 length) +{ + rt2x00_vendor_request( + rt2x00dev, USB_MULTI_WRITE, USB_VENDOR_REQUEST_OUT, + offset, 0x00, value, length, + REGISTER_TIMEOUT * (length / sizeof(u16))); +} + +static u16 rt2x00_bbp_check(const struct rt2x00_dev *rt2x00dev) +{ + u16 reg; + unsigned int i; + + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2x00_register_read(rt2x00dev, PHY_CSR8, ®); + if (!rt2x00_get_field16(reg, PHY_CSR8_BBP_BUSY)) + return reg; + udelay(REGISTER_BUSY_DELAY); + } + + return 0xffff; +} + +static void rt2x00_bbp_write(const struct rt2x00_dev *rt2x00dev, + const u8 reg_id, const u8 value) +{ + u16 reg; + + /* + * Wait until the BBP becomes ready. + */ + if (rt2x00_bbp_check(rt2x00dev) == 0xffff) { + ERROR("PHY_CSR8 register busy. Write failed.\n"); + return; + } + + /* + * Write the data into the BBP. + */ + reg = 0; + rt2x00_set_field16(®, PHY_CSR7_BBP_DATA, value); + rt2x00_set_field16(®, PHY_CSR7_BBP_REG_ID, reg_id); + rt2x00_set_field16(®, PHY_CSR7_BBP_READ_CONTROL, 0); + + rt2x00_register_write(rt2x00dev, PHY_CSR7, reg); +} + +static void rt2x00_bbp_read(const struct rt2x00_dev *rt2x00dev, + const u8 reg_id, u8 *value) +{ + u16 reg; + + /* + * Wait until the BBP becomes ready. + */ + if (rt2x00_bbp_check(rt2x00dev) == 0xffff) { + ERROR("PHY_CSR8 register busy. Read failed.\n"); + return; + } + + /* + * Write the request into the BBP. + */ + reg =0; + rt2x00_set_field16(®, PHY_CSR7_BBP_REG_ID, reg_id); + rt2x00_set_field16(®, PHY_CSR7_BBP_READ_CONTROL, 1); + + rt2x00_register_write(rt2x00dev, PHY_CSR7, reg); + + /* + * Wait until the BBP becomes ready. + */ + if (rt2x00_bbp_check(rt2x00dev) == 0xffff) { + ERROR("PHY_CSR8 register busy. Read failed.\n"); + *value = 0xff; + return; + } + + rt2x00_register_read(rt2x00dev, PHY_CSR7, ®); + *value = rt2x00_get_field16(reg, PHY_CSR7_BBP_DATA); +} + +static void rt2x00_rf_write(const struct rt2x00_dev *rt2x00dev, + const u32 value) +{ + u16 reg; + unsigned int i; + + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2x00_register_read(rt2x00dev, PHY_CSR10, ®); + if (!rt2x00_get_field16(reg, PHY_CSR10_RF_BUSY)) + goto rf_write; + udelay(REGISTER_BUSY_DELAY); + } + + ERROR("PHY_CSR10 register busy. Write failed.\n"); + return; + +rf_write: + reg = 0; + rt2x00_set_field16(®, PHY_CSR9_RF_VALUE, value & 0x0000ffff); + rt2x00_register_write(rt2x00dev, PHY_CSR9, reg); + + reg = 0; + rt2x00_set_field16(®, PHY_CSR10_RF_VALUE, + (value >> 16) & 0x0000ffff); + rt2x00_set_field16(®, PHY_CSR10_RF_NUMBER_OF_BITS, 20); + rt2x00_set_field16(®, PHY_CSR10_RF_IF_SELECT, 0); + rt2x00_set_field16(®, PHY_CSR10_RF_BUSY, 1); + + rt2x00_register_write(rt2x00dev, PHY_CSR10, reg); +} + +#ifdef CONFIG_RT2X00_DEBUGFS +#define CSR_OFFSET(__word) ( CSR_REG_BASE + ((__word) * sizeof(u16)) ) + +static void rt2500usb_read_csr(struct rt2x00_dev *rt2x00dev, + const unsigned long word, void *data) +{ + rt2x00_register_read(rt2x00dev, CSR_OFFSET(word), data); +} + +static void rt2500usb_write_csr(struct rt2x00_dev *rt2x00dev, + const unsigned long word, void *data) +{ + rt2x00_register_write(rt2x00dev, CSR_OFFSET(word), *((u16*)data)); +} + +static void rt2500usb_read_eeprom(struct rt2x00_dev *rt2x00dev, + const unsigned long word, void *data) +{ + rt2x00_eeprom_read(rt2x00dev, word, data); +} + +static void rt2500usb_write_eeprom(struct rt2x00_dev *rt2x00dev, + const unsigned long word, void *data) +{ + rt2x00_eeprom_write(rt2x00dev, word, *((u16*)data)); +} + +static void rt2500usb_read_bbp(struct rt2x00_dev *rt2x00dev, + const unsigned long word, void *data) +{ + rt2x00_bbp_read(rt2x00dev, word, data); +} + +static void rt2500usb_write_bbp(struct rt2x00_dev *rt2x00dev, + const unsigned long word, void *data) +{ + rt2x00_bbp_write(rt2x00dev, word, *((u8*)data)); +} + +static const struct rt2x00debug rt2500usb_rt2x00debug = { + .owner = THIS_MODULE, + .mod_name = DRV_NAME, + .mod_version = DRV_VERSION, + .reg_csr = { + .read = rt2500usb_read_csr, + .write = rt2500usb_write_csr, + .word_size = sizeof(u16), + .word_count = CSR_REG_SIZE / sizeof(u16), + }, + .reg_eeprom = { + .read = rt2500usb_read_eeprom, + .write = rt2500usb_write_eeprom, + .word_size = sizeof(u16), + .word_count = EEPROM_SIZE / sizeof(u16), + }, + .reg_bbp = { + .read = rt2500usb_read_bbp, + .write = rt2500usb_write_bbp, + .word_size = sizeof(u8), + .word_count = BBP_SIZE / sizeof(u8), + }, +}; +#endif /* CONFIG_RT2X00_DEBUGFS */ + +/* + * Configuration handlers. + */ +static void rt2500usb_config_bssid(struct rt2x00_dev *rt2x00dev, u8 *bssid) +{ + u16 reg[3]; + + memset(®, 0, sizeof(reg)); + memcpy(®, bssid, ETH_ALEN); + + /* + * The BSSID is passed to us as an array of bytes, + * that array is little endian, so no need for byte ordering. + */ + rt2x00_register_multiwrite(rt2x00dev, MAC_CSR5, ®, sizeof(reg)); +} + +static void rt2500usb_config_promisc(struct rt2x00_dev *rt2x00dev, int promisc) +{ + u16 reg; + + rt2x00_register_read(rt2x00dev, TXRX_CSR2, ®); + + if (promisc) { + rt2x00_set_field16(®, TXRX_CSR2_DROP_NOT_TO_ME, 0); + SET_FLAG(rt2x00dev, INTERFACE_ENABLED_PROMISC); + } else { + rt2x00_set_field16(®, TXRX_CSR2_DROP_NOT_TO_ME, 1); + CLEAR_FLAG(rt2x00dev, INTERFACE_ENABLED_PROMISC); + } + + rt2x00_register_write(rt2x00dev, TXRX_CSR2, reg); +} + +static void rt2500usb_config_type(struct rt2x00_dev *rt2x00dev, int type) +{ + u16 reg; + + /* + * Only continue when there is something to be done. + */ + if (!(GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED) ^ + GET_FLAG(rt2x00dev, INTERFACE_ENABLED)) && + !(GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED_MONITOR) ^ + GET_FLAG(rt2x00dev, INTERFACE_ENABLED_MONITOR))) + return; + + rt2x00_register_write(rt2x00dev, TXRX_CSR19, 0); + + /* + * Apply hardware packet filter. + */ + rt2x00_register_read(rt2x00dev, TXRX_CSR2, ®); + + if (!GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED_MONITOR) && + (type == IEEE80211_IF_TYPE_IBSS || type == IEEE80211_IF_TYPE_STA)) + rt2x00_set_field16(®, TXRX_CSR2_DROP_TODS, 1); + else + rt2x00_set_field16(®, TXRX_CSR2_DROP_TODS, 0); + + rt2x00_set_field16(®, TXRX_CSR2_DROP_CRC, 1); + if (GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED_MONITOR)) { + rt2x00_set_field16(®, TXRX_CSR2_DROP_PHYSICAL, 0); + rt2x00_set_field16(®, TXRX_CSR2_DROP_CONTROL, 0); + rt2x00_set_field16(®, TXRX_CSR2_DROP_VERSION_ERROR, 0); + } else { + rt2x00_set_field16(®, TXRX_CSR2_DROP_PHYSICAL, 1); + rt2x00_set_field16(®, TXRX_CSR2_DROP_CONTROL, 1); + rt2x00_set_field16(®, TXRX_CSR2_DROP_VERSION_ERROR, 1); + } + + rt2x00_register_write(rt2x00dev, TXRX_CSR2, reg); + + /* + * Enable promisc mode when in monitor mode. + */ + if (GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED_MONITOR)) + rt2500usb_config_promisc(rt2x00dev, 1); + + /* + * Enable beacon config + */ + rt2x00_register_read(rt2x00dev, TXRX_CSR20, ®); + rt2x00_set_field16(®, TXRX_CSR20_OFFSET, + (PREAMBLE + get_duration(IEEE80211_HEADER, 2)) >> 6); + if (type == IEEE80211_IF_TYPE_STA) + rt2x00_set_field16(®, TXRX_CSR20_BCN_EXPECT_WINDOW, 0); + else + rt2x00_set_field16(®, TXRX_CSR20_BCN_EXPECT_WINDOW, 2); + rt2x00_register_write(rt2x00dev, TXRX_CSR20, reg); + + /* + * Enable synchronisation. + */ + rt2x00_register_read(rt2x00dev, TXRX_CSR18, ®); + rt2x00_set_field16(®, TXRX_CSR18_OFFSET, 0); + rt2x00_set_field16(®, TXRX_CSR18_INTERVAL, 100 << 2); + rt2x00_register_write(rt2x00dev, TXRX_CSR18, reg); + + rt2x00_register_read(rt2x00dev, TXRX_CSR19, ®); + if (GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED)) { + rt2x00_set_field16(®, TXRX_CSR19_TSF_COUNT, 1); + rt2x00_set_field16(®, TXRX_CSR19_TBCN, 1); + } + + rt2x00_set_field16(®, TXRX_CSR19_BEACON_GEN, 0); + if (type == IEEE80211_IF_TYPE_IBSS || type == IEEE80211_IF_TYPE_AP) + rt2x00_set_field16(®, TXRX_CSR19_TSF_SYNC, 2); + else if (type == IEEE80211_IF_TYPE_STA) + rt2x00_set_field16(®, TXRX_CSR19_TSF_SYNC, 1); + else if (GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED_MONITOR) && + !GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED)) + rt2x00_set_field16(®, TXRX_CSR19_TSF_SYNC, 0); + + rt2x00_register_write(rt2x00dev, TXRX_CSR19, reg); + + /* + * Change flags of enabled interfaces. + */ + if (type != IEEE80211_IF_TYPE_MNTR) { + if (GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED)) + SET_FLAG(rt2x00dev, INTERFACE_ENABLED); + else + CLEAR_FLAG(rt2x00dev, INTERFACE_ENABLED); + } else { + if (GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED_MONITOR)) + SET_FLAG(rt2x00dev, INTERFACE_ENABLED_MONITOR); + else + CLEAR_FLAG(rt2x00dev, INTERFACE_ENABLED_MONITOR); + } +} + +static void rt2500usb_config_channel(struct rt2x00_dev *rt2x00dev, + int value, int channel, int freq, int txpower) +{ + u32 rf1 = rt2x00dev->rf1; + u32 rf2 = value; + u32 rf3 = rt2x00dev->rf3; + u32 rf4 = rt2x00dev->rf4; + + /* + * Only continue when there is something to be done. + */ + if (channel == rt2x00dev->rx_status.channel) + return; + + if (txpower == 0xff) + txpower = rt2x00dev->tx_power; + else + txpower = TXPOWER_TO_DEV(txpower); + + if (rt2x00_rf(&rt2x00dev->chip, RF2525)) + rf2 |= 0x00080000; + + if ((rt2x00_rf(&rt2x00dev->chip, RF2523) || + rt2x00_rf(&rt2x00dev->chip, RF2524) || + rt2x00_rf(&rt2x00dev->chip, RF2525)) && + channel == 14) + rf4 &= ~0x00000018; + + if (rt2x00_rf(&rt2x00dev->chip, RF2525E)) { + if (channel & 0x01) + rf4 = 0x00000e1b; + else + rf4 = 0x00000e07; + if (channel == 14) + rf4 = 0x00000e23; + } + + if (rt2x00_rf(&rt2x00dev->chip, RF5222)) { + if (channel < 14) { + rf1 = 0x00022020; + rf4 = 0x00000a0b; + } else if (channel == 14) { + rf1 = 0x00022010; + rf4 = 0x00000a1b; + } else if (channel < 64) { + rf1 = 0x00022010; + rf4 = 0x00000a1f; + } else if (channel < 140) { + rf1 = 0x00022010; + rf4 = 0x00000a0f; + } else if (channel < 161) { + rf1 = 0x00022020; + rf4 = 0x00000a07; + } + } + + /* + * Set TXpower. + */ + rt2x00_set_field32(&rf3, RF3_TXPOWER, txpower); + + INFO("Switching channel. RF1: 0x%08x, RF2: 0x%08x, RF3: 0x%08x, " + "RF4: 0x%08x.\n", rf1, rf2, rf3, rf4); + + /* + * For RT2525E we should first set the channel to half band higher. + */ + if (rt2x00_rf(&rt2x00dev->chip, RF2525E)) { + static const u32 vals[] = { + 0x000008aa, 0x000008ae, 0x000008ae, 0x000008b2, + 0x000008b2, 0x000008b6, 0x000008b6, 0x000008ba, + 0x000008ba, 0x000008be, 0x000008b7, 0x00000902, + 0x00000902, 0x00000906 + }; + + rt2x00_rf_write(rt2x00dev, vals[channel - 1]); + if (rf4) + rt2x00_rf_write(rt2x00dev, rf4); + } + + rt2x00_rf_write(rt2x00dev, rf1); + rt2x00_rf_write(rt2x00dev, rf2); + rt2x00_rf_write(rt2x00dev, rf3); + if (rf4) + rt2x00_rf_write(rt2x00dev, rf4); + + /* + * Update active info for RX. + */ + rt2x00dev->rx_status.freq = freq; + rt2x00dev->rx_status.channel = channel; + + /* + * Update rf fields + */ + rt2x00dev->rf1 = rf1; + rt2x00dev->rf2 = rf2; + rt2x00dev->rf3 = rf3; + rt2x00dev->rf4 = rf4; + + rt2x00dev->tx_power = txpower; +} + +static void rt2500usb_config_txpower(struct rt2x00_dev *rt2x00dev, int txpower) +{ + txpower = TXPOWER_TO_DEV(txpower); + + /* + * Only continue when there is something to be done. + */ + if (txpower == rt2x00dev->tx_power) + return; + + rt2x00_set_field32(&rt2x00dev->rf3, RF3_TXPOWER, txpower); + rt2x00_rf_write(rt2x00dev, rt2x00dev->rf3); + + rt2x00dev->tx_power = txpower; +} + +static void rt2500usb_config_antenna(struct rt2x00_dev *rt2x00dev, + int antenna_tx, int antenna_rx) +{ + u8 reg_rx; + u8 reg_tx; + u16 csr5_reg; + u16 csr6_reg; + + /* + * Only continue when there is something to be done. + */ + if (rt2x00dev->rx_status.antenna == antenna_rx) + return; + + rt2x00_bbp_read(rt2x00dev, 2, ®_tx); + rt2x00_bbp_read(rt2x00dev, 14, ®_rx); + rt2x00_register_read(rt2x00dev, PHY_CSR5, &csr5_reg); + rt2x00_register_read(rt2x00dev, PHY_CSR6, &csr6_reg); + + /* + * Clear current config antenna bits. + */ + reg_tx &= ~0x03; + reg_rx &= ~0x03; + + /* + * Configure the TX antenna. + */ + if (antenna_tx == 0) { /* Diversity. */ + reg_tx |= 0x01; + rt2x00_set_field16(&csr5_reg, PHY_CSR5_CCK, 1); + rt2x00_set_field16(&csr6_reg, PHY_CSR6_OFDM, 1); + } else if (antenna_tx == 1) { /* TX: Antenna A */ + reg_tx |= 0x00; + rt2x00_set_field16(&csr5_reg, PHY_CSR5_CCK, 0); + rt2x00_set_field16(&csr6_reg, PHY_CSR6_OFDM, 0); + } else if (antenna_tx == 2) { /* TX: Antenna B */ + reg_tx |= 0x02; + rt2x00_set_field16(&csr5_reg, PHY_CSR5_CCK, 2); + rt2x00_set_field16(&csr6_reg, PHY_CSR6_OFDM, 2); + } + + /* + * Configure the RX antenna. + */ + if (antenna_rx == 0) /* Diversity. */ + reg_rx |= 0x01; + else if (antenna_rx == 1) /* RX: Antenna A */ + reg_rx |= 0x00; + else if (antenna_rx == 2) /* RX: Antenna B */ + reg_rx |= 0x02; + + /* + * RT2525E and RT5222 need to flip TX I/Q + */ + if (rt2x00_rf(&rt2x00dev->chip, RF2525E) || + rt2x00_rf(&rt2x00dev->chip, RF5222)) { + reg_tx |= 0x04; + rt2x00_set_field16(&csr5_reg, PHY_CSR5_CCK_FLIP, 1); + rt2x00_set_field16(&csr6_reg, PHY_CSR6_OFDM_FLIP, 1); + + /* + * RT2525E does not need RX I/Q Flip. + */ + if (rt2x00_rf(&rt2x00dev->chip, RF2525E)) + reg_rx &= ~0x04; + } else { + rt2x00_set_field16(&csr5_reg, PHY_CSR5_CCK_FLIP, 0); + rt2x00_set_field16(&csr6_reg, PHY_CSR6_OFDM_FLIP, 0); + } + + rt2x00_bbp_write(rt2x00dev, 2, reg_tx); + rt2x00_bbp_write(rt2x00dev, 14, reg_rx); + rt2x00_register_write(rt2x00dev, PHY_CSR5, csr5_reg); + rt2x00_register_write(rt2x00dev, PHY_CSR6, csr6_reg); + + /* + * Update active info for RX. + */ + rt2x00dev->rx_status.antenna = antenna_rx; +} + +static void rt2500usb_config_duration(struct rt2x00_dev *rt2x00dev, + int short_slot_time) +{ + short_slot_time = short_slot_time ? SHORT_SLOT_TIME : SLOT_TIME; + + rt2x00_register_write(rt2x00dev, MAC_CSR10, short_slot_time); +} + +static void rt2500usb_config_rate(struct rt2x00_dev *rt2x00dev, const int rate) +{ + struct ieee80211_conf *conf = &rt2x00dev->hw->conf; + u16 reg; + u16 value; + u16 preamble; + + preamble = DEVICE_GET_RATE_FIELD(rate, PREAMBLE) + ? SHORT_PREAMBLE : PREAMBLE; + + reg = DEVICE_GET_RATE_FIELD(rate, RATEMASK) & DEV_BASIC_RATE; + + rt2x00_register_write(rt2x00dev, TXRX_CSR11, reg); + + rt2x00_register_read(rt2x00dev, TXRX_CSR1, ®); + value = ((conf->flags & IEEE80211_CONF_SHORT_SLOT_TIME) ? + SHORT_DIFS : DIFS) + + PLCP + preamble + get_duration(ACK_SIZE, 10); + rt2x00_set_field16(®, TXRX_CSR1_ACK_TIMEOUT, value); + rt2x00_register_write(rt2x00dev, TXRX_CSR1, reg); + + rt2x00_register_read(rt2x00dev, TXRX_CSR10, ®); + if (preamble == SHORT_PREAMBLE) + rt2x00_set_field16(®, TXRX_CSR10_AUTORESPOND_PREAMBLE, 1); + else + rt2x00_set_field16(®, TXRX_CSR10_AUTORESPOND_PREAMBLE, 0); + rt2x00_register_write(rt2x00dev, TXRX_CSR10, reg); +} + +static void rt2500usb_config_phymode(struct rt2x00_dev *rt2x00dev, + const int phymode) +{ + struct ieee80211_hw_mode *mode; + struct ieee80211_rate *rate; + + /* + * Only continue when there is something to be done. + */ + if (rt2x00dev->rx_status.phymode == phymode) + return; + + if (phymode == MODE_IEEE80211A) + rt2x00dev->curr_hwmode = HWMODE_A; + else if (phymode == MODE_IEEE80211B) + rt2x00dev->curr_hwmode = HWMODE_B; + else + rt2x00dev->curr_hwmode = HWMODE_G; + + mode = &rt2x00dev->hwmodes[rt2x00dev->curr_hwmode]; + rate = &mode->rates[mode->num_rates - 1]; + + rt2500usb_config_rate(rt2x00dev, rate->val2); + + if (phymode == MODE_IEEE80211B) { + rt2x00_register_write(rt2x00dev, MAC_CSR11, 0x000b); + rt2x00_register_write(rt2x00dev, MAC_CSR12, 0x0040); + } else { + rt2x00_register_write(rt2x00dev, MAC_CSR11, 0x0005); + rt2x00_register_write(rt2x00dev, MAC_CSR12, 0x016c); + } + + /* + * Update physical mode for rx ring. + */ + rt2x00dev->rx_status.phymode = phymode; +} + +static void rt2500usb_config_mac_addr(struct rt2x00_dev *rt2x00dev, u8 *addr) +{ + u16 reg[3]; + + memset(®, 0, sizeof(reg)); + memcpy(®, addr, ETH_ALEN); + + /* + * The MAC address is passed to us as an array of bytes, + * that array is little endian, so no need for byte ordering. + */ + rt2x00_register_multiwrite(rt2x00dev, MAC_CSR2, ®, sizeof(reg)); +} + +/* + * Link tuning + */ +static void rt2500usb_link_tuner(struct work_struct *work) +{ + struct rt2x00_dev *rt2x00dev = + container_of(work, struct rt2x00_dev, link.work.work); + u32 rssi; + u16 cca_alarm; + u16 bbp_thresh; + u16 reg_r24; + u16 reg_r25; + u16 reg_r61; + u16 reg_r17; + u16 vgc_bound; + u8 bbp_r17; + u8 sens; + u8 up_bound; + u8 low_bound; + + /* + * Don't perform any tuning when it is disabled + * in the EEPROM. + */ + if (GET_FLAG(rt2x00dev, CONFIG_DISABLE_LINK_TUNING)) + return; + + /* + * Retrieve link quality. + */ + rssi = rt2x00_get_link_rssi(&rt2x00dev->link); + if (!rssi) + goto exit; + + rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE, &bbp_thresh); + bbp_thresh = eeprom_valid(bbp_thresh, 75, EEPROM_BBPTUNE_THRESHOLD); + + rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R24, ®_r24); + rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R25, ®_r25); + rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R61, ®_r61); + + if (rssi > bbp_thresh) { + reg_r24 = eeprom_valid(reg_r24, 0x70, EEPROM_BBPTUNE_R24_HIGH); + reg_r25 = eeprom_valid(reg_r25, 0x40, EEPROM_BBPTUNE_R25_HIGH); + reg_r61 = eeprom_valid(reg_r61, 0x6d, EEPROM_BBPTUNE_R61_HIGH); + } else { + reg_r24 = eeprom_valid(reg_r24, 0x80, EEPROM_BBPTUNE_R24_LOW); + reg_r25 = eeprom_valid(reg_r25, 0x50, EEPROM_BBPTUNE_R25_LOW); + reg_r61 = eeprom_valid(reg_r61, 0x60, EEPROM_BBPTUNE_R61_LOW); + } + + rt2x00_bbp_write(rt2x00dev, 24, reg_r24); + rt2x00_bbp_write(rt2x00dev, 25, reg_r25); + rt2x00_bbp_write(rt2x00dev, 61, reg_r61); + + rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_VGC, &vgc_bound); + vgc_bound = eeprom_valid(vgc_bound, 0x40, EEPROM_BBPTUNE_VGCUPPER); + + low_bound = 0x32; + if (rssi >= 43) + up_bound = vgc_bound; + else + up_bound = vgc_bound - (43 - rssi); + if (up_bound < low_bound) + up_bound = low_bound; + + rt2x00_bbp_read(rt2x00dev, 17, &bbp_r17); + rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R17, ®_r17); + + sens = bbp_r17; + + if (rssi > 80) + sens = 0x60; + else if (rssi >= 62) + sens = eeprom_valid(reg_r17, 0x48, EEPROM_BBPTUNE_R17_HIGH); + else if (rssi >= 46) + sens = eeprom_valid(reg_r17, 0x41, EEPROM_BBPTUNE_R17_LOW); + else if (bbp_r17 > up_bound) + sens = up_bound; + else { + rt2x00_register_read(rt2x00dev, STA_CSR3, &cca_alarm); + if (cca_alarm > 512 && bbp_r17 < up_bound) + sens = bbp_r17 + 1; + else if (cca_alarm < 100 && bbp_r17 > low_bound) + sens = bbp_r17 - 1; + } + + rt2x00_bbp_write(rt2x00dev, 17, sens); + + /* + * Update noise statistics. + */ + rt2x00_update_link_noise(&rt2x00dev->link, bbp_r17); + +exit: + queue_delayed_work(rt2x00dev->workqueue, &rt2x00dev->link.work, + LINK_TUNE_INTERVAL); +} + +/* + * LED functions. + */ +static void rt2500usb_enable_led(struct rt2x00_dev *rt2x00dev) +{ + u16 reg; + + rt2x00_register_read(rt2x00dev, MAC_CSR21, ®); + rt2x00_set_field16(®, MAC_CSR21_ON_PERIOD, 70); + rt2x00_set_field16(®, MAC_CSR21_OFF_PERIOD, 30); + rt2x00_register_write(rt2x00dev, MAC_CSR21, reg); + + rt2x00_register_read(rt2x00dev, MAC_CSR20, ®); + + if (rt2x00dev->led_mode == LED_MODE_TXRX_ACTIVITY) { + rt2x00_set_field16(®, MAC_CSR20_LINK, 1); + rt2x00_set_field16(®, MAC_CSR20_ACTIVITY, 0); + } else if (rt2x00dev->led_mode == LED_MODE_ASUS) { + rt2x00_set_field16(®, MAC_CSR20_LINK, 0); + rt2x00_set_field16(®, MAC_CSR20_ACTIVITY, 1); + } else { + rt2x00_set_field16(®, MAC_CSR20_LINK, 1); + rt2x00_set_field16(®, MAC_CSR20_ACTIVITY, 1); + } + + rt2x00_register_write(rt2x00dev, MAC_CSR20, reg); +} + +static void rt2500usb_disable_led(struct rt2x00_dev *rt2x00dev) +{ + u16 reg; + + rt2x00_register_read(rt2x00dev, MAC_CSR20, ®); + rt2x00_set_field16(®, MAC_CSR20_LINK, 0); + rt2x00_set_field16(®, MAC_CSR20_ACTIVITY, 0); + rt2x00_register_write(rt2x00dev, MAC_CSR20, reg); +} + +/* + * Device state switch. + * This will put the device to sleep, or awake it. + */ +static int rt2500usb_set_state(struct rt2x00_dev *rt2x00dev, + enum dev_state state) +{ + u16 reg; + u16 reg2; + unsigned int i; + char put_to_sleep; + char bbp_state; + char rf_state; + + put_to_sleep = (state != STATE_AWAKE); + + reg = 0; + rt2x00_set_field16(®, MAC_CSR17_BBP_DESIRE_STATE, state); + rt2x00_set_field16(®, MAC_CSR17_RF_DESIRE_STATE, state); + rt2x00_set_field16(®, MAC_CSR17_PUT_TO_SLEEP, put_to_sleep); + rt2x00_register_write(rt2x00dev, MAC_CSR17, reg); + rt2x00_set_field16(®, MAC_CSR17_SET_STATE, 1); + rt2x00_register_write(rt2x00dev, MAC_CSR17, reg); + + /* + * Device is not guaranteed to be in the requested state yet. + * We must wait until the register indicates that the + * device has entered the correct state. + */ + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2x00_register_read(rt2x00dev, MAC_CSR17, ®2); + bbp_state = rt2x00_get_field16(reg2, MAC_CSR17_BBP_CURR_STATE); + rf_state = rt2x00_get_field16(reg2, MAC_CSR17_RF_CURR_STATE); + if (bbp_state == state && rf_state == state) + return 0; + rt2x00_register_write(rt2x00dev, MAC_CSR17, reg); + msleep(30); + } + + NOTICE("Device failed to enter state %d, " + "current device state: bbp %d and rf %d.\n", + state, bbp_state, rf_state); + + return -EBUSY; +} + +/* + * Initialization functions. + */ +static void rt2500usb_init_rxring(struct rt2x00_dev *rt2x00dev, + enum ring_index ring_type) +{ + struct data_ring *ring = &rt2x00dev->ring[ring_type]; + struct usb_device *usb_dev = + interface_to_usbdev(rt2x00dev_usb(rt2x00dev)); + unsigned int i; + + ring->type = ring_type; + + for (i = 0; i < ring->stats.limit; i++) { + usb_fill_bulk_urb( + ring->entry[i].priv, + usb_dev, + usb_rcvbulkpipe(usb_dev, 1), + ring->entry[i].skb->data, + ring->entry[i].skb->len, + rt2500usb_interrupt_rxdone, + &ring->entry[i]); + } + + rt2x00_ring_index_clear(ring); +} + +static void rt2500usb_init_txring(struct rt2x00_dev *rt2x00dev, + enum ring_index ring_type) +{ + struct data_ring *ring = &rt2x00dev->ring[ring_type]; + unsigned int i; + + ring->type = ring_type; + + for (i = 0; i < ring->stats.limit; i++) + CLEAR_FLAGS(&ring->entry[i]); + + rt2x00_ring_index_clear(ring); +} + +static int rt2500usb_init_rings(struct rt2x00_dev *rt2x00dev) +{ + rt2500usb_init_rxring(rt2x00dev, RING_RX); + rt2500usb_init_txring(rt2x00dev, RING_TX); + rt2500usb_init_txring(rt2x00dev, RING_ATIM); + rt2500usb_init_txring(rt2x00dev, RING_PRIO); + rt2500usb_init_txring(rt2x00dev, RING_BEACON); + + return 0; +} + +static int rt2500usb_init_registers(struct rt2x00_dev *rt2x00dev) +{ + u16 reg; + + rt2x00_vendor_request(rt2x00dev, USB_DEVICE_MODE, + USB_VENDOR_REQUEST_OUT, 0x0001, USB_MODE_TEST, NULL, 0, + REGISTER_TIMEOUT); + rt2x00_vendor_request(rt2x00dev, USB_SINGLE_WRITE, + USB_VENDOR_REQUEST_OUT, 0x0308, 0xf0, NULL, 0, + REGISTER_TIMEOUT); + + rt2x00_register_write(rt2x00dev, TXRX_CSR2, 0x0001); + rt2x00_register_write(rt2x00dev, MAC_CSR13, 0x1111); + rt2x00_register_write(rt2x00dev, MAC_CSR14, 0x1e11); + + rt2x00_register_write(rt2x00dev, MAC_CSR1, 0x0003); + rt2x00_register_write(rt2x00dev, MAC_CSR1, 0x0000); + rt2x00_register_write(rt2x00dev, TXRX_CSR5, 0x8c8d); + rt2x00_register_write(rt2x00dev, TXRX_CSR6, 0x8b8a); + rt2x00_register_write(rt2x00dev, TXRX_CSR7, 0x8687); + rt2x00_register_write(rt2x00dev, TXRX_CSR8, 0x0085); + rt2x00_register_write(rt2x00dev, TXRX_CSR21, 0xe78f); + rt2x00_register_write(rt2x00dev, MAC_CSR9, 0xff1d); + + if (rt2500usb_set_state(rt2x00dev, STATE_AWAKE)) + return -EBUSY; + + rt2x00_register_write(rt2x00dev, MAC_CSR1, 0x0004); + + reg = 0; + rt2x00_register_read(rt2x00dev, MAC_CSR0, ®); + if (reg >= 0x0003) { + rt2x00_register_read(rt2x00dev, PHY_CSR2, ®); + reg &= ~0x0002; + } else { + reg = 0x3002; + } + rt2x00_register_write(rt2x00dev, PHY_CSR2, reg); + + rt2x00_register_write(rt2x00dev, MAC_CSR11, 0x0002); + rt2x00_register_write(rt2x00dev, MAC_CSR22, 0x0053); + rt2x00_register_write(rt2x00dev, MAC_CSR15, 0x01ee); + rt2x00_register_write(rt2x00dev, MAC_CSR16, 0x0000); + + rt2x00_register_read(rt2x00dev, TXRX_CSR0, ®); + rt2x00_set_field16(®, TXRX_CSR0_IV_OFFSET, IEEE80211_HEADER); + rt2x00_set_field16(®, TXRX_CSR0_KEY_ID, 0xff); + rt2x00_register_write(rt2x00dev, TXRX_CSR0, reg); + + rt2x00_register_read(rt2x00dev, MAC_CSR8, ®); + rt2x00_set_field16(®, MAC_CSR8_MAX_FRAME_UNIT, + rt2x00dev->ring[RING_RX].data_size); + rt2x00_register_write(rt2x00dev, MAC_CSR8, reg); + + rt2x00_register_read(rt2x00dev, MAC_CSR18, ®); + rt2x00_set_field16(®, MAC_CSR18_DELAY_AFTER_BEACON, 0x5a); + rt2x00_register_write(rt2x00dev, MAC_CSR18, reg); + + rt2x00_register_read(rt2x00dev, TXRX_CSR1, ®); + rt2x00_set_field16(®, TXRX_CSR1_AUTO_SEQUENCE, 1); + rt2x00_register_write(rt2x00dev, TXRX_CSR1, reg); + + rt2x00_register_read(rt2x00dev, PHY_CSR4, ®); + rt2x00_register_write(rt2x00dev, PHY_CSR4, reg | 0x0001); + + return 0; +} + +static int rt2500usb_init_bbp(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + u16 eeprom; + u8 value; + u8 reg_id; + + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2x00_bbp_read(rt2x00dev, 0, &value); + if ((value != 0xff) && (value != 0x00)) + goto continue_csr_init; + NOTICE("Waiting for BBP register.\n"); + udelay(REGISTER_BUSY_DELAY); + } + + ERROR("BBP register access failed, aborting.\n"); + return -EACCES; + +continue_csr_init: + rt2x00_bbp_write(rt2x00dev, 3, 0x02); + rt2x00_bbp_write(rt2x00dev, 4, 0x19); + rt2x00_bbp_write(rt2x00dev, 14, 0x1c); + rt2x00_bbp_write(rt2x00dev, 15, 0x30); + rt2x00_bbp_write(rt2x00dev, 16, 0xac); + rt2x00_bbp_write(rt2x00dev, 17, 0x48); + rt2x00_bbp_write(rt2x00dev, 18, 0x18); + rt2x00_bbp_write(rt2x00dev, 19, 0xff); + rt2x00_bbp_write(rt2x00dev, 20, 0x1e); + rt2x00_bbp_write(rt2x00dev, 21, 0x08); + rt2x00_bbp_write(rt2x00dev, 22, 0x08); + rt2x00_bbp_write(rt2x00dev, 23, 0x08); + rt2x00_bbp_write(rt2x00dev, 24, 0x80); + rt2x00_bbp_write(rt2x00dev, 25, 0x50); + rt2x00_bbp_write(rt2x00dev, 26, 0x08); + rt2x00_bbp_write(rt2x00dev, 27, 0x23); + rt2x00_bbp_write(rt2x00dev, 30, 0x10); + rt2x00_bbp_write(rt2x00dev, 31, 0x2b); + rt2x00_bbp_write(rt2x00dev, 32, 0xb9); + rt2x00_bbp_write(rt2x00dev, 34, 0x12); + rt2x00_bbp_write(rt2x00dev, 35, 0x50); + rt2x00_bbp_write(rt2x00dev, 39, 0xc4); + rt2x00_bbp_write(rt2x00dev, 40, 0x02); + rt2x00_bbp_write(rt2x00dev, 41, 0x60); + rt2x00_bbp_write(rt2x00dev, 53, 0x10); + rt2x00_bbp_write(rt2x00dev, 54, 0x18); + rt2x00_bbp_write(rt2x00dev, 56, 0x08); + rt2x00_bbp_write(rt2x00dev, 57, 0x10); + rt2x00_bbp_write(rt2x00dev, 58, 0x08); + rt2x00_bbp_write(rt2x00dev, 61, 0x60); + rt2x00_bbp_write(rt2x00dev, 62, 0x10); + rt2x00_bbp_write(rt2x00dev, 75, 0xff); + + DEBUG("Start initialization from EEPROM...\n"); + for (i = 0; i < EEPROM_BBP_SIZE; i++) { + rt2x00_eeprom_read(rt2x00dev, EEPROM_BBP_START + i, &eeprom); + + if (eeprom != 0xffff && eeprom != 0x0000) { + reg_id = rt2x00_get_field16(eeprom, EEPROM_BBP_REG_ID); + value = rt2x00_get_field16(eeprom, EEPROM_BBP_VALUE); + DEBUG("BBP: 0x%02x, value: 0x%02x.\n", reg_id, value); + rt2x00_bbp_write(rt2x00dev, reg_id, value); + } + } + DEBUG("...End initialization from EEPROM.\n"); + + rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R24, &eeprom); + value = eeprom_valid(eeprom, 0x80, EEPROM_BBPTUNE_R24_LOW); + rt2x00_bbp_write(rt2x00dev, 24, value); + + rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R25, &eeprom); + value = eeprom_valid(eeprom, 0x50, EEPROM_BBPTUNE_R25_LOW); + rt2x00_bbp_write(rt2x00dev, 25, value); + + rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R61, &eeprom); + value = eeprom_valid(eeprom, 0x60, EEPROM_BBPTUNE_R61_LOW); + rt2x00_bbp_write(rt2x00dev, 61, value); + + rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_VGC, &eeprom); + value = eeprom_valid(eeprom, 0x40, EEPROM_BBPTUNE_VGCUPPER); + rt2x00_bbp_write(rt2x00dev, 17, value); + + return 0; +} + +/* + * Radio control functions. + */ +static void rt2500usb_toggle_rx(struct rt2x00_dev *rt2x00dev, int enable) +{ + u16 reg; + + rt2x00_register_read(rt2x00dev, TXRX_CSR2, ®); + rt2x00_set_field16(®, TXRX_CSR2_DISABLE_RX, !enable); + rt2x00_register_write(rt2x00dev, TXRX_CSR2, reg); +} + +static int rt2500usb_enable_radio(struct rt2x00_dev *rt2x00dev) +{ + struct data_ring *ring; + unsigned int i; + + /* + * Initialize all registers. + */ + if (rt2500usb_init_rings(rt2x00dev) || + rt2500usb_init_registers(rt2x00dev) || + rt2500usb_init_bbp(rt2x00dev)) { + ERROR("Register initialization failed.\n"); + return -EIO; + } + + ring = &rt2x00dev->ring[RING_RX]; + for (i = 0; i < ring->stats.limit; i++) { + SET_FLAG(&ring->entry[i], ENTRY_OWNER_NIC); + usb_submit_urb(ring->entry[i].priv, GFP_ATOMIC); + } + + /* + * Enable LED + */ + rt2500usb_enable_led(rt2x00dev); + + return 0; +} + +static void rt2500usb_disable_radio(struct rt2x00_dev *rt2x00dev) +{ + struct data_ring *ring; + unsigned int i; + + /* + * Disable LED + */ + rt2500usb_disable_led(rt2x00dev); + + rt2x00_register_write(rt2x00dev, MAC_CSR13, 0x2121); + rt2x00_register_write(rt2x00dev, MAC_CSR14, 0x2121); + + /* + * Disable synchronisation. + */ + rt2x00_register_write(rt2x00dev, TXRX_CSR19, 0); + + /* + * Cancel RX and TX. + */ + rt2x00_vendor_request(rt2x00dev, USB_RX_CONTROL, + USB_VENDOR_REQUEST_OUT, 0x00, 0x00, NULL, 0, REGISTER_TIMEOUT); + + ring = &rt2x00dev->ring[RING_RX]; + for (i = 0; i < ring->stats.limit; i++) + usb_kill_urb(ring->entry[i].priv); + + ring = &rt2x00dev->ring[RING_TX]; + for (i = 0; i < ring->stats.limit; i++) + usb_kill_urb(ring->entry[i].priv); + + ring = &rt2x00dev->ring[RING_ATIM]; + for (i = 0; i < ring->stats.limit; i++) + usb_kill_urb(ring->entry[i].priv); + + ring = &rt2x00dev->ring[RING_PRIO]; + for (i = 0; i < ring->stats.limit; i++) + usb_kill_urb(ring->entry[i].priv); + + ring = &rt2x00dev->ring[RING_BEACON]; + for (i = 0; i < ring->stats.limit; i++) + usb_kill_urb(ring->entry[i].priv); +} + +/* + * TX descriptor initialization + */ +static void rt2500usb_write_tx_desc(struct rt2x00_dev *rt2x00dev, + struct data_entry *entry, struct data_desc *txd, + struct data_entry_desc *desc, struct ieee80211_hdr *ieee80211hdr, + unsigned int length, struct ieee80211_tx_control *control) +{ + u32 word; + + /* + * Start writing the descriptor words. + */ + rt2x00_desc_read(txd, 1, &word); + rt2x00_set_field32(&word, TXD_W1_IV_OFFSET, IEEE80211_HEADER); + rt2x00_set_field32(&word, TXD_W1_AIFS, entry->ring->tx_params.aifs); + rt2x00_set_field32(&word, TXD_W1_CWMIN, entry->ring->tx_params.cw_min); + rt2x00_set_field32(&word, TXD_W1_CWMAX, entry->ring->tx_params.cw_max); + rt2x00_desc_write(txd, 1, word); + + rt2x00_desc_read(txd, 2, &word); + rt2x00_set_field32(&word, TXD_W2_PLCP_SIGNAL, desc->signal); + rt2x00_set_field32(&word, TXD_W2_PLCP_SERVICE, desc->service); + rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_LOW, desc->length_low); + rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_HIGH, desc->length_high); + rt2x00_desc_write(txd, 2, word); + + rt2x00_desc_read(txd, 0, &word); + rt2x00_set_field32(&word, TXD_W0_RETRY_LIMIT, control->retry_limit); + rt2x00_set_field32(&word, TXD_W0_MORE_FRAG, + !!(entry->reg & ENTRY_TXD_MORE_FRAG)); + rt2x00_set_field32(&word, TXD_W0_ACK, + !!(entry->reg & ENTRY_TXD_REQ_ACK)); + rt2x00_set_field32(&word, TXD_W0_TIMESTAMP, + !!(entry->reg & ENTRY_TXD_REQ_TIMESTAMP)); + rt2x00_set_field32(&word, TXD_W0_OFDM, + !!(entry->reg & ENTRY_TXD_OFDM_RATE)); + rt2x00_set_field32(&word, TXD_W0_NEW_SEQ, + !!(entry->reg & ENTRY_TXD_NEW_SEQ)); + rt2x00_set_field32(&word, TXD_W0_IFS, desc->ifs); + rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, length); + rt2x00_set_field32(&word, TXD_W0_CIPHER, CIPHER_NONE); + rt2x00_desc_write(txd, 0, word); +} + +/* + * TX data initialization + */ +static void rt2500usb_kick_beacon_gen(struct rt2x00_dev *rt2x00dev) +{ + u16 reg; + + rt2x00_register_read(rt2x00dev, TXRX_CSR19, ®); + if (!rt2x00_get_field16(reg, TXRX_CSR19_BEACON_GEN)) { + rt2x00_set_field16(®, TXRX_CSR19_BEACON_GEN, 1); + /* + * Beacon generation will fail initially. + * To prevent this we need to register the TXRX_CSR19 + * register several times. + */ + rt2x00_register_write(rt2x00dev, TXRX_CSR19, reg); + rt2x00_register_write(rt2x00dev, TXRX_CSR19, 0); + rt2x00_register_write(rt2x00dev, TXRX_CSR19, reg); + rt2x00_register_write(rt2x00dev, TXRX_CSR19, 0); + rt2x00_register_write(rt2x00dev, TXRX_CSR19, reg); + } +} + +/* + * Interrupt functions. + */ +static void rt2500usb_interrupt_rxdone(struct urb *urb) +{ + struct data_entry *entry = (struct data_entry*)urb->context; + struct data_ring *ring = entry->ring; + struct rt2x00_dev *rt2x00dev = ring->rt2x00dev; + struct data_desc *rxd = (struct data_desc*) + (entry->skb->data + urb->actual_length - ring->desc_size); + u32 word0; + u32 word1; + int signal; + int rssi; + int ofdm; + u16 size; + + if (!GET_FLAG(rt2x00dev, DEVICE_ENABLED_RADIO) || + !GET_FLAG(entry, ENTRY_OWNER_NIC)) + return; + + CLEAR_FLAG(entry, ENTRY_OWNER_NIC); + + /* + * Check if the received data is simply too small + * to be actually valid, or if the urb is signaling + * a problem. + */ + if (urb->actual_length < entry->ring->desc_size || urb->status) + goto skip_entry; + + rt2x00_desc_read(rxd, 0, &word0); + rt2x00_desc_read(rxd, 1, &word1); + + /* + * TODO: Don't we need to keep statistics + * updated about events like CRC and physical errors? + */ + if (rt2x00_get_field32(word0, RXD_W0_CRC) || + rt2x00_get_field32(word0, RXD_W0_PHYSICAL_ERROR)) + goto skip_entry; + + /* + * Obtain the status about this packet. + */ + size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT) - FCS_LEN; + signal = rt2x00_get_field32(word1, RXD_W1_SIGNAL); + rssi = rt2x00_get_field32(word1, RXD_W1_RSSI); + ofdm = rt2x00_get_field32(word0, RXD_W0_OFDM); + + /* + * Trim the skb_buffer to only contain the valid + * frame data (so ignore the device's descriptor). + */ + skb_trim(entry->skb, size); + + /* + * Send the packet to upper layer, and update urb. + */ + rt2x00lib_rxdone(entry, NULL, ring->data_size + ring->desc_size, + signal, rssi, ofdm); + urb->transfer_buffer = entry->skb->data; + urb->transfer_buffer_length = entry->skb->len; + +skip_entry: + if (GET_FLAG(ring->rt2x00dev, DEVICE_ENABLED_RADIO)) { + SET_FLAG(entry, ENTRY_OWNER_NIC); + usb_submit_urb(urb, GFP_ATOMIC); + } + + rt2x00_ring_index_inc(ring); +} + +/* + * Device initialization functions. + */ +static int rt2500usb_alloc_eeprom(struct rt2x00_dev *rt2x00dev) +{ + /* + * Allocate the eeprom memory, check the eeprom width + * and copy the entire eeprom into this allocated memory. + */ + rt2x00dev->eeprom = kzalloc(EEPROM_SIZE, GFP_KERNEL); + if (!rt2x00dev->eeprom) + return -ENOMEM; + + rt2x00_vendor_request( + rt2x00dev, USB_EEPROM_READ, USB_VENDOR_REQUEST_IN, + EEPROM_BASE, 0x00, rt2x00dev->eeprom, EEPROM_SIZE, + REGISTER_TIMEOUT * (EEPROM_SIZE / sizeof(u16))); + + return 0; +} + +static int rt2500usb_init_eeprom(struct rt2x00_dev *rt2x00dev) +{ + u16 reg; + u16 value; + u16 eeprom; + + /* + * Read EEPROM word for configuration. + */ + rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &eeprom); + + /* + * Identify RF chipset. + */ + value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE); + rt2x00_register_read(rt2x00dev, MAC_CSR0, ®); + rt2x00_set_chip(&rt2x00dev->chip, RT2570, value, reg); + + if (!rt2x00_rf(&rt2x00dev->chip, RF2522) && + !rt2x00_rf(&rt2x00dev->chip, RF2523) && + !rt2x00_rf(&rt2x00dev->chip, RF2524) && + !rt2x00_rf(&rt2x00dev->chip, RF2525) && + !rt2x00_rf(&rt2x00dev->chip, RF2525E) && + !rt2x00_rf(&rt2x00dev->chip, RF5222)) { + ERROR("Invalid RF chipset detected.\n"); + return -ENODEV; + } + + /* + * Identify default antenna configuration. + */ + rt2x00dev->hw->conf.antenna_sel_tx = rt2x00_get_field16(eeprom, + EEPROM_ANTENNA_TX_DEFAULT); + rt2x00dev->hw->conf.antenna_sel_rx = rt2x00_get_field16(eeprom, + EEPROM_ANTENNA_RX_DEFAULT); + + /* + * Store led mode, for correct led behaviour. + */ + rt2x00dev->led_mode = rt2x00_get_field16(eeprom, + EEPROM_ANTENNA_LED_MODE); + + /* + * Check if the BBP tuning should be disabled. + */ + rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &eeprom); + if (eeprom == 0xffff) + eeprom = 0; + if (rt2x00_get_field16(eeprom, EEPROM_NIC_DYN_BBP_TUNE)) + SET_FLAG(rt2x00dev, CONFIG_DISABLE_LINK_TUNING); + + /* + * Read the RSSI <-> dBm offset information. + */ + rt2x00_eeprom_read(rt2x00dev, EEPROM_CALIBRATE_OFFSET, &eeprom); + rt2x00dev->hw->max_rssi = + rt2x00_get_field16(eeprom, EEPROM_CALIBRATE_OFFSET_RSSI); + if (rt2x00dev->hw->max_rssi < 0 || rt2x00dev->hw->max_rssi == (s8)0xff) + rt2x00dev->hw->max_rssi = MAX_RX_SSI; + + return 0; +} + +static const struct { + unsigned int chip; + u32 val[3]; +} rf_vals[] = { + { RF2522, { 0x00002050, 0x00000101, 0x00000000 } }, + { RF2523, { 0x00022010, 0x000e0111, 0x00000a1b } }, + { RF2524, { 0x00032020, 0x00000101, 0x00000a1b } }, + { RF2525, { 0x00022020, 0x00060111, 0x00000a1b } }, + { RF2525E, { 0x00022010, 0x00060111, 0x00000000 } }, + { RF5222, { 0x00000000, 0x00000101, 0x00000000 } } +}; + +/* + * RF value list for RF2522 + * Supports: 2.4 GHz + */ +static const u32 rf_vals_bg_2522[] = { + 0x000c1fda, 0x000c1fee, 0x000c2002, 0x000c2016, 0x000c202a, + 0x000c203e, 0x000c2052, 0x000c2066, 0x000c207a, 0x000c208e, + 0x000c20a2, 0x000c20b6, 0x000c20ca, 0x000c20fa +}; + +/* + * RF value list for RF2523, RF2524 & RF2525 + * Supports: 2.4 GHz + */ +static const u32 rf_vals_bg_252x[] = { + 0x00000c9e, 0x00000ca2, 0x00000ca6, 0x00000caa, 0x00000cae, + 0x00000cb2, 0x00000cb6, 0x00000cba, 0x00000cbe, 0x00000d02, + 0x00000d06, 0x00000d0a, 0x00000d0e, 0x00000d1a +}; + +/* + * RF value list for RF2525E + * Supports: 2.4 GHz + */ +static const u32 rf_vals_bg_2525e[] = { + 0x0000089a, 0x0000089e, 0x0000089e, 0x000008a2, 0x000008a2, + 0x000008a6, 0x000008a6, 0x000008aa, 0x000008aa, 0x000008ae, + 0x000008ae, 0x000008b2, 0x000008b2, 0x000008b6 +}; + +/* + * RF value list for RF5222 + * Supports: 2.4 GHz & 5.2 GHz + */ +static const u32 rf_vals_abg_5222[] = { + 0x00001136, 0x0000113a, 0x0000113e, 0x00001182, 0x00001186, + 0x0000118a, 0x0000118e, 0x00001192, 0x00001196, 0x0000119a, + 0x0000119e, 0x000011a2, 0x000011a6, 0x000011ae, 0x0001889a, + 0x0001889a, 0x0001889e, 0x000188a2, 0x000188a6, 0x000188aa, + 0x000188ae, 0x000188b2, 0x00008802, 0x00008806, 0x0000880a, + 0x0000880e, 0x00008812, 0x00008816, 0x0000881a, 0x0000881e, + 0x00008822, 0x00008826, 0x0000882a, 0x000090a6, 0x000090ae, + 0x000090b6, 0x000090be +}; + +static void rt2500usb_init_hw_mode(struct rt2x00_dev *rt2x00dev) +{ + struct hw_mode_spec *spec = &rt2x00dev->spec; + u8 *txpower; + unsigned int i; + + if (GET_FLAG(rt2x00dev, DEVICE_INITIALIZED_HW)) + return; + + /* + * Initialize all hw fields. + */ + rt2x00dev->hw->flags = IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE | + IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | + IEEE80211_HW_WEP_INCLUDE_IV | + IEEE80211_HW_DATA_NULLFUNC_ACK | + IEEE80211_HW_NO_TKIP_WMM_HWACCEL | + IEEE80211_HW_MONITOR_DURING_OPER; + rt2x00dev->hw->extra_tx_headroom = TXD_DESC_SIZE; + rt2x00dev->hw->max_rssi = MAX_RX_SSI; + rt2x00dev->hw->max_noise = MAX_RX_NOISE; + rt2x00dev->hw->queues = RING_NUM_TX; + + /* + * This device supports ATIM + */ + SET_FLAG(rt2x00dev, DEVICE_SUPPORT_ATIM); + + /* + * Set device specific, but channel independent RF values. + */ + for (i = 0; i < ARRAY_SIZE(rf_vals); i++) { + if (rt2x00_rf(&rt2x00dev->chip, rf_vals[i].chip)) { + rt2x00dev->rf1 = rf_vals[i].val[0]; + rt2x00dev->rf3 = rf_vals[i].val[1]; + rt2x00dev->rf4 = rf_vals[i].val[2]; + } + } + + /* + * Convert tx_power array in eeprom. + */ + txpower = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_START); + for (i = 0; i < 14; i++) + txpower[i] = TXPOWER_FROM_DEV(txpower[i]); + + /* + * Initialize hw_mode information. + */ + spec->mac_addr = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); + spec->num_modes = 2; + spec->num_rates = 12; + spec->num_channels = 14; + spec->tx_power_a = NULL; + spec->tx_power_bg = txpower; + spec->tx_power_default = DEFAULT_TXPOWER; + spec->chan_val_a = NULL; + + if (rt2x00_rf(&rt2x00dev->chip, RF2522)) + spec->chan_val_bg = rf_vals_bg_2522; + else if (rt2x00_rf(&rt2x00dev->chip, RF2523) || + rt2x00_rf(&rt2x00dev->chip, RF2524) || + rt2x00_rf(&rt2x00dev->chip, RF2525)) + spec->chan_val_bg = rf_vals_bg_252x; + else if (rt2x00_rf(&rt2x00dev->chip, RF2525E)) + spec->chan_val_bg = rf_vals_bg_2525e; + else if (rt2x00_rf(&rt2x00dev->chip, RF5222)) + spec->chan_val_bg = rf_vals_abg_5222; + + if (rt2x00_rf(&rt2x00dev->chip, RF5222)) { + spec->num_modes = 3; + spec->num_channels += 23; + spec->chan_val_a = &rf_vals_abg_5222[14]; + } +} + +static int rt2500usb_init_hw(struct rt2x00_dev *rt2x00dev) +{ + int retval; + + rt2x00dev->rxd_size = RXD_DESC_SIZE; + rt2x00dev->txd_size = TXD_DESC_SIZE; + + /* + * Allocate eeprom data. + */ + retval = rt2500usb_alloc_eeprom(rt2x00dev); + if (retval) + return retval; + + retval = rt2500usb_init_eeprom(rt2x00dev); + if (retval) + return retval; + + /* + * Initialize hw specifications. + */ + rt2500usb_init_hw_mode(rt2x00dev); + + return 0; +} + +/* + * IEEE80211 stack callback functions. + */ +static int rt2500usb_get_stats(struct ieee80211_hw *hw, + struct ieee80211_low_level_stats *stats) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + u16 reg; + + /* + * Update FCS error count from register. + * The dot11ACKFailureCount, dot11RTSFailureCount and + * dot11RTSSuccessCount are updated in interrupt time. + */ + rt2x00_register_read(rt2x00dev, STA_CSR0, ®); + rt2x00dev->low_level_stats.dot11FCSErrorCount += + rt2x00_get_field16(reg, STA_CSR0_FCS_ERROR); + + memcpy(stats, &rt2x00dev->low_level_stats, sizeof(*stats)); + + return 0; +} + +static const struct ieee80211_ops rt2500usb_mac80211_ops = { + .tx = rt2x00lib_tx, + .reset = rt2x00lib_reset, + .add_interface = rt2x00lib_add_interface, + .remove_interface = rt2x00lib_remove_interface, + .config = rt2x00lib_config, + .config_interface = rt2x00lib_config_interface, + .set_multicast_list = rt2x00lib_set_multicast_list, + .get_stats = rt2500usb_get_stats, + .conf_tx = rt2x00lib_conf_tx, + .get_tx_stats = rt2x00lib_get_tx_stats, + .beacon_update = rt2x00usb_beacon_update, +}; + +static const struct rt2x00lib_ops rt2500usb_rt2x00_ops = { + .link_tuner = rt2500usb_link_tuner, + .init_hw = rt2500usb_init_hw, + .initialize = rt2x00usb_initialize, + .uninitialize = rt2x00usb_uninitialize, + .enable_radio = rt2500usb_enable_radio, + .disable_radio = rt2500usb_disable_radio, + .set_state = rt2500usb_set_state, + .toggle_rx = rt2500usb_toggle_rx, + .write_tx_desc = rt2500usb_write_tx_desc, + .write_tx_data = rt2x00usb_write_tx_data, + .kick_beacon_gen = rt2500usb_kick_beacon_gen, + .config_type = rt2500usb_config_type, + .config_phymode = rt2500usb_config_phymode, + .config_channel = rt2500usb_config_channel, + .config_mac_addr = rt2500usb_config_mac_addr, + .config_bssid = rt2500usb_config_bssid, + .config_txpower = rt2500usb_config_txpower, + .config_antenna = rt2500usb_config_antenna, + .config_duration = rt2500usb_config_duration, +}; + +static const struct rt2x00_ops rt2500usb_ops = { + .lib = &rt2500usb_rt2x00_ops, + .hw = &rt2500usb_mac80211_ops, +#ifdef CONFIG_RT2X00_DEBUGFS + .debugfs = &rt2500usb_rt2x00debug, +#endif /* CONFIG_RT2X00_DEBUGFS */ +}; + +/* + * rt2500usb module information. + */ +static char version[] = + DRV_NAME " - " DRV_VERSION " (" DRV_RELDATE ") by " DRV_PROJECT; + +static struct usb_device_id rt2500usb_device_table[] = { + /* ASUS */ + { USB_DEVICE(0x0b05, 0x1706), USB_DEVICE_DATA(&rt2500usb_ops) }, + { USB_DEVICE(0x0b05, 0x1707), USB_DEVICE_DATA(&rt2500usb_ops) }, + /* Belkin */ + { USB_DEVICE(0x050d, 0x7050), USB_DEVICE_DATA(&rt2500usb_ops) }, + { USB_DEVICE(0x050d, 0x7051), USB_DEVICE_DATA(&rt2500usb_ops) }, + { USB_DEVICE(0x050d, 0x705a), USB_DEVICE_DATA(&rt2500usb_ops) }, + /* Cisco Systems */ + { USB_DEVICE(0x13b1, 0x000d), USB_DEVICE_DATA(&rt2500usb_ops) }, + { USB_DEVICE(0x13b1, 0x0011), USB_DEVICE_DATA(&rt2500usb_ops) }, + { USB_DEVICE(0x13b1, 0x001a), USB_DEVICE_DATA(&rt2500usb_ops) }, + /* Conceptronic */ + { USB_DEVICE(0x14b2, 0x3c02), USB_DEVICE_DATA(&rt2500usb_ops) }, + /* D-LINK */ + { USB_DEVICE(0x2001, 0x3c00), USB_DEVICE_DATA(&rt2500usb_ops) }, + /* Gigabyte */ + { USB_DEVICE(0x1044, 0x8001), USB_DEVICE_DATA(&rt2500usb_ops) }, + { USB_DEVICE(0x1044, 0x8007), USB_DEVICE_DATA(&rt2500usb_ops) }, + /* Hercules */ + { USB_DEVICE(0x06f8, 0xe000), USB_DEVICE_DATA(&rt2500usb_ops) }, + /* Melco */ + { USB_DEVICE(0x0411, 0x0066), USB_DEVICE_DATA(&rt2500usb_ops) }, + { USB_DEVICE(0x0411, 0x0067), USB_DEVICE_DATA(&rt2500usb_ops) }, + { USB_DEVICE(0x0411, 0x008b), USB_DEVICE_DATA(&rt2500usb_ops) }, + /* MSI */ + { USB_DEVICE(0x0db0, 0x6861), USB_DEVICE_DATA(&rt2500usb_ops) }, + { USB_DEVICE(0x0db0, 0x6865), USB_DEVICE_DATA(&rt2500usb_ops) }, + { USB_DEVICE(0x0db0, 0x6869), USB_DEVICE_DATA(&rt2500usb_ops) }, + /* Ralink */ + { USB_DEVICE(0x148f, 0x1706), USB_DEVICE_DATA(&rt2500usb_ops) }, + { USB_DEVICE(0x148f, 0x2570), USB_DEVICE_DATA(&rt2500usb_ops) }, + { USB_DEVICE(0x148f, 0x2573), USB_DEVICE_DATA(&rt2500usb_ops) }, + { USB_DEVICE(0x148f, 0x9020), USB_DEVICE_DATA(&rt2500usb_ops) }, + /* Siemens */ + { USB_DEVICE(0x0681, 0x3c06), USB_DEVICE_DATA(&rt2500usb_ops) }, + /* SMC */ + { USB_DEVICE(0x0707, 0xee13), USB_DEVICE_DATA(&rt2500usb_ops) }, + /* Spairon */ + { USB_DEVICE(0x114b, 0x0110), USB_DEVICE_DATA(&rt2500usb_ops) }, + /* Trust */ + { USB_DEVICE(0x0eb0, 0x9020), USB_DEVICE_DATA(&rt2500usb_ops) }, + /* Zinwell */ + { USB_DEVICE(0x5a57, 0x0260), USB_DEVICE_DATA(&rt2500usb_ops) }, + { 0, } +}; + +MODULE_AUTHOR(DRV_PROJECT); +MODULE_VERSION(DRV_VERSION); +MODULE_DESCRIPTION("Ralink RT2500 USB Wireless LAN driver."); +MODULE_SUPPORTED_DEVICE("Ralink RT2570 USB chipset based cards"); +MODULE_DEVICE_TABLE(usb, rt2500usb_device_table); +MODULE_LICENSE("GPL"); + +#ifdef CONFIG_RT2X00_DEBUG +module_param_named(debug, rt2x00_debug_level, bool, S_IWUSR | S_IRUGO); +MODULE_PARM_DESC(debug, "Set this parameter to 1 to enable debug output."); +#endif /* CONFIG_RT2X00_DEBUG */ + +static struct usb_driver rt2500usb_driver = { + .name = DRV_NAME, + .id_table = rt2500usb_device_table, + .probe = rt2x00usb_probe, + .disconnect = rt2x00usb_disconnect, +#ifdef CONFIG_PM + .suspend = rt2x00usb_suspend, + .resume = rt2x00usb_resume, +#endif /* CONFIG_PM */ +}; + +static int __init rt2500usb_init(void) +{ + printk(KERN_INFO "Loading module: %s.\n", version); + return usb_register(&rt2500usb_driver); +} + +static void __exit rt2500usb_exit(void) +{ + printk(KERN_INFO "Unloading module: %s.\n", version); + usb_deregister(&rt2500usb_driver); +} + +module_init(rt2500usb_init); +module_exit(rt2500usb_exit); diff --git a/drivers/net/wireless/mac80211/rt2x00/rt2500usb.h b/drivers/net/wireless/mac80211/rt2x00/rt2500usb.h new file mode 100644 index 0000000..25150b1 --- /dev/null +++ b/drivers/net/wireless/mac80211/rt2x00/rt2500usb.h @@ -0,0 +1,738 @@ +/* + Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + + + 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. + */ + +/* + Module: rt2500usb + Abstract: Data structures and registers for the rt2500usb module. + Supported chipsets: RT2570. + */ + +#ifndef RT2500USB_H +#define RT2500USB_H + +/* + * RF chip defines. + */ +#define RF2522 0x0000 +#define RF2523 0x0001 +#define RF2524 0x0002 +#define RF2525 0x0003 +#define RF2525E 0x0005 +#define RF5222 0x0010 + +/* + * Max RSSI value, required for RSSI <-> dBm conversion. + */ +#define MAX_RX_SSI 120 +#define MAX_RX_NOISE -110 + +/* + * Register layout information. + */ +#define CSR_REG_BASE 0x0400 +#define CSR_REG_SIZE 0x0100 +#define EEPROM_BASE 0x0000 +#define EEPROM_SIZE 0x006a +#define BBP_SIZE 0x0060 + +/* + * Control/Status Registers(CSR). + * Some values are set in TU, whereas 1 TU == 1024 us. + */ + +/* + * MAC_CSR0: ASIC revision number. + */ +#define MAC_CSR0 0x0400 + +/* + * MAC_CSR1: System control. + */ +#define MAC_CSR1 0x0402 + +/* + * MAC_CSR2: STA MAC register 0. + */ +#define MAC_CSR2 0x0404 +#define MAC_CSR2_BYTE0 FIELD16(0x00ff) +#define MAC_CSR2_BYTE1 FIELD16(0xff00) + +/* + * MAC_CSR3: STA MAC register 1. + */ +#define MAC_CSR3 0x0406 +#define MAC_CSR3_BYTE2 FIELD16(0x00ff) +#define MAC_CSR3_BYTE3 FIELD16(0xff00) + +/* + * MAC_CSR4: STA MAC register 2. + */ +#define MAC_CSR4 0X0408 +#define MAC_CSR4_BYTE4 FIELD16(0x00ff) +#define MAC_CSR4_BYTE5 FIELD16(0xff00) + +/* + * MAC_CSR5: BSSID register 0. + */ +#define MAC_CSR5 0x040a +#define MAC_CSR5_BYTE0 FIELD16(0x00ff) +#define MAC_CSR5_BYTE1 FIELD16(0xff00) + +/* + * MAC_CSR6: BSSID register 1. + */ +#define MAC_CSR6 0x040c +#define MAC_CSR6_BYTE2 FIELD16(0x00ff) +#define MAC_CSR6_BYTE3 FIELD16(0xff00) + +/* + * MAC_CSR7: BSSID register 2. + */ +#define MAC_CSR7 0x040e +#define MAC_CSR7_BYTE4 FIELD16(0x00ff) +#define MAC_CSR7_BYTE5 FIELD16(0xff00) + +/* + * MAC_CSR8: Max frame length. + */ +#define MAC_CSR8 0x0410 +#define MAC_CSR8_MAX_FRAME_UNIT FIELD16(0x0fff) + +/* + * Misc MAC_CSR registers. + * MAC_CSR9: Timer control. + * MAC_CSR10: Slot time. + * MAC_CSR11: IFS. + * MAC_CSR12: EIFS. + * MAC_CSR13: Power mode0. + * MAC_CSR14: Power mode1. + * MAC_CSR15: Power saving transition0 + * MAC_CSR16: Power saving transition1 + */ +#define MAC_CSR9 0x0412 +#define MAC_CSR10 0x0414 +#define MAC_CSR11 0x0416 +#define MAC_CSR12 0x0418 +#define MAC_CSR13 0x041a +#define MAC_CSR14 0x041c +#define MAC_CSR15 0x041e +#define MAC_CSR16 0x0420 + +/* + * MAC_CSR17: Manual power control / status register. + * Allowed state: 0 deep_sleep, 1: sleep, 2: standby, 3: awake. + * SET_STATE: Set state. Write 1 to trigger, self cleared. + * BBP_DESIRE_STATE: BBP desired state. + * RF_DESIRE_STATE: RF desired state. + * BBP_CURRENT_STATE: BBP current state. + * RF_CURRENT_STATE: RF current state. + * PUT_TO_SLEEP: Put to sleep. Write 1 to trigger, self cleared. + */ +#define MAC_CSR17 0x0422 +#define MAC_CSR17_SET_STATE FIELD16(0x0001) +#define MAC_CSR17_BBP_DESIRE_STATE FIELD16(0x0006) +#define MAC_CSR17_RF_DESIRE_STATE FIELD16(0x0018) +#define MAC_CSR17_BBP_CURR_STATE FIELD16(0x0060) +#define MAC_CSR17_RF_CURR_STATE FIELD16(0x0180) +#define MAC_CSR17_PUT_TO_SLEEP FIELD16(0x0200) + +/* + * MAC_CSR18: Wakeup timer register. + * DELAY_AFTER_BEACON: Delay after Tbcn expired in units of 1/16 TU. + * BEACONS_BEFORE_WAKEUP: Number of beacon before wakeup. + * AUTO_WAKE: Enable auto wakeup / sleep mechanism. + */ +#define MAC_CSR18 0x0424 +#define MAC_CSR18_DELAY_AFTER_BEACON FIELD16(0x00ff) +#define MAC_CSR18_BEACONS_BEFORE_WAKEUP FIELD16(0x7f00) +#define MAC_CSR18_AUTO_WAKE FIELD16(0x8000) + +/* + * MAC_CSR19: GPIO control register. + */ +#define MAC_CSR19 0x0426 + +/* + * MAC_CSR20: LED control register. + * ACTIVITY: 0: idle, 1: active. + * LINK: 0: linkoff, 1: linkup. + * ACTIVITY_POLARITY: 0: active low, 1: active high. + */ +#define MAC_CSR20 0x0428 +#define MAC_CSR20_ACTIVITY FIELD16(0x0001) +#define MAC_CSR20_LINK FIELD16(0x0002) +#define MAC_CSR20_ACTIVITY_POLARITY FIELD16(0x0004) + +/* + * MAC_CSR21: LED control register. + * ON_PERIOD: On period, default 70ms. + * OFF_PERIOD: Off period, default 30ms. + */ +#define MAC_CSR21 0x042a +#define MAC_CSR21_ON_PERIOD FIELD16(0x00ff) +#define MAC_CSR21_OFF_PERIOD FIELD16(0xff00) + +/* + * Collision window control register. + */ +#define MAC_CSR22 0x042c + +/* + * Transmit related CSRs. + * Some values are set in TU, whereas 1 TU == 1024 us. + */ + +/* + * TXRX_CSR0: Security control register. + */ +#define TXRX_CSR0 0x0440 +#define TXRX_CSR0_ALGORITHM FIELD16(0x0007) +#define TXRX_CSR0_IV_OFFSET FIELD16(0x01f8) +#define TXRX_CSR0_KEY_ID FIELD16(0x1e00) + +/* + * TXRX_CSR1: TX configuration. + * ACK_TIMEOUT: ACK Timeout in unit of 1-us. + * TSF_OFFSET: TSF offset in MAC header. + * AUTO_SEQUENCE: Let ASIC control frame sequence number. + */ +#define TXRX_CSR1 0x0442 +#define TXRX_CSR1_ACK_TIMEOUT FIELD16(0x00ff) +#define TXRX_CSR1_TSF_OFFSET FIELD16(0x7f00) +#define TXRX_CSR1_AUTO_SEQUENCE FIELD16(0x8000) + +/* + * TXRX_CSR2: RX control. + * DISABLE_RX: Disable rx engine. + * DROP_CRC: Drop crc error. + * DROP_PHYSICAL: Drop physical error. + * DROP_CONTROL: Drop control frame. + * DROP_NOT_TO_ME: Drop not to me unicast frame. + * DROP_TODS: Drop frame tods bit is true. + * DROP_VERSION_ERROR: Drop version error frame. + * DROP_MCAST: Drop multicast frames. + * DROP_BCAST: Drop broadcast frames. + */ +#define TXRX_CSR2 0x0444 +#define TXRX_CSR2_DISABLE_RX FIELD16(0x0001) +#define TXRX_CSR2_DROP_CRC FIELD16(0x0002) +#define TXRX_CSR2_DROP_PHYSICAL FIELD16(0x0004) +#define TXRX_CSR2_DROP_CONTROL FIELD16(0x0008) +#define TXRX_CSR2_DROP_NOT_TO_ME FIELD16(0x0010) +#define TXRX_CSR2_DROP_TODS FIELD16(0x0020) +#define TXRX_CSR2_DROP_VERSION_ERROR FIELD16(0x0040) +#define TXRX_CSR2_DROP_MCAST FIELD16(0x0200) +#define TXRX_CSR2_DROP_BCAST FIELD16(0x0400) + +/* + * RX BBP ID registers + * TXRX_CSR3: CCK RX BBP ID. + * TXRX_CSR4: OFDM RX BBP ID. + */ +#define TXRX_CSR3 0x0446 +#define TXRX_CSR4 0x0448 + +/* + * TX BBP ID registers + * TXRX_CSR5: CCK TX BBP ID0. + * TXRX_CSR5: CCK TX BBP ID1. + * TXRX_CSR5: OFDM TX BBP ID0. + * TXRX_CSR5: OFDM TX BBP ID1. + */ +#define TXRX_CSR5 0x044a +#define TXRX_CSR6 0x044c +#define TXRX_CSR7 0x044e +#define TXRX_CSR8 0x0450 + +/* + * TXRX_CSR9: TX ACK time-out. + */ +#define TXRX_CSR9 0x0452 + +/* + * TXRX_CSR10: Auto responder control. + */ +#define TXRX_CSR10 0x0454 +#define TXRX_CSR10_AUTORESPOND_PREAMBLE FIELD16(0x0004) + +/* + * TXRX_CSR11: Auto responder basic rate. + */ +#define TXRX_CSR11 0x0456 + +/* + * ACK/CTS time registers. + */ +#define TXRX_CSR12 0x0458 +#define TXRX_CSR13 0x045a +#define TXRX_CSR14 0x045c +#define TXRX_CSR15 0x045e +#define TXRX_CSR16 0x0460 +#define TXRX_CSR17 0x0462 + +/* + * TXRX_CSR18: Synchronization control register. + */ +#define TXRX_CSR18 0x0464 +#define TXRX_CSR18_OFFSET FIELD16(0x000f) +#define TXRX_CSR18_INTERVAL FIELD16(0xfff0) + +/* + * TXRX_CSR19: Synchronization control register. + * TSF_COUNT: Enable TSF auto counting. + * TSF_SYNC: Tsf sync, 0: disable, 1: infra, 2: ad-hoc/master mode. + * TBCN: Enable Tbcn with reload value. + * BEACON_GEN: Enable beacon generator. + */ +#define TXRX_CSR19 0x0466 +#define TXRX_CSR19_TSF_COUNT FIELD16(0x0001) +#define TXRX_CSR19_TSF_SYNC FIELD16(0x0006) +#define TXRX_CSR19_TBCN FIELD16(0x0008) +#define TXRX_CSR19_BEACON_GEN FIELD16(0x0010) + +/* + * TXRX_CSR20: Tx BEACON offset time control register. + * OFFSET: In units of usec. + * BCN_EXPECT_WINDOW: Default: 2^CWmin + */ +#define TXRX_CSR20 0x0468 +#define TXRX_CSR20_OFFSET FIELD16(0x1fff) +#define TXRX_CSR20_BCN_EXPECT_WINDOW FIELD16(0xe000) + +/* + * TXRX_CSR21 + */ +#define TXRX_CSR21 0x046a + +/* + * Encryption related CSRs. + * + */ + +/* + * SEC_CSR0-SEC_CSR7: Shared key 0, word 0-7 + */ +#define SEC_CSR0 0x0480 +#define SEC_CSR1 0x0482 +#define SEC_CSR2 0x0484 +#define SEC_CSR3 0x0486 +#define SEC_CSR4 0x0488 +#define SEC_CSR5 0x048a +#define SEC_CSR6 0x048c +#define SEC_CSR7 0x048e + +/* + * SEC_CSR8-SEC_CSR15: Shared key 1, word 0-7 + */ +#define SEC_CSR8 0x0490 +#define SEC_CSR9 0x0492 +#define SEC_CSR10 0x0494 +#define SEC_CSR11 0x0496 +#define SEC_CSR12 0x0498 +#define SEC_CSR13 0x049a +#define SEC_CSR14 0x049c +#define SEC_CSR15 0x049e + +/* + * SEC_CSR16-SEC_CSR23: Shared key 2, word 0-7 + */ +#define SEC_CSR16 0x04a0 +#define SEC_CSR17 0x04a2 +#define SEC_CSR18 0X04A4 +#define SEC_CSR19 0x04a6 +#define SEC_CSR20 0x04a8 +#define SEC_CSR21 0x04aa +#define SEC_CSR22 0x04ac +#define SEC_CSR23 0x04ae + +/* + * SEC_CSR24-SEC_CSR31: Shared key 3, word 0-7 + */ +#define SEC_CSR24 0x04b0 +#define SEC_CSR25 0x04b2 +#define SEC_CSR26 0x04b4 +#define SEC_CSR27 0x04b6 +#define SEC_CSR28 0x04b8 +#define SEC_CSR29 0x04ba +#define SEC_CSR30 0x04bc +#define SEC_CSR31 0x04be + +/* + * PHY control registers. + */ + +/* + * PHY_CSR0: RF switching timing control. + */ +#define PHY_CSR0 0x04c0 + +/* + * PHY_CSR1: TX PA configuration. + */ +#define PHY_CSR1 0x04c2 + +/* + * MAC configuration registers. + * PHY_CSR2: TX MAC configuration. + * PHY_CSR3: RX MAC configuration. + */ +#define PHY_CSR2 0x04c4 +#define PHY_CSR3 0x04c6 + +/* + * PHY_CSR4: Interface configuration. + */ +#define PHY_CSR4 0x04c8 + +/* + * BBP pre-TX registers. + * PHY_CSR5: BBP pre-TX CCK. + */ +#define PHY_CSR5 0x04ca +#define PHY_CSR5_CCK FIELD16(0x0003) +#define PHY_CSR5_CCK_FLIP FIELD16(0x0004) + +/* + * BBP pre-TX registers. + * PHY_CSR6: BBP pre-TX OFDM. + */ +#define PHY_CSR6 0x04cc +#define PHY_CSR6_OFDM FIELD16(0x0003) +#define PHY_CSR6_OFDM_FLIP FIELD16(0x0004) + +/* + * PHY_CSR7: BBP access register 0. + * BBP_DATA: BBP data. + * BBP_REG_ID: BBP register ID. + * BBP_READ_CONTROL: 0: write, 1: read. + */ +#define PHY_CSR7 0x04ce +#define PHY_CSR7_BBP_DATA FIELD16(0x00ff) +#define PHY_CSR7_BBP_REG_ID FIELD16(0x7f00) +#define PHY_CSR7_BBP_READ_CONTROL FIELD16(0x8000) + +/* + * PHY_CSR8: BBP access register 1. + * BBP_BUSY: ASIC is busy execute BBP programming. + */ +#define PHY_CSR8 0x04d0 +#define PHY_CSR8_BBP_BUSY FIELD16(0x0001) + +/* + * PHY_CSR9: RF access register. + * RF_VALUE: Register value + id to program into rf/if. + */ +#define PHY_CSR9 0x04d2 +#define PHY_CSR9_RF_VALUE FIELD16(0xffff) + +/* + * PHY_CSR10: RF access register. + * RF_VALUE: Register value + id to program into rf/if. + * RF_NUMBER_OF_BITS: Number of bits used in value (i:20, rfmd:22). + * RF_IF_SELECT: Chip to program: 0: rf, 1: if. + * RF_PLL_LD: Rf pll_ld status. + * RF_BUSY: 1: asic is busy execute rf programming. + */ +#define PHY_CSR10 0x04d4 +#define PHY_CSR10_RF_VALUE FIELD16(0x00ff) +#define PHY_CSR10_RF_NUMBER_OF_BITS FIELD16(0x1f00) +#define PHY_CSR10_RF_IF_SELECT FIELD16(0x2000) +#define PHY_CSR10_RF_PLL_LD FIELD16(0x4000) +#define PHY_CSR10_RF_BUSY FIELD16(0x8000) + +/* + * STA_CSR0: FCS error count. + * FCS_ERROR: FCS error count, cleared when read. + */ +#define STA_CSR0 0x04e0 +#define STA_CSR0_FCS_ERROR FIELD16(0xffff) + +/* + * Statistic Register. + * STA_CSR1: PLCP error. + * STA_CSR2: LONG error. + * STA_CSR3: CCA false alarm. + * STA_CSR4: RX FIFO overflow. + * STA_CSR5: Beacon sent counter. + */ +#define STA_CSR1 0x04e2 +#define STA_CSR2 0x04e4 +#define STA_CSR3 0x04e6 +#define STA_CSR4 0x04e8 +#define STA_CSR5 0x04ea +#define STA_CSR6 0x04ec +#define STA_CSR7 0x04ee +#define STA_CSR8 0x04f0 +#define STA_CSR9 0x04f2 +#define STA_CSR10 0x04f4 + +/* + * RF registers. + */ +#define RF1_TUNER FIELD32(0x00020000) +#define RF3_TUNER FIELD32(0x00000100) +#define RF3_TXPOWER FIELD32(0x00003e00) + +/* + * EEPROM contents. + */ + +/* + * HW MAC address. + */ +#define EEPROM_MAC_ADDR_0 0x0002 +#define EEPROM_MAC_ADDR_BYTE0 FIELD16(0x00ff) +#define EEPROM_MAC_ADDR_BYTE1 FIELD16(0xff00) +#define EEPROM_MAC_ADDR1 0x0003 +#define EEPROM_MAC_ADDR_BYTE2 FIELD16(0x00ff) +#define EEPROM_MAC_ADDR_BYTE3 FIELD16(0xff00) +#define EEPROM_MAC_ADDR_2 0x0004 +#define EEPROM_MAC_ADDR_BYTE4 FIELD16(0x00ff) +#define EEPROM_MAC_ADDR_BYTE5 FIELD16(0xff00) + +/* + * EEPROM antenna. + * ANTENNA_NUM: Number of antenna's. + * TX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. + * RX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. + * LED_MODE: 0: default, 1: TX/RX activity, 2: Single (ignore link), 3: rsvd. + * DYN_TXAGC: Dynamic TX AGC control. + * HARDWARE_RADIO: 1: Hardware controlled radio. Read GPIO0. + * RF_TYPE: Rf_type of this adapter. + */ +#define EEPROM_ANTENNA 0x000b +#define EEPROM_ANTENNA_NUM FIELD16(0x0003) +#define EEPROM_ANTENNA_TX_DEFAULT FIELD16(0x000c) +#define EEPROM_ANTENNA_RX_DEFAULT FIELD16(0x0030) +#define EEPROM_ANTENNA_LED_MODE FIELD16(0x01c0) +#define EEPROM_ANTENNA_DYN_TXAGC FIELD16(0x0200) +#define EEPROM_ANTENNA_HARDWARE_RADIO FIELD16(0x0400) +#define EEPROM_ANTENNA_RF_TYPE FIELD16(0xf800) + +/* + * EEPROM NIC config. + * CARDBUS_ACCEL: 0: enable, 1: disable. + * DYN_BBP_TUNE: 0: enable, 1: disable. + * CCK_TX_POWER: CCK TX power compensation. + */ +#define EEPROM_NIC 0x000c +#define EEPROM_NIC_CARDBUS_ACCEL FIELD16(0x0001) +#define EEPROM_NIC_DYN_BBP_TUNE FIELD16(0x0002) +#define EEPROM_NIC_CCK_TX_POWER FIELD16(0x000c) + +/* + * EEPROM geography. + * GEO: Default geography setting for device. + */ +#define EEPROM_GEOGRAPHY 0x000d +#define EEPROM_GEOGRAPHY_GEO FIELD16(0x0f00) + +/* + * EEPROM BBP. + */ +#define EEPROM_BBP_START 0x000e +#define EEPROM_BBP_SIZE 16 +#define EEPROM_BBP_VALUE FIELD16(0x00ff) +#define EEPROM_BBP_REG_ID FIELD16(0xff00) + +/* + * EEPROM TXPOWER + */ +#define EEPROM_TXPOWER_START 0x001e +#define EEPROM_TXPOWER_SIZE 7 +#define EEPROM_TXPOWER_1 FIELD16(0x00ff) +#define EEPROM_TXPOWER_2 FIELD16(0xff00) + +/* + * EEPROM Tuning threshold + */ +#define EEPROM_BBPTUNE 0x0030 +#define EEPROM_BBPTUNE_THRESHOLD FIELD16(0x00ff) + +/* + * EEPROM BBP R24 Tuning. + */ +#define EEPROM_BBPTUNE_R24 0x0031 +#define EEPROM_BBPTUNE_R24_LOW FIELD16(0x00ff) +#define EEPROM_BBPTUNE_R24_HIGH FIELD16(0xff00) + +/* + * EEPROM BBP R25 Tuning. + */ +#define EEPROM_BBPTUNE_R25 0x0032 +#define EEPROM_BBPTUNE_R25_LOW FIELD16(0x00ff) +#define EEPROM_BBPTUNE_R25_HIGH FIELD16(0xff00) + +/* + * EEPROM BBP R24 Tuning. + */ +#define EEPROM_BBPTUNE_R61 0x0033 +#define EEPROM_BBPTUNE_R61_LOW FIELD16(0x00ff) +#define EEPROM_BBPTUNE_R61_HIGH FIELD16(0xff00) + +/* + * EEPROM BBP VGC Tuning. + */ +#define EEPROM_BBPTUNE_VGC 0x0034 +#define EEPROM_BBPTUNE_VGCUPPER FIELD16(0x00ff) + +/* + * EEPROM BBP R17 Tuning. + */ +#define EEPROM_BBPTUNE_R17 0x0035 +#define EEPROM_BBPTUNE_R17_LOW FIELD16(0x00ff) +#define EEPROM_BBPTUNE_R17_HIGH FIELD16(0xff00) + +/* + * RSSI <-> dBm offset calibration + */ +#define EEPROM_CALIBRATE_OFFSET 0x0036 +#define EEPROM_CALIBRATE_OFFSET_RSSI FIELD16(0x00ff) + +/* + * DMA descriptor defines. + */ +#define TXD_DESC_SIZE ( 5 * sizeof(struct data_desc) ) +#define RXD_DESC_SIZE ( 4 * sizeof(struct data_desc) ) + +/* + * TX descriptor format for TX, PRIO, ATIM and Beacon Ring. + */ + +/* + * Word0 + */ +#define TXD_W0_PACKET_ID FIELD32(0x0000000f) +#define TXD_W0_RETRY_LIMIT FIELD32(0x000000f0) +#define TXD_W0_MORE_FRAG FIELD32(0x00000100) +#define TXD_W0_ACK FIELD32(0x00000200) +#define TXD_W0_TIMESTAMP FIELD32(0x00000400) +#define TXD_W0_OFDM FIELD32(0x00000800) +#define TXD_W0_NEW_SEQ FIELD32(0x00001000) +#define TXD_W0_IFS FIELD32(0x00006000) +#define TXD_W0_DATABYTE_COUNT FIELD32(0x0fff0000) +#define TXD_W0_CIPHER FIELD32(0x20000000) +#define TXD_W0_KEY_ID FIELD32(0xc0000000) + +/* + * Word1 + */ +#define TXD_W1_IV_OFFSET FIELD32(0x0000003f) +#define TXD_W1_AIFS FIELD32(0x000000c0) +#define TXD_W1_CWMIN FIELD32(0x00000f00) +#define TXD_W1_CWMAX FIELD32(0x0000f000) + +/* + * Word2: PLCP information + */ +#define TXD_W2_PLCP_SIGNAL FIELD32(0x000000ff) +#define TXD_W2_PLCP_SERVICE FIELD32(0x0000ff00) +#define TXD_W2_PLCP_LENGTH_LOW FIELD32(0x00ff0000) +#define TXD_W2_PLCP_LENGTH_HIGH FIELD32(0xff000000) + +/* + * Word3 + */ +#define TXD_W3_IV FIELD32(0xffffffff) + +/* + * Word4 + */ +#define TXD_W4_EIV FIELD32(0xffffffff) + +/* + * RX descriptor format for RX Ring. + */ + +/* + * Word0 + */ +#define RXD_W0_UNICAST_TO_ME FIELD32(0x00000002) +#define RXD_W0_MULTICAST FIELD32(0x00000004) +#define RXD_W0_BROADCAST FIELD32(0x00000008) +#define RXD_W0_MY_BSS FIELD32(0x00000010) +#define RXD_W0_CRC FIELD32(0x00000020) +#define RXD_W0_OFDM FIELD32(0x00000040) +#define RXD_W0_PHYSICAL_ERROR FIELD32(0x00000080) +#define RXD_W0_CIPHER FIELD32(0x00000100) +#define RXD_W0_CI_ERROR FIELD32(0x00000200) +#define RXD_W0_DATABYTE_COUNT FIELD32(0x0fff0000) + +/* + * Word1 + */ +#define RXD_W1_RSSI FIELD32(0x000000ff) +#define RXD_W1_SIGNAL FIELD32(0x0000ff00) + +/* + * Word2 + */ +#define RXD_W2_IV FIELD32(0xffffffff) + +/* + * Word3 + */ +#define RXD_W3_EIV FIELD32(0xffffffff) + +/* + * TX ring index number for rt2x00_dev structure. + */ +enum ring_index { + RING_PRIO = 0, + RING_TX = 1, + RING_ATIM = 2, + RING_BEACON = 3, + RING_RX = 4, + RING_NUM = 5, + RING_NUM_TX = 2, +}; + +/* + * Macro's for converting txpower from EEPROM to dscape value + * and from dscape value to register value. + */ +#define MIN_TXPOWER 0 +#define MAX_TXPOWER 31 +#define DEFAULT_TXPOWER 24 + +#define TXPOWER_FROM_DEV(__txpower) \ + ({ \ + ((__txpower) > MAX_TXPOWER) ? DEFAULT_TXPOWER : (__txpower); \ + }) + +#define TXPOWER_TO_DEV(__txpower) \ + ({ \ + ((__txpower) <= MIN_TXPOWER) ? MIN_TXPOWER : \ + (((__txpower) >= MAX_TXPOWER) ? MAX_TXPOWER : \ + (__txpower)); \ + }) + +/* + * LED control functions. + */ +static void rt2500usb_enable_led(struct rt2x00_dev *rt2x00dev); +static void rt2500usb_disable_led(struct rt2x00_dev *rt2x00dev); + +/* + * Interrupt functions. + */ +static void rt2500usb_interrupt_rxdone(struct urb *urb); + +#endif /* RT2500USB_H */ diff --git a/drivers/net/wireless/mac80211/rt2x00/rt2x00.h b/drivers/net/wireless/mac80211/rt2x00/rt2x00.h new file mode 100644 index 0000000..74e9a65 --- /dev/null +++ b/drivers/net/wireless/mac80211/rt2x00/rt2x00.h @@ -0,0 +1,1057 @@ +/* + Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + + + 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. + */ + +/* + Module: rt2x00 + Abstract: rt2x00 global information. + Supported chipsets: RT2460, RT2560, RT2570, + rt2561, rt2561s, rt2661, rt2571W & rt2671. + */ + +#ifndef RT2X00_H +#define RT2X00_H + +#include +#include +#include + +#include + +#include "rt2x00lib.h" +#include "rt2x00debug.h" + +/* + * Module information. + */ +#ifndef DRV_NAME +#define DRV_NAME "rt2x00" +#endif /* DRV_NAME */ +#define DRV_VERSION "CVS" +#define DRV_RELDATE "N/A" +#define DRV_PROJECT "http://rt2x00.serialmonkey.com" + +/* + * Debug definitions. + * Debug output has to be enabled during compile time, + * and should be switched on using the module parameter. + */ +#ifdef CONFIG_RT2X00_DEBUG +/* + * Module parameter. + */ +static int rt2x00_debug_level = 0; + +#define DEBUG_PRINTK(__message...) \ + do { if (rt2x00_debug_level) printk(__message); } while (0) +#else /* CONFIG_RT2X00_DEBUG */ +#define DEBUG_PRINTK(__message...) \ + do { } while (0) + +#endif /* CONFIG_RT2X00_DEBUG */ + +/* + * Various debug levels. + * The debug levels PANIC and ERROR both indicate serious problems, + * for this reason they should never be ignored. + */ +#define PANIC(__message, __args...) \ + printk(KERN_PANIC DRV_NAME "->%s: Panic - " __message, \ + __FUNCTION__, ##__args); +#define ERROR(__message, __args...) \ + printk(KERN_ERR DRV_NAME "->%s: Error - " __message, \ + __FUNCTION__, ##__args); +#define WARNING(__message, __args...) \ + DEBUG_PRINTK(KERN_WARNING DRV_NAME "->%s: Warning - " __message, \ + __FUNCTION__, ##__args); +#define NOTICE(__message, __args...) \ + DEBUG_PRINTK(KERN_NOTICE DRV_NAME "->%s: Notice - " __message, \ + __FUNCTION__, ##__args); +#define INFO(__message, __args...) \ + DEBUG_PRINTK(KERN_INFO DRV_NAME "->%s: Info - " __message, \ + __FUNCTION__, ##__args); +#define DEBUG(__message, __args...) \ + DEBUG_PRINTK(KERN_DEBUG DRV_NAME "->%s: Debug - " __message, \ + __FUNCTION__, ##__args); + +/* + * Ring sizes. + * Ralink PCI devices demand the Frame size to be a multiple of 128 bytes. + * DATA_FRAME_SIZE is used for TX, RX, ATIM and PRIO rings. + * MGMT_FRAME_SIZE is used for the BEACON ring. + */ +#define DATA_FRAME_SIZE 2432 +#define MGMT_FRAME_SIZE 256 + +/* + * Number of entries in a packet ring. + */ +#define RX_ENTRIES 12 +#define TX_ENTRIES 12 +#define ATIM_ENTRIES 1 +#define BEACON_ENTRIES 1 + +/* + * Flag handlers + */ +#define SET_FLAG(__dev, __flag) ( (__dev)->flags |= (__flag) ) +#define GET_FLAG(__dev, __flag) ( !!((__dev)->flags & (__flag)) ) +#define CLEAR_FLAG(__dev, __flag) ( (__dev)->flags &= ~(__flag) ) +#define CLEAR_FLAGS(__dev) ( (__dev)->flags = 0 ) + +/* + * Standard timing and size defines. + */ +#define ACK_SIZE 14 +#define IEEE80211_HEADER 24 +#define PLCP 48 +#define BEACON 100 +#define PREAMBLE 144 +#define SHORT_PREAMBLE 72 +#define SLOT_TIME 20 +#define SHORT_SLOT_TIME 9 +#define SIFS 10 +#define PIFS ( SIFS + SLOT_TIME ) +#define SHORT_PIFS ( SIFS + SHORT_SLOT_TIME ) +#define DIFS ( PIFS + SLOT_TIME ) +#define SHORT_DIFS ( SHORT_PIFS + SHORT_SLOT_TIME ) +#define EIFS ( SIFS + (8 * (IEEE80211_HEADER + ACK_SIZE)) ) + +/* + * IEEE802.11 header defines + */ +#define is_rts_frame(__fc) \ + ( !!((((__fc) & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL) && \ + (((__fc) & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_RTS)) ) +#define is_cts_frame(__fc) \ + ( !!((((__fc) & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL) && \ + (((__fc) & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_CTS)) ) +#define is_probe_resp(__fc) \ + ( !!((((__fc) & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) && \ + (((__fc) & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PROBE_RESP)) ) + +/* + * Link tuning at 1 second intervals + */ +#define LINK_TUNE_INTERVAL ( 1 * HZ ) + +/* + * TX result flags. + */ +enum TX_STATUS { + TX_SUCCESS = 0, + TX_SUCCESS_RETRY = 1, + TX_FAIL_RETRY = 2, + TX_FAIL_INVALID = 3, + TX_FAIL_OTHER = 4, +}; + +/* + * Led mode values. + */ +enum led_mode { + LED_MODE_DEFAULT = 0, + LED_MODE_TXRX_ACTIVITY = 1, + LED_MODE_SIGNAL_STRENGTH = 2, + LED_MODE_ASUS = 3, + LED_MODE_ALPHA = 4, +}; + +/* + * Device states + */ +enum dev_state { + STATE_DEEP_SLEEP = 0, + STATE_SLEEP = 1, + STATE_STANDBY = 2, + STATE_AWAKE = 3, +}; + +/* + * IFS backoff values + */ +enum ifs { + IFS_BACKOFF = 0, + IFS_SIFS = 1, + IFS_NEW_BACKOFF = 2, + IFS_NONE = 3, +}; + +/* + * Cipher types for hardware encryption + */ +enum cipher { + CIPHER_NONE = 0, + CIPHER_WEP64 = 1, + CIPHER_WEP128 = 2, + CIPHER_TKIP = 3, + CIPHER_AES = 4, +/* + * The following fields were added by rt61pci and rt73usb. + */ + CIPHER_CKIP64 = 5, + CIPHER_CKIP128 = 6, + CIPHER_TKIP_NO_MIC = 7, +}; + +/* + * Register handlers. + * We store the position of a register field inside a field structure, + * This will simplify the process of setting and reading a certain field + * inside the register while making sure the process remains byte order safe. + */ +struct rt2x00_field16 { + u16 bit_offset; + u16 bit_mask; +}; + +struct rt2x00_field32 { + u32 bit_offset; + u32 bit_mask; +}; + +/* + * Power of two check from Linus Torvalds, + * this will check if the mask that has been + * given contains and contiguous set of bits. + */ +#define is_power_of_two(x) ( !((x) & ((x)-1)) ) +#define low_bit_mask(x) ( ((x)-1) & ~(x) ) +#define is_valid_mask(x) is_power_of_two(1 + (x) + low_bit_mask(x)) + +#define FIELD16(__mask) \ +({ \ + BUILD_BUG_ON(!(__mask) || \ + !is_valid_mask(__mask) || \ + (__mask) != (u16)(__mask));\ + (struct rt2x00_field16) { \ + __ffs(__mask), (__mask) \ + }; \ +}) + +#define FIELD32(__mask) \ +({ \ + BUILD_BUG_ON(!(__mask) || \ + !is_valid_mask(__mask) || \ + (__mask) != (u32)(__mask));\ + (struct rt2x00_field32) { \ + __ffs(__mask), (__mask) \ + }; \ +}) + +static inline void rt2x00_set_field32(u32 *reg, + const struct rt2x00_field32 field, const u32 value) +{ + *reg &= ~(field.bit_mask); + *reg |= (value << field.bit_offset) & field.bit_mask; +} + +static inline u32 rt2x00_get_field32(const u32 reg, + const struct rt2x00_field32 field) +{ + return (reg & field.bit_mask) >> field.bit_offset; +} + +static inline void rt2x00_set_field16(u16 *reg, + const struct rt2x00_field16 field, const u16 value) +{ + *reg &= ~(field.bit_mask); + *reg |= (value << field.bit_offset) & field.bit_mask; +} + +static inline u16 rt2x00_get_field16(const u16 reg, + const struct rt2x00_field16 field) +{ + return (reg & field.bit_mask) >> field.bit_offset; +} + +/* + * Chipset identification + * The chipset on the device is composed of a RT and RF chip. + * The chipset combination is important for determining device capabilities. + */ +struct rt2x00_chip { + u16 rt; +#define RT2460 0x0101 +#define RT2560 0x0201 +#define RT2570 0x1201 +#define RT2561 0x0301 +#define RT2561s 0x0302 +#define RT2661 0x0401 +#define RT2571 0x1300 + + u16 rf; + u32 rev; + u8 fw_h; + u8 fw_l; +}; + +/* + * Set chipset data. + */ +static inline void rt2x00_set_chip(struct rt2x00_chip *chipset, + const u16 rt, const u16 rf, const u32 rev) +{ + INFO("Chipset detected - rt: %04x, rf: %04x, rev: %08x.\n", + rt, rf, rev); + + chipset->rt = rt; + chipset->rf = rf; + chipset->rev = rev; +} + +static inline void rt2x00_set_chip_fw(struct rt2x00_chip *chipset, + const u8 fw_h, const u8 fw_l) +{ + INFO("Firmware detected - version: %d.%d.\n", fw_h, fw_l); + + chipset->fw_h = fw_h; + chipset->fw_l = fw_l; +} + +static inline char rt2x00_rt(const struct rt2x00_chip *chipset, const u16 chip) +{ + return (chipset->rt == chip); +} + +static inline char rt2x00_rf(const struct rt2x00_chip *chipset, const u16 chip) +{ + return (chipset->rf == chip); +} + +static inline u16 rt2x00_rev(const struct rt2x00_chip *chipset) +{ + return chipset->rev; +} + +static inline char* rt2x00_fw(const struct rt2x00_chip *chipset) +{ + return chipset->fw_h + "." + chipset->fw_l; +} + +/* + * data_desc + * Each data entry also contains a descriptor which is used by the + * device to determine what should be done with the packet and + * what the current status is. + * This structure is greatly simplified, but the descriptors + * are basically a list of little endian 32 bit values. + * Make the array by default 1 word big, this will allow us + * to use sizeof() correctly. + */ +struct data_desc { + __le32 word[1]; +}; + +/* + * data_entry_desc + * Summary of information that should be written into the + * descriptor for sending a TX frame. + */ +struct data_entry_desc { + /* + * PLCP values. + */ + u16 length_high; + u16 length_low; + u16 signal; + u16 service; + + int queue; + int ifs; +}; + +/* + * data_entry + * The data ring is a list of data entries. + * Each entry holds a reference to the descriptor + * and the data buffer. For TX rings the reference to the + * sk_buff of the packet being transmitted is also stored here. + */ +struct data_entry { + /* + * Status flags + */ + unsigned int flags; +#define ENTRY_OWNER_NIC 0x00000001 +#define ENTRY_TXDONE 0x00000002 + + /* + * extra register field (Used for txdesc information) + */ + unsigned int reg; +#define ENTRY_TXD_RTS_FRAME 0x00000001 +#define ENTRY_TXD_OFDM_RATE 0x00000002 +#define ENTRY_TXD_MORE_FRAG 0x00000004 +#define ENTRY_TXD_REQ_TIMESTAMP 0x00000008 +#define ENTRY_TXD_REQ_ACK 0x00000010 +#define ENTRY_TXD_NEW_SEQ 0x00000020 + + /* + * Ring we belong to. + */ + struct data_ring *ring; + + /* + * sk_buff for the packet which is being transmitted + * in this entry (Only used with TX related rings). + */ + struct sk_buff *skb; + + /* + * Store a ieee80211_tx_status structure in each + * ring entry, this will optimize the txdone + * handler. + */ + struct ieee80211_tx_status tx_status; + + /* + * private pointer specific to driver. + */ + void *priv; + + /* + * Data address for this entry. + */ + void *data_addr; + dma_addr_t data_dma; +}; + +/* + * data_ring + * Data rings are used by the device to send and receive packets. + * The data_addr is the base address of the data memory. + * To determine at which point in the ring we are, + * have to use the rt2x00_ring_index_*() functions. + */ +struct data_ring { + /* + * Pointer to main rt2x00dev structure where this + * ring belongs to. + */ + struct rt2x00_dev *rt2x00dev; + + /* + * Base address for the device specific data entries. + */ + struct data_entry *entry; + + /* + * TX queue statistic info. + */ + struct ieee80211_tx_queue_stats_data stats; + + /* + * TX Queue parameters. + */ + struct ieee80211_tx_queue_params tx_params; + + /* + * Base address for data ring. + */ + dma_addr_t data_dma; + void *data_addr; + + /* + * Index variables. + */ + u8 index; + u8 index_done; + + /* + * Ring type. + */ + u16 type; + + /* + * Size of packet and descriptor in bytes. + */ + u16 data_size; + u16 desc_size; +}; + +/* + * Handlers to determine the address of the current device specific + * data entry, where either index or index_done points to. + */ +static inline struct data_entry* rt2x00_get_data_entry( + struct data_ring *ring) +{ + return &ring->entry[ring->index]; +} + +static inline struct data_entry* rt2x00_get_data_entry_done( + struct data_ring *ring) +{ + return &ring->entry[ring->index_done]; +} + +/* + * Total ring memory + */ +static inline int rt2x00_get_ring_size(struct data_ring *ring) +{ + return ring->stats.limit * (ring->desc_size + ring->data_size); +} + +/* + * Ring index manipulation functions. + */ +static inline void rt2x00_ring_index_inc(struct data_ring *ring) +{ + ring->index++; + if (ring->index >= ring->stats.limit) + ring->index = 0; + ring->stats.len++; +} + +static inline void rt2x00_ring_index_done_inc(struct data_ring *ring) +{ + ring->index_done++; + if (ring->index_done >= ring->stats.limit) + ring->index_done = 0; + ring->stats.len--; + ring->stats.count++; +} + +static inline void rt2x00_ring_index_clear(struct data_ring *ring) +{ + ring->index = 0; + ring->index_done = 0; + ring->stats.len = 0; + ring->stats.count = 0; +} + +static inline int rt2x00_ring_empty(struct data_ring *ring) +{ + return ring->stats.len == 0; +} + +static inline int rt2x00_ring_full(struct data_ring *ring) +{ + return ring->stats.len == ring->stats.limit; +} + +static inline int rt2x00_ring_free(struct data_ring *ring) +{ + if (ring->index_done >= ring->index) + return ring->index_done - ring->index; + return ring->stats.len - (ring->index - ring->index_done); +} + +/* + * TX/RX Descriptor access functions. + */ +static inline void rt2x00_desc_read(struct data_desc *desc, + const u8 word, u32 *value) +{ + *value = le32_to_cpu(desc->word[word]); +} + +static inline void rt2x00_desc_write(struct data_desc *desc, + const u8 word, const u32 value) +{ + desc->word[word] = cpu_to_le32(value); +} + +/* + * To optimize the quality of the link we need to store + * the quality of received frames and periodically + * optimize the link. + */ +struct link { + /* + * RSSI statistics. + */ + u32 count_rssi; + u32 total_rssi; + + /* + * Noise statistics. + */ + u32 curr_noise; + + /* + * Work structure for scheduling periodic link tuning. + */ + struct delayed_work work; +}; + +static inline void rt2x00_start_link_tune(struct link *link) +{ + link->count_rssi = 0; + link->total_rssi = 0; + link->curr_noise = 0; +} + +static inline void rt2x00_update_link_rssi(struct link *link, u32 rssi) +{ + link->count_rssi++; + link->total_rssi += rssi; +} + +static inline void rt2x00_update_link_noise(struct link *link, u32 noise) +{ + link->curr_noise = noise; +} + +static inline u32 rt2x00_get_link_rssi(struct link *link) +{ + u32 average = 0; + + if (link->count_rssi && link->total_rssi) + average = link->total_rssi / link->count_rssi; + + link->count_rssi = 0; + link->total_rssi = 0; + + return average; +} + +static inline u32 rt2x00_get_link_noise(struct link *link) +{ + return link->curr_noise; +} + +/* + * Interface structure + * Configuration details about the current interface. + */ +struct interface { + /* + * Interface identification. The value is assigned + * to us by the 80211 stack, and is used to request + * new beacons. + */ + int id; + + /* + * Current working type (IEEE80211_IF_TYPE_*). + * This excludes the type IEEE80211_IF_TYPE_MNTR + * since that is counted seperately in the monitor_count + * field. + */ + int type; + + /* + * BBSID of the AP to associate with. + */ + u8 bssid[ETH_ALEN]; + + /* + * Store the promisc mode for the current interface. + * monitor mode always forces promisc mode to be enabled, + * so we need to store the promisc mode seperately. + */ + short promisc; + + /* + * Monitor mode count, the number of interfaces + * in monitor mode that that have been added. + */ + short monitor_count; +}; + +/* + * rt2x00lib callback functions. + */ +struct rt2x00lib_ops { + /* + * Interrupt handlers. + */ + irq_handler_t irq_handler; + work_func_t link_tuner; + + /* + * Device init handlers. + */ + int (*init_hw)(struct rt2x00_dev *rt2x00dev); + int (*load_firmware)(struct rt2x00_dev *rt2x00dev, + const struct firmware *fw); + + /* + * Device initialization/deinitialization handlers. + */ + int (*initialize)(struct rt2x00_dev *rt2x00dev); + void (*uninitialize)(struct rt2x00_dev *rt2x00dev); + + /* + * Radio control handlers. + */ + int (*enable_radio)(struct rt2x00_dev *rt2x00dev); + void (*disable_radio)(struct rt2x00_dev *rt2x00dev); + int (*set_state)(struct rt2x00_dev *rt2x00dev, enum dev_state state); + void (*toggle_rx)(struct rt2x00_dev *rt2x00dev, int enable); + + /* + * TX control handlers + */ + void (*write_tx_desc)(struct rt2x00_dev *rt2x00dev, + struct data_entry *entry, struct data_desc *txd, + struct data_entry_desc *desc, + struct ieee80211_hdr *ieee80211hdr, unsigned int length, + struct ieee80211_tx_control *control); + int (*write_tx_data)(struct rt2x00_dev *rt2x00dev, + struct data_ring *ring, struct sk_buff *skb, + struct ieee80211_tx_control *control); + void (*kick_tx_queue)(struct rt2x00_dev *rt2x00dev, int queue); + void (*kick_beacon_gen)(struct rt2x00_dev *rt2x00dev); + + /* + * Configuration handlers. + */ + void (*config_type)(struct rt2x00_dev *rt2x00dev, int type); + void (*config_phymode)(struct rt2x00_dev *rt2x00dev, const int phy); + void (*config_channel)(struct rt2x00_dev *rt2x00dev, const int value, + const int channel, const int freq, const int txpower); + void (*config_mac_addr)(struct rt2x00_dev *rt2x00dev, u8 *mac); + void (*config_bssid)(struct rt2x00_dev *rt2x00dev, u8 *bssid); + void (*config_promisc)(struct rt2x00_dev *rt2x00dev, int promisc); + void (*config_txpower)(struct rt2x00_dev *rt2x00dev, int txpower); + void (*config_antenna)(struct rt2x00_dev *rt2x00dev, + int antenna_tx, int antenna_rx); + void (*config_duration)(struct rt2x00_dev *rt2x00dev, + int short_slot_time); +}; + +/* + * rt2x00 driver callback operation structure. + */ +struct rt2x00_ops { + const struct rt2x00lib_ops *lib; + const struct ieee80211_ops *hw; + const struct rt2x00debug *debugfs; +}; + +/* + * rt2x00 device structure. + */ +struct rt2x00_dev { + /* + * Device structure. + * The structure stored in here depends on the + * system bus (PCI or USB). + * When accessing this variable, the rt2x00dev_{pci,usb} + * macro's should be used for correct typecasting. + */ + void *dev; + struct device *device; +#define rt2x00dev_pci(__dev) ( (struct pci_dev*)(__dev)->dev ) +#define rt2x00dev_usb(__dev) ( (struct usb_interface*)(__dev)->dev ) + + /* + * Callback functions. + */ + const struct rt2x00_ops *ops; + + /* + * IEEE80211 control structure. + */ + struct ieee80211_hw *hw; + struct ieee80211_hw_mode *hwmodes; + unsigned int curr_hwmode; +#define HWMODE_B 0 +#define HWMODE_G 1 +#define HWMODE_A 2 + + /* + * Device flags. + * In these flags the current status and some + * of the device capabilities are stored. + */ + unsigned int flags; +#define DEVICE_ENABLED_RADIO 0x00000001 + +#define DEVICE_INITIALIZED 0x00000004 +#define DEVICE_INITIALIZED_HW 0x00000008 + +#define FIRMWARE_LOADED 0x00000020 +#define FIRMWARE_FAILED 0x00000040 +#define INTERFACE_INITIALIZED 0x00000080 +#define INTERFACE_INITIALIZED_MONITOR 0x00000100 +#define INTERFACE_ENABLED 0x00000200 +#define INTERFACE_ENABLED_MONITOR 0x00000400 +#define INTERFACE_ENABLED_PROMISC 0x00000800 +#define DEVICE_SUPPORT_ATIM 0x00001000 +#define DEVICE_SUPPORT_HW_BUTTON 0x00002000 +#define CONFIG_FRAME_TYPE 0x00004000 +#define CONFIG_RF_SEQUENCE 0x00008000 +#define CONFIG_EXTERNAL_LNA 0x00010000 +#define CONFIG_EXTERNAL_LNA_A 0x00020000 +#define CONFIG_EXTERNAL_LNA_BG 0x00040000 +#define CONFIG_DOUBLE_ANTENNA 0x00080000 +#define CONFIG_DISABLE_LINK_TUNING 0x00100000 + + /* + * Chipset identification. + */ + struct rt2x00_chip chip; + + /* + * hw capability specifications. + */ + struct hw_mode_spec spec; + + /* + * Base address of device registers (PCI devices only). + */ + void __iomem *csr_addr; + + /* + * If enabled, the debugfs interface structures + * required for deregistration of debugfs. + */ + const struct rt2x00debug_intf *debugfs_intf; + + /* + * Queue for deferred work. + */ + struct workqueue_struct *workqueue; + + /* + * Interface configuration. + */ + struct interface interface; + + /* + * Link quality + */ + struct link link; + + /* + * EEPROM data. + */ + __le16 *eeprom; + + /* + * Active RF register values. + * These are stored here for easier working + * with the rf registers. + */ + u32 rf1; + u32 rf2; + u32 rf3; + u32 rf4; + + /* + * Current TX power value. + */ + u16 tx_power; + + /* + * LED register (for rt61pci & rt73usb). + */ + u16 led_reg; + + /* + * Led mode (LED_MODE_*) + */ + u8 led_mode; + + /* + * EEPROM bus width (PCI devices only). + */ + u8 eeprom_width; + + /* + * False CCA counter. (for rt2400pci). + */ + u8 false_cca; + + /* + * Frequency offset (for rt61pci & rt73usb). + */ + u8 freq_offset; + + /* + * Low level statistics which will have + * to be kept up to date while device is running. + */ + struct ieee80211_low_level_stats low_level_stats; + + /* + * RX configuration information. + */ + struct ieee80211_rx_status rx_status; + + /* + * Data rings for both RX and TX. + * The first entries must be the normal TX + * rings, followed by a possible ATIM ring + * (when atim is used atim_available must be set) + * after that the beacon and RX ring follow. + */ + struct data_ring *ring; + + /* + * Descriptor size for each ring type. + */ + unsigned int rxd_size; + unsigned int txd_size; +}; + +static inline struct data_ring* rt2x00_get_ring( + struct rt2x00_dev *rt2x00dev, const int ring) +{ + int atim; + + atim = GET_FLAG(rt2x00dev, DEVICE_SUPPORT_ATIM); + + /* + * Check if the rings have been allocated. + */ + if (!rt2x00dev->ring) + return NULL; + + /* + * Check for beacon ring, the beacon ring + * is located behing the normal TX and, when available + * the atim ring. + */ + if (ring == IEEE80211_TX_QUEUE_BEACON) + return &rt2x00dev->ring[rt2x00dev->hw->queues + atim]; + + if (ring == IEEE80211_TX_QUEUE_AFTER_BEACON && atim) + return &rt2x00dev->ring[rt2x00dev->hw->queues]; + + /* + * Make sure the requested ring does not exceed + * the maximum number of rings. + */ + if (ring < rt2x00dev->hw->queues) + return &rt2x00dev->ring[ring]; + + return NULL; +} + +/* + * EEPROM access. + * The EEPROM is being accessed by word index. + */ +static inline void* rt2x00_eeprom_addr(const struct rt2x00_dev *rt2x00dev, + const u8 word) +{ + return (void*)&rt2x00dev->eeprom[word]; +} + +static inline void rt2x00_eeprom_read(const struct rt2x00_dev *rt2x00dev, + const u8 word, u16 *data) +{ + *data = le16_to_cpu(rt2x00dev->eeprom[word]); +} + +static inline void rt2x00_eeprom_write(const struct rt2x00_dev *rt2x00dev, + const u8 word, u16 data) +{ + rt2x00dev->eeprom[word] = cpu_to_le16(data); +} + +/* + * Device specific rate value. + * We will have to create the device specific rate value + * passed to the ieee80211 kernel. We need to make it a consist of + * multiple fields because we want to store more then 1 device specific + * values inside the value. + * 1 - rate, stored as 100 kbit/s. + * 2 - preamble, short_preamble enabled flag. + * 3 - MASK_RATE, which rates are enabled in this mode, this mask + * corresponds with the TX register format for the current device. + * 4 - plcp, 802.11b rates are device specific, + * 802.11g rates are set according to the ieee802.11a-1999 p.14. + * The bit to enable preamble is set in a seperate define. + */ +#define DEV_RATE FIELD32(0x000007ff) +#define DEV_PREAMBLE FIELD32(0x00000800) +#define DEV_RATEMASK FIELD32(0x00fff000) +#define DEV_PLCP FIELD32(0xff000000) + +/* + * Bitmask for MASK_RATE + */ +#define DEV_RATE_1MB 0x00000001 +#define DEV_RATE_2MB 0x00000002 +#define DEV_RATE_5_5MB 0x00000004 +#define DEV_RATE_11MB 0x00000008 +#define DEV_RATE_6MB 0x00000010 +#define DEV_RATE_9MB 0x00000020 +#define DEV_RATE_12MB 0x00000040 +#define DEV_RATE_18MB 0x00000080 +#define DEV_RATE_24MB 0x00000100 +#define DEV_RATE_36MB 0x00000200 +#define DEV_RATE_48MB 0x00000400 +#define DEV_RATE_54MB 0x00000800 + +/* + * Bitmask groups of bitrates + */ +#define DEV_BASIC_RATE \ + ( DEV_RATE_1MB | DEV_RATE_2MB | DEV_RATE_5_5MB | DEV_RATE_11MB | \ + DEV_RATE_6MB | DEV_RATE_12MB | DEV_RATE_24MB ) + +#define DEV_CCK_RATE \ + ( DEV_RATE_1MB | DEV_RATE_2MB | DEV_RATE_5_5MB | DEV_RATE_11MB ) + +#define DEV_OFDM_RATE \ + ( DEV_RATE_6MB | DEV_RATE_9MB | DEV_RATE_12MB | DEV_RATE_18MB | \ + DEV_RATE_24MB | DEV_RATE_36MB | DEV_RATE_48MB | DEV_RATE_54MB ) + +/* + * Macro's to set and get specific fields from the device specific val and val2 + * fields inside the ieee80211_rate entry. + */ +#define DEVICE_SET_RATE_FIELD(__value, __mask) \ + (int)( ((__value) << DEV_##__mask.bit_offset) & DEV_##__mask.bit_mask ) + +#define DEVICE_GET_RATE_FIELD(__value, __mask) \ + (int)( ((__value) & DEV_##__mask.bit_mask) >> DEV_##__mask.bit_offset ) + +/* + * Duration calculations + * The rate variable passed is: 100kbs. + * To convert from bytes to bits we multiply size with 8, + * then the size is multiplied with 10 to make the + * real rate -> rate argument correction. + */ +static inline u16 get_duration(const unsigned int size, const u8 rate) +{ + return ((size * 8 * 10) / rate); +} + +static inline u16 get_duration_res(const unsigned int size, const u8 rate) +{ + return ((size * 8 * 10) % rate); +} + +/* + * Helper define for accessing eeprom data that should be + * validated before usage. When the eeprom is invalid the + * default value will be returned. + */ +#define eeprom_valid(__word, __def, __val) \ + ({ \ + u16 __retval; \ + if ((__word) == 0xffff || (__word) == 0x0000) \ + __retval = (__def); \ + else \ + __retval = rt2x00_get_field16( \ + (__word), (__val)); \ + __retval; \ + }) + +#endif /* RT2X00_H */ diff --git a/drivers/net/wireless/mac80211/rt2x00/rt2x00debug.c b/drivers/net/wireless/mac80211/rt2x00/rt2x00debug.c new file mode 100644 index 0000000..68323ed --- /dev/null +++ b/drivers/net/wireless/mac80211/rt2x00/rt2x00debug.c @@ -0,0 +1,353 @@ +/* + Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + + + 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. + */ + +/* + Module: rt2x00lib + Abstract: rt2x00 debugfs specific routines. + Supported chipsets: RT2460, RT2560, RT2570, + rt2561, rt2561s, rt2661, rt2571W & rt2671. + */ + +#include + +#include + +#include "rt2x00debug.h" + +#define PRINT_REG8_STR ( "0x%.2x\n" ) +#define PRINT_REG16_STR ( "0x%.4x\n" ) +#define PRINT_REG32_STR ( "0x%.8x\n" ) +#define PRINT_REG_LEN_MAX ( 16 ) +#define PRINT_LINE_LEN_MAX ( 32 ) + +struct rt2x00debug_intf { + /* + * Pointer to driver structure where + * this debugfs entry belongs to. + */ + struct rt2x00_dev *rt2x00dev; + + /* + * Reference to the rt2x00debug structure + * which can be used to communicate with + * the registers. + */ + const struct rt2x00debug *debug; + + /* + * Debugfs entries for: + * - driver folder + * - driver file + * - chipset file + * - register offset/value files + * - eeprom offset/value files + * - bbp offset/value files + */ + struct dentry *driver_folder; + struct dentry *driver_entry; + struct dentry *chipset_entry; + struct dentry *csr_off_entry; + struct dentry *csr_val_entry; + struct dentry *eeprom_off_entry; + struct dentry *eeprom_val_entry; + struct dentry *bbp_off_entry; + struct dentry *bbp_val_entry; + + /* + * Driver and chipset files will use a data buffer + * that has been created in advance. This will simplify + * the code since we can use the debugfs functions. + */ + struct debugfs_blob_wrapper driver_blob; + struct debugfs_blob_wrapper chipset_blob; + + /* + * Requested offset for each register type. + */ + unsigned int offset_csr; + unsigned int offset_eeprom; + unsigned int offset_bbp; +}; + +static int rt2x00debug_file_open(struct inode *inode, struct file *file) +{ + struct rt2x00debug_intf *intf = inode->i_private; + + file->private_data = inode->i_private; + + if (!try_module_get(intf->debug->owner)) + return -EBUSY; + + return 0; +} + +static int rt2x00debug_file_release(struct inode *inode, struct file *file) +{ + struct rt2x00debug_intf *intf = file->private_data; + + module_put(intf->debug->owner); + + return 0; +} + +static ssize_t rt2x00debug_file_read(void *device, char __user *buf, + loff_t *offset, unsigned int word, const struct rt2x00debug_reg *reg) +{ + unsigned long value; + unsigned int size; + char *line; + + if (*offset) + return 0; + + line = kzalloc(PRINT_REG_LEN_MAX, GFP_KERNEL); + if (!line) + return -ENOMEM; + + reg->read(device, word, &value); + + if (reg->word_size == sizeof(u8)) + size = sprintf(line, PRINT_REG8_STR, (u8)value); + else if (reg->word_size == sizeof(u16)) + size = sprintf(line, PRINT_REG16_STR, (u16)value); + else + size = sprintf(line, PRINT_REG32_STR, (u32)value); + + if (copy_to_user(buf, line, size)) + goto exit; + + kfree(line); + + *offset += size; + return size; + +exit: + kfree(line); + + return -EFAULT; +} + +static ssize_t rt2x00debug_file_write(void *device, const char __user *buf, + loff_t *offset, unsigned int word, unsigned int length, + const struct rt2x00debug_reg *reg) +{ + unsigned long value; + int size; + char *line; + + line = kzalloc(length, GFP_KERNEL); + if (!line) + return -ENOMEM; + + if (copy_from_user(line, buf, length)) + goto exit; + + size = strlen(line); + value = simple_strtoul(line, NULL, 0); + + reg->write(device, word, &value); + + kfree(line); + + *offset += size; + return size; + +exit: + kfree(line); + + return -EFAULT; +} + +#define RT2X00DEBUGFS_OPS_READ(__name) \ + static ssize_t rt2x00debug_read_##__name(struct file *file, \ + char __user *buf, size_t length, loff_t *offset) \ + { \ + struct rt2x00debug_intf *intf = file->private_data; \ + const struct rt2x00debug *debug = intf->debug; \ + const struct rt2x00debug_reg *reg = &debug->reg_##__name;\ + \ + if (intf->offset_##__name > reg->word_count) \ + return -EINVAL; \ + \ + return rt2x00debug_file_read(intf->rt2x00dev, buf, \ + offset, intf->offset_##__name, reg); \ + } + +RT2X00DEBUGFS_OPS_READ(csr); +RT2X00DEBUGFS_OPS_READ(eeprom); +RT2X00DEBUGFS_OPS_READ(bbp); + +#define RT2X00DEBUGFS_OPS_WRITE(__name) \ + static ssize_t rt2x00debug_write_##__name(struct file *file, \ + const char __user *buf, size_t length, loff_t *offset) \ + { \ + struct rt2x00debug_intf *intf = file->private_data; \ + const struct rt2x00debug *debug = intf->debug; \ + const struct rt2x00debug_reg *reg = &debug->reg_##__name;\ + \ + if (intf->offset_##__name > reg->word_count) \ + return -EINVAL; \ + \ + return rt2x00debug_file_write(intf->rt2x00dev, buf, \ + offset, intf->offset_##__name, length, reg); \ + } + +RT2X00DEBUGFS_OPS_WRITE(csr); +RT2X00DEBUGFS_OPS_WRITE(eeprom); +RT2X00DEBUGFS_OPS_WRITE(bbp); + +#define RT2X00DEBUGFS_OPS(__name) \ + static const struct file_operations rt2x00debug_fop_##__name = {\ + .owner = THIS_MODULE, \ + .read = rt2x00debug_read_##__name, \ + .write = rt2x00debug_write_##__name, \ + .open = rt2x00debug_file_open, \ + .release = rt2x00debug_file_release, \ + }; + +RT2X00DEBUGFS_OPS(csr); +RT2X00DEBUGFS_OPS(eeprom); +RT2X00DEBUGFS_OPS(bbp); + +static struct dentry *rt2x00debug_create_file_driver(const char *name, + struct rt2x00debug_intf *intf, struct debugfs_blob_wrapper *blob) +{ + const struct rt2x00debug *debug = intf->debug; + char *data; + + data = kzalloc(3 * PRINT_LINE_LEN_MAX, GFP_KERNEL); + if (!data) + return NULL; + + blob->data = data; + data += sprintf(data, "driver: %s\n", debug->mod_name); + data += sprintf(data, "version: %s\n", debug->mod_version); + data += sprintf(data, "compiled: %s %s\n", __DATE__, __TIME__); + blob->size = strlen(blob->data); + + return debugfs_create_blob(name, S_IRUGO, intf->driver_folder, blob); +} + +static struct dentry *rt2x00debug_create_file_chipset(const char *name, + struct rt2x00debug_intf *intf, struct debugfs_blob_wrapper *blob) +{ + const struct rt2x00debug *debug = intf->debug; + char *data; + + data = kzalloc(3 * PRINT_LINE_LEN_MAX, GFP_KERNEL); + if (!data) + return NULL; + + blob->data = data; + data += sprintf(data, "csr length: %d\n", debug->reg_csr.word_count); + data += sprintf(data, "eeprom length: %d\n", debug->reg_eeprom.word_count); + data += sprintf(data, "bbp length: %d\n", debug->reg_bbp.word_count); + blob->size = strlen(blob->data); + + return debugfs_create_blob(name, S_IRUGO, intf->driver_folder, blob); +} + +const struct rt2x00debug_intf* rt2x00debug_register( + const struct rt2x00debug *debug, struct wiphy *wiphy, + struct rt2x00_dev *rt2x00dev) +{ + struct rt2x00debug_intf *intf; + + intf = kzalloc(sizeof(struct rt2x00debug_intf), GFP_KERNEL); + if (!intf) + return NULL; + + intf->debug = debug; + intf->rt2x00dev = rt2x00dev; + + intf->driver_folder = debugfs_create_dir(debug->mod_name, + wiphy->debugfsdir); + if (IS_ERR(intf->driver_folder)) + goto exit; + + intf->driver_entry = rt2x00debug_create_file_driver("driver", + intf, &intf->driver_blob); + if (IS_ERR(intf->driver_entry)) + goto exit; + + intf->chipset_entry = rt2x00debug_create_file_chipset("chipset", + intf, &intf->chipset_blob); + if (IS_ERR(intf->chipset_entry)) + goto exit; + + intf->csr_off_entry = debugfs_create_u32("csr_offset", + S_IRUGO | S_IWUSR, intf->driver_folder, &intf->offset_csr); + if (IS_ERR(intf->csr_off_entry)) + goto exit; + + intf->csr_val_entry = debugfs_create_file("csr_value", + S_IRUGO | S_IWUSR, intf->driver_folder, intf, + &rt2x00debug_fop_csr); + if (IS_ERR(intf->csr_val_entry)) + goto exit; + + intf->eeprom_off_entry = debugfs_create_u32("eeprom_offset", + S_IRUGO | S_IWUSR, intf->driver_folder, &intf->offset_eeprom); + if (IS_ERR(intf->eeprom_off_entry)) + goto exit; + + intf->eeprom_val_entry = debugfs_create_file("eeprom_value", + S_IRUGO | S_IWUSR, intf->driver_folder, intf, + &rt2x00debug_fop_eeprom); + if (IS_ERR(intf->eeprom_val_entry)) + goto exit; + + intf->bbp_off_entry = debugfs_create_u32("bbp_offset", + S_IRUGO | S_IWUSR, intf->driver_folder, &intf->offset_bbp); + if (IS_ERR(intf->bbp_off_entry)) + goto exit; + + intf->bbp_val_entry = debugfs_create_file("bbp_value", + S_IRUGO | S_IWUSR, intf->driver_folder, intf, + &rt2x00debug_fop_bbp); + if (IS_ERR(intf->bbp_val_entry)) + goto exit; + + return intf; + +exit: + rt2x00debug_deregister(intf); + + return NULL; +} + +void rt2x00debug_deregister(const struct rt2x00debug_intf *intf) +{ + if (unlikely(!intf)) + return; + + debugfs_remove(intf->bbp_val_entry); + debugfs_remove(intf->bbp_off_entry); + debugfs_remove(intf->eeprom_val_entry); + debugfs_remove(intf->eeprom_off_entry); + debugfs_remove(intf->csr_val_entry); + debugfs_remove(intf->csr_off_entry); + debugfs_remove(intf->chipset_entry); + debugfs_remove(intf->driver_entry); + debugfs_remove(intf->driver_folder); + kfree(intf->chipset_blob.data); + kfree(intf->driver_blob.data); + kfree(intf); +} diff --git a/drivers/net/wireless/mac80211/rt2x00/rt2x00debug.h b/drivers/net/wireless/mac80211/rt2x00/rt2x00debug.h new file mode 100644 index 0000000..5f40555 --- /dev/null +++ b/drivers/net/wireless/mac80211/rt2x00/rt2x00debug.h @@ -0,0 +1,72 @@ +/* + Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + + + 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. + */ + +/* + Module: rt2x00debug + Abstract: Data structures for the rt2x00debug module. + Supported chipsets: RT2460, RT2560, RT2570, + rt2561, rt2561s, rt2661, rt2571W & rt2671. + */ + +#ifndef RT2X00DEBUG_H +#define RT2X00DEBUG_H + +#include + +struct rt2x00_dev; +struct rt2x00debug_intf; + +typedef void (debug_access_t)(struct rt2x00_dev *rt2x00dev, + const unsigned long word, void *data); + +struct rt2x00debug_reg { + debug_access_t *read; + debug_access_t *write; + + unsigned int word_size; + unsigned int word_count; +}; + +struct rt2x00debug { + /* + * Reference to the modules structure. + */ + struct module *owner; + + /* + * Driver module information + */ + char *mod_name; + char *mod_version; + + /* + * Register access information. + */ + struct rt2x00debug_reg reg_csr; + struct rt2x00debug_reg reg_eeprom; + struct rt2x00debug_reg reg_bbp; +}; + +const struct rt2x00debug_intf* rt2x00debug_register( + const struct rt2x00debug *debug, struct wiphy *wiphy, + struct rt2x00_dev *rt2x00dev); +void rt2x00debug_deregister(const struct rt2x00debug_intf *intf); + +#endif /* RT2X00DEBUG_H */ diff --git a/drivers/net/wireless/mac80211/rt2x00/rt2x00dev.c b/drivers/net/wireless/mac80211/rt2x00/rt2x00dev.c new file mode 100644 index 0000000..16c5f8a --- /dev/null +++ b/drivers/net/wireless/mac80211/rt2x00/rt2x00dev.c @@ -0,0 +1,831 @@ +/* + Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + + + 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. + */ + +/* + Module: rt2x00lib + Abstract: rt2x00 generic device routines. + Supported chipsets: RT2460, RT2560, RT2570, + rt2561, rt2561s, rt2661, rt2571W & rt2671. + */ + +/* + * Set enviroment defines for rt2x00.h + */ +#define DRV_NAME "rt2x00lib" + +#include +#include +#include +#include +#include +#include + +#include "rt2x00.h" + +/* + * Radio control handlers. + */ +int rt2x00lib_enable_radio(struct rt2x00_dev *rt2x00dev) +{ + int status; + + /* + * Don't enable the radio twice. + * or if the hardware button has been disabled. + */ + if (GET_FLAG(rt2x00dev, DEVICE_ENABLED_RADIO)) + return 0; + + status = rt2x00dev->ops->lib->enable_radio(rt2x00dev); + if (status) + return status; + + SET_FLAG(rt2x00dev, DEVICE_ENABLED_RADIO); + + rt2x00dev->ops->lib->toggle_rx(rt2x00dev, 1); + + ieee80211_start_queues(rt2x00dev->hw); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2x00lib_enable_radio); + +void rt2x00lib_disable_radio(struct rt2x00_dev *rt2x00dev) +{ + if (!GET_FLAG(rt2x00dev, DEVICE_ENABLED_RADIO)) + return; + + ieee80211_stop_queues(rt2x00dev->hw); + + rt2x00dev->ops->lib->toggle_rx(rt2x00dev, 0); + + CLEAR_FLAG(rt2x00dev, DEVICE_ENABLED_RADIO); + + rt2x00dev->ops->lib->disable_radio(rt2x00dev); +} +EXPORT_SYMBOL_GPL(rt2x00lib_disable_radio); + +/* + * Driver initialization handlers. + */ +#define create_channel_a(__entry, __channel, __tx_power, __value) \ +({ \ + (__entry)->chan = (__channel); \ + (__entry)->freq = ( 5000 + (5 * (__channel))); \ + (__entry)->val = __value; \ + (__entry)->flag = \ + IEEE80211_CHAN_W_IBSS | \ + IEEE80211_CHAN_W_ACTIVE_SCAN | \ + IEEE80211_CHAN_W_SCAN; \ + (__entry)->power_level = __tx_power; \ + (__entry)->antenna_max = 0xff; \ +}) + +#define create_channel_bg(__entry, __channel, __tx_power, __value) \ +({ \ + (__entry)->chan = (__channel); \ + (__entry)->freq = 2407 + (5 * (__channel)); \ + (__entry)->val = __value; \ + (__entry)->flag = \ + IEEE80211_CHAN_W_IBSS | \ + IEEE80211_CHAN_W_ACTIVE_SCAN | \ + IEEE80211_CHAN_W_SCAN; \ + (__entry)->power_level = __tx_power; \ + (__entry)->antenna_max = 0xff; \ +}) + +#define create_rate(__entry, __rate, __mask, __plcp, __flags) \ +({ \ + (__entry)->rate = (__rate); \ + (__entry)->val = \ + DEVICE_SET_RATE_FIELD((__rate), RATE) | \ + DEVICE_SET_RATE_FIELD((__mask), RATEMASK) | \ + DEVICE_SET_RATE_FIELD((__plcp), PLCP); \ + (__entry)->flags = (__flags); \ + (__entry)->val2 = (__entry)->val; \ + if ((__entry)->flags & IEEE80211_RATE_PREAMBLE2) \ + (__entry)->val2 |= DEVICE_SET_RATE_FIELD(1, PREAMBLE); \ + (__entry)->min_rssi_ack = 0; \ + (__entry)->min_rssi_ack_delta = 0; \ +}) + +static int rt2x00lib_init_hw_modes(struct rt2x00_dev *rt2x00dev, + struct hw_mode_spec *spec) +{ + struct ieee80211_hw *hw = rt2x00dev->hw; + struct ieee80211_hw_mode *hwmodes; + struct ieee80211_channel *channels; + struct ieee80211_rate *rates; + unsigned int i; + unsigned char tx_power; + + hwmodes = kzalloc(sizeof(*hwmodes) * spec->num_modes, GFP_KERNEL); + if (!hwmodes) + goto exit; + + channels = kzalloc(sizeof(*channels) * spec->num_channels, GFP_KERNEL); + if (!channels) + goto exit_free_modes; + + rates = kzalloc(sizeof(*rates) * spec->num_rates, GFP_KERNEL); + if (!rates) + goto exit_free_channels; + + /* + * Initialize Rate list. + */ + create_rate(&rates[0], 10, 0x001, 0x00, IEEE80211_RATE_CCK); + create_rate(&rates[1], 20, 0x003, 0x01, IEEE80211_RATE_CCK_2); + create_rate(&rates[2], 55, 0x007, 0x02, IEEE80211_RATE_CCK_2); + create_rate(&rates[3], 110, 0x00f, 0x03, IEEE80211_RATE_CCK_2); + + if (spec->num_rates > 4) { + create_rate(&rates[4], 60, 0x01f, 0x0b, IEEE80211_RATE_OFDM); + create_rate(&rates[5], 90, 0x03f, 0x0f, IEEE80211_RATE_OFDM); + create_rate(&rates[6], 120, 0x07f, 0x0a, IEEE80211_RATE_OFDM); + create_rate(&rates[7], 180, 0x0ff, 0x0e, IEEE80211_RATE_OFDM); + create_rate(&rates[8], 240, 0x1ff, 0x09, IEEE80211_RATE_OFDM); + create_rate(&rates[9], 360, 0x3ff, 0x0d, IEEE80211_RATE_OFDM); + create_rate(&rates[10], 480, 0x7ff, 0x08, IEEE80211_RATE_OFDM); + create_rate(&rates[11], 540, 0xfff, 0x0c, IEEE80211_RATE_OFDM); + } + + /* + * Initialize Channel list. + */ + for (i = 0; i < 14; i++) + create_channel_bg(&channels[i], i + 1, + spec->tx_power_bg[i], spec->chan_val_bg[i]); + + if (spec->num_channels > 14) { + for (i = 14; i < spec->num_channels; i++) { + if (i < 22) + channels[i].chan = 36; + else if (i < 33) + channels[i].chan = 100; + else + channels[i].chan = 149; + channels[i].chan += ((i - 14) * 4); + + if (spec->tx_power_a) + tx_power = spec->tx_power_a[i]; + else + tx_power = spec->tx_power_default; + + create_channel_a(&channels[i], channels[i].chan, + tx_power, spec->chan_val_a[i]); + } + } + + /* + * Intitialize 802.11b + * Rates: CCK. + * Channels: OFDM. + */ + if (spec->num_modes > HWMODE_B) { + hwmodes[HWMODE_B].mode = MODE_IEEE80211B; + hwmodes[HWMODE_B].num_channels = 14; + hwmodes[HWMODE_B].num_rates = 4; + hwmodes[HWMODE_B].channels = channels; + hwmodes[HWMODE_B].rates = rates; + } + + /* + * Intitialize 802.11g + * Rates: CCK, OFDM. + * Channels: OFDM. + */ + if (spec->num_modes > HWMODE_G) { + hwmodes[HWMODE_G].mode = MODE_IEEE80211G; + hwmodes[HWMODE_G].num_channels = 14; + hwmodes[HWMODE_G].num_rates = spec->num_rates; + hwmodes[HWMODE_G].channels = channels; + hwmodes[HWMODE_G].rates = rates; + } + + /* + * Intitialize 802.11a + * Rates: OFDM. + * Channels: OFDM, UNII, HiperLAN2. + */ + if (spec->num_modes > HWMODE_A) { + hwmodes[HWMODE_A].mode = MODE_IEEE80211A; + hwmodes[HWMODE_A].num_channels = spec->num_channels - 14; + hwmodes[HWMODE_A].num_rates = spec->num_rates - 4; + hwmodes[HWMODE_A].channels = &channels[14]; + hwmodes[HWMODE_A].rates = &rates[4]; + } + + if (spec->num_modes > HWMODE_G && + ieee80211_register_hwmode(hw, &hwmodes[HWMODE_G])) + goto exit_free_rates; + + if (spec->num_modes > HWMODE_B && + ieee80211_register_hwmode(hw, &hwmodes[HWMODE_B])) + goto exit_free_rates; + + if (spec->num_modes > HWMODE_A && + ieee80211_register_hwmode(hw, &hwmodes[HWMODE_A])) + goto exit_free_rates; + + rt2x00dev->hwmodes = hwmodes; + + return 0; + +exit_free_rates: + kfree(rates); + +exit_free_channels: + kfree(channels); + +exit_free_modes: + kfree(hwmodes); + +exit: + ERROR("Allocation ieee80211 modes failed.\n"); + return -ENOMEM; +} + +static void rt2x00lib_deinit_hw(struct rt2x00_dev *rt2x00dev) +{ + if (GET_FLAG(rt2x00dev, DEVICE_INITIALIZED_HW)) + ieee80211_unregister_hw(rt2x00dev->hw); + + if (likely(rt2x00dev->hwmodes)) { + kfree(rt2x00dev->hwmodes->channels); + kfree(rt2x00dev->hwmodes->rates); + kfree(rt2x00dev->hwmodes); + rt2x00dev->hwmodes = NULL; + } +} + +static int rt2x00lib_init_hw(struct rt2x00_dev *rt2x00dev) +{ + struct hw_mode_spec *spec = &rt2x00dev->spec; + int status; + + /* + * Initialize device. + */ + SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->device); + + /* + * Initialize MAC address. + */ + if (!is_valid_ether_addr(spec->mac_addr)) { + ERROR("Invalid MAC addr: " MAC_FMT ".\n", MAC_ARG(spec->mac_addr)); + return -EINVAL; + } + rt2x00dev->ops->lib->config_mac_addr(rt2x00dev, spec->mac_addr); + SET_IEEE80211_PERM_ADDR(rt2x00dev->hw, spec->mac_addr); + + /* + * Initialize HW modes. + */ + status = rt2x00lib_init_hw_modes(rt2x00dev, spec); + if (status) + return status; + + /* + * Register HW. + */ + status = ieee80211_register_hw(rt2x00dev->hw); + if (status) { + rt2x00lib_deinit_hw(rt2x00dev); + return status; + } + + SET_FLAG(rt2x00dev, DEVICE_INITIALIZED_HW); + + return 0; +} + +#ifdef CONFIG_RT2X00_DEBUGFS +static void rt2x00lib_open_debugfs(struct rt2x00_dev *rt2x00dev) +{ + rt2x00dev->debugfs_intf = rt2x00debug_register( + rt2x00dev->ops->debugfs, rt2x00dev->hw->wiphy, rt2x00dev); + if (!rt2x00dev->debugfs_intf) + ERROR("Failed to register debug handler.\n"); +} + +static void rt2x00lib_close_debugfs(struct rt2x00_dev *rt2x00dev) +{ + rt2x00debug_deregister(rt2x00dev->debugfs_intf); +} +#else /* CONFIG_RT2X00_DEBUGFS */ +static inline void rt2x00lib_open_debugfs(struct rt2x00_dev *rt2x00dev){} +static inline void rt2x00lib_close_debugfs(struct rt2x00_dev *rt2x00dev){} +#endif /* CONFIG_RT2X00_DEBUGFS */ + +#ifdef CONFIG_RT2X00_LIB_FIRMWARE +struct fw_entry { + u32 chip; + char *name; +}; + +static void rt2x00lib_load_firmware_continued(const struct firmware *fw, + void *context) +{ + struct rt2x00_dev *rt2x00dev = context; + + if (!fw || !fw->size || !fw->data) { + ERROR("Failed to read Firmware.\n"); + SET_FLAG(rt2x00dev, FIRMWARE_FAILED); + return; + } + + if (rt2x00dev->ops->lib->load_firmware(rt2x00dev, fw)) { + SET_FLAG(rt2x00dev, FIRMWARE_FAILED); + return; + } + + /* + * Initialize ieee80211 structure. + */ + if (rt2x00lib_init_hw(rt2x00dev)) { + ERROR("Failed to initialize hw.\n"); + rt2x00lib_free_dev(rt2x00dev); + return; + } + + /* + * Open the debugfs entry. + */ + rt2x00lib_open_debugfs(rt2x00dev); + + SET_FLAG(rt2x00dev, FIRMWARE_LOADED); +} + +static int rt2x00lib_load_firmware(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + int status = -EINVAL; + static const struct fw_entry fw_list[] = { + { RT2561, "rt2561.bin" }, + { RT2561s, "rt2561s.bin" }, + { RT2661, "rt2661.bin" }, + { RT2571, "rt73.bin" }, + }; + + /* + * Read correct firmware from harddisk. + */ + for (i = 0; i < ARRAY_SIZE(fw_list); i++) { + if (!rt2x00_rt(&rt2x00dev->chip, fw_list[i].chip)) + continue; + status = request_firmware_nowait(THIS_MODULE, + FW_ACTION_HOTPLUG, fw_list[i].name, rt2x00dev->device, + rt2x00dev, &rt2x00lib_load_firmware_continued); + } + + if (status) + ERROR("Failed to request Firmware.\n"); + + return status; + +} + +int rt2x00lib_load_firmware_wait(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + + for (i = 0; i < 150; i++) { + if (GET_FLAG(rt2x00dev, FIRMWARE_FAILED)) + return -EIO; + if (GET_FLAG(rt2x00dev, FIRMWARE_LOADED)) + return 0; + msleep(20); + } + + ERROR("Firmware loading timed out.\n"); + return -ETIMEDOUT; +} +EXPORT_SYMBOL_GPL(rt2x00lib_load_firmware_wait); +#else /* CONFIG_RT2X00_LIB_FIRMWARE */ +int rt2x00lib_load_firmware(struct rt2x00_dev *rt2x00dev) +{ + ERROR("Firmware loading requested by non-firmware driver.\n"); + return -EINVAL; +} +#endif /* CONFIG_RT2X00_LIB_FIRMWARE */ + +/* + * driver allocation handlers. + */ +static int rt2x00lib_alloc_rings(struct rt2x00_dev *rt2x00dev) +{ + unsigned int ring_num; + unsigned int i; + + /* + * We support 1 RX queue, 1 Beacon ring + * and we could support 1 ATIM ring + * if the driver has raised that flag. + */ + ring_num = 2 + rt2x00dev->hw->queues + + GET_FLAG(rt2x00dev, DEVICE_SUPPORT_ATIM); + + rt2x00dev->ring = kzalloc( + sizeof(struct data_ring) * ring_num, GFP_KERNEL); + if (!rt2x00dev->ring) { + ERROR("Ring allocation failed.\n"); + return -ENOMEM; + } + + for (i = 0; i < ring_num; i++) { + rt2x00dev->ring[i].rt2x00dev = rt2x00dev; + + /* + * Initialize ring parameters. + * cw_min: 2^5 = 32. + * cw_max: 2^10 = 1024. + */ + rt2x00dev->ring[i].tx_params.aifs = 2; + rt2x00dev->ring[i].tx_params.cw_min = 5; + rt2x00dev->ring[i].tx_params.cw_max = 10; + } + + return 0; +} + +int rt2x00lib_alloc_dev(struct rt2x00_dev *rt2x00dev) +{ + /* + * Create workqueue. + */ + rt2x00dev->workqueue = create_singlethread_workqueue(DRV_NAME); + if (!rt2x00dev->workqueue) + goto exit; + + /* + * Initialize configuration work. + */ + INIT_DELAYED_WORK(&rt2x00dev->link.work, + rt2x00dev->ops->lib->link_tuner); + + /* + * Reset current working type. + */ + rt2x00dev->interface.type = -EINVAL; + + /* + * Allocate ring array. + */ + if (rt2x00lib_alloc_rings(rt2x00dev)) + goto exit; + + /* + * Check if we need to load the firmware. + */ + if (rt2x00dev->ops->lib->load_firmware) { + /* + * Request firmware and wait with further + * initializing of the card until the firmware + * has been loaded. + */ + if (rt2x00lib_load_firmware(rt2x00dev)) + goto exit; + } else { + /* + * No firmware needed, just set the flag + * as if it has been set correctly. + */ + SET_FLAG(rt2x00dev, FIRMWARE_LOADED); + + /* + * Initialize ieee80211 structure. + */ + if (rt2x00lib_init_hw(rt2x00dev)) { + ERROR("Failed to initialize hw.\n"); + goto exit; + } + + /* + * Open the debugfs entry. + */ + rt2x00lib_open_debugfs(rt2x00dev); + } + + return 0; + +exit: + rt2x00lib_free_dev(rt2x00dev); + + return -ENODEV; +} +EXPORT_SYMBOL_GPL(rt2x00lib_alloc_dev); + +void rt2x00lib_free_dev(struct rt2x00_dev *rt2x00dev) +{ + /* + * Close debugfs entry. + */ + rt2x00lib_close_debugfs(rt2x00dev); + + /* + * Free workqueue. + */ + if (likely(rt2x00dev->workqueue)) { + destroy_workqueue(rt2x00dev->workqueue); + rt2x00dev->workqueue = NULL; + } + + /* + * Free ring structures. + */ + kfree(rt2x00dev->ring); + rt2x00dev->ring = NULL; + + /* + * Free EEPROM memory. + */ + kfree(rt2x00dev->eeprom); + rt2x00dev->eeprom = NULL; + + /* + * Free ieee80211_hw memory. + */ + rt2x00lib_deinit_hw(rt2x00dev); +} +EXPORT_SYMBOL_GPL(rt2x00lib_free_dev); + +/* + * Interrupt context handlers. + */ +void rt2x00lib_txdone(struct data_entry *entry, + const int status, const int retry) +{ + struct rt2x00_dev *rt2x00dev = entry->ring->rt2x00dev; + struct ieee80211_tx_status *tx_status = &entry->tx_status; + struct ieee80211_low_level_stats *stats = &rt2x00dev->low_level_stats; + + /* + * Update TX statistics. + */ + tx_status->flags = 0; + tx_status->ack_signal = 0; + tx_status->excessive_retries = (status == TX_FAIL_RETRY); + tx_status->retry_count = retry; + + if (!(tx_status->control.flags & IEEE80211_TXCTL_NO_ACK)) { + if (status == TX_SUCCESS || status == TX_SUCCESS_RETRY) + tx_status->flags |= IEEE80211_TX_STATUS_ACK; + else + stats->dot11ACKFailureCount++; + } + + tx_status->queue_length = entry->ring->stats.limit; + tx_status->queue_number = tx_status->control.queue; + + if (tx_status->control.flags & IEEE80211_TXCTL_USE_RTS_CTS) { + if (status == TX_SUCCESS || status == TX_SUCCESS_RETRY) + stats->dot11RTSSuccessCount++; + else + stats->dot11RTSFailureCount++; + } + + /* + * Send the tx_status to mac80211, + * that method also cleans up the skb structure. + */ + ieee80211_tx_status_irqsafe(rt2x00dev->hw, entry->skb, tx_status); + + entry->skb = NULL; +} +EXPORT_SYMBOL_GPL(rt2x00lib_txdone); + +void rt2x00lib_rxdone(struct data_entry *entry, char *data, + const int size, const int signal, const int rssi, const int ofdm) +{ + struct rt2x00_dev *rt2x00dev = entry->ring->rt2x00dev; + struct ieee80211_rx_status *rx_status = &rt2x00dev->rx_status; + struct ieee80211_hw_mode *mode; + struct ieee80211_rate *rate; + struct sk_buff *skb; + unsigned int i; + int val = 0; + + /* + * Update RX statistics. + */ + mode = &rt2x00dev->hwmodes[rt2x00dev->curr_hwmode]; + for (i = 0; i < mode->num_rates; i++) { + rate = &mode->rates[i]; + + /* + * When frame was received with an OFDM bitrate, + * the signal is the PLCP value. If it was received with + * a CCK bitrate the signal is the rate in 0.5kbit/s. + */ + if (!ofdm) + val = DEVICE_GET_RATE_FIELD(rate->val, RATE); + else + val = DEVICE_GET_RATE_FIELD(rate->val, PLCP); + + if (val == signal) { + /* + * Check for preamble bit. + */ + if (signal & 0x08) + val = rate->val2; + val = rate->val; + break; + } + } + + rx_status->rate = val; + rx_status->ssi = rssi; + rx_status->noise = rt2x00_get_link_noise(&rt2x00dev->link); + rt2x00_update_link_rssi(&rt2x00dev->link, rssi); + + /* + * Let's allocate a sk_buff where we can store the received data in, + * note that if data is NULL, we still have to allocate a sk_buff + * but that we should use that to replace the sk_buff which is already + * inside the entry. + */ + skb = dev_alloc_skb(size + NET_IP_ALIGN); + if (!skb) + return; + + skb_reserve(skb, NET_IP_ALIGN); + skb_put(skb, size); + + if (data) { + memcpy(skb->data, data, size); + entry->skb = skb; + skb = NULL; + } + + ieee80211_rx_irqsafe(rt2x00dev->hw, entry->skb, rx_status); + entry->skb = skb; +} +EXPORT_SYMBOL_GPL(rt2x00lib_rxdone); + +/* + * TX descriptor initializer + */ +void rt2x00lib_write_tx_desc(struct rt2x00_dev *rt2x00dev, + struct data_entry *entry, struct data_desc *txd, + struct ieee80211_hdr *ieee80211hdr, unsigned int length, + struct ieee80211_tx_control *control) +{ + struct data_entry_desc desc; + int tx_rate; + int bitrate; + int duration; + int residual; + u16 frame_control; + u16 seq_ctrl; + + /* + * Identify queue + */ + if (control->queue < rt2x00dev->hw->queues) + desc.queue = control->queue; + else + desc.queue = 15; + + /* + * Read required fields from ieee80211 header. + */ + frame_control = le16_to_cpu(ieee80211hdr->frame_control); + seq_ctrl = le16_to_cpu(ieee80211hdr->seq_ctrl); + + tx_rate = control->tx_rate; + + /* + * Check if this is a rts frame + */ + if (is_rts_frame(frame_control)) { + entry->reg |= ENTRY_TXD_RTS_FRAME; + if (control->rts_cts_rate) + tx_rate = control->rts_cts_rate; + } + + /* + * Check for OFDM + */ + if (DEVICE_GET_RATE_FIELD(tx_rate, RATEMASK) & DEV_OFDM_RATE) + entry->reg |= ENTRY_TXD_OFDM_RATE; + + /* + * Check if more fragments are pending + */ + if (ieee80211_get_morefrag(ieee80211hdr)) + entry->reg |= ENTRY_TXD_MORE_FRAG; + + /* + * Check if this is a new sequence + */ + if ((seq_ctrl & IEEE80211_SCTL_FRAG) == 0) + entry->reg |= ENTRY_TXD_NEW_SEQ; + + /* + * Beacons and probe responses require the tsf timestamp + * to be inserted into the frame. + */ + if (control->queue == IEEE80211_TX_QUEUE_BEACON || + is_probe_resp(frame_control)) + entry->reg |= ENTRY_TXD_REQ_TIMESTAMP; + + /* + * Check if ACK is required + */ + if (!(control->flags & IEEE80211_TXCTL_NO_ACK)) + entry->reg |= ENTRY_TXD_REQ_ACK; + + /* + * Determine with what IFS priority this frame should be send. + * Set ifs to IFS_SIFS when the this is not the first fragment, + * or this fragment came after RTS/CTS. + */ + if ((seq_ctrl & IEEE80211_SCTL_FRAG) > 0 || + entry->reg & ENTRY_TXD_RTS_FRAME) + desc.ifs = IFS_SIFS; + else + desc.ifs = IFS_BACKOFF; + + /* + * How the length should be processed depends + * on if we are working with OFDM rates or not. + */ + if (entry->reg & ENTRY_TXD_OFDM_RATE) { + residual = 0; + desc.length_high = ((length + FCS_LEN) >> 6) & 0x3f; + desc.length_low = ((length + FCS_LEN) & 0x3f); + + } else { + bitrate = DEVICE_GET_RATE_FIELD(tx_rate, RATE); + + /* + * Convert length to microseconds. + */ + residual = get_duration_res(length + FCS_LEN, bitrate); + duration = get_duration(length + FCS_LEN, bitrate); + + if (residual != 0) + duration++; + + desc.length_high = duration >> 8; + desc.length_low = duration & 0xff; + } + + /* + * Create the signal and service values. + */ + desc.signal = DEVICE_GET_RATE_FIELD(tx_rate, PLCP); + if (DEVICE_GET_RATE_FIELD(tx_rate, PREAMBLE)) + desc.signal |= 0x08; + + desc.service = 0x04; + if (residual <= (8 % 11)) + desc.service |= 0x80; + + rt2x00dev->ops->lib->write_tx_desc(rt2x00dev, entry, txd, &desc, + ieee80211hdr, length, control); +} +EXPORT_SYMBOL_GPL(rt2x00lib_write_tx_desc); + +/* + * rt2x00lib module information. + */ +static char version[] = + DRV_NAME " - " DRV_VERSION " (" DRV_RELDATE ") by " DRV_PROJECT; + +MODULE_AUTHOR(DRV_PROJECT); +MODULE_VERSION(DRV_VERSION); +MODULE_DESCRIPTION("rt2x00 library"); +MODULE_LICENSE("GPL"); + +static int __init rt2x00lib_init(void) +{ + printk(KERN_INFO "Loading module: %s.\n", version); + return 0; +} + +static void __exit rt2x00lib_exit(void) +{ + printk(KERN_INFO "Unloading module: %s.\n", version); +} + +module_init(rt2x00lib_init); +module_exit(rt2x00lib_exit); diff --git a/drivers/net/wireless/mac80211/rt2x00/rt2x00lib.h b/drivers/net/wireless/mac80211/rt2x00/rt2x00lib.h new file mode 100644 index 0000000..879fcd2 --- /dev/null +++ b/drivers/net/wireless/mac80211/rt2x00/rt2x00lib.h @@ -0,0 +1,128 @@ +/* + Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + + + 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. + */ + +/* + Module: rt2x00lib + Abstract: Data structures for the rt2x00lib module. + Supported chipsets: RT2460, RT2560, RT2570, + rt2561, rt2561s, rt2661, rt2571W & rt2671. + */ + +#ifndef RT2X00LIB_H +#define RT2X00LIB_H + +struct rt2x00_dev; +struct data_desc; +struct data_entry_desc; +struct data_entry; + +/* + * Details about the supported modes, rates and channels + * of a particular chipset. This is used by rt2x00lib + * to build the ieee80211_hw_mode array for mac80211. + */ +struct hw_mode_spec { + /* + * Default mac address. + */ + char *mac_addr; + + /* + * Number of modes, rates and channels. + */ + int num_modes; + int num_rates; + int num_channels; + + /* + * txpower values. + */ + const u8 *tx_power_a; + const u8 *tx_power_bg; + u8 tx_power_default; + + /* + * Device/chipset specific value. + */ + const u32 *chan_val_a; + const u32 *chan_val_bg; +}; + +/* + * Radio control handlers. + */ +int rt2x00lib_enable_radio(struct rt2x00_dev *rt2x00dev); +void rt2x00lib_disable_radio(struct rt2x00_dev *rt2x00dev); + +/* + * Firmware handlers. + */ +#ifdef CONFIG_RT2X00_LIB_FIRMWARE +int rt2x00lib_load_firmware_wait(struct rt2x00_dev *rt2x00dev); +#else /* CONFIG_RT2X00_LIB_FIRMWARE */ +static inline int rt2x00lib_load_firmware_wait(struct rt2x00_dev *rt2x00dev) +{ + return 0; +} +#endif /* CONFIG_RT2X00_LIB_FIRMWARE */ + +/* + * Driver allocation handlers. + */ +int rt2x00lib_alloc_dev(struct rt2x00_dev *rt2x00dev); +void rt2x00lib_free_dev(struct rt2x00_dev *rt2x00dev); + +/* + * Interrupt context handlers. + */ +void rt2x00lib_txdone(struct data_entry *entry, + const int status, const int retry); +void rt2x00lib_rxdone(struct data_entry *entry, char *data, + const int size, const int signal, const int rssi, const int ofdm); + +/* + * TX descriptor initializer + */ +void rt2x00lib_write_tx_desc(struct rt2x00_dev *rt2x00dev, + struct data_entry *entry, struct data_desc *txd, + struct ieee80211_hdr *ieee80211hdr, unsigned int length, + struct ieee80211_tx_control *control); + +/* + * mac80211 handlers. + */ +int rt2x00lib_tx(struct ieee80211_hw *hw, struct sk_buff *skb, + struct ieee80211_tx_control *control); +int rt2x00lib_reset(struct ieee80211_hw *hw); +int rt2x00lib_add_interface(struct ieee80211_hw *hw, + struct ieee80211_if_init_conf *conf); +void rt2x00lib_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_if_init_conf *conf); +int rt2x00lib_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf); +int rt2x00lib_config_interface(struct ieee80211_hw *hw, int if_id, + struct ieee80211_if_conf *conf); +void rt2x00lib_set_multicast_list(struct ieee80211_hw *hw, + unsigned short flags, int mc_count); +int rt2x00lib_get_tx_stats(struct ieee80211_hw *hw, + struct ieee80211_tx_queue_stats *stats); +int rt2x00lib_conf_tx(struct ieee80211_hw *hw, int queue, + const struct ieee80211_tx_queue_params *params); + +#endif /* RT2X00LIB_H */ diff --git a/drivers/net/wireless/mac80211/rt2x00/rt2x00mac.c b/drivers/net/wireless/mac80211/rt2x00/rt2x00mac.c new file mode 100644 index 0000000..7216c22 --- /dev/null +++ b/drivers/net/wireless/mac80211/rt2x00/rt2x00mac.c @@ -0,0 +1,426 @@ +/* + Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + + + 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. + */ + +/* + Module: rt2x00lib + Abstract: rt2x00 generic mac80211 routines. + Supported chipsets: RT2460, RT2560, RT2570, + rt2561, rt2561s, rt2661, rt2571W & rt2671. + */ + +/* + * Set enviroment defines for rt2x00.h + */ +#define DRV_NAME "rt2x00lib" + +#include + +#include "rt2x00.h" +#include "rt2x00lib.h" + +static int rt2x00_tx_rts_cts(struct rt2x00_dev *rt2x00dev, + struct data_ring *ring, struct sk_buff *frag_skb, + struct ieee80211_tx_control *control) +{ + struct sk_buff *skb; + int size; + + if (control->flags & IEEE80211_TXCTL_USE_CTS_PROTECT) + size = sizeof(struct ieee80211_cts); + else + size = sizeof(struct ieee80211_rts); + + skb = dev_alloc_skb(size + rt2x00dev->hw->extra_tx_headroom); + if (!skb) { + WARNING("Failed to create RTS/CTS frame.\n"); + return NETDEV_TX_BUSY; + } + + skb_reserve(skb, rt2x00dev->hw->extra_tx_headroom); + skb_put(skb, size); + + if (control->flags & IEEE80211_TXCTL_USE_CTS_PROTECT) + ieee80211_ctstoself_get(rt2x00dev->hw, + frag_skb->data, frag_skb->len, control, + (struct ieee80211_cts*)(skb->data)); + else + ieee80211_rts_get(rt2x00dev->hw, + frag_skb->data, frag_skb->len, control, + (struct ieee80211_rts*)(skb->data)); + + if (rt2x00dev->ops->lib->write_tx_data(rt2x00dev, ring, skb, control)) { + WARNING("Failed to send RTS/CTS frame.\n"); + return NETDEV_TX_BUSY; + } + + return NETDEV_TX_OK; +} + +int rt2x00lib_tx(struct ieee80211_hw *hw, struct sk_buff *skb, + struct ieee80211_tx_control *control) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct ieee80211_hdr *ieee80211hdr = (struct ieee80211_hdr*)skb->data; + struct data_ring *ring; + u16 frame_control; + + /* + * Determine which ring to put packet on. + */ + ring = rt2x00_get_ring(rt2x00dev, control->queue); + if (unlikely(!ring)) { + ERROR("Attempt to send packet over invalid queue %d.\n" + "Please file bug report to %s.\n", + control->queue, DRV_PROJECT); + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + } + + /* + * If CTS/RTS is required. and this frame is not CTS or RTS, + * create and queue that frame first. But make sure we have + * at least enough entries available to send this CTS/RTS + * frame as well as the data frame. + */ + frame_control = le16_to_cpu(ieee80211hdr->frame_control); + if (control->flags & IEEE80211_TXCTL_USE_RTS_CTS && + !is_cts_frame(frame_control) && !is_rts_frame(frame_control)) { + if (rt2x00_ring_free(ring) <= 1) + return NETDEV_TX_BUSY; + + if (rt2x00_tx_rts_cts(rt2x00dev, ring, skb, control)) + return NETDEV_TX_BUSY; + } + + if (rt2x00dev->ops->lib->write_tx_data(rt2x00dev, ring, skb, control)) + return NETDEV_TX_BUSY; + + if (rt2x00dev->ops->lib->kick_tx_queue) + rt2x00dev->ops->lib->kick_tx_queue(rt2x00dev, control->queue); + + return NETDEV_TX_OK; +} +EXPORT_SYMBOL_GPL(rt2x00lib_tx); + +int rt2x00lib_reset(struct ieee80211_hw *hw) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + + rt2x00lib_disable_radio(rt2x00dev); + return rt2x00lib_enable_radio(rt2x00dev); +} +EXPORT_SYMBOL_GPL(rt2x00lib_reset); + +int rt2x00lib_add_interface(struct ieee80211_hw *hw, + struct ieee80211_if_init_conf *conf) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct interface *intf = &rt2x00dev->interface; + int status; + + /* + * We only support 1 non-monitor interface. + */ + if (conf->type != IEEE80211_IF_TYPE_MNTR && + GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED)) + return -ENOBUFS; + + /* + * We support muliple monitor mode interfaces. + * All we need to do is increase the monitor_count. + */ + if (conf->type == IEEE80211_IF_TYPE_MNTR) { + intf->monitor_count++; + } else { + intf->id = conf->if_id; + intf->type = conf->type; + if (conf->type == IEEE80211_IF_TYPE_AP) + memcpy(&intf->bssid, conf->mac_addr, ETH_ALEN); + intf->promisc = 0; + } + + /* + * Initialize interface, and enable the radio when this + * is the first interface that is brought up. + */ + if (!GET_FLAG(rt2x00dev, DEVICE_ENABLED_RADIO)) { + /* + * Before doing anything else, the MAC address + * of this device should be initialized correctly. + */ + rt2x00dev->ops->lib->config_mac_addr(rt2x00dev, conf->mac_addr); + + /* + * Initialize the device. + */ + status = rt2x00dev->ops->lib->initialize(rt2x00dev); + if (status) + return status; + + /* + * Enable radio. + */ + status = rt2x00lib_enable_radio(rt2x00dev); + if (status) + return status; + } + + /* + * Enable periodic link tuning if this is a non-monitor + * interface. Also set the INTERFACE_INITIALIZED FLAG + * to prevent new non-monitor interfaces to be added. + */ + if (conf->type != IEEE80211_IF_TYPE_MNTR) { + queue_delayed_work(rt2x00dev->workqueue, + &rt2x00dev->link.work, LINK_TUNE_INTERVAL); + SET_FLAG(rt2x00dev, INTERFACE_INITIALIZED); + } else + SET_FLAG(rt2x00dev, INTERFACE_INITIALIZED_MONITOR); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2x00lib_add_interface); + +void rt2x00lib_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_if_init_conf *conf) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct interface *intf = &rt2x00dev->interface; + + /* + * We only support 1 non-monitor interface. + */ + if (conf->type != IEEE80211_IF_TYPE_MNTR && + !GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED)) + return; + + /* + * We support muliple monitor mode interfaces. + * All we need to do is decrease the monitor_count. + */ + if (conf->type == IEEE80211_IF_TYPE_MNTR) { + intf->monitor_count--; + } else if (intf->type == conf->type) { + intf->id = 0; + intf->type = -EINVAL; + memset(&intf->bssid, 0x00, ETH_ALEN); + intf->promisc = 0; + } + + /* + * When this is a non-monitor mode, + * stop the periodic link tuning, + * and clear the INTERFACE_INITIALIZED FLAG to allow + * new non-monitor interfaces to be added. + */ + if (conf->type != IEEE80211_IF_TYPE_MNTR) { + if (work_pending(&rt2x00dev->link.work.work)) + cancel_rearming_delayed_workqueue( + rt2x00dev->workqueue, &rt2x00dev->link.work); + CLEAR_FLAG(rt2x00dev, INTERFACE_INITIALIZED); + } + + /* + * Disable radio if this was the last interface + * that was working with this device. + */ + if (!GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED_MONITOR) && + !GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED)) + rt2x00lib_disable_radio(rt2x00dev); + + /* + * Check if we still have 1 non-monitor or a monitor + * interface enabled. In that case we should update the + * registers. + */ + if (GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED_MONITOR) ^ + GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED)) { + if (GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED)) + rt2x00dev->ops->lib->config_type(rt2x00dev, + rt2x00dev->interface.type); + else + rt2x00dev->ops->lib->config_type(rt2x00dev, + IEEE80211_IF_TYPE_MNTR); + } + + /* + * Check which interfaces have been disabled. + */ + if (!GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED)) + CLEAR_FLAG(rt2x00dev, INTERFACE_ENABLED); + else if (!rt2x00dev->interface.monitor_count) + CLEAR_FLAG(rt2x00dev, INTERFACE_ENABLED_MONITOR); +} +EXPORT_SYMBOL_GPL(rt2x00lib_remove_interface); + +int rt2x00lib_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + + /* + * Check if we need to disable the radio, + * if this is not the case, at least the RX must be disabled. + */ + if (GET_FLAG(rt2x00dev, DEVICE_ENABLED_RADIO)) { + if (!conf->radio_enabled) + rt2x00lib_disable_radio(rt2x00dev); + else { + rt2x00dev->ops->lib->toggle_rx(rt2x00dev, 0); + } + } + + rt2x00dev->ops->lib->config_phymode(rt2x00dev, conf->phymode); + rt2x00dev->ops->lib->config_channel(rt2x00dev, + conf->channel_val, conf->channel, conf->freq, + conf->power_level); + rt2x00dev->ops->lib->config_txpower(rt2x00dev, conf->power_level); + rt2x00dev->ops->lib->config_antenna(rt2x00dev, + conf->antenna_sel_tx, conf->antenna_sel_rx); + rt2x00dev->ops->lib->config_duration(rt2x00dev, + (conf->flags & IEEE80211_CONF_SHORT_SLOT_TIME)); + + /* + * Reenable RX only if the radio should be on. + */ + if (GET_FLAG(rt2x00dev, DEVICE_ENABLED_RADIO)) { + rt2x00dev->ops->lib->toggle_rx(rt2x00dev, 1); + } else if (conf->radio_enabled) + return rt2x00lib_enable_radio(rt2x00dev); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2x00lib_config); + +int rt2x00lib_config_interface(struct ieee80211_hw *hw, int if_id, + struct ieee80211_if_conf *conf) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct interface *intf = &rt2x00dev->interface; + + /* + * Monitor mode does not need configuring. + * If the given type does not match the configured type, + * there has been a problem. + */ + if (conf->type == IEEE80211_IF_TYPE_MNTR) + return 0; + else if (conf->type != intf->type) + return -EINVAL; + + /* + * If the interface does not work in master mode, + * then the bssid value in the interface structure + * should now be set. + */ + if (conf->type != IEEE80211_IF_TYPE_AP) + memcpy(&intf->bssid, conf->bssid, ETH_ALEN); + + /* + * Enable configuration. + */ + rt2x00dev->ops->lib->config_type(rt2x00dev, conf->type); + rt2x00dev->ops->lib->config_bssid(rt2x00dev, intf->bssid); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2x00lib_config_interface); + +void rt2x00lib_set_multicast_list(struct ieee80211_hw *hw, + unsigned short flags, int mc_count) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + int update = 0; + + if (GET_FLAG(rt2x00dev, INTERFACE_ENABLED_PROMISC)) { + if (!(flags & IFF_PROMISC)) { + rt2x00dev->interface.promisc = 0; + update = 1; + } + } else { + if (flags & IFF_PROMISC) { + rt2x00dev->interface.promisc = 1; + update = 1; + } + } + + /* + * Monitor mode works with PROMISC mode forced on, + * so there is nothing to be done here in that case. + */ + if (update && !GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED_MONITOR)) { + if (rt2x00dev->ops->lib->config_promisc) + rt2x00dev->ops->lib->config_promisc(rt2x00dev, + rt2x00dev->interface.promisc); + else + NOTICE("For the moment promisc mode is ignored"); + } +} +EXPORT_SYMBOL_GPL(rt2x00lib_set_multicast_list); + +int rt2x00lib_get_tx_stats(struct ieee80211_hw *hw, + struct ieee80211_tx_queue_stats *stats) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + unsigned int i; + + for (i = 0; i < hw->queues; i++) + memcpy(&stats->data[i], &rt2x00dev->ring[i].stats, + sizeof(rt2x00dev->ring[i].stats)); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2x00lib_get_tx_stats); + +int rt2x00lib_conf_tx(struct ieee80211_hw *hw, int queue, + const struct ieee80211_tx_queue_params *params) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct data_ring *ring; + + ring = rt2x00_get_ring(rt2x00dev, queue); + if (unlikely(!ring)) + return -EINVAL; + + /* + * The passed variables are stored as real value ((2^n)-1). + * Ralink registers require to know the bit number 'n'. + */ + if (params->cw_min) + ring->tx_params.cw_min = fls(params->cw_min); + else + ring->tx_params.cw_min = 5; /* cw_min: 2^5 = 32. */ + + if (params->cw_max) + ring->tx_params.cw_max = fls(params->cw_max); + else + ring->tx_params.cw_max = 10; /* cw_min: 2^10 = 1024. */ + + if (params->aifs) + ring->tx_params.aifs = params->aifs; + else + ring->tx_params.aifs = 2; + + INFO("Configured TX ring %d - CWmin: %d, CWmax: %d, Aifs: %d.\n", + queue, ring->tx_params.cw_min, ring->tx_params.cw_max, + ring->tx_params.aifs); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2x00lib_conf_tx); diff --git a/drivers/net/wireless/mac80211/rt2x00/rt2x00pci.c b/drivers/net/wireless/mac80211/rt2x00/rt2x00pci.c new file mode 100644 index 0000000..3462808 --- /dev/null +++ b/drivers/net/wireless/mac80211/rt2x00/rt2x00pci.c @@ -0,0 +1,587 @@ +/* + Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + + + 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. + */ + +/* + Module: rt2x00pci + Abstract: rt2x00 generic pci device routines. + Supported chipsets: rt2460, rt2560, rt2561, rt2561s & rt2661. + */ + +/* + * Set enviroment defines for rt2x00.h + */ +#define DRV_NAME "rt2x00pci" + +#include +#include +#include +#include +#include + +#include "rt2x00.h" +#include "rt2x00pci.h" + +/* + * Beacon handlers. + */ +int rt2x00pci_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb, + struct ieee80211_tx_control *control) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct data_ring *ring = + rt2x00_get_ring(rt2x00dev, IEEE80211_TX_QUEUE_BEACON); + struct data_entry *entry = rt2x00_get_data_entry(ring); + + /* + * Just in case the ieee80211 doesn't set this, + * but we need this queue set for the descriptor + * initialization. + */ + control->queue = IEEE80211_TX_QUEUE_BEACON; + + /* + * Update the beacon entry. + */ + memcpy(entry->data_addr, skb->data, skb->len); + rt2x00lib_write_tx_desc(rt2x00dev, entry, entry->priv, + (struct ieee80211_hdr*)skb->data, skb->len, control); + + /* + * Enable beacon generation. + */ + rt2x00dev->ops->lib->kick_beacon_gen(rt2x00dev); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2x00pci_beacon_update); + +void rt2x00pci_beacondone(struct rt2x00_dev *rt2x00dev, const int queue) +{ + struct data_ring *ring = &rt2x00dev->ring[queue]; + struct data_entry *entry = rt2x00_get_data_entry(ring); + struct sk_buff *skb; + + skb = ieee80211_beacon_get(rt2x00dev->hw, + rt2x00dev->interface.id, &entry->tx_status.control); + if (!skb) + return; + + rt2x00dev->ops->hw->beacon_update(rt2x00dev->hw, skb, + &entry->tx_status.control); + + dev_kfree_skb(skb); +} +EXPORT_SYMBOL_GPL(rt2x00pci_beacondone); + +/* + * TX data handlers. + */ +int rt2x00pci_write_tx_data(struct rt2x00_dev *rt2x00dev, + struct data_ring *ring, struct sk_buff *skb, + struct ieee80211_tx_control *control) +{ + struct ieee80211_hdr *ieee80211hdr = (struct ieee80211_hdr*)skb->data; + struct data_entry *entry = rt2x00_get_data_entry(ring); + struct data_desc *txd = entry->priv; + u32 word; + + if (rt2x00_ring_full(ring)) { + ieee80211_stop_queue(rt2x00dev->hw, control->queue); + return -EINVAL; + } + + rt2x00_desc_read(txd, 0, &word); + + if (rt2x00_get_field32(word, TXD_ENTRY_AVAILABLE)) { + ERROR("Arrived at non-free entry in the non-full queue %d.\n" + "Please file bug report to %s.\n", + control->queue, DRV_PROJECT); + ieee80211_stop_queue(rt2x00dev->hw, control->queue); + return -EINVAL; + } + + memcpy(entry->data_addr, skb->data, skb->len); + rt2x00lib_write_tx_desc(rt2x00dev, entry, txd, ieee80211hdr, + skb->len, control); + memcpy(&entry->tx_status.control, control, sizeof(*control)); + entry->skb = skb; + + rt2x00_ring_index_inc(ring); + + if (rt2x00_ring_full(ring)) + ieee80211_stop_queue(rt2x00dev->hw, control->queue); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2x00pci_write_tx_data); + +/* + * Device initialization handlers. + */ +#define priv_offset(__ring, __i) \ +({ \ + ring->data_addr + (i * ring->desc_size); \ +}) + +#define data_addr_offset(__ring, __i) \ +({ \ + (__ring)->data_addr \ + + ((__ring)->stats.limit * (__ring)->desc_size) \ + + ((__i) * (__ring)->data_size); \ +}) + +#define data_dma_offset(__ring, __i) \ +({ \ + (__ring)->data_dma \ + + ((__ring)->stats.limit * (__ring)->desc_size) \ + + ((__i) * (__ring)->data_size); \ +}) + +static int rt2x00pci_alloc_dma_ring(struct rt2x00_dev *rt2x00dev, + const int queue, const u16 max_entries, + const u16 data_size, const u16 desc_size) +{ + struct data_ring *ring = &rt2x00dev->ring[queue]; + unsigned int i; + + ring->stats.limit = max_entries; + ring->data_size = data_size; + ring->desc_size = desc_size; + + /* + * Allocate all ring entries. + */ + ring->entry = kzalloc(ring->stats.limit * sizeof(struct data_entry), + GFP_KERNEL); + if (!ring->entry) + return -ENOMEM; + + /* + * Allocate DMA memory for descriptor and buffer. + */ + ring->data_addr = pci_alloc_consistent(rt2x00dev_pci(rt2x00dev), + rt2x00_get_ring_size(ring), &ring->data_dma); + if (!ring->data_addr) { + kfree(ring->entry); + return -ENOMEM; + } + + /* + * Initialize all ring entries to contain valid + * addresses. + */ + for (i = 0; i < ring->stats.limit; i++) { + ring->entry[i].flags = 0; + ring->entry[i].ring = ring; + ring->entry[i].skb = NULL; + ring->entry[i].priv = priv_offset(ring, i); + ring->entry[i].data_addr = data_addr_offset(ring, i); + ring->entry[i].data_dma = data_dma_offset(ring, i); + } + + return 0; +} + +static void rt2x00pci_free_ring(struct rt2x00_dev *rt2x00dev, const int queue) +{ + struct data_ring *ring = &rt2x00dev->ring[queue]; + if (ring->data_addr) + pci_free_consistent(rt2x00dev_pci(rt2x00dev), + rt2x00_get_ring_size(ring), + ring->data_addr, ring->data_dma); + ring->data_addr = NULL; + + kfree(ring->entry); + ring->entry = NULL; +} + +static int rt2x00pci_allocate_dma_rings(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + int rings; + + /* + * The TX rings reported to mac80211, + * plus the additional ATIM ring (if supported). + */ + rings = rt2x00dev->hw->queues + + GET_FLAG(rt2x00dev, DEVICE_SUPPORT_ATIM); + + /* + * First allocate the TX rings (minus BEACON). + */ + for (i = 0; i < rings; i++) { + if (rt2x00pci_alloc_dma_ring(rt2x00dev, i, + TX_ENTRIES, DATA_FRAME_SIZE, rt2x00dev->txd_size)) + return -ENOMEM; + } + + /* + * Allocate the BEACON ring. + */ + if (rt2x00pci_alloc_dma_ring(rt2x00dev, rings, + BEACON_ENTRIES, MGMT_FRAME_SIZE, rt2x00dev->txd_size)) + return -ENOMEM; + + /* + * Allocate the RX ring. + */ + if (rt2x00pci_alloc_dma_ring(rt2x00dev, rings + 1, + RX_ENTRIES, DATA_FRAME_SIZE, rt2x00dev->rxd_size)) + return -ENOMEM; + + return 0; +} + +static void rt2x00pci_free_rings(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + int rings; + + /* + * The BEACON and RX ring, plus TX rings reported to mac80211, + * and the additional ATIM ring (if supported). + */ + rings = 2 + rt2x00dev->hw->queues + + GET_FLAG(rt2x00dev, DEVICE_SUPPORT_ATIM); + + for (i = 0; i < rings; i++) + rt2x00pci_free_ring(rt2x00dev, i); +} + +int rt2x00pci_initialize(struct rt2x00_dev *rt2x00dev) +{ + struct pci_dev *pci_dev = rt2x00dev_pci(rt2x00dev); + int status; + + if (GET_FLAG(rt2x00dev, DEVICE_INITIALIZED)) + return 0; + + /* + * We must wait on the firmware before + * we can safely continue. + */ + if (rt2x00lib_load_firmware_wait(rt2x00dev)) + return -ENODEV; + + /* + * Allocate all data rings. + */ + status = rt2x00pci_allocate_dma_rings(rt2x00dev); + if (status) { + ERROR("DMA allocation failed.\n"); + goto exit_fail; + } + + /* + * Reset the channel_change_time value + * to make sure it will be correctly initialized + * after the radio has been enabled. + */ + rt2x00dev->hw->channel_change_time = 0; + + /* + * Register interrupt handler. + */ + status = request_irq(pci_dev->irq, rt2x00dev->ops->lib->irq_handler, + IRQF_SHARED, pci_dev->driver->name, rt2x00dev); + if (status) { + ERROR("IRQ %d allocation failed (error %d).\n", + pci_dev->irq, status); + goto exit_fail; + } + + SET_FLAG(rt2x00dev, DEVICE_INITIALIZED); + + return 0; + +exit_fail: + rt2x00pci_free_rings(rt2x00dev); + + return status; +} +EXPORT_SYMBOL_GPL(rt2x00pci_initialize); + +void rt2x00pci_uninitialize(struct rt2x00_dev *rt2x00dev) +{ + if (!GET_FLAG(rt2x00dev, DEVICE_INITIALIZED)) + return; + + /* + * Flush out all pending work. + */ + flush_workqueue(rt2x00dev->workqueue); + + /* + * Free DMA rings. + */ + rt2x00pci_free_rings(rt2x00dev); + + /* + * Free irq line. + */ + free_irq(rt2x00dev_pci(rt2x00dev)->irq, rt2x00dev); + + CLEAR_FLAG(rt2x00dev, DEVICE_INITIALIZED); +} +EXPORT_SYMBOL_GPL(rt2x00pci_uninitialize); + +/* + * PCI driver handlers. + */ +static void rt2x00pci_remove_dev(struct rt2x00_dev *rt2x00dev) +{ + /* + * Release CSR memory. + */ + if (rt2x00dev->csr_addr) { + iounmap(rt2x00dev->csr_addr); + rt2x00dev->csr_addr = NULL; + } + + rt2x00lib_free_dev(rt2x00dev); +} + +static int rt2x00pci_probe_dev(struct rt2x00_dev *rt2x00dev) +{ + int retval; + + /* + * Allocate the CSR memory. + */ + rt2x00dev->csr_addr = ioremap( + pci_resource_start(rt2x00dev_pci(rt2x00dev), 0), + pci_resource_len(rt2x00dev_pci(rt2x00dev), 0)); + if (!rt2x00dev->csr_addr) { + ERROR("Ioremap failed.\n"); + return -ENOMEM; + } + + /* + * Let the driver probe the device to + * detect the capabilities. + */ + retval = rt2x00dev->ops->lib->init_hw(rt2x00dev); + if (retval) { + ERROR("Failed to allocate device.\n"); + goto exit; + } + + return rt2x00lib_alloc_dev(rt2x00dev); + +exit: + rt2x00pci_remove_dev(rt2x00dev); + + return retval; +} + +int rt2x00pci_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) +{ + struct rt2x00_ops *ops = (struct rt2x00_ops*)id->driver_data; + struct ieee80211_hw *hw; + struct rt2x00_dev *rt2x00dev; + int retval; + + retval = pci_request_regions(pci_dev, pci_name(pci_dev)); + if (retval) { + ERROR("PCI request regions failed.\n"); + return retval; + } + + retval = pci_enable_device(pci_dev); + if (retval) { + ERROR("Enable device failed.\n"); + goto exit_release_regions; + } + + pci_set_master(pci_dev); + + if (pci_set_mwi(pci_dev)) + NOTICE("MWI not available.\n"); + + if (pci_set_dma_mask(pci_dev, DMA_64BIT_MASK) && + pci_set_dma_mask(pci_dev, DMA_32BIT_MASK)) { + ERROR("PCI DMA not supported.\n"); + retval = -EIO; + goto exit_disable_device; + } + + hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), ops->hw); + if (!hw) { + ERROR("Failed to allocate hardware.\n"); + retval = -ENOMEM; + goto exit_disable_device; + } + + pci_set_drvdata(pci_dev, hw); + + rt2x00dev = hw->priv; + rt2x00dev->dev = pci_dev; + rt2x00dev->device = &pci_dev->dev; + rt2x00dev->ops = ops; + rt2x00dev->hw = hw; + + retval = rt2x00pci_probe_dev(rt2x00dev); + if (retval) + goto exit_free_device; + + return 0; + +exit_free_device: + ieee80211_free_hw(hw); + +exit_disable_device: + if (retval != -EBUSY) + pci_disable_device(pci_dev); + +exit_release_regions: + pci_release_regions(pci_dev); + + pci_set_drvdata(pci_dev, NULL); + + return retval; +} +EXPORT_SYMBOL_GPL(rt2x00pci_probe); + +void rt2x00pci_remove(struct pci_dev *pci_dev) +{ + struct ieee80211_hw *hw = pci_get_drvdata(pci_dev); + struct rt2x00_dev *rt2x00dev = hw->priv; + + /* + * Uninitialize and free the rt61pci driver data. + */ + rt2x00lib_disable_radio(rt2x00dev); + rt2x00pci_uninitialize(rt2x00dev); + rt2x00pci_remove_dev(rt2x00dev); + + /* + * Free the 80211 stack data. + */ + ieee80211_free_hw(hw); + + /* + * Free the PCI device data. + */ + pci_set_drvdata(pci_dev, NULL); + pci_disable_device(pci_dev); + pci_release_regions(pci_dev); +} +EXPORT_SYMBOL_GPL(rt2x00pci_remove); + +#ifdef CONFIG_PM +int rt2x00pci_suspend(struct pci_dev *pci_dev, pm_message_t state) +{ + struct ieee80211_hw *hw = pci_get_drvdata(pci_dev); + struct rt2x00_dev *rt2x00dev = hw->priv; + int retval; + + NOTICE("Going to sleep.\n"); + + /* + * Disable the radio. + */ + rt2x00lib_disable_radio(rt2x00dev); + + /* + * Set device mode to sleep for power management. + */ + retval = rt2x00dev->ops->lib->set_state(rt2x00dev, STATE_SLEEP); + if (retval) + return retval; + + /* + * Uninitialize device and hardware. + */ + rt2x00pci_uninitialize(rt2x00dev); + rt2x00pci_remove_dev(rt2x00dev); + + /* + * Disable PCI. + */ + pci_save_state(pci_dev); + pci_disable_device(pci_dev); + return pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state)); +} +EXPORT_SYMBOL_GPL(rt2x00pci_suspend); + +int rt2x00pci_resume(struct pci_dev *pci_dev) +{ + struct ieee80211_hw *hw = pci_get_drvdata(pci_dev); + struct rt2x00_dev *rt2x00dev = hw->priv; + int retval; + + NOTICE("Waking up.\n"); + + /* + * Enable PCI. + */ + if (pci_set_power_state(pci_dev, PCI_D0) || + pci_enable_device(pci_dev) || + pci_restore_state(pci_dev)) { + ERROR("Failed to resume device.\n"); + return -EIO; + } + + /* + * Initialize hardware. + */ + retval = rt2x00pci_probe_dev(rt2x00dev); + if (retval) { + ERROR("Failed to allocate device.\n"); + return retval; + } + + /* + * Set device mode to awake for power management. + */ + retval = rt2x00dev->ops->lib->set_state(rt2x00dev, STATE_AWAKE); + if (retval) + return retval; + + return 0; +} +EXPORT_SYMBOL_GPL(rt2x00pci_resume); +#endif /* CONFIG_PM */ + +/* + * rt2x00pci module information. + */ +static char version[] = + DRV_NAME " - " DRV_VERSION " (" DRV_RELDATE ") by " DRV_PROJECT; + +MODULE_AUTHOR(DRV_PROJECT); +MODULE_VERSION(DRV_VERSION); +MODULE_DESCRIPTION("rt2x00 library"); +MODULE_LICENSE("GPL"); + +static int __init rt2x00pci_init(void) +{ + printk(KERN_INFO "Loading module: %s.\n", version); + return 0; +} + +static void __exit rt2x00pci_exit(void) +{ + printk(KERN_INFO "Unloading module: %s.\n", version); +} + +module_init(rt2x00pci_init); +module_exit(rt2x00pci_exit); diff --git a/drivers/net/wireless/mac80211/rt2x00/rt2x00pci.h b/drivers/net/wireless/mac80211/rt2x00/rt2x00pci.h new file mode 100644 index 0000000..729999e --- /dev/null +++ b/drivers/net/wireless/mac80211/rt2x00/rt2x00pci.h @@ -0,0 +1,83 @@ +/* + Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + + + 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. + */ + +/* + Module: rt2x00pci + Abstract: Data structures for the rt2x00pci module. + Supported chipsets: rt2460, rt2560, rt2561, rt2561s & rt2661. + */ + +#ifndef RT2X00PCI_H +#define RT2X00PCI_H + +/* + * This variable should be used with the + * pci_driver structure initialization. + */ +#define PCI_DEVICE_DATA(__ops) .driver_data = (kernel_ulong_t)(__ops) + +/* + * Register defines. + * When register access attempts should be repeated + * only REGISTER_BUSY_COUNT attempts with a delay + * of REGISTER_BUSY_DELAY us should be taken. + */ +#define REGISTER_BUSY_COUNT 5 +#define REGISTER_BUSY_DELAY 100 + +/* + * TX descriptor available flag. + * This flag is the combination of the TXD_W0_OWNER_NIC + * and TXD_W0_VALID flag which have the same value on all + * PCI drivers. + */ +#define TXD_ENTRY_AVAILABLE FIELD32(0x00000003) + +/* + * Beacon handlers. + */ +int rt2x00pci_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb, + struct ieee80211_tx_control *control); +void rt2x00pci_beacondone(struct rt2x00_dev *rt2x00dev, const int queue); + +/* + * TX data handlers. + */ +int rt2x00pci_write_tx_data(struct rt2x00_dev *rt2x00dev, + struct data_ring *ring, struct sk_buff *skb, + struct ieee80211_tx_control *control); + +/* + * Device initialization handlers. + */ +int rt2x00pci_initialize(struct rt2x00_dev *rt2x00dev); +void rt2x00pci_uninitialize(struct rt2x00_dev *rt2x00dev); + +/* + * PCI driver handlers. + */ +int rt2x00pci_probe(struct pci_dev *pci_dev, const struct pci_device_id *id); +void rt2x00pci_remove(struct pci_dev *pci_dev); +#ifdef CONFIG_PM +int rt2x00pci_suspend(struct pci_dev *pci_dev, pm_message_t state); +int rt2x00pci_resume(struct pci_dev *pci_dev); +#endif /* CONFIG_PM */ + +#endif /* RT2X00PCI_H */ diff --git a/drivers/net/wireless/mac80211/rt2x00/rt2x00usb.c b/drivers/net/wireless/mac80211/rt2x00/rt2x00usb.c new file mode 100644 index 0000000..f59f9b8 --- /dev/null +++ b/drivers/net/wireless/mac80211/rt2x00/rt2x00usb.c @@ -0,0 +1,637 @@ +/* + Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + + + 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. + */ + +/* + Module: rt2x00usb + Abstract: rt2x00 generic usb device routines. + Supported chipsets: rt2570, rt2571W & rt2671. + */ + +/* + * Set enviroment defines for rt2x00.h + */ +#define DRV_NAME "rt2x00usb" + +#include +#include +#include +#include +#include + +#include "rt2x00.h" +#include "rt2x00usb.h" + +/* + * Beacon handlers. + */ +int rt2x00usb_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb, + struct ieee80211_tx_control *control) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct usb_device *usb_dev = + interface_to_usbdev(rt2x00dev_usb(rt2x00dev)); + struct data_ring *ring = + rt2x00_get_ring(rt2x00dev, IEEE80211_TX_QUEUE_BEACON); + struct data_entry *beacon; + struct data_entry *guardian; + int length; + + /* + * Just in case the ieee80211 doesn't set this, + * but we need this queue set for the descriptor + * initialization. + */ + control->queue = IEEE80211_TX_QUEUE_BEACON; + + /* + * Obtain 2 entries, one for the guardian byte, + * the second for the actual beacon. + */ + guardian = rt2x00_get_data_entry(ring); + rt2x00_ring_index_inc(ring); + beacon = rt2x00_get_data_entry(ring); + + /* + * First we create the beacon. + */ + skb_push(skb, ring->desc_size); + rt2x00lib_write_tx_desc(rt2x00dev, beacon, + (struct data_desc*)skb->data, + (struct ieee80211_hdr*)(skb->data + ring->desc_size), + skb->len - ring->desc_size, + control); + + /* + * Length passed to usb_fill_urb cannot be an odd number, + * so add 1 byte to make it even. + */ + length = skb->len; + if (length % 2) + length++; + + usb_fill_bulk_urb( + beacon->priv, + usb_dev, + usb_sndbulkpipe(usb_dev, 1), + skb->data, + length, + rt2x00usb_beacondone, + beacon); + + beacon->skb = skb; + + /* + * Second we need to create the guardian byte. + * We only need a single byte, so lets recycle + * the 'flags' field we are not using for beacons. + */ + guardian->reg = 0; + usb_fill_bulk_urb( + guardian->priv, + usb_dev, + usb_sndbulkpipe(usb_dev, 1), + &guardian->reg, + 1, + rt2x00usb_beacondone, + guardian); + + /* + * Send out the guardian byte. + */ + usb_submit_urb(guardian->priv, GFP_ATOMIC); + + /* + * Enable beacon generation. + */ + rt2x00dev->ops->lib->kick_beacon_gen(rt2x00dev); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2x00usb_beacon_update); + +void rt2x00usb_beacondone(struct urb *urb) +{ + struct data_entry *entry = (struct data_entry*)urb->context; + struct data_ring *ring = entry->ring; + + if (!GET_FLAG(ring->rt2x00dev, DEVICE_ENABLED_RADIO)) + return; + + /* + * Check if this was the guardian beacon, + * if that was the case we need to send the real beacon now. + * Otherwise we should free the sk_buffer, the device + * should be doing the rest of the work now. + */ + if (ring->index == 1) { + rt2x00_ring_index_done_inc(ring); + entry = rt2x00_get_data_entry(ring); + usb_submit_urb(entry->priv, GFP_ATOMIC); + rt2x00_ring_index_inc(ring); + } else if (ring->index_done == 1) { + entry = rt2x00_get_data_entry_done(ring); + if (entry->skb) { + dev_kfree_skb(entry->skb); + entry->skb = NULL; + } + rt2x00_ring_index_done_inc(ring); + } +} +EXPORT_SYMBOL_GPL(rt2x00usb_beacondone); + +/* + * TX data handlers. + */ +static void rt2x00usb_interrupt_txdone(struct urb *urb) +{ + struct data_entry *entry = (struct data_entry*)urb->context; + struct data_ring *ring = entry->ring; + struct rt2x00_dev *rt2x00dev = ring->rt2x00dev; + struct data_desc *txd = (struct data_desc *)entry->skb->data; + u32 word; + int tx_status; + + if (!GET_FLAG(rt2x00dev, DEVICE_ENABLED_RADIO) || + !GET_FLAG(entry, ENTRY_OWNER_NIC)) + return; + + CLEAR_FLAG(entry, ENTRY_OWNER_NIC); + + rt2x00_desc_read(txd, 0, &word); + + /* + * Remove the descriptor data from the buffer. + */ + skb_pull(entry->skb, ring->desc_size); + + /* + * Obtain the status about this packet. + */ + tx_status = !urb->status ? TX_SUCCESS : TX_FAIL_RETRY; + + rt2x00lib_txdone(entry, tx_status, 0); + + /* + * Make this entry available for reuse. + */ + entry->reg = 0; + rt2x00_ring_index_done_inc(entry->ring); + + /* + * If the data ring was full before the txdone handler + * we must make sure the packet queue in the mac80211 stack + * is reenabled when the txdone handler has finished. + */ + if (!rt2x00_ring_full(ring)) + ieee80211_wake_queue(rt2x00dev->hw, + entry->tx_status.control.queue); +} + +int rt2x00usb_write_tx_data(struct rt2x00_dev *rt2x00dev, + struct data_ring *ring, struct sk_buff *skb, + struct ieee80211_tx_control *control) +{ + struct usb_device *usb_dev = + interface_to_usbdev(rt2x00dev_usb(rt2x00dev)); + struct ieee80211_hdr *ieee80211hdr = (struct ieee80211_hdr*)skb->data; + struct data_entry *entry = rt2x00_get_data_entry(ring); + struct data_desc *txd; + u32 length = skb->len; + + if (rt2x00_ring_full(ring)) { + ieee80211_stop_queue(rt2x00dev->hw, control->queue); + return -EINVAL; + } + + if (GET_FLAG(entry, ENTRY_OWNER_NIC)) { + ERROR("Arrived at non-free entry in the non-full queue %d.\n" + "Please file bug report to %s.\n", + control->queue, DRV_PROJECT); + ieee80211_stop_queue( rt2x00dev->hw, control->queue); + return -EINVAL; + } + + skb_push(skb, rt2x00dev->hw->extra_tx_headroom); + txd = (struct data_desc*)skb->data; + rt2x00lib_write_tx_desc(rt2x00dev, entry, txd, ieee80211hdr, + length, control); + memcpy(&entry->tx_status.control, control, sizeof(*control)); + entry->skb = skb; + + /* + * Length passed to usb_fill_urb cannot be an odd number, + * so add 1 byte to make it even. + */ + length += rt2x00dev->hw->extra_tx_headroom; + if (length % 2) + length++; + + SET_FLAG(entry, ENTRY_OWNER_NIC); + usb_fill_bulk_urb( + entry->priv, + usb_dev, + usb_sndbulkpipe(usb_dev, 1), + skb->data, + length, + rt2x00usb_interrupt_txdone, + entry); + usb_submit_urb(entry->priv, GFP_ATOMIC); + + rt2x00_ring_index_inc(ring); + + if (rt2x00_ring_full(ring)) + ieee80211_stop_queue(rt2x00dev->hw, control->queue); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2x00usb_write_tx_data); + +/* + * Device initialization handlers. + */ +static int rt2x00usb_alloc_dma_ring(struct rt2x00_dev *rt2x00dev, + const int queue, const u16 max_entries, const u16 data_size, + const u16 desc_size, const int rx_ring) +{ + struct data_ring *ring = &rt2x00dev->ring[queue]; + unsigned int entry_size; + unsigned int i; + + ring->stats.limit = max_entries; + ring->data_size = data_size; + ring->desc_size = desc_size; + + /* + * Allocate all ring entries. + */ + ring->entry = kzalloc(ring->stats.limit * sizeof(struct data_entry), + GFP_KERNEL); + if (!ring->entry) + return -ENOMEM; + + /* + * Initialize all ring entries to contain valid + * addresses. + */ + entry_size = ring->data_size + ring->desc_size; + for (i = 0; i < ring->stats.limit; i++) { + ring->entry[i].flags = 0; + ring->entry[i].ring = ring; + ring->entry[i].priv = usb_alloc_urb(0, GFP_KERNEL); + if (!ring->entry[i].priv) + return -ENOMEM; + + if (rx_ring) { + ring->entry[i].skb = + dev_alloc_skb(NET_IP_ALIGN + entry_size); + if (!ring->entry[i].skb) + return -ENOMEM; + + skb_reserve(ring->entry[i].skb, NET_IP_ALIGN); + skb_put(ring->entry[i].skb, entry_size); + } + } + + return 0; +} + +static void rt2x00usb_free_ring(struct rt2x00_dev *rt2x00dev, const int queue) +{ + struct data_ring *ring = &rt2x00dev->ring[queue]; + unsigned int i; + + if (!ring->entry) + return; + + for (i = 0; i < ring->stats.limit; i++) { + usb_kill_urb(ring->entry[i].priv); + usb_free_urb(ring->entry[i].priv); + if (ring->entry[i].skb) + kfree_skb(ring->entry[i].skb); + } + + kfree(ring->entry); + ring->entry = NULL; +} + +static int rt2x00usb_allocate_dma_rings(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + int rings; + + /* + * The TX rings reported to mac80211, + * plus the additional ATIM ring (if supported). + */ + rings = rt2x00dev->hw->queues + + GET_FLAG(rt2x00dev, DEVICE_SUPPORT_ATIM); + + /* + * First allocate the TX rings (minus BEACON). + */ + for (i = 0; i < rings; i++) { + if (rt2x00usb_alloc_dma_ring(rt2x00dev, i, + TX_ENTRIES, DATA_FRAME_SIZE, rt2x00dev->txd_size, 0)) + return -ENOMEM; + } + + /* + * Allocate the BEACON ring. + */ + if (rt2x00usb_alloc_dma_ring(rt2x00dev, rings, + BEACON_ENTRIES, MGMT_FRAME_SIZE, rt2x00dev->txd_size, 0)) + return -ENOMEM; + + /* + * Allocate the RX ring. + */ + if (rt2x00usb_alloc_dma_ring(rt2x00dev, rings + 1, + RX_ENTRIES, DATA_FRAME_SIZE, rt2x00dev->rxd_size, 1)) + return -ENOMEM; + + return 0; +} + +static void rt2x00usb_free_rings(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + int rings; + + /* + * The BEACON and RX ring, plus TX rings reported to mac80211, + * and the additional ATIM ring (if supported). + */ + rings = 2 + rt2x00dev->hw->queues + + GET_FLAG(rt2x00dev, DEVICE_SUPPORT_ATIM); + + for (i = 0; i < rings; i++) + rt2x00usb_free_ring(rt2x00dev, i); +} + +int rt2x00usb_initialize(struct rt2x00_dev *rt2x00dev) +{ + if (GET_FLAG(rt2x00dev, DEVICE_INITIALIZED)) + return 0; + + /* + * We must wait on the firmware before + * we can safely continue. + */ + if (rt2x00lib_load_firmware_wait(rt2x00dev)) + return -ENODEV; + + /* + * Allocate all data rings. + */ + if (rt2x00usb_allocate_dma_rings(rt2x00dev)) { + ERROR("DMA allocation failed.\n"); + goto exit_fail; + } + + /* + * Reset the channel_change_time value + * to make sure it will be correctly initialized + * after the radio has been enabled. + */ + rt2x00dev->hw->channel_change_time = 0; + + SET_FLAG(rt2x00dev, DEVICE_INITIALIZED); + + return 0; + +exit_fail: + rt2x00usb_free_rings(rt2x00dev); + + return -EIO; +} +EXPORT_SYMBOL_GPL(rt2x00usb_initialize); + +void rt2x00usb_uninitialize(struct rt2x00_dev *rt2x00dev) +{ + if (!GET_FLAG(rt2x00dev, DEVICE_INITIALIZED)) + return; + + /* + * Flush out all pending work. + */ + flush_workqueue(rt2x00dev->workqueue); + + /* + * Free DMA rings. + */ + rt2x00usb_free_rings(rt2x00dev); + + CLEAR_FLAG(rt2x00dev, DEVICE_INITIALIZED); +} +EXPORT_SYMBOL_GPL(rt2x00usb_uninitialize); + +/* + * USB driver handlers. + */ +static void rt2x00usb_remove_dev(struct rt2x00_dev *rt2x00dev) +{ + rt2x00lib_free_dev(rt2x00dev); +} + +static int rt2x00usb_probe_dev(struct rt2x00_dev *rt2x00dev) +{ + int retval; + + /* + * Let the driver probe the device to + * detect the capabilities. + */ + retval = rt2x00dev->ops->lib->init_hw(rt2x00dev); + if (retval) { + ERROR("Failed to allocate device.\n"); + goto exit; + } + + return rt2x00lib_alloc_dev(rt2x00dev); + +exit: + rt2x00usb_remove_dev(rt2x00dev); + + return retval; +} + +int rt2x00usb_probe(struct usb_interface *usb_intf, + const struct usb_device_id *id) +{ + struct usb_device *usb_dev = interface_to_usbdev(usb_intf); + struct rt2x00_ops *ops = (struct rt2x00_ops*)id->driver_info; + struct ieee80211_hw *hw; + struct rt2x00_dev *rt2x00dev; + int retval; + + usb_dev = usb_get_dev(usb_dev); + + hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), ops->hw); + if (!hw) { + ERROR("Failed to allocate hardware.\n"); + retval = -ENOMEM; + goto exit_put_device; + } + + usb_set_intfdata(usb_intf, hw); + + rt2x00dev = hw->priv; + rt2x00dev->dev = usb_intf; + rt2x00dev->device = &usb_intf->dev; + rt2x00dev->ops = ops; + rt2x00dev->hw = hw; + + retval = rt2x00usb_probe_dev(rt2x00dev); + if (retval) + goto exit_free_device; + + return 0; + +exit_free_device: + ieee80211_free_hw(hw); + +exit_put_device: + usb_put_dev(usb_dev); + + usb_set_intfdata(usb_intf, NULL); + + return retval; +} +EXPORT_SYMBOL_GPL(rt2x00usb_probe); + +void rt2x00usb_disconnect(struct usb_interface *usb_intf) +{ + struct ieee80211_hw *hw = usb_get_intfdata(usb_intf); + struct rt2x00_dev *rt2x00dev = hw->priv; + + /* + * Uninitialize and free the rt73usb driver data. + */ + rt2x00lib_disable_radio(rt2x00dev); + rt2x00usb_uninitialize(rt2x00dev); + rt2x00usb_remove_dev(rt2x00dev); + + /* + * Free the 80211 stack data. + */ + ieee80211_free_hw(hw); + + /* + * Free the USB device data. + */ + usb_set_intfdata(usb_intf, NULL); + usb_put_dev(interface_to_usbdev(usb_intf)); +} +EXPORT_SYMBOL_GPL(rt2x00usb_disconnect); + +#ifdef CONFIG_PM +int rt2x00usb_suspend(struct usb_interface *usb_intf, pm_message_t state) +{ + struct ieee80211_hw *hw = usb_get_intfdata(usb_intf); + struct rt2x00_dev *rt2x00dev = hw->priv; + int retval; + + NOTICE("Going to sleep.\n"); + + /* + * Disable the radio. + */ + rt2x00lib_disable_radio(rt2x00dev); + + /* + * Set device mode to sleep for power management. + */ + retval = rt2x00dev->ops->lib->set_state(rt2x00dev, STATE_SLEEP); + if (retval) + return retval; + + /* + * Uninitialize device and hardware. + */ + rt2x00usb_uninitialize(rt2x00dev); + rt2x00usb_remove_dev(rt2x00dev); + + /* + * Decrease usbdev refcount. + */ + usb_put_dev(interface_to_usbdev(usb_intf)); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2x00usb_suspend); + +int rt2x00usb_resume(struct usb_interface *usb_intf) +{ + struct ieee80211_hw *hw = usb_get_intfdata(usb_intf); + struct rt2x00_dev *rt2x00dev = hw->priv; + int retval; + + NOTICE("Waking up.\n"); + + /* + * Increase usbdev refcount. + */ + usb_get_dev(interface_to_usbdev(usb_intf)); + + /* + * Initialize hardware. + */ + retval = rt2x00usb_probe_dev(rt2x00dev); + if (retval) { + ERROR("Failed to allocate device.\n"); + return retval; + } + + /* + * Set device mode to awake for power management. + */ + retval = rt2x00dev->ops->lib->set_state(rt2x00dev, STATE_AWAKE); + if (retval) + return retval; + + return 0; +} +EXPORT_SYMBOL_GPL(rt2x00usb_resume); +#endif /* CONFIG_PM */ + +/* + * rt2x00usb module information. + */ +static char version[] = + DRV_NAME " - " DRV_VERSION " (" DRV_RELDATE ") by " DRV_PROJECT; + +MODULE_AUTHOR(DRV_PROJECT); +MODULE_VERSION(DRV_VERSION); +MODULE_DESCRIPTION("rt2x00 library"); +MODULE_LICENSE("GPL"); + +static int __init rt2x00usb_init(void) +{ + printk(KERN_INFO "Loading module: %s.\n", version); + return 0; +} + +static void __exit rt2x00usb_exit(void) +{ + printk(KERN_INFO "Unloading module: %s.\n", version); +} + +module_init(rt2x00usb_init); +module_exit(rt2x00usb_exit); diff --git a/drivers/net/wireless/mac80211/rt2x00/rt2x00usb.h b/drivers/net/wireless/mac80211/rt2x00/rt2x00usb.h new file mode 100644 index 0000000..384cde7 --- /dev/null +++ b/drivers/net/wireless/mac80211/rt2x00/rt2x00usb.h @@ -0,0 +1,120 @@ +/* + Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + + + 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. + */ + +/* + Module: rt2x00usb + Abstract: Data structures for the rt2x00usb module. + Supported chipsets: rt2570, rt2571W & rt2671. + */ + +#ifndef RT2X00USB_H +#define RT2X00USB_H + +/* + * This variable should be used with the + * usb_driver structure initialization. + */ +#define USB_DEVICE_DATA(__ops) .driver_info = (kernel_ulong_t)(__ops) + +/* + * Register defines. + * When register access attempts should be repeated + * only REGISTER_BUSY_COUNT attempts with a delay + * of REGISTER_BUSY_DELAY us should be taken. + * For USB vendor requests we need to pass a timeout + * time in ms, for this we use the REGISTER_TIMEOUT, + * however when loading firmware a higher value is + * required. For that we use the REGISTER_TIMEOUT_FIRMWARE. + */ +#define REGISTER_BUSY_COUNT 5 +#define REGISTER_BUSY_DELAY 100 +#define REGISTER_TIMEOUT 20 +#define REGISTER_TIMEOUT_FIRMWARE 1000 + +/* + * USB request types. + */ +#define USB_VENDOR_REQUEST ( USB_TYPE_VENDOR | USB_RECIP_DEVICE ) +#define USB_VENDOR_REQUEST_IN ( USB_DIR_IN | USB_VENDOR_REQUEST ) +#define USB_VENDOR_REQUEST_OUT ( USB_DIR_OUT | USB_VENDOR_REQUEST ) + +/* + * USB vendor commands. + */ +#define USB_DEVICE_MODE 0x01 +#define USB_SINGLE_WRITE 0x02 +#define USB_SINGLE_READ 0x03 +#define USB_MULTI_WRITE 0x06 +#define USB_MULTI_READ 0x07 +#define USB_EEPROM_WRITE 0x08 +#define USB_EEPROM_READ 0x09 +#define USB_LED_CONTROL 0x0a /* RT73USB */ +#define USB_RX_CONTROL 0x0c + +/* + * Device modes offset + */ +#define USB_MODE_RESET 0x01 +#define USB_MODE_UNPLUG 0x02 +#define USB_MODE_FUNCTION 0x03 +#define USB_MODE_TEST 0x04 +#define USB_MODE_SLEEP 0x07 /* RT73USB */ +#define USB_MODE_FIRMWARE 0x08 /* RT73USB */ +#define USB_MODE_WAKEUP 0x09 /* RT73USB */ + +/* + * USB devices need an additional Beacon (guardian beacon) to be generated. + */ +#undef BEACON_ENTRIES +#define BEACON_ENTRIES 2 + +/* + * Beacon handlers. + */ +int rt2x00usb_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb, + struct ieee80211_tx_control *control); +void rt2x00usb_beacondone(struct urb *urb); + + +/* + * TX data handlers. + */ +int rt2x00usb_write_tx_data(struct rt2x00_dev *rt2x00dev, + struct data_ring *ring, struct sk_buff *skb, + struct ieee80211_tx_control *control); + +/* + * Device initialization handlers. + */ +int rt2x00usb_initialize(struct rt2x00_dev *rt2x00dev); +void rt2x00usb_uninitialize(struct rt2x00_dev *rt2x00dev); + +/* + * USB driver handlers. + */ +int rt2x00usb_probe(struct usb_interface *usb_intf, + const struct usb_device_id *id); +void rt2x00usb_disconnect(struct usb_interface *usb_intf); +#ifdef CONFIG_PM +int rt2x00usb_suspend(struct usb_interface *usb_intf, pm_message_t state); +int rt2x00usb_resume(struct usb_interface *usb_intf); +#endif /* CONFIG_PM */ + +#endif /* RT2X00USB_H */ diff --git a/drivers/net/wireless/mac80211/rt2x00/rt61pci.c b/drivers/net/wireless/mac80211/rt2x00/rt61pci.c new file mode 100644 index 0000000..b9b70ac --- /dev/null +++ b/drivers/net/wireless/mac80211/rt2x00/rt61pci.c @@ -0,0 +1,2332 @@ +/* + Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + + + 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. + */ + +/* + Module: rt61pci + Abstract: rt61pci device specific routines. + Supported chipsets: RT2561, RT2561s, RT2661. + */ + +/* + * Set enviroment defines for rt2x00.h + */ +#define DRV_NAME "rt61pci" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "rt2x00.h" +#include "rt2x00pci.h" +#include "rt61pci.h" + +/* + * Register access. + * All access to the CSR registers will go through the methods + * rt2x00_register_read and rt2x00_register_write. + * BBP and RF register require indirect register access, + * and use the CSR registers PHY_CSR3 and PHY_CSR4 to achieve this. + * These indirect registers work with busy bits, + * and we will try maximal REGISTER_BUSY_COUNT times to access + * the register while taking a REGISTER_BUSY_DELAY us delay + * between each attampt. When the busy bit is still set at that time, + * the access attempt is considered to have failed, + * and we will print an error. + */ +static inline void rt2x00_register_read( + const struct rt2x00_dev *rt2x00dev, + const unsigned long offset, u32 *value) +{ + readl(rt2x00dev->csr_addr + MAC_CSR0); + *value = readl(rt2x00dev->csr_addr + offset); +} + +static inline void rt2x00_register_multiread( + const struct rt2x00_dev *rt2x00dev, + const unsigned long offset, void *value, const u16 length) +{ + readl(rt2x00dev->csr_addr + MAC_CSR0); + memcpy_fromio(value, rt2x00dev->csr_addr + offset, length); +} + +static inline void rt2x00_register_write( + const struct rt2x00_dev *rt2x00dev, + const unsigned long offset, u32 value) +{ + readl(rt2x00dev->csr_addr + MAC_CSR0); + writel(value, rt2x00dev->csr_addr + offset); +} + +static inline void rt2x00_register_multiwrite( + const struct rt2x00_dev *rt2x00dev, + const unsigned long offset, void *value, const u16 length) +{ + readl(rt2x00dev->csr_addr + MAC_CSR0); + memcpy_toio(rt2x00dev->csr_addr + offset, value, length); +} + +static u32 rt2x00_bbp_check(const struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + unsigned int i; + + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2x00_register_read(rt2x00dev, PHY_CSR3, ®); + if (!rt2x00_get_field32(reg, PHY_CSR3_BUSY)) + return reg; + udelay(REGISTER_BUSY_DELAY); + } + + return 0xffff; +} + +static void rt2x00_bbp_write(const struct rt2x00_dev *rt2x00dev, + const u8 reg_id, const u8 value) +{ + u32 reg; + + /* + * Wait until the BBP becomes ready. + */ + if (rt2x00_bbp_check(rt2x00dev) == 0xffff) { + ERROR("PHY_CSR3 register busy. Write failed.\n"); + return; + } + + /* + * Write the data into the BBP. + */ + reg = 0; + rt2x00_set_field32(®, PHY_CSR3_VALUE, value); + rt2x00_set_field32(®, PHY_CSR3_REGNUM, reg_id); + rt2x00_set_field32(®, PHY_CSR3_BUSY, 1); + rt2x00_set_field32(®, PHY_CSR3_READ_CONTROL, 0); + + rt2x00_register_write(rt2x00dev, PHY_CSR3, reg); +} + +static void rt2x00_bbp_read(const struct rt2x00_dev *rt2x00dev, + const u8 reg_id, u8 *value) +{ + u32 reg; + + /* + * Wait until the BBP becomes ready. + */ + if (rt2x00_bbp_check(rt2x00dev) == 0xffff) { + ERROR("PHY_CSR3 register busy. Read failed.\n"); + return; + } + + /* + * Write the request into the BBP. + */ + reg =0; + rt2x00_set_field32(®, PHY_CSR3_REGNUM, reg_id); + rt2x00_set_field32(®, PHY_CSR3_BUSY, 1); + rt2x00_set_field32(®, PHY_CSR3_READ_CONTROL, 1); + + rt2x00_register_write(rt2x00dev, PHY_CSR3, reg); + + /* + * Wait until the BBP becomes ready. + */ + reg = rt2x00_bbp_check(rt2x00dev); + if (reg == 0xffff) + ERROR("PHY_CSR3 register busy. Read failed.\n"); + + *value = rt2x00_get_field32(reg, PHY_CSR3_VALUE); +} + +static void rt2x00_rf_write(const struct rt2x00_dev *rt2x00dev, + const u32 value) +{ + u32 reg; + unsigned int i; + + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2x00_register_read(rt2x00dev, PHY_CSR4, ®); + if (!rt2x00_get_field32(reg, PHY_CSR4_BUSY)) + goto rf_write; + udelay(REGISTER_BUSY_DELAY); + } + + ERROR("PHY_CSR4 register busy. Write failed.\n"); + return; + +rf_write: + reg = 0; + rt2x00_set_field32(®, PHY_CSR4_VALUE, value); + rt2x00_set_field32(®, PHY_CSR4_NUMBER_OF_BITS, 21); + rt2x00_set_field32(®, PHY_CSR4_IF_SELECT, 0); + rt2x00_set_field32(®, PHY_CSR4_BUSY, 1); + + rt2x00_register_write(rt2x00dev, PHY_CSR4, reg); +} + +static void rt2x00_mcu_request(const struct rt2x00_dev *rt2x00dev, + const u8 command, const u8 token, const u8 arg0, const u8 arg1) +{ + u32 reg; + + rt2x00_register_read(rt2x00dev, H2M_MAILBOX_CSR, ®); + + if (rt2x00_get_field32(reg, H2M_MAILBOX_CSR_OWNER)) { + ERROR("mcu request error. Request 0x%02x failed for " + "token 0x%02x.\n", command, token); + return; + } + + rt2x00_set_field32(®, H2M_MAILBOX_CSR_OWNER, 1); + rt2x00_set_field32(®, H2M_MAILBOX_CSR_CMD_TOKEN, token); + rt2x00_set_field32(®, H2M_MAILBOX_CSR_ARG0, arg0); + rt2x00_set_field32(®, H2M_MAILBOX_CSR_ARG1, arg1); + rt2x00_register_write(rt2x00dev, H2M_MAILBOX_CSR, reg); + + rt2x00_register_read(rt2x00dev, HOST_CMD_CSR, ®); + rt2x00_set_field32(®, HOST_CMD_CSR_HOST_COMMAND, command); + rt2x00_set_field32(®, HOST_CMD_CSR_INTERRUPT_MCU, 1); + rt2x00_register_write(rt2x00dev, HOST_CMD_CSR, reg); +} + +static void rt61pci_eepromregister_read(struct eeprom_93cx6 *eeprom) +{ + struct rt2x00_dev *rt2x00dev = eeprom->data; + u32 reg; + + rt2x00_register_read(rt2x00dev, E2PROM_CSR, ®); + + eeprom->reg_data_in = !!rt2x00_get_field32(reg, + E2PROM_CSR_DATA_IN); + eeprom->reg_data_out = !!rt2x00_get_field32(reg, + E2PROM_CSR_DATA_OUT); + eeprom->reg_data_clock = !!rt2x00_get_field32(reg, + E2PROM_CSR_DATA_CLOCK); + eeprom->reg_chip_select = !!rt2x00_get_field32(reg, + E2PROM_CSR_CHIP_SELECT); +} + +static void rt61pci_eepromregister_write(struct eeprom_93cx6 *eeprom) +{ + struct rt2x00_dev *rt2x00dev = eeprom->data; + u32 reg = 0; + + rt2x00_set_field32(®, E2PROM_CSR_DATA_IN, + !!eeprom->reg_data_in); + rt2x00_set_field32(®, E2PROM_CSR_DATA_OUT, + !!eeprom->reg_data_out); + rt2x00_set_field32(®, E2PROM_CSR_DATA_CLOCK, + !!eeprom->reg_data_clock); + rt2x00_set_field32(®, E2PROM_CSR_CHIP_SELECT, + !!eeprom->reg_chip_select); + + rt2x00_register_write(rt2x00dev, E2PROM_CSR, reg); +} + +#ifdef CONFIG_RT2X00_DEBUGFS +#define CSR_OFFSET(__word) ( CSR_REG_BASE + ((__word) * sizeof(u32)) ) + +static void rt61pci_read_csr(struct rt2x00_dev *rt2x00dev, + const unsigned long word, void *data) +{ + rt2x00_register_read(rt2x00dev, CSR_OFFSET(word), data); +} + +static void rt61pci_write_csr(struct rt2x00_dev *rt2x00dev, + const unsigned long word, void *data) +{ + rt2x00_register_write(rt2x00dev, CSR_OFFSET(word), *((u32*)data)); +} + +static void rt61pci_read_eeprom(struct rt2x00_dev *rt2x00dev, + const unsigned long word, void *data) +{ + rt2x00_eeprom_read(rt2x00dev, word, data); +} + +static void rt61pci_write_eeprom(struct rt2x00_dev *rt2x00dev, + const unsigned long word, void *data) +{ + rt2x00_eeprom_write(rt2x00dev, word, *((u16*)data)); +} + +static void rt61pci_read_bbp(struct rt2x00_dev *rt2x00dev, + const unsigned long word, void *data) +{ + rt2x00_bbp_read(rt2x00dev, word, data); +} + +static void rt61pci_write_bbp(struct rt2x00_dev *rt2x00dev, + const unsigned long word, void *data) +{ + rt2x00_bbp_write(rt2x00dev, word, *((u8*)data)); +} + +static const struct rt2x00debug rt61pci_rt2x00debug = { + .owner = THIS_MODULE, + .mod_name = DRV_NAME, + .mod_version = DRV_VERSION, + .reg_csr = { + .read = rt61pci_read_csr, + .write = rt61pci_write_csr, + .word_size = sizeof(u32), + .word_count = CSR_REG_SIZE / sizeof(u32), + }, + .reg_eeprom = { + .read = rt61pci_read_eeprom, + .write = rt61pci_write_eeprom, + .word_size = sizeof(u16), + .word_count = EEPROM_SIZE / sizeof(u16), + }, + .reg_bbp = { + .read = rt61pci_read_bbp, + .write = rt61pci_write_bbp, + .word_size = sizeof(u8), + .word_count = BBP_SIZE / sizeof(u8), + }, +}; +#endif /* CONFIG_RT2X00_DEBUGFS */ + +/* + * Configuration handlers. + */ +static void rt61pci_config_bssid(struct rt2x00_dev *rt2x00dev, u8 *bssid) +{ + u32 reg[2]; + + memset(®, 0, sizeof(reg)); + memcpy(®, bssid, ETH_ALEN); + + rt2x00_set_field32(®[1], MAC_CSR5_BSS_ID_MASK, 3); + + /* + * The BSSID is passed to us as an array of bytes, + * that array is little endian, so no need for byte ordering. + */ + rt2x00_register_multiwrite(rt2x00dev, MAC_CSR4, ®, sizeof(reg)); +} + +static void rt61pci_config_promisc(struct rt2x00_dev *rt2x00dev, int promisc) +{ + u32 reg; + + rt2x00_register_read(rt2x00dev, TXRX_CSR0, ®); + + if (promisc) { + rt2x00_set_field32(®, TXRX_CSR0_DROP_NOT_TO_ME, 0); + SET_FLAG(rt2x00dev, INTERFACE_ENABLED_PROMISC); + } else { + rt2x00_set_field32(®, TXRX_CSR0_DROP_NOT_TO_ME, 1); + CLEAR_FLAG(rt2x00dev, INTERFACE_ENABLED_PROMISC); + } + + rt2x00_register_write(rt2x00dev, TXRX_CSR0, reg); +} + +static void rt61pci_config_type(struct rt2x00_dev *rt2x00dev, int type) +{ + u32 reg; + + /* + * Only continue when there is something to be done. + */ + if (!(GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED) ^ + GET_FLAG(rt2x00dev, INTERFACE_ENABLED)) && + !(GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED_MONITOR) ^ + GET_FLAG(rt2x00dev, INTERFACE_ENABLED_MONITOR))) + return; + + rt2x00_register_write(rt2x00dev, TXRX_CSR9, 0); + + /* + * Apply hardware packet filter. + */ + rt2x00_register_read(rt2x00dev, TXRX_CSR0, ®); + + if (!GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED_MONITOR) && + (type == IEEE80211_IF_TYPE_IBSS || type == IEEE80211_IF_TYPE_STA)) + rt2x00_set_field32(®, TXRX_CSR0_DROP_TO_DS, 1); + else + rt2x00_set_field32(®, TXRX_CSR0_DROP_TO_DS, 0); + + rt2x00_set_field32(®, TXRX_CSR0_DROP_CRC, 1); + if (GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED_MONITOR)) { + rt2x00_set_field32(®, TXRX_CSR0_DROP_PHYSICAL, 0); + rt2x00_set_field32(®, TXRX_CSR0_DROP_CONTROL, 0); + rt2x00_set_field32(®, TXRX_CSR0_DROP_VERSION_ERROR, 0); + } else { + rt2x00_set_field32(®, TXRX_CSR0_DROP_PHYSICAL, 1); + rt2x00_set_field32(®, TXRX_CSR0_DROP_CONTROL, 1); + rt2x00_set_field32(®, TXRX_CSR0_DROP_VERSION_ERROR, 1); + } + + rt2x00_set_field32(®, TXRX_CSR0_DROP_MULTICAST, 0); + rt2x00_set_field32(®, TXRX_CSR0_DROP_BORADCAST, 0); + + rt2x00_register_write(rt2x00dev, TXRX_CSR0, reg); + + /* + * Enable promisc mode when in monitor mode. + */ + if (GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED_MONITOR)) + rt61pci_config_promisc(rt2x00dev, 1); + + /* + * Enable synchronisation. + */ + rt2x00_register_read(rt2x00dev, TXRX_CSR9, ®); + if (GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED)) { + rt2x00_set_field32(®, TXRX_CSR9_BEACON_INTERVAL, 100 * 16); + rt2x00_set_field32(®, TXRX_CSR9_TSF_TICKING, 1); + rt2x00_set_field32(®, TXRX_CSR9_TBTT_ENABLE, 1); + } + + rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); + if (type == IEEE80211_IF_TYPE_IBSS || type == IEEE80211_IF_TYPE_AP) + rt2x00_set_field32(®, TXRX_CSR9_TSF_SYNC, 2); + else if (type == IEEE80211_IF_TYPE_STA) + rt2x00_set_field32(®, TXRX_CSR9_TSF_SYNC, 1); + else if (GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED_MONITOR) && + !GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED)) + rt2x00_set_field32(®, TXRX_CSR9_TSF_SYNC, 0); + + rt2x00_register_write(rt2x00dev, TXRX_CSR9, reg); + + /* + * Change flags of enabled interfaces. + */ + if (type != IEEE80211_IF_TYPE_MNTR) { + if (GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED)) + SET_FLAG(rt2x00dev, INTERFACE_ENABLED); + else + CLEAR_FLAG(rt2x00dev, INTERFACE_ENABLED); + } else { + if (GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED_MONITOR)) + SET_FLAG(rt2x00dev, INTERFACE_ENABLED_MONITOR); + else + CLEAR_FLAG(rt2x00dev, INTERFACE_ENABLED_MONITOR); + } +} + +static void rt61pci_config_channel(struct rt2x00_dev *rt2x00dev, + int value, int channel, int freq, int txpower) +{ + u8 reg = 0; + u32 rf1 = 0; + u32 rf2 = value; + u32 rf3 = 0; + u32 rf4 = 0; + + /* + * Only continue when there is something to be done. + */ + if (channel == rt2x00dev->rx_status.channel) + return; + + if (txpower == 0xff) + txpower = rt2x00dev->tx_power; + else + txpower = TXPOWER_TO_DEV(txpower); + + if (!GET_FLAG(rt2x00dev, CONFIG_RF_SEQUENCE) || channel <= 14) + rf1 = 0x00002ccc; + else if (channel == 36 || + (channel >= 100 && channel <= 116) || + channel >= 157) + rf1 = 0x00002cd4; + else + rf1 = 0x00002cd0; + + if (channel <= 14) { + rf3 = 0x00068455; + } else if (!GET_FLAG(rt2x00dev, CONFIG_RF_SEQUENCE)) { + if (channel >= 36 && channel <= 48) + rf3 = 0x0009be55; + else if (channel >= 52 && channel <= 64) + rf3 = 0x0009ae55; + else if (channel >= 100 && channel <= 112) + rf3 = 0x000bae55; + else + rf3 = 0x000bbe55; + } else { + switch (channel) { + case 36: + case 40: + case 44: + rf3 = 0x00098455; + break; + case 48: + rf3 = 0x00098655; + break; + case 52: + rf3 = 0x00098855; + break; + case 56: + rf3 = 0x00098c55; + + case 60: + rf3 = 0x00098e55; + break; + case 64: + rf3 = 0x00099255; + break; + case 100: + case 104: + case 108: + rf3 = 0x000b9855; + break; + case 112: + case 116: + case 120: + case 124: + rf3 = 0x000b9a55; + break; + case 128: + case 132: + rf3 = 0x000b9c55; + break; + case 136: + case 140: + rf3 = 0x000b9e55; + break; + case 149: + case 153: + case 157: + case 161: + case 165: + rf3 = 0x000ba255; + break; + } + } + + if (channel < 14) { + if (channel & 1) + rf4 = 0x000ffa0b; + else + rf4 = 0x000ffa1f; + } else if (channel == 14) { + rf4 = 0x000ffa13; + } else if (!GET_FLAG(rt2x00dev, CONFIG_RF_SEQUENCE)) { + switch (channel) { + case 36: + case 56: + case 116: + case 136: + rf4 = 0x000ffa23; + break; + case 40: + case 60: + case 100: + case 120: + case 140: + rf4 = 0x000ffa03; + break; + case 44: + case 64: + case 104: + case 124: + rf4 = 0x000ffa0b; + break; + case 48: + case 108: + case 128: + rf4 = 0x000ffa13; + break; + case 52: + case 112: + case 132: + rf4 = 0x000ffa1b; + break; + case 149: + rf4 = 0x000ffa1f; + break; + case 153: + rf4 = 0x000ffa27; + break; + case 157: + rf4 = 0x000ffa07; + break; + case 161: + rf4 = 0x000ffa0f; + break; + case 165: + rf4 = 0x000ffa17; + break; + } + } else { + switch (channel) { + case 36: + case 40: + case 60: + case 140: + case 100: + case 104: + case 108: + case 112: + case 116: + case 120: + rf4 = 0x000c0a03; + break; + case 44: + case 64: + case 124: + case 149: + rf4 = 0x000c0a1b; + break; + case 48: + case 128: + case 153: + rf4 = 0x000c0a0b; + break; + case 52: + case 132: + rf4 = 0x000c0a23; + break; + case 56: + case 136: + rf4 = 0x000c0a13; + break; + case 157: + case 161: + case 165: + rf4 = 0x000c0a17; + break; + } + } + + /* + * Set TXpower. + */ + rt2x00_set_field32(&rf3, RF3_TXPOWER, txpower); + + INFO("Switching channel. RF1: 0x%08x, RF2: 0x%08x, RF3: 0x%08x, " + "RF4: 0x%08x.\n", rf1, rf2, rf3, rf4); + + /* + * Set Frequency offset. + */ + rt2x00_set_field32(&rf4, RF4_FREQ_OFFSET, rt2x00dev->freq_offset); + + rt2x00_rf_write(rt2x00dev, rf1); + rt2x00_rf_write(rt2x00dev, rf2); + rt2x00_rf_write(rt2x00dev, rf3 & ~0x00000004); + rt2x00_rf_write(rt2x00dev, rf4); + + udelay(200); + + rt2x00_rf_write(rt2x00dev, rf1); + rt2x00_rf_write(rt2x00dev, rf2); + rt2x00_rf_write(rt2x00dev, rf3 | 0x00000004); + rt2x00_rf_write(rt2x00dev, rf4); + + udelay(200); + + rt2x00_rf_write(rt2x00dev, rf1); + rt2x00_rf_write(rt2x00dev, rf2); + rt2x00_rf_write(rt2x00dev, rf3 & ~0x00000004); + rt2x00_rf_write(rt2x00dev, rf4); + + rt2x00_bbp_read(rt2x00dev, 3, ®); + if (rt2x00_rf(&rt2x00dev->chip, RF5225) || + rt2x00_rf(&rt2x00dev->chip, RF2527)) + reg &= ~0x01; + else + reg |= 0x01; + rt2x00_bbp_write(rt2x00dev, 3, reg); + + msleep(1); + + /* + * Update active info for RX. + */ + rt2x00dev->rx_status.freq = freq; + rt2x00dev->rx_status.channel = channel; + + /* + * Update rf fields + */ + rt2x00dev->rf1 = rf1; + rt2x00dev->rf2 = rf2; + rt2x00dev->rf3 = rf3; + rt2x00dev->rf4 = rf4; + + rt2x00dev->tx_power = txpower; +} + +static void rt61pci_config_txpower(struct rt2x00_dev *rt2x00dev, int txpower) +{ + txpower = TXPOWER_TO_DEV(txpower); + + /* + * Only continue when there is something to be done. + */ + if (txpower == rt2x00dev->tx_power) + return; + + rt2x00_set_field32(&rt2x00dev->rf3, RF3_TXPOWER, txpower); + + rt2x00_rf_write(rt2x00dev, rt2x00dev->rf1); + rt2x00_rf_write(rt2x00dev, rt2x00dev->rf2); + rt2x00_rf_write(rt2x00dev, rt2x00dev->rf3 & ~0x00000004); + rt2x00_rf_write(rt2x00dev, rt2x00dev->rf4); + + udelay(200); + + rt2x00_rf_write(rt2x00dev, rt2x00dev->rf1); + rt2x00_rf_write(rt2x00dev, rt2x00dev->rf2); + rt2x00_rf_write(rt2x00dev, rt2x00dev->rf3 | 0x00000004); + rt2x00_rf_write(rt2x00dev, rt2x00dev->rf4); + + udelay(200); + + rt2x00_rf_write(rt2x00dev, rt2x00dev->rf1); + rt2x00_rf_write(rt2x00dev, rt2x00dev->rf2); + rt2x00_rf_write(rt2x00dev, rt2x00dev->rf3 & ~0x00000004); + rt2x00_rf_write(rt2x00dev, rt2x00dev->rf4); + + rt2x00dev->tx_power = txpower; +} + +static void rt61pci_config_antenna(struct rt2x00_dev *rt2x00dev, + int antenna_tx, int antenna_rx) +{ + u32 reg; + u8 reg_r3; + u8 reg_r4; + u8 reg_r77; + u8 frame_type; + + /* + * Only continue when there is something to be done. + */ + if (rt2x00dev->rx_status.antenna == antenna_rx) + return; + + rt2x00_register_read(rt2x00dev, PHY_CSR0, ®); + + if (rt2x00dev->curr_hwmode == HWMODE_A) { + if (GET_FLAG(rt2x00dev, CONFIG_EXTERNAL_LNA_A)) { + rt2x00_bbp_write(rt2x00dev, 17, 0x38); + rt2x00_bbp_write(rt2x00dev, 96, 0x78); + rt2x00_bbp_write(rt2x00dev, 104, 0x48); + rt2x00_bbp_write(rt2x00dev, 75, 0x80); + rt2x00_bbp_write(rt2x00dev, 86, 0x80); + rt2x00_bbp_write(rt2x00dev, 88, 0x80); + } else { + rt2x00_bbp_write(rt2x00dev, 17, 0x28); + rt2x00_bbp_write(rt2x00dev, 96, 0x58); + rt2x00_bbp_write(rt2x00dev, 104, 0x38); + rt2x00_bbp_write(rt2x00dev, 75, 0xfe); + rt2x00_bbp_write(rt2x00dev, 86, 0xfe); + rt2x00_bbp_write(rt2x00dev, 88, 0xfe); + } + rt2x00_bbp_write(rt2x00dev, 35, 0x60); + rt2x00_bbp_write(rt2x00dev, 97, 0x58); + rt2x00_bbp_write(rt2x00dev, 98, 0x58); + + rt2x00_set_field32(®, PHY_CSR0_PA_PE_BG, 0); + rt2x00_set_field32(®, PHY_CSR0_PA_PE_A, 1); + } else { + if (GET_FLAG(rt2x00dev, CONFIG_EXTERNAL_LNA_BG)) { + rt2x00_bbp_write(rt2x00dev, 17, 0x30); + rt2x00_bbp_write(rt2x00dev, 96, 0x68); + rt2x00_bbp_write(rt2x00dev, 104, 0x3c); + rt2x00_bbp_write(rt2x00dev, 75, 0x80); + rt2x00_bbp_write(rt2x00dev, 86, 0x80); + rt2x00_bbp_write(rt2x00dev, 88, 0x80); + } else { + rt2x00_bbp_write(rt2x00dev, 17, 0x20); + rt2x00_bbp_write(rt2x00dev, 96, 0x48); + rt2x00_bbp_write(rt2x00dev, 104, 0x2c); + rt2x00_bbp_write(rt2x00dev, 75, 0xfe); + rt2x00_bbp_write(rt2x00dev, 86, 0xfe); + rt2x00_bbp_write(rt2x00dev, 88, 0xfe); + } + rt2x00_bbp_write(rt2x00dev, 35, 0x50); + rt2x00_bbp_write(rt2x00dev, 97, 0x48); + rt2x00_bbp_write(rt2x00dev, 98, 0x48); + + rt2x00_set_field32(®, PHY_CSR0_PA_PE_BG, 1); + rt2x00_set_field32(®, PHY_CSR0_PA_PE_A, 0); + } + + rt2x00_register_write(rt2x00dev, PHY_CSR0, reg); + + rt2x00_bbp_read(rt2x00dev, 3, ®_r3); + rt2x00_bbp_read(rt2x00dev, 4, ®_r4); + rt2x00_bbp_read(rt2x00dev, 77, ®_r77); + + if (rt2x00_rf(&rt2x00dev->chip, RF5225) || + rt2x00_rf(&rt2x00dev->chip, RF2527)) + reg_r3 &= ~0x01; + reg_r4 &= ~0x23; + frame_type = ~(GET_FLAG(rt2x00dev, CONFIG_FRAME_TYPE) << 5); + + if (rt2x00_rf(&rt2x00dev->chip, RF5225) || + rt2x00_rf(&rt2x00dev->chip, RF5325)) { + if (antenna_rx == 0) { /* Diversity. */ + reg_r4 |= 0x02; + if (rt2x00dev->curr_hwmode != HWMODE_A) + reg_r4 |= 0x20; + } else if (antenna_rx == 1) { /* RX: Antenna A */ + reg_r4 |= 0x01; + if (rt2x00dev->curr_hwmode == HWMODE_A) + reg_r77 &= ~0x03; + else + reg_r77 |= 0x03; + rt2x00_bbp_write(rt2x00dev, 77, reg_r77); + } else if (antenna_rx == 2) { /* RX: Antenna B */ + reg_r4 |= 0x01; + if (rt2x00dev->curr_hwmode == HWMODE_A) + reg_r77 |= 0x03; + else + reg_r77 &= ~0x03; + rt2x00_bbp_write(rt2x00dev, 77, reg_r77); + } + } else if (rt2x00_rf(&rt2x00dev->chip, RF2527) || + (rt2x00_rf(&rt2x00dev->chip, RF2529) && + GET_FLAG(rt2x00dev, CONFIG_DOUBLE_ANTENNA))) { + if (antenna_rx == 0) { /* Diversity. */ + reg_r4 |= 0x22; + reg_r4 &= frame_type; + } else if (antenna_rx == 1) { /* RX: Antenna A */ + reg_r4 |= 0x21; + reg_r4 &= frame_type; + reg_r77 |= 0x03; + rt2x00_bbp_write(rt2x00dev, 77, reg_r77); + } else if (antenna_rx == 2) { /* RX: Antenna B */ + reg_r4 |= 0x21; + reg_r4 &= frame_type; + reg_r77 &= ~0x03; + rt2x00_bbp_write(rt2x00dev, 77, reg_r77); + } + } + + /* + * TODO: RF2529 with another antenna value than 2 are ignored. + * The legacy driver is unclear whether in those cases there is + * a possibility to switch antenna. + */ + + rt2x00_bbp_write(rt2x00dev, 3, reg_r3); + rt2x00_bbp_write(rt2x00dev, 4, reg_r4); + + /* + * Update active info for RX. + */ + rt2x00dev->rx_status.antenna = antenna_rx; +} + +static void rt61pci_config_duration(struct rt2x00_dev *rt2x00dev, + int short_slot_time) +{ + u32 reg; + + short_slot_time = short_slot_time ? SHORT_SLOT_TIME : SLOT_TIME; + + rt2x00_register_read(rt2x00dev, MAC_CSR9, ®); + rt2x00_set_field32(®, MAC_CSR9_SLOT_TIME, short_slot_time); + rt2x00_register_write(rt2x00dev, MAC_CSR9, reg); + + rt2x00_register_read(rt2x00dev, MAC_CSR8, ®); + rt2x00_set_field32(®, MAC_CSR8_SIFS, SIFS); + rt2x00_set_field32(®, MAC_CSR8_SIFS_AFTER_RX_OFDM, 3); + rt2x00_set_field32(®, MAC_CSR8_EIFS, EIFS); + rt2x00_register_write(rt2x00dev, MAC_CSR8, reg); + + rt2x00_register_read(rt2x00dev, TXRX_CSR0, ®); + rt2x00_set_field32(®, TXRX_CSR0_TSF_OFFSET, IEEE80211_HEADER); + rt2x00_register_write(rt2x00dev, TXRX_CSR0, reg); + + rt2x00_register_read(rt2x00dev, TXRX_CSR4, ®); + rt2x00_set_field32(®, TXRX_CSR4_AUTORESPOND_ENABLE, 1); + rt2x00_register_write(rt2x00dev, TXRX_CSR4, reg); +} + +static void rt61pci_config_rate(struct rt2x00_dev *rt2x00dev, const int rate) +{ + struct ieee80211_conf *conf = &rt2x00dev->hw->conf; + u32 reg; + u32 value; + u32 preamble; + + preamble = DEVICE_GET_RATE_FIELD(rate, PREAMBLE) + ? SHORT_PREAMBLE : PREAMBLE; + + /* + * Extract the allowed ratemask from the device specific rate value, + * We need to set TXRX_CSR5 to the basic rate mask so we need to mask + * off the non-basic rates. + */ + reg = DEVICE_GET_RATE_FIELD(rate, RATEMASK) & DEV_BASIC_RATE; + + rt2x00_register_write(rt2x00dev, TXRX_CSR5, reg); + + rt2x00_register_read(rt2x00dev, TXRX_CSR0, ®); + value = ((conf->flags & IEEE80211_CONF_SHORT_SLOT_TIME) ? + SHORT_DIFS : DIFS) + + PLCP + preamble + get_duration(ACK_SIZE, 10); + rt2x00_set_field32(®, TXRX_CSR0_RX_ACK_TIMEOUT, value); + rt2x00_register_write(rt2x00dev, TXRX_CSR0, reg); + + rt2x00_register_read(rt2x00dev, TXRX_CSR4, ®); + if (preamble == SHORT_PREAMBLE) + rt2x00_set_field32(®, TXRX_CSR4_AUTORESPOND_PREAMBLE, 1); + else + rt2x00_set_field32(®, TXRX_CSR4_AUTORESPOND_PREAMBLE, 0); + rt2x00_register_write(rt2x00dev, TXRX_CSR4, reg); +} + +static void rt61pci_config_phymode(struct rt2x00_dev *rt2x00dev, + const int phymode) +{ + struct ieee80211_hw_mode *mode; + struct ieee80211_rate *rate; + + /* + * Only continue when there is something to be done. + */ + if (rt2x00dev->rx_status.phymode == phymode) + return; + + if (phymode == MODE_IEEE80211A) + rt2x00dev->curr_hwmode = HWMODE_A; + else if (phymode == MODE_IEEE80211B) + rt2x00dev->curr_hwmode = HWMODE_B; + else + rt2x00dev->curr_hwmode = HWMODE_G; + + mode = &rt2x00dev->hwmodes[rt2x00dev->curr_hwmode]; + rate = &mode->rates[mode->num_rates - 1]; + + rt61pci_config_rate(rt2x00dev, rate->val2); + + /* + * Update physical mode for rx ring. + */ + rt2x00dev->rx_status.phymode = phymode; +} + +static void rt61pci_config_mac_addr(struct rt2x00_dev *rt2x00dev, u8 *addr) +{ + u32 reg[2]; + + memset(®, 0, sizeof(reg)); + memcpy(®, addr, ETH_ALEN); + + rt2x00_set_field32(®[1], MAC_CSR3_UNICAST_TO_ME_MASK, 0xff); + + /* + * The MAC address is passed to us as an array of bytes, + * that array is little endian, so no need for byte ordering. + */ + rt2x00_register_multiwrite(rt2x00dev, MAC_CSR2, ®, sizeof(reg)); +} + +/* + * Link tuning + */ +static void rt61pci_link_tuner(struct work_struct *work) +{ + struct rt2x00_dev *rt2x00dev = + container_of(work, struct rt2x00_dev, link.work.work); + u32 reg; + u32 rssi; + u8 reg_r17; + u8 up_bound; + u8 low_bound; + + /* + * Retrieve link quality. + */ + rssi = rt2x00_get_link_rssi(&rt2x00dev->link); + if (!rssi) + goto exit; + + /* + * Update LED. + */ + rt61pci_activity_led(rt2x00dev, rssi); + + /* + * Determine upper and lower limits for BBP17 register. + */ + if (rt2x00dev->rx_status.phymode == MODE_IEEE80211A) { + up_bound = 0x48; + low_bound = 0x28; + } else { + up_bound = 0x40; + low_bound = 0x20; + } + + rt2x00_bbp_read(rt2x00dev, 17, ®_r17); + + if (rssi >= 85) { + if (reg_r17 != 0x60) + rt2x00_bbp_write(rt2x00dev, 17, 0x60); + goto exit; + } else if (rssi >= 62) { + if (reg_r17 != up_bound) + rt2x00_bbp_write(rt2x00dev, 17, up_bound); + goto exit; + } else if (rssi >= 54) { + low_bound += 0x10; + if (reg_r17 != low_bound) + rt2x00_bbp_write(rt2x00dev, 17, low_bound); + goto exit; + } else if (rssi >= 46) { + low_bound += 0x08; + if (reg_r17 != low_bound) + rt2x00_bbp_write(rt2x00dev, 17, low_bound); + goto exit; + } else if (reg_r17 >= up_bound) { + rt2x00_bbp_write(rt2x00dev, 17, up_bound); + goto exit; + } + + rt2x00_register_read(rt2x00dev, STA_CSR1, ®); + reg = rt2x00_get_field32(reg, STA_CSR1_FALSE_CCA_ERROR); + + if (reg > 512 && reg_r17 < up_bound) + rt2x00_bbp_write(rt2x00dev, 17, ++reg_r17); + else if (reg < 100 && reg_r17 > low_bound) + rt2x00_bbp_write(rt2x00dev, 17, --reg_r17); + +exit: + if (reg_r17) + rt2x00_update_link_noise(&rt2x00dev->link, reg_r17); + + queue_delayed_work(rt2x00dev->workqueue, &rt2x00dev->link.work, + LINK_TUNE_INTERVAL); +} + +/* + * LED functions. + */ +static void rt61pci_enable_led(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + u16 led_reg; + u8 arg0; + u8 arg1; + + rt2x00_register_read(rt2x00dev, MAC_CSR14, ®); + rt2x00_set_field32(®, MAC_CSR14_ON_PERIOD, 70); + rt2x00_set_field32(®, MAC_CSR14_OFF_PERIOD, 30); + rt2x00_register_write(rt2x00dev, MAC_CSR14, reg); + + led_reg = rt2x00dev->led_reg; + rt2x00_set_field16(&led_reg, MCU_LEDCS_RADIO_STATUS, 1); + if (rt2x00dev->rx_status.phymode == MODE_IEEE80211A) + rt2x00_set_field16(&led_reg, MCU_LEDCS_LINK_A_STATUS, 1); + else + rt2x00_set_field16(&led_reg, MCU_LEDCS_LINK_BG_STATUS, 1); + + arg0 = led_reg & 0xff; + arg1 = (led_reg >> 8) & 0xff; + + rt2x00_mcu_request(rt2x00dev, MCU_LED, 0xff, arg0, arg1); +} + +static void rt61pci_disable_led(struct rt2x00_dev *rt2x00dev) +{ + u16 led_reg; + u8 arg0; + u8 arg1; + + led_reg = rt2x00dev->led_reg; + rt2x00_set_field16(&led_reg, MCU_LEDCS_RADIO_STATUS, 0); + rt2x00_set_field16(&led_reg, MCU_LEDCS_LINK_BG_STATUS, 0); + rt2x00_set_field16(&led_reg, MCU_LEDCS_LINK_A_STATUS, 0); + + arg0 = led_reg & 0xff; + arg1 = (led_reg >> 8) & 0xff; + + rt2x00_mcu_request(rt2x00dev, MCU_LED, 0xff, arg0, arg1); +} + +static void rt61pci_activity_led(struct rt2x00_dev *rt2x00dev, char rssi) +{ + u8 led; + + if (rt2x00dev->led_mode != LED_MODE_SIGNAL_STRENGTH) + return; + + if (rssi <= 30) + led = 0; + else if (rssi <= 39) + led = 1; + else if (rssi <= 49) + led = 2; + else if (rssi <= 53) + led = 3; + else if (rssi <= 63) + led = 4; + else + led = 5; + + rt2x00_mcu_request(rt2x00dev, MCU_LED_STRENGTH, 0xff, led, 0); +} + +/* + * Device state switch. + * This will put the device to sleep, or awake it. + */ +static int rt61pci_set_state(struct rt2x00_dev *rt2x00dev, + enum dev_state state) +{ + u32 reg; + unsigned int i; + char put_to_sleep; + char current_state; + + put_to_sleep = (state != STATE_AWAKE); + + rt2x00_register_read(rt2x00dev, MAC_CSR12, ®); + rt2x00_set_field32(®, MAC_CSR12_FORCE_WAKEUP, !put_to_sleep); + rt2x00_set_field32(®, MAC_CSR12_PUT_TO_SLEEP, put_to_sleep); + rt2x00_register_write(rt2x00dev, MAC_CSR12, reg); + + if (put_to_sleep) { + rt2x00_register_write(rt2x00dev, SOFT_RESET_CSR, 0x00000005); + rt2x00_register_write(rt2x00dev, IO_CNTL_CSR, 0x0000001c); + rt2x00_register_write(rt2x00dev, PCI_USEC_CSR, 0x00000060); + rt2x00_mcu_request(rt2x00dev, MCU_SLEEP, 0xff, 0x00, 0x00); + } else { + rt2x00_register_write(rt2x00dev, SOFT_RESET_CSR, 0x00000007); + rt2x00_register_write(rt2x00dev, IO_CNTL_CSR, 0x00000018); + rt2x00_register_write(rt2x00dev, PCI_USEC_CSR, 0x00000020); + rt2x00_mcu_request(rt2x00dev, MCU_WAKEUP, 0xff, 0x00, 0x00); + } + + /* + * Device is not guaranteed to be in the requested state yet. + * We must wait until the register indicates that the + * device has entered the correct state. + */ + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2x00_register_read(rt2x00dev, MAC_CSR12, ®); + current_state = rt2x00_get_field32(reg, + MAC_CSR12_BBP_CURRENT_STATE); + if (current_state == !put_to_sleep) + return 0; + msleep(10); + } + + NOTICE("Device failed to enter state %d, " + "current device state %d.\n", !put_to_sleep, current_state); + + return -EBUSY; +} + +/* + * Initialization functions. + */ +static int rt61pci_load_firmware(struct rt2x00_dev *rt2x00dev, + const struct firmware *fw) +{ + int i; + u32 reg; + u16 crc; + + /* + * Wait for stable hardware. + */ + for (i = 0; i < 100; i++) { + rt2x00_register_read(rt2x00dev, MAC_CSR0, ®); + if (reg) + break; + msleep(1); + } + + if (!reg) { + ERROR("Unstable hardware.\n"); + return -EBUSY; + } + + /* + * Prepare MCU and mailbox for firmware loading. + */ + reg = 0; + rt2x00_set_field32(®, MCU_CNTL_CSR_RESET, 1); + rt2x00_register_write(rt2x00dev, MCU_CNTL_CSR, reg); + rt2x00_register_write(rt2x00dev, M2H_CMD_DONE_CSR, 0xffffffff); + rt2x00_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0); + rt2x00_register_write(rt2x00dev, HOST_CMD_CSR, 0); + + /* + * Validate the firmware using 16 bit CRC. + * The last 2 bytes of the firmware are the CRC + * so substract those 2 bytes from the CRC checksum, + * and set those 2 bytes to 0 when calculating CRC. + */ + reg = 0; + crc = crc_itu_t(0, fw->data, fw->size - 2); + crc = crc_itu_t(crc, (u8*)®, 2); + + if (crc != (fw->data[fw->size - 2] << 8 | fw->data[fw->size - 1])) { + ERROR("Firmware CRC error.\n"); + return -EINVAL; + } + + rt2x00_set_chip_fw(&rt2x00dev->chip, + fw->data[fw->size - 4], fw->data[fw->size - 3]); + + /* + * Write firmware to device. + */ + reg = 0; + rt2x00_set_field32(®, MCU_CNTL_CSR_RESET, 1); + rt2x00_set_field32(®, MCU_CNTL_CSR_SELECT_BANK, 1); + rt2x00_register_write(rt2x00dev, MCU_CNTL_CSR, reg); + + rt2x00_register_multiwrite( + rt2x00dev, FIRMWARE_IMAGE_BASE, fw->data, fw->size); + + rt2x00_set_field32(®, MCU_CNTL_CSR_SELECT_BANK, 0); + rt2x00_register_write(rt2x00dev, MCU_CNTL_CSR, reg); + + rt2x00_set_field32(®, MCU_CNTL_CSR_RESET, 0); + rt2x00_register_write(rt2x00dev, MCU_CNTL_CSR, reg); + + for (i = 0; i < 100; i++) { + rt2x00_register_read(rt2x00dev, MCU_CNTL_CSR, ®); + if (rt2x00_get_field32(reg, MCU_CNTL_CSR_READY)) + break; + msleep(1); + } + + if (i == 100) { + ERROR("MCU Control register not ready.\n"); + return -EBUSY; + } + + /* + * Reset MAC and BBP registers. + */ + reg = 0; + rt2x00_set_field32(®, MAC_CSR1_SOFT_RESET, 1); + rt2x00_set_field32(®, MAC_CSR1_BBP_RESET, 1); + rt2x00_register_write(rt2x00dev, MAC_CSR1, reg); + + rt2x00_register_read(rt2x00dev, MAC_CSR1, ®); + rt2x00_set_field32(®, MAC_CSR1_SOFT_RESET, 0); + rt2x00_set_field32(®, MAC_CSR1_BBP_RESET, 0); + rt2x00_register_write(rt2x00dev, MAC_CSR1, reg); + + rt2x00_register_read(rt2x00dev, MAC_CSR1, ®); + rt2x00_set_field32(®, MAC_CSR1_HOST_READY, 1); + rt2x00_register_write(rt2x00dev, MAC_CSR1, reg); + + return 0; +} + +static void rt61pci_init_rxring(struct rt2x00_dev *rt2x00dev, + enum ring_index ring_type) +{ + struct data_ring *ring = &rt2x00dev->ring[ring_type]; + struct data_desc *rxd; + unsigned int i; + u32 word; + + memset(ring->data_addr, 0x00, rt2x00_get_ring_size(ring)); + + ring->type = ring_type; + + for (i = 0; i < ring->stats.limit; i++) { + rxd = ring->entry[i].priv; + + rt2x00_desc_read(rxd, 5, &word); + rt2x00_set_field32(&word, RXD_W5_BUFFER_PHYSICAL_ADDRESS, + ring->entry[i].data_dma); + rt2x00_desc_write(rxd, 5, word); + + rt2x00_desc_read(rxd, 0, &word); + rt2x00_set_field32(&word, RXD_W0_OWNER_NIC, 1); + rt2x00_desc_write(rxd, 0, word); + } + + rt2x00_ring_index_clear(ring); +} + +static void rt61pci_init_txring(struct rt2x00_dev *rt2x00dev, + enum ring_index ring_type) +{ + struct data_ring *ring = &rt2x00dev->ring[ring_type]; + struct data_desc *txd; + unsigned int i; + u32 word; + + memset(ring->data_addr, 0x00, rt2x00_get_ring_size(ring)); + + ring->type = ring_type; + + for (i = 0; i < ring->stats.limit; i++) { + txd = ring->entry[i].priv; + + rt2x00_desc_read(txd, 1, &word); + rt2x00_set_field32(&word, TXD_W1_BUFFER_COUNT, 1); + rt2x00_desc_write(txd, 1, word); + + rt2x00_desc_read(txd, 5, &word); + rt2x00_set_field32(&word, TXD_W5_PID_TYPE, ring_type); + rt2x00_set_field32(&word, TXD_W5_PID_SUBTYPE, i); + rt2x00_desc_write(txd, 5, word); + + rt2x00_desc_read(txd, 6, &word); + rt2x00_set_field32(&word, TXD_W6_BUFFER_PHYSICAL_ADDRESS, + ring->entry[i].data_dma); + rt2x00_desc_write(txd, 6, word); + + rt2x00_desc_read(txd, 0, &word); + rt2x00_set_field32(&word, TXD_W0_VALID, 0); + rt2x00_set_field32(&word, TXD_W0_OWNER_NIC, 0); + rt2x00_desc_write(txd, 0, word); + } + + rt2x00_ring_index_clear(ring); +} + +static int rt61pci_init_rings(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + /* + * Initialize rings. + */ + rt61pci_init_rxring(rt2x00dev, RING_RX); + rt61pci_init_txring(rt2x00dev, RING_AC_VO); + rt61pci_init_txring(rt2x00dev, RING_AC_VI); + rt61pci_init_txring(rt2x00dev, RING_AC_BE); + rt61pci_init_txring(rt2x00dev, RING_AC_BK); + rt61pci_init_txring(rt2x00dev, RING_PRIO); + rt61pci_init_txring(rt2x00dev, RING_BEACON); + + /* + * Initialize registers. + */ + reg = 0; + rt2x00_set_field32(®, TX_RING_CSR0_AC0_RING_SIZE, + rt2x00dev->ring[RING_AC_VO].stats.limit); + rt2x00_set_field32(®, TX_RING_CSR0_AC1_RING_SIZE, + rt2x00dev->ring[RING_AC_VI].stats.limit); + rt2x00_set_field32(®, TX_RING_CSR0_AC2_RING_SIZE, + rt2x00dev->ring[RING_AC_BE].stats.limit); + rt2x00_set_field32(®, TX_RING_CSR0_AC3_RING_SIZE, + rt2x00dev->ring[RING_AC_BK].stats.limit); + rt2x00_register_write(rt2x00dev, TX_RING_CSR0, reg); + + reg = 0; + rt2x00_set_field32(®, TX_RING_CSR1_MGMT_RING_SIZE, + rt2x00dev->ring[RING_PRIO].stats.limit); + rt2x00_set_field32(®, TX_RING_CSR1_TXD_SIZE, + rt2x00dev->ring[RING_AC_VO].desc_size / 4); + rt2x00_register_write(rt2x00dev, TX_RING_CSR1, reg); + + reg = 0; + rt2x00_set_field32(®, AC0_BASE_CSR_RING_REGISTER, + rt2x00dev->ring[RING_AC_VO].data_dma); + rt2x00_register_write(rt2x00dev, AC0_BASE_CSR, reg); + + reg = 0; + rt2x00_set_field32(®, AC1_BASE_CSR_RING_REGISTER, + rt2x00dev->ring[RING_AC_VI].data_dma); + rt2x00_register_write(rt2x00dev, AC1_BASE_CSR, reg); + + reg = 0; + rt2x00_set_field32(®, AC2_BASE_CSR_RING_REGISTER, + rt2x00dev->ring[RING_AC_BE].data_dma); + rt2x00_register_write(rt2x00dev, AC2_BASE_CSR, reg); + + reg = 0; + rt2x00_set_field32(®, AC3_BASE_CSR_RING_REGISTER, + rt2x00dev->ring[RING_AC_BK].data_dma); + rt2x00_register_write(rt2x00dev, AC3_BASE_CSR, reg); + + reg = 0; + rt2x00_set_field32(®, MGMT_BASE_CSR_RING_REGISTER, + rt2x00dev->ring[RING_PRIO].data_dma); + rt2x00_register_write(rt2x00dev, MGMT_BASE_CSR, reg); + + reg = 0; + rt2x00_set_field32(®, RX_RING_CSR_RING_SIZE, + rt2x00dev->ring[RING_RX].stats.limit); + rt2x00_set_field32(®, RX_RING_CSR_RXD_SIZE, + rt2x00dev->ring[RING_RX].desc_size / 4); + rt2x00_set_field32(®, RX_RING_CSR_RXD_WRITEBACK_SIZE, 4); + rt2x00_register_write(rt2x00dev, RX_RING_CSR, reg); + + reg = 0; + rt2x00_set_field32(®, RX_BASE_CSR_RING_REGISTER, + rt2x00dev->ring[RING_RX].data_dma); + rt2x00_register_write(rt2x00dev, RX_BASE_CSR, reg); + + rt2x00_register_write(rt2x00dev, TX_DMA_DST_CSR, 0x000000aa); + rt2x00_register_write(rt2x00dev, LOAD_TX_RING_CSR, 0x0000001f); + rt2x00_register_write(rt2x00dev, RX_CNTL_CSR, 0x00000002); + + return 0; +} + +static int rt61pci_init_registers(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + if (rt61pci_set_state(rt2x00dev, STATE_AWAKE)) + return -EBUSY; + + rt2x00_register_write(rt2x00dev, MAC_CSR10, 0x00000718); + + rt2x00_register_write(rt2x00dev, TXRX_CSR0, 0x025eb032); + + rt2x00_register_write(rt2x00dev, TXRX_CSR1, 0x9eb39eb3); + rt2x00_register_write(rt2x00dev, TXRX_CSR2, 0x8a8b8c8d); + rt2x00_register_write(rt2x00dev, TXRX_CSR3, 0x00858687); + + rt2x00_register_write(rt2x00dev, TXRX_CSR7, 0x2e31353b); + rt2x00_register_write(rt2x00dev, TXRX_CSR8, 0x2a2a2a2c); + + rt2x00_register_write(rt2x00dev, TXRX_CSR15, 0x0000000f); + + rt2x00_register_write(rt2x00dev, MAC_CSR6, 0x00000fff); + + rt2x00_register_write(rt2x00dev, MAC_CSR13, 0x0000e000); + + rt2x00_register_write(rt2x00dev, SEC_CSR0, 0x00000000); + rt2x00_register_write(rt2x00dev, SEC_CSR1, 0x00000000); + rt2x00_register_write(rt2x00dev, SEC_CSR5, 0x00000000); + + rt2x00_register_read(rt2x00dev, AC_TXOP_CSR0, ®); + rt2x00_set_field32(®, AC_TXOP_CSR0_AC0_TX_OP, 0); + rt2x00_set_field32(®, AC_TXOP_CSR0_AC1_TX_OP, 0); + rt2x00_register_write(rt2x00dev, AC_TXOP_CSR0, reg); + + rt2x00_register_read(rt2x00dev, AC_TXOP_CSR1, ®); + rt2x00_set_field32(®, AC_TXOP_CSR1_AC2_TX_OP, 192); + rt2x00_set_field32(®, AC_TXOP_CSR1_AC3_TX_OP, 48); + rt2x00_register_write(rt2x00dev, AC_TXOP_CSR1, reg); + + rt2x00_register_read(rt2x00dev, MAC_CSR9, ®); + rt2x00_set_field32(®, MAC_CSR9_CW_SELECT, 0); + rt2x00_register_write(rt2x00dev, MAC_CSR9, reg); + + rt2x00_register_read(rt2x00dev, TXRX_CSR0, ®); + rt2x00_set_field32(®, TXRX_CSR0_AUTO_TX_SEQ, 1); + rt2x00_register_write(rt2x00dev, TXRX_CSR0, reg); + + rt2x00_register_write(rt2x00dev, PHY_CSR1, 0x000023b0); + rt2x00_register_write(rt2x00dev, PHY_CSR5, 0x060a100c); + rt2x00_register_write(rt2x00dev, PHY_CSR6, 0x00080606); + rt2x00_register_write(rt2x00dev, PHY_CSR7, 0x00000a08); + + rt2x00_register_write(rt2x00dev, PCI_CFG_CSR, 0x28ca4404); + + rt2x00_register_write(rt2x00dev, TEST_MODE_CSR, 0x00000200); + + rt2x00_register_write(rt2x00dev, M2H_CMD_DONE_CSR, 0xffffffff); + + /* + * We must clear the error counters. + * These registers are cleared on read, + * so we may pass a useless variable to store the value. + */ + rt2x00_register_read(rt2x00dev, STA_CSR0, ®); + rt2x00_register_read(rt2x00dev, STA_CSR1, ®); + rt2x00_register_read(rt2x00dev, STA_CSR2, ®); + + /* + * Reset MAC and BBP registers. + */ + reg = 0; + rt2x00_set_field32(®, MAC_CSR1_SOFT_RESET, 1); + rt2x00_set_field32(®, MAC_CSR1_BBP_RESET, 1); + rt2x00_register_write(rt2x00dev, MAC_CSR1, reg); + + rt2x00_register_read(rt2x00dev, MAC_CSR1, ®); + rt2x00_set_field32(®, MAC_CSR1_SOFT_RESET, 0); + rt2x00_set_field32(®, MAC_CSR1_BBP_RESET, 0); + rt2x00_register_write(rt2x00dev, MAC_CSR1, reg); + + rt2x00_register_read(rt2x00dev, MAC_CSR1, ®); + rt2x00_set_field32(®, MAC_CSR1_HOST_READY, 1); + rt2x00_register_write(rt2x00dev, MAC_CSR1, reg); + + return 0; +} + +static int rt61pci_init_bbp(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + u16 eeprom; + u8 reg_id; + u8 value; + + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2x00_bbp_read(rt2x00dev, 0, &value); + if ((value != 0xff) && (value != 0x00)) + goto continue_csr_init; + NOTICE("Waiting for BBP register.\n"); + udelay(REGISTER_BUSY_DELAY); + } + + ERROR("BBP register access failed, aborting.\n"); + return -EACCES; + +continue_csr_init: + rt2x00_bbp_write(rt2x00dev, 3, 0x00); + rt2x00_bbp_write(rt2x00dev, 15, 0x30); + rt2x00_bbp_write(rt2x00dev, 17, 0x20); + rt2x00_bbp_write(rt2x00dev, 21, 0xc8); + rt2x00_bbp_write(rt2x00dev, 22, 0x38); + rt2x00_bbp_write(rt2x00dev, 23, 0x06); + rt2x00_bbp_write(rt2x00dev, 24, 0xfe); + rt2x00_bbp_write(rt2x00dev, 25, 0x0a); + rt2x00_bbp_write(rt2x00dev, 26, 0x0d); + rt2x00_bbp_write(rt2x00dev, 34, 0x12); + rt2x00_bbp_write(rt2x00dev, 37, 0x07); + rt2x00_bbp_write(rt2x00dev, 39, 0xf8); + rt2x00_bbp_write(rt2x00dev, 41, 0x60); + rt2x00_bbp_write(rt2x00dev, 53, 0x10); + rt2x00_bbp_write(rt2x00dev, 54, 0x18); + rt2x00_bbp_write(rt2x00dev, 60, 0x10); + rt2x00_bbp_write(rt2x00dev, 61, 0x04); + rt2x00_bbp_write(rt2x00dev, 62, 0x04); + rt2x00_bbp_write(rt2x00dev, 75, 0xfe); + rt2x00_bbp_write(rt2x00dev, 86, 0xfe); + rt2x00_bbp_write(rt2x00dev, 88, 0xfe); + rt2x00_bbp_write(rt2x00dev, 90, 0x0f); + rt2x00_bbp_write(rt2x00dev, 99, 0x00); + rt2x00_bbp_write(rt2x00dev, 102, 0x16); + rt2x00_bbp_write(rt2x00dev, 107, 0x04); + + DEBUG("Start initialization from EEPROM...\n"); + for (i = 0; i < EEPROM_BBP_SIZE; i++) { + rt2x00_eeprom_read(rt2x00dev, EEPROM_BBP_START + i, &eeprom); + + if (eeprom != 0xffff && eeprom != 0x0000) { + reg_id = rt2x00_get_field16(eeprom, EEPROM_BBP_REG_ID); + value = rt2x00_get_field16(eeprom, EEPROM_BBP_VALUE); + DEBUG("BBP: 0x%02x, value: 0x%02x.\n", reg_id, value); + rt2x00_bbp_write(rt2x00dev, reg_id, value); + } + } + DEBUG("...End initialization from EEPROM.\n"); + + return 0; +} + +/* + * Radio control functions. + */ +static void rt61pci_toggle_rx(struct rt2x00_dev *rt2x00dev, int enable) +{ + u32 reg; + + rt2x00_register_read(rt2x00dev, TXRX_CSR0, ®); + rt2x00_set_field32(®, TXRX_CSR0_DISABLE_RX, !enable); + rt2x00_register_write(rt2x00dev, TXRX_CSR0, reg); +} + +static int rt61pci_enable_radio(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + /* + * Initialize all registers. + */ + if (rt61pci_init_rings(rt2x00dev) || + rt61pci_init_registers(rt2x00dev) || + rt61pci_init_bbp(rt2x00dev)) { + ERROR("Register initialization failed.\n"); + return -EIO; + } + + /* + * Clear interrupts. + */ + rt2x00_register_read(rt2x00dev, INT_SOURCE_CSR, ®); + rt2x00_register_write(rt2x00dev, INT_SOURCE_CSR, reg); + + rt2x00_register_read(rt2x00dev, MCU_INT_SOURCE_CSR, ®); + rt2x00_register_write(rt2x00dev, MCU_INT_SOURCE_CSR, reg); + + /* + * Enable interrupts. + */ + reg = 0; + rt2x00_set_field32(®, INT_MASK_CSR_TX_ABORT_DONE, 1); + rt2x00_set_field32(®, INT_MASK_CSR_MITIGATION_PERIOD, 0xff); + rt2x00_register_write(rt2x00dev, INT_MASK_CSR, reg); + + rt2x00_register_write(rt2x00dev, MCU_INT_MASK_CSR, 0x00000000); + + /* + * Enable RX. + */ + rt2x00_register_write(rt2x00dev, RX_CNTL_CSR, 0x00000001); + + /* + * Enable LED + */ + rt61pci_enable_led(rt2x00dev); + + return 0; +} + +static void rt61pci_disable_radio(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + /* + * Disable LED + */ + rt61pci_disable_led(rt2x00dev); + + rt2x00_register_write(rt2x00dev, MAC_CSR10, 0x00001818); + + /* + * Disable synchronisation. + */ + rt2x00_register_write(rt2x00dev, TXRX_CSR9, 0); + + /* + * Cancel RX and TX. + */ + rt2x00_register_read(rt2x00dev, TX_CNTL_CSR, ®); + rt2x00_set_field32(®, TX_CNTL_CSR_ABORT_TX_AC0, 1); + rt2x00_set_field32(®, TX_CNTL_CSR_ABORT_TX_AC1, 1); + rt2x00_set_field32(®, TX_CNTL_CSR_ABORT_TX_AC2, 1); + rt2x00_set_field32(®, TX_CNTL_CSR_ABORT_TX_AC3, 1); + rt2x00_set_field32(®, TX_CNTL_CSR_ABORT_TX_MGMT, 1); + rt2x00_register_write(rt2x00dev, TX_CNTL_CSR, reg); + + /* + * Disable interrupts. + */ + reg = 0xffffffff; + rt2x00_set_field32(®, INT_MASK_CSR_ENABLE_MITIGATION, 0); + rt2x00_register_write(rt2x00dev, INT_MASK_CSR, reg); + + rt2x00_register_write(rt2x00dev, MCU_INT_MASK_CSR, 0xffffffff); +} + +/* + * TX descriptor initialization + */ +static void rt61pci_write_tx_desc(struct rt2x00_dev *rt2x00dev, + struct data_entry *entry, struct data_desc *txd, + struct data_entry_desc *desc, struct ieee80211_hdr *ieee80211hdr, + unsigned int length, struct ieee80211_tx_control *control) +{ + u32 word; + + /* + * Start writing the descriptor words. + */ + rt2x00_desc_read(txd, 1, &word); + rt2x00_set_field32(&word, TXD_W1_HOST_Q_ID, desc->queue); + rt2x00_set_field32(&word, TXD_W1_AIFSN, entry->ring->tx_params.aifs); + rt2x00_set_field32(&word, TXD_W1_CWMIN, entry->ring->tx_params.cw_min); + rt2x00_set_field32(&word, TXD_W1_CWMAX, entry->ring->tx_params.cw_max); + rt2x00_set_field32(&word, TXD_W1_IV_OFFSET, IEEE80211_HEADER); + rt2x00_set_field32(&word, TXD_W1_HW_SEQUENCE, 1); + rt2x00_desc_write(txd, 1, word); + + rt2x00_desc_read(txd, 2, &word); + rt2x00_set_field32(&word, TXD_W2_PLCP_SIGNAL, desc->signal); + rt2x00_set_field32(&word, TXD_W2_PLCP_SERVICE, desc->service); + rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_LOW, desc->length_low); + rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_HIGH, desc->length_high); + rt2x00_desc_write(txd, 2, word); + + rt2x00_desc_read(txd, 5, &word); + rt2x00_set_field32(&word, TXD_W5_TX_POWER, + TXPOWER_TO_DEV(control->power_level)); + rt2x00_set_field32(&word, TXD_W5_WAITING_DMA_DONE_INT, 1); + rt2x00_desc_write(txd, 5, word); + + rt2x00_desc_read(txd, 11, &word); + rt2x00_set_field32(&word, TXD_W11_BUFFER_LENGTH0, length); + rt2x00_desc_write(txd, 11, word); + + rt2x00_desc_read(txd, 0, &word); + rt2x00_set_field32(&word, TXD_W0_OWNER_NIC, 1); + rt2x00_set_field32(&word, TXD_W0_VALID, 1); + rt2x00_set_field32(&word, TXD_W0_MORE_FRAG, + !!(entry->reg & ENTRY_TXD_MORE_FRAG)); + rt2x00_set_field32(&word, TXD_W0_ACK, + !!(entry->reg & ENTRY_TXD_REQ_ACK)); + rt2x00_set_field32(&word, TXD_W0_TIMESTAMP, + !!(entry->reg & ENTRY_TXD_REQ_TIMESTAMP)); + rt2x00_set_field32(&word, TXD_W0_OFDM, + !!(entry->reg & ENTRY_TXD_OFDM_RATE)); + rt2x00_set_field32(&word, TXD_W0_IFS, desc->ifs); + rt2x00_set_field32(&word, TXD_W0_RETRY_MODE, 0); + rt2x00_set_field32(&word, TXD_W0_TKIP_MIC, 0); + rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, length); + rt2x00_set_field32(&word, TXD_W0_CIPHER_ALG, CIPHER_NONE); + rt2x00_desc_write(txd, 0, word); +} + +/* + * TX data initialization + */ +static void rt61pci_kick_tx_queue(struct rt2x00_dev *rt2x00dev, int queue) +{ + u32 reg; + + rt2x00_register_read(rt2x00dev, TX_CNTL_CSR, ®); + if (queue == IEEE80211_TX_QUEUE_DATA0) + rt2x00_set_field32(®, TX_CNTL_CSR_KICK_TX_AC0, 1); + else if (queue == IEEE80211_TX_QUEUE_DATA1) + rt2x00_set_field32(®, TX_CNTL_CSR_KICK_TX_AC1, 1); + else if (queue == IEEE80211_TX_QUEUE_DATA2) + rt2x00_set_field32(®, TX_CNTL_CSR_KICK_TX_AC2, 1); + else if (queue == IEEE80211_TX_QUEUE_DATA3) + rt2x00_set_field32(®, TX_CNTL_CSR_KICK_TX_AC3, 1); + else if (queue == IEEE80211_TX_QUEUE_DATA4) + rt2x00_set_field32(®, TX_CNTL_CSR_KICK_TX_MGMT, 1); + rt2x00_register_write(rt2x00dev, TX_CNTL_CSR, reg); +} + +static void rt61pci_kick_beacon_gen(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + rt2x00_register_read(rt2x00dev, TXRX_CSR9, ®); + if (!rt2x00_get_field32(reg, TXRX_CSR9_BEACON_GEN)) { + rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 1); + rt2x00_register_write(rt2x00dev, TXRX_CSR9, reg); + } +} + +/* + * Interrupt functions. + */ +static void rt61pci_rxdone(struct rt2x00_dev *rt2x00dev, int queue) +{ + struct data_ring *ring = &rt2x00dev->ring[queue]; + struct data_entry *entry; + struct data_desc *rxd; + u32 word0; + u32 word1; + int signal; + int rssi; + int ofdm; + u16 size; + + while (1) { + entry = rt2x00_get_data_entry(ring); + rxd = entry->priv; + rt2x00_desc_read(rxd, 0, &word0); + rt2x00_desc_read(rxd, 1, &word1); + + if (rt2x00_get_field32(word0, RXD_W0_OWNER_NIC)) + break; + + /* + * TODO: Don't we need to keep statistics + * updated about events like CRC and physical errors? + */ + if (rt2x00_get_field32(word0, RXD_W0_CRC)) + goto skip_entry; + + /* + * Obtain the status about this packet. + */ + size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT); + signal = rt2x00_get_field32(word1, RXD_W1_SIGNAL); + rssi = rt2x00_get_field32(word1, RXD_W1_RSSI); + ofdm = rt2x00_get_field32(word0, RXD_W0_OFDM); + + /* + * Send the packet to upper layer. + */ + rt2x00lib_rxdone(entry, entry->data_addr, size, + signal, rssi, ofdm); + +skip_entry: + if (GET_FLAG(ring->rt2x00dev, DEVICE_ENABLED_RADIO)) { + rt2x00_set_field32(&word0, RXD_W0_OWNER_NIC, 1); + rt2x00_desc_write(rxd, 0, word0); + } + + rt2x00_ring_index_inc(ring); + } +} + +static void rt61pci_txdone(struct rt2x00_dev *rt2x00dev) +{ + struct data_ring *ring; + struct data_entry *entry; + struct data_desc *txd; + u32 word; + u32 reg; + int index; + int tx_status; + int retry; + + while (1) { + rt2x00_register_read(rt2x00dev, STA_CSR4, ®); + if (!rt2x00_get_field32(reg, STA_CSR4_VALID)) + break; + + /* + * Skip this entry when it contains an invalid + * ring identication number. + */ + ring = rt2x00_get_ring(rt2x00dev, + rt2x00_get_field32(reg, STA_CSR4_PID_TYPE)); + if (unlikely(!ring)) + continue; + + /* + * Skip this entry when it contains an invalid + * index number. + */ + index = rt2x00_get_field32(reg, STA_CSR4_PID_SUBTYPE); + if (unlikely(index >= ring->stats.limit)) + continue; + + entry = &ring->entry[index]; + txd = entry->priv; + rt2x00_desc_read(txd, 0, &word); + + if (rt2x00_get_field32(word, TXD_W0_OWNER_NIC) || + !rt2x00_get_field32(word, TXD_W0_VALID)) + return; + + /* + * Obtain the status about this packet. + */ + tx_status = rt2x00_get_field32(entry->reg, STA_CSR4_TX_RESULT); + retry = rt2x00_get_field32(entry->reg, STA_CSR4_RETRY_COUNT); + + rt2x00lib_txdone(entry, tx_status, retry); + + /* + * Make this entry available for reuse. + */ + entry->reg = 0; + rt2x00_set_field32(&word, TXD_W0_VALID, 0); + rt2x00_desc_write(txd, 0, word); + rt2x00_ring_index_done_inc(entry->ring); + + /* + * If the data ring was full before the txdone handler + * we must make sure the packet queue in the mac80211 stack + * is reenabled when the txdone handler has finished. + */ + if (!rt2x00_ring_full(ring)) + ieee80211_wake_queue(rt2x00dev->hw, + entry->tx_status.control.queue); + } +} + +static irqreturn_t rt61pci_interrupt(int irq, void *dev_instance) +{ + struct rt2x00_dev *rt2x00dev = dev_instance; + u32 reg; + + /* + * Get the interrupt sources & saved to local variable. + * Write register value back to clear pending interrupts. + */ + rt2x00_register_read(rt2x00dev, MCU_INT_SOURCE_CSR, ®); + rt2x00_register_write(rt2x00dev, MCU_INT_SOURCE_CSR, reg); + + rt2x00_register_read(rt2x00dev, INT_SOURCE_CSR, ®); + rt2x00_register_write(rt2x00dev, INT_SOURCE_CSR, reg); + + if (!reg) + return IRQ_NONE; + + if (!GET_FLAG(rt2x00dev, DEVICE_ENABLED_RADIO)) + return IRQ_HANDLED; + + /* + * Handle interrupts, walk through all bits + * and run the tasks, the bits are checked in order of + * priority. + */ + + /* + * 1 - Beacon timer expired interrupt. + */ + if (rt2x00_get_field32(reg, INT_SOURCE_CSR_BEACON_DONE)) + rt2x00pci_beacondone(rt2x00dev, RING_BEACON); + + /* + * 2 - Rx ring done interrupt. + */ + if (rt2x00_get_field32(reg, INT_SOURCE_CSR_RXDONE)) + rt61pci_rxdone(rt2x00dev, RING_RX); + + /* + * 3 - Tx ring done interrupt. + */ + if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TXDONE)) + rt61pci_txdone(rt2x00dev); + + return IRQ_HANDLED; +} + +/* + * Device initialization functions. + */ +static int rt61pci_alloc_eeprom(struct rt2x00_dev *rt2x00dev) +{ + struct eeprom_93cx6 eeprom; + u32 reg; + + /* + * Allocate the eeprom memory, check the eeprom width + * and copy the entire eeprom into this allocated memory. + */ + rt2x00dev->eeprom = kzalloc(EEPROM_SIZE, GFP_KERNEL); + if (!rt2x00dev->eeprom) + return -ENOMEM; + + rt2x00_register_read(rt2x00dev, E2PROM_CSR, ®); + + eeprom.data = rt2x00dev; + eeprom.register_read = rt61pci_eepromregister_read; + eeprom.register_write = rt61pci_eepromregister_write; + eeprom.width = rt2x00_get_field32(reg, E2PROM_CSR_TYPE_93C46) ? + PCI_EEPROM_WIDTH_93C46 : PCI_EEPROM_WIDTH_93C66; + eeprom.reg_data_in = 0; + eeprom.reg_data_out = 0; + eeprom.reg_data_clock = 0; + eeprom.reg_chip_select = 0; + + eeprom_93cx6_multiread(&eeprom, EEPROM_BASE, rt2x00dev->eeprom, + EEPROM_SIZE / sizeof(u16)); + + return 0; +} + +static int rt61pci_init_eeprom(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + u16 value; + u16 eeprom; + u16 device; + + /* + * Read EEPROM word for configuration. + */ + rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &eeprom); + + /* + * Identify RF chipset. + * To determine the RT chip we have to read the + * PCI header of the device. + */ + pci_read_config_word(rt2x00dev_pci(rt2x00dev), + PCI_CONFIG_HEADER_DEVICE, &device); + value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE); + rt2x00_register_read(rt2x00dev, MAC_CSR0, ®); + rt2x00_set_chip(&rt2x00dev->chip, device, value, reg); + + if (!rt2x00_rf(&rt2x00dev->chip, RF5225) && + !rt2x00_rf(&rt2x00dev->chip, RF5325) && + !rt2x00_rf(&rt2x00dev->chip, RF2527) && + !rt2x00_rf(&rt2x00dev->chip, RF2529)) { + ERROR("Invalid RF chipset detected.\n"); + return -ENODEV; + } + + /* + * Identify default antenna configuration. + */ + rt2x00dev->hw->conf.antenna_sel_tx = rt2x00_get_field16(eeprom, + EEPROM_ANTENNA_TX_DEFAULT); + rt2x00dev->hw->conf.antenna_sel_rx = rt2x00_get_field16(eeprom, + EEPROM_ANTENNA_RX_DEFAULT); + + /* + * Read the Frame type. + */ + if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_FRAME_TYPE)) + SET_FLAG(rt2x00dev, CONFIG_FRAME_TYPE); + + /* + * Determine number of antenna's. + */ + if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_NUM) == 2) + SET_FLAG(rt2x00dev, CONFIG_DOUBLE_ANTENNA); + + /* + * Detect if this device has an hardware controlled radio. + */ + if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO)) + SET_FLAG(rt2x00dev, DEVICE_SUPPORT_HW_BUTTON); + + /* + * Read frequency offset and RF programming sequence. + */ + rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &eeprom); + if (rt2x00_get_field16(eeprom, EEPROM_FREQ_SEQ_MASK) != 0xff && + rt2x00_get_field16(eeprom, EEPROM_FREQ_SEQ)) + SET_FLAG(rt2x00dev, CONFIG_RF_SEQUENCE); + + rt2x00dev->freq_offset = rt2x00_get_field16(eeprom, + EEPROM_FREQ_OFFSET); + if (rt2x00dev->freq_offset == 0xff) + rt2x00dev->freq_offset = 0; + + /* + * Read external LNA informations. + */ + rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &eeprom); + if (eeprom == 0xffff) + eeprom = 0; + if (rt2x00_get_field16(eeprom, EEPROM_NIC_EXTERNAL_LNA_A)) + SET_FLAG(rt2x00dev, CONFIG_EXTERNAL_LNA_A); + if (rt2x00_get_field16(eeprom, EEPROM_NIC_EXTERNAL_LNA_BG)) + SET_FLAG(rt2x00dev, CONFIG_EXTERNAL_LNA_BG); + + /* + * Store led settings, for correct led behaviour. + * If the eeprom value is invalid, + * switch to default led mode. + */ + rt2x00_eeprom_read(rt2x00dev, EEPROM_LED, &eeprom); + if (eeprom == 0xffff) + rt2x00dev->led_mode = LED_MODE_DEFAULT; + else + rt2x00dev->led_mode = rt2x00_get_field16(eeprom, + EEPROM_LED_LED_MODE); + + rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_LED_MODE, + rt2x00dev->led_mode); + rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_POLARITY_GPIO_0, + rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_GPIO_0)); + rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_POLARITY_GPIO_1, + rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_GPIO_1)); + rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_POLARITY_GPIO_2, + rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_GPIO_2)); + rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_POLARITY_GPIO_3, + rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_GPIO_3)); + rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_POLARITY_GPIO_4, + rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_GPIO_4)); + rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_POLARITY_ACT, + rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_ACT)); + rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_POLARITY_READY_BG, + rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_RDY_G)); + rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_POLARITY_READY_A, + rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_RDY_A)); + + return 0; +} + +/* + * RF value list for RF5225, RF5325, RF2527 & RF2529 + * Supports: 2.4 GHz + */ +static const u32 rf_vals_bg[] = { + 0x00004786, 0x00004786, 0x0000478a, 0x0000478a, 0x0000478e, + 0x0000478e, 0x00004792, 0x00004792, 0x00004796, 0x00004796, + 0x0000479a, 0x0000479a, 0x0000479e, 0x000047a2 +}; + +/* + * RF value list for RF5225 & RF5325 (supplement to vals_bg) + * Supports: 5.2 GHz, rf_sequence disabled + */ +static const u32 rf_vals_a_5x_noseq[] = { + 0x0000499a, 0x000049a2, 0x000049a6, 0x000049aa, 0x000049ae, + 0x000049b2, 0x000049ba, 0x000049be, 0x00004a2a, 0x00004a2e, + 0x00004a32, 0x00004a36, 0x00004a3a, 0x00004a82, 0x00004a86, + 0x00004a8a, 0x00004a8e, 0x00004a92, 0x00004a9a, 0x00004aa2, + 0x00004aa6, 0x00004aae, 0x00004ab2, 0x00004ab6 +}; + +/* + * RF value list for RF5225 & RF5325 (supplement to vals_bg) + * Supports: 5.2 GHz, rf_sequence enabled + */ +static const u32 rf_vals_a_5x_seq[] = { + 0x0004481a, 0x00044682, 0x00044686, 0x0004468e, 0x00044692, + 0x0004469a, 0x000446a2, 0x000446a6, 0x0004489a, 0x000448a2, + 0x000448aa, 0x000448b2, 0x000448ba, 0x00044702, 0x00044706, + 0x0004470e, 0x00044712, 0x0004471a, 0x00044722, 0x0004472e, + 0x00044736, 0x0004490a, 0x00044912, 0x0004491a +}; + +static void rt61pci_init_hw_mode(struct rt2x00_dev *rt2x00dev) +{ + struct hw_mode_spec *spec = &rt2x00dev->spec; + u8 *txpower; + unsigned int i; + + if (GET_FLAG(rt2x00dev, DEVICE_INITIALIZED_HW)) + return; + + /* + * Initialize all hw fields. + */ + rt2x00dev->hw->flags = IEEE80211_HW_HOST_GEN_BEACON | + IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | + IEEE80211_HW_WEP_INCLUDE_IV | + IEEE80211_HW_DATA_NULLFUNC_ACK | + IEEE80211_HW_NO_TKIP_WMM_HWACCEL | + IEEE80211_HW_MONITOR_DURING_OPER; + rt2x00dev->hw->extra_tx_headroom = 0; + rt2x00dev->hw->max_rssi = MAX_RX_SSI; + rt2x00dev->hw->max_noise = MAX_RX_NOISE; + rt2x00dev->hw->queues = RING_NUM_TX; + + /* + * Convert tx_power array in eeprom. + */ + txpower = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_G_START); + for (i = 0; i < 14; i++) + txpower[i] = TXPOWER_FROM_DEV(txpower[i]); + + /* + * Initialize hw_mode information. + */ + spec->mac_addr = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); + spec->num_modes = 2; + spec->num_rates = 12; + spec->num_channels = 14; + spec->tx_power_a = NULL; + spec->tx_power_bg = txpower; + spec->tx_power_default = DEFAULT_TXPOWER; + spec->chan_val_a = NULL; + spec->chan_val_bg = rf_vals_bg; + + if (rt2x00_rf(&rt2x00dev->chip, RF5225) || + rt2x00_rf(&rt2x00dev->chip, RF5325)) { + spec->num_modes = 3; + spec->num_channels += 24; + + txpower = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A_START); + for (i = 0; i < 14; i++) + txpower[i] = TXPOWER_FROM_DEV(txpower[i]); + + spec->tx_power_a = txpower; + if (!GET_FLAG(rt2x00dev, CONFIG_RF_SEQUENCE)) + spec->chan_val_a = rf_vals_a_5x_noseq; + else + spec->chan_val_a = rf_vals_a_5x_seq; + } +} + +static int rt61pci_init_hw(struct rt2x00_dev *rt2x00dev) +{ + int retval; + + rt2x00dev->rxd_size = RXD_DESC_SIZE; + rt2x00dev->txd_size = TXD_DESC_SIZE; + + /* + * Allocate eeprom data. + */ + retval = rt61pci_alloc_eeprom(rt2x00dev); + if (retval) + return retval; + + retval = rt61pci_init_eeprom(rt2x00dev); + if (retval) + return retval; + + /* + * Initialize hw specifications. + */ + rt61pci_init_hw_mode(rt2x00dev); + + return 0; +} + +/* + * IEEE80211 stack callback functions. + */ +static int rt61pci_get_stats(struct ieee80211_hw *hw, + struct ieee80211_low_level_stats *stats) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + u32 reg; + + /* + * Update FCS error count from register. + * The dot11ACKFailureCount, dot11RTSFailureCount and + * dot11RTSSuccessCount are updated in interrupt time. + */ + rt2x00_register_read(rt2x00dev, STA_CSR0, ®); + rt2x00dev->low_level_stats.dot11FCSErrorCount += + rt2x00_get_field32(reg, STA_CSR0_FCS_ERROR); + + memcpy(stats, &rt2x00dev->low_level_stats, sizeof(*stats)); + + return 0; +} + +static int rt61pci_set_retry_limit(struct ieee80211_hw *hw, + u32 short_retry, u32 long_retry) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + u32 reg; + + rt2x00_register_read(rt2x00dev, TXRX_CSR4, ®); + rt2x00_set_field32(®, TXRX_CSR4_LONG_RETRY_LIMIT, long_retry); + rt2x00_set_field32(®, TXRX_CSR4_SHORT_RETRY_LIMIT, short_retry); + rt2x00_register_write(rt2x00dev, TXRX_CSR4, reg); + + return 0; +} + +static u64 rt61pci_get_tsf(struct ieee80211_hw *hw) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + u64 tsf; + u32 reg; + + rt2x00_register_read(rt2x00dev, TXRX_CSR13, ®); + tsf = (u64)rt2x00_get_field32(reg, TXRX_CSR13_HIGH_TSFTIMER) << 32; + rt2x00_register_read(rt2x00dev, TXRX_CSR12, ®); + tsf |= rt2x00_get_field32(reg, TXRX_CSR12_LOW_TSFTIMER); + + return tsf; +} + +static void rt61pci_reset_tsf(struct ieee80211_hw *hw) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + + rt2x00_register_write(rt2x00dev, TXRX_CSR12, 0); + rt2x00_register_write(rt2x00dev, TXRX_CSR13, 0); +} + +static const struct ieee80211_ops rt61pci_mac80211_ops = { + .tx = rt2x00lib_tx, + .reset = rt2x00lib_reset, + .add_interface = rt2x00lib_add_interface, + .remove_interface = rt2x00lib_remove_interface, + .config = rt2x00lib_config, + .config_interface = rt2x00lib_config_interface, + .set_multicast_list = rt2x00lib_set_multicast_list, + .get_stats = rt61pci_get_stats, + .set_retry_limit = rt61pci_set_retry_limit, + .conf_tx = rt2x00lib_conf_tx, + .get_tx_stats = rt2x00lib_get_tx_stats, + .get_tsf = rt61pci_get_tsf, + .reset_tsf = rt61pci_reset_tsf, + .beacon_update = rt2x00pci_beacon_update, +}; + +static const struct rt2x00lib_ops rt61pci_rt2x00_ops = { + .irq_handler = rt61pci_interrupt, + .link_tuner = rt61pci_link_tuner, + .init_hw = rt61pci_init_hw, + .load_firmware = rt61pci_load_firmware, + .initialize = rt2x00pci_initialize, + .uninitialize = rt2x00pci_uninitialize, + .enable_radio = rt61pci_enable_radio, + .disable_radio = rt61pci_disable_radio, + .set_state = rt61pci_set_state, + .toggle_rx = rt61pci_toggle_rx, + .write_tx_desc = rt61pci_write_tx_desc, + .write_tx_data = rt2x00pci_write_tx_data, + .kick_tx_queue = rt61pci_kick_tx_queue, + .kick_beacon_gen = rt61pci_kick_beacon_gen, + .config_type = rt61pci_config_type, + .config_phymode = rt61pci_config_phymode, + .config_channel = rt61pci_config_channel, + .config_mac_addr = rt61pci_config_mac_addr, + .config_bssid = rt61pci_config_bssid, + .config_promisc = rt61pci_config_promisc, + .config_txpower = rt61pci_config_txpower, + .config_antenna = rt61pci_config_antenna, + .config_duration = rt61pci_config_duration, +}; + +static const struct rt2x00_ops rt61pci_ops = { + .lib = &rt61pci_rt2x00_ops, + .hw = &rt61pci_mac80211_ops, +#ifdef CONFIG_RT2X00_DEBUGFS + .debugfs = &rt61pci_rt2x00debug, +#endif /* CONFIG_RT2X00_DEBUGFS */ +}; + +/* + * RT61pci module information. + */ +static char version[] = + DRV_NAME " - " DRV_VERSION " (" DRV_RELDATE ") by " DRV_PROJECT; + +static struct pci_device_id rt61pci_device_table[] = { + /* RT2561s */ + { PCI_DEVICE(0x1814, 0x0301), PCI_DEVICE_DATA(&rt61pci_ops) }, + /* RT2561 v2 */ + { PCI_DEVICE(0x1814, 0x0302), PCI_DEVICE_DATA(&rt61pci_ops) }, + /* RT2661 */ + { PCI_DEVICE(0x1814, 0x0401), PCI_DEVICE_DATA(&rt61pci_ops) }, + { 0, } +}; + +MODULE_AUTHOR(DRV_PROJECT); +MODULE_VERSION(DRV_VERSION); +MODULE_DESCRIPTION("Ralink RT61 PCI & PCMCIA Wireless LAN driver."); +MODULE_SUPPORTED_DEVICE("Ralink RT2561, RT2561s & RT2661 " + "PCI & PCMCIA chipset based cards"); +MODULE_DEVICE_TABLE(pci, rt61pci_device_table); +MODULE_LICENSE("GPL"); + +#ifdef CONFIG_RT2X00_DEBUG +module_param_named(debug, rt2x00_debug_level, bool, S_IWUSR | S_IRUGO); +MODULE_PARM_DESC(debug, "Set this parameter to 1 to enable debug output."); +#endif /* CONFIG_RT2X00_DEBUG */ + +static struct pci_driver rt61pci_driver = { + .name = DRV_NAME, + .id_table = rt61pci_device_table, + .probe = rt2x00pci_probe, + .remove = __devexit_p(rt2x00pci_remove), +#ifdef CONFIG_PM + .suspend = rt2x00pci_suspend, + .resume = rt2x00pci_resume, +#endif /* CONFIG_PM */ +}; + +static int __init rt61pci_init(void) +{ + printk(KERN_INFO "Loading module: %s.\n", version); + return pci_register_driver(&rt61pci_driver); +} + +static void __exit rt61pci_exit(void) +{ + printk(KERN_INFO "Unloading module: %s.\n", version); + pci_unregister_driver(&rt61pci_driver); +} + +module_init(rt61pci_init); +module_exit(rt61pci_exit); diff --git a/drivers/net/wireless/mac80211/rt2x00/rt61pci.h b/drivers/net/wireless/mac80211/rt2x00/rt61pci.h new file mode 100644 index 0000000..873f1fb --- /dev/null +++ b/drivers/net/wireless/mac80211/rt2x00/rt61pci.h @@ -0,0 +1,1348 @@ +/* + Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + + + 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. + */ + +/* + Module: rt61pci + Abstract: Data structures and registers for the rt61pci module. + Supported chipsets: RT2561, RT2561s, RT2661. + */ + +#ifndef RT61PCI_H +#define RT61PCI_H + +/* + * RF chip defines. + */ +#define RF5225 0x0001 +#define RF5325 0x0002 +#define RF2527 0x0003 +#define RF2529 0x0004 + +/* + * Max RSSI value, required for RSSI <-> dBm conversion. + */ +#define MAX_RX_SSI 120 +#define MAX_RX_NOISE -110 + +/* + * Register layout information. + */ +#define CSR_REG_BASE 0x3000 +#define CSR_REG_SIZE 0x04b0 +#define EEPROM_BASE 0x0000 +#define EEPROM_SIZE 0x0100 +#define BBP_SIZE 0x0080 + +/* + * PCI registers. + */ + +/* + * PCI Configuration Header + */ +#define PCI_CONFIG_HEADER_VENDOR 0x0000 +#define PCI_CONFIG_HEADER_DEVICE 0x0002 + +/* + * HOST_CMD_CSR: For HOST to interrupt embedded processor + */ +#define HOST_CMD_CSR 0x0008 +#define HOST_CMD_CSR_HOST_COMMAND FIELD32(0x0000007f) +#define HOST_CMD_CSR_INTERRUPT_MCU FIELD32(0x00000080) + +/* + * MCU_CNTL_CSR + * SELECT_BANK: Select 8051 program bank. + * RESET: Enable 8051 reset state. + * READY: Ready state for 8051. + */ +#define MCU_CNTL_CSR 0x000c +#define MCU_CNTL_CSR_SELECT_BANK FIELD32(0x00000001) +#define MCU_CNTL_CSR_RESET FIELD32(0x00000002) +#define MCU_CNTL_CSR_READY FIELD32(0x00000004) + +/* + * SOFT_RESET_CSR + */ +#define SOFT_RESET_CSR 0x0010 + +/* + * MCU_INT_SOURCE_CSR: MCU interrupt source/mask register. + */ +#define MCU_INT_SOURCE_CSR 0x0014 +#define MCU_INT_SOURCE_CSR_0 FIELD32(0x00000001) +#define MCU_INT_SOURCE_CSR_1 FIELD32(0x00000002) +#define MCU_INT_SOURCE_CSR_2 FIELD32(0x00000004) +#define MCU_INT_SOURCE_CSR_3 FIELD32(0x00000008) +#define MCU_INT_SOURCE_CSR_4 FIELD32(0x00000010) +#define MCU_INT_SOURCE_CSR_5 FIELD32(0x00000020) +#define MCU_INT_SOURCE_CSR_6 FIELD32(0x00000040) +#define MCU_INT_SOURCE_CSR_7 FIELD32(0x00000080) +#define MCU_INT_SOURCE_CSR_TWAKEUP FIELD32(0x00000100) +#define MCU_INT_SOURCE_CSR_TBTT_EXPIRE FIELD32(0x00000200) + +/* + * MCU_INT_MASK_CSR: MCU interrupt source/mask register. + */ +#define MCU_INT_MASK_CSR 0x0018 +#define MCU_INT_MASK_CSR_0 FIELD32(0x00000001) +#define MCU_INT_MASK_CSR_1 FIELD32(0x00000002) +#define MCU_INT_MASK_CSR_2 FIELD32(0x00000004) +#define MCU_INT_MASK_CSR_3 FIELD32(0x00000008) +#define MCU_INT_MASK_CSR_4 FIELD32(0x00000010) +#define MCU_INT_MASK_CSR_5 FIELD32(0x00000020) +#define MCU_INT_MASK_CSR_6 FIELD32(0x00000040) +#define MCU_INT_MASK_CSR_7 FIELD32(0x00000080) +#define MCU_INT_MASK_CSR_TWAKEUP FIELD32(0x00000100) +#define MCU_INT_MASK_CSR_TBTT_EXPIRE FIELD32(0x00000200) + +/* + * PCI_USEC_CSR + */ +#define PCI_USEC_CSR 0x001c + +/* + * Security key table memory. + * 16 entries 32-byte for shared key table + * 64 entries 32-byte for pairwise key table + * 64 entries 8-byte for pairwise ta key table + */ +#define SHARED_KEY_TABLE_BASE 0x1000 +#define PAIRWISE_KEY_TABLE_BASE 0x1200 +#define PAIRWISE_TA_TABLE_BASE 0x1a00 + +struct hw_key_entry { + u8 key[16]; + u8 tx_mic[8]; + u8 rx_mic[8]; +} __attribute__ ((packed)); + +struct hw_pairwise_ta_entry { + u8 address[6]; + u8 reserved[2]; +} __attribute__ ((packed)); + +/* + * Other on-chip shared memory space. + */ +#define HW_CIS_BASE 0x2000 +#define HW_NULL_BASE 0x2b00 + +/* + * Since NULL frame won't be that long (256 byte), + * We steal 16 tail bytes to save debugging settings. + */ +#define HW_DEBUG_SETTING_BASE 0x2bf0 + +/* + * On-chip BEACON frame space. + */ +#define HW_BEACON_BASE0 0x2c00 +#define HW_BEACON_BASE1 0x2d00 +#define HW_BEACON_BASE2 0x2e00 +#define HW_BEACON_BASE3 0x2f00 +#define HW_BEACON_OFFSET 0x0100 + +/* + * HOST-MCU shared memory. + */ + +/* + * H2M_MAILBOX_CSR: Host-to-MCU Mailbox. + */ +#define H2M_MAILBOX_CSR 0x2100 +#define H2M_MAILBOX_CSR_ARG0 FIELD32(0x000000ff) +#define H2M_MAILBOX_CSR_ARG1 FIELD32(0x0000ff00) +#define H2M_MAILBOX_CSR_CMD_TOKEN FIELD32(0x00ff0000) +#define H2M_MAILBOX_CSR_OWNER FIELD32(0xff000000) + +/* + * MCU_LEDCS: LED control for MCU Mailbox. + */ +#define MCU_LEDCS_LED_MODE FIELD16(0x001f) +#define MCU_LEDCS_RADIO_STATUS FIELD16(0x0020) +#define MCU_LEDCS_LINK_BG_STATUS FIELD16(0x0040) +#define MCU_LEDCS_LINK_A_STATUS FIELD16(0x0080) +#define MCU_LEDCS_POLARITY_GPIO_0 FIELD16(0x0100) +#define MCU_LEDCS_POLARITY_GPIO_1 FIELD16(0x0200) +#define MCU_LEDCS_POLARITY_GPIO_2 FIELD16(0x0400) +#define MCU_LEDCS_POLARITY_GPIO_3 FIELD16(0x0800) +#define MCU_LEDCS_POLARITY_GPIO_4 FIELD16(0x1000) +#define MCU_LEDCS_POLARITY_ACT FIELD16(0x2000) +#define MCU_LEDCS_POLARITY_READY_BG FIELD16(0x4000) +#define MCU_LEDCS_POLARITY_READY_A FIELD16(0x8000) + +/* + * M2H_CMD_DONE_CSR. + */ +#define M2H_CMD_DONE_CSR 0x2104 + +/* + * MCU_TXOP_ARRAY_BASE. + */ +#define MCU_TXOP_ARRAY_BASE 0x2110 + +/* + * MAC Control/Status Registers(CSR). + * Some values are set in TU, whereas 1 TU == 1024 us. + */ + +/* + * MAC_CSR0: ASIC revision number. + */ +#define MAC_CSR0 0x3000 + +/* + * MAC_CSR1: System control register. + * SOFT_RESET: Software reset bit, 1: reset, 0: normal. + * BBP_RESET: Hardware reset BBP. + * HOST_READY: Host is ready after initialization, 1: ready. + */ +#define MAC_CSR1 0x3004 +#define MAC_CSR1_SOFT_RESET FIELD32(0x00000001) +#define MAC_CSR1_BBP_RESET FIELD32(0x00000002) +#define MAC_CSR1_HOST_READY FIELD32(0x00000004) + +/* + * MAC_CSR2: STA MAC register 0. + */ +#define MAC_CSR2 0x3008 +#define MAC_CSR2_BYTE0 FIELD32(0x000000ff) +#define MAC_CSR2_BYTE1 FIELD32(0x0000ff00) +#define MAC_CSR2_BYTE2 FIELD32(0x00ff0000) +#define MAC_CSR2_BYTE3 FIELD32(0xff000000) + +/* + * MAC_CSR3: STA MAC register 1. + */ +#define MAC_CSR3 0x300c +#define MAC_CSR3_BYTE4 FIELD32(0x000000ff) +#define MAC_CSR3_BYTE5 FIELD32(0x0000ff00) +#define MAC_CSR3_UNICAST_TO_ME_MASK FIELD32(0x00ff0000) + +/* + * MAC_CSR4: BSSID register 0. + */ +#define MAC_CSR4 0x3010 +#define MAC_CSR4_BYTE0 FIELD32(0x000000ff) +#define MAC_CSR4_BYTE1 FIELD32(0x0000ff00) +#define MAC_CSR4_BYTE2 FIELD32(0x00ff0000) +#define MAC_CSR4_BYTE3 FIELD32(0xff000000) + +/* + * MAC_CSR5: BSSID register 1. + * BSS_ID_MASK: 3: one BSSID, 0: 4 BSSID, 2 or 1: 2 BSSID. + */ +#define MAC_CSR5 0x3014 +#define MAC_CSR5_BYTE4 FIELD32(0x000000ff) +#define MAC_CSR5_BYTE5 FIELD32(0x0000ff00) +#define MAC_CSR5_BSS_ID_MASK FIELD32(0x00ff0000) + +/* + * MAC_CSR6: Maximum frame length register. + */ +#define MAC_CSR6 0x3018 +#define MAC_CSR6_MAX_FRAME_UNIT FIELD32(0x000007ff) + +/* + * MAC_CSR7: Reserved + */ +#define MAC_CSR7 0x301c + +/* + * MAC_CSR8: SIFS/EIFS register. + * All units are in US. + */ +#define MAC_CSR8 0x3020 +#define MAC_CSR8_SIFS FIELD32(0x000000ff) +#define MAC_CSR8_SIFS_AFTER_RX_OFDM FIELD32(0x0000ff00) +#define MAC_CSR8_EIFS FIELD32(0xffff0000) + +/* + * MAC_CSR9: Back-Off control register. + * SLOT_TIME: Slot time, default is 20us for 802.11BG. + * CWMIN: Bit for Cwmin. default Cwmin is 31 (2^5 - 1). + * CWMAX: Bit for Cwmax, default Cwmax is 1023 (2^10 - 1). + * CW_SELECT: 1: CWmin/Cwmax select from register, 0:select from TxD. + */ +#define MAC_CSR9 0x3024 +#define MAC_CSR9_SLOT_TIME FIELD32(0x000000ff) +#define MAC_CSR9_CWMIN FIELD32(0x00000f00) +#define MAC_CSR9_CWMAX FIELD32(0x0000f000) +#define MAC_CSR9_CW_SELECT FIELD32(0x00010000) + +/* + * MAC_CSR10: Power state configuration. + */ +#define MAC_CSR10 0x3028 + +/* + * MAC_CSR11: Power saving transition time register. + * DELAY_AFTER_TBCN: Delay after Tbcn expired in units of TU. + * TBCN_BEFORE_WAKEUP: Number of beacon before wakeup. + * WAKEUP_LATENCY: In unit of TU. + */ +#define MAC_CSR11 0x302c +#define MAC_CSR11_DELAY_AFTER_TBCN FIELD32(0x000000ff) +#define MAC_CSR11_TBCN_BEFORE_WAKEUP FIELD32(0x00007f00) +#define MAC_CSR11_AUTOWAKE FIELD32(0x00008000) +#define MAC_CSR11_WAKEUP_LATENCY FIELD32(0x000f0000) + +/* + * MAC_CSR12: Manual power control / status register (merge CSR20 & PWRCSR1). + * CURRENT_STATE: 0:sleep, 1:awake. + * FORCE_WAKEUP: This has higher priority than PUT_TO_SLEEP. + * BBP_CURRENT_STATE: 0: BBP sleep, 1: BBP awake. + */ +#define MAC_CSR12 0x3030 +#define MAC_CSR12_CURRENT_STATE FIELD32(0x00000001) +#define MAC_CSR12_PUT_TO_SLEEP FIELD32(0x00000002) +#define MAC_CSR12_FORCE_WAKEUP FIELD32(0x00000004) +#define MAC_CSR12_BBP_CURRENT_STATE FIELD32(0x00000008) + +/* + * MAC_CSR13: GPIO. + */ +#define MAC_CSR13 0x3034 +#define MAC_CSR13_BIT0 FIELD32(0x00000001) +#define MAC_CSR13_BIT1 FIELD32(0x00000002) +#define MAC_CSR13_BIT2 FIELD32(0x00000004) +#define MAC_CSR13_BIT3 FIELD32(0x00000008) +#define MAC_CSR13_BIT4 FIELD32(0x00000010) +#define MAC_CSR13_BIT5 FIELD32(0x00000020) +#define MAC_CSR13_BIT6 FIELD32(0x00000040) +#define MAC_CSR13_BIT7 FIELD32(0x00000080) + +/* + * MAC_CSR14: LED control register. + * ON_PERIOD: On period, default 70ms. + * OFF_PERIOD: Off period, default 30ms. + * HW_LED: HW TX activity, 1: normal OFF, 0: normal ON. + * SW_LED: s/w LED, 1: ON, 0: OFF. + * HW_LED_POLARITY: 0: active low, 1: active high. + */ +#define MAC_CSR14 0x3038 +#define MAC_CSR14_ON_PERIOD FIELD32(0x000000ff) +#define MAC_CSR14_OFF_PERIOD FIELD32(0x0000ff00) +#define MAC_CSR14_HW_LED FIELD32(0x00010000) +#define MAC_CSR14_SW_LED FIELD32(0x00020000) +#define MAC_CSR14_HW_LED_POLARITY FIELD32(0x00040000) +#define MAC_CSR14_SW_LED2 FIELD32(0x00080000) + +/* + * MAC_CSR15: NAV control. + */ +#define MAC_CSR15 0x303c + +/* + * TXRX control registers. + * Some values are set in TU, whereas 1 TU == 1024 us. + */ + +/* + * TXRX_CSR0: TX/RX configuration register. + * TSF_OFFSET: Default is 24. + * AUTO_TX_SEQ: 1: ASIC auto replace sequence nr in outgoing frame. + * DISABLE_RX: Disable Rx engine. + * DROP_CRC: Drop CRC error. + * DROP_PHYSICAL: Drop physical error. + * DROP_CONTROL: Drop control frame. + * DROP_NOT_TO_ME: Drop not to me unicast frame. + * DROP_TO_DS: Drop fram ToDs bit is true. + * DROP_VERSION_ERROR: Drop version error frame. + * DROP_MULTICAST: Drop multicast frames. + * DROP_BORADCAST: Drop broadcast frames. + * ROP_ACK_CTS: Drop received ACK and CTS. + */ +#define TXRX_CSR0 0x3040 +#define TXRX_CSR0_RX_ACK_TIMEOUT FIELD32(0x000001ff) +#define TXRX_CSR0_TSF_OFFSET FIELD32(0x00007e00) +#define TXRX_CSR0_AUTO_TX_SEQ FIELD32(0x00008000) +#define TXRX_CSR0_DISABLE_RX FIELD32(0x00010000) +#define TXRX_CSR0_DROP_CRC FIELD32(0x00020000) +#define TXRX_CSR0_DROP_PHYSICAL FIELD32(0x00040000) +#define TXRX_CSR0_DROP_CONTROL FIELD32(0x00080000) +#define TXRX_CSR0_DROP_NOT_TO_ME FIELD32(0x00100000) +#define TXRX_CSR0_DROP_TO_DS FIELD32(0x00200000) +#define TXRX_CSR0_DROP_VERSION_ERROR FIELD32(0x00400000) +#define TXRX_CSR0_DROP_MULTICAST FIELD32(0x00800000) +#define TXRX_CSR0_DROP_BORADCAST FIELD32(0x01000000) +#define TXRX_CSR0_DROP_ACK_CTS FIELD32(0x02000000) +#define TXRX_CSR0_TX_WITHOUT_WAITING FIELD32(0x04000000) + +/* + * TXRX_CSR1 + */ +#define TXRX_CSR1 0x3044 + +/* + * TXRX_CSR2 + */ +#define TXRX_CSR2 0x3048 + +/* + * TXRX_CSR3 + */ +#define TXRX_CSR3 0x304c + +/* + * TXRX_CSR4: Auto-Responder/Tx-retry register. + * AUTORESPOND_PREAMBLE: 0:long, 1:short preamble. + * OFDM_TX_RATE_DOWN: 1:enable. + * OFDM_TX_RATE_STEP: 0:1-step, 1: 2-step, 2:3-step, 3:4-step. + * OFDM_TX_FALLBACK_CCK: 0: Fallback to OFDM 6M only, 1: Fallback to CCK 1M,2M. + */ +#define TXRX_CSR4 0x3050 +#define TXRX_CSR4_TX_ACK_TIMEOUT FIELD32(0x000000ff) +#define TXRX_CSR4_CNTL_ACK_POLICY FIELD32(0x00000700) +#define TXRX_CSR4_ACK_CTS_PSM FIELD32(0x00010000) +#define TXRX_CSR4_AUTORESPOND_ENABLE FIELD32(0x00020000) +#define TXRX_CSR4_AUTORESPOND_PREAMBLE FIELD32(0x00040000) +#define TXRX_CSR4_OFDM_TX_RATE_DOWN FIELD32(0x00080000) +#define TXRX_CSR4_OFDM_TX_RATE_STEP FIELD32(0x00300000) +#define TXRX_CSR4_OFDM_TX_FALLBACK_CCK FIELD32(0x00400000) +#define TXRX_CSR4_LONG_RETRY_LIMIT FIELD32(0x0f000000) +#define TXRX_CSR4_SHORT_RETRY_LIMIT FIELD32(0xf0000000) + +/* + * TXRX_CSR5 + */ +#define TXRX_CSR5 0x3054 + +/* + * ACK/CTS payload consumed time registers. + */ +#define TXRX_CSR6 0x3058 +#define TXRX_CSR7 0x305c +#define TXRX_CSR8 0x3060 + +/* + * TXRX_CSR9: Synchronization control register. + * BEACON_INTERVAL: In unit of 1/16 TU. + * TSF_TICKING: Enable TSF auto counting. + * TSF_SYNC: Tsf sync, 0: disable, 1: infra, 2: ad-hoc/master mode. + * BEACON_GEN: Enable beacon generator. + */ +#define TXRX_CSR9 0x3064 +#define TXRX_CSR9_BEACON_INTERVAL FIELD32(0x0000ffff) +#define TXRX_CSR9_TSF_TICKING FIELD32(0x00010000) +#define TXRX_CSR9_TSF_SYNC FIELD32(0x00060000) +#define TXRX_CSR9_TBTT_ENABLE FIELD32(0x00080000) +#define TXRX_CSR9_BEACON_GEN FIELD32(0x00100000) +#define TXRX_CSR9_TIMESTAMP_COMPENSATE FIELD32(0xff000000) + +/* + * TXRX_CSR10: BEACON alignment. + */ +#define TXRX_CSR10 0x3068 + +/* + * TXRX_CSR11: AES mask. + */ +#define TXRX_CSR11 0x306c + +/* + * TXRX_CSR12: TSF low 32. + */ +#define TXRX_CSR12 0x3070 +#define TXRX_CSR12_LOW_TSFTIMER FIELD32(0xffffffff) + +/* + * TXRX_CSR13: TSF high 32. + */ +#define TXRX_CSR13 0x3074 +#define TXRX_CSR13_HIGH_TSFTIMER FIELD32(0xffffffff) + +/* + * TXRX_CSR14: TBTT timer. + */ +#define TXRX_CSR14 0x3078 + +/* + * TXRX_CSR15: TKIP MIC priority byte "AND" mask. + */ +#define TXRX_CSR15 0x307c + + +/* + * PHY control registers. + * Some values are set in TU, whereas 1 TU == 1024 us. + */ + +/* + * PHY_CSR0: RF/PS control. + */ +#define PHY_CSR0 0x3080 +#define PHY_CSR0_PA_PE_BG FIELD32(0x00010000) +#define PHY_CSR0_PA_PE_A FIELD32(0x00020000) + +/* + * PHY_CSR1 + */ +#define PHY_CSR1 0x3084 + +/* + * PHY_CSR2: Pre-TX BBP control. + */ +#define PHY_CSR2 0x3088 + +/* + * PHY_CSR3: BBP serial control register. + * VALUE: Register value to program into BBP. + * REG_NUM: Selected BBP register. + * READ_CONTROL: 0: Write BBP, 1: Read BBP. + * BUSY: 1: ASIC is busy execute BBP programming. + */ +#define PHY_CSR3 0x308c +#define PHY_CSR3_VALUE FIELD32(0x000000ff) +#define PHY_CSR3_REGNUM FIELD32(0x00007f00) +#define PHY_CSR3_READ_CONTROL FIELD32(0x00008000) +#define PHY_CSR3_BUSY FIELD32(0x00010000) + +/* + * PHY_CSR4: RF serial control register + * VALUE: Register value (include register id) serial out to RF/IF chip. + * NUMBER_OF_BITS: Number of bits used in RFRegValue (I:20, RFMD:22). + * IF_SELECT: 1: select IF to program, 0: select RF to program. + * PLL_LD: RF PLL_LD status. + * BUSY: 1: ASIC is busy execute RF programming. + */ +#define PHY_CSR4 0x3090 +#define PHY_CSR4_VALUE FIELD32(0x00ffffff) +#define PHY_CSR4_NUMBER_OF_BITS FIELD32(0x1f000000) +#define PHY_CSR4_IF_SELECT FIELD32(0x20000000) +#define PHY_CSR4_PLL_LD FIELD32(0x40000000) +#define PHY_CSR4_BUSY FIELD32(0x80000000) + +/* + * PHY_CSR5: RX to TX signal switch timing control. + */ +#define PHY_CSR5 0x3094 + +/* + * PHY_CSR6: TX to RX signal timing control. + */ +#define PHY_CSR6 0x3098 + +/* + * PHY_CSR7: TX DAC switching timing control. + */ +#define PHY_CSR7 0x309c + +/* + * Security control register. + */ + +/* + * SEC_CSR0: Shared key table control. + */ +#define SEC_CSR0 0x30a0 + +/* + * SEC_CSR1: Shared key table security mode register. + */ +#define SEC_CSR1 0x30a4 +#define SEC_CSR1_BSS0_KEY0_CIPHER_ALG FIELD32(0x00000007) +#define SEC_CSR1_BSS0_KEY1_CIPHER_ALG FIELD32(0x00000070) +#define SEC_CSR1_BSS0_KEY2_CIPHER_ALG FIELD32(0x00000700) +#define SEC_CSR1_BSS0_KEY3_CIPHER_ALG FIELD32(0x00007000) +#define SEC_CSR1_BSS1_KEY0_CIPHER_ALG FIELD32(0x00070000) +#define SEC_CSR1_BSS1_KEY1_CIPHER_ALG FIELD32(0x00700000) +#define SEC_CSR1_BSS1_KEY2_CIPHER_ALG FIELD32(0x07000000) +#define SEC_CSR1_BSS1_KEY3_CIPHER_ALG FIELD32(0x70000000) + +/* + * Pairwise key table valid bitmap registers. + * SEC_CSR2: pairwise key table valid bitmap 0. + * SEC_CSR3: pairwise key table valid bitmap 1. + */ +#define SEC_CSR2 0x30a8 +#define SEC_CSR3 0x30ac + +/* + * SEC_CSR4: Pairwise key table lookup control. + */ +#define SEC_CSR4 0x30b0 + +/* + * SEC_CSR5: shared key table security mode register. + */ +#define SEC_CSR5 0x30b4 +#define SEC_CSR5_BSS2_KEY0_CIPHER_ALG FIELD32(0x00000007) +#define SEC_CSR5_BSS2_KEY1_CIPHER_ALG FIELD32(0x00000070) +#define SEC_CSR5_BSS2_KEY2_CIPHER_ALG FIELD32(0x00000700) +#define SEC_CSR5_BSS2_KEY3_CIPHER_ALG FIELD32(0x00007000) +#define SEC_CSR5_BSS3_KEY0_CIPHER_ALG FIELD32(0x00070000) +#define SEC_CSR5_BSS3_KEY1_CIPHER_ALG FIELD32(0x00700000) +#define SEC_CSR5_BSS3_KEY2_CIPHER_ALG FIELD32(0x07000000) +#define SEC_CSR5_BSS3_KEY3_CIPHER_ALG FIELD32(0x70000000) + +/* + * STA control registers. + */ + +/* + * STA_CSR0: RX PLCP error count & RX FCS error count. + */ +#define STA_CSR0 0x30c0 +#define STA_CSR0_FCS_ERROR FIELD32(0x0000ffff) +#define STA_CSR0_PLCP_ERROR FIELD32(0xffff0000) + +/* + * STA_CSR1: RX False CCA count & RX LONG frame count. + */ +#define STA_CSR1 0x30c4 +#define STA_CSR1_PHYSICAL_ERROR FIELD32(0x0000ffff) +#define STA_CSR1_FALSE_CCA_ERROR FIELD32(0xffff0000) + +/* + * STA_CSR2: TX Beacon count and RX FIFO overflow count. + */ +#define STA_CSR2 0x30c8 +#define STA_CSR2_RX_FIFO_OVERFLOW_COUNT FIELD32(0x0000ffff) +#define STA_CSR2_RX_OVERFLOW_COUNT FIELD32(0xffff0000) + +/* + * STA_CSR3: TX Beacon count. + */ +#define STA_CSR3 0x30cc +#define STA_CSR3_TX_BEACON_COUNT FIELD32(0x0000ffff) + +/* + * STA_CSR4: TX Result status register. + * VALID: 1:This register contains a valid TX result. + */ +#define STA_CSR4 0x30d0 +#define STA_CSR4_VALID FIELD32(0x00000001) +#define STA_CSR4_TX_RESULT FIELD32(0x0000000e) +#define STA_CSR4_RETRY_COUNT FIELD32(0x000000f0) +#define STA_CSR4_PID_SUBTYPE FIELD32(0x00001f00) +#define STA_CSR4_PID_TYPE FIELD32(0x0000e000) +#define STA_CSR4_TXRATE FIELD32(0x000f0000) + +/* + * QOS control registers. + */ + +/* + * QOS_CSR0: TXOP holder MAC address register. + */ +#define QOS_CSR0 0x30e0 +#define QOS_CSR0_BYTE0 FIELD32(0x000000ff) +#define QOS_CSR0_BYTE1 FIELD32(0x0000ff00) +#define QOS_CSR0_BYTE2 FIELD32(0x00ff0000) +#define QOS_CSR0_BYTE3 FIELD32(0xff000000) + +/* + * QOS_CSR1: TXOP holder MAC address register. + */ +#define QOS_CSR1 0x30e4 +#define QOS_CSR1_BYTE4 FIELD32(0x000000ff) +#define QOS_CSR1_BYTE5 FIELD32(0x0000ff00) + +/* + * QOS_CSR2: TXOP holder timeout register. + */ +#define QOS_CSR2 0x30e8 + +/* + * RX QOS-CFPOLL MAC address register. + * QOS_CSR3: RX QOS-CFPOLL MAC address 0. + * QOS_CSR4: RX QOS-CFPOLL MAC address 1. + */ +#define QOS_CSR3 0x30ec +#define QOS_CSR4 0x30f0 + +/* + * QOS_CSR5: "QosControl" field of the RX QOS-CFPOLL. + */ +#define QOS_CSR5 0x30f4 + +/* + * Host DMA registers. + */ + +/* + * AC0_BASE_CSR: AC_BK base address. + */ +#define AC0_BASE_CSR 0x3400 +#define AC0_BASE_CSR_RING_REGISTER FIELD32(0xffffffff) + +/* + * AC1_BASE_CSR: AC_BE base address. + */ +#define AC1_BASE_CSR 0x3404 +#define AC1_BASE_CSR_RING_REGISTER FIELD32(0xffffffff) + +/* + * AC2_BASE_CSR: AC_VI base address. + */ +#define AC2_BASE_CSR 0x3408 +#define AC2_BASE_CSR_RING_REGISTER FIELD32(0xffffffff) + +/* + * AC3_BASE_CSR: AC_VO base address. + */ +#define AC3_BASE_CSR 0x340c +#define AC3_BASE_CSR_RING_REGISTER FIELD32(0xffffffff) + +/* + * MGMT_BASE_CSR: MGMT ring base address. + */ +#define MGMT_BASE_CSR 0x3410 +#define MGMT_BASE_CSR_RING_REGISTER FIELD32(0xffffffff) + +/* + * TX_RING_CSR0: TX Ring size for AC_BK, AC_BE, AC_VI, AC_VO. + */ +#define TX_RING_CSR0 0x3418 +#define TX_RING_CSR0_AC0_RING_SIZE FIELD32(0x000000ff) +#define TX_RING_CSR0_AC1_RING_SIZE FIELD32(0x0000ff00) +#define TX_RING_CSR0_AC2_RING_SIZE FIELD32(0x00ff0000) +#define TX_RING_CSR0_AC3_RING_SIZE FIELD32(0xff000000) + +/* + * TX_RING_CSR1: TX Ring size for MGMT Ring, HCCA Ring + * TXD_SIZE: In unit of 32-bit. + */ +#define TX_RING_CSR1 0x341c +#define TX_RING_CSR1_MGMT_RING_SIZE FIELD32(0x000000ff) +#define TX_RING_CSR1_HCCA_RING_SIZE FIELD32(0x0000ff00) +#define TX_RING_CSR1_TXD_SIZE FIELD32(0x003f0000) + +/* + * AIFSN_CSR: AIFSN for each EDCA AC. + * AIFSN0: For AC_BK. + * AIFSN1: For AC_BE. + * AIFSN2: For AC_VI. + * AIFSN3: For AC_VO. + */ +#define AIFSN_CSR 0x3420 +#define AIFSN_CSR_AIFSN0 FIELD32(0x0000000f) +#define AIFSN_CSR_AIFSN1 FIELD32(0x000000f0) +#define AIFSN_CSR_AIFSN2 FIELD32(0x00000f00) +#define AIFSN_CSR_AIFSN3 FIELD32(0x0000f000) + +/* + * CWMIN_CSR: CWmin for each EDCA AC. + * CWMIN0: For AC_BK. + * CWMIN1: For AC_BE. + * CWMIN2: For AC_VI. + * CWMIN3: For AC_VO. + */ +#define CWMIN_CSR 0x3424 +#define CWMIN_CSR_CWMIN0 FIELD32(0x0000000f) +#define CWMIN_CSR_CWMIN1 FIELD32(0x000000f0) +#define CWMIN_CSR_CWMIN2 FIELD32(0x00000f00) +#define CWMIN_CSR_CWMIN3 FIELD32(0x0000f000) + +/* + * CWMAX_CSR: CWmax for each EDCA AC. + * CWMAX0: For AC_BK. + * CWMAX1: For AC_BE. + * CWMAX2: For AC_VI. + * CWMAX3: For AC_VO. + */ +#define CWMAX_CSR 0x3428 +#define CWMAX_CSR_CWMAX0 FIELD32(0x0000000f) +#define CWMAX_CSR_CWMAX1 FIELD32(0x000000f0) +#define CWMAX_CSR_CWMAX2 FIELD32(0x00000f00) +#define CWMAX_CSR_CWMAX3 FIELD32(0x0000f000) + +/* + * TX_DMA_DST_CSR + */ +#define TX_DMA_DST_CSR 0x342c + +/* + * TX_CNTL_CSR: KICK/Abort TX. + * KICK_TX_AC0: For AC_BK. + * KICK_TX_AC1: For AC_BE. + * KICK_TX_AC2: For AC_VI. + * KICK_TX_AC3: For AC_VO. + * ABORT_TX_AC0: For AC_BK. + * ABORT_TX_AC1: For AC_BE. + * ABORT_TX_AC2: For AC_VI. + * ABORT_TX_AC3: For AC_VO. + */ +#define TX_CNTL_CSR 0x3430 +#define TX_CNTL_CSR_KICK_TX_AC0 FIELD32(0x00000001) +#define TX_CNTL_CSR_KICK_TX_AC1 FIELD32(0x00000002) +#define TX_CNTL_CSR_KICK_TX_AC2 FIELD32(0x00000004) +#define TX_CNTL_CSR_KICK_TX_AC3 FIELD32(0x00000008) +#define TX_CNTL_CSR_KICK_TX_MGMT FIELD32(0x00000010) +#define TX_CNTL_CSR_ABORT_TX_AC0 FIELD32(0x00010000) +#define TX_CNTL_CSR_ABORT_TX_AC1 FIELD32(0x00020000) +#define TX_CNTL_CSR_ABORT_TX_AC2 FIELD32(0x00040000) +#define TX_CNTL_CSR_ABORT_TX_AC3 FIELD32(0x00080000) +#define TX_CNTL_CSR_ABORT_TX_MGMT FIELD32(0x00100000) + +/* + * LOAD_TX_RING_CSR + */ +#define LOAD_TX_RING_CSR 0x3434 + +/* + * Several read-only registers, for debugging. + */ +#define AC0_TXPTR_CSR 0x3438 +#define AC1_TXPTR_CSR 0x343c +#define AC2_TXPTR_CSR 0x3440 +#define AC3_TXPTR_CSR 0x3444 +#define MGMT_TXPTR_CSR 0x3448 + +/* + * RX_BASE_CSR + */ +#define RX_BASE_CSR 0x3450 +#define RX_BASE_CSR_RING_REGISTER FIELD32(0xffffffff) + +/* + * RX_RING_CSR. + * RXD_SIZE: In unit of 32-bit. + */ +#define RX_RING_CSR 0x3454 +#define RX_RING_CSR_RING_SIZE FIELD32(0x000000ff) +#define RX_RING_CSR_RXD_SIZE FIELD32(0x00003f00) +#define RX_RING_CSR_RXD_WRITEBACK_SIZE FIELD32(0x00070000) + +/* + * RX_CNTL_CSR + */ +#define RX_CNTL_CSR 0x3458 + +/* + * RXPTR_CSR: Read-only, for debugging. + */ +#define RXPTR_CSR 0x345c + +/* + * PCI_CFG_CSR + */ +#define PCI_CFG_CSR 0x3460 + +/* + * BUF_FORMAT_CSR + */ +#define BUF_FORMAT_CSR 0x3464 + +/* + * INT_SOURCE_CSR: Interrupt source register. + * Write one to clear corresponding bit. + */ +#define INT_SOURCE_CSR 0x3468 +#define INT_SOURCE_CSR_TXDONE FIELD32(0x00000001) +#define INT_SOURCE_CSR_RXDONE FIELD32(0x00000002) +#define INT_SOURCE_CSR_BEACON_DONE FIELD32(0x00000004) +#define INT_SOURCE_CSR_TX_ABORT_DONE FIELD32(0x00000010) +#define INT_SOURCE_CSR_AC0_DMA_DONE FIELD32(0x00010000) +#define INT_SOURCE_CSR_AC1_DMA_DONE FIELD32(0x00020000) +#define INT_SOURCE_CSR_AC2_DMA_DONE FIELD32(0x00040000) +#define INT_SOURCE_CSR_AC3_DMA_DONE FIELD32(0x00080000) +#define INT_SOURCE_CSR_MGMT_DMA_DONE FIELD32(0x00100000) +#define INT_SOURCE_CSR_HCCA_DMA_DONE FIELD32(0x00200000) + +/* + * INT_MASK_CSR: Interrupt MASK register. 1: the interrupt is mask OFF. + * MITIGATION_PERIOD: Interrupt mitigation in unit of 32 PCI clock. + */ +#define INT_MASK_CSR 0x346c +#define INT_MASK_CSR_TXDONE FIELD32(0x00000001) +#define INT_MASK_CSR_RXDONE FIELD32(0x00000002) +#define INT_MASK_CSR_BEACON_DONE FIELD32(0x00000004) +#define INT_MASK_CSR_TX_ABORT_DONE FIELD32(0x00000010) +#define INT_MASK_CSR_ENABLE_MITIGATION FIELD32(0x00000080) +#define INT_MASK_CSR_MITIGATION_PERIOD FIELD32(0x0000ff00) +#define INT_MASK_CSR_AC0_DMA_DONE FIELD32(0x00010000) +#define INT_MASK_CSR_AC1_DMA_DONE FIELD32(0x00020000) +#define INT_MASK_CSR_AC2_DMA_DONE FIELD32(0x00040000) +#define INT_MASK_CSR_AC3_DMA_DONE FIELD32(0x00080000) +#define INT_MASK_CSR_MGMT_DMA_DONE FIELD32(0x00100000) +#define INT_MASK_CSR_HCCA_DMA_DONE FIELD32(0x00200000) + +/* + * E2PROM_CSR: EEPROM control register. + * RELOAD: Write 1 to reload eeprom content. + * TYPE_93C46: 1: 93c46, 0:93c66. + * LOAD_STATUS: 1:loading, 0:done. + */ +#define E2PROM_CSR 0x3470 +#define E2PROM_CSR_RELOAD FIELD32(0x00000001) +#define E2PROM_CSR_DATA_CLOCK FIELD32(0x00000002) +#define E2PROM_CSR_CHIP_SELECT FIELD32(0x00000004) +#define E2PROM_CSR_DATA_IN FIELD32(0x00000008) +#define E2PROM_CSR_DATA_OUT FIELD32(0x00000010) +#define E2PROM_CSR_TYPE_93C46 FIELD32(0x00000020) +#define E2PROM_CSR_LOAD_STATUS FIELD32(0x00000040) + +/* + * AC_TXOP_CSR0: AC_BK/AC_BE TXOP register. + * AC0_TX_OP: For AC_BK, in unit of 32us. + * AC1_TX_OP: For AC_BE, in unit of 32us. + */ +#define AC_TXOP_CSR0 0x3474 +#define AC_TXOP_CSR0_AC0_TX_OP FIELD32(0x0000ffff) +#define AC_TXOP_CSR0_AC1_TX_OP FIELD32(0xffff0000) + +/* + * AC_TXOP_CSR1: AC_VO/AC_VI TXOP register. + * AC2_TX_OP: For AC_VI, in unit of 32us. + * AC3_TX_OP: For AC_VO, in unit of 32us. + */ +#define AC_TXOP_CSR1 0x3478 +#define AC_TXOP_CSR1_AC2_TX_OP FIELD32(0x0000ffff) +#define AC_TXOP_CSR1_AC3_TX_OP FIELD32(0xffff0000) + +/* + * DMA_STATUS_CSR + */ +#define DMA_STATUS_CSR 0x3480 + +/* + * TEST_MODE_CSR + */ +#define TEST_MODE_CSR 0x3484 + +/* + * UART0_TX_CSR + */ +#define UART0_TX_CSR 0x3488 + +/* + * UART0_RX_CSR + */ +#define UART0_RX_CSR 0x348c + +/* + * UART0_FRAME_CSR + */ +#define UART0_FRAME_CSR 0x3490 + +/* + * UART0_BUFFER_CSR + */ +#define UART0_BUFFER_CSR 0x3494 + +/* + * IO_CNTL_CSR + */ +#define IO_CNTL_CSR 0x3498 + +/* + * UART_INT_SOURCE_CSR + */ +#define UART_INT_SOURCE_CSR 0x34a8 + +/* + * UART_INT_MASK_CSR + */ +#define UART_INT_MASK_CSR 0x34ac + +/* + * PBF_QUEUE_CSR + */ +#define PBF_QUEUE_CSR 0x34b0 + +/* + * Firmware DMA registers. + * Firmware DMA registers are dedicated for MCU usage + * and should not be touched by host driver. + * Therefore we skip the definition of these registers. + */ +#define FW_TX_BASE_CSR 0x34c0 +#define FW_TX_START_CSR 0x34c4 +#define FW_TX_LAST_CSR 0x34c8 +#define FW_MODE_CNTL_CSR 0x34cc +#define FW_TXPTR_CSR 0x34d0 + +/* + * 8051 firmware image. + */ +#define FIRMWARE_IMAGE_BASE 0x4000 + +/* + * RF registers + */ +#define RF3_TXPOWER FIELD32(0x00003e00) +#define RF4_FREQ_OFFSET FIELD32(0x0003f000) + +/* + * EEPROM content. + * The wordsize of the EEPROM is 16 bits. + */ + +/* + * HW MAC address. + */ +#define EEPROM_MAC_ADDR_0 0x0002 +#define EEPROM_MAC_ADDR_BYTE0 FIELD16(0x00ff) +#define EEPROM_MAC_ADDR_BYTE1 FIELD16(0xff00) +#define EEPROM_MAC_ADDR1 0x0004 +#define EEPROM_MAC_ADDR_BYTE2 FIELD16(0x00ff) +#define EEPROM_MAC_ADDR_BYTE3 FIELD16(0xff00) +#define EEPROM_MAC_ADDR_2 0x0006 +#define EEPROM_MAC_ADDR_BYTE4 FIELD16(0x00ff) +#define EEPROM_MAC_ADDR_BYTE5 FIELD16(0xff00) + +/* + * EEPROM antenna. + * ANTENNA_NUM: Number of antenna's. + * TX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. + * RX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. + * FRAME_TYPE: 0: DPDT , 1: SPDT , noted this bit is valid for g only. + * DYN_TXAGC: Dynamic TX AGC control. + * HARDWARE_RADIO: 1: Hardware controlled radio. Read GPIO0. + * RF_TYPE: Rf_type of this adapter. + */ +#define EEPROM_ANTENNA 0x0010 +#define EEPROM_ANTENNA_NUM FIELD16(0x0003) +#define EEPROM_ANTENNA_TX_DEFAULT FIELD16(0x000c) +#define EEPROM_ANTENNA_RX_DEFAULT FIELD16(0x0030) +#define EEPROM_ANTENNA_FRAME_TYPE FIELD16(0x0040) +#define EEPROM_ANTENNA_DYN_TXAGC FIELD16(0x0200) +#define EEPROM_ANTENNA_HARDWARE_RADIO FIELD16(0x0400) +#define EEPROM_ANTENNA_RF_TYPE FIELD16(0xf800) + +/* + * EEPROM NIC config. + * ENABLE_DIVERSITY: 1:enable, 0:disable. + * EXTERNAL_LNA_BG: External LNA enable for 2.4G. + * CARDBUS_ACCEL: 0:enable, 1:disable. + * EXTERNAL_LNA_A: External LNA enable for 5G. + */ +#define EEPROM_NIC 0x0011 +#define EEPROM_NIC_ENABLE_DIVERSITY FIELD16(0x0001) +#define EEPROM_NIC_TX_DIVERSITY FIELD16(0x0002) +#define EEPROM_NIC_TX_RX_FIXED FIELD16(0x000c) +#define EEPROM_NIC_EXTERNAL_LNA_BG FIELD16(0x0010) +#define EEPROM_NIC_CARDBUS_ACCEL FIELD16(0x0020) +#define EEPROM_NIC_EXTERNAL_LNA_A FIELD16(0x0040) + +/* + * EEPROM geography. + * GEO_A: Default geographical setting for 5GHz band + * GEO: Default geographical setting. + */ +#define EEPROM_GEOGRAPHY 0x0012 +#define EEPROM_GEOGRAPHY_GEO_A FIELD16(0x00ff) +#define EEPROM_GEOGRAPHY_GEO FIELD16(0xff00) + +/* + * EEPROM BBP. + */ +#define EEPROM_BBP_START 0x0013 +#define EEPROM_BBP_SIZE 16 +#define EEPROM_BBP_VALUE FIELD16(0x00ff) +#define EEPROM_BBP_REG_ID FIELD16(0xff00) + +/* + * EEPROM TXPOWER 802.11G + */ +#define EEPROM_TXPOWER_G_START 0x0023 +#define EEPROM_TXPOWER_G_SIZE 7 +#define EEPROM_TXPOWER_G_1 FIELD16(0x00ff) +#define EEPROM_TXPOWER_G_2 FIELD16(0xff00) + +/* + * EEPROM Frequency + */ +#define EEPROM_FREQ 0x002f +#define EEPROM_FREQ_OFFSET FIELD16(0x00ff) +#define EEPROM_FREQ_SEQ_MASK FIELD16(0xff00) +#define EEPROM_FREQ_SEQ FIELD16(0x0300) + +/* + * EEPROM LED. + * POLARITY_RDY_G: Polarity RDY_G setting. + * POLARITY_RDY_A: Polarity RDY_A setting. + * POLARITY_ACT: Polarity ACT setting. + * POLARITY_GPIO_0: Polarity GPIO0 setting. + * POLARITY_GPIO_1: Polarity GPIO1 setting. + * POLARITY_GPIO_2: Polarity GPIO2 setting. + * POLARITY_GPIO_3: Polarity GPIO3 setting. + * POLARITY_GPIO_4: Polarity GPIO4 setting. + * LED_MODE: Led mode. + */ +#define EEPROM_LED 0x0030 +#define EEPROM_LED_POLARITY_RDY_G FIELD16(0x0001) +#define EEPROM_LED_POLARITY_RDY_A FIELD16(0x0002) +#define EEPROM_LED_POLARITY_ACT FIELD16(0x0004) +#define EEPROM_LED_POLARITY_GPIO_0 FIELD16(0x0008) +#define EEPROM_LED_POLARITY_GPIO_1 FIELD16(0x0010) +#define EEPROM_LED_POLARITY_GPIO_2 FIELD16(0x0020) +#define EEPROM_LED_POLARITY_GPIO_3 FIELD16(0x0040) +#define EEPROM_LED_POLARITY_GPIO_4 FIELD16(0x0080) +#define EEPROM_LED_LED_MODE FIELD16(0x1f00) + +/* + * EEPROM TXPOWER 802.11A + */ +#define EEPROM_TXPOWER_A_START 0x0031 +#define EEPROM_TXPOWER_A_SIZE 12 +#define EEPROM_TXPOWER_A_1 FIELD16(0x00ff) +#define EEPROM_TXPOWER_A_2 FIELD16(0xff00) + +/* + * MCU mailbox commands. + */ +#define MCU_SLEEP 0x30 +#define MCU_WAKEUP 0x31 +#define MCU_LED 0x50 +#define MCU_LED_STRENGTH 0x52 + +/* + * DMA descriptor defines. + */ +#define TXD_DESC_SIZE ( 16 * sizeof(struct data_desc) ) +#define RXD_DESC_SIZE ( 16 * sizeof(struct data_desc) ) + +/* + * TX descriptor format for TX, PRIO and Beacon Ring. + */ + +/* + * Word0 + * TKIP_MIC: ASIC appends TKIP MIC if TKIP is used. + * KEY_TABLE: Use per-client pairwise KEY table. + * KEY_INDEX: + * Key index (0~31) to the pairwise KEY table. + * 0~3 to shared KEY table 0 (BSS0). + * 4~7 to shared KEY table 1 (BSS1). + * 8~11 to shared KEY table 2 (BSS2). + * 12~15 to shared KEY table 3 (BSS3). + * BURST: Next frame belongs to same "burst" event. + */ +#define TXD_W0_OWNER_NIC FIELD32(0x00000001) +#define TXD_W0_VALID FIELD32(0x00000002) +#define TXD_W0_MORE_FRAG FIELD32(0x00000004) +#define TXD_W0_ACK FIELD32(0x00000008) +#define TXD_W0_TIMESTAMP FIELD32(0x00000010) +#define TXD_W0_OFDM FIELD32(0x00000020) +#define TXD_W0_IFS FIELD32(0x00000040) +#define TXD_W0_RETRY_MODE FIELD32(0x00000080) +#define TXD_W0_TKIP_MIC FIELD32(0x00000100) +#define TXD_W0_KEY_TABLE FIELD32(0x00000200) +#define TXD_W0_KEY_INDEX FIELD32(0x0000fc00) +#define TXD_W0_DATABYTE_COUNT FIELD32(0x0fff0000) +#define TXD_W0_BURST FIELD32(0x10000000) +#define TXD_W0_CIPHER_ALG FIELD32(0xe0000000) + +/* + * Word1 + * HOST_Q_ID: EDCA/HCCA queue ID. + * HW_SEQUENCE: MAC overwrites the frame sequence number. + * BUFFER_COUNT: Number of buffers in this TXD. + */ +#define TXD_W1_HOST_Q_ID FIELD32(0x0000000f) +#define TXD_W1_AIFSN FIELD32(0x000000f0) +#define TXD_W1_CWMIN FIELD32(0x00000f00) +#define TXD_W1_CWMAX FIELD32(0x0000f000) +#define TXD_W1_IV_OFFSET FIELD32(0x003f0000) +#define TXD_W1_PIGGY_BACK FIELD32(0x01000000) +#define TXD_W1_HW_SEQUENCE FIELD32(0x10000000) +#define TXD_W1_BUFFER_COUNT FIELD32(0xe0000000) + +/* + * Word2: PLCP information + */ +#define TXD_W2_PLCP_SIGNAL FIELD32(0x000000ff) +#define TXD_W2_PLCP_SERVICE FIELD32(0x0000ff00) +#define TXD_W2_PLCP_LENGTH_LOW FIELD32(0x00ff0000) +#define TXD_W2_PLCP_LENGTH_HIGH FIELD32(0xff000000) + +/* + * Word3 + */ +#define TXD_W3_IV FIELD32(0xffffffff) + +/* + * Word4 + */ +#define TXD_W4_EIV FIELD32(0xffffffff) + +/* + * Word5 + * FRAME_OFFSET: Frame start offset inside ASIC TXFIFO (after TXINFO field). + * TXD_W5_PID_SUBTYPE: Driver assigned packet ID index for txdone handler. + * TXD_W5_PID_TYPE: Driver assigned packet ID type for txdone handler. + * WAITING_DMA_DONE_INT: TXD been filled with data + * and waiting for TxDoneISR housekeeping. + */ +#define TXD_W5_FRAME_OFFSET FIELD32(0x000000ff) +#define TXD_W5_PID_SUBTYPE FIELD32(0x00001f00) +#define TXD_W5_PID_TYPE FIELD32(0x0000e000) +#define TXD_W5_TX_POWER FIELD32(0x00ff0000) +#define TXD_W5_WAITING_DMA_DONE_INT FIELD32(0x01000000) + +/* + * the above 24-byte is called TXINFO and will be DMAed to MAC block + * through TXFIFO. MAC block use this TXINFO to control the transmission + * behavior of this frame. + * The following fields are not used by MAC block. + * They are used by DMA block and HOST driver only. + * Once a frame has been DMA to ASIC, all the following fields are useless + * to ASIC. + */ + +/* + * Word6-10: Buffer physical address + */ +#define TXD_W6_BUFFER_PHYSICAL_ADDRESS FIELD32(0xffffffff) +#define TXD_W7_BUFFER_PHYSICAL_ADDRESS FIELD32(0xffffffff) +#define TXD_W8_BUFFER_PHYSICAL_ADDRESS FIELD32(0xffffffff) +#define TXD_W9_BUFFER_PHYSICAL_ADDRESS FIELD32(0xffffffff) +#define TXD_W10_BUFFER_PHYSICAL_ADDRESS FIELD32(0xffffffff) + +/* + * Word11-13: Buffer length + */ +#define TXD_W11_BUFFER_LENGTH0 FIELD32(0x00000fff) +#define TXD_W11_BUFFER_LENGTH1 FIELD32(0x0fff0000) +#define TXD_W12_BUFFER_LENGTH2 FIELD32(0x00000fff) +#define TXD_W12_BUFFER_LENGTH3 FIELD32(0x0fff0000) +#define TXD_W13_BUFFER_LENGTH4 FIELD32(0x00000fff) + +/* + * Word14 + */ +#define TXD_W14_SK_BUFFER FIELD32(0xffffffff) + +/* + * Word15 + */ +#define TXD_W15_NEXT_SK_BUFFER FIELD32(0xffffffff) + +/* + * RX descriptor format for RX Ring. + */ + +/* + * Word0 + * CIPHER_ERROR: 1:ICV error, 2:MIC error, 3:invalid key. + * KEY_INDEX: Decryption key actually used. + */ +#define RXD_W0_OWNER_NIC FIELD32(0x00000001) +#define RXD_W0_DROP FIELD32(0x00000002) +#define RXD_W0_UNICAST_TO_ME FIELD32(0x00000004) +#define RXD_W0_MULTICAST FIELD32(0x00000008) +#define RXD_W0_BROADCAST FIELD32(0x00000010) +#define RXD_W0_MY_BSS FIELD32(0x00000020) +#define RXD_W0_CRC FIELD32(0x00000040) +#define RXD_W0_OFDM FIELD32(0x00000080) +#define RXD_W0_CIPHER_ERROR FIELD32(0x00000300) +#define RXD_W0_KEY_INDEX FIELD32(0x0000fc00) +#define RXD_W0_DATABYTE_COUNT FIELD32(0x0fff0000) +#define RXD_W0_CIPHER_ALG FIELD32(0xe0000000) + +/* + * Word1 + * SIGNAL: RX raw data rate reported by BBP. + * RSSI: RSSI reported by BBP. + */ +#define RXD_W1_SIGNAL FIELD32(0x000000ff) +#define RXD_W1_RSSI FIELD32(0x0000ff00) +#define RXD_W1_FRAME_OFFSET FIELD32(0x7f000000) + +/* + * Word2 + * IV: Received IV of originally encrypted. + */ +#define RXD_W2_IV FIELD32(0xffffffff) + +/* + * Word3 + * EIV: Received EIV of originally encrypted. + */ +#define RXD_W3_EIV FIELD32(0xffffffff) + +/* + * Word4 + */ +#define RXD_W4_RESERVED FIELD32(0xffffffff) + +/* + * the above 20-byte is called RXINFO and will be DMAed to MAC RX block + * and passed to the HOST driver. + * The following fields are for DMA block and HOST usage only. + * Can't be touched by ASIC MAC block. + */ + +/* + * Word5 + */ +#define RXD_W5_BUFFER_PHYSICAL_ADDRESS FIELD32(0xffffffff) + +/* + * Word6-15: Reserved + */ +#define RXD_W6_RESERVED FIELD32(0xffffffff) +#define RXD_W7_RESERVED FIELD32(0xffffffff) +#define RXD_W8_RESERVED FIELD32(0xffffffff) +#define RXD_W9_RESERVED FIELD32(0xffffffff) +#define RXD_W10_RESERVED FIELD32(0xffffffff) +#define RXD_W11_RESERVED FIELD32(0xffffffff) +#define RXD_W12_RESERVED FIELD32(0xffffffff) +#define RXD_W13_RESERVED FIELD32(0xffffffff) +#define RXD_W14_RESERVED FIELD32(0xffffffff) +#define RXD_W15_RESERVED FIELD32(0xffffffff) + +/* + * TX ring index number for rt2x00_dev structure. + */ +enum ring_index { + RING_AC_VO = 0, + RING_AC_VI = 1, + RING_AC_BE = 2, + RING_AC_BK = 3, + RING_PRIO = 4, + RING_BEACON = 5, + RING_RX = 6, + RING_NUM = 7, + RING_NUM_TX = 5, +}; + +/* + * Macro's for converting txpower from EEPROM to dscape value + * and from dscape value to register value. + */ +#define MIN_TXPOWER 0 +#define MAX_TXPOWER 31 +#define DEFAULT_TXPOWER 24 + +#define TXPOWER_FROM_DEV(__txpower) \ + ({ \ + ((__txpower) > MAX_TXPOWER) ? DEFAULT_TXPOWER : (__txpower); \ + }) + +#define TXPOWER_TO_DEV(__txpower) \ + ({ \ + ((__txpower) <= MIN_TXPOWER) ? MIN_TXPOWER : \ + (((__txpower) >= MAX_TXPOWER) ? MAX_TXPOWER : \ + (__txpower)); \ + }) + +/* + * LED control functions. + */ +static void rt61pci_enable_led(struct rt2x00_dev *rt2x00dev); +static void rt61pci_disable_led(struct rt2x00_dev *rt2x00dev); +static void rt61pci_activity_led(struct rt2x00_dev *rt2x00dev, char rssi); + +#endif /* RT61PCI_H */ diff --git a/drivers/net/wireless/mac80211/rt2x00/rt73usb.c b/drivers/net/wireless/mac80211/rt2x00/rt73usb.c new file mode 100644 index 0000000..07ec1bd --- /dev/null +++ b/drivers/net/wireless/mac80211/rt2x00/rt73usb.c @@ -0,0 +1,1966 @@ +/* + Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + + + 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. + */ + +/* + Module: rt73usb + Abstract: rt73usb device specific routines. + Supported chipsets: rt2571W & rt2671. + */ + +/* + * Set enviroment defines for rt2x00.h + */ +#define DRV_NAME "rt73usb" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt2x00.h" +#include "rt2x00usb.h" +#include "rt73usb.h" + +/* + * Register access. + * All access to the CSR registers will go through the methods + * rt2x00_register_read and rt2x00_register_write. + * BBP and RF register require indirect register access, + * and use the CSR registers BBPCSR and RFCSR to achieve this. + * These indirect registers work with busy bits, + * and we will try maximal REGISTER_BUSY_COUNT times to access + * the register while taking a REGISTER_BUSY_DELAY us delay + * between each attampt. When the busy bit is still set at that time, + * the access attempt is considered to have failed, + * and we will print an error. + */ +static int rt2x00_vendor_request(const struct rt2x00_dev *rt2x00dev, + const u8 request, const u8 type, const u16 offset, + u32 value, void *buffer, const u16 buffer_length, const u16 timeout) +{ + struct usb_device *usb_dev = interface_to_usbdev( + rt2x00dev_usb(rt2x00dev)); + int status; + unsigned int i; + + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + status = usb_control_msg( + usb_dev, + (type == USB_VENDOR_REQUEST_IN) ? + usb_rcvctrlpipe(usb_dev, 0) : + usb_sndctrlpipe(usb_dev, 0), + request, type, value, offset, buffer, buffer_length, + timeout); + if (status >= 0) + return 0; + } + + ERROR("vendor request error. Request 0x%02x failed " + "for offset 0x%04x with error %d.\n", request, offset, status); + + return status; +} + +static inline void rt2x00_register_read( + const struct rt2x00_dev *rt2x00dev, + const u16 offset, u32 *value) +{ + __le32 reg; + rt2x00_vendor_request( + rt2x00dev, USB_MULTI_READ, USB_VENDOR_REQUEST_IN, + offset, 0x00, ®, sizeof(u32), REGISTER_TIMEOUT); + *value = le32_to_cpu(reg); +} + +static inline void rt2x00_register_multiread( + const struct rt2x00_dev *rt2x00dev, + const u16 offset, void *value, const u32 length) +{ + rt2x00_vendor_request( + rt2x00dev, USB_MULTI_READ, USB_VENDOR_REQUEST_IN, + offset, 0x00, value, length, + REGISTER_TIMEOUT * (length / sizeof(u32))); +} + +static inline void rt2x00_register_write( + const struct rt2x00_dev *rt2x00dev, + const u16 offset, u32 value) +{ + __le32 reg = cpu_to_le32(value); + rt2x00_vendor_request( + rt2x00dev, USB_MULTI_WRITE, USB_VENDOR_REQUEST_OUT, + offset, 0x00, ®, sizeof(u32), REGISTER_TIMEOUT); +} + +static inline void rt2x00_register_multiwrite( + const struct rt2x00_dev *rt2x00dev, + const u16 offset, void *value, const u32 length) +{ + rt2x00_vendor_request( + rt2x00dev, USB_MULTI_WRITE, USB_VENDOR_REQUEST_OUT, + offset, 0x00, value, length, + REGISTER_TIMEOUT * (length / sizeof(u32))); +} + +static u32 rt2x00_bbp_check(const struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + unsigned int i; + + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2x00_register_read(rt2x00dev, PHY_CSR3, ®); + if (!rt2x00_get_field32(reg, PHY_CSR3_BUSY)) + return reg; + udelay(REGISTER_BUSY_DELAY); + } + + return 0xffff; +} + +static void rt2x00_bbp_write(const struct rt2x00_dev *rt2x00dev, + const u8 reg_id, const u8 value) +{ + u32 reg; + + /* + * Wait until the BBP becomes ready. + */ + if (rt2x00_bbp_check(rt2x00dev) == 0xffff) { + ERROR("PHY_CSR3 register busy. Write failed.\n"); + return; + } + + /* + * Write the data into the BBP. + */ + reg = 0; + rt2x00_set_field32(®, PHY_CSR3_VALUE, value); + rt2x00_set_field32(®, PHY_CSR3_REGNUM, reg_id); + rt2x00_set_field32(®, PHY_CSR3_BUSY, 1); + rt2x00_set_field32(®, PHY_CSR3_READ_CONTROL, 0); + + rt2x00_register_write(rt2x00dev, PHY_CSR3, reg); +} + +static void rt2x00_bbp_read(const struct rt2x00_dev *rt2x00dev, + const u8 reg_id, u8 *value) +{ + u32 reg; + + /* + * Wait until the BBP becomes ready. + */ + if (rt2x00_bbp_check(rt2x00dev) == 0xffff) { + ERROR("PHY_CSR3 register busy. Read failed.\n"); + return; + } + + /* + * Write the request into the BBP. + */ + reg =0; + rt2x00_set_field32(®, PHY_CSR3_REGNUM, reg_id); + rt2x00_set_field32(®, PHY_CSR3_BUSY, 1); + rt2x00_set_field32(®, PHY_CSR3_READ_CONTROL, 1); + + rt2x00_register_write(rt2x00dev, PHY_CSR3, reg); + + /* + * Wait until the BBP becomes ready. + */ + reg = rt2x00_bbp_check(rt2x00dev); + if (reg == 0xffff) + ERROR("PHY_CSR3 register busy. Read failed.\n"); + + *value = rt2x00_get_field32(reg, PHY_CSR3_VALUE); +} + +static void rt2x00_rf_write(const struct rt2x00_dev *rt2x00dev, + const u32 value) +{ + u32 reg; + unsigned int i; + + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2x00_register_read(rt2x00dev, PHY_CSR4, ®); + if (!rt2x00_get_field32(reg, PHY_CSR4_BUSY)) + goto rf_write; + udelay(REGISTER_BUSY_DELAY); + } + + ERROR("PHY_CSR4 register busy. Write failed.\n"); + return; + +rf_write: + reg = 0; + rt2x00_set_field32(®, PHY_CSR4_VALUE, value); + rt2x00_set_field32(®, PHY_CSR4_NUMBER_OF_BITS, 20); + rt2x00_set_field32(®, PHY_CSR4_IF_SELECT, 0); + rt2x00_set_field32(®, PHY_CSR4_BUSY, 1); + + rt2x00_register_write(rt2x00dev, PHY_CSR4, reg); +} + +#ifdef CONFIG_RT2X00_DEBUGFS +#define CSR_OFFSET(__word) ( CSR_REG_BASE + ((__word) * sizeof(u32)) ) + +static void rt73usb_read_csr(struct rt2x00_dev *rt2x00dev, + const unsigned long word, void *data) +{ + rt2x00_register_read(rt2x00dev, CSR_OFFSET(word), data); +} + +static void rt73usb_write_csr(struct rt2x00_dev *rt2x00dev, + const unsigned long word, void *data) +{ + rt2x00_register_write(rt2x00dev, CSR_OFFSET(word), *((u32*)data)); +} + +static void rt73usb_read_eeprom(struct rt2x00_dev *rt2x00dev, + const unsigned long word, void *data) +{ + rt2x00_eeprom_read(rt2x00dev, word, data); +} + +static void rt73usb_write_eeprom(struct rt2x00_dev *rt2x00dev, + const unsigned long word, void *data) +{ + rt2x00_eeprom_write(rt2x00dev, word, *((u16*)data)); +} + +static void rt73usb_read_bbp(struct rt2x00_dev *rt2x00dev, + const unsigned long word, void *data) +{ + rt2x00_bbp_read(rt2x00dev, word, data); +} + +static void rt73usb_write_bbp(struct rt2x00_dev *rt2x00dev, + const unsigned long word, void *data) +{ + rt2x00_bbp_write(rt2x00dev, word, *((u8*)data)); +} + +static const struct rt2x00debug rt73usb_rt2x00debug = { + .owner = THIS_MODULE, + .mod_name = DRV_NAME, + .mod_version = DRV_VERSION, + .reg_csr = { + .read = rt73usb_read_csr, + .write = rt73usb_write_csr, + .word_size = sizeof(u32), + .word_count = CSR_REG_SIZE / sizeof(u32), + }, + .reg_eeprom = { + .read = rt73usb_read_eeprom, + .write = rt73usb_write_eeprom, + .word_size = sizeof(u16), + .word_count = EEPROM_SIZE / sizeof(u16), + }, + .reg_bbp = { + .read = rt73usb_read_bbp, + .write = rt73usb_write_bbp, + .word_size = sizeof(u8), + .word_count = BBP_SIZE / sizeof(u8), + }, +}; +#endif /* CONFIG_RT2X00_DEBUGFS */ + +/* + * Configuration handlers. + */ +static void rt73usb_config_bssid(struct rt2x00_dev *rt2x00dev, u8 *bssid) +{ + u32 reg[2]; + + memset(®, 0, sizeof(reg)); + memcpy(®, bssid, ETH_ALEN); + + rt2x00_set_field32(®[1], MAC_CSR5_BSS_ID_MASK, 3); + + /* + * The BSSID is passed to us as an array of bytes, + * that array is little endian, so no need for byte ordering. + */ + rt2x00_register_multiwrite(rt2x00dev, MAC_CSR4, ®, sizeof(reg)); +} + +static void rt73usb_config_promisc(struct rt2x00_dev *rt2x00dev, int promisc) +{ + u32 reg; + + rt2x00_register_read(rt2x00dev, TXRX_CSR0, ®); + + if (promisc) { + rt2x00_set_field32(®, TXRX_CSR0_DROP_NOT_TO_ME, 0); + SET_FLAG(rt2x00dev, INTERFACE_ENABLED_PROMISC); + } else { + rt2x00_set_field32(®, TXRX_CSR0_DROP_NOT_TO_ME, 1); + CLEAR_FLAG(rt2x00dev, INTERFACE_ENABLED_PROMISC); + } + + rt2x00_register_write(rt2x00dev, TXRX_CSR0, reg); +} + +static void rt73usb_config_type(struct rt2x00_dev *rt2x00dev, int type) +{ + u32 reg; + + /* + * Only continue when there is something to be done. + */ + if (!(GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED) ^ + GET_FLAG(rt2x00dev, INTERFACE_ENABLED)) && + !(GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED_MONITOR) ^ + GET_FLAG(rt2x00dev, INTERFACE_ENABLED_MONITOR))) + return; + + rt2x00_register_write(rt2x00dev, TXRX_CSR9, 0); + + /* + * Apply hardware packet filter. + */ + rt2x00_register_read(rt2x00dev, TXRX_CSR0, ®); + + if (!GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED_MONITOR) && + (type == IEEE80211_IF_TYPE_IBSS || type == IEEE80211_IF_TYPE_STA)) + rt2x00_set_field32(®, TXRX_CSR0_DROP_TO_DS, 1); + else + rt2x00_set_field32(®, TXRX_CSR0_DROP_TO_DS, 0); + + rt2x00_set_field32(®, TXRX_CSR0_DROP_CRC, 1); + if (GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED_MONITOR)) { + rt2x00_set_field32(®, TXRX_CSR0_DROP_PHYSICAL, 0); + rt2x00_set_field32(®, TXRX_CSR0_DROP_CONTROL, 0); + rt2x00_set_field32(®, TXRX_CSR0_DROP_VERSION_ERROR, 0); + } else { + rt2x00_set_field32(®, TXRX_CSR0_DROP_PHYSICAL, 1); + rt2x00_set_field32(®, TXRX_CSR0_DROP_CONTROL, 1); + rt2x00_set_field32(®, TXRX_CSR0_DROP_VERSION_ERROR, 1); + } + + rt2x00_set_field32(®, TXRX_CSR0_DROP_MULTICAST, 0); + rt2x00_set_field32(®, TXRX_CSR0_DROP_BORADCAST, 0); + + rt2x00_register_write(rt2x00dev, TXRX_CSR0, reg); + + /* + * Enable promisc mode when in monitor mode. + */ + if (GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED_MONITOR)) + rt73usb_config_promisc(rt2x00dev, 1); + + /* + * Enable synchronisation. + */ + rt2x00_register_read(rt2x00dev, TXRX_CSR9, ®); + if (GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED)) { + rt2x00_set_field32(®, TXRX_CSR9_BEACON_INTERVAL, 100 * 16); + rt2x00_set_field32(®, TXRX_CSR9_TSF_TICKING, 1); + rt2x00_set_field32(®, TXRX_CSR9_TBTT_ENABLE, 1); + } + + rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); + if (type == IEEE80211_IF_TYPE_IBSS || type == IEEE80211_IF_TYPE_AP) + rt2x00_set_field32(®, TXRX_CSR9_TSF_SYNC, 2); + else if (type == IEEE80211_IF_TYPE_STA) + rt2x00_set_field32(®, TXRX_CSR9_TSF_SYNC, 1); + else if (GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED_MONITOR) && + !GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED)) + rt2x00_set_field32(®, TXRX_CSR9_TSF_SYNC, 0); + + rt2x00_register_write(rt2x00dev, TXRX_CSR9, reg); + + /* + * Change flags of enabled interfaces. + */ + if (type != IEEE80211_IF_TYPE_MNTR) { + if (GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED)) + SET_FLAG(rt2x00dev, INTERFACE_ENABLED); + else + CLEAR_FLAG(rt2x00dev, INTERFACE_ENABLED); + } else { + if (GET_FLAG(rt2x00dev, INTERFACE_INITIALIZED_MONITOR)) + SET_FLAG(rt2x00dev, INTERFACE_ENABLED_MONITOR); + else + CLEAR_FLAG(rt2x00dev, INTERFACE_ENABLED_MONITOR); + } +} + +static void rt73usb_config_channel(struct rt2x00_dev *rt2x00dev, + int value, int channel, int freq, int txpower) +{ + u8 reg = 0; + u32 rf1 = rt2x00dev->rf1; + u32 rf2 = value; + u32 rf3 = rt2x00dev->rf3; + u32 rf4 = 0; + + /* + * Only continue when there is something to be done. + */ + if (channel == rt2x00dev->rx_status.channel) + return; + + if (txpower == 0xff) + txpower = rt2x00dev->tx_power; + else + txpower = TXPOWER_TO_DEV(txpower); + + if (rt2x00_rf(&rt2x00dev->chip, RF5225) || + rt2x00_rf(&rt2x00dev->chip, RF2527)) + rf2 |= 0x00004000; + + if (rt2x00_rf(&rt2x00dev->chip, RF5225)) { + if (channel <= 14) + rf3 = 0x00068455; + else if (channel >= 36 && channel <= 48) + rf3 = 0x0009be55; + else if (channel >= 52 && channel <= 64) + rf3 = 0x0009ae55; + else if (channel >= 100 && channel <= 112) + rf3 = 0x000bae55; + else + rf3 = 0x000bbe55; + } + + if (channel < 14) { + if (channel & 0x01) + rf4 = 0x000fea0b; + else + rf4 = 0x000fea1f; + } else if (channel == 14) { + rf4 = 0x000fea13; + } else { + switch (channel) { + case 36: + case 56: + case 116: + case 136: + rf4 = 0x000fea23; + break; + case 40: + case 60: + case 100: + case 120: + case 140: + rf4 = 0x000fea03; + break; + case 44: + case 64: + case 104: + case 124: + rf4 = 0x000fea0b; + break; + case 48: + case 108: + case 128: + rf4 = 0x000fea13; + break; + case 52: + case 112: + case 132: + rf4 = 0x000fea1b; + break; + case 149: + rf4 = 0x000fea1f; + break; + case 153: + rf4 = 0x000fea27; + break; + case 157: + rf4 = 0x000fea07; + break; + case 161: + rf4 = 0x000fea0f; + break; + case 165: + rf4 = 0x000fea17; + break; + } + } + + if (rt2x00_rf(&rt2x00dev->chip, RF2527) || + rt2x00_rf(&rt2x00dev->chip, RF5225)) + rf4 |= 0x00010000; + + /* + * Set TXpower. + */ + rt2x00_set_field32(&rf3, RF3_TXPOWER, txpower); + + INFO("Switching channel. RF1: 0x%08x, RF2: 0x%08x, RF3: 0x%08x, " + "RF4: 0x%08x.\n", rf1, rf2, rf3, rf4); + + /* + * Set Frequency offset. + */ + rt2x00_set_field32(&rf4, RF4_FREQ_OFFSET, rt2x00dev->freq_offset); + + rt2x00_bbp_read(rt2x00dev, 3, ®); + if (rt2x00_rf(&rt2x00dev->chip, RF5225) || + rt2x00_rf(&rt2x00dev->chip, RF2527)) + reg &= ~0x01; + else + reg |= 0x01; + rt2x00_bbp_write(rt2x00dev, 3, reg); + + rt2x00_rf_write(rt2x00dev, rf1); + rt2x00_rf_write(rt2x00dev, rf2); + rt2x00_rf_write(rt2x00dev, rf3 & ~0x00000004); + rt2x00_rf_write(rt2x00dev, rf4); + + rt2x00_rf_write(rt2x00dev, rf1); + rt2x00_rf_write(rt2x00dev, rf2); + rt2x00_rf_write(rt2x00dev, rf3 | 0x00000004); + rt2x00_rf_write(rt2x00dev, rf4); + + rt2x00_rf_write(rt2x00dev, rf1); + rt2x00_rf_write(rt2x00dev, rf2); + rt2x00_rf_write(rt2x00dev, rf3 & ~0x00000004); + rt2x00_rf_write(rt2x00dev, rf4); + + msleep(1); + + /* + * Update active info for RX. + */ + rt2x00dev->rx_status.freq = freq; + rt2x00dev->rx_status.channel = channel; + + /* + * Update rf fields + */ + rt2x00dev->rf1 = rf1; + rt2x00dev->rf2 = rf2; + rt2x00dev->rf3 = rf3; + rt2x00dev->rf4 = rf4; + + rt2x00dev->tx_power = txpower; +} + +static void rt73usb_config_txpower(struct rt2x00_dev *rt2x00dev, int txpower) +{ + txpower = TXPOWER_TO_DEV(txpower); + + /* + * Only continue when there is something to be done. + */ + if (txpower == rt2x00dev->tx_power) + return; + + rt2x00_set_field32(&rt2x00dev->rf3, RF3_TXPOWER, txpower); + + rt2x00_rf_write(rt2x00dev, rt2x00dev->rf1); + rt2x00_rf_write(rt2x00dev, rt2x00dev->rf2); + rt2x00_rf_write(rt2x00dev, rt2x00dev->rf3 & ~0x00000004); + rt2x00_rf_write(rt2x00dev, rt2x00dev->rf4); + + rt2x00_rf_write(rt2x00dev, rt2x00dev->rf1); + rt2x00_rf_write(rt2x00dev, rt2x00dev->rf2); + rt2x00_rf_write(rt2x00dev, rt2x00dev->rf3 | 0x00000004); + rt2x00_rf_write(rt2x00dev, rt2x00dev->rf4); + + rt2x00_rf_write(rt2x00dev, rt2x00dev->rf1); + rt2x00_rf_write(rt2x00dev, rt2x00dev->rf2); + rt2x00_rf_write(rt2x00dev, rt2x00dev->rf3 & ~0x00000004); + rt2x00_rf_write(rt2x00dev, rt2x00dev->rf4); + + rt2x00dev->tx_power = txpower; +} + +static void rt73usb_config_antenna(struct rt2x00_dev *rt2x00dev, + int antenna_tx, int antenna_rx) +{ + u32 reg; + u8 reg_r3; + u8 reg_r4; + u8 reg_r77; + u8 frame_type; + + /* + * Only continue when there is something to be done. + */ + if (rt2x00dev->rx_status.antenna == antenna_rx) + return; + + rt2x00_register_read(rt2x00dev, PHY_CSR0, ®); + + if (rt2x00dev->curr_hwmode == HWMODE_A) { + if (GET_FLAG(rt2x00dev, CONFIG_EXTERNAL_LNA)) { + rt2x00_bbp_write(rt2x00dev, 17, 0x38); + rt2x00_bbp_write(rt2x00dev, 96, 0x78); + rt2x00_bbp_write(rt2x00dev, 104, 0x48); + rt2x00_bbp_write(rt2x00dev, 75, 0x80); + rt2x00_bbp_write(rt2x00dev, 86, 0x80); + rt2x00_bbp_write(rt2x00dev, 88, 0x80); + } else { + rt2x00_bbp_write(rt2x00dev, 17, 0x28); + rt2x00_bbp_write(rt2x00dev, 96, 0x58); + rt2x00_bbp_write(rt2x00dev, 104, 0x38); + rt2x00_bbp_write(rt2x00dev, 75, 0xfe); + rt2x00_bbp_write(rt2x00dev, 86, 0xfe); + rt2x00_bbp_write(rt2x00dev, 88, 0xfe); + } + rt2x00_bbp_write(rt2x00dev, 35, 0x60); + rt2x00_bbp_write(rt2x00dev, 97, 0x58); + rt2x00_bbp_write(rt2x00dev, 98, 0x58); + + rt2x00_set_field32(®, PHY_CSR0_PA_PE_BG, 0); + rt2x00_set_field32(®, PHY_CSR0_PA_PE_A, 1); + } else { + if (GET_FLAG(rt2x00dev, CONFIG_EXTERNAL_LNA)) { + rt2x00_bbp_write(rt2x00dev, 17, 0x30); + rt2x00_bbp_write(rt2x00dev, 96, 0x68); + rt2x00_bbp_write(rt2x00dev, 104, 0x3c); + rt2x00_bbp_write(rt2x00dev, 75, 0x80); + rt2x00_bbp_write(rt2x00dev, 86, 0x80); + rt2x00_bbp_write(rt2x00dev, 88, 0x80); + } else { + rt2x00_bbp_write(rt2x00dev, 17, 0x20); + rt2x00_bbp_write(rt2x00dev, 96, 0x48); + rt2x00_bbp_write(rt2x00dev, 104, 0x2c); + rt2x00_bbp_write(rt2x00dev, 75, 0xfe); + rt2x00_bbp_write(rt2x00dev, 86, 0xfe); + rt2x00_bbp_write(rt2x00dev, 88, 0xfe); + } + rt2x00_bbp_write(rt2x00dev, 35, 0x50); + rt2x00_bbp_write(rt2x00dev, 97, 0x48); + rt2x00_bbp_write(rt2x00dev, 98, 0x48); + + rt2x00_set_field32(®, PHY_CSR0_PA_PE_BG, 1); + rt2x00_set_field32(®, PHY_CSR0_PA_PE_A, 0); + } + + rt2x00_register_write(rt2x00dev, PHY_CSR0, reg); + + rt2x00_bbp_read(rt2x00dev, 3, ®_r3); + rt2x00_bbp_read(rt2x00dev, 4, ®_r4); + rt2x00_bbp_read(rt2x00dev, 77, ®_r77); + + reg_r3 &= ~0x01; + reg_r4 &= ~0x23; + frame_type = ~(GET_FLAG(rt2x00dev, CONFIG_FRAME_TYPE) << 5); + + if (rt2x00_rf(&rt2x00dev->chip, RF5226) || + rt2x00_rf(&rt2x00dev->chip, RF5225)) { + if (antenna_rx == 0) { /* Diversity. */ + reg_r4 |= 0x02; + if (rt2x00dev->curr_hwmode != HWMODE_A) + reg_r4 |= 0x20; + reg_r4 &= frame_type; + } else if (antenna_rx == 1) { /* RX: Antenna A */ + reg_r4 |= 0x01; + reg_r4 &= frame_type; + if (rt2x00dev->curr_hwmode == HWMODE_A) + reg_r77 &= ~0x03; + else + reg_r77 |= 0x03; + rt2x00_bbp_write(rt2x00dev, 77, reg_r77); + } else if (antenna_rx == 2) { /* RX: Antenna B */ + reg_r4 |= 0x01; + reg_r4 &= frame_type; + if (rt2x00dev->curr_hwmode == HWMODE_A) + reg_r77 |= 0x03; + else + reg_r77 &= ~0x03; + rt2x00_bbp_write(rt2x00dev, 77, reg_r77); + } + } else if (rt2x00_rf(&rt2x00dev->chip, RF2528) || + rt2x00_rf(&rt2x00dev->chip, RF2527)) { + if (antenna_rx == 0) { /* Diversity. */ + reg_r4 |= 0x22; + reg_r4 &= frame_type; + } else if (antenna_rx == 1) { /* RX: Antenna A */ + reg_r4 |= 0x21; + reg_r4 &= frame_type; + reg_r77 |= 0x03; + rt2x00_bbp_write(rt2x00dev, 77, reg_r77); + } else if (antenna_rx == 2) { /* RX: Antenna B */ + reg_r4 |= 0x21; + reg_r4 &= frame_type; + reg_r77 &= ~0x03; + rt2x00_bbp_write(rt2x00dev, 77, reg_r77); + } + } + + rt2x00_bbp_write(rt2x00dev, 3, reg_r3); + rt2x00_bbp_write(rt2x00dev, 4, reg_r4); + + /* + * Update active info for RX. + */ + rt2x00dev->rx_status.antenna = antenna_rx; +} + +static void rt73usb_config_duration(struct rt2x00_dev *rt2x00dev, + int short_slot_time) +{ + u32 reg; + + short_slot_time = short_slot_time ? SHORT_SLOT_TIME : SLOT_TIME; + + rt2x00_register_read(rt2x00dev, MAC_CSR9, ®); + rt2x00_set_field32(®, MAC_CSR9_SLOT_TIME, short_slot_time); + rt2x00_register_write(rt2x00dev, MAC_CSR9, reg); + + rt2x00_register_read(rt2x00dev, MAC_CSR8, ®); + rt2x00_set_field32(®, MAC_CSR8_SIFS, SIFS); + rt2x00_set_field32(®, MAC_CSR8_SIFS_AFTER_RX_OFDM, 3); + rt2x00_set_field32(®, MAC_CSR8_EIFS, EIFS); + rt2x00_register_write(rt2x00dev, MAC_CSR8, reg); + + rt2x00_register_read(rt2x00dev, TXRX_CSR0, ®); + rt2x00_set_field32(®, TXRX_CSR0_TSF_OFFSET, IEEE80211_HEADER); + rt2x00_register_write(rt2x00dev, TXRX_CSR0, reg); + + rt2x00_register_read(rt2x00dev, TXRX_CSR4, ®); + rt2x00_set_field32(®, TXRX_CSR4_AUTORESPOND_ENABLE, 1); + rt2x00_register_write(rt2x00dev, TXRX_CSR4, reg); +} + +static void rt73usb_config_rate(struct rt2x00_dev *rt2x00dev, const int rate) +{ + struct ieee80211_conf *conf = &rt2x00dev->hw->conf; + u32 reg; + u32 value; + u32 preamble; + + preamble = DEVICE_GET_RATE_FIELD(rate, PREAMBLE) + ? SHORT_PREAMBLE : PREAMBLE; + + reg = DEVICE_GET_RATE_FIELD(rate, RATEMASK) & DEV_BASIC_RATE; + + rt2x00_register_write(rt2x00dev, TXRX_CSR5, reg); + + rt2x00_register_read(rt2x00dev, TXRX_CSR0, ®); + value = ((conf->flags & IEEE80211_CONF_SHORT_SLOT_TIME) ? + SHORT_DIFS : DIFS) + + PLCP + preamble + get_duration(ACK_SIZE, 10); + rt2x00_set_field32(®, TXRX_CSR0_RX_ACK_TIMEOUT, value); + rt2x00_register_write(rt2x00dev, TXRX_CSR0, reg); + + rt2x00_register_read(rt2x00dev, TXRX_CSR4, ®); + if (preamble == SHORT_PREAMBLE) + rt2x00_set_field32(®, TXRX_CSR4_AUTORESPOND_PREAMBLE, 1); + else + rt2x00_set_field32(®, TXRX_CSR4_AUTORESPOND_PREAMBLE, 0); + rt2x00_register_write(rt2x00dev, TXRX_CSR4, reg); +} + +static void rt73usb_config_phymode(struct rt2x00_dev *rt2x00dev, + const int phymode) +{ + struct ieee80211_hw_mode *mode; + struct ieee80211_rate *rate; + + /* + * Only continue when there is something to be done. + */ + if (rt2x00dev->rx_status.phymode == phymode) + return; + + if (phymode == MODE_IEEE80211A) + rt2x00dev->curr_hwmode = HWMODE_A; + else if (phymode == MODE_IEEE80211B) + rt2x00dev->curr_hwmode = HWMODE_B; + else + rt2x00dev->curr_hwmode = HWMODE_G; + + mode = &rt2x00dev->hwmodes[rt2x00dev->curr_hwmode]; + rate = &mode->rates[mode->num_rates - 1]; + + rt73usb_config_rate(rt2x00dev, rate->val2); + + /* + * Update physical mode for rx ring. + */ + rt2x00dev->rx_status.phymode = phymode; +} + +static void rt73usb_config_mac_addr(struct rt2x00_dev *rt2x00dev, u8 *addr) +{ + u32 reg[2]; + + memset(®, 0, sizeof(reg)); + memcpy(®, addr, ETH_ALEN); + + rt2x00_set_field32(®[1], MAC_CSR3_UNICAST_TO_ME_MASK, 0xff); + + /* + * The MAC address is passed to us as an array of bytes, + * that array is little endian, so no need for byte ordering. + */ + rt2x00_register_multiwrite(rt2x00dev, MAC_CSR2, ®, sizeof(reg)); +} + +/* + * Link tuning + */ +static void rt73usb_link_tuner(struct work_struct *work) +{ + struct rt2x00_dev *rt2x00dev = + container_of(work, struct rt2x00_dev, link.work.work); + u32 reg; + u32 rssi; + u8 reg_r17; + u8 up_bound; + u8 low_bound; + + /* + * Retrieve link quality. + */ + rssi = rt2x00_get_link_rssi(&rt2x00dev->link); + if (!rssi) + goto exit; + + /* + * Update LED. + */ + rt73usb_activity_led(rt2x00dev, rssi); + + /* + * Determine upper and lower limits for BBP17 register. + */ + if (rt2x00dev->rx_status.phymode == MODE_IEEE80211A) { + up_bound = 0x48; + low_bound = 0x28; + } else { + if (rssi > 38) { + up_bound = 0x40; + low_bound = 0x1c; + } else if (rssi > 36) { + up_bound = 0x20; + low_bound = 0x1c; + } else { + up_bound = 0x1c; + low_bound = 0x1c; + } + + if (GET_FLAG(rt2x00dev, CONFIG_EXTERNAL_LNA)) { + up_bound += 0x10; + low_bound += 0x14; + } + } + + rt2x00_bbp_read(rt2x00dev, 17, ®_r17); + + if (rssi >= 85) { + if (reg_r17 != 0x60) + rt2x00_bbp_write(rt2x00dev, 17, 0x60); + goto exit; + } else if (rssi >= 62) { + if (reg_r17 != up_bound) + rt2x00_bbp_write(rt2x00dev, 17, up_bound); + goto exit; + } else if (rssi >= 54) { + low_bound += 0x10; + if (reg_r17 != low_bound) + rt2x00_bbp_write(rt2x00dev, 17, low_bound); + goto exit; + } else if (rssi >= 46) { + low_bound += 0x08; + if (reg_r17 != low_bound) + rt2x00_bbp_write(rt2x00dev, 17, low_bound); + goto exit; + } else { + up_bound -= 2 * (46 - rssi); + if (up_bound < low_bound) + up_bound = low_bound; + + if (reg_r17 > up_bound) { + rt2x00_bbp_write(rt2x00dev, 17, up_bound); + goto exit; + } + } + + rt2x00_register_read(rt2x00dev, STA_CSR1, ®); + reg = rt2x00_get_field32(reg, STA_CSR1_FALSE_CCA_ERROR); + + if (reg > 512 && reg_r17 < up_bound) + rt2x00_bbp_write(rt2x00dev, 17, ++reg_r17); + else if (reg < 100 && reg_r17 > low_bound) + rt2x00_bbp_write(rt2x00dev, 17, --reg_r17); + +exit: + if (reg_r17) + rt2x00_update_link_noise(&rt2x00dev->link, reg_r17); + + queue_delayed_work(rt2x00dev->workqueue, &rt2x00dev->link.work, + LINK_TUNE_INTERVAL); +} + +/* + * LED functions. + */ +static void rt73usb_enable_led(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + rt2x00_register_read(rt2x00dev, MAC_CSR14, ®); + rt2x00_set_field32(®, MAC_CSR14_ON_PERIOD, 70); + rt2x00_set_field32(®, MAC_CSR14_OFF_PERIOD, 30); + rt2x00_register_write(rt2x00dev, MAC_CSR14, reg); + + rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_RADIO_STATUS, 1); + if (rt2x00dev->rx_status.phymode == MODE_IEEE80211A) + rt2x00_set_field16( + &rt2x00dev->led_reg, MCU_LEDCS_LINK_A_STATUS, 1); + else + rt2x00_set_field16( + &rt2x00dev->led_reg, MCU_LEDCS_LINK_BG_STATUS, 1); + + rt2x00_vendor_request( + rt2x00dev, USB_LED_CONTROL, USB_VENDOR_REQUEST_OUT, + 0x00, rt2x00dev->led_reg, NULL, 0, REGISTER_TIMEOUT); +} + +static void rt73usb_disable_led(struct rt2x00_dev *rt2x00dev) +{ + rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_RADIO_STATUS, 0); + rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_LINK_BG_STATUS, 0); + rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_LINK_A_STATUS, 0); + + rt2x00_vendor_request( + rt2x00dev, USB_LED_CONTROL, USB_VENDOR_REQUEST_OUT, + 0x00, rt2x00dev->led_reg, NULL, 0, REGISTER_TIMEOUT); +} + +static void rt73usb_activity_led(struct rt2x00_dev *rt2x00dev, char rssi) +{ + u32 led; + + if (rt2x00dev->led_mode != LED_MODE_SIGNAL_STRENGTH) + return; + + if (rssi <= 30) + led = 0; + else if (rssi <= 39) + led = 1; + else if (rssi <= 49) + led = 2; + else if (rssi <= 53) + led = 3; + else if (rssi <= 63) + led = 4; + else + led = 5; + + rt2x00_vendor_request( + rt2x00dev, USB_LED_CONTROL, USB_VENDOR_REQUEST_OUT, + led, rt2x00dev->led_reg, NULL, 0, REGISTER_TIMEOUT); +} + +/* + * Device state switch. + * This will put the device to sleep, or awake it. + */ +static int rt73usb_set_state(struct rt2x00_dev *rt2x00dev, + enum dev_state state) +{ + u32 reg; + unsigned int i; + char put_to_sleep; + char current_state; + + put_to_sleep = (state != STATE_AWAKE); + + if (!put_to_sleep) + rt2x00_vendor_request(rt2x00dev, + USB_DEVICE_MODE, USB_VENDOR_REQUEST_OUT, + 0x00, USB_MODE_WAKEUP, NULL, 0, REGISTER_TIMEOUT); + + rt2x00_register_read(rt2x00dev, MAC_CSR12, ®); + rt2x00_set_field32(®, MAC_CSR12_FORCE_WAKEUP, !put_to_sleep); + rt2x00_set_field32(®, MAC_CSR12_PUT_TO_SLEEP, put_to_sleep); + rt2x00_register_write(rt2x00dev, MAC_CSR12, reg); + + if (put_to_sleep) + rt2x00_vendor_request(rt2x00dev, + USB_DEVICE_MODE, USB_VENDOR_REQUEST_OUT, + 0x00, USB_MODE_SLEEP, NULL, 0, REGISTER_TIMEOUT); + + /* + * Device is not guaranteed to be in the requested state yet. + * We must wait until the register indicates that the + * device has entered the correct state. + */ + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2x00_register_read(rt2x00dev, MAC_CSR12, ®); + current_state = rt2x00_get_field32(reg, + MAC_CSR12_BBP_CURRENT_STATE); + if (current_state == !put_to_sleep) + return 0; + msleep(10); + } + + NOTICE("Device failed to enter state %d, " + "current device state %d.\n", !put_to_sleep, current_state); + + return -EBUSY; +} + +/* + * Initialization functions. + */ +static int rt73usb_load_firmware(struct rt2x00_dev *rt2x00dev, + const struct firmware *fw) +{ + unsigned int i; + int status; + u32 reg; + u16 crc; + + /* + * Wait for stable hardware. + */ + for (i = 0; i < 100; i++) { + rt2x00_register_read(rt2x00dev, MAC_CSR0, ®); + if (reg) + break; + msleep(1); + } + + if (!reg) { + ERROR("Unstable hardware.\n"); + return -EBUSY; + } + + /* + * Validate the firmware using 16 bit CRC. + * The last 2 bytes of the firmware are the CRC + * so substract those 2 bytes from the CRC checksum, + * and set those 2 bytes to 0 when calculating CRC. + */ + reg = 0; + crc = crc_itu_t(0, fw->data, fw->size - 2); + crc = crc_itu_t(crc, (u8*)®, 2); + + if (crc != (fw->data[fw->size - 2] << 8 | fw->data[fw->size - 1])) { + ERROR("Firmware CRC error.\n"); + return -EINVAL; + } + + rt2x00_set_chip_fw(&rt2x00dev->chip, + fw->data[fw->size - 4], fw->data[fw->size - 3]); + + /* + * Write firmware to device. + */ + for (i = 0; i < fw->size; i += sizeof(u32)) { + reg = *((u32*)&fw->data[i]); + rt2x00_register_multiwrite(rt2x00dev, FIRMWARE_IMAGE_BASE + i, + ®, sizeof(u32)); + } + + /* + * Send firmware request to device to load firmware, + * we need to specify a long timeout time. + */ + status = rt2x00_vendor_request(rt2x00dev, USB_DEVICE_MODE, + USB_VENDOR_REQUEST_OUT, 0x00, USB_MODE_FIRMWARE, + NULL, 0, REGISTER_TIMEOUT_FIRMWARE); + if (status < 0) { + ERROR("Failed to write Firmware to device.\n"); + return status; + } + + rt73usb_disable_led(rt2x00dev); + + return 0; +} + +static void rt73usb_init_rxring(struct rt2x00_dev *rt2x00dev, + enum ring_index ring_type) +{ + struct data_ring *ring = &rt2x00dev->ring[ring_type]; + struct usb_device *usb_dev = + interface_to_usbdev(rt2x00dev_usb(rt2x00dev)); + unsigned int i; + + ring->type = ring_type; + + for (i = 0; i < ring->stats.limit; i++) { + usb_fill_bulk_urb( + ring->entry[i].priv, + usb_dev, + usb_rcvbulkpipe(usb_dev, 1), + ring->entry[i].skb->data, + ring->entry[i].skb->len, + rt73usb_interrupt_rxdone, + &ring->entry[i]); + } + + rt2x00_ring_index_clear(ring); +} + +static void rt73usb_init_txring(struct rt2x00_dev *rt2x00dev, + enum ring_index ring_type) +{ + struct data_ring *ring = &rt2x00dev->ring[ring_type]; + unsigned int i; + + ring->type = ring_type; + + for (i = 0; i < ring->stats.limit; i++) + CLEAR_FLAGS(&ring->entry[i]); + + rt2x00_ring_index_clear(ring); +} + +static int rt73usb_init_rings(struct rt2x00_dev *rt2x00dev) +{ + rt73usb_init_rxring(rt2x00dev, RING_RX); + rt73usb_init_txring(rt2x00dev, RING_AC_VO); + rt73usb_init_txring(rt2x00dev, RING_AC_VI); + rt73usb_init_txring(rt2x00dev, RING_AC_BE); + rt73usb_init_txring(rt2x00dev, RING_AC_BK); + rt73usb_init_txring(rt2x00dev, RING_PRIO); + rt73usb_init_txring(rt2x00dev, RING_BEACON); + + return 0; +} + +static int rt73usb_init_registers(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + if (rt73usb_set_state(rt2x00dev, STATE_AWAKE)) + return -EBUSY; + + rt2x00_register_write(rt2x00dev, MAC_CSR10, 0x00000718); + + rt2x00_register_write(rt2x00dev, TXRX_CSR0, 0x025eb032); + + rt2x00_register_write(rt2x00dev, TXRX_CSR1, 0x9eaa9eaf); + rt2x00_register_write(rt2x00dev, TXRX_CSR2, 0x8a8b8c8d); + rt2x00_register_write(rt2x00dev, TXRX_CSR3, 0x00858687); + + rt2x00_register_write(rt2x00dev, TXRX_CSR7, 0x2e31353b); + rt2x00_register_write(rt2x00dev, TXRX_CSR8, 0x2a2a2a2c); + + rt2x00_register_write(rt2x00dev, TXRX_CSR15, 0x0000000f); + + rt2x00_register_write(rt2x00dev, MAC_CSR6, 0x00000fff); + + rt2x00_register_write(rt2x00dev, MAC_CSR13, 0x00007f00); + + rt2x00_register_write(rt2x00dev, SEC_CSR0, 0x00000000); + rt2x00_register_write(rt2x00dev, SEC_CSR1, 0x00000000); + rt2x00_register_write(rt2x00dev, SEC_CSR5, 0x00000000); + + rt2x00_register_write(rt2x00dev, PHY_CSR1, 0x000023b0); + rt2x00_register_write(rt2x00dev, PHY_CSR5, 0x00040a06); + rt2x00_register_write(rt2x00dev, PHY_CSR6, 0x00080606); + rt2x00_register_write(rt2x00dev, PHY_CSR7, 0x00000408); + + rt2x00_register_read(rt2x00dev, AC_TXOP_CSR0, ®); + rt2x00_set_field32(®, AC_TXOP_CSR0_AC0_TX_OP, 0); + rt2x00_set_field32(®, AC_TXOP_CSR0_AC1_TX_OP, 0); + rt2x00_register_write(rt2x00dev, AC_TXOP_CSR0, reg); + + rt2x00_register_read(rt2x00dev, AC_TXOP_CSR1, ®); + rt2x00_set_field32(®, AC_TXOP_CSR1_AC2_TX_OP, 192); + rt2x00_set_field32(®, AC_TXOP_CSR1_AC3_TX_OP, 48); + rt2x00_register_write(rt2x00dev, AC_TXOP_CSR1, reg); + + rt2x00_register_read(rt2x00dev, MAC_CSR9, ®); + rt2x00_set_field32(®, MAC_CSR9_CW_SELECT, 0); + rt2x00_register_write(rt2x00dev, MAC_CSR9, reg); + + rt2x00_register_read(rt2x00dev, TXRX_CSR0, ®); + rt2x00_set_field32(®, TXRX_CSR0_AUTO_TX_SEQ, 1); + rt2x00_register_write(rt2x00dev, TXRX_CSR0, reg); + + /* + * We must clear the error counters. + * These registers are cleared on read, + * so we may pass a useless variable to store the value. + */ + rt2x00_register_read(rt2x00dev, STA_CSR0, ®); + rt2x00_register_read(rt2x00dev, STA_CSR1, ®); + rt2x00_register_read(rt2x00dev, STA_CSR2, ®); + + /* + * Reset MAC and BBP registers. + */ + reg = 0; + rt2x00_set_field32(®, MAC_CSR1_SOFT_RESET, 1); + rt2x00_set_field32(®, MAC_CSR1_BBP_RESET, 1); + rt2x00_register_write(rt2x00dev, MAC_CSR1, reg); + + rt2x00_register_read(rt2x00dev, MAC_CSR1, ®); + rt2x00_set_field32(®, MAC_CSR1_SOFT_RESET, 0); + rt2x00_set_field32(®, MAC_CSR1_BBP_RESET, 0); + rt2x00_register_write(rt2x00dev, MAC_CSR1, reg); + + rt2x00_register_read(rt2x00dev, MAC_CSR1, ®); + rt2x00_set_field32(®, MAC_CSR1_HOST_READY, 1); + rt2x00_register_write(rt2x00dev, MAC_CSR1, reg); + + return 0; +} + +static int rt73usb_init_bbp(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + u16 eeprom; + u8 reg_id; + u8 value; + + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2x00_bbp_read(rt2x00dev, 0, &value); + if ((value != 0xff) && (value != 0x00)) + goto continue_csr_init; + NOTICE("Waiting for BBP register.\n"); + udelay(REGISTER_BUSY_DELAY); + } + + ERROR("BBP register access failed, aborting.\n"); + return -EACCES; + +continue_csr_init: + rt2x00_bbp_write(rt2x00dev, 3, 0x80); + rt2x00_bbp_write(rt2x00dev, 15, 0x30); + rt2x00_bbp_write(rt2x00dev, 17, 0x20); + rt2x00_bbp_write(rt2x00dev, 21, 0xc8); + rt2x00_bbp_write(rt2x00dev, 22, 0x38); + rt2x00_bbp_write(rt2x00dev, 23, 0x06); + rt2x00_bbp_write(rt2x00dev, 24, 0xfe); + rt2x00_bbp_write(rt2x00dev, 25, 0x0a); + rt2x00_bbp_write(rt2x00dev, 26, 0x0d); + rt2x00_bbp_write(rt2x00dev, 32, 0x0b); + rt2x00_bbp_write(rt2x00dev, 34, 0x12); + rt2x00_bbp_write(rt2x00dev, 37, 0x07); + rt2x00_bbp_write(rt2x00dev, 39, 0xf8); + rt2x00_bbp_write(rt2x00dev, 41, 0x60); + rt2x00_bbp_write(rt2x00dev, 53, 0x10); + rt2x00_bbp_write(rt2x00dev, 54, 0x18); + rt2x00_bbp_write(rt2x00dev, 60, 0x10); + rt2x00_bbp_write(rt2x00dev, 61, 0x04); + rt2x00_bbp_write(rt2x00dev, 62, 0x04); + rt2x00_bbp_write(rt2x00dev, 75, 0xfe); + rt2x00_bbp_write(rt2x00dev, 86, 0xfe); + rt2x00_bbp_write(rt2x00dev, 88, 0xfe); + rt2x00_bbp_write(rt2x00dev, 90, 0x0f); + rt2x00_bbp_write(rt2x00dev, 99, 0x00); + rt2x00_bbp_write(rt2x00dev, 102, 0x16); + rt2x00_bbp_write(rt2x00dev, 107, 0x04); + + DEBUG("Start initialization from EEPROM...\n"); + for (i = 0; i < EEPROM_BBP_SIZE; i++) { + rt2x00_eeprom_read(rt2x00dev, EEPROM_BBP_START + i, &eeprom); + + if (eeprom != 0xffff && eeprom != 0x0000) { + reg_id = rt2x00_get_field16(eeprom, EEPROM_BBP_REG_ID); + value = rt2x00_get_field16(eeprom, EEPROM_BBP_VALUE); + DEBUG("BBP: 0x%02x, value: 0x%02x.\n", reg_id, value); + rt2x00_bbp_write(rt2x00dev, reg_id, value); + } + } + DEBUG("...End initialization from EEPROM.\n"); + + return 0; +} + +/* + * Radio control functions. + */ +static void rt73usb_toggle_rx(struct rt2x00_dev *rt2x00dev, int enable) +{ + u32 reg; + + rt2x00_register_read(rt2x00dev, TXRX_CSR0, ®); + rt2x00_set_field32(®, TXRX_CSR0_DISABLE_RX, !enable); + rt2x00_register_write(rt2x00dev, TXRX_CSR0, reg); +} + +static int rt73usb_enable_radio(struct rt2x00_dev *rt2x00dev) +{ + struct data_ring *ring; + unsigned int i; + + /* + * Initialize all registers. + */ + if (rt73usb_init_rings(rt2x00dev) || + rt73usb_init_registers(rt2x00dev) || + rt73usb_init_bbp(rt2x00dev)) { + ERROR("Register initialization failed.\n"); + return -EIO; + } + + ring = &rt2x00dev->ring[RING_RX]; + for (i = 0; i < ring->stats.limit; i++) { + SET_FLAG(&ring->entry[i], ENTRY_OWNER_NIC); + usb_submit_urb(ring->entry[i].priv, GFP_ATOMIC); + } + + /* + * Enable LED + */ + rt73usb_enable_led(rt2x00dev); + + return 0; +} + +static void rt73usb_disable_radio(struct rt2x00_dev *rt2x00dev) +{ + struct data_ring *ring; + unsigned int i; + + /* + * Disable LED + */ + rt73usb_disable_led(rt2x00dev); + + rt2x00_register_write(rt2x00dev, MAC_CSR10, 0x00001818); + + /* + * Disable synchronisation. + */ + rt2x00_register_write(rt2x00dev, TXRX_CSR9, 0); + + /* + * Cancel RX and TX. + */ + rt2x00_vendor_request(rt2x00dev, USB_RX_CONTROL, + USB_VENDOR_REQUEST_OUT, 0x00, 0x00, NULL, 0, REGISTER_TIMEOUT); + + ring = &rt2x00dev->ring[RING_RX]; + for (i = 0; i < ring->stats.limit; i++) + usb_kill_urb(ring->entry[i].priv); + + ring = &rt2x00dev->ring[RING_AC_VO]; + for (i = 0; i < ring->stats.limit; i++) + usb_kill_urb(ring->entry[i].priv); + + ring = &rt2x00dev->ring[RING_AC_VI]; + for (i = 0; i < ring->stats.limit; i++) + usb_kill_urb(ring->entry[i].priv); + + ring = &rt2x00dev->ring[RING_AC_BE]; + for (i = 0; i < ring->stats.limit; i++) + usb_kill_urb(ring->entry[i].priv); + + ring = &rt2x00dev->ring[RING_AC_BK]; + for (i = 0; i < ring->stats.limit; i++) + usb_kill_urb(ring->entry[i].priv); + + ring = &rt2x00dev->ring[RING_PRIO]; + for (i = 0; i < ring->stats.limit; i++) + usb_kill_urb(ring->entry[i].priv); + + ring = &rt2x00dev->ring[RING_BEACON]; + for (i = 0; i < ring->stats.limit; i++) + usb_kill_urb(ring->entry[i].priv); +} + +/* + * TX descriptor initialization + */ +static void rt73usb_write_tx_desc(struct rt2x00_dev *rt2x00dev, + struct data_entry *entry, struct data_desc *txd, + struct data_entry_desc *desc, struct ieee80211_hdr *ieee80211hdr, + unsigned int length, struct ieee80211_tx_control *control) +{ + u32 word; + + /* + * Start writing the descriptor words. + */ + rt2x00_desc_read(txd, 1, &word); + rt2x00_set_field32(&word, TXD_W1_HOST_Q_ID, desc->queue); + rt2x00_set_field32(&word, TXD_W1_AIFSN, entry->ring->tx_params.aifs); + rt2x00_set_field32(&word, TXD_W1_CWMIN, entry->ring->tx_params.cw_min); + rt2x00_set_field32(&word, TXD_W1_CWMAX, entry->ring->tx_params.cw_max); + rt2x00_set_field32(&word, TXD_W1_IV_OFFSET, IEEE80211_HEADER); + rt2x00_set_field32(&word, TXD_W1_HW_SEQUENCE, 1); + rt2x00_desc_write(txd, 1, word); + + rt2x00_desc_read(txd, 2, &word); + rt2x00_set_field32(&word, TXD_W2_PLCP_SIGNAL, desc->signal); + rt2x00_set_field32(&word, TXD_W2_PLCP_SERVICE, desc->service); + rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_LOW, desc->length_low); + rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_HIGH, desc->length_high); + rt2x00_desc_write(txd, 2, word); + + rt2x00_desc_read(txd, 5, &word); + rt2x00_set_field32(&word, TXD_W5_TX_POWER, + TXPOWER_TO_DEV(control->power_level)); + rt2x00_set_field32(&word, TXD_W5_WAITING_DMA_DONE_INT, 1); + rt2x00_desc_write(txd, 5, word); + + rt2x00_desc_read(txd, 0, &word); + rt2x00_set_field32(&word, TXD_W0_VALID, 1); + rt2x00_set_field32(&word, TXD_W0_MORE_FRAG, + !!(entry->reg & ENTRY_TXD_MORE_FRAG)); + rt2x00_set_field32(&word, TXD_W0_ACK, + !!(entry->reg & ENTRY_TXD_REQ_ACK)); + rt2x00_set_field32(&word, TXD_W0_TIMESTAMP, + !!(entry->reg & ENTRY_TXD_REQ_TIMESTAMP)); + rt2x00_set_field32(&word, TXD_W0_OFDM, + !!(entry->reg & ENTRY_TXD_OFDM_RATE)); + rt2x00_set_field32(&word, TXD_W0_IFS, desc->ifs); + rt2x00_set_field32(&word, TXD_W0_RETRY_MODE, 0); + rt2x00_set_field32(&word, TXD_W0_TKIP_MIC, 0); + rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, length); + rt2x00_set_field32(&word, TXD_W0_CIPHER_ALG, CIPHER_NONE); + rt2x00_desc_write(txd, 0, word); +} + +/* + * TX data initialization + */ +static void rt73usb_kick_beacon_gen(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + rt2x00_register_read(rt2x00dev, TXRX_CSR9, ®); + if (!rt2x00_get_field32(reg, TXRX_CSR9_BEACON_GEN)) { + rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 1); + rt2x00_register_write(rt2x00dev, TXRX_CSR9, reg); + } +} + +/* + * Interrupt functions. + */ +static void rt73usb_interrupt_rxdone(struct urb *urb) +{ + struct data_entry *entry = (struct data_entry*)urb->context; + struct data_ring *ring = entry->ring; + struct rt2x00_dev *rt2x00dev = ring->rt2x00dev; + struct data_desc *rxd = (struct data_desc*)entry->skb->data; + u32 word0; + u32 word1; + int signal; + int rssi; + int ofdm; + u16 size; + + if (!GET_FLAG(rt2x00dev, DEVICE_ENABLED_RADIO) || + !GET_FLAG(entry, ENTRY_OWNER_NIC)) + return; + + CLEAR_FLAG(entry, ENTRY_OWNER_NIC); + + /* + * Check if the received data is simply too small + * to be actually valid, or if the urb is signaling + * a problem. + */ + if (urb->actual_length < entry->ring->desc_size || urb->status) + goto skip_entry; + + rt2x00_desc_read(rxd, 0, &word0); + rt2x00_desc_read(rxd, 1, &word1); + + /* + * TODO: Don't we need to keep statistics + * updated about events like CRC and physical errors? + */ + if (rt2x00_get_field32(word0, RXD_W0_CRC) || + rt2x00_get_field32(word0, RXD_W0_CIPHER_ERROR)) + goto skip_entry; + + /* + * Obtain the status about this packet. + */ + size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT); + signal = rt2x00_get_field32(word1, RXD_W1_SIGNAL); + rssi = rt2x00_get_field32(word1, RXD_W1_RSSI); + ofdm = rt2x00_get_field32(word0, RXD_W0_OFDM); + + /* + * Trim the skb_buffer to only contain the valid + * frame data (so ignore the device's descriptor). + */ + skb_pull(entry->skb, ring->desc_size); + skb_trim(entry->skb, size); + + /* + * Send the packet to upper layer, and update urb. + */ + rt2x00lib_rxdone(entry, NULL, ring->data_size + ring->desc_size, + signal, rssi, ofdm); + urb->transfer_buffer = entry->skb->data; + urb->transfer_buffer_length = entry->skb->len; + +skip_entry: + if (GET_FLAG(ring->rt2x00dev, DEVICE_ENABLED_RADIO)) { + SET_FLAG(entry, ENTRY_OWNER_NIC); + usb_submit_urb(urb, GFP_ATOMIC); + } + + rt2x00_ring_index_inc(ring); +} + +/* + * Device initialization functions. + */ +static int rt73usb_alloc_eeprom(struct rt2x00_dev *rt2x00dev) +{ + /* + * Allocate the eeprom memory, check the eeprom width + * and copy the entire eeprom into this allocated memory. + */ + rt2x00dev->eeprom = kzalloc(EEPROM_SIZE, GFP_KERNEL); + if (!rt2x00dev->eeprom) + return -ENOMEM; + + rt2x00_vendor_request( + rt2x00dev, USB_EEPROM_READ, USB_VENDOR_REQUEST_IN, + EEPROM_BASE, 0x00, rt2x00dev->eeprom, EEPROM_SIZE, + REGISTER_TIMEOUT * (EEPROM_SIZE / sizeof(u16))); + + return 0; +} + +static int rt73usb_init_eeprom(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + u16 value; + u16 eeprom; + + /* + * Read EEPROM word for configuration. + */ + rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &eeprom); + + /* + * Identify RF chipset. + */ + value = rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RF_TYPE); + rt2x00_register_read(rt2x00dev, MAC_CSR0, ®); + rt2x00_set_chip(&rt2x00dev->chip, RT2571, value, reg); + + if (!rt2x00_rf(&rt2x00dev->chip, RF5226) && + !rt2x00_rf(&rt2x00dev->chip, RF2528) && + !rt2x00_rf(&rt2x00dev->chip, RF5225) && + !rt2x00_rf(&rt2x00dev->chip, RF2527)) { + ERROR("Invalid RF chipset detected.\n"); + return -ENODEV; + } + + /* + * Identify default antenna configuration. + */ + rt2x00dev->hw->conf.antenna_sel_tx = rt2x00_get_field16(eeprom, + EEPROM_ANTENNA_TX_DEFAULT); + rt2x00dev->hw->conf.antenna_sel_rx = rt2x00_get_field16(eeprom, + EEPROM_ANTENNA_RX_DEFAULT); + + /* + * Read the Frame type. + */ + if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_FRAME_TYPE)) + SET_FLAG(rt2x00dev, CONFIG_FRAME_TYPE); + + /* + * Read frequency offset. + */ + rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &eeprom); + rt2x00dev->freq_offset = rt2x00_get_field16(eeprom, + EEPROM_FREQ_OFFSET); + if (rt2x00dev->freq_offset == 0xff) + rt2x00dev->freq_offset = 0; + + /* + * Read external LNA informations. + */ + rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &eeprom); + if (eeprom == 0xffff) + eeprom = 0; + if (rt2x00_get_field16(eeprom, EEPROM_NIC_EXTERNAL_LNA)) + SET_FLAG(rt2x00dev, CONFIG_EXTERNAL_LNA); + + /* + * Store led settings, for correct led behaviour. + */ + rt2x00_eeprom_read(rt2x00dev, EEPROM_LED, &eeprom); + + /* + * If the eeprom value is invalid, + * switch to default led mode, and clear all bits. + */ + if (eeprom == 0xffff) { + rt2x00dev->led_mode = LED_MODE_DEFAULT; + eeprom = 0x0000; + } else + rt2x00dev->led_mode = rt2x00_get_field16(eeprom, + EEPROM_LED_LED_MODE); + + rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_LED_MODE, + rt2x00dev->led_mode); + rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_POLARITY_GPIO_0, + rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_GPIO_0)); + rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_POLARITY_GPIO_1, + rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_GPIO_1)); + rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_POLARITY_GPIO_2, + rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_GPIO_2)); + rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_POLARITY_GPIO_3, + rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_GPIO_3)); + rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_POLARITY_GPIO_4, + rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_GPIO_4)); + rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_POLARITY_ACT, + rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_ACT)); + rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_POLARITY_READY_BG, + rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_RDY_G)); + rt2x00_set_field16(&rt2x00dev->led_reg, MCU_LEDCS_POLARITY_READY_A, + rt2x00_get_field16(eeprom, EEPROM_LED_POLARITY_RDY_A)); + + return 0; +} + +static const struct { + unsigned int chip; + u32 val[3]; +} rf_vals[] = { + { RF5226, { 0x00002c0c, 0x00068255 } }, + { RF2528, { 0x00002c0c, 0x00068255 } }, + { RF5225, { 0x00002ccc, 0x00000000 } }, + { RF2527, { 0x00002ccc, 0x00068455 } }, +}; + +/* + * RF value list for RF5226, RF2528, RF5225 & RF2527 + * Supports: 2.4 GHz + */ +static const u32 rf_vals_bg[] = { + 0x00000786, 0x00000786, 0x0000078a, 0x0000078a, 0x0000078e, + 0x0000078e, 0x00000792, 0x00000792, 0x00000796, 0x00000796, + 0x0000079a, 0x0000079a, 0x0000079e, 0x000007a2 +}; + +/* + * RF value list for RF5226 & RF5225 (supplement to vals_bg) + * Supports: 5.2 GHz + */ +static const u32 rf_vals_a_5x[] = { + 0x0000099a, 0x000009a2, 0x000009a6, 0x000009aa, 0x000009ae, + 0x000009b2, 0x000009ba, 0x000009be, 0x00000a2a, 0x00000a2e, + 0x00000a32, 0x00000a36, 0x00000a3a, 0x00000a82, 0x00000a86, + 0x00000a8a, 0x00000a8e, 0x00000a92, 0x00000a9a, 0x00000aa2, + 0x00000aa6, 0x00000aae, 0x00000ab2, 0x00000ab6 +}; + +static void rt73usb_init_hw_mode(struct rt2x00_dev *rt2x00dev) +{ + struct hw_mode_spec *spec = &rt2x00dev->spec; + u8 *txpower; + unsigned int i; + + if (GET_FLAG(rt2x00dev, DEVICE_INITIALIZED_HW)) + return; + + /* + * Initialize all hw fields. + */ + rt2x00dev->hw->flags = IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE | + IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | + IEEE80211_HW_WEP_INCLUDE_IV | + IEEE80211_HW_DATA_NULLFUNC_ACK | + IEEE80211_HW_NO_TKIP_WMM_HWACCEL | + IEEE80211_HW_MONITOR_DURING_OPER; + rt2x00dev->hw->extra_tx_headroom = TXD_DESC_SIZE; + rt2x00dev->hw->max_rssi = MAX_RX_SSI; + rt2x00dev->hw->max_noise = MAX_RX_NOISE; + rt2x00dev->hw->queues = RING_NUM_TX; + + /* + * Set device specific, but channel independent RF values. + */ + for (i = 0; i < ARRAY_SIZE(rf_vals); i++) { + if (rt2x00_rf(&rt2x00dev->chip, rf_vals[i].chip)) { + rt2x00dev->rf1 = rf_vals[i].val[0]; + rt2x00dev->rf3 = rf_vals[i].val[1]; + } + } + + /* + * Convert tx_power array in eeprom. + */ + txpower = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_G_START); + for (i = 0; i < 14; i++) + txpower[i] = TXPOWER_FROM_DEV(txpower[i]); + + /* + * Initialize hw_mode information. + */ + spec->mac_addr = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); + spec->num_modes = 2; + spec->num_rates = 12; + spec->num_channels = 14; + spec->tx_power_a = NULL; + spec->tx_power_bg = txpower; + spec->tx_power_default = DEFAULT_TXPOWER; + spec->chan_val_a = NULL; + spec->chan_val_bg = rf_vals_bg; + + if (rt2x00_rf(&rt2x00dev->chip, RF5225) || + rt2x00_rf(&rt2x00dev->chip, RF5226)) { + spec->num_modes = 3; + spec->num_channels += 24; + + txpower = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A_START); + for (i = 0; i < 14; i++) + txpower[i] = TXPOWER_FROM_DEV(txpower[i]); + + spec->tx_power_a = txpower; + spec->chan_val_a = rf_vals_a_5x; + } +} + +static int rt73usb_init_hw(struct rt2x00_dev *rt2x00dev) +{ + int retval; + + rt2x00dev->rxd_size = RXD_DESC_SIZE; + rt2x00dev->txd_size = TXD_DESC_SIZE; + + /* + * Allocate eeprom data. + */ + retval = rt73usb_alloc_eeprom(rt2x00dev); + if (retval) + return retval; + + retval = rt73usb_init_eeprom(rt2x00dev); + if (retval) + return retval; + + /* + * Initialize hw specifications. + */ + rt73usb_init_hw_mode(rt2x00dev); + + return 0; +} + +/* + * IEEE80211 stack callback functions. + */ +static int rt73usb_get_stats(struct ieee80211_hw *hw, + struct ieee80211_low_level_stats *stats) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + u32 reg; + + /* + * Update FCS error count from register. + * The dot11ACKFailureCount, dot11RTSFailureCount and + * dot11RTSSuccessCount are updated in interrupt time. + */ + rt2x00_register_read(rt2x00dev, STA_CSR0, ®); + rt2x00dev->low_level_stats.dot11FCSErrorCount += + rt2x00_get_field32(reg, STA_CSR0_FCS_ERROR); + + memcpy(stats, &rt2x00dev->low_level_stats, sizeof(*stats)); + + return 0; +} + +static int rt73usb_set_retry_limit(struct ieee80211_hw *hw, + u32 short_retry, u32 long_retry) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + u32 reg; + + rt2x00_register_read(rt2x00dev, TXRX_CSR4, ®); + rt2x00_set_field32(®, TXRX_CSR4_LONG_RETRY_LIMIT, long_retry); + rt2x00_set_field32(®, TXRX_CSR4_SHORT_RETRY_LIMIT, short_retry); + rt2x00_register_write(rt2x00dev, TXRX_CSR4, reg); + + return 0; +} + +static u64 rt73usb_get_tsf(struct ieee80211_hw *hw) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + u64 tsf; + u32 reg; + + rt2x00_register_read(rt2x00dev, TXRX_CSR13, ®); + tsf = (u64)rt2x00_get_field32(reg, TXRX_CSR13_HIGH_TSFTIMER) << 32; + rt2x00_register_read(rt2x00dev, TXRX_CSR12, ®); + tsf |= rt2x00_get_field32(reg, TXRX_CSR12_LOW_TSFTIMER); + + return tsf; +} + +static void rt73usb_reset_tsf(struct ieee80211_hw *hw) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + + rt2x00_register_write(rt2x00dev, TXRX_CSR12, 0); + rt2x00_register_write(rt2x00dev, TXRX_CSR13, 0); +} + +static const struct ieee80211_ops rt73usb_mac80211_ops = { + .tx = rt2x00lib_tx, + .reset = rt2x00lib_reset, + .add_interface = rt2x00lib_add_interface, + .remove_interface = rt2x00lib_remove_interface, + .config = rt2x00lib_config, + .config_interface = rt2x00lib_config_interface, + .set_multicast_list = rt2x00lib_set_multicast_list, + .get_stats = rt73usb_get_stats, + .set_retry_limit = rt73usb_set_retry_limit, + .conf_tx = rt2x00lib_conf_tx, + .get_tx_stats = rt2x00lib_get_tx_stats, + .get_tsf = rt73usb_get_tsf, + .reset_tsf = rt73usb_reset_tsf, + .beacon_update = rt2x00usb_beacon_update, +}; + +static const struct rt2x00lib_ops rt73usb_rt2x00_ops = { + .link_tuner = rt73usb_link_tuner, + .init_hw = rt73usb_init_hw, + .load_firmware = rt73usb_load_firmware, + .initialize = rt2x00usb_initialize, + .uninitialize = rt2x00usb_uninitialize, + .enable_radio = rt73usb_enable_radio, + .disable_radio = rt73usb_disable_radio, + .set_state = rt73usb_set_state, + .toggle_rx = rt73usb_toggle_rx, + .write_tx_desc = rt73usb_write_tx_desc, + .write_tx_data = rt2x00usb_write_tx_data, + .kick_beacon_gen = rt73usb_kick_beacon_gen, + .config_type = rt73usb_config_type, + .config_phymode = rt73usb_config_phymode, + .config_channel = rt73usb_config_channel, + .config_mac_addr = rt73usb_config_mac_addr, + .config_bssid = rt73usb_config_bssid, + .config_txpower = rt73usb_config_txpower, + .config_antenna = rt73usb_config_antenna, + .config_duration = rt73usb_config_duration, +}; + +static const struct rt2x00_ops rt73usb_ops = { + .lib = &rt73usb_rt2x00_ops, + .hw = &rt73usb_mac80211_ops, +#ifdef CONFIG_RT2X00_DEBUGFS + .debugfs = &rt73usb_rt2x00debug, +#endif /* CONFIG_RT2X00_DEBUGFS */ +}; + +/* + * rt73usb module information. + */ +static char version[] = + DRV_NAME " - " DRV_VERSION " (" DRV_RELDATE ") by " DRV_PROJECT; + +static struct usb_device_id rt73usb_device_table[] = { + /* AboCom */ + { USB_DEVICE(0x07b8, 0xb21d), USB_DEVICE_DATA(&rt73usb_ops) }, + /* Askey */ + { USB_DEVICE(0x1690, 0x0722), USB_DEVICE_DATA(&rt73usb_ops) }, + /* ASUS */ + { USB_DEVICE(0x0b05, 0x1723), USB_DEVICE_DATA(&rt73usb_ops) }, + { USB_DEVICE(0x0b05, 0x1724), USB_DEVICE_DATA(&rt73usb_ops) }, + /* Belkin */ + { USB_DEVICE(0x050d, 0x7050), USB_DEVICE_DATA(&rt73usb_ops) }, + { USB_DEVICE(0x050d, 0x705a), USB_DEVICE_DATA(&rt73usb_ops) }, + { USB_DEVICE(0x050d, 0x905b), USB_DEVICE_DATA(&rt73usb_ops) }, + /* Billionton */ + { USB_DEVICE(0x1631, 0xc019), USB_DEVICE_DATA(&rt73usb_ops) }, + /* CNet */ + { USB_DEVICE(0x1371, 0x9022), USB_DEVICE_DATA(&rt73usb_ops) }, + { USB_DEVICE(0x1371, 0x9032), USB_DEVICE_DATA(&rt73usb_ops) }, + /* Conceptronic */ + { USB_DEVICE(0x14b2, 0x3c22), USB_DEVICE_DATA(&rt73usb_ops) }, + /* D-Link */ + { USB_DEVICE(0x07d1, 0x3c03), USB_DEVICE_DATA(&rt73usb_ops) }, + { USB_DEVICE(0x07d1, 0x3c04), USB_DEVICE_DATA(&rt73usb_ops) }, + /* Gemtek */ + { USB_DEVICE(0x15a9, 0x0004), USB_DEVICE_DATA(&rt73usb_ops) }, + /* Gigabyte */ + { USB_DEVICE(0x1044, 0x8008), USB_DEVICE_DATA(&rt73usb_ops) }, + { USB_DEVICE(0x1044, 0x800a), USB_DEVICE_DATA(&rt73usb_ops) }, + /* Huawei-3Com */ + { USB_DEVICE(0x1472, 0x0009), USB_DEVICE_DATA(&rt73usb_ops) }, + /* Hercules */ + { USB_DEVICE(0x06f8, 0xe010), USB_DEVICE_DATA(&rt73usb_ops) }, + { USB_DEVICE(0x06f8, 0xe020), USB_DEVICE_DATA(&rt73usb_ops) }, + /* Linksys */ + { USB_DEVICE(0x13b1, 0x0020), USB_DEVICE_DATA(&rt73usb_ops) }, + { USB_DEVICE(0x13b1, 0x0023), USB_DEVICE_DATA(&rt73usb_ops) }, + /* MSI */ + { USB_DEVICE(0x0db0, 0x6877), USB_DEVICE_DATA(&rt73usb_ops) }, + { USB_DEVICE(0x0db0, 0xa861), USB_DEVICE_DATA(&rt73usb_ops) }, + { USB_DEVICE(0x0db0, 0xa874), USB_DEVICE_DATA(&rt73usb_ops) }, + /* Ralink */ + { USB_DEVICE(0x148f, 0x2573), USB_DEVICE_DATA(&rt73usb_ops) }, + { USB_DEVICE(0x148f, 0x2671), USB_DEVICE_DATA(&rt73usb_ops) }, + /* Qcom */ + { USB_DEVICE(0x18e8, 0x6196), USB_DEVICE_DATA(&rt73usb_ops) }, + { USB_DEVICE(0x18e8, 0x6229), USB_DEVICE_DATA(&rt73usb_ops) }, + /* Sitecom */ + { USB_DEVICE(0x0df6, 0x9712), USB_DEVICE_DATA(&rt73usb_ops) }, + { USB_DEVICE(0x0df6, 0x90ac), USB_DEVICE_DATA(&rt73usb_ops) }, + /* Surecom */ + { USB_DEVICE(0x0769, 0x31f3), USB_DEVICE_DATA(&rt73usb_ops) }, + /* Planex */ + { USB_DEVICE(0x2019, 0xab01), USB_DEVICE_DATA(&rt73usb_ops) }, + { USB_DEVICE(0x2019, 0xab50), USB_DEVICE_DATA(&rt73usb_ops) }, + { 0, } +}; + +MODULE_AUTHOR(DRV_PROJECT); +MODULE_VERSION(DRV_VERSION); +MODULE_DESCRIPTION("Ralink RT73 USB Wireless LAN driver."); +MODULE_SUPPORTED_DEVICE("Ralink RT2571W & RT2671 USB chipset based cards"); +MODULE_DEVICE_TABLE(usb, rt73usb_device_table); +MODULE_LICENSE("GPL"); + +#ifdef CONFIG_RT2X00_DEBUG +module_param_named(debug, rt2x00_debug_level, bool, S_IWUSR | S_IRUGO); +MODULE_PARM_DESC(debug, "Set this parameter to 1 to enable debug output."); +#endif /* CONFIG_RT2X00_DEBUG */ + +static struct usb_driver rt73usb_driver = { + .name = DRV_NAME, + .id_table = rt73usb_device_table, + .probe = rt2x00usb_probe, + .disconnect = rt2x00usb_disconnect, +#ifdef CONFIG_PM + .suspend = rt2x00usb_suspend, + .resume = rt2x00usb_resume, +#endif /* CONFIG_PM */ +}; + +static int __init rt73usb_init(void) +{ + printk(KERN_INFO "Loading module: %s.\n", version); + return usb_register(&rt73usb_driver); +} + +static void __exit rt73usb_exit(void) +{ + printk(KERN_INFO "Unloading module: %s.\n", version); + usb_deregister(&rt73usb_driver); +} + +module_init(rt73usb_init); +module_exit(rt73usb_exit); diff --git a/drivers/net/wireless/mac80211/rt2x00/rt73usb.h b/drivers/net/wireless/mac80211/rt2x00/rt73usb.h new file mode 100644 index 0000000..4cf317d --- /dev/null +++ b/drivers/net/wireless/mac80211/rt2x00/rt73usb.h @@ -0,0 +1,937 @@ +/* + Copyright (C) 2004 - 2007 rt2x00 SourceForge Project + + + 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. + */ + +/* + Module: rt73usb + Abstract: Data structures and registers for the rt73usb module. + Supported chipsets: rt2571W & rt2671. + */ + +#ifndef RT73USB_H +#define RT73USB_H + +/* + * RF chip defines. + */ +#define RF5226 0x0001 +#define RF2528 0x0002 +#define RF5225 0x0003 +#define RF2527 0x0004 + +/* + * Max RSSI value, required for RSSI <-> dBm conversion. + */ +#define MAX_RX_SSI 120 +#define MAX_RX_NOISE -110 + +/* + * Register layout information. + */ +#define CSR_REG_BASE 0x3000 +#define CSR_REG_SIZE 0x04b0 +#define EEPROM_BASE 0x0000 +#define EEPROM_SIZE 0x0100 +#define BBP_SIZE 0x0080 + +/* + * USB registers. + */ + +/* + * MCU_LEDCS: LED control for MCU Mailbox. + */ +#define MCU_LEDCS_LED_MODE FIELD16(0x001f) +#define MCU_LEDCS_RADIO_STATUS FIELD16(0x0020) +#define MCU_LEDCS_LINK_BG_STATUS FIELD16(0x0040) +#define MCU_LEDCS_LINK_A_STATUS FIELD16(0x0080) +#define MCU_LEDCS_POLARITY_GPIO_0 FIELD16(0x0100) +#define MCU_LEDCS_POLARITY_GPIO_1 FIELD16(0x0200) +#define MCU_LEDCS_POLARITY_GPIO_2 FIELD16(0x0400) +#define MCU_LEDCS_POLARITY_GPIO_3 FIELD16(0x0800) +#define MCU_LEDCS_POLARITY_GPIO_4 FIELD16(0x1000) +#define MCU_LEDCS_POLARITY_ACT FIELD16(0x2000) +#define MCU_LEDCS_POLARITY_READY_BG FIELD16(0x4000) +#define MCU_LEDCS_POLARITY_READY_A FIELD16(0x8000) + +/* + * 8051 firmware image. + */ +#define FIRMWARE_IMAGE_BASE 0x0800 + +/* + * Security key table memory. + * 16 entries 32-byte for shared key table + * 64 entries 32-byte for pairwise key table + * 64 entries 8-byte for pairwise ta key table + */ +#define SHARED_KEY_TABLE_BASE 0x1000 +#define PAIRWISE_KEY_TABLE_BASE 0x1200 +#define PAIRWISE_TA_TABLE_BASE 0x1a00 + +struct hw_key_entry { + u8 key[16]; + u8 tx_mic[8]; + u8 rx_mic[8]; +} __attribute__ ((packed)); + +struct hw_pairwise_ta_entry { + u8 address[6]; + u8 reserved[2]; +} __attribute__ ((packed)); + +/* + * Since NULL frame won't be that long (256 byte), + * We steal 16 tail bytes to save debugging settings. + */ +#define HW_DEBUG_SETTING_BASE 0x2bf0 + +/* + * On-chip BEACON frame space. + */ +#define HW_BEACON_BASE0 0x2400 +#define HW_BEACON_BASE1 0x2500 +#define HW_BEACON_BASE2 0x2600 +#define HW_BEACON_BASE3 0x2700 + +/* + * MAC Control/Status Registers(CSR). + * Some values are set in TU, whereas 1 TU == 1024 us. + */ + +/* + * MAC_CSR0: ASIC revision number. + */ +#define MAC_CSR0 0x3000 + +/* + * MAC_CSR1: System control register. + * SOFT_RESET: Software reset bit, 1: reset, 0: normal. + * BBP_RESET: Hardware reset BBP. + * HOST_READY: Host is ready after initialization, 1: ready. + */ +#define MAC_CSR1 0x3004 +#define MAC_CSR1_SOFT_RESET FIELD32(0x00000001) +#define MAC_CSR1_BBP_RESET FIELD32(0x00000002) +#define MAC_CSR1_HOST_READY FIELD32(0x00000004) + +/* + * MAC_CSR2: STA MAC register 0. + */ +#define MAC_CSR2 0x3008 +#define MAC_CSR2_BYTE0 FIELD32(0x000000ff) +#define MAC_CSR2_BYTE1 FIELD32(0x0000ff00) +#define MAC_CSR2_BYTE2 FIELD32(0x00ff0000) +#define MAC_CSR2_BYTE3 FIELD32(0xff000000) + +/* + * MAC_CSR3: STA MAC register 1. + */ +#define MAC_CSR3 0x300c +#define MAC_CSR3_BYTE4 FIELD32(0x000000ff) +#define MAC_CSR3_BYTE5 FIELD32(0x0000ff00) +#define MAC_CSR3_UNICAST_TO_ME_MASK FIELD32(0x00ff0000) + +/* + * MAC_CSR4: BSSID register 0. + */ +#define MAC_CSR4 0x3010 +#define MAC_CSR4_BYTE0 FIELD32(0x000000ff) +#define MAC_CSR4_BYTE1 FIELD32(0x0000ff00) +#define MAC_CSR4_BYTE2 FIELD32(0x00ff0000) +#define MAC_CSR4_BYTE3 FIELD32(0xff000000) + +/* + * MAC_CSR5: BSSID register 1. + * BSS_ID_MASK: 3: one BSSID, 0: 4 BSSID, 2 or 1: 2 BSSID. + */ +#define MAC_CSR5 0x3014 +#define MAC_CSR5_BYTE4 FIELD32(0x000000ff) +#define MAC_CSR5_BYTE5 FIELD32(0x0000ff00) +#define MAC_CSR5_BSS_ID_MASK FIELD32(0x00ff0000) + +/* + * MAC_CSR6: Maximum frame length register. + */ +#define MAC_CSR6 0x3018 +#define MAC_CSR6_MAX_FRAME_UNIT FIELD32(0x000007ff) + +/* + * MAC_CSR7: Reserved + */ +#define MAC_CSR7 0x301c + +/* + * MAC_CSR8: SIFS/EIFS register. + * All units are in US. + */ +#define MAC_CSR8 0x3020 +#define MAC_CSR8_SIFS FIELD32(0x000000ff) +#define MAC_CSR8_SIFS_AFTER_RX_OFDM FIELD32(0x0000ff00) +#define MAC_CSR8_EIFS FIELD32(0xffff0000) + +/* + * MAC_CSR9: Back-Off control register. + * SLOT_TIME: Slot time, default is 20us for 802.11BG. + * CWMIN: Bit for Cwmin. default Cwmin is 31 (2^5 - 1). + * CWMAX: Bit for Cwmax, default Cwmax is 1023 (2^10 - 1). + * CW_SELECT: 1: CWmin/Cwmax select from register, 0:select from TxD. + */ +#define MAC_CSR9 0x3024 +#define MAC_CSR9_SLOT_TIME FIELD32(0x000000ff) +#define MAC_CSR9_CWMIN FIELD32(0x00000f00) +#define MAC_CSR9_CWMAX FIELD32(0x0000f000) +#define MAC_CSR9_CW_SELECT FIELD32(0x00010000) + +/* + * MAC_CSR10: Power state configuration. + */ +#define MAC_CSR10 0x3028 + +/* + * MAC_CSR11: Power saving transition time register. + * DELAY_AFTER_TBCN: Delay after Tbcn expired in units of TU. + * TBCN_BEFORE_WAKEUP: Number of beacon before wakeup. + * WAKEUP_LATENCY: In unit of TU. + */ +#define MAC_CSR11 0x302c +#define MAC_CSR11_DELAY_AFTER_TBCN FIELD32(0x000000ff) +#define MAC_CSR11_TBCN_BEFORE_WAKEUP FIELD32(0x00007f00) +#define MAC_CSR11_AUTOWAKE FIELD32(0x00008000) +#define MAC_CSR11_WAKEUP_LATENCY FIELD32(0x000f0000) + +/* + * MAC_CSR12: Manual power control / status register (merge CSR20 & PWRCSR1). + * CURRENT_STATE: 0:sleep, 1:awake. + * FORCE_WAKEUP: This has higher priority than PUT_TO_SLEEP. + * BBP_CURRENT_STATE: 0: BBP sleep, 1: BBP awake. + */ +#define MAC_CSR12 0x3030 +#define MAC_CSR12_CURRENT_STATE FIELD32(0x00000001) +#define MAC_CSR12_PUT_TO_SLEEP FIELD32(0x00000002) +#define MAC_CSR12_FORCE_WAKEUP FIELD32(0x00000004) +#define MAC_CSR12_BBP_CURRENT_STATE FIELD32(0x00000008) + +/* + * MAC_CSR13: GPIO. + */ +#define MAC_CSR13 0x3034 + +/* + * MAC_CSR14: LED control register. + * ON_PERIOD: On period, default 70ms. + * OFF_PERIOD: Off period, default 30ms. + * HW_LED: HW TX activity, 1: normal OFF, 0: normal ON. + * SW_LED: s/w LED, 1: ON, 0: OFF. + * HW_LED_POLARITY: 0: active low, 1: active high. + */ +#define MAC_CSR14 0x3038 +#define MAC_CSR14_ON_PERIOD FIELD32(0x000000ff) +#define MAC_CSR14_OFF_PERIOD FIELD32(0x0000ff00) +#define MAC_CSR14_HW_LED FIELD32(0x00010000) +#define MAC_CSR14_SW_LED FIELD32(0x00020000) +#define MAC_CSR14_HW_LED_POLARITY FIELD32(0x00040000) +#define MAC_CSR14_SW_LED2 FIELD32(0x00080000) + +/* + * MAC_CSR15: NAV control. + */ +#define MAC_CSR15 0x303c + +/* + * TXRX control registers. + * Some values are set in TU, whereas 1 TU == 1024 us. + */ + +/* + * TXRX_CSR0: TX/RX configuration register. + * TSF_OFFSET: Default is 24. + * AUTO_TX_SEQ: 1: ASIC auto replace sequence nr in outgoing frame. + * DISABLE_RX: Disable Rx engine. + * DROP_CRC: Drop CRC error. + * DROP_PHYSICAL: Drop physical error. + * DROP_CONTROL: Drop control frame. + * DROP_NOT_TO_ME: Drop not to me unicast frame. + * DROP_TO_DS: Drop fram ToDs bit is true. + * DROP_VERSION_ERROR: Drop version error frame. + * DROP_MULTICAST: Drop multicast frames. + * DROP_BORADCAST: Drop broadcast frames. + * ROP_ACK_CTS: Drop received ACK and CTS. + */ +#define TXRX_CSR0 0x3040 +#define TXRX_CSR0_RX_ACK_TIMEOUT FIELD32(0x000001ff) +#define TXRX_CSR0_TSF_OFFSET FIELD32(0x00007e00) +#define TXRX_CSR0_AUTO_TX_SEQ FIELD32(0x00008000) +#define TXRX_CSR0_DISABLE_RX FIELD32(0x00010000) +#define TXRX_CSR0_DROP_CRC FIELD32(0x00020000) +#define TXRX_CSR0_DROP_PHYSICAL FIELD32(0x00040000) +#define TXRX_CSR0_DROP_CONTROL FIELD32(0x00080000) +#define TXRX_CSR0_DROP_NOT_TO_ME FIELD32(0x00100000) +#define TXRX_CSR0_DROP_TO_DS FIELD32(0x00200000) +#define TXRX_CSR0_DROP_VERSION_ERROR FIELD32(0x00400000) +#define TXRX_CSR0_DROP_MULTICAST FIELD32(0x00800000) +#define TXRX_CSR0_DROP_BORADCAST FIELD32(0x01000000) +#define TXRX_CSR0_DROP_ACK_CTS FIELD32(0x02000000) +#define TXRX_CSR0_TX_WITHOUT_WAITING FIELD32(0x04000000) + +/* + * TXRX_CSR1 + */ +#define TXRX_CSR1 0x3044 + +/* + * TXRX_CSR2 + */ +#define TXRX_CSR2 0x3048 + +/* + * TXRX_CSR3 + */ +#define TXRX_CSR3 0x304c + +/* + * TXRX_CSR4: Auto-Responder/Tx-retry register. + * AUTORESPOND_PREAMBLE: 0:long, 1:short preamble. + * OFDM_TX_RATE_DOWN: 1:enable. + * OFDM_TX_RATE_STEP: 0:1-step, 1: 2-step, 2:3-step, 3:4-step. + * OFDM_TX_FALLBACK_CCK: 0: Fallback to OFDM 6M only, 1: Fallback to CCK 1M,2M. + */ +#define TXRX_CSR4 0x3050 +#define TXRX_CSR4_TX_ACK_TIMEOUT FIELD32(0x000000ff) +#define TXRX_CSR4_CNTL_ACK_POLICY FIELD32(0x00000700) +#define TXRX_CSR4_ACK_CTS_PSM FIELD32(0x00010000) +#define TXRX_CSR4_AUTORESPOND_ENABLE FIELD32(0x00020000) +#define TXRX_CSR4_AUTORESPOND_PREAMBLE FIELD32(0x00040000) +#define TXRX_CSR4_OFDM_TX_RATE_DOWN FIELD32(0x00080000) +#define TXRX_CSR4_OFDM_TX_RATE_STEP FIELD32(0x00300000) +#define TXRX_CSR4_OFDM_TX_FALLBACK_CCK FIELD32(0x00400000) +#define TXRX_CSR4_LONG_RETRY_LIMIT FIELD32(0x0f000000) +#define TXRX_CSR4_SHORT_RETRY_LIMIT FIELD32(0xf0000000) + +/* + * TXRX_CSR5 + */ +#define TXRX_CSR5 0x3054 + +/* + * ACK/CTS payload consumed time registers. + */ +#define TXRX_CSR6 0x3058 +#define TXRX_CSR7 0x305c +#define TXRX_CSR8 0x3060 + +/* + * TXRX_CSR9: Synchronization control register. + * BEACON_INTERVAL: In unit of 1/16 TU. + * TSF_TICKING: Enable TSF auto counting. + * TSF_SYNC: Tsf sync, 0: disable, 1: infra, 2: ad-hoc/master mode. + * BEACON_GEN: Enable beacon generator. + */ +#define TXRX_CSR9 0x3064 +#define TXRX_CSR9_BEACON_INTERVAL FIELD32(0x0000ffff) +#define TXRX_CSR9_TSF_TICKING FIELD32(0x00010000) +#define TXRX_CSR9_TSF_SYNC FIELD32(0x00060000) +#define TXRX_CSR9_TBTT_ENABLE FIELD32(0x00080000) +#define TXRX_CSR9_BEACON_GEN FIELD32(0x00100000) +#define TXRX_CSR9_TIMESTAMP_COMPENSATE FIELD32(0xff000000) + +/* + * TXRX_CSR10: BEACON alignment. + */ +#define TXRX_CSR10 0x3068 + +/* + * TXRX_CSR11: AES mask. + */ +#define TXRX_CSR11 0x306c + +/* + * TXRX_CSR12: TSF low 32. + */ +#define TXRX_CSR12 0x3070 +#define TXRX_CSR12_LOW_TSFTIMER FIELD32(0xffffffff) + +/* + * TXRX_CSR13: TSF high 32. + */ +#define TXRX_CSR13 0x3074 +#define TXRX_CSR13_HIGH_TSFTIMER FIELD32(0xffffffff) + +/* + * TXRX_CSR14: TBTT timer. + */ +#define TXRX_CSR14 0x3078 + +/* + * TXRX_CSR15: TKIP MIC priority byte "AND" mask. + */ +#define TXRX_CSR15 0x307c + +/* + * PHY control registers. + * Some values are set in TU, whereas 1 TU == 1024 us. + */ + +/* + * PHY_CSR0: RF/PS control. + */ +#define PHY_CSR0 0x3080 +#define PHY_CSR0_PA_PE_BG FIELD32(0x00010000) +#define PHY_CSR0_PA_PE_A FIELD32(0x00020000) + +/* + * PHY_CSR1 + */ +#define PHY_CSR1 0x3084 + +/* + * PHY_CSR2: Pre-TX BBP control. + */ +#define PHY_CSR2 0x3088 + +/* + * PHY_CSR3: BBP serial control register. + * VALUE: Register value to program into BBP. + * REG_NUM: Selected BBP register. + * READ_CONTROL: 0: Write BBP, 1: Read BBP. + * BUSY: 1: ASIC is busy execute BBP programming. + */ +#define PHY_CSR3 0x308c +#define PHY_CSR3_VALUE FIELD32(0x000000ff) +#define PHY_CSR3_REGNUM FIELD32(0x00007f00) +#define PHY_CSR3_READ_CONTROL FIELD32(0x00008000) +#define PHY_CSR3_BUSY FIELD32(0x00010000) + +/* + * PHY_CSR4: RF serial control register + * VALUE: Register value (include register id) serial out to RF/IF chip. + * NUMBER_OF_BITS: Number of bits used in RFRegValue (I:20, RFMD:22). + * IF_SELECT: 1: select IF to program, 0: select RF to program. + * PLL_LD: RF PLL_LD status. + * BUSY: 1: ASIC is busy execute RF programming. + */ +#define PHY_CSR4 0x3090 +#define PHY_CSR4_VALUE FIELD32(0x00ffffff) +#define PHY_CSR4_NUMBER_OF_BITS FIELD32(0x1f000000) +#define PHY_CSR4_IF_SELECT FIELD32(0x20000000) +#define PHY_CSR4_PLL_LD FIELD32(0x40000000) +#define PHY_CSR4_BUSY FIELD32(0x80000000) + +/* + * PHY_CSR5: RX to TX signal switch timing control. + */ +#define PHY_CSR5 0x3094 + +/* + * PHY_CSR6: TX to RX signal timing control. + */ +#define PHY_CSR6 0x3098 + +/* + * PHY_CSR7: TX DAC switching timing control. + */ +#define PHY_CSR7 0x309c + +/* + * Security control register. + */ + +/* + * SEC_CSR0: Shared key table control. + */ +#define SEC_CSR0 0x30a0 + +/* + * SEC_CSR1: Shared key table security mode register. + */ +#define SEC_CSR1 0x30a4 +#define SEC_CSR1_BSS0_KEY0_CIPHER_ALG FIELD32(0x00000007) +#define SEC_CSR1_BSS0_KEY1_CIPHER_ALG FIELD32(0x00000070) +#define SEC_CSR1_BSS0_KEY2_CIPHER_ALG FIELD32(0x00000700) +#define SEC_CSR1_BSS0_KEY3_CIPHER_ALG FIELD32(0x00007000) +#define SEC_CSR1_BSS1_KEY0_CIPHER_ALG FIELD32(0x00070000) +#define SEC_CSR1_BSS1_KEY1_CIPHER_ALG FIELD32(0x00700000) +#define SEC_CSR1_BSS1_KEY2_CIPHER_ALG FIELD32(0x07000000) +#define SEC_CSR1_BSS1_KEY3_CIPHER_ALG FIELD32(0x70000000) + +/* + * Pairwise key table valid bitmap registers. + * SEC_CSR2: pairwise key table valid bitmap 0. + * SEC_CSR3: pairwise key table valid bitmap 1. + */ +#define SEC_CSR2 0x30a8 +#define SEC_CSR3 0x30ac + +/* + * SEC_CSR4: Pairwise key table lookup control. + */ +#define SEC_CSR4 0x30b0 + +/* + * SEC_CSR5: shared key table security mode register. + */ +#define SEC_CSR5 0x30b4 +#define SEC_CSR5_BSS2_KEY0_CIPHER_ALG FIELD32(0x00000007) +#define SEC_CSR5_BSS2_KEY1_CIPHER_ALG FIELD32(0x00000070) +#define SEC_CSR5_BSS2_KEY2_CIPHER_ALG FIELD32(0x00000700) +#define SEC_CSR5_BSS2_KEY3_CIPHER_ALG FIELD32(0x00007000) +#define SEC_CSR5_BSS3_KEY0_CIPHER_ALG FIELD32(0x00070000) +#define SEC_CSR5_BSS3_KEY1_CIPHER_ALG FIELD32(0x00700000) +#define SEC_CSR5_BSS3_KEY2_CIPHER_ALG FIELD32(0x07000000) +#define SEC_CSR5_BSS3_KEY3_CIPHER_ALG FIELD32(0x70000000) + +/* + * STA control registers. + */ + +/* + * STA_CSR0: RX PLCP error count & RX FCS error count. + */ +#define STA_CSR0 0x30c0 +#define STA_CSR0_FCS_ERROR FIELD32(0x0000ffff) +#define STA_CSR0_PLCP_ERROR FIELD32(0xffff0000) + +/* + * STA_CSR1: RX False CCA count & RX LONG frame count. + */ +#define STA_CSR1 0x30c4 +#define STA_CSR1_PHYSICAL_ERROR FIELD32(0x0000ffff) +#define STA_CSR1_FALSE_CCA_ERROR FIELD32(0xffff0000) + +/* + * STA_CSR2: TX Beacon count and RX FIFO overflow count. + */ +#define STA_CSR2 0x30c8 +#define STA_CSR2_RX_FIFO_OVERFLOW_COUNT FIELD32(0x0000ffff) +#define STA_CSR2_RX_OVERFLOW_COUNT FIELD32(0xffff0000) + +/* + * STA_CSR3: TX Beacon count. + */ +#define STA_CSR3 0x30cc +#define STA_CSR3_TX_BEACON_COUNT FIELD32(0x0000ffff) + +/* + * STA_CSR4: TX Retry count. + */ +#define STA_CSR4 0x30d0 +#define STA_CSR4_TX_NO_RETRY_COUNT FIELD32(0x0000ffff) +#define STA_CSR4_TX_ONE_RETRY_COUNT FIELD32(0xffff0000) + +/* + * STA_CSR5: TX Retry count. + */ +#define STA_CSR5 0x30d4 +#define STA_CSR4_TX_MULTI_RETRY_COUNT FIELD32(0x0000ffff) +#define STA_CSR4_TX_RETRY_FAIL_COUNT FIELD32(0xffff0000) + +/* + * QOS control registers. + */ + +/* + * QOS_CSR1: TXOP holder MAC address register. + */ +#define QOS_CSR1 0x30e4 +#define QOS_CSR1_BYTE4 FIELD32(0x000000ff) +#define QOS_CSR1_BYTE5 FIELD32(0x0000ff00) + +/* + * QOS_CSR2: TXOP holder timeout register. + */ +#define QOS_CSR2 0x30e8 + +/* + * RX QOS-CFPOLL MAC address register. + * QOS_CSR3: RX QOS-CFPOLL MAC address 0. + * QOS_CSR4: RX QOS-CFPOLL MAC address 1. + */ +#define QOS_CSR3 0x30ec +#define QOS_CSR4 0x30f0 + +/* + * QOS_CSR5: "QosControl" field of the RX QOS-CFPOLL. + */ +#define QOS_CSR5 0x30f4 + +/* + * WMM Scheduler Register + */ + +/* + * AIFSN_CSR: AIFSN for each EDCA AC. + * AIFSN0: For AC_BK. + * AIFSN1: For AC_BE. + * AIFSN2: For AC_VI. + * AIFSN3: For AC_VO. + */ +#define AIFSN_CSR 0x0400 +#define AIFSN_CSR_AIFSN0 FIELD32(0x0000000f) +#define AIFSN_CSR_AIFSN1 FIELD32(0x000000f0) +#define AIFSN_CSR_AIFSN2 FIELD32(0x00000f00) +#define AIFSN_CSR_AIFSN3 FIELD32(0x0000f000) + +/* + * CWMIN_CSR: CWmin for each EDCA AC. + * CWMIN0: For AC_BK. + * CWMIN1: For AC_BE. + * CWMIN2: For AC_VI. + * CWMIN3: For AC_VO. + */ +#define CWMIN_CSR 0x0404 +#define CWMIN_CSR_CWMIN0 FIELD32(0x0000000f) +#define CWMIN_CSR_CWMIN1 FIELD32(0x000000f0) +#define CWMIN_CSR_CWMIN2 FIELD32(0x00000f00) +#define CWMIN_CSR_CWMIN3 FIELD32(0x0000f000) + +/* + * CWMAX_CSR: CWmax for each EDCA AC. + * CWMAX0: For AC_BK. + * CWMAX1: For AC_BE. + * CWMAX2: For AC_VI. + * CWMAX3: For AC_VO. + */ +#define CWMAX_CSR 0x0408 +#define CWMAX_CSR_CWMAX0 FIELD32(0x0000000f) +#define CWMAX_CSR_CWMAX1 FIELD32(0x000000f0) +#define CWMAX_CSR_CWMAX2 FIELD32(0x00000f00) +#define CWMAX_CSR_CWMAX3 FIELD32(0x0000f000) + +/* + * AC_TXOP_CSR0: AC_BK/AC_BE TXOP register. + * AC0_TX_OP: For AC_BK, in unit of 32us. + * AC1_TX_OP: For AC_BE, in unit of 32us. + */ +#define AC_TXOP_CSR0 0x040c +#define AC_TXOP_CSR0_AC0_TX_OP FIELD32(0x0000ffff) +#define AC_TXOP_CSR0_AC1_TX_OP FIELD32(0xffff0000) + +/* + * AC_TXOP_CSR1: AC_VO/AC_VI TXOP register. + * AC2_TX_OP: For AC_VI, in unit of 32us. + * AC3_TX_OP: For AC_VO, in unit of 32us. + */ +#define AC_TXOP_CSR1 0x0410 +#define AC_TXOP_CSR1_AC2_TX_OP FIELD32(0x0000ffff) +#define AC_TXOP_CSR1_AC3_TX_OP FIELD32(0xffff0000) + +/* + * RF registers + */ +#define RF3_TXPOWER FIELD32(0x00003e00) +#define RF4_FREQ_OFFSET FIELD32(0x0003f000) + +/* + * EEPROM content. + * The wordsize of the EEPROM is 16 bits. + */ + +/* + * HW MAC address. + */ +#define EEPROM_MAC_ADDR_0 0x0002 +#define EEPROM_MAC_ADDR_BYTE0 FIELD16(0x00ff) +#define EEPROM_MAC_ADDR_BYTE1 FIELD16(0xff00) +#define EEPROM_MAC_ADDR1 0x0003 +#define EEPROM_MAC_ADDR_BYTE2 FIELD16(0x00ff) +#define EEPROM_MAC_ADDR_BYTE3 FIELD16(0xff00) +#define EEPROM_MAC_ADDR_2 0x0004 +#define EEPROM_MAC_ADDR_BYTE4 FIELD16(0x00ff) +#define EEPROM_MAC_ADDR_BYTE5 FIELD16(0xff00) + +/* + * EEPROM antenna. + * ANTENNA_NUM: Number of antenna's. + * TX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. + * RX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. + * FRAME_TYPE: 0: DPDT , 1: SPDT , noted this bit is valid for g only. + * DYN_TXAGC: Dynamic TX AGC control. + * HARDWARE_RADIO: 1: Hardware controlled radio. Read GPIO0. + * RF_TYPE: Rf_type of this adapter. + */ +#define EEPROM_ANTENNA 0x0010 +#define EEPROM_ANTENNA_NUM FIELD16(0x0003) +#define EEPROM_ANTENNA_TX_DEFAULT FIELD16(0x000c) +#define EEPROM_ANTENNA_RX_DEFAULT FIELD16(0x0030) +#define EEPROM_ANTENNA_FRAME_TYPE FIELD16(0x0040) +#define EEPROM_ANTENNA_DYN_TXAGC FIELD16(0x0200) +#define EEPROM_ANTENNA_HARDWARE_RADIO FIELD16(0x0400) +#define EEPROM_ANTENNA_RF_TYPE FIELD16(0xf800) + +/* + * EEPROM NIC config. + * EXTERNAL_LNA: External LNA. + */ +#define EEPROM_NIC 0x0011 +#define EEPROM_NIC_EXTERNAL_LNA FIELD16(0x0010) + +/* + * EEPROM geography. + * GEO_A: Default geographical setting for 5GHz band + * GEO: Default geographical setting. + */ +#define EEPROM_GEOGRAPHY 0x0012 +#define EEPROM_GEOGRAPHY_GEO_A FIELD16(0x00ff) +#define EEPROM_GEOGRAPHY_GEO FIELD16(0xff00) + +/* + * EEPROM BBP. + */ +#define EEPROM_BBP_START 0x0013 +#define EEPROM_BBP_SIZE 16 +#define EEPROM_BBP_VALUE FIELD16(0x00ff) +#define EEPROM_BBP_REG_ID FIELD16(0xff00) + +/* + * EEPROM TXPOWER 802.11G + */ +#define EEPROM_TXPOWER_G_START 0x0023 +#define EEPROM_TXPOWER_G_SIZE 7 +#define EEPROM_TXPOWER_G_1 FIELD16(0x00ff) +#define EEPROM_TXPOWER_G_2 FIELD16(0xff00) + +/* + * EEPROM Frequency + */ +#define EEPROM_FREQ 0x002f +#define EEPROM_FREQ_OFFSET FIELD16(0x00ff) +#define EEPROM_FREQ_SEQ_MASK FIELD16(0xff00) +#define EEPROM_FREQ_SEQ FIELD16(0x0300) + +/* + * EEPROM LED. + * POLARITY_RDY_G: Polarity RDY_G setting. + * POLARITY_RDY_A: Polarity RDY_A setting. + * POLARITY_ACT: Polarity ACT setting. + * POLARITY_GPIO_0: Polarity GPIO0 setting. + * POLARITY_GPIO_1: Polarity GPIO1 setting. + * POLARITY_GPIO_2: Polarity GPIO2 setting. + * POLARITY_GPIO_3: Polarity GPIO3 setting. + * POLARITY_GPIO_4: Polarity GPIO4 setting. + * LED_MODE: Led mode. + */ +#define EEPROM_LED 0x0030 +#define EEPROM_LED_POLARITY_RDY_G FIELD16(0x0001) +#define EEPROM_LED_POLARITY_RDY_A FIELD16(0x0002) +#define EEPROM_LED_POLARITY_ACT FIELD16(0x0004) +#define EEPROM_LED_POLARITY_GPIO_0 FIELD16(0x0008) +#define EEPROM_LED_POLARITY_GPIO_1 FIELD16(0x0010) +#define EEPROM_LED_POLARITY_GPIO_2 FIELD16(0x0020) +#define EEPROM_LED_POLARITY_GPIO_3 FIELD16(0x0040) +#define EEPROM_LED_POLARITY_GPIO_4 FIELD16(0x0080) +#define EEPROM_LED_LED_MODE FIELD16(0x1f00) + +/* + * EEPROM TXPOWER 802.11A + */ +#define EEPROM_TXPOWER_A_START 0x0031 +#define EEPROM_TXPOWER_A_SIZE 12 +#define EEPROM_TXPOWER_A_1 FIELD16(0x00ff) +#define EEPROM_TXPOWER_A_2 FIELD16(0xff00) + +/* + * DMA descriptor defines. + */ +#define TXD_DESC_SIZE ( 6 * sizeof(struct data_desc) ) +#define RXD_DESC_SIZE ( 6 * sizeof(struct data_desc) ) + +/* + * TX descriptor format for TX, PRIO and Beacon Ring. + */ + +/* + * Word0 + * BURST: Next frame belongs to same "burst" event. + * TKIP_MIC: ASIC appends TKIP MIC if TKIP is used. + * KEY_TABLE: Use per-client pairwise KEY table. + * KEY_INDEX: + * Key index (0~31) to the pairwise KEY table. + * 0~3 to shared KEY table 0 (BSS0). + * 4~7 to shared KEY table 1 (BSS1). + * 8~11 to shared KEY table 2 (BSS2). + * 12~15 to shared KEY table 3 (BSS3). + * BURST2: For backward compatibility, set to same value as BURST. + */ +#define TXD_W0_BURST FIELD32(0x00000001) +#define TXD_W0_VALID FIELD32(0x00000002) +#define TXD_W0_MORE_FRAG FIELD32(0x00000004) +#define TXD_W0_ACK FIELD32(0x00000008) +#define TXD_W0_TIMESTAMP FIELD32(0x00000010) +#define TXD_W0_OFDM FIELD32(0x00000020) +#define TXD_W0_IFS FIELD32(0x00000040) +#define TXD_W0_RETRY_MODE FIELD32(0x00000080) +#define TXD_W0_TKIP_MIC FIELD32(0x00000100) +#define TXD_W0_KEY_TABLE FIELD32(0x00000200) +#define TXD_W0_KEY_INDEX FIELD32(0x0000fc00) +#define TXD_W0_DATABYTE_COUNT FIELD32(0x0fff0000) +#define TXD_W0_BURST2 FIELD32(0x10000000) +#define TXD_W0_CIPHER_ALG FIELD32(0xe0000000) + +/* + * Word1 + * HOST_Q_ID: EDCA/HCCA queue ID. + * HW_SEQUENCE: MAC overwrites the frame sequence number. + * BUFFER_COUNT: Number of buffers in this TXD. + */ +#define TXD_W1_HOST_Q_ID FIELD32(0x0000000f) +#define TXD_W1_AIFSN FIELD32(0x000000f0) +#define TXD_W1_CWMIN FIELD32(0x00000f00) +#define TXD_W1_CWMAX FIELD32(0x0000f000) +#define TXD_W1_IV_OFFSET FIELD32(0x003f0000) +#define TXD_W1_HW_SEQUENCE FIELD32(0x10000000) +#define TXD_W1_BUFFER_COUNT FIELD32(0xe0000000) + +/* + * Word2: PLCP information + */ +#define TXD_W2_PLCP_SIGNAL FIELD32(0x000000ff) +#define TXD_W2_PLCP_SERVICE FIELD32(0x0000ff00) +#define TXD_W2_PLCP_LENGTH_LOW FIELD32(0x00ff0000) +#define TXD_W2_PLCP_LENGTH_HIGH FIELD32(0xff000000) + +/* + * Word3 + */ +#define TXD_W3_IV FIELD32(0xffffffff) + +/* + * Word4 + */ +#define TXD_W4_EIV FIELD32(0xffffffff) + +/* + * Word5 + * FRAME_OFFSET: Frame start offset inside ASIC TXFIFO (after TXINFO field). + * PACKET_ID: Driver assigned packet ID to categorize TXResult in interrupt. + * WAITING_DMA_DONE_INT: TXD been filled with data + * and waiting for TxDoneISR housekeeping. + */ +#define TXD_W5_FRAME_OFFSET FIELD32(0x000000ff) +#define TXD_W5_PACKET_ID FIELD32(0x0000ff00) +#define TXD_W5_TX_POWER FIELD32(0x00ff0000) +#define TXD_W5_WAITING_DMA_DONE_INT FIELD32(0x01000000) + +/* + * RX descriptor format for RX Ring. + */ + +/* + * Word0 + * CIPHER_ERROR: 1:ICV error, 2:MIC error, 3:invalid key. + * KEY_INDEX: Decryption key actually used. + */ +#define RXD_W0_OWNER_NIC FIELD32(0x00000001) +#define RXD_W0_DROP FIELD32(0x00000002) +#define RXD_W0_UNICAST_TO_ME FIELD32(0x00000004) +#define RXD_W0_MULTICAST FIELD32(0x00000008) +#define RXD_W0_BROADCAST FIELD32(0x00000010) +#define RXD_W0_MY_BSS FIELD32(0x00000020) +#define RXD_W0_CRC FIELD32(0x00000040) +#define RXD_W0_OFDM FIELD32(0x00000080) +#define RXD_W0_CIPHER_ERROR FIELD32(0x00000300) +#define RXD_W0_KEY_INDEX FIELD32(0x0000fc00) +#define RXD_W0_DATABYTE_COUNT FIELD32(0x0fff0000) +#define RXD_W0_CIPHER_ALG FIELD32(0xe0000000) + +/* + * WORD1 + * SIGNAL: RX raw data rate reported by BBP. + * RSSI: RSSI reported by BBP. + */ +#define RXD_W1_SIGNAL FIELD32(0x000000ff) +#define RXD_W1_RSSI FIELD32(0x0000ff00) +#define RXD_W1_FRAME_OFFSET FIELD32(0x7f000000) + +/* + * Word2 + * IV: Received IV of originally encrypted. + */ +#define RXD_W2_IV FIELD32(0xffffffff) + +/* + * Word3 + * EIV: Received EIV of originally encrypted. + */ +#define RXD_W3_EIV FIELD32(0xffffffff) + +/* + * Word4 + */ +#define RXD_W4_RESERVED FIELD32(0xffffffff) + +/* + * the above 20-byte is called RXINFO and will be DMAed to MAC RX block + * and passed to the HOST driver. + * The following fields are for DMA block and HOST usage only. + * Can't be touched by ASIC MAC block. + */ + +/* + * Word5 + */ +#define RXD_W5_RESERVED FIELD32(0xffffffff) + +/* + * TX ring index number for rt2x00_dev structure. + */ +enum ring_index { + RING_AC_VO = 0, + RING_AC_VI = 1, + RING_AC_BE = 2, + RING_AC_BK = 3, + RING_PRIO = 4, + RING_BEACON = 5, + RING_RX = 6, + RING_NUM = 7, + RING_NUM_TX = 5, +}; + +/* + * Macro's for converting txpower from EEPROM to dscape value + * and from dscape value to register value. + */ +#define MIN_TXPOWER 0 +#define MAX_TXPOWER 31 +#define DEFAULT_TXPOWER 24 + +#define TXPOWER_FROM_DEV(__txpower) \ + ({ \ + ((__txpower) > MAX_TXPOWER) ? DEFAULT_TXPOWER : (__txpower); \ + }) + +#define TXPOWER_TO_DEV(__txpower) \ + ({ \ + ((__txpower) <= MIN_TXPOWER) ? MIN_TXPOWER : \ + (((__txpower) >= MAX_TXPOWER) ? MAX_TXPOWER : \ + (__txpower)); \ + }) + +/* + * LED control functions. + */ +static void rt73usb_enable_led(struct rt2x00_dev *rt2x00dev); +static void rt73usb_disable_led(struct rt2x00_dev *rt2x00dev); +static void rt73usb_activity_led(struct rt2x00_dev *rt2x00dev, char rssi); + +/* + * Interrupt functions. + */ +static void rt73usb_interrupt_rxdone(struct urb *urb); + +#endif /* RT73USB_H */ diff --git a/drivers/net/wireless/mac80211/rtl818x/Kconfig b/drivers/net/wireless/mac80211/rtl818x/Kconfig new file mode 100644 index 0000000..e2fde30 --- /dev/null +++ b/drivers/net/wireless/mac80211/rtl818x/Kconfig @@ -0,0 +1,9 @@ +config RTL818X + bool + default n + +config RTL8187 + tristate "Realtek 8187 USB support" + depends on MAC80211 && USB && WLAN_80211 && EXPERIMENTAL + select RTL818X + select EEPROM_93CX6 diff --git a/drivers/net/wireless/mac80211/rtl818x/Makefile b/drivers/net/wireless/mac80211/rtl818x/Makefile new file mode 100644 index 0000000..fe5dd6f --- /dev/null +++ b/drivers/net/wireless/mac80211/rtl818x/Makefile @@ -0,0 +1,2 @@ +rtl8187-objs := rtl8187_dev.o rtl8187_rtl8225.o +obj-$(CONFIG_RTL8187) += rtl8187.o diff --git a/drivers/net/wireless/mac80211/rtl818x/rtl8187.h b/drivers/net/wireless/mac80211/rtl818x/rtl8187.h new file mode 100644 index 0000000..476de5e --- /dev/null +++ b/drivers/net/wireless/mac80211/rtl818x/rtl8187.h @@ -0,0 +1,126 @@ +#ifndef RTL8187_H +#define RTL8187_H + +#include "rtl818x.h" + +#define RTL8187_REQT_READ 0xC0 +#define RTL8187_REQT_WRITE 0x40 +#define RTL8187_REQ_GET_REG 0x05 +#define RTL8187_REQ_SET_REG 0x05 + +#define RTL8187_MAX_RX 0x9C4 + +struct rtl8187_rx_info { + struct urb *urb; + struct ieee80211_hw *dev; +}; + +struct rtl8187_rx_hdr { + __le16 len; + __le16 rate; + u8 noise; + u8 signal; + u8 agc; + u8 reserved; + __le64 mac_time; +} __attribute__((packed)); + +struct rtl8187_tx_info { + struct ieee80211_tx_control *control; + struct urb *urb; + struct ieee80211_hw *dev; +}; + +struct rtl8187_tx_hdr { + __le32 flags; +#define RTL8187_TX_FLAG_NO_ENCRYPT (1 << 15) +#define RTL8187_TX_FLAG_MORE_FRAG (1 << 17) +#define RTL8187_TX_FLAG_CTS (1 << 18) +#define RTL8187_TX_FLAG_RTS (1 << 23) + __le16 rts_duration; + __le16 len; + __le32 retry; +} __attribute__((packed)); + +struct rtl8187_priv { + /* common between rtl818x drivers */ + struct rtl818x_csr __iomem *map; + void (*rf_init)(struct ieee80211_hw *); + int mode; + u16 seq_counter; /* FIXME: remove when mac80211 gets support */ + + /* rtl8187 specific */ + struct ieee80211_channel channels[14]; + struct ieee80211_rate rates[12]; + struct ieee80211_hw_mode modes[2]; + struct usb_device *udev; + u8 *hwaddr; + u16 txpwr_base; + u8 asic_rev; + struct sk_buff_head rx_queue; +}; + +void rtl8187_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data); + +static inline u8 rtl818x_ioread8(struct rtl8187_priv *priv, u8 __iomem *addr) +{ + u8 val; + + usb_control_msg(priv->udev, usb_rcvctrlpipe(priv->udev, 0), + RTL8187_REQ_GET_REG, RTL8187_REQT_READ, + (unsigned long)addr, 0, &val, sizeof(val), HZ/2); + + return val; +} + +static inline u16 rtl818x_ioread16(struct rtl8187_priv *priv, __le16 __iomem *addr) +{ + __le16 val; + + usb_control_msg(priv->udev, usb_rcvctrlpipe(priv->udev, 0), + RTL8187_REQ_GET_REG, RTL8187_REQT_READ, + (unsigned long)addr, 0, &val, sizeof(val), HZ/2); + + return le16_to_cpu(val); +} + +static inline u32 rtl818x_ioread32(struct rtl8187_priv *priv, __le32 __iomem *addr) +{ + __le32 val; + + usb_control_msg(priv->udev, usb_rcvctrlpipe(priv->udev, 0), + RTL8187_REQ_GET_REG, RTL8187_REQT_READ, + (unsigned long)addr, 0, &val, sizeof(val), HZ/2); + + return le32_to_cpu(val); +} + +static inline void rtl818x_iowrite8(struct rtl8187_priv *priv, + u8 __iomem *addr, u8 val) +{ + usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0), + RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE, + (unsigned long)addr, 0, &val, sizeof(val), HZ/2); +} + +static inline void rtl818x_iowrite16(struct rtl8187_priv *priv, + __le16 __iomem *addr, u16 val) +{ + __le16 buf = cpu_to_le16(val); + + usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0), + RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE, + (unsigned long)addr, 0, &buf, sizeof(buf), HZ/2); +} + +static inline void rtl818x_iowrite32(struct rtl8187_priv *priv, + __le32 __iomem *addr, u32 val) +{ + __le32 buf = cpu_to_le32(val); + + usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0), + RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE, + (unsigned long)addr, 0, &buf, sizeof(buf), HZ/2); +} + +#endif /* RTL8187_H */ diff --git a/drivers/net/wireless/mac80211/rtl818x/rtl8187_dev.c b/drivers/net/wireless/mac80211/rtl818x/rtl8187_dev.c new file mode 100644 index 0000000..adf9c61 --- /dev/null +++ b/drivers/net/wireless/mac80211/rtl818x/rtl8187_dev.c @@ -0,0 +1,726 @@ + +/* + * Linux device driver for RTL8187 + * + * Copyright 2007 Michael Wu + * Copyright 2007 Andrea Merello + * + * Based on the r8187 driver, which is: + * Copyright 2005 Andrea Merello , et al. + * + * Thanks to Realtek for their support! + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include "rtl8187.h" +#include "rtl8187_rtl8225.h" + +MODULE_AUTHOR("Michael Wu "); +MODULE_AUTHOR("Andrea Merello "); +MODULE_DESCRIPTION("RTL8187 USB wireless driver"); +MODULE_LICENSE("GPL"); + +static struct usb_device_id rtl8187_table[] __devinitdata = { + /* Realtek */ + {USB_DEVICE(0x0bda, 0x8187)}, + /* Netgear */ + {USB_DEVICE(0x0846, 0x6100)}, + {USB_DEVICE(0x0846, 0x6a00)}, + {} +}; + +MODULE_DEVICE_TABLE(usb, rtl8187_table); + +void rtl8187_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data) +{ + struct rtl8187_priv *priv = dev->priv; + + data <<= 8; + data |= addr | 0x80; + + rtl818x_iowrite8(priv, &priv->map->PHY[3], (data >> 24) & 0xFF); + rtl818x_iowrite8(priv, &priv->map->PHY[2], (data >> 16) & 0xFF); + rtl818x_iowrite8(priv, &priv->map->PHY[1], (data >> 8) & 0xFF); + rtl818x_iowrite8(priv, &priv->map->PHY[0], data & 0xFF); + + mdelay(1); +} + +static void rtl8187_tx_cb(struct urb *urb) +{ + struct ieee80211_tx_status status = {{0}}; + struct sk_buff *skb = (struct sk_buff *)urb->context; + struct rtl8187_tx_info *info = (struct rtl8187_tx_info *) skb->cb; + + usb_free_urb(info->urb); + if (info->control) + memcpy(&status.control, info->control, sizeof(status.control)); + kfree(info->control); + skb_pull(skb, sizeof(struct rtl8187_tx_hdr)); + status.flags |= IEEE80211_TX_STATUS_ACK; + ieee80211_tx_status_irqsafe(info->dev, skb, &status); +} + +static int rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb, + struct ieee80211_tx_control *control) +{ + struct rtl8187_priv *priv = dev->priv; + struct rtl8187_tx_hdr *hdr; + struct rtl8187_tx_info *info; + struct ieee80211_hdr *ieeehdr; + struct urb *urb; + u32 tmp; + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) { + kfree_skb(skb); + return 0; + } + + if (ieee80211_get_hdrlen_from_skb(skb) >= 24) { + ieeehdr = (struct ieee80211_hdr *) skb->data; + ieeehdr->seq_ctrl = cpu_to_le16(priv->seq_counter); + priv->seq_counter += 0x10; + priv->seq_counter &= IEEE80211_SCTL_SEQ; + } + + hdr = (struct rtl8187_tx_hdr *) skb_push(skb, sizeof(*hdr)); + tmp = skb->len - sizeof(*hdr); + tmp |= RTL8187_TX_FLAG_NO_ENCRYPT; + tmp |= control->rts_cts_rate << 19; + tmp |= control->tx_rate << 24; + if(ieee80211_get_morefrag((struct ieee80211_hdr *)skb)) + tmp |= RTL8187_TX_FLAG_MORE_FRAG; + if(control->flags & IEEE80211_TXCTL_USE_RTS_CTS) + tmp |= RTL8187_TX_FLAG_RTS; + if(control->flags & IEEE80211_TXCTL_USE_CTS_PROTECT) + tmp |= RTL8187_TX_FLAG_CTS; + hdr->flags = cpu_to_le32(tmp); + hdr->rts_duration = 0; /* TODO : follow RTL sample */ + hdr->len = 0; + tmp = control->retry_limit << 8; + hdr->retry = cpu_to_le32(tmp); + + info = (struct rtl8187_tx_info *) skb->cb; + info->control = kmemdup(control, sizeof(*control), GFP_ATOMIC); + info->urb = urb; + info->dev = dev; + usb_fill_bulk_urb(urb, priv->udev, usb_sndbulkpipe(priv->udev, 2), + hdr, skb->len, rtl8187_tx_cb, skb); + usb_submit_urb(urb, GFP_ATOMIC); + + return 0; +} + + +static void rtl8187_rx_cb(struct urb *urb) +{ + struct sk_buff *skb = (struct sk_buff *) urb->context; + struct rtl8187_rx_info *info = (struct rtl8187_rx_info *)skb->cb; + struct ieee80211_hw *dev = info->dev; + struct rtl8187_priv *priv = dev->priv; + struct rtl8187_rx_hdr *hdr; + struct ieee80211_rx_status rx_status = {0}; + int rate, signal; + + if (unlikely(urb->status)) { + info->urb = NULL; + usb_free_urb(urb); + return; + } + + skb_unlink(skb, &priv->rx_queue); + skb_put(skb, urb->actual_length); + hdr = (struct rtl8187_rx_hdr *) (skb->tail - sizeof(*hdr)); + skb_trim(skb, le16_to_cpu(hdr->len) & 0x0FFF); + + signal = hdr->agc >> 1; + rate = (le16_to_cpu(hdr->rate) >> 4) & 0xF; + if (rate > 3) { // OFDM rate. + if (signal > 90) + signal = 90; + else if (signal < 25) + signal = 25; + signal = 90 - signal; + } else { // CCK rate. + if (signal > 95) + signal = 95; + else if (signal < 30) + signal = 30; + signal = 95 - signal; + } + + rx_status.antenna = (hdr->signal >> 7) & 1; + rx_status.signal = 64 - min(hdr->noise, (u8)64); + rx_status.ssi = signal; + rx_status.rate = priv->rates[rate].rate; + rx_status.freq = dev->conf.freq; + rx_status.channel = dev->conf.channel; + rx_status.phymode = dev->conf.phymode; + rx_status.mactime = le64_to_cpu(hdr->mac_time); + ieee80211_rx_irqsafe(dev, skb, &rx_status); + + skb = dev_alloc_skb(RTL8187_MAX_RX); + if (unlikely(!skb)) { + usb_free_urb(urb); + /* TODO check rx queue length and refill *somewhere* */ + return; + } + + info = (struct rtl8187_rx_info *) skb->cb; + info->urb = urb; + info->dev = dev; + urb->transfer_buffer = skb->tail; + urb->context = skb; + skb_queue_tail(&priv->rx_queue, skb); + + usb_submit_urb(urb, GFP_ATOMIC); +} + +static int rtl8187_init_urbs(struct ieee80211_hw *dev) +{ + struct rtl8187_priv *priv = dev->priv; + struct urb *entry; + struct sk_buff *skb; + struct rtl8187_rx_info *info; + + while (skb_queue_len(&priv->rx_queue) < 8) { + skb = __dev_alloc_skb(RTL8187_MAX_RX, GFP_KERNEL); + if (!skb) + break; + entry = usb_alloc_urb(0, GFP_KERNEL); + if (!entry) { + kfree_skb(skb); + break; + } + usb_fill_bulk_urb(entry, priv->udev, + usb_rcvbulkpipe(priv->udev, 1), skb->tail, + RTL8187_MAX_RX, rtl8187_rx_cb, skb); + info = (struct rtl8187_rx_info *) skb->cb; + info->urb = entry; + info->dev = dev; + skb_queue_tail(&priv->rx_queue, skb); + usb_submit_urb(entry, GFP_KERNEL); + } + + return 0; +} + +static int rtl8187_init_hw(struct ieee80211_hw *dev) +{ + struct rtl8187_priv *priv = dev->priv; + u8 reg; + int i; + + /* reset */ + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | RTL818X_CONFIG3_ANAPARAM_WRITE); + rtl818x_iowrite32(priv, &priv->map->ANAPARAM, RTL8225_ANAPARAM_ON); + rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, RTL8225_ANAPARAM2_ON); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); + + rtl818x_iowrite16(priv, &priv->map->INTA_MASK, 0); + + mdelay(200); + rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x10); + rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x11); + rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x00); + mdelay(200); + + reg = rtl818x_ioread8(priv, &priv->map->CMD); + reg &= (1 << 1); + reg |= RTL818X_CMD_RESET; + rtl818x_iowrite8(priv, &priv->map->CMD, reg); + + mdelay(200); + + // check success of reset + if (rtl818x_ioread8(priv, &priv->map->CMD) & RTL818X_CMD_RESET) { + printk(KERN_ERR "rtl8187: reset timeout!\n"); + return -ETIMEDOUT; + } + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_LOAD); + mdelay(200); + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | RTL818X_CONFIG3_ANAPARAM_WRITE); + rtl818x_iowrite32(priv, &priv->map->ANAPARAM, RTL8225_ANAPARAM_ON); + rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, RTL8225_ANAPARAM2_ON); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); + + /* setup card */ + rtl818x_iowrite8(priv, (u8 *)0xFF85, 0); + rtl818x_iowrite8(priv, &priv->map->GPIO, 0); + + rtl818x_iowrite8(priv, (u8 *)0xFF85, 4); + rtl818x_iowrite8(priv, &priv->map->GPIO, 1); + rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0); + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + for (i = 0; i < ETH_ALEN; i++) + rtl818x_iowrite8(priv, &priv->map->MAC[i], priv->hwaddr[i]); + + rtl818x_iowrite16(priv, (__le16 *)0xFFF4, 0xFFFF); + reg = rtl818x_ioread8(priv, &priv->map->CONFIG1); + reg &= 0x3F; + reg |= 0x80; + rtl818x_iowrite8(priv, &priv->map->CONFIG1, reg); + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, 0); + + rtl818x_iowrite32(priv, &priv->map->INT_TIMEOUT, 0); + rtl818x_iowrite8(priv, &priv->map->WPA_CONF, 0); + rtl818x_iowrite8(priv, &priv->map->RATE_FALLBACK, 0x81); + + // TODO: set RESP_RATE and BRSR properly + rtl818x_iowrite8(priv, &priv->map->RESP_RATE, (8 << 4) | 0); + rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3); + + // ehh? + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | RTL818X_CONFIG3_ANAPARAM_WRITE); + rtl818x_iowrite32(priv, &priv->map->ANAPARAM, RTL8225_ANAPARAM_ON); + reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); + + // host_usb_init + rtl818x_iowrite8(priv, (u8 *)0xFF85, 0); + rtl818x_iowrite8(priv, &priv->map->GPIO, 0); + reg = rtl818x_ioread8(priv, (u8 *)0xFE53); + rtl818x_iowrite8(priv, (u8 *)0xFE53, reg | (1 << 7)); + rtl818x_iowrite8(priv, (u8 *)0xFF85, 4); + rtl818x_iowrite8(priv, &priv->map->GPIO, 0x20); + rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x80); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x80); + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x80); + mdelay(100); + + rtl818x_iowrite32(priv, &priv->map->RF_TIMING, 0x000a8008); + rtl818x_iowrite16(priv, &priv->map->BRSR, 0xFFFF); + rtl818x_iowrite32(priv, &priv->map->RF_PARA, 0x00100044); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, 0x44); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FF7); + mdelay(100); + + priv->rf_init(dev); + + rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3); + reg = rtl818x_ioread16(priv, &priv->map->PGSELECT) & 0xfffe; + rtl818x_iowrite16(priv, &priv->map->PGSELECT, reg | 0x1); + rtl818x_iowrite16(priv, (__le16 *)0xFFFE, 0x10); + rtl818x_iowrite8(priv, &priv->map->TALLY_SEL, 0x80); + rtl818x_iowrite8(priv, (u8 *)0xFFFF, 0x60); + rtl818x_iowrite16(priv, &priv->map->PGSELECT, reg); + + return 0; +} + +static void rtl8187_set_channel(struct ieee80211_hw *dev, int channel) +{ + u32 reg; + struct rtl8187_priv *priv = dev->priv; + + reg = rtl818x_ioread32(priv, &priv->map->TX_CONF); + /* Enable TX loopback on MAC level to avoid TX during channel + * changes, as this has be seen to causes problems and the + * card will stop work until next reset + */ + rtl818x_iowrite32(priv, &priv->map->TX_CONF, + reg | RTL818X_TX_CONF_LOOPBACK_MAC); + mdelay(10); + rtl8225_rf_set_channel(dev, channel); + mdelay(10); + rtl818x_iowrite32(priv, &priv->map->TX_CONF, reg); +} + +static int rtl8187_open(struct ieee80211_hw *dev) +{ + struct rtl8187_priv *priv = dev->priv; + u32 reg; + int ret; + + ret = rtl8187_init_hw(dev); + if (ret) + return ret; + + rtl818x_iowrite16(priv, &priv->map->INTA_MASK, 0xFFFF); + + rtl8187_init_urbs(dev); + + reg = RTL818X_RX_CONF_ONLYERLPKT | + RTL818X_RX_CONF_RX_AUTORESETPHY | + RTL818X_RX_CONF_BSSID | + RTL818X_RX_CONF_MGMT | + RTL818X_RX_CONF_CTRL | + RTL818X_RX_CONF_DATA | + (7 << 13 /* RX FIFO threshold NONE */) | + (7 << 10 /* MAX RX DMA */) | + RTL818X_RX_CONF_BROADCAST | + RTL818X_RX_CONF_MULTICAST | + RTL818X_RX_CONF_NICMAC; + if(priv->mode == IEEE80211_IF_TYPE_MNTR) + reg |= RTL818X_RX_CONF_MONITOR; + + rtl818x_iowrite32(priv, &priv->map->RX_CONF, reg); + + reg = rtl818x_ioread8(priv, &priv->map->CW_CONF); + reg &= ~RTL818X_CW_CONF_PERPACKET_CW_SHIFT; + reg |= RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT; + rtl818x_iowrite8(priv, &priv->map->CW_CONF, reg); + + reg = rtl818x_ioread8(priv, &priv->map->TX_AGC_CTL); + reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT; + reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT; + reg &= ~RTL818X_TX_AGC_CTL_FEEDBACK_ANT; + rtl818x_iowrite8(priv, &priv->map->TX_AGC_CTL, reg); + + reg = RTL818X_TX_CONF_CW_MIN | + (7 << 21 /* MAX TX DMA */) | + RTL818X_TX_CONF_NO_ICV; + rtl818x_iowrite32(priv, &priv->map->TX_CONF, reg); + + reg = rtl818x_ioread8(priv, &priv->map->CMD); + reg |= RTL818X_CMD_TX_ENABLE; + reg |= RTL818X_CMD_RX_ENABLE; + rtl818x_iowrite8(priv, &priv->map->CMD, reg); + + return 0; +} + +static int rtl8187_stop(struct ieee80211_hw *dev) +{ + struct rtl8187_priv *priv = dev->priv; + struct rtl8187_rx_info *info; + struct sk_buff *skb; + u32 reg; + + rtl818x_iowrite16(priv, &priv->map->INTA_MASK, 0); + + reg = rtl818x_ioread8(priv, &priv->map->CMD); + reg &= ~RTL818X_CMD_TX_ENABLE; + reg &= ~RTL818X_CMD_RX_ENABLE; + rtl818x_iowrite8(priv, &priv->map->CMD, reg); + + rtl8225_rf_stop(dev); + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + reg = rtl818x_ioread8(priv, &priv->map->CONFIG4); + rtl818x_iowrite8(priv, &priv->map->CONFIG4, reg | RTL818X_CONFIG4_VCOOFF); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); + + while ((skb = skb_dequeue(&priv->rx_queue))) { + info = (struct rtl8187_rx_info *) skb->cb; + if (!info->urb) + continue; + + usb_kill_urb(info->urb); + kfree_skb(skb); + } + return 0; +} + +static int rtl8187_add_interface(struct ieee80211_hw *dev, + struct ieee80211_if_init_conf *conf) +{ + struct rtl8187_priv *priv = dev->priv; + + /* NOTE: using IEEE80211_IF_TYPE_MGMT to indicate no mode selected */ + if (priv->mode != IEEE80211_IF_TYPE_MGMT) + return -1; + + switch (conf->type) { + case IEEE80211_IF_TYPE_STA: + case IEEE80211_IF_TYPE_MNTR: + priv->mode = conf->type; + break; + default: + return -EOPNOTSUPP; + } + + priv->hwaddr = conf->mac_addr; + + return 0; +} + +static void rtl8187_remove_interface(struct ieee80211_hw *dev, + struct ieee80211_if_init_conf *conf) +{ + struct rtl8187_priv *priv = dev->priv; + priv->mode = IEEE80211_IF_TYPE_MGMT; +} + +static int rtl8187_config(struct ieee80211_hw *dev, struct ieee80211_conf *conf) +{ + struct rtl8187_priv *priv = dev->priv; + rtl8187_set_channel(dev, conf->channel); + + rtl818x_iowrite8(priv, &priv->map->SIFS, 0x22); + + if (conf->flags & IEEE80211_CONF_SHORT_SLOT_TIME) + rtl818x_iowrite8(priv, &priv->map->SLOT, 0x9); + else + rtl818x_iowrite8(priv, &priv->map->SLOT, 0x14); + + switch (conf->phymode) { + case MODE_IEEE80211B: + rtl818x_iowrite8(priv, &priv->map->DIFS, 0x24); + rtl818x_iowrite8(priv, &priv->map->EIFS, 91 - 0x24); + rtl818x_iowrite8(priv, &priv->map->CW_VAL, 0xa5); + break; + case MODE_IEEE80211G: + rtl818x_iowrite8(priv, &priv->map->DIFS, 0x14); + rtl818x_iowrite8(priv, &priv->map->EIFS, 91 - 0x14); + rtl818x_iowrite8(priv, &priv->map->CW_VAL, 0x73); + break; + default: + BUG(); + break; + } + + rtl818x_iowrite16(priv, &priv->map->ATIM_WND, 2); + rtl818x_iowrite16(priv, &priv->map->ATIMTR_INTERVAL, 100); + rtl818x_iowrite16(priv, &priv->map->BEACON_INTERVAL, 100); + rtl818x_iowrite16(priv, &priv->map->BEACON_INTERVAL_TIME, 100); + return 0; +} + +static int rtl8187_config_interface(struct ieee80211_hw *dev, int if_id, + struct ieee80211_if_conf *conf) +{ + struct rtl8187_priv *priv = dev->priv; + int i; + + for (i = 0; i < ETH_ALEN; i++) + rtl818x_iowrite8(priv, &priv->map->BSSID[i], conf->bssid[i]); + + if (is_valid_ether_addr(conf->bssid)) + rtl818x_iowrite8(priv, &priv->map->MSR, RTL818X_MSR_INFRA); + else + rtl818x_iowrite8(priv, &priv->map->MSR, RTL818X_MSR_NO_LINK); + + return 0; +} + +static const struct ieee80211_ops rtl8187_ops = { + .tx = rtl8187_tx, + .open = rtl8187_open, + .stop = rtl8187_stop, + .add_interface = rtl8187_add_interface, + .remove_interface = rtl8187_remove_interface, + .config = rtl8187_config, + .config_interface = rtl8187_config_interface, +}; + +static void rtl8187_register_read(struct eeprom_93cx6 *eeprom) +{ + struct ieee80211_hw *dev = eeprom->data; + struct rtl8187_priv *priv = dev->priv; + u8 reg = rtl818x_ioread8(priv, &priv->map->EEPROM_CMD); + + eeprom->reg_data_in = reg & RTL818X_EEPROM_CMD_WRITE; + eeprom->reg_data_out = reg & RTL818X_EEPROM_CMD_READ; + eeprom->reg_data_clock = reg & RTL818X_EEPROM_CMD_CK; + eeprom->reg_chip_select = reg & RTL818X_EEPROM_CMD_CS; +} + +static void rtl8187_register_write(struct eeprom_93cx6 *eeprom) +{ + struct ieee80211_hw *dev = eeprom->data; + struct rtl8187_priv *priv = dev->priv; + u8 reg = 2 << 6; + + if (eeprom->reg_data_in) + reg |= RTL818X_EEPROM_CMD_WRITE; + if (eeprom->reg_data_out) + reg |= RTL818X_EEPROM_CMD_READ; + if (eeprom->reg_data_clock) + reg |= RTL818X_EEPROM_CMD_CK; + if (eeprom->reg_chip_select) + reg |= RTL818X_EEPROM_CMD_CS; + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, reg); + udelay(10); +} + +static int __devinit rtl8187_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct ieee80211_hw *dev; + struct rtl8187_priv *priv; + struct eeprom_93cx6 eeprom; + struct ieee80211_channel *channel; + u16 txpwr, reg; + int err, i; + + dev = ieee80211_alloc_hw(sizeof(*priv), &rtl8187_ops); + if (!dev) { + printk(KERN_ERR "rtl8187: ieee80211 alloc failed\n"); + return -ENOMEM; + } + + priv = dev->priv; + + SET_IEEE80211_DEV(dev, &intf->dev); + usb_set_intfdata(intf, dev); + priv->udev = udev; + + usb_get_dev(udev); + + skb_queue_head_init(&priv->rx_queue); + memcpy(priv->channels, rtl818x_channels, sizeof(rtl818x_channels)); + memcpy(priv->rates, rtl818x_rates, sizeof(rtl818x_rates)); + priv->map = (struct rtl818x_csr __iomem *) 0xFF00; + priv->modes[0].mode = MODE_IEEE80211G; + priv->modes[0].num_rates = ARRAY_SIZE(rtl818x_rates); + priv->modes[0].rates = priv->rates; + priv->modes[0].num_channels = ARRAY_SIZE(rtl818x_channels); + priv->modes[0].channels = priv->channels; + priv->modes[1].mode = MODE_IEEE80211B; + priv->modes[1].num_rates = 4; + priv->modes[1].rates = priv->rates; + priv->modes[1].num_channels = ARRAY_SIZE(rtl818x_channels); + priv->modes[1].channels = priv->channels; + priv->mode = IEEE80211_IF_TYPE_MGMT; + dev->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | + IEEE80211_HW_RX_INCLUDES_FCS | + IEEE80211_HW_WEP_INCLUDE_IV | + IEEE80211_HW_DATA_NULLFUNC_ACK; + dev->extra_tx_headroom = sizeof(struct rtl8187_tx_hdr); + dev->queues = 1; + dev->max_rssi = 65; + dev->max_signal = 64; + + for (i = 0; i < 2; i++) + if ((err = ieee80211_register_hwmode(dev, &priv->modes[i]))) + goto err_free_dev; + + eeprom.data = dev; + eeprom.register_read = rtl8187_register_read; + eeprom.register_write = rtl8187_register_write; + if (rtl818x_ioread32(priv, &priv->map->RX_CONF) & (1 << 6)) + eeprom.width = PCI_EEPROM_WIDTH_93C66; + else + eeprom.width = PCI_EEPROM_WIDTH_93C46; + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + udelay(10); + + eeprom_93cx6_multiread(&eeprom, 0x7, dev->wiphy->perm_addr, 3); + if (!is_valid_ether_addr(dev->wiphy->perm_addr)) { + printk(KERN_WARNING "rtl8187: Invalid hwaddr! Using randomly generated MAC addr\n"); + random_ether_addr(dev->wiphy->perm_addr); + } + + channel = priv->channels; + for (i = 0; i < 3; i++) { + eeprom_93cx6_read(&eeprom, 0x16 + i, &txpwr); + (*channel++).val = txpwr & 0xFF; + (*channel++).val = txpwr >> 8; + } + for (i = 0; i < 2; i++) { + eeprom_93cx6_read(&eeprom, 0x3D + i, &txpwr); + (*channel++).val = txpwr & 0xFF; + (*channel++).val = txpwr >> 8; + } + for (i = 0; i < 2; i++) { + eeprom_93cx6_read(&eeprom, 0x1B + i, &txpwr); + (*channel++).val = txpwr & 0xFF; + (*channel++).val = txpwr >> 8; + } + + eeprom_93cx6_read(&eeprom, 0x05, &priv->txpwr_base); + + reg = rtl818x_ioread16(priv, &priv->map->PGSELECT) & ~1; + rtl818x_iowrite16(priv, &priv->map->PGSELECT, reg | 1); + /* 0 means asic B-cut, we should use SW 3 wire + * bit-by-bit banging for radio. 1 means we can use + * USB specific request to write radio registers */ + priv->asic_rev = rtl818x_ioread8(priv, (u8*)0xFFFE) & 0x3; + rtl818x_iowrite16(priv, &priv->map->PGSELECT, reg); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); + + rtl8225_write(dev, 0, 0x1B7); + + if (rtl8225_read(dev, 8) != 0x588 || + rtl8225_read(dev, 9) != 0x700) + priv->rf_init = rtl8225_rf_init; + else + priv->rf_init = rtl8225z2_rf_init; + + rtl8225_write(dev, 0, 0x0B7); + + err = ieee80211_register_hw(dev); + if (err) { + printk(KERN_ERR "rtl8187: Cannot register device\n"); + goto err_free_dev; + } + + printk(KERN_INFO "%s: hwaddr " MAC_FMT ", rtl8187 V%d + %s\n", + wiphy_name(dev->wiphy), MAC_ARG(dev->wiphy->perm_addr), + priv->asic_rev, priv->rf_init == rtl8225_rf_init ? + "rtl8225" : "rtl8225z2"); + + return 0; + + err_free_dev: + ieee80211_free_hw(dev); + usb_set_intfdata(intf, NULL); + usb_put_dev(udev); + return err; +} + +static void __devexit rtl8187_disconnect(struct usb_interface *intf) +{ + struct ieee80211_hw *dev = usb_get_intfdata(intf); + struct rtl8187_priv *priv; + + if (!dev) + return; + + ieee80211_unregister_hw(dev); + + priv = dev->priv; + usb_put_dev(interface_to_usbdev(intf)); + ieee80211_free_hw(dev); +} + +static struct usb_driver rtl8187_driver = { + .name = KBUILD_MODNAME, + .id_table = rtl8187_table, + .probe = rtl8187_probe, + .disconnect = rtl8187_disconnect, +}; + +static int __init rtl8187_init(void) +{ + return usb_register(&rtl8187_driver); +} + +static void __exit rtl8187_exit(void) +{ + usb_deregister(&rtl8187_driver); +} + +module_init(rtl8187_init); +module_exit(rtl8187_exit); diff --git a/drivers/net/wireless/mac80211/rtl818x/rtl8187_rtl8225.c b/drivers/net/wireless/mac80211/rtl818x/rtl8187_rtl8225.c new file mode 100644 index 0000000..a8d86ec --- /dev/null +++ b/drivers/net/wireless/mac80211/rtl818x/rtl8187_rtl8225.c @@ -0,0 +1,747 @@ + +/* + * Radio tuning for RTL8225 on RTL8187 + * + * Copyright 2007 Michael Wu + * Copyright 2007 Andrea Merello + * + * Based on the r8187 driver, which is: + * Copyright 2005 Andrea Merello , et al. + * + * Thanks to Realtek for their support! + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include "rtl8187.h" +#include "rtl8187_rtl8225.h" + +void rtl8225_write_bitbang(struct ieee80211_hw *dev, u8 addr, u16 data) +{ + struct rtl8187_priv *priv = dev->priv; + u16 reg80, reg84, reg82; + u32 bangdata; + int i; + + bangdata = (data << 4) | (addr & 0xf); + + reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput) & 0xfff3; + reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable); + + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x7); + + reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x7); + udelay(10); + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); + udelay(2); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80); + udelay(10); + + for(i = 15; i >= 0; i--){ + u16 reg = reg80 | (bangdata & (1 << i)) >> i; + + if (i & 1) + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1)); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1)); + + if (!(i & 1)) + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); + } + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); + udelay(10); + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84); + mdelay(2); +} + +void rtl8225_write_8051(struct ieee80211_hw *dev, u8 addr, u16 data) +{ + struct rtl8187_priv *priv = dev->priv; + u16 reg80, reg82, reg84; + + reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput); + reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable); + reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect); + + reg80 &= ~(0x3 << 2); + reg84 &= ~0xF; + + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x0007); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x0007); + udelay(10); + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); + udelay(2); + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80); + udelay(10); + + usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0), + RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE, + addr, 0x8225, &data, sizeof(data), HZ / 2); + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); + udelay(10); + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84); + mdelay(2); +} + +void rtl8225_write(struct ieee80211_hw *dev, u8 addr, u16 data) +{ + struct rtl8187_priv *priv = dev->priv; + + if(priv->asic_rev) + rtl8225_write_8051(dev, addr, data); + else + rtl8225_write_bitbang(dev, addr, data); +} + +u16 rtl8225_read(struct ieee80211_hw *dev, u8 addr) +{ + struct rtl8187_priv *priv = dev->priv; + u16 reg80, reg82, reg84, out; + int i; + + reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput); + reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable); + reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect); + + reg80 &= ~0xF; + + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x000F); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x000F); + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2)); + udelay(4); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80); + udelay(5); + + for (i = 4; i >= 0; i--) { + u16 reg = reg80 | ((addr >> i) & 1); + + if (!(i & 1)) { + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); + udelay(1); + } + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg | (1 << 1)); + udelay(2); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg | (1 << 1)); + udelay(2); + + if (i & 1) { + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg); + udelay(1); + } + } + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3) | (1 << 1)); + udelay(2); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3)); + udelay(2); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3)); + udelay(2); + + out = 0; + for (i = 11; i >= 0; i--) { + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3)); + udelay(1); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3) | (1 << 1)); + udelay(2); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3) | (1 << 1)); + udelay(2); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3) | (1 << 1)); + udelay(2); + + if (rtl818x_ioread16(priv, &priv->map->RFPinsInput) & (1 << 1)) + out |= 1 << i; + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3)); + udelay(2); + } + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, + reg80 | (1 << 3) | (1 << 2)); + udelay(2); + + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84); + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x03A0); + + return out; +} + +static const u16 rtl8225bcd_rxgain[] = { + 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409, + 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541, + 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583, + 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644, + 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688, + 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745, + 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789, + 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793, + 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d, + 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9, + 0x07aa, 0x07ab, 0x07ac, 0x07ad, 0x07b0, 0x07b1, 0x07b2, 0x07b3, + 0x07b4, 0x07b5, 0x07b8, 0x07b9, 0x07ba, 0x07bb, 0x07bb +}; + +static const u8 rtl8225_agc[] = { + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9d, 0x9c, 0x9b, 0x9a, 0x99, 0x98, 0x97, 0x96, + 0x95, 0x94, 0x93, 0x92, 0x91, 0x90, 0x8f, 0x8e, + 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88, 0x87, 0x86, + 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, 0x3f, 0x3e, + 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36, + 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x2f, 0x2e, + 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, + 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x1f, 0x1e, + 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, + 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, + 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, + 0x05, 0x04, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 +}; + +static const u8 rtl8225_gain[] = { + 0x23, 0x88, 0x7c, 0xa5,// -82dbm + 0x23, 0x88, 0x7c, 0xb5,// -82dbm + 0x23, 0x88, 0x7c, 0xc5,// -82dbm + 0x33, 0x80, 0x79, 0xc5,// -78dbm + 0x43, 0x78, 0x76, 0xc5,// -74dbm + 0x53, 0x60, 0x73, 0xc5,// -70dbm + 0x63, 0x58, 0x70, 0xc5,// -66dbm +}; + +static const u8 rtl8225_threshold[] = { + 0x8d, 0x8d, 0x8d, 0x8d, 0x9d, 0xad, 0xbd +}; + +static const u8 rtl8225_tx_gain_cck_ofdm[] = { + 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0x7e +}; + +static const u8 rtl8225_tx_power_cck[] = { + 0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02, + 0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02, + 0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02, + 0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02, + 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03, + 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03 +}; + +static const u8 rtl8225_tx_power_cck_ch14[] = { + 0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00, + 0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00, + 0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00 +}; + +static const u8 rtl8225_tx_power_ofdm[] = { + 0x80, 0x90, 0xa2, 0xb5, 0xcb, 0xe4 +}; + +static const u32 rtl8225_chan[] = { + 0x085c, 0x08dc, 0x095c, 0x09dc, 0x0a5c, 0x0adc, 0x0b5c, + 0x0bdc, 0x0c5c, 0x0cdc, 0x0d5c, 0x0ddc, 0x0e5c, 0x0f72 +}; + +static void rtl8225_rf_set_tx_power(struct ieee80211_hw *dev, int channel) +{ + struct rtl8187_priv *priv = dev->priv; + u8 cck_power, ofdm_power; + const u8 *tmp; + u32 reg; + int i; + + cck_power = priv->channels[channel - 1].val & 0xF; + ofdm_power = priv->channels[channel - 1].val >> 4; + + cck_power = min(cck_power, (u8)11); + ofdm_power = min(ofdm_power, (u8)35); + + rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, + rtl8225_tx_gain_cck_ofdm[cck_power / 6] >> 1); + + if (channel == 14) + tmp = &rtl8225_tx_power_cck_ch14[(cck_power % 6) * 8]; + else + tmp = &rtl8225_tx_power_cck[(cck_power % 6) * 8]; + + for (i = 0; i < 8; i++) + rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++); + + mdelay(1); // FIXME: optional? + + // anaparam2 on + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | RTL818X_CONFIG3_ANAPARAM_WRITE); + rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, RTL8225_ANAPARAM2_ON); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); + + rtl8225_write_phy_ofdm(dev, 2, 0x42); + rtl8225_write_phy_ofdm(dev, 6, 0x00); + rtl8225_write_phy_ofdm(dev, 8, 0x00); + + rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, + rtl8225_tx_gain_cck_ofdm[ofdm_power/6] >> 1); + + tmp = &rtl8225_tx_power_ofdm[ofdm_power % 6]; + + rtl8225_write_phy_ofdm(dev, 5, *tmp); + rtl8225_write_phy_ofdm(dev, 7, *tmp); + + mdelay(1); +} + +void rtl8225_rf_init(struct ieee80211_hw *dev) +{ + struct rtl8187_priv *priv = dev->priv; + int i; + + rtl8225_write(dev, 0x0, 0x067); mdelay(1); + rtl8225_write(dev, 0x1, 0xFE0); mdelay(1); + rtl8225_write(dev, 0x2, 0x44D); mdelay(1); + rtl8225_write(dev, 0x3, 0x441); mdelay(1); + rtl8225_write(dev, 0x4, 0x486); mdelay(1); + rtl8225_write(dev, 0x5, 0xBC0); mdelay(1); + rtl8225_write(dev, 0x6, 0xAE6); mdelay(1); + rtl8225_write(dev, 0x7, 0x82A); mdelay(1); + rtl8225_write(dev, 0x8, 0x01F); mdelay(1); + rtl8225_write(dev, 0x9, 0x334); mdelay(1); + rtl8225_write(dev, 0xA, 0xFD4); mdelay(1); + rtl8225_write(dev, 0xB, 0x391); mdelay(1); + rtl8225_write(dev, 0xC, 0x050); mdelay(1); + rtl8225_write(dev, 0xD, 0x6DB); mdelay(1); + rtl8225_write(dev, 0xE, 0x029); mdelay(1); + rtl8225_write(dev, 0xF, 0x914); mdelay(100); + + rtl8225_write(dev, 0x2, 0xC4D); mdelay(200); + rtl8225_write(dev, 0x2, 0x44D); mdelay(200); + + if (!(rtl8225_read(dev, 6) & (1 << 7))){ + rtl8225_write(dev, 0x02, 0x0c4d); + mdelay(200); + rtl8225_write(dev, 0x02, 0x044d); + mdelay(100); + if (!(rtl8225_read(dev, 6) & (1 << 7))) + printk("RF Calibration Failed!!!! %x\n", + rtl8225_read(dev, 6)); + } + + rtl8225_write(dev, 0x0, 0x127); + + for (i = 0; i < ARRAY_SIZE(rtl8225bcd_rxgain); i++) { + rtl8225_write(dev, 0x1, i + 1); + rtl8225_write(dev, 0x2, rtl8225bcd_rxgain[i]); + } + + rtl8225_write(dev, 0x0, 0x027); + rtl8225_write(dev, 0x0, 0x22F); + + for (i = 0; i < ARRAY_SIZE(rtl8225_agc); i++) { + rtl8225_write_phy_ofdm(dev, 0xB, rtl8225_agc[i]); + mdelay(1); + rtl8225_write_phy_ofdm(dev, 0xA, 0x80 + i); + mdelay(1); + } + + mdelay(1); + + rtl8225_write_phy_ofdm(dev, 0x00, 0x01); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x01, 0x02); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x02, 0x42); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x03, 0x00); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x04, 0x00); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x05, 0x00); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x06, 0x40); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x07, 0x00); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x08, 0x40); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x09, 0xfe); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x0a, 0x09); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x0b, 0x80); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x0c, 0x01); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x0e, 0xd3); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x0f, 0x38); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x10, 0x84); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x11, 0x06); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x12, 0x20); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x13, 0x20); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x14, 0x00); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x15, 0x40); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x16, 0x00); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x17, 0x40); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x18, 0xef); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x19, 0x19); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x1a, 0x20); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x1b, 0x76); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x1c, 0x04); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x1e, 0x95); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x1f, 0x75); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x20, 0x1f); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x21, 0x27); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x22, 0x16); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x24, 0x46); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x25, 0x20); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x26, 0x90); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x27, 0x88); mdelay(1); + + rtl8225_write_phy_ofdm(dev, 0x0d, rtl8225_gain[2 * 4]); + rtl8225_write_phy_ofdm(dev, 0x1b, rtl8225_gain[2 * 4 + 2]); + rtl8225_write_phy_ofdm(dev, 0x1d, rtl8225_gain[2 * 4 + 3]); + rtl8225_write_phy_ofdm(dev, 0x23, rtl8225_gain[2 * 4 + 1]); + + rtl8225_write_phy_cck(dev, 0x00, 0x98); mdelay(1); + rtl8225_write_phy_cck(dev, 0x03, 0x20); mdelay(1); + rtl8225_write_phy_cck(dev, 0x04, 0x7e); mdelay(1); + rtl8225_write_phy_cck(dev, 0x05, 0x12); mdelay(1); + rtl8225_write_phy_cck(dev, 0x06, 0xfc); mdelay(1); + rtl8225_write_phy_cck(dev, 0x07, 0x78); mdelay(1); + rtl8225_write_phy_cck(dev, 0x08, 0x2e); mdelay(1); + rtl8225_write_phy_cck(dev, 0x10, 0x9b); mdelay(1); + rtl8225_write_phy_cck(dev, 0x11, 0x88); mdelay(1); + rtl8225_write_phy_cck(dev, 0x12, 0x47); mdelay(1); + rtl8225_write_phy_cck(dev, 0x13, 0xd0); + rtl8225_write_phy_cck(dev, 0x19, 0x00); + rtl8225_write_phy_cck(dev, 0x1a, 0xa0); + rtl8225_write_phy_cck(dev, 0x1b, 0x08); + rtl8225_write_phy_cck(dev, 0x40, 0x86); + rtl8225_write_phy_cck(dev, 0x41, 0x8d); mdelay(1); + rtl8225_write_phy_cck(dev, 0x42, 0x15); mdelay(1); + rtl8225_write_phy_cck(dev, 0x43, 0x18); mdelay(1); + rtl8225_write_phy_cck(dev, 0x44, 0x1f); mdelay(1); + rtl8225_write_phy_cck(dev, 0x45, 0x1e); mdelay(1); + rtl8225_write_phy_cck(dev, 0x46, 0x1a); mdelay(1); + rtl8225_write_phy_cck(dev, 0x47, 0x15); mdelay(1); + rtl8225_write_phy_cck(dev, 0x48, 0x10); mdelay(1); + rtl8225_write_phy_cck(dev, 0x49, 0x0a); mdelay(1); + rtl8225_write_phy_cck(dev, 0x4a, 0x05); mdelay(1); + rtl8225_write_phy_cck(dev, 0x4b, 0x02); mdelay(1); + rtl8225_write_phy_cck(dev, 0x4c, 0x05); mdelay(1); + + rtl818x_iowrite8(priv, &priv->map->TESTR, 0x0D); + + rtl8225_rf_set_tx_power(dev, 1); + + // RX antenna default to A + rtl8225_write_phy_cck(dev, 0x10, 0x9b); mdelay(1); // B: 0xDB + rtl8225_write_phy_ofdm(dev, 0x26, 0x90); mdelay(1); // B: 0x10 + + rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); // B: 0x00 + mdelay(1); + rtl818x_iowrite32(priv, (__le32 *)0xFF94, 0x3dc00002); + + // set sensitivity + rtl8225_write(dev, 0x0c, 0x50); + rtl8225_write_phy_ofdm(dev, 0x0d, rtl8225_gain[2 * 4]); + rtl8225_write_phy_ofdm(dev, 0x1b, rtl8225_gain[2 * 4 + 2]); + rtl8225_write_phy_ofdm(dev, 0x1d, rtl8225_gain[2 * 4 + 3]); + rtl8225_write_phy_ofdm(dev, 0x23, rtl8225_gain[2 * 4 + 1]); + rtl8225_write_phy_cck(dev, 0x41, rtl8225_threshold[2]); +} + + +static const u8 rtl8225z2_tx_power_cck_ch14[] = { + 0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00 +}; + +static const u8 rtl8225z2_tx_power_cck[] = { + 0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04 +}; + +static const u8 rtl8225z2_tx_power_ofdm[] = { + 0x42, 0x00, 0x40, 0x00, 0x40 +}; + +static const u8 rtl8225z2_tx_gain_cck_ofdm[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, + 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23 +}; + +static void rtl8225z2_rf_set_tx_power(struct ieee80211_hw *dev, int channel) +{ + struct rtl8187_priv *priv = dev->priv; + u8 cck_power, ofdm_power; + const u8 *tmp; + u32 reg; + int i; + + cck_power = priv->channels[channel - 1].val & 0xF; + ofdm_power = priv->channels[channel - 1].val >> 4; + + cck_power = min(cck_power, (u8)15); + cck_power += priv->txpwr_base & 0xF; + cck_power = min(cck_power, (u8)35); + + ofdm_power = min(ofdm_power, (u8)15); + ofdm_power += priv->txpwr_base >> 4; + ofdm_power = min(ofdm_power, (u8)35); + + if (channel == 14) + tmp = rtl8225z2_tx_power_cck_ch14; + else + tmp = rtl8225z2_tx_power_cck; + + for (i = 0; i < 8; i++) + rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++); + + rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, + rtl8225z2_tx_gain_cck_ofdm[cck_power]); + mdelay(1); + + // anaparam2 on + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | RTL818X_CONFIG3_ANAPARAM_WRITE); + rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, RTL8225_ANAPARAM2_ON); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); + + rtl8225_write_phy_ofdm(dev, 2, 0x42); + rtl8225_write_phy_ofdm(dev, 5, 0x00); + rtl8225_write_phy_ofdm(dev, 6, 0x40); + rtl8225_write_phy_ofdm(dev, 7, 0x00); + rtl8225_write_phy_ofdm(dev, 8, 0x40); + + rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, + rtl8225z2_tx_gain_cck_ofdm[ofdm_power]); + mdelay(1); +} + +static const u16 rtl8225z2_rxgain[] = { + 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409, + 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541, + 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583, + 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644, + 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688, + 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745, + 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789, + 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793, + 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d, + 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9, + 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03b0, 0x03b1, 0x03b2, 0x03b3, + 0x03b4, 0x03b5, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bb +}; + +static const u8 rtl8225z2_gain_bg[] = { + 0x23, 0x15, 0xa5, // -82-1dBm + 0x23, 0x15, 0xb5, // -82-2dBm + 0x23, 0x15, 0xc5, // -82-3dBm + 0x33, 0x15, 0xc5, // -78dBm + 0x43, 0x15, 0xc5, // -74dBm + 0x53, 0x15, 0xc5, // -70dBm + 0x63, 0x15, 0xc5 // -66dBm +}; + +void rtl8225z2_rf_init(struct ieee80211_hw *dev) +{ + struct rtl8187_priv *priv = dev->priv; + int i; + + rtl8225_write(dev, 0x0, 0x2BF); mdelay(1); + rtl8225_write(dev, 0x1, 0xEE0); mdelay(1); + rtl8225_write(dev, 0x2, 0x44D); mdelay(1); + rtl8225_write(dev, 0x3, 0x441); mdelay(1); + rtl8225_write(dev, 0x4, 0x8C3); mdelay(1); + rtl8225_write(dev, 0x5, 0xC72); mdelay(1); + rtl8225_write(dev, 0x6, 0x0E6); mdelay(1); + rtl8225_write(dev, 0x7, 0x82A); mdelay(1); + rtl8225_write(dev, 0x8, 0x03F); mdelay(1); + rtl8225_write(dev, 0x9, 0x335); mdelay(1); + rtl8225_write(dev, 0xa, 0x9D4); mdelay(1); + rtl8225_write(dev, 0xb, 0x7BB); mdelay(1); + rtl8225_write(dev, 0xc, 0x850); mdelay(1); + rtl8225_write(dev, 0xd, 0xCDF); mdelay(1); + rtl8225_write(dev, 0xe, 0x02B); mdelay(1); + rtl8225_write(dev, 0xf, 0x114); mdelay(100); + + rtl8225_write(dev, 0x0, 0x1B7); + + for (i = 0; i < ARRAY_SIZE(rtl8225z2_rxgain); i++) { + rtl8225_write(dev, 0x1, i + 1); + rtl8225_write(dev, 0x2, rtl8225z2_rxgain[i]); + } + + rtl8225_write(dev, 0x3, 0x080); + rtl8225_write(dev, 0x5, 0x004); + rtl8225_write(dev, 0x0, 0x0B7); + rtl8225_write(dev, 0x2, 0xc4D); + + mdelay(200); + rtl8225_write(dev, 0x2, 0x44D); + mdelay(100); + + if (!(rtl8225_read(dev, 6) & (1 << 7))) { + rtl8225_write(dev, 0x02, 0x0C4D); + mdelay(200); + rtl8225_write(dev, 0x02, 0x044D); + mdelay(100); + if (!(rtl8225_read(dev, 6) & (1 << 7))) + printk("RF Calibration Failed!!!! %x\n", + rtl8225_read(dev, 6)); + } + + mdelay(200); + + rtl8225_write(dev, 0x0, 0x2BF); + + for (i = 0; i < ARRAY_SIZE(rtl8225_agc); i++) { + rtl8225_write_phy_ofdm(dev, 0xB, rtl8225_agc[i]); + mdelay(1); + rtl8225_write_phy_ofdm(dev, 0xA, 0x80 + i); + mdelay(1); + } + + mdelay(1); + + rtl8225_write_phy_ofdm(dev, 0x00, 0x01); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x01, 0x02); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x02, 0x42); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x03, 0x00); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x04, 0x00); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x05, 0x00); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x06, 0x40); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x07, 0x00); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x08, 0x40); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x09, 0xfe); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x0a, 0x08); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x0b, 0x80); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x0c, 0x01); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x0d, 0x43); + rtl8225_write_phy_ofdm(dev, 0x0e, 0xd3); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x0f, 0x38); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x10, 0x84); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x11, 0x07); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x12, 0x20); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x13, 0x20); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x14, 0x00); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x15, 0x40); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x16, 0x00); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x17, 0x40); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x18, 0xef); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x19, 0x19); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x1a, 0x20); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x1b, 0x15); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x1c, 0x04); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x1d, 0xc5); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x1e, 0x95); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x1f, 0x75); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x20, 0x1f); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x21, 0x17); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x22, 0x16); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x23, 0x80); mdelay(1); //FIXME: not needed? + rtl8225_write_phy_ofdm(dev, 0x24, 0x46); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x25, 0x00); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x26, 0x90); mdelay(1); + rtl8225_write_phy_ofdm(dev, 0x27, 0x88); mdelay(1); + + rtl8225_write_phy_ofdm(dev, 0x0b, rtl8225z2_gain_bg[4 * 3]); + rtl8225_write_phy_ofdm(dev, 0x1b, rtl8225z2_gain_bg[4 * 3 + 1]); + rtl8225_write_phy_ofdm(dev, 0x1d, rtl8225z2_gain_bg[4 * 3 + 2]); + rtl8225_write_phy_ofdm(dev, 0x21, 0x37); + + rtl8225_write_phy_cck(dev, 0x00, 0x98); mdelay(1); + rtl8225_write_phy_cck(dev, 0x03, 0x20); mdelay(1); + rtl8225_write_phy_cck(dev, 0x04, 0x7e); mdelay(1); + rtl8225_write_phy_cck(dev, 0x05, 0x12); mdelay(1); + rtl8225_write_phy_cck(dev, 0x06, 0xfc); mdelay(1); + rtl8225_write_phy_cck(dev, 0x07, 0x78); mdelay(1); + rtl8225_write_phy_cck(dev, 0x08, 0x2e); mdelay(1); + rtl8225_write_phy_cck(dev, 0x10, 0x9b); mdelay(1); + rtl8225_write_phy_cck(dev, 0x11, 0x88); mdelay(1); + rtl8225_write_phy_cck(dev, 0x12, 0x47); mdelay(1); + rtl8225_write_phy_cck(dev, 0x13, 0xd0); + rtl8225_write_phy_cck(dev, 0x19, 0x00); + rtl8225_write_phy_cck(dev, 0x1a, 0xa0); + rtl8225_write_phy_cck(dev, 0x1b, 0x08); + rtl8225_write_phy_cck(dev, 0x40, 0x86); + rtl8225_write_phy_cck(dev, 0x41, 0x8d); mdelay(1); + rtl8225_write_phy_cck(dev, 0x42, 0x15); mdelay(1); + rtl8225_write_phy_cck(dev, 0x43, 0x18); mdelay(1); + rtl8225_write_phy_cck(dev, 0x44, 0x36); mdelay(1); + rtl8225_write_phy_cck(dev, 0x45, 0x35); mdelay(1); + rtl8225_write_phy_cck(dev, 0x46, 0x2e); mdelay(1); + rtl8225_write_phy_cck(dev, 0x47, 0x25); mdelay(1); + rtl8225_write_phy_cck(dev, 0x48, 0x1c); mdelay(1); + rtl8225_write_phy_cck(dev, 0x49, 0x12); mdelay(1); + rtl8225_write_phy_cck(dev, 0x4a, 0x09); mdelay(1); + rtl8225_write_phy_cck(dev, 0x4b, 0x04); mdelay(1); + rtl8225_write_phy_cck(dev, 0x4c, 0x05); mdelay(1); + + rtl818x_iowrite8(priv, (u8 *)0xFF5B, 0x0D); mdelay(1); + + rtl8225z2_rf_set_tx_power(dev, 1); + + // RX antenna default to A + rtl8225_write_phy_cck(dev, 0x10, 0x9b); mdelay(1); // B: 0xDB + rtl8225_write_phy_ofdm(dev, 0x26, 0x90); mdelay(1); // B: 0x10 + + rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); // B: 0x00 + mdelay(1); + rtl818x_iowrite32(priv, (__le32 *)0xFF94, 0x3dc00002); +} + +void rtl8225_rf_stop(struct ieee80211_hw *dev) +{ + u8 reg; + struct rtl8187_priv *priv = dev->priv; + + rtl8225_write(dev, 0x4, 0x1f); mdelay(1); + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); + reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | RTL818X_CONFIG3_ANAPARAM_WRITE); + rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, RTL8225_ANAPARAM2_OFF); + rtl818x_iowrite32(priv, &priv->map->ANAPARAM, RTL8225_ANAPARAM_OFF); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE); + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); +} + +void rtl8225_rf_set_channel(struct ieee80211_hw *dev, int channel) +{ + struct rtl8187_priv *priv = dev->priv; + + if (priv->rf_init == rtl8225_rf_init) + rtl8225_rf_set_tx_power(dev, channel); + else + rtl8225z2_rf_set_tx_power(dev, channel); + + rtl8225_write(dev, 0x7, rtl8225_chan[channel - 1]); + mdelay(10); +} + + diff --git a/drivers/net/wireless/mac80211/rtl818x/rtl8187_rtl8225.h b/drivers/net/wireless/mac80211/rtl818x/rtl8187_rtl8225.h new file mode 100644 index 0000000..ed28118 --- /dev/null +++ b/drivers/net/wireless/mac80211/rtl818x/rtl8187_rtl8225.h @@ -0,0 +1,30 @@ +#ifndef RTL8187_RTL8225_H +#define RTL8187_RTL8225_H + +#define RTL8225_ANAPARAM_ON 0xa0000a59 +#define RTL8225_ANAPARAM2_ON 0x860c7312 +#define RTL8225_ANAPARAM_OFF 0xa00beb59 +#define RTL8225_ANAPARAM2_OFF 0x840dec11 + +void rtl8225_write(struct ieee80211_hw *, u8 addr, u16 data); +u16 rtl8225_read(struct ieee80211_hw *, u8 addr); + +void rtl8225_rf_init(struct ieee80211_hw *); +void rtl8225z2_rf_init(struct ieee80211_hw *); +void rtl8225_rf_stop(struct ieee80211_hw *); +void rtl8225_rf_set_channel(struct ieee80211_hw *, int); + + +static inline void rtl8225_write_phy_ofdm(struct ieee80211_hw *dev, + u8 addr, u32 data) +{ + rtl8187_write_phy(dev, addr, data); +} + +static inline void rtl8225_write_phy_cck(struct ieee80211_hw *dev, + u8 addr, u32 data) +{ + rtl8187_write_phy(dev, addr, data | 0x10000); +} + +#endif /* RTL8187_RTL8225_H */ diff --git a/drivers/net/wireless/mac80211/rtl818x/rtl818x.h b/drivers/net/wireless/mac80211/rtl818x/rtl818x.h new file mode 100644 index 0000000..ff20d68 --- /dev/null +++ b/drivers/net/wireless/mac80211/rtl818x/rtl818x.h @@ -0,0 +1,180 @@ +#ifndef RTL818X_H +#define RTL818X_H + +struct rtl818x_csr { + u8 MAC[6]; + u8 reserved_0[10]; + u8 RX_FIFO_COUNT; + u8 reserved_1; + u8 TX_FIFO_COUNT; + u8 BQREQ; + u8 reserved_2[24]; + __le16 BRSR; + u8 BSSID[6]; + u8 RESP_RATE; + u8 EIFS; + u8 reserved_3[1]; + u8 CMD; +#define RTL818X_CMD_TX_ENABLE (1 << 2) +#define RTL818X_CMD_RX_ENABLE (1 << 3) +#define RTL818X_CMD_RESET (1 << 4) + u8 reserved_4[4]; + __le16 INTA_MASK; + u8 reserved_5[2]; + __le32 TX_CONF; +#define RTL818X_TX_CONF_LOOPBACK_MAC (1 << 17) +#define RTL818X_TX_CONF_NO_ICV (1 << 19) +#define RTL818X_TX_CONF_DISCW (1 << 20) +#define RTL818X_TX_CONF_CW_MIN (1 << 31) + __le32 RX_CONF; +#define RTL818X_RX_CONF_MONITOR (1 << 0) +#define RTL818X_RX_CONF_NICMAC (1 << 1) +#define RTL818X_RX_CONF_MULTICAST (1 << 2) +#define RTL818X_RX_CONF_BROADCAST (1 << 3) +#define RTL818X_RX_CONF_DATA (1 << 18) +#define RTL818X_RX_CONF_CTRL (1 << 19) +#define RTL818X_RX_CONF_MGMT (1 << 20) +#define RTL818X_RX_CONF_BSSID (1 << 23) +#define RTL818X_RX_CONF_RX_AUTORESETPHY (1 << 28) +#define RTL818X_RX_CONF_ONLYERLPKT (1 << 31) + __le32 INT_TIMEOUT; + u8 reserved_6[4]; + u8 EEPROM_CMD; +#define RTL818X_EEPROM_CMD_READ (1 << 0) +#define RTL818X_EEPROM_CMD_WRITE (1 << 1) +#define RTL818X_EEPROM_CMD_CK (1 << 2) +#define RTL818X_EEPROM_CMD_CS (1 << 3) +#define RTL818X_EEPROM_CMD_NORMAL (0 << 6) +#define RTL818X_EEPROM_CMD_LOAD (1 << 6) +#define RTL818X_EEPROM_CMD_PROGRAM (2 << 6) +#define RTL818X_EEPROM_CMD_CONFIG (3 << 6) + u8 CONFIG0; + u8 CONFIG1; + u8 CONFIG2; + __le32 ANAPARAM; + u8 MSR; +#define RTL818X_MSR_NO_LINK (0 << 2) +#define RTL818X_MSR_ADHOC (1 << 2) +#define RTL818X_MSR_INFRA (2 << 2) + u8 CONFIG3; +#define RTL818X_CONFIG3_ANAPARAM_WRITE (1 << 6) + u8 CONFIG4; +#define RTL818X_CONFIG4_POWEROFF (1 << 6) +#define RTL818X_CONFIG4_VCOOFF (1 << 7) + u8 TESTR; + u8 reserved_9[2]; + __le16 PGSELECT; + __le32 ANAPARAM2; + u8 reserved_10[12]; + __le16 BEACON_INTERVAL; + __le16 ATIM_WND; + __le16 BEACON_INTERVAL_TIME; + __le16 ATIMTR_INTERVAL; + u8 reserved_11[4]; + u8 PHY[4]; + __le16 RFPinsOutput; + __le16 RFPinsEnable; + __le16 RFPinsSelect; + __le16 RFPinsInput; + __le32 RF_PARA; + __le32 RF_TIMING; + u8 GP_ENABLE; + u8 GPIO; + u8 reserved_12[10]; + u8 TX_AGC_CTL; +#define RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT (1 << 0) +#define RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT (1 << 1) +#define RTL818X_TX_AGC_CTL_FEEDBACK_ANT (1 << 2) + u8 TX_GAIN_CCK; + u8 TX_GAIN_OFDM; + u8 TX_ANTENNA; + u8 reserved_13[16]; + u8 WPA_CONF; + u8 reserved_14[3]; + u8 SIFS; + u8 DIFS; + u8 SLOT; + u8 reserved_15[5]; + u8 CW_CONF; +#define RTL818X_CW_CONF_PERPACKET_CW_SHIFT (1 << 0) +#define RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT (1 << 1) + u8 CW_VAL; + u8 RATE_FALLBACK; + u8 reserved_16[26]; + u8 TX_DMA_POLLING; + u8 reserved_17[32]; + u16 TALLY_CNT; + u8 TALLY_SEL; +} __attribute__ ((packed)); + +static const struct ieee80211_rate rtl818x_rates[] = { + { .rate = 10, + .val = 0, + .flags = IEEE80211_RATE_CCK }, + { .rate = 20, + .val = 1, + .flags = IEEE80211_RATE_CCK }, + { .rate = 55, + .val = 2, + .flags = IEEE80211_RATE_CCK }, + { .rate = 110, + .val = 3, + .flags = IEEE80211_RATE_CCK }, + { .rate = 60, + .val = 4, + .flags = IEEE80211_RATE_OFDM }, + { .rate = 90, + .val = 5, + .flags = IEEE80211_RATE_OFDM }, + { .rate = 120, + .val = 6, + .flags = IEEE80211_RATE_OFDM }, + { .rate = 180, + .val = 7, + .flags = IEEE80211_RATE_OFDM }, + { .rate = 240, + .val = 8, + .flags = IEEE80211_RATE_OFDM }, + { .rate = 360, + .val = 9, + .flags = IEEE80211_RATE_OFDM }, + { .rate = 480, + .val = 10, + .flags = IEEE80211_RATE_OFDM }, + { .rate = 540, + .val = 11, + .flags = IEEE80211_RATE_OFDM }, +}; + +static const struct ieee80211_channel rtl818x_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} +}; + +#endif /* RTL818X_H */ diff --git a/drivers/net/wireless/mac80211/zd1211rw/Kconfig b/drivers/net/wireless/mac80211/zd1211rw/Kconfig new file mode 100644 index 0000000..b3c64e0 --- /dev/null +++ b/drivers/net/wireless/mac80211/zd1211rw/Kconfig @@ -0,0 +1,19 @@ +config ZD1211RW_MAC80211 + tristate "ZyDAS ZD1211/ZD1211B USB support (mac80211 stack)" + depends on USB && MAC80211 && WLAN_80211 && EXPERIMENTAL + 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 + problems. + diff --git a/drivers/net/wireless/mac80211/zd1211rw/Makefile b/drivers/net/wireless/mac80211/zd1211rw/Makefile new file mode 100644 index 0000000..87ee798 --- /dev/null +++ b/drivers/net/wireless/mac80211/zd1211rw/Makefile @@ -0,0 +1,11 @@ +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.o zd_usb.o zd_util.o + +ifeq ($(CONFIG_ZD1211RW_MAC80211_DEBUG),y) +EXTRA_CFLAGS += -DDEBUG +endif + diff --git a/drivers/net/wireless/mac80211/zd1211rw/zd_chip.c b/drivers/net/wireless/mac80211/zd1211rw/zd_chip.c new file mode 100644 index 0000000..8dc8f88 --- /dev/null +++ b/drivers/net/wireless/mac80211/zd1211rw/zd_chip.c @@ -0,0 +1,1681 @@ +/* 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" +#include "zd_util.h" + +void zd_chip_init(struct zd_chip *chip, + struct ieee80211_hw *dev, + struct usb_interface *intf) +{ + memset(chip, 0, sizeof(*chip)); + mutex_init(&chip->mutex); + zd_usb_init(&chip->usb, dev, 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(const u8 *addr, char *buffer, size_t size) +{ + 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 ", + chip->is_zd1211b ? "b" : ""); + i += zd_usb_scnprint_id(&chip->usb, buffer+i, size-i); + i += scnprintf(buffer+i, size-i, " "); + i += scnprint_mac_oui(chip->e2p_mac, 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; +} + +static int _read_mac_addr(struct zd_chip *chip, u8 *mac_addr, + const zd_addr_t *addr) +{ + int r; + u32 parts[2]; + + r = zd_ioread32v_locked(chip, parts, (const zd_addr_t *)addr, 2); + if (r) { + dev_dbg_f(zd_chip_dev(chip), + "error: couldn't read e2p macs. Error number %d\n", r); + return r; + } + + mac_addr[0] = parts[0]; + mac_addr[1] = parts[0] >> 8; + mac_addr[2] = parts[0] >> 16; + mac_addr[3] = parts[0] >> 24; + mac_addr[4] = parts[1]; + mac_addr[5] = parts[1] >> 8; + + return 0; +} + +static int read_e2p_mac_addr(struct zd_chip *chip) +{ + static const zd_addr_t addr[2] = { E2P_MAC_ADDR_P1, E2P_MAC_ADDR_P2 }; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + return _read_mac_addr(chip, chip->e2p_mac, (const zd_addr_t *)addr); +} + +/* MAC address: if custom mac addresses are to to be used CR_MAC_ADDR_P1 and + * CR_MAC_ADDR_P2 must be overwritten + */ +void zd_get_e2p_mac_addr(struct zd_chip *chip, u8 *mac_addr) +{ + mutex_lock(&chip->mutex); + memcpy(mac_addr, chip->e2p_mac, ETH_ALEN); + mutex_unlock(&chip->mutex); +} + +static int read_mac_addr(struct zd_chip *chip, u8 *mac_addr) +{ + static const zd_addr_t addr[2] = { CR_MAC_ADDR_P1, CR_MAC_ADDR_P2 }; + return _read_mac_addr(chip, mac_addr, (const zd_addr_t *)addr); +} + +int zd_read_mac_addr(struct zd_chip *chip, u8 *mac_addr) +{ + int r; + + dev_dbg_f(zd_chip_dev(chip), "\n"); + mutex_lock(&chip->mutex); + r = read_mac_addr(chip, mac_addr); + mutex_unlock(&chip->mutex); + return r; +} + +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 }, + }; + + 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 " MAC_FMT "\n", MAC_ARG(mac_addr)); + + mutex_lock(&chip->mutex); + r = zd_iowrite32a_locked(chip, reqs, ARRAY_SIZE(reqs)); +#ifdef DEBUG + { + u8 tmp[ETH_ALEN]; + read_mac_addr(chip, tmp); + } +#endif /* DEBUG */ + 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; + } + + dev_dbg_f(zd_chip_dev(chip), + "CR_REG1: 0x%02x -> 0x%02x\n", tmp, tmp & ~UNLOCK_PHY_REGS); + 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; + } + + dev_dbg_f(zd_chip_dev(chip), + "CR_REG1: 0x%02x -> 0x%02x\n", tmp, tmp | UNLOCK_PHY_REGS); + 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 chip->is_zd1211b ? 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 chip->is_zd1211b ? + 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]; + dev_dbg_f(zd_chip_dev(chip), "aw %u pt %u bi %u\n", + s->atim_wnd_period, s->pre_tbtt, s->beacon_interval); + 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; + + dev_dbg_f(zd_chip_dev(chip), + "aw %u pt %u bi %u\n", s->atim_wnd_period, s->pre_tbtt, + 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_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; +} + + +int zd_chip_init_hw(struct zd_chip *chip, u8 device_type) +{ + int r; + u8 rf_type; + + dev_dbg_f(zd_chip_dev(chip), "\n"); + + mutex_lock(&chip->mutex); + chip->is_zd1211b = (device_type == DEVICE_ZD1211B) != 0; + +#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_e2p_mac_addr(chip); + if (r) + goto out; + + 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]; + dev_dbg_f(zd_chip_dev(chip), "channel %d pwr_int %#04x\n", + channel, value); + 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]; + dev_dbg_f(zd_chip_dev(chip), "channel %d pwr_cal %#04x\n", + channel, value); + 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]; + + dev_dbg_f(zd_chip_dev(chip), + "channel %d ofdm_cal 36M %#04x 48M %#04x 54M %#04x\n", + channel, ioreqs[0].value, ioreqs[1].value, ioreqs[2].value); + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static int update_channel_integration_and_calibration(struct zd_chip *chip, + u8 channel) +{ + int r; + + r = update_pwr_int(chip, channel); + if (r) + return r; + if (chip->is_zd1211b) { + 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) + 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 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 (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 (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 rate, unsigned int size) +{ + int r; + + r = ofdm_qual_db(status_quality, 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; +} + +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_ofdm_plcp_header_rate(rx_frame), + size) : + cck_qual_percent(status->signal_quality_cck); +} + +u8 zd_rx_strength_percent(u8 rssi) +{ + int r = (rssi*100) / 41; + if (r > 100) + r = 100; + return (u8) r; +} + +u16 zd_rx_rate(const void *rx_frame, const struct rx_status *status) +{ + static const u16 ofdm_rates[] = { + [ZD_OFDM_RATE_6M] = 60, + [ZD_OFDM_RATE_9M] = 90, + [ZD_OFDM_RATE_12M] = 120, + [ZD_OFDM_RATE_18M] = 180, + [ZD_OFDM_RATE_24M] = 240, + [ZD_OFDM_RATE_36M] = 360, + [ZD_OFDM_RATE_48M] = 480, + [ZD_OFDM_RATE_54M] = 540, + }; + u16 rate; + if (status->frame_status & ZD_RX_OFDM) { + u8 ofdm_rate = zd_ofdm_plcp_header_rate(rx_frame); + rate = ofdm_rates[ofdm_rate & 0xf]; + } else { + u8 cck_rate = zd_cck_plcp_header_rate(rx_frame); + switch (cck_rate) { + case ZD_CCK_SIGNAL_1M: + rate = 10; + break; + case ZD_CCK_SIGNAL_2M: + rate = 20; + break; + case ZD_CCK_SIGNAL_5M5: + rate = 55; + break; + case ZD_CCK_SIGNAL_11M: + rate = 110; + break; + default: + rate = 0; + } + } + + return 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 }, + }; + + dev_dbg_f(zd_chip_dev(chip), "hash l 0x%08x h 0x%08x\n", + ioreqs[0].value, ioreqs[1].value); + return zd_iowrite32a(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} diff --git a/drivers/net/wireless/mac80211/zd1211rw/zd_chip.h b/drivers/net/wireless/mac80211/zd1211rw/zd_chip.h new file mode 100644 index 0000000..c884311 --- /dev/null +++ b/drivers/net/wireless/mac80211/zd1211rw/zd_chip.h @@ -0,0 +1,910 @@ +/* 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) + +#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_RESPONSE (1 << 1) +#define RX_FILTER_REASSOC_RESPONSE (1 << 3) +#define RX_FILTER_PROBE_RESPONSE (1 << 5) +#define RX_FILTER_BEACON (1 << 8) +#define RX_FILTER_DISASSOC (1 << 10) +#define RX_FILTER_AUTH (1 << 11) +#define RX_FILTER_ACK (1 << 29) +#define AP_RX_FILTER 0x0400feff +#define STA_RX_FILTER 0x2000ffff + +/* 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) + +#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; + u8 e2p_mac[ETH_ALEN]; + /* 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, + is_zd1211b: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 *dev, + struct usb_interface *intf); +void zd_chip_clear(struct zd_chip *chip); +int zd_chip_init_hw(struct zd_chip *chip, u8 device_type); +int zd_chip_reset(struct zd_chip *chip); + +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); +void zd_get_e2p_mac_addr(struct zd_chip *chip, u8 *mac_addr); +int zd_read_mac_addr(struct zd_chip *chip, u8 *mac_addr); +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); + +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); + +static inline int zd_chip_set_rx_filter(struct zd_chip *chip, u32 filter) +{ + return zd_iowrite32(chip, CR_RX_FILTER, filter); +} + +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_strength_percent(u8 rssi); + +u16 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 --git a/drivers/net/wireless/mac80211/zd1211rw/zd_def.h b/drivers/net/wireless/mac80211/zd1211rw/zd_def.h new file mode 100644 index 0000000..deb99d1 --- /dev/null +++ b/drivers/net/wireless/mac80211/zd1211rw/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 --git a/drivers/net/wireless/mac80211/zd1211rw/zd_ieee80211.h b/drivers/net/wireless/mac80211/zd1211rw/zd_ieee80211.h new file mode 100644 index 0000000..87d35df --- /dev/null +++ b/drivers/net/wireless/mac80211/zd1211rw/zd_ieee80211.h @@ -0,0 +1,67 @@ +#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, +}; + +#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; +} + +#define ZD_OFDM_RATE_6M 0xb +#define ZD_OFDM_RATE_9M 0xf +#define ZD_OFDM_RATE_12M 0xa +#define ZD_OFDM_RATE_18M 0xe +#define ZD_OFDM_RATE_24M 0x9 +#define ZD_OFDM_RATE_36M 0xd +#define ZD_OFDM_RATE_48M 0x8 +#define ZD_OFDM_RATE_54M 0xc + +struct cck_plcp_header { + u8 signal; + u8 service; + __le16 length; + __le16 crc16; +} __attribute__((packed)); + +static inline u8 zd_cck_plcp_header_rate(const struct cck_plcp_header *header) +{ + return header->signal; +} + +#define ZD_CCK_SIGNAL_1M 0x0a +#define ZD_CCK_SIGNAL_2M 0x14 +#define ZD_CCK_SIGNAL_5M5 0x37 +#define ZD_CCK_SIGNAL_11M 0x6e + +enum ieee80211_std { + IEEE80211B = 0x01, + IEEE80211A = 0x02, + IEEE80211G = 0x04, +}; + +#endif /* _ZD_IEEE80211_H */ diff --git a/drivers/net/wireless/mac80211/zd1211rw/zd_mac.c b/drivers/net/wireless/mac80211/zd1211rw/zd_mac.c new file mode 100644 index 0000000..618913d --- /dev/null +++ b/drivers/net/wireless/mac80211/zd1211rw/zd_mac.c @@ -0,0 +1,942 @@ +/* zd_mac.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 "zd_def.h" +#include "zd_chip.h" +#include "zd_mac.h" +#include "zd_ieee80211.h" +#include "zd_rf.h" +#include "zd_util.h" + +/* TODO: remove this once we have a general modes/channels/rates filling func */ +static const struct ieee80211_rate zd_rates[] = { + { .rate = 10, + .val = ZD_CS_CCK | ZD_CS_CCK_RATE_1M, + .flags = IEEE80211_RATE_CCK }, + { .rate = 20, + .val = ZD_CS_CCK | ZD_CS_CCK_RATE_2M, + .val2 = ZD_CS_CCK | ZD_CS_CCK_RATE_2M | ZD_CS_CCK_PREA_SHORT, + .flags = IEEE80211_RATE_CCK_2 }, + { .rate = 55, + .val = ZD_CS_CCK | ZD_CS_CCK_RATE_5_5M, + .val2 = ZD_CS_CCK | ZD_CS_CCK_RATE_5_5M | ZD_CS_CCK_PREA_SHORT, + .flags = IEEE80211_RATE_CCK_2 }, + { .rate = 110, + .val = ZD_CS_CCK | ZD_CS_CCK_RATE_11M, + .val2 = ZD_CS_CCK | ZD_CS_CCK_RATE_11M | ZD_CS_CCK_PREA_SHORT, + .flags = IEEE80211_RATE_CCK_2 }, + { .rate = 60, + .val = ZD_CS_OFDM | ZD_OFDM_RATE_6M, + .flags = IEEE80211_RATE_OFDM }, + { .rate = 90, + .val = ZD_CS_OFDM | ZD_OFDM_RATE_9M, + .flags = IEEE80211_RATE_OFDM }, + { .rate = 120, + .val = ZD_CS_OFDM | ZD_OFDM_RATE_12M, + .flags = IEEE80211_RATE_OFDM }, + { .rate = 180, + .val = ZD_CS_OFDM | ZD_OFDM_RATE_18M, + .flags = IEEE80211_RATE_OFDM }, + { .rate = 240, + .val = ZD_CS_OFDM | ZD_OFDM_RATE_24M, + .flags = IEEE80211_RATE_OFDM }, + { .rate = 360, + .val = ZD_CS_OFDM | ZD_OFDM_RATE_36M, + .flags = IEEE80211_RATE_OFDM }, + { .rate = 480, + .val = ZD_CS_OFDM | ZD_OFDM_RATE_48M, + .flags = IEEE80211_RATE_OFDM }, + { .rate = 540, + .val = ZD_CS_OFDM | 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_init_hw(struct ieee80211_hw *dev, u8 device_type) +{ + int r; + struct zd_mac *mac = zd_dev_mac(dev); + struct zd_chip *chip = &mac->chip; + u8 addr[ETH_ALEN]; + u8 default_regdomain; + + r = zd_chip_enable_int(chip); + if (r) + goto out; + r = zd_chip_init_hw(chip, device_type); + if (r) + goto disable_int; + + zd_get_e2p_mac_addr(chip, addr); + r = zd_write_mac_addr(chip, addr); + if (r) + goto disable_int; + ZD_ASSERT(!irqs_disabled()); + spin_lock_irq(&mac->lock); + SET_IEEE80211_PERM_ADDR(dev, addr); + spin_unlock_irq(&mac->lock); + + 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; + + /* TODO: waiting for regulatory domain support in mac80211 */ + /*r = zd_geo_init(zd_mac_to_ieee80211(mac), mac->regdomain); + if (r) + goto disable_int;*/ + + 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 reset_mode(struct zd_mac *mac) +{ + u32 filter = mac->mode == IEEE80211_IF_TYPE_MNTR ? ~0 : STA_RX_FILTER; + + return zd_iowrite32(&mac->chip, CR_RX_FILTER, filter); +} + +static int zd_mac_open(struct ieee80211_hw *dev) +{ + struct zd_mac *mac = zd_dev_mac(dev); + struct zd_chip *chip = &mac->chip; + int r; + + 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 = reset_mode(mac); + if (r) + goto disable_int; + r = zd_chip_switch_radio_on(chip); + if (r < 0) + goto disable_int; + r = zd_write_mac_addr(chip, mac->hwaddr); + if (r) + goto disable_radio; + 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 int zd_mac_stop(struct ieee80211_hw *dev) +{ + struct zd_mac *mac = zd_dev_mac(dev); + struct zd_chip *chip = &mac->chip; + struct sk_buff *skb; + struct sk_buff_head *tx_queue = &mac->tx_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 softmac after we have stopped it. + */ + + zd_chip_disable_rxtx(chip); + housekeeping_disable(mac); + + zd_chip_disable_hwint(chip); + zd_chip_switch_radio_off(chip); + zd_chip_disable_int(chip); + + + while ((skb = skb_dequeue(tx_queue))) + kfree_tx_skb(skb); + + return 0; +} + +/** + * tx_frames - returns the number of incompleted frames + * @mac: a &struct zd_mac pointer + * + * This is the number of frames, which have not been completed so far. + * Packets without ACKs are completed if the have been transmitted to the + * decice and all others if they have been removed from the tx_queue. + */ +static int tx_frames(struct zd_mac *mac) +{ + return skb_queue_len(&mac->tx_queue) + zd_usb_tx_frames(&mac->chip.usb); +} + +/** + * try_stop - if necessary closes the incoming network queues + * @dev: a &struct ieee80211_hw pointer + * + * If the number of incompleted frames is higher than @tx_high, the function + * stops the incoming queues of the mac80211 stack. Nothing happens if the + * queues have already been stopped. + */ +static void try_stop(struct ieee80211_hw *dev) +{ + unsigned long flags; + struct zd_mac *mac = zd_dev_mac(dev); + + spin_lock_irqsave(&mac->lock, flags); + if (!mac->tx_stopped && tx_frames(mac) > mac->tx_high) { + ieee80211_stop_queues(dev); + mac->tx_stopped = 1; + } + spin_unlock_irqrestore(&mac->lock, flags); +} + +/** + * try_wakeup - wake queue + * @dev: a &struct ieee80211_hw pointer + * + * If the number of incompleted frames drops under the the low level and the + * upper-layer transfer queues have been stopped, the queues will be wakened + * again. + */ +static void try_wakeup(struct ieee80211_hw *dev) +{ + unsigned long flags; + struct zd_mac *mac = zd_dev_mac(dev); + + spin_lock_irqsave(&mac->lock, flags); + if (mac->tx_stopped && tx_frames(mac) <= mac->tx_low) { + ieee80211_wake_queues(dev); + mac->tx_stopped = 0; + } + spin_unlock_irqrestore(&mac->lock, flags); +} + +/** + * 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. Notify that + * the control parameter will be only copied into the control block, if ACKs + * are requieed. + */ +static int init_tx_skb_control_block(struct sk_buff *skb, + struct ieee80211_hw *dev, + 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->dev = dev; + if (!(control->flags & IEEE80211_TXCTL_NO_ACK)) { + cb->control = kmalloc(sizeof(*control), GFP_ATOMIC); + if (cb->control == NULL) + return -ENOMEM; + memcpy(cb->control, control, sizeof(*control)); + } + + return 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 @tx_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 *dev = cb->dev; + + if (likely(cb->control)) { + skb_pull(skb, sizeof(struct zd_ctrlset)); + if (unlikely(error)) { + struct ieee80211_tx_status status = {{0}}; + + memcpy(&status.control, + cb->control, sizeof(status.control)); + clear_tx_skb_control_block(skb); + ieee80211_tx_status_irqsafe(dev, skb, &status); + } else { + skb_queue_tail(&zd_dev_mac(dev)->tx_queue, skb); + return; + } + } else { + kfree_tx_skb(skb); + } + try_wakeup(dev); +} + +static int zd_calc_tx_length_us(u8 *service, u8 cs_rate, u16 tx_length) +{ + static const u8 rate_divisor[] = { + [ZD_CS_CCK_RATE_1M] = 1, + [ZD_CS_CCK_RATE_2M] = 2, + [ZD_CS_CCK_RATE_5_5M] = 11, /* bits must be doubled */ + [ZD_CS_CCK_RATE_11M] = 11, + [ZD_OFDM_RATE_6M] = 6, + [ZD_OFDM_RATE_9M] = 9, + [ZD_OFDM_RATE_12M] = 12, + [ZD_OFDM_RATE_18M] = 18, + [ZD_OFDM_RATE_24M] = 24, + [ZD_OFDM_RATE_36M] = 36, + [ZD_OFDM_RATE_48M] = 48, + [ZD_OFDM_RATE_54M] = 54, + }; + + u32 bits = (u32)tx_length * 8; + u32 divisor; + + divisor = rate_divisor[cs_rate]; + if (divisor == 0) + return -EINVAL; + + switch (cs_rate) { + case ZD_CS_CCK_RATE_5_5M: + bits = (2*bits) + 10; /* round up to the next integer */ + break; + case ZD_CS_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_STYPE) == 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(mac->chip.is_zd1211b ? + 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_CS_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_mac_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_mac_tx(struct ieee80211_hw *dev, struct sk_buff *skb, + struct ieee80211_tx_control *control) +{ + struct zd_mac *mac = zd_dev_mac(dev); + int r; + + r = fill_ctrlset(mac, skb, control); + if (r) + return r; + + r = init_tx_skb_control_block(skb, dev, control); + if (r) + return r; + r = zd_usb_tx(&mac->chip.usb, skb); + if (r) { + clear_tx_skb_control_block(skb); + return r; + } + try_stop(dev); + return 0; +} + +/** + * 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 *dev) +{ + struct sk_buff_head *tx_queue = &zd_dev_mac(dev)->tx_queue; + struct sk_buff *skb; + struct ieee80211_tx_status status; + struct zd_tx_skb_control_block *cb; + + skb = skb_dequeue(tx_queue); + if (skb == NULL) + return; + cb = (struct zd_tx_skb_control_block *)skb->cb; + ZD_ASSERT(cb->control != NULL); + memset(&status, 0, sizeof(status)); + memcpy(&status.control, cb->control, sizeof(status.control)); + clear_tx_skb_control_block(skb); + ieee80211_tx_status_irqsafe(dev, skb, &status); + try_wakeup(dev); +} + +struct zd_rt_hdr { + struct ieee80211_radiotap_header rt_hdr; + u8 rt_flags; + u8 rt_rate; + __le16 rt_channel; + __le16 rt_chbitmask; +} __attribute__((packed)); + +static void fill_rt_header(void *buffer, struct zd_mac *mac, + const struct ieee80211_rx_status *stats, + const struct rx_status *status) +{ + struct zd_rt_hdr *hdr = buffer; + + hdr->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION; + hdr->rt_hdr.it_pad = 0; + hdr->rt_hdr.it_len = cpu_to_le16(sizeof(struct zd_rt_hdr)); + hdr->rt_hdr.it_present = cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) | + (1 << IEEE80211_RADIOTAP_CHANNEL) | + (1 << IEEE80211_RADIOTAP_RATE)); + + hdr->rt_flags = 0; + if (status->decryption_type & (ZD_RX_WEP64|ZD_RX_WEP128|ZD_RX_WEP256)) + hdr->rt_flags |= IEEE80211_RADIOTAP_F_WEP; + + hdr->rt_rate = stats->rate / 5; + + /* FIXME: 802.11a */ + hdr->rt_channel = cpu_to_le16(ieee80211chan2mhz( + _zd_chip_get_channel(&mac->chip))); + hdr->rt_chbitmask = cpu_to_le16(IEEE80211_CHAN_2GHZ | + ((status->frame_status & ZD_RX_FRAME_MODULATION_MASK) == + ZD_RX_OFDM ? IEEE80211_CHAN_OFDM : IEEE80211_CHAN_CCK)); +} + +static int fill_rx_stats(struct ieee80211_rx_status *stats, + const struct rx_status **pstatus, + struct zd_mac *mac, + const u8 *buffer, unsigned int length) +{ + const struct rx_status *status; + + *pstatus = status = zd_tail(buffer, length, sizeof(struct rx_status)); + if (status->frame_status & ZD_RX_ERROR) { + /* FIXME: update? */ + return -EINVAL; + } + memset(stats, 0, sizeof(*stats)); + + stats->channel = _zd_chip_get_channel(&mac->chip); + stats->freq = zd_channels[stats->channel - 1].freq; + stats->phymode = MODE_IEEE80211G; + stats->ssi = zd_rx_strength_percent(status->signal_strength); + stats->signal = zd_rx_qual_percent(buffer, + length - sizeof(struct rx_status), + status); + stats->rate = zd_rx_rate(buffer, status); + + 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. + */ +static int filter_ack(struct ieee80211_hw *dev, 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 *tx_queue; + unsigned long flags; + + if ((fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) != + (IEEE80211_FTYPE_CTL | IEEE80211_STYPE_ACK)) + return 0; + + tx_queue = &zd_dev_mac(dev)->tx_queue; + spin_lock_irqsave(&tx_queue->lock, flags); + for (skb = tx_queue->next; skb != (struct sk_buff *)tx_queue; + skb = skb->next) + { + struct ieee80211_hdr *tx_hdr; + struct zd_tx_skb_control_block *cb = + (struct zd_tx_skb_control_block *)skb->cb; + + ZD_ASSERT(cb->control != NULL); + tx_hdr = (struct ieee80211_hdr *)skb->data; + if (likely(!compare_ether_addr(tx_hdr->addr2, rx_hdr->addr1))) + { + struct ieee80211_tx_status status = {{0}}; + memcpy(&status.control, + cb->control, sizeof(status.control)); + status.flags = IEEE80211_TX_STATUS_ACK; + status.ack_signal = stats->ssi; + __skb_unlink(skb, tx_queue); + clear_tx_skb_control_block(skb); + ieee80211_tx_status_irqsafe(dev, skb, &status); + try_wakeup(dev); + goto out; + } + } +out: + spin_unlock_irqrestore(&tx_queue->lock, flags); + return 1; +} + +int zd_mac_rx(struct ieee80211_hw *dev, const u8 *buffer, unsigned int length) +{ + int r; + struct zd_mac *mac = zd_dev_mac(dev); + struct ieee80211_rx_status stats; + const struct rx_status *status; + struct sk_buff *skb; + + if (length < ZD_PLCP_HEADER_SIZE + 10 /* IEEE80211_1ADDR_LEN */ + + FCS_LEN + sizeof(struct rx_status)) + return -EINVAL; + + r = fill_rx_stats(&stats, &status, mac, buffer, length); + if (r) + return r; + + length -= ZD_PLCP_HEADER_SIZE+ + sizeof(struct rx_status); + buffer += ZD_PLCP_HEADER_SIZE; + + if (length == (10 /* IEEE80211_1ADDR_LEN */ + FCS_LEN) && + filter_ack(dev, (struct ieee80211_hdr *)buffer, &stats) && + mac->mode != IEEE80211_IF_TYPE_MNTR) + return 0; + + skb = dev_alloc_skb(sizeof(struct zd_rt_hdr) + length); + if (!skb) + return -ENOMEM; + /* FIXME: reenable when mac80211 has support */ + /*if (mac->mode == IEEE80211_IF_TYPE_MNTR) + fill_rt_header(skb_put(skb, sizeof(struct zd_rt_hdr)), mac, + &stats, status);*/ + memcpy(skb_put(skb, length), buffer, length); + + ieee80211_rx_irqsafe(dev, skb, &stats); + return 0; +} + +static int zd_mac_add_interface(struct ieee80211_hw *dev, + struct ieee80211_if_init_conf *conf) +{ + struct zd_mac *mac = zd_dev_mac(dev); + + /* NOTE: using IEEE80211_IF_TYPE_MGMT to indicate no mode selected */ + if (mac->mode != IEEE80211_IF_TYPE_MGMT) + return -1; + + switch (conf->type) { + case IEEE80211_IF_TYPE_MNTR: + case IEEE80211_IF_TYPE_STA: + mac->mode = conf->type; + break; + default: + return -EOPNOTSUPP; + } + + mac->hwaddr = conf->mac_addr; + + return 0; +} + +static void zd_mac_remove_interface(struct ieee80211_hw *dev, + struct ieee80211_if_init_conf *conf) +{ + struct zd_mac *mac = zd_dev_mac(dev); + mac->mode = IEEE80211_IF_TYPE_MGMT; +} + +static int zd_mac_config(struct ieee80211_hw *dev, struct ieee80211_conf *conf) +{ + struct zd_mac *mac = zd_dev_mac(dev); + return zd_chip_set_channel(&mac->chip, conf->channel); +} + +static int zd_mac_config_interface(struct ieee80211_hw *dev, int if_id, + struct ieee80211_if_conf *conf) +{ + struct zd_mac *mac = zd_dev_mac(dev); + + mac->associated = is_valid_ether_addr(conf->bssid); + + /* 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 zd_mac_set_multicast_list(struct ieee80211_hw *dev, + unsigned short dev_flags, int mc_count) +{ + struct zd_mc_hash hash; + struct zd_mac *mac = zd_dev_mac(dev); + unsigned long flags; + + if (dev_flags & (IFF_PROMISC|IFF_ALLMULTI)) { + zd_mc_add_all(&hash); + } else { + struct dev_mc_list *mc = NULL; + void *tmp = NULL; + zd_mc_clear(&hash); + while ((mc = ieee80211_get_mc_list_item(dev, mc, &tmp))) { + dev_dbg_f(zd_mac_dev(mac), "mc addr " MAC_FMT "\n", + MAC_ARG(mc->dmi_addr)); + zd_mc_add_addr(&hash, mc->dmi_addr); + } + } + + spin_lock_irqsave(&mac->lock, flags); + mac->multicast_hash = hash; + spin_unlock_irqrestore(&mac->lock, flags); + queue_work(zd_workqueue, &mac->set_multicast_hash_work); +} + +static const struct ieee80211_ops zd_ops = { + .tx = zd_mac_tx, + .open = zd_mac_open, + .stop = zd_mac_stop, + .add_interface = zd_mac_add_interface, + .remove_interface = zd_mac_remove_interface, + .config = zd_mac_config, + .config_interface = zd_mac_config_interface, + .set_multicast_list = zd_mac_set_multicast_list +}; + +struct ieee80211_hw *zd_mac_alloc(struct usb_interface *intf) +{ + struct zd_mac *mac; + struct ieee80211_hw *dev; + int i; + + dev = ieee80211_alloc_hw(sizeof(struct zd_mac), &zd_ops); + if (!dev) { + dev_dbg_f(&intf->dev, "out of memory\n"); + return NULL; + } + + mac = zd_dev_mac(dev); + + memset(mac, 0, sizeof(*mac)); + spin_lock_init(&mac->lock); + mac->dev = dev; + + mac->mode = IEEE80211_IF_TYPE_MGMT; + mac->hwaddr = dev->wiphy->perm_addr; + + 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; + + dev->flags = IEEE80211_HW_RX_INCLUDES_FCS | + IEEE80211_HW_WEP_INCLUDE_IV; + dev->max_rssi = 100; + dev->max_signal = 100; + + dev->queues = 1; + dev->extra_tx_headroom = sizeof(struct zd_ctrlset); + + mac->tx_low = ZD_MAC_TX_LOW; + mac->tx_high = ZD_MAC_TX_HIGH; + skb_queue_head_init(&mac->tx_queue); + + for (i = 0; i < 2; i++) { + if (ieee80211_register_hwmode(dev, &mac->modes[i])) { + dev_dbg_f(&intf->dev, "cannot register hwmode\n"); + ieee80211_free_hw(dev); + return NULL; + } + } + + zd_chip_init(&mac->chip, dev, intf); + housekeeping_init(mac); + INIT_WORK(&mac->set_multicast_hash_work, set_multicast_hash_handler); + + SET_IEEE80211_DEV(dev, &intf->dev); + return dev; +} + +#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_err(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 --git a/drivers/net/wireless/mac80211/zd1211rw/zd_mac.h b/drivers/net/wireless/mac80211/zd1211rw/zd_mac.h new file mode 100644 index 0000000..5faa562 --- /dev/null +++ b/drivers/net/wireless/mac80211/zd1211rw/zd_mac.h @@ -0,0 +1,199 @@ +/* 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 + +/* zd_crtlset field modulation */ +#define ZD_CS_RATE_MASK 0x0f +#define ZD_CS_TYPE_MASK 0x10 +#define ZD_CS_RATE(modulation) ((modulation) & ZD_CS_RATE_MASK) +#define ZD_CS_TYPE(modulation) ((modulation) & ZD_CS_TYPE_MASK) + +#define ZD_CS_CCK 0x00 +#define ZD_CS_OFDM 0x10 + +#define ZD_CS_CCK_RATE_1M 0x00 +#define ZD_CS_CCK_RATE_2M 0x01 +#define ZD_CS_CCK_RATE_5_5M 0x02 +#define ZD_CS_CCK_RATE_11M 0x03 +/* The rates for OFDM are encoded as in the PLCP header. Use ZD_OFDM_RATE_*. + */ + +/* bit 5 is preamble (when in CCK mode), or a/g selection (when in OFDM mode) */ +#define ZD_CS_CCK_PREA_LONG 0x00 +#define ZD_CS_CCK_PREA_SHORT 0x20 +#define ZD_CS_OFDM_MODE_11G 0x00 +#define ZD_CS_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 *dev; + void *context; +}; + +#define ZD_MAC_STATS_BUFFER_SIZE 16 + +#define ZD_MAC_TX_HIGH 6 +#define ZD_MAC_TX_LOW 2 + +struct zd_mac { + struct zd_chip chip; + spinlock_t lock; + struct ieee80211_hw *dev; + struct housekeeping housekeeping; + struct work_struct set_multicast_hash_work; + struct zd_mc_hash multicast_hash; + u8 regdomain; + u8 default_regdomain; + int mode; + int associated; + u8 *hwaddr; + struct sk_buff_head tx_queue; + int tx_high; + int tx_low; + int tx_stopped; + struct ieee80211_channel channels[14]; + struct ieee80211_rate rates[12]; + struct ieee80211_hw_mode modes[2]; +}; + +static inline struct zd_mac *zd_dev_mac(struct ieee80211_hw *dev) +{ + return dev->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)); +} + +#define zd_mac_dev(mac) (zd_chip_dev(&(mac)->chip)) + +struct ieee80211_hw *zd_mac_alloc(struct usb_interface *intf); +void zd_mac_clear(struct zd_mac *mac); + +int zd_mac_init_hw(struct ieee80211_hw *dev, u8 device_type); + +int zd_mac_rx(struct ieee80211_hw *dev, const u8 *buffer, unsigned int length); +void zd_mac_tx_failed(struct ieee80211_hw *dev); +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 --git a/drivers/net/wireless/mac80211/zd1211rw/zd_rf.c b/drivers/net/wireless/mac80211/zd1211rw/zd_rf.c new file mode 100644 index 0000000..549c23b --- /dev/null +++ b/drivers/net/wireless/mac80211/zd1211rw/zd_rf.c @@ -0,0 +1,170 @@ +/* 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", + [UNKNOWN_A_RF] = "UNKNOWN_A_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)); +} + +void zd_rf_clear(struct zd_rf *rf) +{ + ZD_MEMCLEAR(rf, sizeof(*rf)); +} + +int zd_rf_init_hw(struct zd_rf *rf, u8 type) +{ + int r, 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); + if (r) + return r; + break; + case AL2230_RF: + r = zd_rf_init_al2230(rf); + if (r) + return r; + break; + case AL7230B_RF: + r = zd_rf_init_al7230b(rf); + if (r) + return r; + 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; + } + + 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 --git a/drivers/net/wireless/mac80211/zd1211rw/zd_rf.h b/drivers/net/wireless/mac80211/zd1211rw/zd_rf.h new file mode 100644 index 0000000..aa9cc10 --- /dev/null +++ b/drivers/net/wireless/mac80211/zd1211rw/zd_rf.h @@ -0,0 +1,80 @@ +/* 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 UNKNOWN_A_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; + + /* 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); +}; + +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); + +/* 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); + +#endif /* _ZD_RF_H */ diff --git a/drivers/net/wireless/mac80211/zd1211rw/zd_rf_al2230.c b/drivers/net/wireless/mac80211/zd1211rw/zd_rf_al2230.c new file mode 100644 index 0000000..511392a --- /dev/null +++ b/drivers/net/wireless/mac80211/zd1211rw/zd_rf_al2230.c @@ -0,0 +1,436 @@ +/* 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" + +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 (chip->al2230s_bit) { + 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 (chip->al2230s_bit) + 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 (chip->al2230s_bit) { + 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 (chip->al2230s_bit) + 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 (chip->is_zd1211b) { + 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; + return 0; +} diff --git a/drivers/net/wireless/mac80211/zd1211rw/zd_rf_al7230b.c b/drivers/net/wireless/mac80211/zd1211rw/zd_rf_al7230b.c new file mode 100644 index 0000000..5e5e9dd --- /dev/null +++ b/drivers/net/wireless/mac80211/zd1211rw/zd_rf_al7230b.c @@ -0,0 +1,491 @@ +/* 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 (chip->is_zd1211b) { + 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->switch_radio_off = al7230b_switch_radio_off; + + return 0; +} diff --git a/drivers/net/wireless/mac80211/zd1211rw/zd_rf_rf2959.c b/drivers/net/wireless/mac80211/zd1211rw/zd_rf_rf2959.c new file mode 100644 index 0000000..2d736bd --- /dev/null +++ b/drivers/net/wireless/mac80211/zd1211rw/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 (chip->is_zd1211b) { + 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 --git a/drivers/net/wireless/mac80211/zd1211rw/zd_usb.c b/drivers/net/wireless/mac80211/zd1211rw/zd_usb.c new file mode 100644 index 0000000..e623043 --- /dev/null +++ b/drivers/net/wireless/mac80211/zd1211rw/zd_usb.c @@ -0,0 +1,1444 @@ +/* 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" +#include "zd_util.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 }, + /* 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 }, + /* "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(char *buffer, size_t size, u8 device_type, + const char* postfix) +{ + scnprintf(buffer, size, "%s%s", + device_type == DEVICE_ZD1211B ? + FW_ZD1211B_PREFIX : FW_ZD1211_PREFIX, + postfix); + return buffer; +} + +static int handle_version_mismatch(struct usb_device *udev, u8 device_type, + const struct firmware *ub_fw) +{ + const struct firmware *ur_fw = NULL; + int offset; + int r = 0; + char fw_name[128]; + + r = request_fw_file(&ur_fw, + get_fw_name(fw_name, sizeof(fw_name), device_type, "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 usb_device *udev, u8 device_type) +{ + int r; + u16 fw_bcdDevice; + u16 bcdDevice; + 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(fw_name, sizeof(fw_name), device_type, "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(udev, device_type, 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(fw_name, sizeof(fw_name), device_type, "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; +} + +#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_dev(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_dev(usb), buffer+l, k); + if (i >= 2) + return; + l = (n+3) & ~3; + } + } else { + zd_mac_rx(zd_usb_to_dev(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; + atomic_set(&tx->submitted_urbs, 0); + 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; + atomic_set(&tx->submitted_urbs, 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); +} + +/** + * 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_dev_mac(cb->dev)->chip.usb; + atomic_dec(&usb->tx.submitted_urbs); + free_tx_urb(usb, urb); + 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; + atomic_inc(&usb->tx.submitted_urbs); + 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; + INIT_LIST_HEAD(&tx->free_urb_list); + atomic_set(&tx->submitted_urbs, 0); +} + +void zd_usb_init(struct zd_usb *usb, struct ieee80211_hw *dev, + struct usb_interface *intf) +{ + memset(usb, 0, sizeof(*usb)); + usb->intf = usb_get_intf(intf); + usb_set_intfdata(usb->intf, dev); + 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; +} + +static int probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + int r; + struct usb_device *udev = interface_to_usbdev(intf); + struct ieee80211_hw *dev = 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; + } + + dev = zd_mac_alloc(intf); + if (dev == NULL) { + r = -ENOMEM; + goto error; + } + + r = upload_firmware(udev, id->driver_info); + if (r) { + dev_err(&intf->dev, + "couldn't load firmware. Error number %d\n", r); + goto error; + } + + r = usb_reset_configuration(udev); + if (r) { + dev_dbg_f(&intf->dev, + "couldn't reset configuration. Error number %d\n", r); + goto error; + } + + /* At this point the interrupt endpoint is not generally enabled. We + * save the USB bandwidth until the network device is opened. But + * notify that the initialization of the MAC will require the + * interrupts to be temporary enabled. + */ + r = zd_mac_init_hw(dev, id->driver_info); + if (r) { + dev_dbg_f(&intf->dev, + "couldn't initialize mac. Error number %d\n", r); + goto error; + } + + r = ieee80211_register_hw(dev); + 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(dev->wiphy)); + return 0; +error: + usb_reset_device(interface_to_usbdev(intf)); + if (dev) { + zd_mac_clear(zd_dev_mac(dev)); + ieee80211_free_hw(dev); + } + return r; +} + +static void disconnect(struct usb_interface *intf) +{ + struct ieee80211_hw *dev = zd_intf_to_dev(intf); + struct zd_mac *mac; + struct zd_usb *usb; + + /* Either something really bad happened, or we're just dealing with + * a DEVICE_INSTALLER. */ + if (dev == NULL) + return; + + mac = zd_dev_mac(dev); + usb = &mac->chip.usb; + + dev_dbg_f(zd_usb_dev(usb), "\n"); + + ieee80211_unregister_hw(dev); + + /* 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(dev); + 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 --git a/drivers/net/wireless/mac80211/zd1211rw/zd_usb.h b/drivers/net/wireless/mac80211/zd1211rw/zd_usb.h new file mode 100644 index 0000000..f01d0bb --- /dev/null +++ b/drivers/net/wireless/mac80211/zd1211rw/zd_usb.h @@ -0,0 +1,267 @@ +/* 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" + +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 + */ +struct zd_usb_tx { + spinlock_t lock; + struct list_head free_urb_list; + atomic_t submitted_urbs; + int enabled; +}; + +/* 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; +}; + +#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_dev(struct usb_interface *intf) +{ + return usb_get_intfdata(intf); +} + +static inline struct ieee80211_hw *zd_usb_to_dev(struct zd_usb *usb) +{ + return zd_intf_to_dev(usb->intf); +} + +void zd_usb_init(struct zd_usb *usb, struct ieee80211_hw *dev, + 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); + +/** + * zd_usb_tx_frames - frames in transfer to the device + * @usb: a &struct zd_usb pointer + * + * This function returns the number of frames, which are currently + * transmitted to the device. + */ +static inline int zd_usb_tx_frames(struct zd_usb *usb) +{ + return atomic_read(&usb->tx.submitted_urbs); +} + +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); + +extern struct workqueue_struct *zd_workqueue; + +#endif /* _ZD_USB_H */ diff --git a/drivers/net/wireless/mac80211/zd1211rw/zd_util.c b/drivers/net/wireless/mac80211/zd1211rw/zd_util.c new file mode 100644 index 0000000..d20036c --- /dev/null +++ b/drivers/net/wireless/mac80211/zd1211rw/zd_util.c @@ -0,0 +1,82 @@ +/* zd_util.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 + * + * Utility program + */ + +#include "zd_def.h" +#include "zd_util.h" + +#ifdef DEBUG +static char hex(u8 v) +{ + v &= 0xf; + return (v < 10 ? '0' : 'a' - 10) + v; +} + +static char hex_print(u8 c) +{ + return (0x20 <= c && c < 0x7f) ? c : '.'; +} + +static void dump_line(const u8 *bytes, size_t size) +{ + char c; + size_t i; + + size = size <= 8 ? size : 8; + printk(KERN_DEBUG "zd1211 %p ", bytes); + for (i = 0; i < 8; i++) { + switch (i) { + case 1: + case 5: + c = '.'; + break; + case 3: + c = ':'; + break; + default: + c = ' '; + } + if (i < size) { + printk("%c%c%c", hex(bytes[i] >> 4), hex(bytes[i]), c); + } else { + printk(" %c", c); + } + } + + for (i = 0; i < size; i++) + printk("%c", hex_print(bytes[i])); + printk("\n"); +} + +void zd_hexdump(const void *bytes, size_t size) +{ + size_t i = 0; + + do { + dump_line((u8 *)bytes + i, size-i); + i += 8; + } while (i < size); +} +#endif /* DEBUG */ + +void *zd_tail(const void *buffer, size_t buffer_size, size_t tail_size) +{ + if (buffer_size < tail_size) + return NULL; + return (u8 *)buffer + (buffer_size - tail_size); +} diff --git a/drivers/net/wireless/mac80211/zd1211rw/zd_util.h b/drivers/net/wireless/mac80211/zd1211rw/zd_util.h new file mode 100644 index 0000000..ce26f7a --- /dev/null +++ b/drivers/net/wireless/mac80211/zd1211rw/zd_util.h @@ -0,0 +1,29 @@ +/* zd_util.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_UTIL_H +#define _ZD_UTIL_H + +void *zd_tail(const void *buffer, size_t buffer_size, size_t tail_size); + +#ifdef DEBUG +void zd_hexdump(const void *bytes, size_t size); +#else +#define zd_hexdump(bytes, size) +#endif /* DEBUG */ + +#endif /* _ZD_UTIL_H */ diff --git a/drivers/net/wireless/zd1211rw/Kconfig b/drivers/net/wireless/zd1211rw/Kconfig index d1ab24a..b5f294e 100644 --- a/drivers/net/wireless/zd1211rw/Kconfig +++ b/drivers/net/wireless/zd1211rw/Kconfig @@ -1,6 +1,7 @@ config ZD1211RW tristate "ZyDAS ZD1211/ZD1211B USB-wireless support" depends on USB && IEEE80211_SOFTMAC && WLAN_80211 && EXPERIMENTAL + depends on ZD1211RW_MAC80211 != 'y' select WIRELESS_EXT select FW_LOADER ---help--- diff --git a/drivers/ssb/Kconfig b/drivers/ssb/Kconfig new file mode 100644 index 0000000..03c4945 --- /dev/null +++ b/drivers/ssb/Kconfig @@ -0,0 +1,93 @@ +menu "Sonics Silicon Backplane" + +config SSB + tristate "Sonics Silicon Backplane support" + depends on EXPERIMENTAL + help + Support for the Sonics Silicon Backplane bus + + The module will be called ssb + + If unsure, say M + +config SSB_PCIHOST + bool "Support for SSB on PCI-bus host" + depends on SSB && PCI + default y + help + Support for a Sonics Silicon Backplane on top + of a PCI device. + + If unsure, say Y + +config SSB_PCMCIAHOST + bool "Support for SSB on PCMCIA-bus host" + depends on SSB && PCMCIA + help + Support for a Sonics Silicon Backplane on top + of a PCMCIA device. + + If unsure, say N + +config SSB_SILENT + bool "No SSB kernel messages" + depends on SSB + help + This option turns off all Sonics Silicon Backplane printks. + Note that you won't be able to identify problems, once + messages are turned off. + This might only be desired for production kernels on + embedded devices to reduce the kernel size. + + Say N + +config SSB_DEBUG + bool "SSB debugging" + depends on SSB && !SSB_SILENT + help + This turns on additional runtime checks and debugging + messages. Turn this on for SSB troubleshooting. + + If unsure, say N + +config SSB_SERIAL + bool + depends on SSB + # ChipCommon and ExtIf serial support routines. + +config SSB_DRIVER_PCICORE + bool "SSB PCI core driver" + depends on SSB && SSB_PCIHOST + default y + help + Driver for the Sonics Silicon Backplane attached + Broadcom PCI core. + + If unsure, say Y + +config SSB_PCICORE_HOSTMODE + bool "Hostmode support for SSB PCI core" + depends on SSB_DRIVER_PCICORE && SSB_DRIVER_MIPS + help + PCIcore hostmode operation (external PCI bus). + +config SSB_DRIVER_MIPS + bool "SSB Broadcom MIPS core driver" + depends on SSB && MIPS + select SSB_SERIAL + help + Driver for the Sonics Silicon Backplane attached + Broadcom MIPS core. + + If unsure, say N + +config SSB_DRIVER_EXTIF + bool "SSB Broadcom EXTIF core driver" + depends on SSB_DRIVER_MIPS + help + Driver for the Sonics Silicon Backplane attached + Broadcom EXTIF core. + + If unsure, say N + +endmenu diff --git a/drivers/ssb/Makefile b/drivers/ssb/Makefile new file mode 100644 index 0000000..9a2b379 --- /dev/null +++ b/drivers/ssb/Makefile @@ -0,0 +1,11 @@ +ssb-builtin-drivers-y += driver_chipcommon.o +ssb-builtin-drivers-$(CONFIG_SSB_DRIVER_MIPS) += driver_mipscore.o +ssb-builtin-drivers-$(CONFIG_SSB_DRIVER_PCICORE) += driver_pcicore.o + +ssb-hostsupport-$(CONFIG_SSB_PCIHOST) += pci.o pcihost_wrapper.o +ssb-hostsupport-$(CONFIG_SSB_PCMCIAHOST) += pcmcia.o + +obj-$(CONFIG_SSB) += ssb.o + +ssb-objs := main.o scan.o \ + $(ssb-hostsupport-y) $(ssb-builtin-drivers-y) diff --git a/drivers/ssb/driver_chipcommon.c b/drivers/ssb/driver_chipcommon.c new file mode 100644 index 0000000..8e511c7 --- /dev/null +++ b/drivers/ssb/driver_chipcommon.c @@ -0,0 +1,402 @@ +/* + * Sonics Silicon Backplane + * Broadcom ChipCommon core driver + * + * Copyright 2005, Broadcom Corporation + * Copyright 2006, 2007, Michael Buesch + * + * Licensed under the GNU/GPL. See COPYING for details. + */ + +#include +#include +#include + +#include "ssb_private.h" + + +/* Clock sources */ +enum { + /* PCI clock */ + SSB_CHIPCO_CLKSRC_PCI, + /* Crystal slow clock oscillator */ + SSB_CHIPCO_CLKSRC_XTALOS, + /* Low power oscillator */ + SSB_CHIPCO_CLKSRC_LOPWROS, +}; + + +static inline u32 chipco_read32(struct ssb_chipcommon *cc, + u16 offset) +{ + return ssb_read32(cc->dev, offset); +} + +static inline void chipco_write32(struct ssb_chipcommon *cc, + u16 offset, + u32 value) +{ + ssb_write32(cc->dev, offset, value); +} + +void ssb_chipco_set_clockmode(struct ssb_chipcommon *cc, + enum ssb_clkmode mode) +{ + struct ssb_device *ccdev = cc->dev; + struct ssb_bus *bus; + u32 tmp; + + if (!ccdev) + return; + bus = ccdev->bus; + /* chipcommon cores prior to rev6 don't support dynamic clock control */ + if (ccdev->id.revision < 6) + return; + /* chipcommon cores rev10 are a whole new ball game */ + if (ccdev->id.revision >= 10) + return; + if (!(cc->capabilities & SSB_CHIPCO_CAP_PCTL)) + return; + + switch (mode) { + case SSB_CLKMODE_SLOW: + tmp = chipco_read32(cc, SSB_CHIPCO_SLOWCLKCTL); + tmp |= SSB_CHIPCO_SLOWCLKCTL_FSLOW; + chipco_write32(cc, SSB_CHIPCO_SLOWCLKCTL, tmp); + break; + case SSB_CLKMODE_FAST: + ssb_pci_xtal(bus, SSB_GPIO_XTAL, 1); /* Force crystal on */ + tmp = chipco_read32(cc, SSB_CHIPCO_SLOWCLKCTL); + tmp &= ~SSB_CHIPCO_SLOWCLKCTL_FSLOW; + tmp |= SSB_CHIPCO_SLOWCLKCTL_IPLL; + chipco_write32(cc, SSB_CHIPCO_SLOWCLKCTL, tmp); + break; + case SSB_CLKMODE_DYNAMIC: + tmp = chipco_read32(cc, SSB_CHIPCO_SLOWCLKCTL); + tmp &= ~SSB_CHIPCO_SLOWCLKCTL_FSLOW; + tmp &= ~SSB_CHIPCO_SLOWCLKCTL_IPLL; + tmp &= ~SSB_CHIPCO_SLOWCLKCTL_ENXTAL; + if ((tmp & SSB_CHIPCO_SLOWCLKCTL_SRC) != SSB_CHIPCO_SLOWCLKCTL_SRC_XTAL) + tmp |= SSB_CHIPCO_SLOWCLKCTL_ENXTAL; + chipco_write32(cc, SSB_CHIPCO_SLOWCLKCTL, tmp); + + /* for dynamic control, we have to release our xtal_pu "force on" */ + if (tmp & SSB_CHIPCO_SLOWCLKCTL_ENXTAL) + ssb_pci_xtal(bus, SSB_GPIO_XTAL, 0); + break; + default: + assert(0); + } +} + +/* Get the Slow Clock Source */ +static int chipco_pctl_get_slowclksrc(struct ssb_chipcommon *cc) +{ + struct ssb_bus *bus = cc->dev->bus; + u32 tmp = 0; + + if (cc->dev->id.revision < 6) { + if (bus->bustype == SSB_BUSTYPE_SSB || + bus->bustype == SSB_BUSTYPE_PCMCIA) + return SSB_CHIPCO_CLKSRC_XTALOS; + if (bus->bustype == SSB_BUSTYPE_PCI) { + pci_read_config_dword(bus->host_pci, SSB_GPIO_OUT, &tmp); + if (tmp & 0x10) + return SSB_CHIPCO_CLKSRC_PCI; + return SSB_CHIPCO_CLKSRC_XTALOS; + } + } + if (cc->dev->id.revision < 10) { + tmp = chipco_read32(cc, SSB_CHIPCO_SLOWCLKCTL); + tmp &= 0x7; + if (tmp == 0) + return SSB_CHIPCO_CLKSRC_LOPWROS; + if (tmp == 1) + return SSB_CHIPCO_CLKSRC_XTALOS; + if (tmp == 2) + return SSB_CHIPCO_CLKSRC_PCI; + } + + return SSB_CHIPCO_CLKSRC_XTALOS; +} + +/* Get maximum or minimum (depending on get_max flag) slowclock frequency. */ +static int chipco_pctl_clockfreqlimit(struct ssb_chipcommon *cc, int get_max) +{ + int limit; + int clocksrc; + int divisor; + u32 tmp; + + clocksrc = chipco_pctl_get_slowclksrc(cc); + if (cc->dev->id.revision < 6) { + switch (clocksrc) { + case SSB_CHIPCO_CLKSRC_PCI: + divisor = 64; + break; + case SSB_CHIPCO_CLKSRC_XTALOS: + divisor = 32; + break; + default: + assert(0); + divisor = 1; + } + } else if (cc->dev->id.revision < 10) { + switch (clocksrc) { + case SSB_CHIPCO_CLKSRC_LOPWROS: + divisor = 1; + break; + case SSB_CHIPCO_CLKSRC_XTALOS: + case SSB_CHIPCO_CLKSRC_PCI: + tmp = chipco_read32(cc, SSB_CHIPCO_SLOWCLKCTL); + divisor = (tmp >> 16) + 1; + divisor *= 4; + break; + default: + assert(0); + divisor = 1; + } + } else { + tmp = chipco_read32(cc, SSB_CHIPCO_SYSCLKCTL); + divisor = (tmp >> 16) + 1; + divisor *= 4; + } + + switch (clocksrc) { + case SSB_CHIPCO_CLKSRC_LOPWROS: + if (get_max) + limit = 43000; + else + limit = 25000; + break; + case SSB_CHIPCO_CLKSRC_XTALOS: + if (get_max) + limit = 20200000; + else + limit = 19800000; + break; + case SSB_CHIPCO_CLKSRC_PCI: + if (get_max) + limit = 34000000; + else + limit = 25000000; + break; + default: + assert(0); + limit = 0; + } + limit /= divisor; + + return limit; +} + +static void chipco_powercontrol_init(struct ssb_chipcommon *cc) +{ + struct ssb_bus *bus = cc->dev->bus; + + if (bus->chip_id == 0x4321) { + if (bus->chip_rev == 0) + chipco_write32(cc, SSB_CHIPCO_CHIPCTL, 0x3A4); + else if (bus->chip_rev == 1) + chipco_write32(cc, SSB_CHIPCO_CHIPCTL, 0xA4); + } + + if (!(cc->capabilities & SSB_CHIPCO_CAP_PCTL)) + return; + + if (cc->dev->id.revision >= 10) { + /* Set Idle Power clock rate to 1Mhz */ + chipco_write32(cc, SSB_CHIPCO_SYSCLKCTL, + (chipco_read32(cc, SSB_CHIPCO_SYSCLKCTL) & + 0x0000FFFF) | 0x00040000); + } else { + int maxfreq; + + maxfreq = chipco_pctl_clockfreqlimit(cc, 1); + chipco_write32(cc, SSB_CHIPCO_PLLONDELAY, + (maxfreq * 150 + 999999) / 1000000); + chipco_write32(cc, SSB_CHIPCO_FREFSELDELAY, + (maxfreq * 15 + 999999) / 1000000); + } +} + +static void calc_fast_powerup_delay(struct ssb_chipcommon *cc) +{ + struct ssb_bus *bus = cc->dev->bus; + int minfreq; + unsigned int tmp; + u32 pll_on_delay; + + if (bus->bustype != SSB_BUSTYPE_PCI) + return; + if (!(cc->capabilities & SSB_CHIPCO_CAP_PCTL)) + return; + + minfreq = chipco_pctl_clockfreqlimit(cc, 0); + pll_on_delay = chipco_read32(cc, SSB_CHIPCO_PLLONDELAY); + tmp = (((pll_on_delay + 2) * 1000000) + (minfreq - 1)) / minfreq; + assert((tmp & ~0xFFFF) == 0); + + cc->fast_pwrup_delay = tmp; +} + +void ssb_chipcommon_init(struct ssb_chipcommon *cc) +{ + if (!cc->dev) + return; /* We don't have a ChipCommon */ + chipco_powercontrol_init(cc); + ssb_chipco_set_clockmode(cc, SSB_CLKMODE_FAST); + calc_fast_powerup_delay(cc); +} + +void ssb_chipco_suspend(struct ssb_chipcommon *cc, pm_message_t state) +{ + if (!cc->dev) + return; + ssb_chipco_set_clockmode(cc, SSB_CLKMODE_SLOW); +} + +void ssb_chipco_resume(struct ssb_chipcommon *cc) +{ + if (!cc->dev) + return; + chipco_powercontrol_init(cc); + ssb_chipco_set_clockmode(cc, SSB_CLKMODE_FAST); +} + +void ssb_chipco_get_clockcontrol(struct ssb_chipcommon *cc, + u32 *plltype, u32 *n, u32 *m) +{ + *n = chipco_read32(cc, SSB_CHIPCO_CLOCK_N); + *plltype = (cc->capabilities & SSB_CHIPCO_CAP_PLLT); + switch (*plltype) { + case SSB_PLLTYPE_6: /* 100/200 or 120/240 only */ + *m = chipco_read32(cc, SSB_CHIPCO_CLOCK_MIPS); + break; + case SSB_PLLTYPE_3: /* 25Mhz, 2 dividers */ + if (cc->dev->bus->chip_id != 0x5365) { + *m = chipco_read32(cc, SSB_CHIPCO_CLOCK_M2); + break; + } + /* Fallthough */ + default: + *m = chipco_read32(cc, SSB_CHIPCO_CLOCK_SB); + } +} + +void ssb_chipco_timing_init(struct ssb_chipcommon *cc, + unsigned long ns) +{ + struct ssb_device *dev = cc->dev; + struct ssb_bus *bus = dev->bus; + u32 tmp; + + /* set register for external IO to control LED. */ + chipco_write32(cc, SSB_CHIPCO_PROG_CFG, 0x11); + tmp = ceildiv(10, ns) << SSB_PROG_WCNT_3_SHIFT; /* Waitcount-3 = 10ns */ + tmp |= ceildiv(40, ns) << SSB_PROG_WCNT_1_SHIFT; /* Waitcount-1 = 40ns */ + tmp |= ceildiv(240, ns); /* Waitcount-0 = 240ns */ + chipco_write32(cc, SSB_CHIPCO_PROG_WAITCNT, tmp); /* 0x01020a0c for a 100Mhz clock */ + + /* Set timing for the flash */ + tmp = ceildiv(10, ns) << SSB_FLASH_WCNT_3_SHIFT; /* Waitcount-3 = 10nS */ + tmp |= ceildiv(10, ns) << SSB_FLASH_WCNT_1_SHIFT; /* Waitcount-1 = 10nS */ + tmp |= ceildiv(120, ns); /* Waitcount-0 = 120nS */ + if ((bus->chip_id == 0x5365) || + (dev->id.revision < 9)) + chipco_write32(cc, SSB_CHIPCO_FLASH_WAITCNT, tmp); + if ((bus->chip_id == 0x5365) || + (dev->id.revision < 9) || + ((bus->chip_id == 0x5350) && (bus->chip_rev == 0))) + chipco_write32(cc, SSB_CHIPCO_PCMCIA_MEMWAIT, tmp); + + if (bus->chip_id == 0x5350) { + /* Enable EXTIF */ + tmp = ceildiv(10, ns) << SSB_PROG_WCNT_3_SHIFT; /* Waitcount-3 = 10ns */ + tmp |= ceildiv(20, ns) << SSB_PROG_WCNT_2_SHIFT; /* Waitcount-2 = 20ns */ + tmp |= ceildiv(100, ns) << SSB_PROG_WCNT_1_SHIFT; /* Waitcount-1 = 100ns */ + tmp |= ceildiv(120, ns); /* Waitcount-0 = 120ns */ + chipco_write32(cc, SSB_CHIPCO_PROG_WAITCNT, tmp); /* 0x01020a0c for a 100Mhz clock */ + } +} + + +#ifdef CONFIG_SSB_SERIAL +int ssb_chipco_serial_init(struct ssb_chipcommon *cc, + struct ssb_serial_port *ports) +{ + struct ssb_bus *bus = cc->dev->bus; + int nr_ports = 0; + u32 plltype; + unsigned int irq; + u32 baud_base, div; + u32 i, n; + + plltype = (cc->capabilities & SSB_CHIPCO_CAP_PLLT); + irq = ssb_mips_irq(cc->dev); + + if (plltype == SSB_PLLTYPE_1) { + /* PLL clock */ + baud_base = ssb_calc_clock_rate(plltype, + chipco_read32(cc, SSB_CHIPCO_CLOCK_N), + chipco_read32(cc, SSB_CHIPCO_CLOCK_M2)); + div = 1; + } else { + if (cc->dev->id.revision >= 11) { + /* Fixed ALP clock */ + baud_base = 20000000; + div = 1; + /* Set the override bit so we don't divide it */ + chipco_write32(cc, SSB_CHIPCO_CORECTL, + SSB_CHIPCO_CORECTL_UARTCLK0); + } else if (cc->dev->id.revision >= 3) { + /* Internal backplane clock */ + baud_base = ssb_clockspeed(bus); + div = 2; /* Minimum divisor */ + chipco_write32(cc, SSB_CHIPCO_CLKDIV, + (chipco_read32(cc, SSB_CHIPCO_CLKDIV) + & ~SSB_CHIPCO_CLKDIV_UART) | div); + } else { + /* Fixed internal backplane clock */ + baud_base = 88000000; + div = 48; + } + + /* Clock source depends on strapping if UartClkOverride is unset */ + if ((cc->dev->id.revision > 0) && + !(chipco_read32(cc, SSB_CHIPCO_CORECTL) & SSB_CHIPCO_CORECTL_UARTCLK0)) { + if ((cc->capabilities & SSB_CHIPCO_CAP_UARTCLK) == + SSB_CHIPCO_CAP_UARTCLK_INT) { + /* Internal divided backplane clock */ + baud_base /= div; + } else { + /* Assume external clock of 1.8432 MHz */ + baud_base = 1843200; + } + } + } + + /* Determine the registers of the UARTs */ + n = (cc->capabilities & SSB_CHIPCO_CAP_NRUART); + for (i = 0; i < n; i++) { + void __iomem *cc_mmio; + void __iomem *uart_regs; + + cc_mmio = cc->dev->bus->mmio + (cc->dev->core_index * SSB_CORE_SIZE); + uart_regs = cc_mmio + SSB_CHIPCO_UART0_DATA; + /* Offset changed at after rev 0 */ + if (cc->dev->id.revision == 0) + uart_regs += (i * 8); + else + uart_regs += (i * 256); + + nr_ports++; + ports[i].regs = uart_regs; + ports[i].irq = irq; + ports[i].baud_base = baud_base; + ports[i].reg_shift = 0; + } + + return nr_ports; +} +#endif /* CONFIG_SSB_SERIAL */ diff --git a/drivers/ssb/driver_mipscore.c b/drivers/ssb/driver_mipscore.c new file mode 100644 index 0000000..67d1017 --- /dev/null +++ b/drivers/ssb/driver_mipscore.c @@ -0,0 +1,258 @@ +/* + * Sonics Silicon Backplane + * Broadcom MIPS core driver + * + * Copyright 2005, Broadcom Corporation + * Copyright 2006, 2007, Michael Buesch + * + * Licensed under the GNU/GPL. See COPYING for details. + */ + +#include + +#include +#include +#include +#include + +#include "ssb_private.h" + + +static inline u32 mips_read32(struct ssb_mipscore *mcore, + u16 offset) +{ + return ssb_read32(mcore->dev, offset); +} + +static inline void mips_write32(struct ssb_mipscore *mcore, + u16 offset, + u32 value) +{ + ssb_write32(mcore->dev, offset, value); +} + +static const u32 ipsflag_irq_mask[] = { + 0, + SSB_IPSFLAG_IRQ1, + SSB_IPSFLAG_IRQ2, + SSB_IPSFLAG_IRQ3, + SSB_IPSFLAG_IRQ4, +}; + +static const u32 ipsflag_irq_shift[] = { + 0, + SSB_IPSFLAG_IRQ1_SHIFT, + SSB_IPSFLAG_IRQ2_SHIFT, + SSB_IPSFLAG_IRQ3_SHIFT, + SSB_IPSFLAG_IRQ4_SHIFT, +}; + +static inline u32 ssb_irqflag(struct ssb_device *dev) +{ + return ssb_read32(dev, SSB_TPSFLAG) & SSB_TPSFLAG_BPFLAG; +} + +/* Get the MIPS IRQ assignment for a specified device. + * If unassigned, 0 is returned. + */ +unsigned int ssb_mips_irq(struct ssb_device *dev) +{ + struct ssb_bus *bus = dev->bus; + u32 irqflag; + u32 ipsflag; + u32 tmp; + unsigned int irq; + + irqflag = ssb_irqflag(dev); + ipsflag = ssb_read32(bus->mipscore.dev, SSB_IPSFLAG); + for (irq = 1; irq <= 4; irq++) { + tmp = ((ipsflag & ipsflag_irq_mask[irq]) >> ipsflag_irq_shift[irq]); + if (tmp == irqflag) + break; + } + if (irq == 5) + irq = 0; + + return irq; +} + +static void clear_irq(struct ssb_bus *bus, unsigned int irq) +{ + struct ssb_device *dev = bus->mipscore.dev; + + /* Clear the IRQ in the MIPScore backplane registers */ + if (irq == 0) { + ssb_write32(dev, SSB_INTVEC, 0); + } else { + ssb_write32(dev, SSB_IPSFLAG, + ssb_read32(dev, SSB_IPSFLAG) | + ipsflag_irq_mask[irq]); + } +} + +static void set_irq(struct ssb_device *dev, unsigned int irq) +{ + unsigned int oldirq = ssb_mips_irq(dev); + struct ssb_bus *bus = dev->bus; + struct ssb_device *mdev = bus->mipscore.dev; + u32 irqflag = ssb_irqflag(dev); + + dev->irq = irq + 2; + + ssb_dprintk(KERN_INFO PFX + "set_irq: core 0x%04x, irq %d => %d\n", + dev->id.coreid, oldirq, irq); + /* clear the old irq */ + if (oldirq == 0) + ssb_write32(mdev, SSB_INTVEC, (~(1 << irqflag) & ssb_read32(mdev, SSB_INTVEC))); + else + clear_irq(bus, oldirq); + + /* assign the new one */ + if (irq == 0) + ssb_write32(mdev, SSB_INTVEC, ((1 << irqflag) & ssb_read32(mdev, SSB_INTVEC))); + + irqflag <<= ipsflag_irq_shift[irq]; + irqflag |= (ssb_read32(mdev, SSB_IPSFLAG) & ~ipsflag_irq_mask[irq]); + ssb_write32(mdev, SSB_IPSFLAG, irqflag); +} + +/* XXX: leave here or move into separate extif driver? */ +static int ssb_extif_serial_init(struct ssb_device *dev, struct ssb_serial_ports *ports) +{ + +} + + +static void ssb_mips_serial_init(struct ssb_mipscore *mcore) +{ + struct ssb_bus *bus = mcore->dev->bus; + + //TODO if (EXTIF available +#if 0 + extifregs_t *eir = (extifregs_t *) regs; + sbconfig_t *sb; + + /* Determine external UART register base */ + sb = (sbconfig_t *)((ulong) eir + SBCONFIGOFF); + base = EXTIF_CFGIF_BASE(sb_base(R_REG(&sb->sbadmatch1))); + + /* Determine IRQ */ + irq = sb_irq(sbh); + + /* Disable GPIO interrupt initially */ + W_REG(&eir->gpiointpolarity, 0); + W_REG(&eir->gpiointmask, 0); + + /* Search for external UARTs */ + n = 2; + for (i = 0; i < 2; i++) { + regs = (void *) REG_MAP(base + (i * 8), 8); + if (BCMINIT(serial_exists)(regs)) { + /* Set GPIO 1 to be the external UART IRQ */ + W_REG(&eir->gpiointmask, 2); + if (add) + add(regs, irq, 13500000, 0); + } + } + + /* Add internal UART if enabled */ + if (R_REG(&eir->corecontrol) & CC_UE) + if (add) + add((void *) &eir->uartdata, irq, sb_clock(sbh), 2); + +#endif + if (bus->extif.dev) + mcore->nr_serial_ports = ssb_extif_serial_init(&bus->extif, mcore->serial_ports); + else if (bus->chipco.dev) + mcore->nr_serial_ports = ssb_chipco_serial_init(&bus->chipco, mcore->serial_ports); + else + mcore->nr_serial_ports = 0; +} + +static void ssb_mips_flash_detect(struct ssb_mipscore *mcore) +{ + struct ssb_bus *bus = mcore->dev->bus; + + if (bus->chipco.dev) { + mcore->flash_window = 0x1c000000; + mcore->flash_window_size = 0x800000; + } else { + mcore->flash_window = 0x1fc00000; + mcore->flash_window_size = 0x400000; + } +} + + +static void ssb_cpu_clock(struct ssb_mipscore *mcore) +{ +} + +void ssb_mipscore_init(struct ssb_mipscore *mcore) +{ + struct ssb_bus *bus = mcore->dev->bus; + struct ssb_device *dev; + unsigned long hz, ns; + unsigned int irq, i; + + if (!mcore->dev) + return; /* We don't have a MIPS core */ + + ssb_dprintk(KERN_INFO PFX "Initializing MIPS core...\n"); + + hz = ssb_clockspeed(bus); + if (!hz) + hz = 100000000; + ns = 1000000000 / hz; + +//TODO +#if 0 + if (have EXTIF) { + /* Initialize extif so we can get to the LEDs and external UART */ + W_REG(&eir->prog_config, CF_EN); + + /* Set timing for the flash */ + tmp = CEIL(10, ns) << FW_W3_SHIFT; /* W3 = 10nS */ + tmp = tmp | (CEIL(40, ns) << FW_W1_SHIFT); /* W1 = 40nS */ + tmp = tmp | CEIL(120, ns); /* W0 = 120nS */ + W_REG(&eir->prog_waitcount, tmp); /* 0x01020a0c for a 100Mhz clock */ + + /* Set programmable interface timing for external uart */ + tmp = CEIL(10, ns) << FW_W3_SHIFT; /* W3 = 10nS */ + tmp = tmp | (CEIL(20, ns) << FW_W2_SHIFT); /* W2 = 20nS */ + tmp = tmp | (CEIL(100, ns) << FW_W1_SHIFT); /* W1 = 100nS */ + tmp = tmp | CEIL(120, ns); /* W0 = 120nS */ + W_REG(&eir->prog_waitcount, tmp); + } + else... chipcommon +#endif + if (bus->chipco.dev) + ssb_chipco_timing_init(&bus->chipco, ns); + + /* Assign IRQs to all cores on the bus, start with irq line 2, because serial usually takes 1 */ + for (irq = 2, i = 0; i < bus->nr_devices; i++) { + dev = &(bus->devices[i]); + dev->irq = ssb_mips_irq(dev) + 2; + switch(dev->id.coreid) { + case SSB_DEV_USB11_HOST: + /* shouldn't need a separate irq line for non-4710, most of them have a proper + * external usb controller on the pci */ + if ((bus->chip_id == 0x4710) && (irq <= 4)) { + set_irq(dev, irq++); + break; + } + case SSB_DEV_PCI: + case SSB_DEV_ETHERNET: + case SSB_DEV_80211: + case SSB_DEV_USB20_HOST: + /* These devices get their own IRQ line if available, the rest goes on IRQ0 */ + if (irq <= 4) { + set_irq(dev, irq++); + break; + } + } + } + + ssb_mips_serial_init(mcore); + ssb_mips_flash_detect(mcore); +} diff --git a/drivers/ssb/driver_pcicore.c b/drivers/ssb/driver_pcicore.c new file mode 100644 index 0000000..a59dff0 --- /dev/null +++ b/drivers/ssb/driver_pcicore.c @@ -0,0 +1,556 @@ +/* + * Sonics Silicon Backplane + * Broadcom PCI-core driver + * + * Copyright 2005, Broadcom Corporation + * Copyright 2006, 2007, Michael Buesch + * + * Licensed under the GNU/GPL. See COPYING for details. + */ + +#include +#include +#include + +#include "ssb_private.h" + + +static inline +u32 pcicore_read32(struct ssb_pcicore *pc, u16 offset) +{ + return ssb_read32(pc->dev, offset); +} + +static inline +void pcicore_write32(struct ssb_pcicore *pc, u16 offset, u32 value) +{ + ssb_write32(pc->dev, offset, value); +} + +/************************************************** + * Code for hostmode operation. + **************************************************/ + +#ifdef CONFIG_SSB_PCICORE_HOSTMODE + +#include +/* Read the bus and catch bus exceptions. This is MIPS specific. */ +#define mips_busprobe(val, addr) get_dbe((val), (addr)) + +/* Assume one-hot slot wiring */ +#define SSB_PCI_SLOT_MAX 16 + +/* Global lock is OK, as we won't have more than one extpci anyway. */ +static DEFINE_SPINLOCK(cfgspace_lock); +/* Core to access the external PCI config space. Can only have one. */ +static struct ssb_pcicore *extpci_core; + +u32 pci_iobase = 0x100; +u32 pci_membase = SSB_PCI_DMA; + +int pcibios_plat_dev_init(struct pci_dev *d) +{ + struct resource *res; + int pos, size; + u32 *base; + + printk("PCI: Fixing up device %s\n", pci_name(d)); + + /* Fix up resource bases */ + for (pos = 0; pos < 6; pos++) { + res = &d->resource[pos]; + base = ((res->flags & IORESOURCE_IO) ? &pci_iobase : &pci_membase); + if (res->end) { + size = res->end - res->start + 1; + if (*base & (size - 1)) + *base = (*base + size) & ~(size - 1); + res->start = *base; + res->end = res->start + size - 1; + *base += size; + pci_write_config_dword(d, PCI_BASE_ADDRESS_0 + (pos << 2), res->start); + } + /* Fix up PCI bridge BAR0 only */ + if (d->bus->number == 0 && PCI_SLOT(d->devfn) == 0) + break; + } + /* Fix up interrupt lines */ + d->irq = ssb_mips_irq(extpci_core->dev) + 2; + pci_write_config_byte(d, PCI_INTERRUPT_LINE, d->irq); + + return 0; +} + +static void __init ssb_fixup_pcibridge(struct pci_dev *dev) +{ + if (dev->bus->number != 0 || PCI_SLOT(dev->devfn) != 0) + return; + + printk("PCI: fixing up bridge\n"); + + /* Enable PCI bridge bus mastering and memory space */ + pci_set_master(dev); + pcibios_enable_device(dev, ~0); + + /* Enable PCI bridge BAR1 prefetch and burst */ + pci_write_config_dword(dev, SSB_BAR1_CONTROL, 3); +} +DECLARE_PCI_FIXUP_EARLY(PCI_ANY_ID, PCI_ANY_ID, ssb_fixup_pcibridge); + +int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + return ssb_mips_irq(extpci_core->dev) + 2; +} + +static u32 get_cfgspace_addr(struct ssb_pcicore *pc, + unsigned int bus, unsigned int dev, + unsigned int func, unsigned int off) +{ + u32 addr = 0; + u32 tmp; + + if (unlikely(pc->cardbusmode && dev > 1)) + goto out; + if (bus == 0) { + /* Type 0 transaction */ + if (unlikely(dev >= SSB_PCI_SLOT_MAX)) + goto out; + /* Slide the window */ + tmp = SSB_PCICORE_SBTOPCI_CFG0; + tmp |= ((1 << (dev + 16)) & SSB_PCICORE_SBTOPCI1_MASK); + pcicore_write32(pc, SSB_PCICORE_SBTOPCI1, tmp); + /* Calculate the address */ + addr = SSB_PCI_CFG; + addr |= ((1 << (dev + 16)) & ~SSB_PCICORE_SBTOPCI1_MASK); + addr |= (func << 8); + addr |= (off & ~3); + } else { + /* Type 1 transaction */ + pcicore_write32(pc, SSB_PCICORE_SBTOPCI1, + SSB_PCICORE_SBTOPCI_CFG1); + /* Calculate the address */ + addr = SSB_PCI_CFG; + addr |= (bus << 16); + addr |= (dev << 11); + addr |= (func << 8); + addr |= (off & ~3); + } +out: + return addr; +} + +static int ssb_extpci_read_config(struct ssb_pcicore *pc, + unsigned int bus, unsigned int dev, + unsigned int func, unsigned int off, + void *buf, int len) +{ + int err = -EINVAL; + u32 addr, val; + void __iomem *mmio; + + assert(pc->hostmode); + if (unlikely(len != 1 && len != 2 && len != 4)) + goto out; + addr = get_cfgspace_addr(pc, bus, dev, func, off); + if (unlikely(!addr)) + goto out; + err = -ENOMEM; + mmio = ioremap_nocache(addr, len); + if (!mmio) + goto out; + + if (mips_busprobe(val, (u32 *) mmio)) { + val = 0xffffffff; + goto unmap; + } + + val = readl(mmio); + val >>= (8 * (off & 3)); + + switch (len) { + case 1: + *((u8 *)buf) = (u8)val; + break; + case 2: + *((u16 *)buf) = (u16)val; + break; + case 4: + *((u32 *)buf) = (u32)val; + break; + } + err = 0; +unmap: + iounmap(mmio); +out: + return err; +} + +static int ssb_extpci_write_config(struct ssb_pcicore *pc, + unsigned int bus, unsigned int dev, + unsigned int func, unsigned int off, + const void *buf, int len) +{ + int err = -EINVAL; + u32 addr, val = 0; + void __iomem *mmio; + + assert(pc->hostmode); + if (unlikely(len != 1 && len != 2 && len != 4)) + goto out; + addr = get_cfgspace_addr(pc, bus, dev, func, off); + if (unlikely(!addr)) + goto out; + err = -ENOMEM; + mmio = ioremap_nocache(addr, len); + if (!mmio) + goto out; + + if (mips_busprobe(val, (u32 *) mmio)) { + val = 0xffffffff; + goto unmap; + } + + switch (len) { + case 1: + val = readl(mmio); + val &= ~(0xFF << (8 * (off & 3))); + val |= *((const u8 *)buf) << (8 * (off & 3)); + break; + case 2: + val = readl(mmio); + val &= ~(0xFFFF << (8 * (off & 3))); + val |= *((const u16 *)buf) << (8 * (off & 3)); + break; + case 4: + val = *((const u32 *)buf); + break; + } + writel(*((const u32 *)buf), mmio); + + err = 0; +unmap: + iounmap(mmio); +out: + return err; +} + +static int ssb_pcicore_read_config(struct pci_bus *bus, unsigned int devfn, + int reg, int size, u32 *val) +{ + unsigned long flags; + int err; + + spin_lock_irqsave(&cfgspace_lock, flags); + err = ssb_extpci_read_config(extpci_core, bus->number, PCI_SLOT(devfn), + PCI_FUNC(devfn), reg, val, size); + spin_unlock_irqrestore(&cfgspace_lock, flags); + + return err ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL; +} + +static int ssb_pcicore_write_config(struct pci_bus *bus, unsigned int devfn, + int reg, int size, u32 val) +{ + unsigned long flags; + int err; + + spin_lock_irqsave(&cfgspace_lock, flags); + err = ssb_extpci_write_config(extpci_core, bus->number, PCI_SLOT(devfn), + PCI_FUNC(devfn), reg, &val, size); + spin_unlock_irqrestore(&cfgspace_lock, flags); + + return err ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops ssb_pcicore_pciops = { + .read = ssb_pcicore_read_config, + .write = ssb_pcicore_write_config, +}; + +static struct resource ssb_pcicore_mem_resource = { + .name = "SSB PCIcore external memory", + .start = SSB_PCI_DMA, + .end = (u32)SSB_PCI_DMA + (u32)SSB_PCI_DMA_SZ - 1, + .flags = IORESOURCE_MEM, +}; + +static struct resource ssb_pcicore_io_resource = { + .name = "SSB PCIcore external I/O", + .start = 0x100, + .end = 0x7FF, + .flags = IORESOURCE_IO, +}; + +static struct pci_controller ssb_pcicore_controller = { + .pci_ops = &ssb_pcicore_pciops, + .io_resource = &ssb_pcicore_io_resource, + .mem_resource = &ssb_pcicore_mem_resource, + .mem_offset = 0x24000000, +}; + +static void ssb_pcicore_init_hostmode(struct ssb_pcicore *pc) +{ + u32 val; + + if (extpci_core) { + WARN_ON(1); + return; + } + extpci_core = pc; + + ssb_dprintk(KERN_INFO PFX "PCIcore in host mode found\n"); + /* Reset devices on the external PCI bus */ + val = SSB_PCICORE_CTL_RST_OE; + val |= SSB_PCICORE_CTL_CLK_OE; + pcicore_write32(pc, SSB_PCICORE_CTL, val); + val |= SSB_PCICORE_CTL_CLK; /* Clock on */ + pcicore_write32(pc, SSB_PCICORE_CTL, val); + udelay(150); + val |= SSB_PCICORE_CTL_RST; /* Deassert RST# */ + pcicore_write32(pc, SSB_PCICORE_CTL, val); + udelay(1); + + //TODO cardbus mode + + /* 64MB I/O window */ + pcicore_write32(pc, SSB_PCICORE_SBTOPCI0, + SSB_PCICORE_SBTOPCI_IO); + /* 64MB config space */ + pcicore_write32(pc, SSB_PCICORE_SBTOPCI1, + SSB_PCICORE_SBTOPCI_CFG0); + /* 1GB memory window */ + pcicore_write32(pc, SSB_PCICORE_SBTOPCI2, + SSB_PCICORE_SBTOPCI_MEM | SSB_PCI_DMA); + + /* Enable PCI bridge BAR0 prefetch and burst */ + val = PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY; + ssb_extpci_write_config(pc, 0, 0, 0, PCI_COMMAND, &val, 2); + /* Clear error conditions */ + val = 0; + ssb_extpci_write_config(pc, 0, 0, 0, PCI_STATUS, &val, 2); + + /* Enable PCI interrupts */ + pcicore_write32(pc, SSB_PCICORE_IMASK, + SSB_PCICORE_IMASK_INTA); + + /* Ok, ready to run, register it to the system. + * The following needs change, if we want to port hostmode + * to non-MIPS platform. */ + set_io_port_base((unsigned long)ioremap_nocache(SSB_PCI_MEM, 0x04000000)); + register_pci_controller(&ssb_pcicore_controller); +} + +static int pcicore_is_in_hostmode(struct ssb_pcicore *pc) +{ + struct ssb_bus *bus = pc->dev->bus; + u16 chipid_top; + u32 tmp; + + chipid_top = (bus->chip_id & 0xFF00); + if (chipid_top != 0x4700 && + chipid_top != 0x5300) + return 0; + + if (bus->sprom.r1.boardflags_lo & SSB_PCICORE_BFL_NOPCI) + return 0; + + /* The 200-pin BCM4712 package does not bond out PCI. Even when + * PCI is bonded out, some boards may leave the pins floating. */ + if (bus->chip_id == 0x4712) { + if (bus->chip_package == SSB_CHIPPACK_BCM4712S) + return 0; + if (bus->chip_package == SSB_CHIPPACK_BCM4712M) + return 0; + } + if (bus->chip_id == 0x5350) + return 0; + + return !mips_busprobe(tmp, (u32 *) (bus->mmio + (pc->dev->core_index * SSB_CORE_SIZE))); +} +#endif /* CONFIG_SSB_PCICORE_HOSTMODE */ + + +/************************************************** + * Generic and Clientmode operation code. + **************************************************/ + +static void ssb_pcicore_init_clientmode(struct ssb_pcicore *pc) +{ + /* Disable PCI interrupts. */ + ssb_write32(pc->dev, SSB_INTVEC, 0); +} + +void ssb_pcicore_init(struct ssb_pcicore *pc) +{ + struct ssb_device *dev = pc->dev; + struct ssb_bus *bus; + + if (!dev) + return; + bus = dev->bus; + if (!ssb_device_is_enabled(dev)) + ssb_device_enable(dev, 0); + +#ifdef CONFIG_SSB_PCICORE_HOSTMODE + pc->hostmode = pcicore_is_in_hostmode(pc); + if (pc->hostmode) + ssb_pcicore_init_hostmode(pc); +#endif /* CONFIG_SSB_PCICORE_HOSTMODE */ + if (!pc->hostmode) + ssb_pcicore_init_clientmode(pc); +} + +static u32 ssb_pcie_read(struct ssb_pcicore *pc, u32 address) +{ + pcicore_write32(pc, 0x130, address); + return pcicore_read32(pc, 0x134); +} + +static void ssb_pcie_write(struct ssb_pcicore *pc, u32 address, u32 data) +{ + pcicore_write32(pc, 0x130, address); + pcicore_write32(pc, 0x134, data); +} + +static void ssb_pcie_mdio_write(struct ssb_pcicore *pc, u8 device, + u8 address, u16 data) +{ + const u16 mdio_control = 0x128; + const u16 mdio_data = 0x12C; + u32 v; + int i; + + v = 0x80; /* Enable Preamble Sequence */ + v |= 0x2; /* MDIO Clock Divisor */ + pcicore_write32(pc, mdio_control, v); + + v = (1 << 30); /* Start of Transaction */ + v |= (1 << 28); /* Write Transaction */ + v |= (1 << 17); /* Turnaround */ + v |= (u32)device << 22; + v |= (u32)address << 18; + v |= data; + pcicore_write32(pc, mdio_data, v); + udelay(10); + for (i = 0; i < 10; i++) { + v = pcicore_read32(pc, mdio_control); + if (v & 0x100 /* Trans complete */) + break; + msleep(1); + } + pcicore_write32(pc, mdio_control, 0); +} + +static void ssb_broadcast_value(struct ssb_device *dev, + u32 address, u32 data) +{ + /* This is used for both, PCI and ChipCommon core, so be careful. */ + BUILD_BUG_ON(SSB_PCICORE_BCAST_ADDR != SSB_CHIPCO_BCAST_ADDR); + BUILD_BUG_ON(SSB_PCICORE_BCAST_DATA != SSB_CHIPCO_BCAST_DATA); + + ssb_write32(dev, SSB_PCICORE_BCAST_ADDR, address); + ssb_read32(dev, SSB_PCICORE_BCAST_ADDR); /* flush */ + ssb_write32(dev, SSB_PCICORE_BCAST_DATA, data); + ssb_read32(dev, SSB_PCICORE_BCAST_DATA); /* flush */ +} + +static void ssb_commit_settings(struct ssb_bus *bus) +{ + struct ssb_device *dev; + + dev = bus->chipco.dev ? bus->chipco.dev : bus->pcicore.dev; + assert(dev); + /* This forces an update of the cached registers. */ + ssb_broadcast_value(dev, 0xFD8, 0); +} + +int ssb_pcicore_dev_irqvecs_enable(struct ssb_pcicore *pc, + struct ssb_device *dev) +{ + struct ssb_device *pdev = pc->dev; + struct ssb_bus *bus; + int err = 0; + u32 tmp; + + might_sleep(); + + if (!pdev) + goto out; + bus = pdev->bus; + + /* Enable interrupts for this device. */ + if (bus->host_pci && + ((pdev->id.revision >= 6) || (pdev->id.coreid == SSB_DEV_PCIE))) { + u32 coremask; + + /* Calculate the "coremask" for the device. */ + coremask = (1 << dev->core_index); + + err = pci_read_config_dword(bus->host_pci, SSB_PCI_IRQMASK, &tmp); + if (err) + goto out; + tmp |= coremask << 8; + err = pci_write_config_dword(bus->host_pci, SSB_PCI_IRQMASK, tmp); + if (err) + goto out; + } else { + u32 intvec; + + intvec = ssb_read32(pdev, SSB_INTVEC); + tmp = ssb_read32(dev, SSB_TPSFLAG); + tmp &= SSB_TPSFLAG_BPFLAG; + intvec |= tmp; + ssb_write32(pdev, SSB_INTVEC, intvec); + } + + /* Setup PCIcore operation. */ + if (pc->setup_done) + goto out; + if (pdev->id.coreid == SSB_DEV_PCI) { + tmp = pcicore_read32(pc, SSB_PCICORE_SBTOPCI2); + tmp |= SSB_PCICORE_SBTOPCI_PREF; + tmp |= SSB_PCICORE_SBTOPCI_BURST; + pcicore_write32(pc, SSB_PCICORE_SBTOPCI2, tmp); + + if (pdev->id.revision < 5) { + tmp = ssb_read32(pdev, SSB_IMCFGLO); + tmp &= ~SSB_IMCFGLO_SERTO; + tmp |= 2; + tmp &= ~SSB_IMCFGLO_REQTO; + tmp |= 3 << SSB_IMCFGLO_REQTO_SHIFT; + ssb_write32(pdev, SSB_IMCFGLO, tmp); + ssb_commit_settings(bus); + } else if (pdev->id.revision >= 11) { + tmp = pcicore_read32(pc, SSB_PCICORE_SBTOPCI2); + tmp |= SSB_PCICORE_SBTOPCI_MRM; + pcicore_write32(pc, SSB_PCICORE_SBTOPCI2, tmp); + } + } else { + assert(pdev->id.coreid == SSB_DEV_PCIE); + //TODO: Better make defines for all these magic PCIE values. + if ((pdev->id.revision == 0) || (pdev->id.revision == 1)) { + /* TLP Workaround register. */ + tmp = ssb_pcie_read(pc, 0x4); + tmp |= 0x8; + ssb_pcie_write(pc, 0x4, tmp); + } + if (pdev->id.revision == 0) { + const u8 serdes_rx_device = 0x1F; + + ssb_pcie_mdio_write(pc, serdes_rx_device, + 2 /* Timer */, 0x8128); + ssb_pcie_mdio_write(pc, serdes_rx_device, + 6 /* CDR */, 0x0100); + ssb_pcie_mdio_write(pc, serdes_rx_device, + 7 /* CDR BW */, 0x1466); + } else if (pdev->id.revision == 1) { + /* DLLP Link Control register. */ + tmp = ssb_pcie_read(pc, 0x100); + tmp |= 0x40; + ssb_pcie_write(pc, 0x100, tmp); + } + } + pc->setup_done = 1; +out: + return err; +} +EXPORT_SYMBOL(ssb_pcicore_dev_irqvecs_enable); diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c new file mode 100644 index 0000000..682aa5c --- /dev/null +++ b/drivers/ssb/main.c @@ -0,0 +1,1047 @@ +/* + * Sonics Silicon Backplane + * Subsystem core + * + * Copyright 2005, Broadcom Corporation + * Copyright 2006, 2007, Michael Buesch + * + * Licensed under the GNU/GPL. See COPYING for details. + */ + +#include "ssb_private.h" + +#include +#include +#include + +#ifdef CONFIG_SSB_PCIHOST +# include +#endif + +#ifdef CONFIG_SSB_PCMCIAHOST +# include +# include +# include +# include +#endif + + +MODULE_DESCRIPTION("Sonics Silicon Backplane driver"); +MODULE_LICENSE("GPL"); + + +static LIST_HEAD(attach_queue); +static LIST_HEAD(buses); +static int nr_buses; +static DEFINE_MUTEX(buses_mutex); + +static void ssb_buses_lock(void); +static void ssb_buses_unlock(void); + + +#ifdef CONFIG_SSB_PCIHOST +struct ssb_bus * ssb_pci_dev_to_bus(struct pci_dev *pdev) +{ + struct ssb_bus *bus; + + ssb_buses_lock(); + list_for_each_entry(bus, &buses, list) { + if (bus->bustype == SSB_BUSTYPE_PCI && + bus->host_pci == pdev) + goto found; + } + bus = NULL; +found: + ssb_buses_unlock(); + + return bus; +} +#endif /* CONFIG_SSB_PCIHOST */ + +static struct ssb_device * ssb_device_get(struct ssb_device *dev) +{ + if (dev) + get_device(dev->dev); + return dev; +} + +static void ssb_device_put(struct ssb_device *dev) +{ + if (dev) + put_device(dev->dev); +} + +static int ssb_bus_resume(struct ssb_bus *bus) +{ + int err; + + ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 1); + err = ssb_pcmcia_init(bus); + if (err) { + /* No need to disable XTAL, as we don't have one on PCMCIA. */ + return err; + } + ssb_chipco_resume(&bus->chipco); + + return 0; +} + +static int ssb_device_resume(struct device *dev) +{ + struct ssb_device *ssb_dev = dev_to_ssb_dev(dev); + struct ssb_driver *ssb_drv; + struct ssb_bus *bus; + int err = 0; + + bus = ssb_dev->bus; + if (bus->suspend_cnt == bus->nr_devices) { + err = ssb_bus_resume(bus); + if (err) + return err; + } + bus->suspend_cnt--; + if (dev->driver) { + ssb_drv = drv_to_ssb_drv(dev->driver); + if (ssb_drv && ssb_drv->resume) + err = ssb_drv->resume(ssb_dev); + if (err) + goto out; + } +out: + return err; +} + +static void ssb_bus_suspend(struct ssb_bus *bus, pm_message_t state) +{ + ssb_chipco_suspend(&bus->chipco, state); + ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 0); + + /* Reset HW state information in memory, so that HW is + * completely reinitialized on resume. */ + bus->mapped_device = NULL; +#ifdef CONFIG_SSB_DRIVER_PCICORE + bus->pcicore.setup_done = 0; +#endif +} + +static int ssb_device_suspend(struct device *dev, pm_message_t state) +{ + struct ssb_device *ssb_dev = dev_to_ssb_dev(dev); + struct ssb_driver *ssb_drv; + struct ssb_bus *bus; + int err = 0; + + if (dev->driver) { + ssb_drv = drv_to_ssb_drv(dev->driver); + if (ssb_drv && ssb_drv->suspend) + err = ssb_drv->suspend(ssb_dev, state); + if (err) + goto out; + } + + bus = ssb_dev->bus; + bus->suspend_cnt++; + if (bus->suspend_cnt == bus->nr_devices) { + /* All devices suspended. Shutdown the bus. */ + ssb_bus_suspend(bus, state); + } + +out: + return err; +} + +#ifdef CONFIG_SSB_PCIHOST +int ssb_devices_freeze(struct ssb_bus *bus) +{ + struct ssb_device *dev; + struct ssb_driver *drv; + int err = 0; + int i; + pm_message_t state = PMSG_FREEZE; + + for (i = 0; i < bus->nr_devices; i++) { + dev = &(bus->devices[i]); + if (!dev->dev->driver) + continue; + if (!device_is_registered(dev->dev)) + continue; + drv = drv_to_ssb_drv(dev->dev->driver); + if (drv && drv->suspend) { + err = drv->suspend(dev, state); + if (err) + goto out; + } + } +out: + return err; +} + +int ssb_devices_thaw(struct ssb_bus *bus) +{ + struct ssb_device *dev; + struct ssb_driver *drv; + int err = 0; + int i; + + for (i = 0; i < bus->nr_devices; i++) { + dev = &(bus->devices[i]); + if (!dev->dev->driver) + continue; + if (!device_is_registered(dev->dev)) + continue; + drv = drv_to_ssb_drv(dev->dev->driver); + if (drv && drv->resume) { + err = drv->resume(dev); + if (err) + goto out; + } + } +out: + return err; +} +#endif /* CONFIG_SSB_PCIHOST */ + +static void ssb_device_shutdown(struct device *dev) +{ + struct ssb_device *ssb_dev = dev_to_ssb_dev(dev); + struct ssb_driver *ssb_drv; + + if (!dev->driver) + return; + ssb_drv = drv_to_ssb_drv(dev->driver); + if (ssb_drv && ssb_drv->shutdown) + ssb_drv->shutdown(ssb_dev); +} + +static int ssb_device_remove(struct device *dev) +{ + struct ssb_device *ssb_dev = dev_to_ssb_dev(dev); + struct ssb_driver *ssb_drv = drv_to_ssb_drv(dev->driver); + + if (ssb_drv && ssb_drv->remove) + ssb_drv->remove(ssb_dev); + ssb_device_put(ssb_dev); + + return 0; +} + +static int ssb_device_probe(struct device *dev) +{ + struct ssb_device *ssb_dev = dev_to_ssb_dev(dev); + struct ssb_driver *ssb_drv = drv_to_ssb_drv(dev->driver); + int err = 0; + + ssb_device_get(ssb_dev); + if (ssb_drv && ssb_drv->probe) + err = ssb_drv->probe(ssb_dev, &ssb_dev->id); + if (err) + ssb_device_put(ssb_dev); + + return err; +} + +static int ssb_match_devid(const struct ssb_device_id *tabid, + const struct ssb_device_id *devid) +{ + if ((tabid->vendor != devid->vendor) && + tabid->vendor != SSB_ANY_VENDOR) + return 0; + if ((tabid->coreid != devid->coreid) && + tabid->coreid != SSB_ANY_ID) + return 0; + if ((tabid->revision != devid->revision) && + tabid->revision != SSB_ANY_REV) + return 0; + return 1; +} + +static int ssb_bus_match(struct device *dev, struct device_driver *drv) +{ + struct ssb_device *ssb_dev = dev_to_ssb_dev(dev); + struct ssb_driver *ssb_drv = drv_to_ssb_drv(drv); + const struct ssb_device_id *id; + + for (id = ssb_drv->id_table; + id->vendor || id->coreid || id->revision; + id++) { + if (ssb_match_devid(id, &ssb_dev->id)) + return 1; /* found */ + } + + return 0; +} + +static struct bus_type ssb_bustype = { + .name = NULL, /* Intentionally NULL to indicate early boot */ + .match = ssb_bus_match, + .probe = ssb_device_probe, + .remove = ssb_device_remove, + .shutdown = ssb_device_shutdown, + .suspend = ssb_device_suspend, + .resume = ssb_device_resume, +}; + +#define is_early_boot() (ssb_bustype.name == NULL) + +static void ssb_buses_lock(void) +{ + if (!is_early_boot()) + mutex_lock(&buses_mutex); +} + +static void ssb_buses_unlock(void) +{ + if (!is_early_boot()) + mutex_unlock(&buses_mutex); +} + +static void ssb_devices_unregister(struct ssb_bus *bus) +{ + struct ssb_device *sdev; + int i; + + for (i = bus->nr_devices - 1; i >= 0; i--) { + sdev = &(bus->devices[i]); + if (sdev->dev) + device_unregister(sdev->dev); + } +} + +void ssb_bus_unregister(struct ssb_bus *bus) +{ + ssb_buses_lock(); + ssb_devices_unregister(bus); + list_del(&bus->list); + ssb_buses_unlock(); + + /* ssb_pcmcia_exit(bus); */ + ssb_pci_exit(bus); + ssb_iounmap(bus); +} +EXPORT_SYMBOL(ssb_bus_unregister); + +static void ssb_release_dev(struct device *dev) +{ + struct __ssb_dev_wrapper *devwrap; + + devwrap = container_of(dev, struct __ssb_dev_wrapper, dev); + kfree(devwrap); +} + +static int ssb_devices_register(struct ssb_bus *bus) +{ + struct ssb_device *sdev; + struct device *dev; + struct __ssb_dev_wrapper *devwrap; + int i, err = 0; + int dev_idx = 0; + + for (i = 0; i < bus->nr_devices; i++) { + sdev = &(bus->devices[i]); + + /* We don't register SSB-system devices to the kernel, + * as the drivers for them are built into SSB. */ + switch (sdev->id.coreid) { + case SSB_DEV_CHIPCOMMON: + case SSB_DEV_PCI: + case SSB_DEV_PCIE: + case SSB_DEV_PCMCIA: + case SSB_DEV_MIPS: + case SSB_DEV_MIPS_3302: + case SSB_DEV_EXTIF: + continue; + } + + devwrap = kzalloc(sizeof(*devwrap), GFP_KERNEL); + if (!devwrap) { + ssb_printk(KERN_ERR PFX + "Could not allocate device\n"); + err = -ENOMEM; + goto error; + } + dev = &devwrap->dev; + devwrap->sdev = sdev; + + dev->release = ssb_release_dev; + dev->bus = &ssb_bustype; + snprintf(dev->bus_id, sizeof(dev->bus_id), + "ssb%d:%d", bus->busnumber, dev_idx); + + switch (bus->bustype) { + case SSB_BUSTYPE_PCI: +#ifdef CONFIG_SSB_PCIHOST + sdev->irq = bus->host_pci->irq; + dev->parent = &bus->host_pci->dev; +#endif + break; + case SSB_BUSTYPE_PCMCIA: +#ifdef CONFIG_SSB_PCMCIAHOST + dev->parent = &bus->host_pcmcia->dev; +#endif + break; + case SSB_BUSTYPE_SSB: + break; + } + + err = device_register(dev); + if (err) { + ssb_printk(KERN_ERR PFX + "Could not register %s\n", + dev->bus_id); + kfree(devwrap); + goto error; + } + sdev->dev = dev; + dev_idx++; + } + + return 0; +error: + /* Unwind the already registered devices. */ + ssb_devices_unregister(bus); + return err; +} + +/* Needs ssb_buses_lock() */ +static int ssb_attach_queued_buses(void) +{ + struct ssb_bus *bus, *n; + int err = 0; + int drop_them_all = 0; + + list_for_each_entry_safe(bus, n, &attach_queue, list) { + if (drop_them_all) { + list_del(&bus->list); + continue; + } + /* Can't init the PCIcore in ssb_bus_register(), as that + * is too early in boot for embedded systems + * (no udelay() available). So do it here in attach stage. + */ + ssb_pcicore_init(&bus->pcicore); + + err = ssb_devices_register(bus); + if (err) { + drop_them_all = 1; + list_del(&bus->list); + continue; + } + list_move_tail(&bus->list, &buses); + } + + return err; +} + +static void ssb_get_boardtype(struct ssb_bus *bus) +{//FIXME for pcmcia? + if (bus->bustype != SSB_BUSTYPE_PCI) { + /* Must set board_vendor, board_type and board_rev + * before calling ssb_bus_*_register() */ + assert(bus->board_vendor && bus->board_type); + return; + } + ssb_pci_get_boardtype(bus); +} + +static u16 ssb_ssb_read16(struct ssb_device *dev, u16 offset) +{ + struct ssb_bus *bus = dev->bus; + + offset += dev->core_index * SSB_CORE_SIZE; + return readw(bus->mmio + offset); +} + +static u32 ssb_ssb_read32(struct ssb_device *dev, u16 offset) +{ + struct ssb_bus *bus = dev->bus; + + offset += dev->core_index * SSB_CORE_SIZE; + return readl(bus->mmio + offset); +} + +static void ssb_ssb_write16(struct ssb_device *dev, u16 offset, u16 value) +{ + struct ssb_bus *bus = dev->bus; + + offset += dev->core_index * SSB_CORE_SIZE; + writew(value, bus->mmio + offset); +} + +static void ssb_ssb_write32(struct ssb_device *dev, u16 offset, u32 value) +{ + struct ssb_bus *bus = dev->bus; + + offset += dev->core_index * SSB_CORE_SIZE; + writel(value, bus->mmio + offset); +} + +static const struct ssb_bus_ops ssb_ssb_ops = { + .read16 = ssb_ssb_read16, + .read32 = ssb_ssb_read32, + .write16 = ssb_ssb_write16, + .write32 = ssb_ssb_write32, +}; + +static int ssb_bus_register(struct ssb_bus *bus, + unsigned long baseaddr) +{ + int err; + + spin_lock_init(&bus->bar_lock); + INIT_LIST_HEAD(&bus->list); + + ssb_get_boardtype(bus); + /* Powerup the bus */ + err = ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 1); + if (err) + goto out; + ssb_buses_lock(); + bus->busnumber = nr_buses; + /* Scan for devices (cores) */ + err = ssb_bus_scan(bus, baseaddr); + if (err) + goto err_disable_xtal; + + /* Init PCI-host device (if any) */ + err = ssb_pci_init(bus); + if (err) + goto err_unmap; + /* Init PCMCIA-host device (if any) */ + err = ssb_pcmcia_init(bus); + if (err) + goto err_pci_exit; + + /* Initialize basic system devices (if available) */ + ssb_chipcommon_init(&bus->chipco); + ssb_mipscore_init(&bus->mipscore); + + /* Queue it for attach */ + list_add_tail(&bus->list, &attach_queue); + if (!is_early_boot()) { + /* This is not early boot, so we must attach the bus now */ + err = ssb_attach_queued_buses(); + if (err) + goto err_dequeue; + } + nr_buses++; + ssb_buses_unlock(); + +out: + return err; + +err_dequeue: + list_del(&bus->list); +/* err_pcmcia_exit: */ +/* ssb_pcmcia_exit(bus); */ +err_pci_exit: + ssb_pci_exit(bus); +err_unmap: + ssb_iounmap(bus); +err_disable_xtal: + ssb_buses_unlock(); + ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 0); + return err; +} + +#ifdef CONFIG_SSB_PCIHOST +int ssb_bus_pcibus_register(struct ssb_bus *bus, + struct pci_dev *host_pci) +{ + int err; + + ssb_printk(KERN_INFO PFX "Sonics Silicon Backplane found on " + "PCI device %s\n", bus->host_pci->dev.bus_id); + + bus->bustype = SSB_BUSTYPE_PCI; + bus->host_pci = host_pci; + bus->ops = &ssb_pci_ops; + + err = ssb_bus_register(bus, 0); + + return err; +} +EXPORT_SYMBOL(ssb_bus_pcibus_register); +#endif /* CONFIG_SSB_PCIHOST */ + +#ifdef CONFIG_SSB_PCMCIAHOST +int ssb_bus_pcmciabus_register(struct ssb_bus *bus, + struct pcmcia_device *pcmcia_dev, + unsigned long baseaddr, + void (*fill_sprom)(struct ssb_sprom *sprom)) +{ + int err; + + ssb_printk(KERN_INFO PFX "Sonics Silicon Backplane found on " + "PCMCIA device %s\n", bus->host_pcmcia->devname); + + bus->bustype = SSB_BUSTYPE_PCMCIA; + bus->host_pcmcia = pcmcia_dev; + bus->ops = &ssb_pcmcia_ops; + fill_sprom(&bus->sprom); + + err = ssb_bus_register(bus, baseaddr); + + return err; +} +EXPORT_SYMBOL(ssb_bus_pcmciabus_register); +#endif /* CONFIG_SSB_PCMCIAHOST */ + +int ssb_bus_ssbbus_register(struct ssb_bus *bus, + unsigned long baseaddr, + void (*fill_sprom)(struct ssb_sprom *sprom)) +{ + int err; + + ssb_printk(KERN_INFO PFX "Sonics Silicon Backplane found at " + "address 0x%08lX\n", baseaddr); + + bus->bustype = SSB_BUSTYPE_SSB; + bus->ops = &ssb_ssb_ops; + fill_sprom(&bus->sprom); + err = ssb_bus_register(bus, baseaddr); + + return err; +} + +int __ssb_driver_register(struct ssb_driver *drv, struct module *owner) +{ + drv->drv.name = drv->name; + drv->drv.bus = &ssb_bustype; + drv->drv.owner = owner; + + return driver_register(&drv->drv); +} +EXPORT_SYMBOL(__ssb_driver_register); + +void ssb_driver_unregister(struct ssb_driver *drv) +{ + driver_unregister(&drv->drv); +} +EXPORT_SYMBOL(ssb_driver_unregister); + +void ssb_set_devtypedata(struct ssb_device *dev, void *data) +{ + struct ssb_bus *bus = dev->bus; + struct ssb_device *ent; + int i; + + for (i = 0; i < bus->nr_devices; i++) { + ent = &(bus->devices[i]); + if (ent->id.vendor != dev->id.vendor) + continue; + if (ent->id.coreid != dev->id.coreid) + continue; + + ent->devtypedata = data; + } +} +EXPORT_SYMBOL(ssb_set_devtypedata); + +static u32 clkfactor_f6_resolve(u32 v) +{ + /* map the magic values */ + switch (v) { + case SSB_CHIPCO_CLK_F6_2: + return 2; + case SSB_CHIPCO_CLK_F6_3: + return 3; + case SSB_CHIPCO_CLK_F6_4: + return 4; + case SSB_CHIPCO_CLK_F6_5: + return 5; + case SSB_CHIPCO_CLK_F6_6: + return 6; + case SSB_CHIPCO_CLK_F6_7: + return 7; + } + return 0; +} + +/* Calculate the speed the backplane would run at a given set of clockcontrol values */ +u32 ssb_calc_clock_rate(u32 plltype, u32 n, u32 m) +{ + u32 n1, n2, clock, m1, m2, m3, mc; + + n1 = (n & SSB_CHIPCO_CLK_N1); + n2 = ((n & SSB_CHIPCO_CLK_N2) >> SSB_CHIPCO_CLK_N2_SHIFT); + + switch (plltype) { + case SSB_PLLTYPE_6: /* 100/200 or 120/240 only */ + if (m & SSB_CHIPCO_CLK_T6_MMASK) + return SSB_CHIPCO_CLK_T6_M0; + return SSB_CHIPCO_CLK_T6_M1; + case SSB_PLLTYPE_1: /* 48Mhz base, 3 dividers */ + case SSB_PLLTYPE_3: /* 25Mhz, 2 dividers */ + case SSB_PLLTYPE_4: /* 48Mhz, 4 dividers */ + case SSB_PLLTYPE_7: /* 25Mhz, 4 dividers */ + n1 = clkfactor_f6_resolve(n1); + n2 += SSB_CHIPCO_CLK_F5_BIAS; + break; + case SSB_PLLTYPE_2: /* 48Mhz, 4 dividers */ + n1 += SSB_CHIPCO_CLK_T2_BIAS; + n2 += SSB_CHIPCO_CLK_T2_BIAS; + assert((n1 >= 2) && (n1 <= 7)); + assert((n2 >= 5) && (n2 <= 23)); + break; + case SSB_PLLTYPE_5: /* 25Mhz, 4 dividers */ + return 100000000; + default: + assert(0); + } + + switch (plltype) { + case SSB_PLLTYPE_3: /* 25Mhz, 2 dividers */ + case SSB_PLLTYPE_7: /* 25Mhz, 4 dividers */ + clock = SSB_CHIPCO_CLK_BASE2 * n1 * n2; + break; + default: + clock = SSB_CHIPCO_CLK_BASE1 * n1 * n2; + } + if (!clock) + return 0; + + m1 = (m & SSB_CHIPCO_CLK_M1); + m2 = ((m & SSB_CHIPCO_CLK_M2) >> SSB_CHIPCO_CLK_M2_SHIFT); + m3 = ((m & SSB_CHIPCO_CLK_M3) >> SSB_CHIPCO_CLK_M3_SHIFT); + mc = ((m & SSB_CHIPCO_CLK_MC) >> SSB_CHIPCO_CLK_MC_SHIFT); + + switch (plltype) { + case SSB_PLLTYPE_1: /* 48Mhz base, 3 dividers */ + case SSB_PLLTYPE_3: /* 25Mhz, 2 dividers */ + case SSB_PLLTYPE_4: /* 48Mhz, 4 dividers */ + case SSB_PLLTYPE_7: /* 25Mhz, 4 dividers */ + m1 = clkfactor_f6_resolve(m1); + if ((plltype == SSB_PLLTYPE_1) || + (plltype == SSB_PLLTYPE_3)) + m2 += SSB_CHIPCO_CLK_F5_BIAS; + else + m2 = clkfactor_f6_resolve(m2); + m3 = clkfactor_f6_resolve(m3); + + switch (mc) { + case SSB_CHIPCO_CLK_MC_BYPASS: + return clock; + case SSB_CHIPCO_CLK_MC_M1: + return (clock / m1); + case SSB_CHIPCO_CLK_MC_M1M2: + return (clock / (m1 * m2)); + case SSB_CHIPCO_CLK_MC_M1M2M3: + return (clock / (m1 * m2 * m3)); + case SSB_CHIPCO_CLK_MC_M1M3: + return (clock / (m1 * m3)); + } + return 0; + case SSB_PLLTYPE_2: + m1 += SSB_CHIPCO_CLK_T2_BIAS; + m2 += SSB_CHIPCO_CLK_T2M2_BIAS; + m3 += SSB_CHIPCO_CLK_T2_BIAS; + assert((m1 >= 2) && (m1 <= 7)); + assert((m2 >= 3) && (m2 <= 10)); + assert((m3 >= 2) && (m3 <= 7)); + + if (!(mc & SSB_CHIPCO_CLK_T2MC_M1BYP)) + clock /= m1; + if (!(mc & SSB_CHIPCO_CLK_T2MC_M2BYP)) + clock /= m2; + if (!(mc & SSB_CHIPCO_CLK_T2MC_M3BYP)) + clock /= m3; + return clock; + default: + assert(0); + } + return 0; +} + +/* Get the current speed the backplane is running at */ +u32 ssb_clockspeed(struct ssb_bus *bus) +{ + u32 rate; + u32 plltype; + u32 clkctl_n, clkctl_m; + + //TODO if EXTIF: PLLTYPE == 1, read n from clockcontrol_n, m from clockcontrol_sb + + if (bus->chipco.dev) { + ssb_chipco_get_clockcontrol(&bus->chipco, &plltype, + &clkctl_n, &clkctl_m); + } else + return 0; + + if (bus->chip_id == 0x5365) { + rate = 100000000; + } else { + rate = ssb_calc_clock_rate(plltype, clkctl_n, clkctl_m); + if (plltype == SSB_PLLTYPE_3) /* 25Mhz, 2 dividers */ + rate /= 2; + } + + return rate; +} +EXPORT_SYMBOL(ssb_clockspeed); + +static u32 ssb_tmslow_reject_bitmask(struct ssb_device *dev) +{ + /* The REJECT bit changed position in TMSLOW between + * Backplane revisions. */ + switch (ssb_read32(dev, SSB_IDLOW) & SSB_IDLOW_SSBREV) { + case SSB_IDLOW_SSBREV_22: + return SSB_TMSLOW_REJECT_22; + case SSB_IDLOW_SSBREV_23: + return SSB_TMSLOW_REJECT_23; + default: + assert(0); + } + return (SSB_TMSLOW_REJECT_22 | SSB_TMSLOW_REJECT_23); +} + +int ssb_device_is_enabled(struct ssb_device *dev) +{ + u32 val; + u32 reject; + + reject = ssb_tmslow_reject_bitmask(dev); + val = ssb_read32(dev, SSB_TMSLOW); + val &= SSB_TMSLOW_CLOCK | SSB_TMSLOW_RESET | reject; + + return (val == SSB_TMSLOW_CLOCK); +} +EXPORT_SYMBOL(ssb_device_is_enabled); + +void ssb_device_enable(struct ssb_device *dev, u32 core_specific_flags) +{ + u32 val; + + ssb_device_disable(dev, core_specific_flags); + ssb_write32(dev, SSB_TMSLOW, + SSB_TMSLOW_RESET | SSB_TMSLOW_CLOCK | + SSB_TMSLOW_FGC | core_specific_flags); + /* flush */ + ssb_read32(dev, SSB_TMSLOW); + udelay(1); + + /* Clear SERR if set. This is a hw bug workaround. */ + if (ssb_read32(dev, SSB_TMSHIGH) & SSB_TMSHIGH_SERR) + ssb_write32(dev, SSB_TMSHIGH, 0); + + val = ssb_read32(dev, SSB_IMSTATE); + if (val & (SSB_IMSTATE_IBE | SSB_IMSTATE_TO)) { + val &= ~(SSB_IMSTATE_IBE | SSB_IMSTATE_TO); + ssb_write32(dev, SSB_IMSTATE, val); + } + + ssb_write32(dev, SSB_TMSLOW, + SSB_TMSLOW_CLOCK | SSB_TMSLOW_FGC | + core_specific_flags); + /* flush */ + ssb_read32(dev, SSB_TMSLOW); + udelay(1); + + ssb_write32(dev, SSB_TMSLOW, SSB_TMSLOW_CLOCK | + core_specific_flags); + /* flush */ + ssb_read32(dev, SSB_TMSLOW); + udelay(1); +} +EXPORT_SYMBOL(ssb_device_enable); + +static int ssb_wait_bit(struct ssb_device *dev, u16 reg, u32 bitmask, + int timeout, int set) +{ + int i; + u32 val; + + for (i = 0; i < timeout; i++) { + val = ssb_read32(dev, reg); + if (set) { + if (val & bitmask) + return 0; + } else { + if (!(val & bitmask)) + return 0; + } + udelay(10); + } + printk(KERN_ERR PFX "Timeout waiting for bitmask %08X on " + "register %04X to %s.\n", + bitmask, reg, (set ? "set" : "clear")); + + return -ETIMEDOUT; +} + +void ssb_device_disable(struct ssb_device *dev, u32 core_specific_flags) +{ + u32 reject; + + if (ssb_read32(dev, SSB_TMSLOW) & SSB_TMSLOW_RESET) + return; + + reject = ssb_tmslow_reject_bitmask(dev); + ssb_write32(dev, SSB_TMSLOW, reject | SSB_TMSLOW_CLOCK); + ssb_wait_bit(dev, SSB_TMSLOW, reject, 1000, 1); + ssb_wait_bit(dev, SSB_TMSHIGH, SSB_TMSHIGH_BUSY, 1000, 0); + ssb_write32(dev, SSB_TMSLOW, + SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | + reject | SSB_TMSLOW_RESET | + core_specific_flags); + /* flush */ + ssb_read32(dev, SSB_TMSLOW); + udelay(1); + + ssb_write32(dev, SSB_TMSLOW, + reject | SSB_TMSLOW_RESET | + core_specific_flags); + /* flush */ + ssb_read32(dev, SSB_TMSLOW); + udelay(1); +} +EXPORT_SYMBOL(ssb_device_disable); + +u32 ssb_dma_translation(struct ssb_device *dev) +{ + switch(dev->bus->bustype) { + case SSB_BUSTYPE_SSB: + return 0; + case SSB_BUSTYPE_PCI: + case SSB_BUSTYPE_PCMCIA: + return SSB_PCI_DMA; + } + return 0; +} +EXPORT_SYMBOL(ssb_dma_translation); + +int ssb_dma_set_mask(struct ssb_device *ssb_dev, u64 mask) +{ + struct device *dev = ssb_dev->dev; + +#ifdef CONFIG_SSB_PCIHOST + if (ssb_dev->bus->bustype == SSB_BUSTYPE_PCI && + !dma_supported(dev, mask)) + return -EIO; +#endif + dev->coherent_dma_mask = mask; + dev->dma_mask = &dev->coherent_dma_mask; + + return 0; +} +EXPORT_SYMBOL(ssb_dma_set_mask); + +int ssb_bus_may_powerdown(struct ssb_bus *bus) +{ + struct ssb_chipcommon *cc; + int err; + + /* On buses where more than one core may be working + * at a time, we must not powerdown stuff if there are + * still cores that may want to run. */ + if (bus->bustype == SSB_BUSTYPE_SSB) + return 0; + + cc = &bus->chipco; + ssb_chipco_set_clockmode(cc, SSB_CLKMODE_SLOW); + err = ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 0); + if (err) + goto error; + + return 0; +error: + ssb_printk(KERN_ERR PFX "Bus powerdown failed\n"); + return err; +} +EXPORT_SYMBOL(ssb_bus_may_powerdown); + +int ssb_bus_powerup(struct ssb_bus *bus, int dynamic_pctl) +{ + struct ssb_chipcommon *cc; + int err; + enum ssb_clkmode mode; + + err = ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 1); + if (err) + goto error; + cc = &bus->chipco; + mode = dynamic_pctl ? SSB_CLKMODE_DYNAMIC : SSB_CLKMODE_FAST; + ssb_chipco_set_clockmode(cc, mode); + + return 0; +error: + ssb_printk(KERN_ERR PFX "Bus powerup failed\n"); + return err; +} +EXPORT_SYMBOL(ssb_bus_powerup); + +u32 ssb_admatch_base(u32 adm) +{ + u32 base = 0; + + switch (adm & SSB_ADM_TYPE) { + case SSB_ADM_TYPE0: + base = (adm & SSB_ADM_BASE0); + break; + case SSB_ADM_TYPE1: + assert(!(adm & SSB_ADM_NEG)); /* unsupported */ + base = (adm & SSB_ADM_BASE1); + break; + case SSB_ADM_TYPE2: + assert(!(adm & SSB_ADM_NEG)); /* unsupported */ + base = (adm & SSB_ADM_BASE2); + break; + default: + assert(0); + } + + return base; +} +EXPORT_SYMBOL(ssb_admatch_base); + +u32 ssb_admatch_size(u32 adm) +{ + u32 size = 0; + + switch (adm & SSB_ADM_TYPE) { + case SSB_ADM_TYPE0: + size = ((adm & SSB_ADM_SZ0) >> SSB_ADM_SZ0_SHIFT); + break; + case SSB_ADM_TYPE1: + assert(!(adm & SSB_ADM_NEG)); /* unsupported */ + size = ((adm & SSB_ADM_SZ1) >> SSB_ADM_SZ1_SHIFT); + break; + case SSB_ADM_TYPE2: + assert(!(adm & SSB_ADM_NEG)); /* unsupported */ + size = ((adm & SSB_ADM_SZ2) >> SSB_ADM_SZ2_SHIFT); + break; + default: + assert(0); + } + size = (1 << (size + 1)); + + return size; +} +EXPORT_SYMBOL(ssb_admatch_size); + +static int __init ssb_modinit(void) +{ + int err; + + ssb_bustype.name = "ssb"; + err = bus_register(&ssb_bustype); + if (err) + return err; + + /* Maybe we already registered some buses at early boot. + * Check for this and attach them + */ + ssb_buses_lock(); + err = ssb_attach_queued_buses(); + ssb_buses_unlock(); + if (err) + bus_unregister(&ssb_bustype); + + return err; +} +subsys_initcall(ssb_modinit); + +static void __exit ssb_modexit(void) +{ + bus_unregister(&ssb_bustype); +} +module_exit(ssb_modexit) diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c new file mode 100644 index 0000000..fcd8e87 --- /dev/null +++ b/drivers/ssb/pci.c @@ -0,0 +1,672 @@ +/* + * Sonics Silicon Backplane PCI-Hostbus related functions. + * + * Copyright (C) 2005-2006 Michael Buesch + * Copyright (C) 2005 Martin Langer + * Copyright (C) 2005 Stefano Brivio + * Copyright (C) 2005 Danny van Dyk + * Copyright (C) 2005 Andreas Jaggi + * + * Derived from the Broadcom 4400 device driver. + * Copyright (C) 2002 David S. Miller (davem@redhat.com) + * Fixed by Pekka Pietikainen (pp@ee.oulu.fi) + * Copyright (C) 2006 Broadcom Corporation. + * + * Licensed under the GNU/GPL. See COPYING for details. + */ + +#include +#include +#include +#include + +#include "ssb_private.h" + + +int ssb_pci_switch_coreidx(struct ssb_bus *bus, u8 coreidx) +{ + int err; + int attempts = 0; + u32 cur_core; + + while (1) { + err = pci_write_config_dword(bus->host_pci, SSB_BAR0_WIN, + (coreidx * SSB_CORE_SIZE) + + SSB_ENUM_BASE); + if (err) + goto error; + err = pci_read_config_dword(bus->host_pci, SSB_BAR0_WIN, + &cur_core); + if (err) + goto error; + cur_core = (cur_core - SSB_ENUM_BASE) + / SSB_CORE_SIZE; + if (cur_core == coreidx) + break; + + if (attempts++ > SSB_BAR0_MAX_RETRIES) + goto error; + udelay(10); + } + return 0; +error: + ssb_printk(KERN_ERR PFX "Failed to switch to core %u\n", coreidx); + return -ENODEV; +} + +int ssb_pci_switch_core(struct ssb_bus *bus, + struct ssb_device *dev) +{ + int err; + unsigned long flags; + + ssb_dprintk(KERN_INFO PFX + "Switching to %s core, index %d\n", + ssb_core_name(dev->id.coreid), + dev->core_index); + + spin_lock_irqsave(&bus->bar_lock, flags); + err = ssb_pci_switch_coreidx(bus, dev->core_index); + if (!err) + bus->mapped_device = dev; + spin_unlock_irqrestore(&bus->bar_lock, flags); + + return err; +} + +int ssb_pci_xtal(struct ssb_bus *bus, u32 what, int turn_on) +{ + int err; + u32 in, out, outenable; + u16 pci_status; + + if (bus->bustype != SSB_BUSTYPE_PCI) + return 0; + + err = pci_read_config_dword(bus->host_pci, SSB_GPIO_IN, &in); + if (err) + goto err_pci; + err = pci_read_config_dword(bus->host_pci, SSB_GPIO_OUT, &out); + if (err) + goto err_pci; + err = pci_read_config_dword(bus->host_pci, SSB_GPIO_OUT_ENABLE, &outenable); + if (err) + goto err_pci; + + outenable |= what; + + if (turn_on) { + /* Avoid glitching the clock if GPRS is already using it. + * We can't actually read the state of the PLLPD so we infer it + * by the value of XTAL_PU which *is* readable via gpioin. + */ + if (!(in & SSB_GPIO_XTAL)) { + if (what & SSB_GPIO_XTAL) { + /* Turn the crystal on */ + out |= SSB_GPIO_XTAL; + if (what & SSB_GPIO_PLL) + out |= SSB_GPIO_PLL; + err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT, out); + if (err) + goto err_pci; + err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT_ENABLE, + outenable); + if (err) + goto err_pci; + msleep(1); + } + if (what & SSB_GPIO_PLL) { + /* Turn the PLL on */ + out &= ~SSB_GPIO_PLL; + err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT, out); + if (err) + goto err_pci; + msleep(5); + } + } + + err = pci_read_config_word(bus->host_pci, PCI_STATUS, &pci_status); + if (err) + goto err_pci; + pci_status &= ~PCI_STATUS_SIG_TARGET_ABORT; + err = pci_write_config_word(bus->host_pci, PCI_STATUS, pci_status); + if (err) + goto err_pci; + } else { + if (what & SSB_GPIO_XTAL) { + /* Turn the crystal off */ + out &= ~SSB_GPIO_XTAL; + } + if (what & SSB_GPIO_PLL) { + /* Turn the PLL off */ + out |= SSB_GPIO_PLL; + } + err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT, out); + if (err) + goto err_pci; + err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT_ENABLE, outenable); + if (err) + goto err_pci; + } + +out: + return err; + +err_pci: + printk(KERN_ERR PFX "Error: ssb_pci_xtal() could not access PCI config space!\n"); + err = -EBUSY; + goto out; +} + +#define SPOFF(offset) (((offset) - SSB_SPROM_BASE) / sizeof(u16)) +#define SPEX(_outvar, _offset, _mask, _shift) \ + out->_outvar = ((in[SPOFF(_offset)] & (_mask)) >> (_shift)) + +static inline u8 ssb_crc8(u8 crc, u8 data) +{ + /* Polynomial: x^8 + x^7 + x^6 + x^4 + x^2 + 1 */ + static const u8 t[] = { + 0x00, 0xF7, 0xB9, 0x4E, 0x25, 0xD2, 0x9C, 0x6B, + 0x4A, 0xBD, 0xF3, 0x04, 0x6F, 0x98, 0xD6, 0x21, + 0x94, 0x63, 0x2D, 0xDA, 0xB1, 0x46, 0x08, 0xFF, + 0xDE, 0x29, 0x67, 0x90, 0xFB, 0x0C, 0x42, 0xB5, + 0x7F, 0x88, 0xC6, 0x31, 0x5A, 0xAD, 0xE3, 0x14, + 0x35, 0xC2, 0x8C, 0x7B, 0x10, 0xE7, 0xA9, 0x5E, + 0xEB, 0x1C, 0x52, 0xA5, 0xCE, 0x39, 0x77, 0x80, + 0xA1, 0x56, 0x18, 0xEF, 0x84, 0x73, 0x3D, 0xCA, + 0xFE, 0x09, 0x47, 0xB0, 0xDB, 0x2C, 0x62, 0x95, + 0xB4, 0x43, 0x0D, 0xFA, 0x91, 0x66, 0x28, 0xDF, + 0x6A, 0x9D, 0xD3, 0x24, 0x4F, 0xB8, 0xF6, 0x01, + 0x20, 0xD7, 0x99, 0x6E, 0x05, 0xF2, 0xBC, 0x4B, + 0x81, 0x76, 0x38, 0xCF, 0xA4, 0x53, 0x1D, 0xEA, + 0xCB, 0x3C, 0x72, 0x85, 0xEE, 0x19, 0x57, 0xA0, + 0x15, 0xE2, 0xAC, 0x5B, 0x30, 0xC7, 0x89, 0x7E, + 0x5F, 0xA8, 0xE6, 0x11, 0x7A, 0x8D, 0xC3, 0x34, + 0xAB, 0x5C, 0x12, 0xE5, 0x8E, 0x79, 0x37, 0xC0, + 0xE1, 0x16, 0x58, 0xAF, 0xC4, 0x33, 0x7D, 0x8A, + 0x3F, 0xC8, 0x86, 0x71, 0x1A, 0xED, 0xA3, 0x54, + 0x75, 0x82, 0xCC, 0x3B, 0x50, 0xA7, 0xE9, 0x1E, + 0xD4, 0x23, 0x6D, 0x9A, 0xF1, 0x06, 0x48, 0xBF, + 0x9E, 0x69, 0x27, 0xD0, 0xBB, 0x4C, 0x02, 0xF5, + 0x40, 0xB7, 0xF9, 0x0E, 0x65, 0x92, 0xDC, 0x2B, + 0x0A, 0xFD, 0xB3, 0x44, 0x2F, 0xD8, 0x96, 0x61, + 0x55, 0xA2, 0xEC, 0x1B, 0x70, 0x87, 0xC9, 0x3E, + 0x1F, 0xE8, 0xA6, 0x51, 0x3A, 0xCD, 0x83, 0x74, + 0xC1, 0x36, 0x78, 0x8F, 0xE4, 0x13, 0x5D, 0xAA, + 0x8B, 0x7C, 0x32, 0xC5, 0xAE, 0x59, 0x17, 0xE0, + 0x2A, 0xDD, 0x93, 0x64, 0x0F, 0xF8, 0xB6, 0x41, + 0x60, 0x97, 0xD9, 0x2E, 0x45, 0xB2, 0xFC, 0x0B, + 0xBE, 0x49, 0x07, 0xF0, 0x9B, 0x6C, 0x22, 0xD5, + 0xF4, 0x03, 0x4D, 0xBA, 0xD1, 0x26, 0x68, 0x9F, + }; + return t[crc ^ data]; +} + +static u8 ssb_sprom_crc(const u16 *sprom) +{ + int word; + u8 crc = 0xFF; + + for (word = 0; word < SSB_SPROMSIZE_WORDS - 1; word++) { + crc = ssb_crc8(crc, sprom[word] & 0x00FF); + crc = ssb_crc8(crc, (sprom[word] & 0xFF00) >> 8); + } + crc = ssb_crc8(crc, sprom[SPOFF(SSB_SPROM_REVISION)] & 0x00FF); + crc ^= 0xFF; + + return crc; +} + +static int sprom_check_crc(const u16 *sprom) +{ + u8 crc; + u8 expected_crc; + u16 tmp; + + crc = ssb_sprom_crc(sprom); + tmp = sprom[SPOFF(SSB_SPROM_REVISION)] & SSB_SPROM_REVISION_CRC; + expected_crc = tmp >> SSB_SPROM_REVISION_CRC_SHIFT; + if (crc != expected_crc) + return -EPROTO; + + return 0; +} + +static void sprom_do_read(struct ssb_bus *bus, u16 *sprom) +{ + int i; + + for (i = 0; i < SSB_SPROMSIZE_WORDS; i++) + sprom[i] = readw(bus->mmio + SSB_SPROM_BASE + (i * 2)); +} + +static int sprom_do_write(struct ssb_bus *bus, const u16 *sprom) +{ + struct pci_dev *pdev = bus->host_pci; + int i, err; + u32 spromctl; + + ssb_printk(KERN_NOTICE PFX "Writing SPROM. Do NOT turn off the power! Please stand by...\n"); + err = pci_read_config_dword(pdev, SSB_SPROMCTL, &spromctl); + if (err) + goto err_ctlreg; + spromctl |= SSB_SPROMCTL_WE; + err = pci_write_config_dword(pdev, SSB_SPROMCTL, spromctl); + if (err) + goto err_ctlreg; + ssb_printk(KERN_NOTICE PFX "[ 0%%"); + msleep(500); + for (i = 0; i < SSB_SPROMSIZE_WORDS; i++) { + if (i == SSB_SPROMSIZE_WORDS / 4) + ssb_printk("25%%"); + else if (i == SSB_SPROMSIZE_WORDS / 2) + ssb_printk("50%%"); + else if (i == (SSB_SPROMSIZE_WORDS / 4) * 3) + ssb_printk("75%%"); + else if (i % 2) + ssb_printk("."); + writew(sprom[i], bus->mmio + SSB_SPROM_BASE + (i * 2)); + mmiowb(); + msleep(20); + } + err = pci_read_config_dword(pdev, SSB_SPROMCTL, &spromctl); + if (err) + goto err_ctlreg; + spromctl &= ~SSB_SPROMCTL_WE; + err = pci_write_config_dword(pdev, SSB_SPROMCTL, spromctl); + if (err) + goto err_ctlreg; + msleep(500); + ssb_printk("100%% ]\n"); + ssb_printk(KERN_NOTICE PFX "SPROM written.\n"); + + return 0; +err_ctlreg: + ssb_printk(KERN_ERR PFX "Could not access SPROM control register.\n"); + return err; +} + +static void sprom_extract_r1(struct ssb_sprom_r1 *out, const u16 *in) +{ + int i; + u16 v; + + SPEX(pci_spid, SSB_SPROM1_SPID, 0xFFFF, 0); + SPEX(pci_svid, SSB_SPROM1_SVID, 0xFFFF, 0); + SPEX(pci_pid, SSB_SPROM1_PID, 0xFFFF, 0); + for (i = 0; i < 3; i++) { + v = in[SPOFF(SSB_SPROM1_IL0MAC) + i]; + *(((u16 *)out->il0mac) + i) = cpu_to_be16(v); + } + for (i = 0; i < 3; i++) { + v = in[SPOFF(SSB_SPROM1_ET0MAC) + i]; + *(((u16 *)out->et0mac) + i) = cpu_to_be16(v); + } + for (i = 0; i < 3; i++) { + v = in[SPOFF(SSB_SPROM1_ET1MAC) + i]; + *(((u16 *)out->et1mac) + i) = cpu_to_be16(v); + } + SPEX(et0phyaddr, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET0A, 0); + SPEX(et1phyaddr, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET1A, + SSB_SPROM1_ETHPHY_ET1A_SHIFT); + SPEX(et0mdcport, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET0M, 14); + SPEX(et1mdcport, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET1M, 15); + SPEX(board_rev, SSB_SPROM1_BINF, SSB_SPROM1_BINF_BREV, 0); + SPEX(country_code, SSB_SPROM1_BINF, SSB_SPROM1_BINF_CCODE, + SSB_SPROM1_BINF_CCODE_SHIFT); + SPEX(antenna_a, SSB_SPROM1_BINF, SSB_SPROM1_BINF_ANTA, + SSB_SPROM1_BINF_ANTA_SHIFT); + SPEX(antenna_bg, SSB_SPROM1_BINF, SSB_SPROM1_BINF_ANTBG, + SSB_SPROM1_BINF_ANTBG_SHIFT); + SPEX(pa0b0, SSB_SPROM1_PA0B0, 0xFFFF, 0); + SPEX(pa0b1, SSB_SPROM1_PA0B1, 0xFFFF, 0); + SPEX(pa0b2, SSB_SPROM1_PA0B2, 0xFFFF, 0); + SPEX(pa1b0, SSB_SPROM1_PA1B0, 0xFFFF, 0); + SPEX(pa1b1, SSB_SPROM1_PA1B1, 0xFFFF, 0); + SPEX(pa1b2, SSB_SPROM1_PA1B2, 0xFFFF, 0); + SPEX(gpio0, SSB_SPROM1_GPIOA, SSB_SPROM1_GPIOA_P0, 0); + SPEX(gpio1, SSB_SPROM1_GPIOA, SSB_SPROM1_GPIOA_P1, + SSB_SPROM1_GPIOA_P1_SHIFT); + SPEX(gpio2, SSB_SPROM1_GPIOB, SSB_SPROM1_GPIOB_P2, 0); + SPEX(gpio3, SSB_SPROM1_GPIOB, SSB_SPROM1_GPIOB_P3, + SSB_SPROM1_GPIOB_P3_SHIFT); + SPEX(maxpwr_a, SSB_SPROM1_MAXPWR, SSB_SPROM1_MAXPWR_A, 0); + SPEX(maxpwr_bg, SSB_SPROM1_MAXPWR, SSB_SPROM1_MAXPWR_BG, + SSB_SPROM1_MAXPWR_BG_SHIFT); + SPEX(itssi_a, SSB_SPROM1_ITSSI, SSB_SPROM1_ITSSI_A, 0); + SPEX(itssi_bg, SSB_SPROM1_ITSSI, SSB_SPROM1_ITSSI_BG, + SSB_SPROM1_ITSSI_BG_SHIFT); + SPEX(boardflags_lo, SSB_SPROM1_BFLLO, 0xFFFF, 0); + SPEX(antenna_gain_a, SSB_SPROM1_AGAIN, SSB_SPROM1_AGAIN_A, 0); + SPEX(antenna_gain_bg, SSB_SPROM1_AGAIN, SSB_SPROM1_AGAIN_BG, + SSB_SPROM1_AGAIN_BG_SHIFT); + for (i = 0; i < 4; i++) { + v = in[SPOFF(SSB_SPROM1_OEM) + i]; + *(((u16 *)out->oem) + i) = cpu_to_le16(v); + } +} + +static void sprom_extract_r2(struct ssb_sprom_r2 *out, const u16 *in) +{ + int i; + u16 v; + + SPEX(boardflags_hi, SSB_SPROM2_BFLHI, 0xFFFF, 0); + SPEX(maxpwr_a_hi, SSB_SPROM2_MAXP_A, SSB_SPROM2_MAXP_A_HI, 0); + SPEX(maxpwr_a_lo, SSB_SPROM2_MAXP_A, SSB_SPROM2_MAXP_A_LO, + SSB_SPROM2_MAXP_A_LO_SHIFT); + SPEX(pa1lob0, SSB_SPROM2_PA1LOB0, 0xFFFF, 0); + SPEX(pa1lob1, SSB_SPROM2_PA1LOB1, 0xFFFF, 0); + SPEX(pa1lob2, SSB_SPROM2_PA1LOB2, 0xFFFF, 0); + SPEX(pa1hib0, SSB_SPROM2_PA1HIB0, 0xFFFF, 0); + SPEX(pa1hib1, SSB_SPROM2_PA1HIB1, 0xFFFF, 0); + SPEX(pa1hib2, SSB_SPROM2_PA1HIB2, 0xFFFF, 0); + SPEX(ofdm_pwr_off, SSB_SPROM2_OPO, SSB_SPROM2_OPO_VALUE, 0); + for (i = 0; i < 4; i++) { + v = in[SPOFF(SSB_SPROM2_CCODE) + i]; + *(((u16 *)out->country_str) + i) = cpu_to_le16(v); + } +} + +static void sprom_extract_r3(struct ssb_sprom_r3 *out, const u16 *in) +{ + out->ofdmapo = (in[SPOFF(SSB_SPROM3_OFDMAPO) + 0] & 0xFF00) >> 8; + out->ofdmapo |= (in[SPOFF(SSB_SPROM3_OFDMAPO) + 0] & 0x00FF) << 8; + out->ofdmapo <<= 16; + out->ofdmapo |= (in[SPOFF(SSB_SPROM3_OFDMAPO) + 1] & 0xFF00) >> 8; + out->ofdmapo |= (in[SPOFF(SSB_SPROM3_OFDMAPO) + 1] & 0x00FF) << 8; + + out->ofdmalpo = (in[SPOFF(SSB_SPROM3_OFDMALPO) + 0] & 0xFF00) >> 8; + out->ofdmalpo |= (in[SPOFF(SSB_SPROM3_OFDMALPO) + 0] & 0x00FF) << 8; + out->ofdmalpo <<= 16; + out->ofdmalpo |= (in[SPOFF(SSB_SPROM3_OFDMALPO) + 1] & 0xFF00) >> 8; + out->ofdmalpo |= (in[SPOFF(SSB_SPROM3_OFDMALPO) + 1] & 0x00FF) << 8; + + out->ofdmahpo = (in[SPOFF(SSB_SPROM3_OFDMAHPO) + 0] & 0xFF00) >> 8; + out->ofdmahpo |= (in[SPOFF(SSB_SPROM3_OFDMAHPO) + 0] & 0x00FF) << 8; + out->ofdmahpo <<= 16; + out->ofdmahpo |= (in[SPOFF(SSB_SPROM3_OFDMAHPO) + 1] & 0xFF00) >> 8; + out->ofdmahpo |= (in[SPOFF(SSB_SPROM3_OFDMAHPO) + 1] & 0x00FF) << 8; + + SPEX(gpioldc_on_cnt, SSB_SPROM3_GPIOLDC, SSB_SPROM3_GPIOLDC_ON, + SSB_SPROM3_GPIOLDC_ON_SHIFT); + SPEX(gpioldc_off_cnt, SSB_SPROM3_GPIOLDC, SSB_SPROM3_GPIOLDC_OFF, + SSB_SPROM3_GPIOLDC_OFF_SHIFT); + SPEX(cckpo_1M, SSB_SPROM3_CCKPO, SSB_SPROM3_CCKPO_1M, 0); + SPEX(cckpo_2M, SSB_SPROM3_CCKPO, SSB_SPROM3_CCKPO_2M, + SSB_SPROM3_CCKPO_2M_SHIFT); + SPEX(cckpo_55M, SSB_SPROM3_CCKPO, SSB_SPROM3_CCKPO_55M, + SSB_SPROM3_CCKPO_55M_SHIFT); + SPEX(cckpo_11M, SSB_SPROM3_CCKPO, SSB_SPROM3_CCKPO_11M, + SSB_SPROM3_CCKPO_11M_SHIFT); + + out->ofdmgpo = (in[SPOFF(SSB_SPROM3_OFDMGPO) + 0] & 0xFF00) >> 8; + out->ofdmgpo |= (in[SPOFF(SSB_SPROM3_OFDMGPO) + 0] & 0x00FF) << 8; + out->ofdmgpo <<= 16; + out->ofdmgpo |= (in[SPOFF(SSB_SPROM3_OFDMGPO) + 1] & 0xFF00) >> 8; + out->ofdmgpo |= (in[SPOFF(SSB_SPROM3_OFDMGPO) + 1] & 0x00FF) << 8; +} + +static int sprom_extract(struct ssb_sprom *out, const u16 *in) +{ + memset(out, 0, sizeof(*out)); + + SPEX(revision, SSB_SPROM_REVISION, SSB_SPROM_REVISION_REV, 0); + SPEX(crc, SSB_SPROM_REVISION, SSB_SPROM_REVISION_CRC, + SSB_SPROM_REVISION_CRC_SHIFT); + + if (out->revision == 0) + goto unsupported; + if (out->revision >= 1 && out->revision <= 3) + sprom_extract_r1(&out->r1, in); + if (out->revision >= 2 && out->revision <= 3) + sprom_extract_r2(&out->r2, in); + if (out->revision == 3) + sprom_extract_r3(&out->r3, in); + if (out->revision >= 4) + goto unsupported; + + return 0; +unsupported: + ssb_printk(KERN_WARNING PFX "Unsupported SPROM revision %d " + "detected. Will extract v1\n", out->revision); + sprom_extract_r1(&out->r1, in); + return 0; +} + +int ssb_pci_sprom_get(struct ssb_bus *bus) +{ + int err = -ENOMEM; + u16 *buf; + + assert(bus->bustype == SSB_BUSTYPE_PCI); + + buf = kcalloc(SSB_SPROMSIZE_WORDS, sizeof(u16), GFP_KERNEL); + if (!buf) + goto out; + sprom_do_read(bus, buf); + err = sprom_check_crc(buf); + if (err) { + ssb_printk(KERN_WARNING PFX + "WARNING: Invalid SPROM CRC (corrupt SPROM)\n"); + } + err = sprom_extract(&bus->sprom, buf); + + kfree(buf); +out: + return err; +} + +void ssb_pci_get_boardtype(struct ssb_bus *bus) +{ + pci_read_config_word(bus->host_pci, PCI_SUBSYSTEM_VENDOR_ID, + &bus->board_vendor); + pci_read_config_word(bus->host_pci, PCI_SUBSYSTEM_ID, + &bus->board_type); + pci_read_config_word(bus->host_pci, PCI_REVISION_ID, + &bus->board_rev); +} + +static u16 ssb_pci_read16(struct ssb_device *dev, u16 offset) +{ + struct ssb_bus *bus = dev->bus; + + if (unlikely(bus->mapped_device != dev)) { + if (unlikely(ssb_pci_switch_core(bus, dev))) + return 0xFFFF; + } + return readw(bus->mmio + offset); +} + +static u32 ssb_pci_read32(struct ssb_device *dev, u16 offset) +{ + struct ssb_bus *bus = dev->bus; + + if (unlikely(bus->mapped_device != dev)) { + if (unlikely(ssb_pci_switch_core(bus, dev))) + return 0xFFFFFFFF; + } + return readl(bus->mmio + offset); +} + +static void ssb_pci_write16(struct ssb_device *dev, u16 offset, u16 value) +{ + struct ssb_bus *bus = dev->bus; + + if (unlikely(bus->mapped_device != dev)) { + if (unlikely(ssb_pci_switch_core(bus, dev))) + return; + } + writew(value, bus->mmio + offset); +} + +static void ssb_pci_write32(struct ssb_device *dev, u16 offset, u32 value) +{ + struct ssb_bus *bus = dev->bus; + + if (unlikely(bus->mapped_device != dev)) { + if (unlikely(ssb_pci_switch_core(bus, dev))) + return; + } + writel(value, bus->mmio + offset); +} + +const struct ssb_bus_ops ssb_pci_ops = { + .read16 = ssb_pci_read16, + .read32 = ssb_pci_read32, + .write16 = ssb_pci_write16, + .write32 = ssb_pci_write32, +}; + +static int sprom2hex(const u16 *sprom, char *buf, size_t buf_len) +{ + int i, pos = 0; + + for (i = 0; i < SSB_SPROMSIZE_WORDS; i++) { + pos += snprintf(buf + pos, buf_len - pos - 1, + "%04X", swab16(sprom[i]) & 0xFFFF); + } + pos += snprintf(buf + pos, buf_len - pos - 1, "\n"); + + return pos + 1; +} + +static int hex2sprom(u16 *sprom, const char *dump, size_t len) +{ + char tmp[5] = { 0 }; + int cnt = 0; + unsigned long parsed; + + if (len < SSB_SPROMSIZE_BYTES * 2) + return -EINVAL; + + while (cnt < SSB_SPROMSIZE_WORDS) { + memcpy(tmp, dump, 4); + dump += 4; + parsed = simple_strtoul(tmp, NULL, 16); + sprom[cnt++] = swab16((u16)parsed); + } + + return 0; +} + +static ssize_t ssb_pci_attr_sprom_show(struct device *pcidev, + struct device_attribute *attr, + char *buf) +{ + struct pci_dev *pdev = container_of(pcidev, struct pci_dev, dev); + struct ssb_bus *bus; + u16 *sprom; + int err = -ENODEV; + ssize_t count = 0; + + bus = ssb_pci_dev_to_bus(pdev); + if (!bus) + goto out; + err = -ENOMEM; + sprom = kcalloc(SSB_SPROMSIZE_WORDS, sizeof(u16), GFP_KERNEL); + if (!sprom) + goto out; + + err = -ERESTARTSYS; + if (mutex_lock_interruptible(&bus->pci_sprom_mutex)) + goto out_kfree; + sprom_do_read(bus, sprom); + mutex_unlock(&bus->pci_sprom_mutex); + + count = sprom2hex(sprom, buf, PAGE_SIZE); + err = 0; + +out_kfree: + kfree(sprom); +out: + return err ? err : count; +} + +static ssize_t ssb_pci_attr_sprom_store(struct device *pcidev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct pci_dev *pdev = container_of(pcidev, struct pci_dev, dev); + struct ssb_bus *bus; + u16 *sprom; + int res = 0, err = -ENODEV; + + bus = ssb_pci_dev_to_bus(pdev); + if (!bus) + goto out; + err = -ENOMEM; + sprom = kcalloc(SSB_SPROMSIZE_WORDS, sizeof(u16), GFP_KERNEL); + if (!sprom) + goto out; + err = hex2sprom(sprom, buf, count); + if (err) { + err = -EINVAL; + goto out_kfree; + } + err = sprom_check_crc(sprom); + if (err) { + err = -EINVAL; + goto out_kfree; + } + + err = -ERESTARTSYS; + if (mutex_lock_interruptible(&bus->pci_sprom_mutex)) + goto out_kfree; + err = ssb_devices_freeze(bus); + if (err) { + ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze all devices\n"); + goto out_unlock; + } + res = sprom_do_write(bus, sprom); + err = ssb_devices_thaw(bus); + if (err) + ssb_printk(KERN_ERR PFX "SPROM write: Could not thaw all devices\n"); +out_unlock: + mutex_unlock(&bus->pci_sprom_mutex); +out_kfree: + kfree(sprom); +out: + if (res) + return res; + return err ? err : count; +} + +static DEVICE_ATTR(ssb_sprom, 0600, + ssb_pci_attr_sprom_show, + ssb_pci_attr_sprom_store); + +void ssb_pci_exit(struct ssb_bus *bus) +{ + struct pci_dev *pdev; + + if (bus->bustype != SSB_BUSTYPE_PCI) + return; + + pdev = bus->host_pci; + device_remove_file(&pdev->dev, &dev_attr_ssb_sprom); +} + +int ssb_pci_init(struct ssb_bus *bus) +{ + struct pci_dev *pdev; + int err; + + if (bus->bustype != SSB_BUSTYPE_PCI) + return 0; + + pdev = bus->host_pci; + mutex_init(&bus->pci_sprom_mutex); + err = device_create_file(&pdev->dev, &dev_attr_ssb_sprom); + if (err) + goto out; + err = ssb_pci_sprom_get(bus); + if (err) + goto err_remove_sprom_file; + +out: + return err; +err_remove_sprom_file: + device_remove_file(&pdev->dev, &dev_attr_ssb_sprom); + return err; +} diff --git a/drivers/ssb/pcihost_wrapper.c b/drivers/ssb/pcihost_wrapper.c new file mode 100644 index 0000000..82a10ab --- /dev/null +++ b/drivers/ssb/pcihost_wrapper.c @@ -0,0 +1,104 @@ +/* + * Sonics Silicon Backplane + * PCI Hostdevice wrapper + * + * Copyright (c) 2005 Martin Langer + * Copyright (c) 2005 Stefano Brivio + * Copyright (c) 2005 Danny van Dyk + * Copyright (c) 2005 Andreas Jaggi + * Copyright (c) 2005-2007 Michael Buesch + * + * Licensed under the GNU/GPL. See COPYING for details. + */ + +#include +#include + + +#ifdef CONFIG_PM +static int ssb_pcihost_suspend(struct pci_dev *dev, pm_message_t state) +{ + pci_save_state(dev); + pci_disable_device(dev); + pci_set_power_state(dev, pci_choose_state(dev, state)); + + return 0; +} + +static int ssb_pcihost_resume(struct pci_dev *dev) +{ + int err; + + pci_set_power_state(dev, 0); + err = pci_enable_device(dev); + if (err) + return err; + pci_restore_state(dev); + + return 0; +} +#else /* CONFIG_PM */ +# define ssb_pcihost_suspend NULL +# define ssb_pcihost_resume NULL +#endif /* CONFIG_PM */ + +static int ssb_pcihost_probe(struct pci_dev *dev, + const struct pci_device_id *id) +{ + struct ssb_bus *ssb; + int err = -ENOMEM; + const char *name; + + ssb = kzalloc(sizeof(*ssb), GFP_KERNEL); + if (!ssb) + goto out; + err = pci_enable_device(dev); + if (err) + goto err_kfree_ssb; + name = dev->dev.bus_id; + if (dev->driver && dev->driver->name) + name = dev->driver->name; + err = pci_request_regions(dev, name); + if (err) + goto err_pci_disable; + pci_set_master(dev); + + err = ssb_bus_pcibus_register(ssb, dev); + if (err) + goto err_pci_release_regions; + + pci_set_drvdata(dev, ssb); + +out: + return err; + +err_pci_release_regions: + pci_release_regions(dev); +err_pci_disable: + pci_disable_device(dev); +err_kfree_ssb: + kfree(ssb); + return err; +} + +static void ssb_pcihost_remove(struct pci_dev *dev) +{ + struct ssb_bus *ssb = pci_get_drvdata(dev); + + ssb_bus_unregister(ssb); + pci_release_regions(dev); + pci_disable_device(dev); + kfree(ssb); + pci_set_drvdata(dev, NULL); +} + +int ssb_pcihost_register(struct pci_driver *driver) +{ + driver->probe = ssb_pcihost_probe; + driver->remove = ssb_pcihost_remove; + driver->suspend = ssb_pcihost_suspend; + driver->resume = ssb_pcihost_resume; + + return pci_register_driver(driver); +} +EXPORT_SYMBOL(ssb_pcihost_register); diff --git a/drivers/ssb/pcmcia.c b/drivers/ssb/pcmcia.c new file mode 100644 index 0000000..60cf5ad --- /dev/null +++ b/drivers/ssb/pcmcia.c @@ -0,0 +1,256 @@ +/* + * Sonics Silicon Backplane + * PCMCIA-Hostbus related functions + * + * Copyright 2006 Johannes Berg + * Copyright 2007 Michael Buesch + * + * Licensed under the GNU/GPL. See COPYING for details. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "ssb_private.h" + + +int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus, + u8 coreidx) +{ + struct pcmcia_device *pdev = bus->host_pcmcia; + int err; + int attempts = 0; + u32 cur_core; + conf_reg_t reg; + u32 addr; + u32 read_addr; + + addr = (coreidx * SSB_CORE_SIZE) + SSB_ENUM_BASE; + while (1) { + reg.Action = CS_WRITE; + reg.Offset = 0x2E; + reg.Value = (addr & 0x0000F000) >> 12; + err = pcmcia_access_configuration_register(pdev, ®); + if (err != CS_SUCCESS) + goto error; + reg.Offset = 0x30; + reg.Value = (addr & 0x00FF0000) >> 16; + err = pcmcia_access_configuration_register(pdev, ®); + if (err != CS_SUCCESS) + goto error; + reg.Offset = 0x32; + reg.Value = (addr & 0xFF000000) >> 24; + err = pcmcia_access_configuration_register(pdev, ®); + if (err != CS_SUCCESS) + goto error; + + read_addr = 0; + + reg.Action = CS_READ; + reg.Offset = 0x2E; + err = pcmcia_access_configuration_register(pdev, ®); + if (err != CS_SUCCESS) + goto error; + read_addr |= (reg.Value & 0xF) << 12; + reg.Offset = 0x30; + err = pcmcia_access_configuration_register(pdev, ®); + if (err != CS_SUCCESS) + goto error; + read_addr |= reg.Value << 16; + reg.Offset = 0x32; + err = pcmcia_access_configuration_register(pdev, ®); + if (err != CS_SUCCESS) + goto error; + read_addr |= reg.Value << 24; + + cur_core = (read_addr - SSB_ENUM_BASE) / SSB_CORE_SIZE; + if (cur_core == coreidx) + break; + + if (attempts++ > SSB_BAR0_MAX_RETRIES) + goto error; + udelay(10); + } + + return 0; +error: + ssb_printk(KERN_ERR PFX "Failed to switch to core %u\n", coreidx); + return -ENODEV; +} + +int ssb_pcmcia_switch_core(struct ssb_bus *bus, + struct ssb_device *dev) +{ + int err; + unsigned long flags; + + ssb_dprintk(KERN_INFO PFX + "Switching to %s core, index %d\n", + ssb_core_name(dev->id.coreid), + dev->core_index); + + spin_lock_irqsave(&bus->bar_lock, flags); + err = ssb_pcmcia_switch_coreidx(bus, dev->core_index); + if (!err) + bus->mapped_device = dev; + spin_unlock_irqrestore(&bus->bar_lock, flags); + + return err; +} + +int ssb_pcmcia_switch_segment(struct ssb_bus *bus, u8 seg) +{ + int attempts = 0; + unsigned long flags; + conf_reg_t reg; + int res, err = 0; + + assert(seg == 0 || seg == 1); + reg.Offset = 0x34; + reg.Function = 0; + spin_lock_irqsave(&bus->bar_lock, flags); + while (1) { + reg.Action = CS_WRITE; + reg.Value = seg; + res = pcmcia_access_configuration_register(bus->host_pcmcia, ®); + if (unlikely(res != CS_SUCCESS)) + goto error; + reg.Value = 0xFF; + reg.Action = CS_READ; + res = pcmcia_access_configuration_register(bus->host_pcmcia, ®); + if (unlikely(res != CS_SUCCESS)) + goto error; + + if (reg.Value == seg) + break; + + if (unlikely(attempts++ > SSB_BAR0_MAX_RETRIES)) + goto error; + udelay(10); + } + bus->mapped_pcmcia_seg = seg; +out_unlock: + spin_unlock_irqrestore(&bus->bar_lock, flags); + return err; +error: + ssb_printk(KERN_ERR PFX "Failed to switch pcmcia segment\n"); + err = -ENODEV; + goto out_unlock; +} + +static inline int do_select_core(struct ssb_bus *bus, + struct ssb_device *dev, + u16 *offset) +{ + int err; + u8 need_seg = (*offset >= 0x800) ? 1 : 0; + + if (unlikely(dev != bus->mapped_device)) { + err = ssb_pcmcia_switch_core(bus, dev); + if (unlikely(err)) + return err; + } + if (unlikely(need_seg != bus->mapped_pcmcia_seg)) { + err = ssb_pcmcia_switch_segment(bus, need_seg); + if (unlikely(err)) + return err; + } + if (need_seg == 1) + *offset -= 0x800; + + return 0; +} + +static u16 ssb_pcmcia_read16(struct ssb_device *dev, u16 offset) +{ + struct ssb_bus *bus = dev->bus; + u16 x; + + if (unlikely(do_select_core(bus, dev, &offset))) + return 0xFFFF; + x = readw(bus->mmio + offset); +//printk("R16 0x%04X, 0x%04X\n", offset, x); + return x; +} + +static u32 ssb_pcmcia_read32(struct ssb_device *dev, u16 offset) +{ + struct ssb_bus *bus = dev->bus; + u32 x; + + if (unlikely(do_select_core(bus, dev, &offset))) + return 0xFFFFFFFF; + x = readl(bus->mmio + offset); +//printk("R32 0x%04X, 0x%08X\n", offset, x); + return x; +} + +static void ssb_pcmcia_write16(struct ssb_device *dev, u16 offset, u16 value) +{ + struct ssb_bus *bus = dev->bus; + + if (unlikely(do_select_core(bus, dev, &offset))) + return; +//printk("W16 0x%04X, 0x%04X\n", offset, value); + writew(value, bus->mmio + offset); +} + +static void ssb_pcmcia_write32(struct ssb_device *dev, u16 offset, u32 value) +{ + struct ssb_bus *bus = dev->bus; + + if (unlikely(do_select_core(bus, dev, &offset))) + return; +//printk("W32 0x%04X, 0x%08X\n", offset, value); + readw(bus->mmio + offset); + writew(value >> 16, bus->mmio + offset + 2); + readw(bus->mmio + offset); + writew(value, bus->mmio + offset); +} + +const struct ssb_bus_ops ssb_pcmcia_ops = { + .read16 = ssb_pcmcia_read16, + .read32 = ssb_pcmcia_read32, + .write16 = ssb_pcmcia_write16, + .write32 = ssb_pcmcia_write32, +}; + +int ssb_pcmcia_init(struct ssb_bus *bus) +{ + conf_reg_t reg; + int err; + + if (bus->bustype != SSB_BUSTYPE_PCMCIA) + return 0; + + /* Switch segment to a known state and sync + * bus->mapped_pcmcia_seg with hardware state. */ + ssb_pcmcia_switch_segment(bus, 0); + + /* Init IRQ routing */ + reg.Action = CS_READ; + reg.Function = 0; + if (bus->chip_id == 0x4306) + reg.Offset = 0x00; + else + reg.Offset = 0x80; + err = pcmcia_access_configuration_register(bus->host_pcmcia, ®); + if (err != CS_SUCCESS) + goto error; + reg.Action = CS_WRITE; + reg.Value |= 0x04 | 0x01; + err = pcmcia_access_configuration_register(bus->host_pcmcia, ®); + if (err != CS_SUCCESS) + goto error; + + return 0; +error: + return -ENODEV; +} diff --git a/drivers/ssb/scan.c b/drivers/ssb/scan.c new file mode 100644 index 0000000..feaf1e5 --- /dev/null +++ b/drivers/ssb/scan.c @@ -0,0 +1,407 @@ +/* + * Sonics Silicon Backplane + * Bus scanning + * + * Copyright (C) 2005-2007 Michael Buesch + * Copyright (C) 2005 Martin Langer + * Copyright (C) 2005 Stefano Brivio + * Copyright (C) 2005 Danny van Dyk + * Copyright (C) 2005 Andreas Jaggi + * Copyright (C) 2006 Broadcom Corporation. + * + * Licensed under the GNU/GPL. See COPYING for details. + */ + +#include +#include +#include +#include + +#ifdef CONFIG_SSB_PCMCIAHOST +# include +# include +# include +# include +#endif + +#include "ssb_private.h" + + +const char * ssb_core_name(u16 coreid) +{ + switch (coreid) { + case SSB_DEV_CHIPCOMMON: + return "ChipCommon"; + case SSB_DEV_ILINE20: + return "ILine 20"; + case SSB_DEV_SDRAM: + return "SDRAM"; + case SSB_DEV_PCI: + return "PCI"; + case SSB_DEV_MIPS: + return "MIPS"; + case SSB_DEV_ETHERNET: + return "Fast Ethernet"; + case SSB_DEV_V90: + return "V90"; + case SSB_DEV_USB11_HOSTDEV: + return "USB 1.1 Hostdev"; + case SSB_DEV_ADSL: + return "ADSL"; + case SSB_DEV_ILINE100: + return "ILine 100"; + case SSB_DEV_IPSEC: + return "IPSEC"; + case SSB_DEV_PCMCIA: + return "PCMCIA"; + case SSB_DEV_INTERNAL_MEM: + return "Internal Memory"; + case SSB_DEV_MEMC_SDRAM: + return "MEMC SDRAM"; + case SSB_DEV_EXTIF: + return "EXTIF"; + case SSB_DEV_80211: + return "IEEE 802.11"; + case SSB_DEV_MIPS_3302: + return "MIPS 3302"; + case SSB_DEV_USB11_HOST: + return "USB 1.1 Host"; + case SSB_DEV_USB11_DEV: + return "USB 1.1 Device"; + case SSB_DEV_USB20_HOST: + return "USB 2.0 Host"; + case SSB_DEV_USB20_DEV: + return "USB 2.0 Device"; + case SSB_DEV_SDIO_HOST: + return "SDIO Host"; + case SSB_DEV_ROBOSWITCH: + return "Roboswitch"; + case SSB_DEV_PARA_ATA: + return "PATA"; + case SSB_DEV_SATA_XORDMA: + return "SATA XOR-DMA"; + case SSB_DEV_ETHERNET_GBIT: + return "GBit Ethernet"; + case SSB_DEV_PCIE: + return "PCI-E"; + case SSB_DEV_MIMO_PHY: + return "MIMO PHY"; + case SSB_DEV_SRAM_CTRLR: + return "SRAM Controller"; + case SSB_DEV_MINI_MACPHY: + return "Mini MACPHY"; + case SSB_DEV_ARM_1176: + return "ARM 1176"; + case SSB_DEV_ARM_7TDMI: + return "ARM 7TDMI"; + } + return "UNKNOWN"; +} + +static u16 pcidev_to_chipid(struct pci_dev *pci_dev) +{ + u16 chipid_fallback = 0; + + switch (pci_dev->device) { + case 0x4301: + chipid_fallback = 0x4301; + break; + case 0x4305 ... 0x4307: + chipid_fallback = 0x4307; + break; + case 0x4403: + chipid_fallback = 0x4402; + break; + case 0x4610 ... 0x4615: + chipid_fallback = 0x4610; + break; + case 0x4710 ... 0x4715: + chipid_fallback = 0x4710; + break; + case 0x4320 ... 0x4325: + chipid_fallback = 0x4309; + break; + case PCI_DEVICE_ID_BCM4401: + case PCI_DEVICE_ID_BCM4401B0: + case PCI_DEVICE_ID_BCM4401B1: + chipid_fallback = 0x4401; + break; + default: + ssb_printk(KERN_ERR PFX + "PCI-ID not in fallback list\n"); + } + + return chipid_fallback; +} + +static u8 chipid_to_nrcores(u16 chipid) +{ + switch (chipid) { + case 0x5365: + return 7; + case 0x4306: + return 6; + case 0x4310: + return 8; + case 0x4307: + case 0x4301: + return 5; + case 0x4401: + case 0x4402: + return 3; + case 0x4710: + case 0x4610: + case 0x4704: + return 9; + default: + ssb_printk(KERN_ERR PFX + "CHIPID not in nrcores fallback list\n"); + } + + return 1; +} + +static u32 scan_read32(struct ssb_bus *bus, u8 current_coreidx, + u16 offset) +{ + switch (bus->bustype) { + case SSB_BUSTYPE_SSB: + offset += current_coreidx * SSB_CORE_SIZE; + break; + case SSB_BUSTYPE_PCI: + break; + case SSB_BUSTYPE_PCMCIA: + if (offset >= 0x800) { + ssb_pcmcia_switch_segment(bus, 1); + offset -= 0x800; + } else + ssb_pcmcia_switch_segment(bus, 0); + break; + } + return readl(bus->mmio + offset); +} + +static int scan_switchcore(struct ssb_bus *bus, u8 coreidx) +{ + switch (bus->bustype) { + case SSB_BUSTYPE_SSB: + break; + case SSB_BUSTYPE_PCI: + return ssb_pci_switch_coreidx(bus, coreidx); + case SSB_BUSTYPE_PCMCIA: + return ssb_pcmcia_switch_coreidx(bus, coreidx); + } + return 0; +} + +void ssb_iounmap(struct ssb_bus *bus) +{ + switch (bus->bustype) { + case SSB_BUSTYPE_SSB: + case SSB_BUSTYPE_PCMCIA: + iounmap(bus->mmio); + break; + case SSB_BUSTYPE_PCI: + pci_iounmap(bus->host_pci, bus->mmio); + break; + } + bus->mmio = NULL; + bus->mapped_device = NULL; +} + +static void __iomem * ssb_ioremap(struct ssb_bus *bus, + unsigned long baseaddr) +{ + void __iomem *mmio = NULL; + + switch (bus->bustype) { + case SSB_BUSTYPE_SSB: + /* Only map the first core for now. */ + /* fallthrough... */ + case SSB_BUSTYPE_PCMCIA: + mmio = ioremap(baseaddr, SSB_CORE_SIZE); + break; + case SSB_BUSTYPE_PCI: + mmio = pci_iomap(bus->host_pci, 0, ~0UL); + break; + } + + return mmio; +} + +static int we_support_multiple_80211_cores(struct ssb_bus *bus) +{ + /* More than one 802.11 core is only supported by special chips. + * There are chips with two 802.11 cores, but with dangling + * pins on the second core. Be careful and reject them here. + */ + +#ifdef CONFIG_SSB_PCIHOST + if (bus->bustype == SSB_BUSTYPE_PCI) { + if (bus->host_pci->vendor == PCI_VENDOR_ID_BROADCOM && + bus->host_pci->device == 0x4324) + return 1; + } +#endif /* CONFIG_SSB_PCIHOST */ + return 0; +} + +int ssb_bus_scan(struct ssb_bus *bus, + unsigned long baseaddr) +{ + int err = -ENOMEM; + void __iomem *mmio; + u32 idhi, cc, rev, tmp; + int dev_i, i; + struct ssb_device *dev; + int nr_80211_cores = 0; + + mmio = ssb_ioremap(bus, baseaddr); + if (!mmio) + goto out; + bus->mmio = mmio; + + err = scan_switchcore(bus, 0); /* Switch to first core */ + if (err) + goto err_unmap; + + idhi = scan_read32(bus, 0, SSB_IDHIGH); + cc = (idhi & SSB_IDHIGH_CC) >> SSB_IDHIGH_CC_SHIFT; + rev = (idhi & SSB_IDHIGH_RCLO); + rev |= (idhi & SSB_IDHIGH_RCHI) >> SSB_IDHIGH_RCHI_SHIFT; + + bus->nr_devices = 0; + if (cc == SSB_DEV_CHIPCOMMON) { + tmp = scan_read32(bus, 0, SSB_CHIPCO_CHIPID); + + bus->chip_id = (tmp & SSB_CHIPCO_IDMASK); + bus->chip_rev = (tmp & SSB_CHIPCO_REVMASK) >> + SSB_CHIPCO_REVSHIFT; + bus->chip_package = (tmp & SSB_CHIPCO_PACKMASK) >> + SSB_CHIPCO_PACKSHIFT; + if (rev >= 4) { + bus->nr_devices = (tmp & SSB_CHIPCO_NRCORESMASK) >> + SSB_CHIPCO_NRCORESSHIFT; + } + tmp = scan_read32(bus, 0, SSB_CHIPCO_CAP); + bus->chipco.capabilities = tmp; + } else { + if (bus->bustype == SSB_BUSTYPE_PCI) { + bus->chip_id = pcidev_to_chipid(bus->host_pci); + pci_read_config_word(bus->host_pci, PCI_REVISION_ID, + &bus->chip_rev); + bus->chip_package = 0; + } else { + bus->chip_id = 0x4710; + bus->chip_rev = 0; + bus->chip_package = 0; + } + } + if (!bus->nr_devices) + bus->nr_devices = chipid_to_nrcores(bus->chip_id); + if (bus->nr_devices > ARRAY_SIZE(bus->devices)) { + ssb_printk(KERN_ERR PFX + "More than %d ssb cores found (%d)\n", + SSB_MAX_NR_CORES, bus->nr_devices); + goto err_unmap; + } + if (bus->bustype == SSB_BUSTYPE_SSB) { + /* Now that we know the number of cores, + * remap the whole IO space for all cores. + */ + err = -ENOMEM; + iounmap(mmio); + mmio = ioremap(baseaddr, SSB_CORE_SIZE * bus->nr_devices); + if (!mmio) + goto out; + bus->mmio = mmio; + } + + /* Fetch basic information about each core/device */ + for (i = 0, dev_i = 0; i < bus->nr_devices; i++) { + err = scan_switchcore(bus, i); + if (err) + goto err_unmap; + dev = &(bus->devices[dev_i]); + + idhi = scan_read32(bus, i, SSB_IDHIGH); + dev->id.coreid = (idhi & SSB_IDHIGH_CC) >> SSB_IDHIGH_CC_SHIFT; + dev->id.revision = (idhi & SSB_IDHIGH_RCLO); + dev->id.revision |= (idhi & SSB_IDHIGH_RCHI) >> SSB_IDHIGH_RCHI_SHIFT; + dev->id.vendor = (idhi & SSB_IDHIGH_VC) >> SSB_IDHIGH_VC_SHIFT; + dev->core_index = i; + dev->bus = bus; + dev->ops = bus->ops; + + ssb_dprintk(KERN_INFO PFX + "Core %d found: %s " + "(cc 0x%03X, rev 0x%02X, vendor 0x%04X)\n", + i, ssb_core_name(dev->id.coreid), + dev->id.coreid, dev->id.revision, dev->id.vendor); + + switch (dev->id.coreid) { + case SSB_DEV_80211: + nr_80211_cores++; + if (nr_80211_cores > 1) { + if (!we_support_multiple_80211_cores(bus)) { + ssb_dprintk(KERN_INFO PFX "Ignoring additional " + "802.11 core\n"); + continue; + } + } + break; + case SSB_DEV_EXTIF: +#ifdef CONFIG_SSB_DRIVER_EXTIF + if (bus->extif.dev) { + ssb_printk(KERN_WARNING PFX + "WARNING: Multiple EXTIFs found\n"); + break; + } + bus->extif.dev = dev; +#endif /* CONFIG_SSB_DRIVER_EXTIF */ + break; + case SSB_DEV_CHIPCOMMON: + if (bus->chipco.dev) { + ssb_printk(KERN_WARNING PFX + "WARNING: Multiple ChipCommon found\n"); + break; + } + bus->chipco.dev = dev; + break; + case SSB_DEV_MIPS: + case SSB_DEV_MIPS_3302: +#ifdef CONFIG_SSB_DRIVER_MIPS + if (bus->mipscore.dev) { + ssb_printk(KERN_WARNING PFX + "WARNING: Multiple MIPS cores found\n"); + break; + } + bus->mipscore.dev = dev; +#endif /* CONFIG_SSB_DRIVER_MIPS */ + break; + case SSB_DEV_PCI: + case SSB_DEV_PCIE: +#ifdef CONFIG_SSB_DRIVER_PCICORE + if (bus->pcicore.dev) { + ssb_printk(KERN_WARNING PFX + "WARNING: Multiple PCI(E) cores found\n"); + break; + } + bus->pcicore.dev = dev; +#endif /* CONFIG_SSB_DRIVER_PCICORE */ + break; + default: + break; + } + + dev_i++; + } + bus->nr_devices = dev_i; + + err = 0; +out: + return err; +err_unmap: + ssb_iounmap(bus); + goto out; +} diff --git a/drivers/ssb/ssb_private.h b/drivers/ssb/ssb_private.h new file mode 100644 index 0000000..ae1fc06 --- /dev/null +++ b/drivers/ssb/ssb_private.h @@ -0,0 +1,151 @@ +#ifndef LINUX_SSB_PRIVATE_H_ +#define LINUX_SSB_PRIVATE_H_ + +#include +#include +#include + + +#define PFX "ssb: " + +#ifdef CONFIG_SSB_SILENT +# define ssb_printk(fmt, x...) do { /* nothing */ } while (0) +#else +# define ssb_printk printk +#endif /* CONFIG_SSB_SILENT */ + +/* dprintk: Debugging printk; vanishes for non-debug compilation */ +#ifdef CONFIG_SSB_DEBUG +# define ssb_dprintk(fmt, x...) ssb_printk(fmt ,##x) +#else +# define ssb_dprintk(fmt, x...) do { /* nothing */ } while (0) +#endif + +/* printkl: Rate limited printk */ +#define ssb_printkl(fmt, x...) do { \ + if (printk_ratelimit()) \ + ssb_printk(fmt ,##x); \ + } while (0) + +/* dprintkl: Rate limited debugging printk */ +#ifdef CONFIG_SSB_DEBUG +# define ssb_dprintkl ssb_printkl +#else +# define ssb_dprintkl(fmt, x...) do { /* nothing */ } while (0) +#endif + +#define assert(cond) do { \ + if (unlikely(!(cond))) { \ + ssb_dprintk(KERN_ERR PFX "BUG: Assertion failed (%s) " \ + "at: %s:%d:%s()\n", \ + #cond, __FILE__, __LINE__, __func__); \ + } \ + } while (0) + + +/* pci.c */ +#ifdef CONFIG_SSB_PCIHOST +extern int ssb_pci_switch_core(struct ssb_bus *bus, + struct ssb_device *dev); +extern int ssb_pci_switch_coreidx(struct ssb_bus *bus, + u8 coreidx); +extern int ssb_pci_xtal(struct ssb_bus *bus, u32 what, + int turn_on); +extern int ssb_pci_sprom_get(struct ssb_bus *bus); +extern void ssb_pci_get_boardtype(struct ssb_bus *bus); +extern void ssb_pci_exit(struct ssb_bus *bus); +extern int ssb_pci_init(struct ssb_bus *bus); +extern const struct ssb_bus_ops ssb_pci_ops; + +#else /* CONFIG_SSB_PCIHOST */ + +static inline int ssb_pci_switch_core(struct ssb_bus *bus, + struct ssb_device *dev) +{ + return 0; +} +static inline int ssb_pci_switch_coreidx(struct ssb_bus *bus, + u8 coreidx) +{ + return 0; +} +static inline int ssb_pci_xtal(struct ssb_bus *bus, u32 what, + int turn_on) +{ + return 0; +} +static inline int ssb_pci_sprom_get(struct ssb_bus *bus) +{ + return 0; +} +static inline void ssb_pci_get_boardtype(struct ssb_bus *bus) +{ +} +static inline void ssb_pci_exit(struct ssb_bus *bus) +{ +} +static inline int ssb_pci_init(struct ssb_bus *bus) +{ + return 0; +} +#endif /* CONFIG_SSB_PCIHOST */ + + +/* pcmcia.c */ +#ifdef CONFIG_SSB_PCMCIAHOST +extern int ssb_pcmcia_switch_core(struct ssb_bus *bus, + struct ssb_device *dev); +extern int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus, + u8 coreidx); +extern int ssb_pcmcia_switch_segment(struct ssb_bus *bus, + u8 seg); +extern int ssb_pcmcia_init(struct ssb_bus *bus); +extern const struct ssb_bus_ops ssb_pcmcia_ops; +#else /* CONFIG_SSB_PCMCIAHOST */ +static inline int ssb_pcmcia_switch_core(struct ssb_bus *bus, + struct ssb_device *dev) +{ + return 0; +} +static inline int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus, + u8 coreidx) +{ + return 0; +} +static inline int ssb_pcmcia_switch_segment(struct ssb_bus *bus, + u8 seg) +{ + return 0; +} +static inline int ssb_pcmcia_init(struct ssb_bus *bus) +{ + return 0; +} +#endif /* CONFIG_SSB_PCMCIAHOST */ + + +/* scan.c */ +extern const char * ssb_core_name(u16 coreid); +extern int ssb_bus_scan(struct ssb_bus *bus, + unsigned long baseaddr); +extern void ssb_iounmap(struct ssb_bus *ssb); + + +/* core.c */ +extern u32 ssb_calc_clock_rate(u32 plltype, u32 n, u32 m); +#ifdef CONFIG_SSB_PCIHOST +extern int ssb_devices_freeze(struct ssb_bus *bus); +extern int ssb_devices_thaw(struct ssb_bus *bus); +extern struct ssb_bus * ssb_pci_dev_to_bus(struct pci_dev *pdev); +#endif /* CONFIG_SSB_PCIHOST */ + + +/* Ceiling division helper. Divides x by y. */ +static inline +unsigned long ceildiv(unsigned long x, unsigned long y) +{ + return ((x + (y - 1)) / y); +} + + +#endif /* LINUX_SSB_PRIVATE_H_ */ diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 6271187..1cbed2d 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -142,6 +142,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 --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index e8bbe8b..3f6da59 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -921,11 +921,17 @@ #include "ohci-ps3.c" #define PS3_SYSTEM_BUS_DRIVER ps3_ohci_sb_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 @@ -973,10 +979,20 @@ #ifdef PCI_DRIVER 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 @@ -1002,6 +1018,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 --git a/drivers/usb/host/ohci-ssb.c b/drivers/usb/host/ohci-ssb.c new file mode 100644 index 0000000..2b3ef36 --- /dev/null +++ b/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, +}; diff --git a/include/linux/crc-itu-t.h b/include/linux/crc-itu-t.h new file mode 100644 index 0000000..3e6d80c --- /dev/null +++ b/include/linux/crc-itu-t.h @@ -0,0 +1,27 @@ +/* + * crc-itu-t.h - CRC ITU-T V.41 routine + * + * Implements the standard CRC ITU-T V.41: + * Width 16 + * Poly 0x1021 (x^16 + x^12 + x^15 + 1) + * Init 0 + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#ifndef CRC_ITU_T_H +#define CRC_ITU_T_H + +#include + +extern u16 const crc_itu_t_table[256]; + +extern u16 crc_itu_t(u16 crc, const u8 *buffer, size_t len); + +static inline u16 crc_itu_t_byte(u16 crc, const u8 data) +{ + return (crc << 8) ^ crc_itu_t_table[((crc >> 8) ^ data) & 0xff]; +} + +#endif /* CRC_ITU_T_H */ diff --git a/include/linux/eeprom_93cx6.h b/include/linux/eeprom_93cx6.h new file mode 100644 index 0000000..4b9be59 --- /dev/null +++ b/include/linux/eeprom_93cx6.h @@ -0,0 +1,77 @@ +/* + Copyright (C) 2004 - 2006 rt2x00 SourceForge Project + + + 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. + */ + +/* + Module: eeprom_93cx6 + Abstract: EEPROM reader datastructures for 93cx6 chipsets. + Supported chipsets: 93c46 & 93c66. + */ + +/* + * EEPROM operation defines. + */ +#define PCI_EEPROM_WIDTH_93C46 6 +#define PCI_EEPROM_WIDTH_93C66 8 +#define PCI_EEPROM_WIDTH_OPCODE 3 +#define PCI_EEPROM_WRITE_OPCODE 0x05 +#define PCI_EEPROM_READ_OPCODE 0x06 +#define PCI_EEPROM_EWDS_OPCODE 0x10 +#define PCI_EEPROM_EWEN_OPCODE 0x13 + +/** + * struct eeprom_93cx6 - control structure for setting the commands + * for reading the eeprom data. + * @data: private pointer for the driver. + * @register_read(struct eeprom_93cx6 *eeprom): handler to + * read the eeprom register, this function should set all reg_* fields. + * @register_write(struct eeprom_93cx6 *eeprom): handler to + * write to the eeprom register by using all reg_* fields. + * @width: eeprom width, should be one of the PCI_EEPROM_WIDTH_* defines + * @reg_data_in: register field to indicate data input + * @reg_data_out: register field to indicate data output + * @reg_data_clock: register field to set the data clock + * @reg_chip_select: register field to set the chip select + * + * This structure is used for the communication between the driver + * and the eeprom_93cx6 handlers for reading the eeprom. + */ +struct eeprom_93cx6 { + void *data; + + void (*register_read)(struct eeprom_93cx6 *eeprom); + void (*register_write)(struct eeprom_93cx6 *eeprom); + + int width; + + char reg_data_in; + char reg_data_out; + char reg_data_clock; + char reg_chip_select; +}; + +extern void eeprom_93cx6_read(struct eeprom_93cx6 *eeprom, + const u8 word, u16 *data); +extern void eeprom_93cx6_multiread(struct eeprom_93cx6 *eeprom, + const u8 word, __le16 *data, const u16 words); + +extern void eeprom_93cx6_write(struct eeprom_93cx6 *eeprom, + const u8 word, u16 data); +extern void eeprom_93cx6_multiwrite(struct eeprom_93cx6 *eeprom, + const u8 word, __le16 *data, const u16 words); diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h new file mode 100644 index 0000000..0cd2d35 --- /dev/null +++ b/include/linux/ieee80211.h @@ -0,0 +1,403 @@ +/* + * IEEE 802.11 defines + * + * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen + * + * Copyright (c) 2002-2003, Jouni Malinen + * Copyright (c) 2005, Devicescape Software, Inc. + * Copyright (c) 2006, Michael Wu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef IEEE80211_H +#define IEEE80211_H + +#include + +#define FCS_LEN 4 + +#define IEEE80211_FCTL_VERS 0x0003 +#define IEEE80211_FCTL_FTYPE 0x000c +#define IEEE80211_FCTL_STYPE 0x00f0 +#define IEEE80211_FCTL_TODS 0x0100 +#define IEEE80211_FCTL_FROMDS 0x0200 +#define IEEE80211_FCTL_MOREFRAGS 0x0400 +#define IEEE80211_FCTL_RETRY 0x0800 +#define IEEE80211_FCTL_PM 0x1000 +#define IEEE80211_FCTL_MOREDATA 0x2000 +#define IEEE80211_FCTL_PROTECTED 0x4000 +#define IEEE80211_FCTL_ORDER 0x8000 + +#define IEEE80211_SCTL_FRAG 0x000F +#define IEEE80211_SCTL_SEQ 0xFFF0 + +#define IEEE80211_FTYPE_MGMT 0x0000 +#define IEEE80211_FTYPE_CTL 0x0004 +#define IEEE80211_FTYPE_DATA 0x0008 + +/* management */ +#define IEEE80211_STYPE_ASSOC_REQ 0x0000 +#define IEEE80211_STYPE_ASSOC_RESP 0x0010 +#define IEEE80211_STYPE_REASSOC_REQ 0x0020 +#define IEEE80211_STYPE_REASSOC_RESP 0x0030 +#define IEEE80211_STYPE_PROBE_REQ 0x0040 +#define IEEE80211_STYPE_PROBE_RESP 0x0050 +#define IEEE80211_STYPE_BEACON 0x0080 +#define IEEE80211_STYPE_ATIM 0x0090 +#define IEEE80211_STYPE_DISASSOC 0x00A0 +#define IEEE80211_STYPE_AUTH 0x00B0 +#define IEEE80211_STYPE_DEAUTH 0x00C0 +#define IEEE80211_STYPE_ACTION 0x00D0 + +/* control */ +#define IEEE80211_STYPE_PSPOLL 0x00A0 +#define IEEE80211_STYPE_RTS 0x00B0 +#define IEEE80211_STYPE_CTS 0x00C0 +#define IEEE80211_STYPE_ACK 0x00D0 +#define IEEE80211_STYPE_CFEND 0x00E0 +#define IEEE80211_STYPE_CFENDACK 0x00F0 + +/* data */ +#define IEEE80211_STYPE_DATA 0x0000 +#define IEEE80211_STYPE_DATA_CFACK 0x0010 +#define IEEE80211_STYPE_DATA_CFPOLL 0x0020 +#define IEEE80211_STYPE_DATA_CFACKPOLL 0x0030 +#define IEEE80211_STYPE_NULLFUNC 0x0040 +#define IEEE80211_STYPE_CFACK 0x0050 +#define IEEE80211_STYPE_CFPOLL 0x0060 +#define IEEE80211_STYPE_CFACKPOLL 0x0070 +#define IEEE80211_STYPE_QOS_DATA 0x0080 +#define IEEE80211_STYPE_QOS_DATA_CFACK 0x0090 +#define IEEE80211_STYPE_QOS_DATA_CFPOLL 0x00A0 +#define IEEE80211_STYPE_QOS_DATA_CFACKPOLL 0x00B0 +#define IEEE80211_STYPE_QOS_NULLFUNC 0x00C0 +#define IEEE80211_STYPE_QOS_CFACK 0x00D0 +#define IEEE80211_STYPE_QOS_CFPOLL 0x00E0 +#define IEEE80211_STYPE_QOS_CFACKPOLL 0x00F0 + + +/* miscellaneous IEEE 802.11 constants */ +#define IEEE80211_MAX_FRAG_THRESHOLD 2346 +#define IEEE80211_MAX_RTS_THRESHOLD 2347 +#define IEEE80211_MAX_AID 2007 +#define IEEE80211_MAX_TIM_LEN 251 +#define IEEE80211_MAX_DATA_LEN 2304 +/* Maximum size for the MA-UNITDATA primitive, 802.11 standard section + 6.2.1.1.2. + + The figure in section 7.1.2 suggests a body size of up to 2312 + bytes is allowed, which is a bit confusing, I suspect this + represents the 2304 bytes of real data, plus a possible 8 bytes of + WEP IV and ICV. (this interpretation suggested by Ramiro Barreiro) */ + +#define IEEE80211_MAX_SSID_LEN 32 + +struct ieee80211_hdr { + __le16 frame_control; + __le16 duration_id; + u8 addr1[6]; + u8 addr2[6]; + u8 addr3[6]; + __le16 seq_ctrl; + u8 addr4[6]; +} __attribute__ ((packed)); + + +struct ieee80211_ht_capability { + __le16 capabilities_info; + u8 mac_ht_params_info; + u8 supported_mcs_set[16]; + __le16 extended_ht_capability_info; + __le32 tx_BF_capability_info; + u8 antenna_selection_info; +}__attribute__ ((packed)); + +struct ieee80211_ht_additional_info { + u8 control_chan; + u8 ht_param; + __le16 operation_mode; + __le16 stbc_param; + u8 basic_set[16]; +}__attribute__ ((packed)); + +struct ieee80211_mgmt { + __le16 frame_control; + __le16 duration; + u8 da[6]; + u8 sa[6]; + u8 bssid[6]; + __le16 seq_ctrl; + union { + struct { + __le16 auth_alg; + __le16 auth_transaction; + __le16 status_code; + /* possibly followed by Challenge text */ + u8 variable[0]; + } __attribute__ ((packed)) auth; + struct { + __le16 reason_code; + } __attribute__ ((packed)) deauth; + struct { + __le16 capab_info; + __le16 listen_interval; + /* followed by SSID and Supported rates */ + u8 variable[0]; + } __attribute__ ((packed)) assoc_req; + struct { + __le16 capab_info; + __le16 status_code; + __le16 aid; + /* followed by Supported rates */ + u8 variable[0]; + } __attribute__ ((packed)) assoc_resp, reassoc_resp; + struct { + __le16 capab_info; + __le16 listen_interval; + u8 current_ap[6]; + /* followed by SSID and Supported rates */ + u8 variable[0]; + } __attribute__ ((packed)) reassoc_req; + struct { + __le16 reason_code; + } __attribute__ ((packed)) disassoc; + struct { + __le64 timestamp; + __le16 beacon_int; + __le16 capab_info; + /* followed by some of SSID, Supported rates, + * FH Params, DS Params, CF Params, IBSS Params, TIM */ + u8 variable[0]; + } __attribute__ ((packed)) beacon; + struct { + /* only variable items: SSID, Supported rates */ + u8 variable[0]; + } __attribute__ ((packed)) probe_req; + struct { + __le64 timestamp; + __le16 beacon_int; + __le16 capab_info; + /* followed by some of SSID, Supported rates, + * FH Params, DS Params, CF Params, IBSS Params */ + u8 variable[0]; + } __attribute__ ((packed)) probe_resp; + struct { + u8 category; + union { + struct { + u8 action_code; + u8 dialog_token; + u8 status_code; + u8 variable[0]; + } __attribute__ ((packed)) wme_action; + struct{ + u8 action_code; + u8 element_id; + u8 length; + u8 switch_mode; + u8 new_chan; + u8 switch_count; + } __attribute__((packed)) chan_switch; + struct{ + u8 action_code; + u8 dialog_token; + __le16 capab; + __le16 timeout; + __le16 start_seq_num; + } __attribute__((packed)) addba_req; + struct{ + u8 action_code; + u8 dialog_token; + __le16 status; + __le16 capab; + __le16 timeout; + } __attribute__((packed)) addba_resp; + struct{ + u8 action_code; + __le16 params; + __le16 reason_code; + }__attribute__((packed)) delba; + } u; + } __attribute__ ((packed)) action; + } u; +} __attribute__ ((packed)); + + +/* Control frames */ +struct ieee80211_rts { + __le16 frame_control; + __le16 duration; + u8 ra[6]; + u8 ta[6]; +} __attribute__ ((packed)); + +struct ieee80211_cts { + __le16 frame_control; + __le16 duration; + u8 ra[6]; +} __attribute__ ((packed)); + + +/* Authentication algorithms */ +#define WLAN_AUTH_OPEN 0 +#define WLAN_AUTH_SHARED_KEY 1 +#define WLAN_AUTH_FAST_BSS_TRANSITION 2 +#define WLAN_AUTH_LEAP 128 + +#define WLAN_AUTH_CHALLENGE_LEN 128 + +#define WLAN_CAPABILITY_ESS (1<<0) +#define WLAN_CAPABILITY_IBSS (1<<1) +#define WLAN_CAPABILITY_CF_POLLABLE (1<<2) +#define WLAN_CAPABILITY_CF_POLL_REQUEST (1<<3) +#define WLAN_CAPABILITY_PRIVACY (1<<4) +#define WLAN_CAPABILITY_SHORT_PREAMBLE (1<<5) +#define WLAN_CAPABILITY_PBCC (1<<6) +#define WLAN_CAPABILITY_CHANNEL_AGILITY (1<<7) +/* 802.11h */ +#define WLAN_CAPABILITY_SPECTRUM_MGMT (1<<8) +#define WLAN_CAPABILITY_QOS (1<<9) +#define WLAN_CAPABILITY_SHORT_SLOT_TIME (1<<10) +#define WLAN_CAPABILITY_DSSS_OFDM (1<<13) + +/* Status codes */ +enum ieee80211_statuscode { + WLAN_STATUS_SUCCESS = 0, + WLAN_STATUS_UNSPECIFIED_FAILURE = 1, + WLAN_STATUS_CAPS_UNSUPPORTED = 10, + WLAN_STATUS_REASSOC_NO_ASSOC = 11, + WLAN_STATUS_ASSOC_DENIED_UNSPEC = 12, + WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG = 13, + WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION = 14, + WLAN_STATUS_CHALLENGE_FAIL = 15, + WLAN_STATUS_AUTH_TIMEOUT = 16, + WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA = 17, + WLAN_STATUS_ASSOC_DENIED_RATES = 18, + /* 802.11b */ + WLAN_STATUS_ASSOC_DENIED_NOSHORTPREAMBLE = 19, + WLAN_STATUS_ASSOC_DENIED_NOPBCC = 20, + WLAN_STATUS_ASSOC_DENIED_NOAGILITY = 21, + /* 802.11h */ + WLAN_STATUS_ASSOC_DENIED_NOSPECTRUM = 22, + WLAN_STATUS_ASSOC_REJECTED_BAD_POWER = 23, + WLAN_STATUS_ASSOC_REJECTED_BAD_SUPP_CHAN = 24, + /* 802.11g */ + WLAN_STATUS_ASSOC_DENIED_NOSHORTTIME = 25, + WLAN_STATUS_ASSOC_DENIED_NODSSSOFDM = 26, + /* 802.11e */ + WLAN_STATUS_REQUEST_DECLINED = 37, + /* 802.11i */ + WLAN_STATUS_INVALID_IE = 40, + WLAN_STATUS_INVALID_GROUP_CIPHER = 41, + WLAN_STATUS_INVALID_PAIRWISE_CIPHER = 42, + WLAN_STATUS_INVALID_AKMP = 43, + WLAN_STATUS_UNSUPP_RSN_VERSION = 44, + WLAN_STATUS_INVALID_RSN_IE_CAP = 45, + WLAN_STATUS_CIPHER_SUITE_REJECTED = 46, +}; + + +/* Reason codes */ +enum ieee80211_reasoncode { + WLAN_REASON_UNSPECIFIED = 1, + WLAN_REASON_PREV_AUTH_NOT_VALID = 2, + WLAN_REASON_DEAUTH_LEAVING = 3, + WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY = 4, + WLAN_REASON_DISASSOC_AP_BUSY = 5, + WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA = 6, + WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA = 7, + WLAN_REASON_DISASSOC_STA_HAS_LEFT = 8, + WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH = 9, + /* 802.11h */ + WLAN_REASON_DISASSOC_BAD_POWER = 10, + WLAN_REASON_DISASSOC_BAD_SUPP_CHAN = 11, + /* 802.11i */ + WLAN_REASON_INVALID_IE = 13, + WLAN_REASON_MIC_FAILURE = 14, + WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT = 15, + WLAN_REASON_GROUP_KEY_HANDSHAKE_TIMEOUT = 16, + WLAN_REASON_IE_DIFFERENT = 17, + WLAN_REASON_INVALID_GROUP_CIPHER = 18, + WLAN_REASON_INVALID_PAIRWISE_CIPHER = 19, + WLAN_REASON_INVALID_AKMP = 20, + WLAN_REASON_UNSUPP_RSN_VERSION = 21, + WLAN_REASON_INVALID_RSN_IE_CAP = 22, + WLAN_REASON_IEEE8021X_FAILED = 23, + WLAN_REASON_CIPHER_SUITE_REJECTED = 24, +}; + + +/* Category Code */ +enum ieee80211_category { + WLAN_CATEGORY_SPECTRUM_MGMT = 0, + WLAN_CATEGORY_QOS = 1, + WLAN_CATEGORY_DLS = 2, + WLAN_CATEGORY_BACK = 3, + WLAN_CATEGORY_WMM = 17, +}; + + +/* BACK Action Code */ +enum ieee80211_back_actioncode { + WLAN_ACTION_ADDBA_REQ = 0, + WLAN_ACTION_ADDBA_RESP = 1, + WLAN_ACTION_DELBA = 2, +}; + +/* Information Element IDs */ +enum ieee80211_eid { + WLAN_EID_SSID = 0, + WLAN_EID_SUPP_RATES = 1, + WLAN_EID_FH_PARAMS = 2, + WLAN_EID_DS_PARAMS = 3, + WLAN_EID_CF_PARAMS = 4, + WLAN_EID_TIM = 5, + WLAN_EID_IBSS_PARAMS = 6, + WLAN_EID_CHALLENGE = 16, + /* 802.11d */ + WLAN_EID_COUNTRY = 7, + WLAN_EID_HP_PARAMS = 8, + WLAN_EID_HP_TABLE = 9, + WLAN_EID_REQUEST = 10, + /* 802.11h */ + WLAN_EID_PWR_CONSTRAINT = 32, + WLAN_EID_PWR_CAPABILITY = 33, + WLAN_EID_TPC_REQUEST = 34, + WLAN_EID_TPC_REPORT = 35, + WLAN_EID_SUPPORTED_CHANNELS = 36, + WLAN_EID_CHANNEL_SWITCH = 37, + WLAN_EID_MEASURE_REQUEST = 38, + WLAN_EID_MEASURE_REPORT = 39, + WLAN_EID_QUIET = 40, + WLAN_EID_IBSS_DFS = 41, + /* 802.11g */ + WLAN_EID_ERP_INFO = 42, + WLAN_EID_EXT_SUPP_RATES = 50, + /* 802.11n */ + WLAN_EID_HT_CAPABILITY = 45, + WLAN_EID_HT_EXTRA_INFO = 61, + /* 802.11i */ + WLAN_EID_RSN = 48, + WLAN_EID_WPA = 221, + WLAN_EID_GENERIC = 221, + WLAN_EID_VENDOR_SPECIFIC = 221, + WLAN_EID_QOS_PARAMETER = 222 +}; + +/* 80211n */ +#define IEEE80211_QOS_CONTROL_A_MSDU_PRESENT 0x0080 + +/* cipher suite selectors */ +#define WLAN_CIPHER_SUITE_USE_GROUP 0x000FAC00 +#define WLAN_CIPHER_SUITE_WEP40 0x000FAC01 +#define WLAN_CIPHER_SUITE_TKIP 0x000FAC02 +/* reserved: 0x000FAC03 */ +#define WLAN_CIPHER_SUITE_CCMP 0x000FAC04 +#define WLAN_CIPHER_SUITE_WEP104 0x000FAC05 + +#define WLAN_MAX_KEY_LEN 32 + +#endif /* IEEE80211_H */ diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 9a30ba2..cef969c 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -7,6 +7,217 @@ #define __LINUX_NL80211_H */ /** + * enum nl80211_commands - supported nl80211 commands + * @NL80211_CMD_UNSPEC: unspecified command to catch errors + * @NL80211_CMD_RENAME_WIPHY: rename a wiphy, needs + * %NL80211_ATTR_WIPHY and %NL80211_ATTR_WIPHY_NAME + * @NL80211_CMD_WIPHY_NEWNAME: rename notification + * @NL80211_CMD_GET_CMDLIST: TO BE DEFINED PROPERLY. currently the code makes + * it depend on the wiphy only but it really should depend on the + * interface type too.... + * @NL80211_CMD_NEW_CMDLIST: command list result + * @NL80211_CMD_ADD_VIRTUAL_INTERFACE: create a virtual interface for the + * wiphy identified by an %NL80211_ATTR_WIPHY attribute with the given + * %NL80211_ATTR_IFTYPE and %NL80211_ATTR_IFNAME. + * @NL80211_CMD_DEL_VIRTUAL_INTERFACE: destroy a virtual interface identified + * by %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_CHANGE_VIRTUAL_INTERFACE: change type of virtual interface to + * the type given by %NL80211_ATTR_IFTYPE, the interface is identified by + * %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_GET_WIPHYS: request a list of all wiphys present in the system + * @NL80211_CMD_NEW_WIPHYS: returned list of all wiphys + * @NL80211_CMD_GET_INTERFACES: request a list of all interfaces belonging to + * the wiphy identified by %NL80211_ATTR_WIPHY + * @NL80211_CMD_NEW_INTERFACES: result for %NL80211_CMD_GET_INTERFACES + * @NL80211_CMD_INITIATE_SCAN: initiate a scan with the passed parameters. THe + * parameters may contain %NL80211_ATTR_FLAG_SCAN_ACTIVE, + * %NL80211_ATTR_PHYMODE and a list of channels in an + * %NL80211_ATTR_CHANNEL_LIST attribute (an array of nested attributes) + * containing %NL80211_ATTR_CHANNEL, %NL80211_ATTR_PHYMODE, and possibly + * %NL80211_ATTR_FLAG_SCAN_ACTIVE. The outer %NL80211_ATTR_FLAG_SCAN_ACTIVE + * is ignored when a channel list is present. + * @NL80211_CMD_SCAN_RESULT: scan result, contains an array in + * %NL80211_ATTR_BSS_LIST. + * @NL80211_CMD_ASSOCIATE: associate with the given parameters + * (%NL80211_ATTR_SSID is mandatory, %NL80211_ATTR_TIMEOUT_TU, + * %NL80211_ATTR_BSSID, %NL80211_ATTR_CHANNEL, %NL80211_ATTR_PHYMODE, + * and %NL80211_ATTR_IE may be given) + * @NL80211_CMD_ADD_KEY: add a key with given %NL80211_ATTR_KEY_DATA, + * %NL80211_ATTR_KEY_ID, %NL80211_ATTR_KEY_TYPE, %NL80211_ATTR_MAC and + * %NL80211_ATTR_KEY_CIPHER attributes. + * @NL80211_CMD_DEL_KEY: delete a key identified by %NL80211_ATTR_KEY_ID, + * %NL80211_ATTR_KEY_TYPE and %NL80211_ATTR_MAC or all keys. + * @__NL80211_CMD_AFTER_LAST: internal use + */ +enum nl80211_commands { +/* don't change the order or add anything inbetween, this is ABI! */ + NL80211_CMD_UNSPEC, + /* %input: wiphy, wiphy_name */ + NL80211_CMD_RENAME_WIPHY, + NL80211_CMD_WIPHY_NEWNAME, + /* %input: wiphy|ifindex */ + NL80211_CMD_GET_CMDLIST, + NL80211_CMD_NEW_CMDLIST, + /* %input: wiphy, ifname, {iftype} */ + NL80211_CMD_ADD_VIRTUAL_INTERFACE, + /* %input: wiphy, ifindex */ + NL80211_CMD_DEL_VIRTUAL_INTERFACE, + /* %input: ifindex, iftype */ + NL80211_CMD_CHANGE_VIRTUAL_INTERFACE, + /* %input: */ + NL80211_CMD_GET_WIPHYS, + NL80211_CMD_NEW_WIPHYS, + /* %input: wiphy */ + NL80211_CMD_GET_INTERFACES, + NL80211_CMD_NEW_INTERFACES, + NL80211_CMD_INITIATE_SCAN, + NL80211_CMD_SCAN_RESULT, + NL80211_CMD_GET_ASSOCIATION, + NL80211_CMD_ASSOCIATION_CHANGED, + NL80211_CMD_ASSOCIATE, + NL80211_CMD_DISASSOCIATE, + NL80211_CMD_DEAUTH, + NL80211_CMD_GET_AUTH_LIST, + NL80211_CMD_NEW_AUTH_LIST, + NL80211_CMD_AUTHENTICATION_CHANGED, + NL80211_CMD_AP_SET_BEACON, + NL80211_CMD_AP_ADD_STA, + NL80211_CMD_AP_UPDATE_STA, + NL80211_CMD_AP_GET_STA_INFO, + NL80211_CMD_AP_SET_RATESETS, + NL80211_CMD_ADD_KEY, + NL80211_CMD_DEL_KEY, + + /* add commands here */ + + /* used to define NL80211_CMD_MAX below */ + __NL80211_CMD_AFTER_LAST +}; +#define NL80211_CMD_MAX (__NL80211_CMD_AFTER_LAST - 1) + + +/** + * enum nl80211_attrs - nl80211 netlink attributes + * @NL80211_ATTR_UNSPEC: unspecified attribute to catch errors + * @NL80211_ATTR_IFINDEX: network interface index of the device to operate on + * @NL80211_ATTR_IFNAME: network interface name + * @NL80211_ATTR_WIPHY: index of wiphy to operate on, cf. + * /sys/class/ieee80211//index + * @NL80211_ATTR_WIPHY_NAME: wiphy name (used for renaming) + * @NL80211_ATTR_CMDS: list of u8's identifying commands a device supports + * @NL80211_ATTR_IFTYPE: type of virtual interface, see &enum nl80211_iftype + * @NL80211_ATTR_INTERFACE_LIST: interface array, nested netlink attribute + * @NL80211_ATTR_WIPHY_LIST: wiphy array, nested netlink attribute + * @NL80211_ATTR_BSSID: BSSID (must be 6 bytes) + * @NL80211_ATTR_SSID: SSID (1-32 bytes) + * @NL80211_ATTR_CHANNEL: channel number + * @NL80211_ATTR_PHYMODE: PHY mode, see &enum nl80211_phymode + * @NL80211_ATTR_CHANNEL_LIST: netlink nested attribute array containing scan + * parameters for channels + * @NL80211_ATTR_BSS_LIST: nested attribute containing an array + * @NL80211_ATTR_BSSTYPE: BSS type, see &enum nl80211_bsstype + * @NL80211_ATTR_BEACON_PERIOD: beacon period + * @NL80211_ATTR_DTIM_PERIOD: DTIM period + * @NL80211_ATTR_TIMESTAMP: 64-bit timestamp of received beacon/probe response + * @NL80211_ATTR_IE: information element(s), maximum length %NL80211_MAX_IE_LEN + * @NL80211_ATTR_AUTH_ALGORITHM: authentication algorithm + * @NL80211_ATTR_TIMEOUT_TU: timeout in TU (TO BE USED) + * @NL80211_ATTR_REASON_CODE: 802.11 reason code + * @NL80211_ATTR_ASSOCIATION_ID: association ID (u16, 1-2007) + * @NL80211_ATTR_DEAUTHENTICATED: TO BE USED + * @NL80211_ATTR_RX_SENSITIVITY: receiver sensitivity in dBm + * @NL80211_ATTR_TRANSMIT_POWER: transmit power in mW + * @NL80211_ATTR_FRAG_THRESHOLD: fragmentation threshold (bytes) + * @NL80211_ATTR_FLAG_SCAN_ACTIVE: netlink flag indiciating active scan + * @NL80211_ATTR_KEY_DATA: temporal key data + * @NL80211_ATTR_KEY_ID: key ID (u8, 0-3) + * @NL80211_ATTR_KEY_TYPE: key type (see &enum nl80211_keytype) + * @NL80211_ATTR_MAC: MAC address + * @NL80211_ATTR_KEY_CIPHER: key cipher suite (u32) + * @__NL80211_ATTR_AFTER_LAST: internal use + */ +enum nl80211_attrs { +/* don't change the order or add anything inbetween, this is ABI! */ + NL80211_ATTR_UNSPEC, + /* %type: u32 */ + NL80211_ATTR_IFINDEX, + /* %type: nulstring */ + NL80211_ATTR_IFNAME, + /* %type: u32 */ + NL80211_ATTR_WIPHY, + /* %type: nulstring */ + NL80211_ATTR_WIPHY_NAME, + NL80211_ATTR_CMDS, + /* %type: u32 */ + NL80211_ATTR_IFTYPE, + NL80211_ATTR_INTERFACE_LIST, + NL80211_ATTR_WIPHY_LIST, + NL80211_ATTR_BSSID, + NL80211_ATTR_SSID, + NL80211_ATTR_CHANNEL, + NL80211_ATTR_PHYMODE, + NL80211_ATTR_CHANNEL_LIST, + NL80211_ATTR_BSS_LIST, + NL80211_ATTR_BSSTYPE, + NL80211_ATTR_BEACON_PERIOD, + NL80211_ATTR_DTIM_PERIOD, + NL80211_ATTR_TIMESTAMP, + NL80211_ATTR_IE, + NL80211_ATTR_AUTH_ALGORITHM, + NL80211_ATTR_TIMEOUT_TU, + NL80211_ATTR_REASON_CODE, + NL80211_ATTR_ASSOCIATION_ID, + NL80211_ATTR_DEAUTHENTICATED, + NL80211_ATTR_RX_SENSITIVITY, + NL80211_ATTR_TRANSMIT_POWER, + NL80211_ATTR_FRAG_THRESHOLD, + NL80211_ATTR_FLAG_SCAN_ACTIVE, + + NL80211_ATTR_KEY_DATA, + NL80211_ATTR_KEY_ID, + NL80211_ATTR_KEY_TYPE, + NL80211_ATTR_MAC, + NL80211_ATTR_KEY_CIPHER, + + NL80211_ATTR_BEACON_HEAD, + NL80211_ATTR_BEACON_TAIL, + + /* add attributes here, update the policy in nl80211.c */ + + /* used to define NL80211_ATTR_MAX below */ + __NL80211_ATTR_AFTER_LAST, +}; +#define NL80211_ATTR_MAX (__NL80211_ATTR_AFTER_LAST - 1) + +/** + * enum nl80211_multicast_groups - multicast groups for nl80211 + * @NL80211_GROUP_CONFIG: members of this group are notified of + * configuration changes + */ +enum nl80211_multicast_groups { + /* be notified of configuration changes like wiphy renames */ + NL80211_GROUP_CONFIG, + + /* add groups here */ + + /* keep last */ + __NL80211_GROUP_AFTER_LAST +}; +#define NL80211_GROUP_MAX (__NL80211_GROUP_AFTER_LAST - 1) + +/* + * maximum length of IE(s) passed in an NL80211_ATTR_IE. + * this is an arbitrary limit, 774 means three full-length + * IEs would fit... increase if necessary */ +#define NL80211_MAX_IE_LEN 774 + +/* + * maximum number of items in an ATTR_CHANNEL_LIST, + * just to avoid too large allocations + */ +#define NL80211_MAX_CHANNEL_LIST_ITEM 200 + +/** * enum nl80211_iftype - (virtual) interface types * @NL80211_IFTYPE_UNSPECIFIED: unspecified type, driver decides * @NL80211_IFTYPE_ADHOC: independent BSS member @@ -35,4 +246,56 @@ enum nl80211_iftype { }; #define NL80211_IFTYPE_MAX (__NL80211_IFTYPE_AFTER_LAST - 1) +/** + * enum nl80211_phymode - PHY modes + * @NL80211_PHYMODE_A: 5 GHz PHY + * @NL80211_PHYMODE_B: 2.4 GHz PHY (B mode) + * @NL80211_PHYMODE_G: 2.4 GHz PHY (G, compatible with B) + * @__NL80211_PHYMODE_AFTER_LAST: internal use + * + * These values are used for %NL80211_ATTR_PHYMODE. + */ +enum nl80211_phymode { + NL80211_PHYMODE_A, + NL80211_PHYMODE_B, + NL80211_PHYMODE_G, + + /* keep last */ + __NL80211_PHYMODE_AFTER_LAST +}; +#define NL80211_PHYMODE_MAX (__NL80211_PHYMODE_AFTER_LAST - 1) + +/** + * enum nl80211_bsstype - BSS types + * @NL80211_BSSTYPE_INFRASTRUCTURE: infrastructure BSS + * @NL80211_BSSTYPE_INDEPENDENT: independent BSS (ad-hoc network) + * @__NL80211_BSSTYPE_AFTER_LAST: internal use + * + * These values are used for %NL80211_ATTR_BSSTYPE. + */ +enum nl80211_bsstype { + NL80211_BSSTYPE_INFRASTRUCTURE, + NL80211_BSSTYPE_INDEPENDENT, + + /* keep last */ + __NL80211_BSSTYPE_AFTER_LAST +}; +#define NL80211_BSSTYPE_MAX (__NL80211_BSSTYPE_AFTER_LAST - 1) + +/** + * enum nl80211_keytype - key types + * @NL80211_KEYTYPE_GROUP: group key + * @NL80211_KEYTYPE_PAIRWISE: pairwise key + * @NL80211_KEYTYPE_PEER: peer key + */ +enum nl80211_keytype { + NL80211_KEYTYPE_GROUP, + NL80211_KEYTYPE_PAIRWISE, + NL80211_KEYTYPE_PEER, + + /* keep last */ + __NL80211_KEYTYPE_AFTER_LAST +}; +#define NL80211_KEYTYPE_MAX (__NL80211_KEYTYPE_AFTER_LAST - 1) + #endif /* __LINUX_NL80211_H */ diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h new file mode 100644 index 0000000..1ed3cbc --- /dev/null +++ b/include/linux/ssb/ssb.h @@ -0,0 +1,403 @@ +#ifndef LINUX_SSB_H_ +#define LINUX_SSB_H_ +#ifdef __KERNEL__ + +#include +#include +#include +#include +#ifdef CONFIG_SSB_PCIHOST +# include +#endif + +#include + + +struct pcmcia_device; +struct ssb_bus; +struct ssb_driver; + + +struct ssb_sprom_r1 { + u16 pci_spid; /* Subsystem Product ID for PCI */ + u16 pci_svid; /* Subsystem Vendor ID for PCI */ + u16 pci_pid; /* Product ID for PCI */ + u8 il0mac[6]; /* MAC address for 802.11b/g */ + u8 et0mac[6]; /* MAC address for Ethernet */ + u8 et1mac[6]; /* MAC address for 802.11a */ + u8 et0phyaddr:5; /* MII address for enet0 */ + u8 et1phyaddr:5; /* MII address for enet1 */ + u8 et0mdcport:1; /* MDIO for enet0 */ + u8 et1mdcport:1; /* MDIO for enet1 */ + u8 board_rev; /* Board revision */ + u8 country_code:4; /* Country Code */ + u8 antenna_a:2; /* Antenna 0/1 available for A-PHY */ + u8 antenna_bg:2; /* Antenna 0/1 available for B-PHY and G-PHY */ + u16 pa0b0; + u16 pa0b1; + u16 pa0b2; + u16 pa1b0; + u16 pa1b1; + u16 pa1b2; + u8 gpio0; /* GPIO pin 0 */ + u8 gpio1; /* GPIO pin 1 */ + u8 gpio2; /* GPIO pin 2 */ + u8 gpio3; /* GPIO pin 3 */ + u16 maxpwr_a; /* A-PHY Power Amplifier Max Power (in dBm Q5.2) */ + u16 maxpwr_bg; /* B/G-PHY Power Amplifier Max Power (in dBm Q5.2) */ + u8 itssi_a; /* Idle TSSI Target for A-PHY */ + u8 itssi_bg; /* Idle TSSI Target for B/G-PHY */ + u16 boardflags_lo; /* Boardflags (low 16 bits) */ + u8 antenna_gain_a; /* A-PHY Antenna gain (in dBm Q5.2) */ + u8 antenna_gain_bg; /* B/G-PHY Antenna gain (in dBm Q5.2) */ + u8 oem[8]; /* OEM string (rev 1 only) */ +}; + +struct ssb_sprom_r2 { + u16 boardflags_hi; /* Boardflags (high 16 bits) */ + u8 maxpwr_a_lo; /* A-PHY Max Power Low */ + u8 maxpwr_a_hi; /* A-PHY Max Power High */ + u16 pa1lob0; /* A-PHY PA Low Settings */ + u16 pa1lob1; /* A-PHY PA Low Settings */ + u16 pa1lob2; /* A-PHY PA Low Settings */ + u16 pa1hib0; /* A-PHY PA High Settings */ + u16 pa1hib1; /* A-PHY PA High Settings */ + u16 pa1hib2; /* A-PHY PA High Settings */ + u8 ofdm_pwr_off; /* OFDM Power Offset from CCK Level */ + u8 country_str[2]; /* Two char Country Code */ +}; + +struct ssb_sprom_r3 { + u32 ofdmapo; /* A-PHY OFDM Mid Power Offset */ + u32 ofdmalpo; /* A-PHY OFDM Low Power Offset */ + u32 ofdmahpo; /* A-PHY OFDM High Power Offset */ + u8 gpioldc_on_cnt; /* GPIO LED Powersave Duty Cycle ON count */ + u8 gpioldc_off_cnt; /* GPIO LED Powersave Duty Cycle OFF count */ + u8 cckpo_1M:4; /* CCK Power Offset for Rate 1M */ + u8 cckpo_2M:4; /* CCK Power Offset for Rate 2M */ + u8 cckpo_55M:4; /* CCK Power Offset for Rate 5.5M */ + u8 cckpo_11M:4; /* CCK Power Offset for Rate 11M */ + u32 ofdmgpo; /* G-PHY OFDM Power Offset */ +}; + +struct ssb_sprom_r4 { + /* TODO */ +}; + +struct ssb_sprom { + u8 revision; + u8 crc; + /* The valid r# fields are selected by the "revision". + * Revision 3 and lower inherit from lower revisions. + */ + union { + struct { + struct ssb_sprom_r1 r1; + struct ssb_sprom_r2 r2; + struct ssb_sprom_r3 r3; + }; + struct ssb_sprom_r4 r4; + }; +}; + + +struct ssb_device; +/* Lowlevel read/write operations on the device MMIO. + * Internal, don't use that outside of ssb. */ +struct ssb_bus_ops { + u16 (*read16)(struct ssb_device *dev, u16 offset); + u32 (*read32)(struct ssb_device *dev, u16 offset); + void (*write16)(struct ssb_device *dev, u16 offset, u16 value); + void (*write32)(struct ssb_device *dev, u16 offset, u32 value); +}; + + +/* Core-ID values. */ +#define SSB_DEV_CHIPCOMMON 0x800 +#define SSB_DEV_ILINE20 0x801 +#define SSB_DEV_SDRAM 0x803 +#define SSB_DEV_PCI 0x804 +#define SSB_DEV_MIPS 0x805 +#define SSB_DEV_ETHERNET 0x806 +#define SSB_DEV_V90 0x807 +#define SSB_DEV_USB11_HOSTDEV 0x808 +#define SSB_DEV_ADSL 0x809 +#define SSB_DEV_ILINE100 0x80A +#define SSB_DEV_IPSEC 0x80B +#define SSB_DEV_PCMCIA 0x80D +#define SSB_DEV_INTERNAL_MEM 0x80E +#define SSB_DEV_MEMC_SDRAM 0x80F +#define SSB_DEV_EXTIF 0x811 +#define SSB_DEV_80211 0x812 +#define SSB_DEV_MIPS_3302 0x816 +#define SSB_DEV_USB11_HOST 0x817 +#define SSB_DEV_USB11_DEV 0x818 +#define SSB_DEV_USB20_HOST 0x819 +#define SSB_DEV_USB20_DEV 0x81A +#define SSB_DEV_SDIO_HOST 0x81B +#define SSB_DEV_ROBOSWITCH 0x81C +#define SSB_DEV_PARA_ATA 0x81D +#define SSB_DEV_SATA_XORDMA 0x81E +#define SSB_DEV_ETHERNET_GBIT 0x81F +#define SSB_DEV_PCIE 0x820 +#define SSB_DEV_MIMO_PHY 0x821 +#define SSB_DEV_SRAM_CTRLR 0x822 +#define SSB_DEV_MINI_MACPHY 0x823 +#define SSB_DEV_ARM_1176 0x824 +#define SSB_DEV_ARM_7TDMI 0x825 + +/* Vendor-ID values */ +#define SSB_VENDOR_BROADCOM 0x4243 + +struct ssb_device_id { + u16 vendor; + u16 coreid; + u8 revision; +}; +#define SSB_DEVICE(_vendor, _coreid, _revision) \ + { .vendor = _vendor, .coreid = _coreid, .revision = _revision, } +#define SSB_DEVTABLE_END \ + { 0, }, + +#define SSB_ANY_VENDOR 0xFFFF +#define SSB_ANY_ID 0xFFFF +#define SSB_ANY_REV 0xFF + +/* Some kernel subsystems poke with dev->drvdata, so we must use the + * following ugly workaround to get from struct device to struct ssb_device */ +struct __ssb_dev_wrapper { + struct device dev; + struct ssb_device *sdev; +}; + +struct ssb_device { + /* Having a copy of the ops pointer in each dev struct + * is an optimization. */ + const struct ssb_bus_ops *ops; + + struct device *dev; + struct ssb_bus *bus; + struct ssb_device_id id; + + u8 core_index; + unsigned int irq; + + /* Internal-only stuff follows. */ + void *drvdata; /* Per-device data */ + void *devtypedata; /* Per-devicetype (eg 802.11) data */ +}; + +/* Go from struct device to struct ssb_device. */ +static inline +struct ssb_device * dev_to_ssb_dev(struct device *dev) +{ + struct __ssb_dev_wrapper *wrap = container_of(dev, struct __ssb_dev_wrapper, dev); + return wrap->sdev; +} + +/* Device specific user data */ +static inline +void ssb_set_drvdata(struct ssb_device *dev, void *data) +{ + dev->drvdata = data; +} +static inline +void * ssb_get_drvdata(struct ssb_device *dev) +{ + return dev->drvdata; +} + +/* Devicetype specific user data. This is per device-type (not per device) */ +void ssb_set_devtypedata(struct ssb_device *dev, void *data); +static inline +void * ssb_get_devtypedata(struct ssb_device *dev) +{ + return dev->devtypedata; +} + + +struct ssb_driver { + const char *name; + const struct ssb_device_id *id_table; + + int (*probe)(struct ssb_device *dev, const struct ssb_device_id *id); + void (*remove)(struct ssb_device *dev); + int (*suspend)(struct ssb_device *dev, pm_message_t state); + int (*resume)(struct ssb_device *dev); + void (*shutdown)(struct ssb_device *dev); + + struct device_driver drv; +}; +#define drv_to_ssb_drv(_drv) container_of(_drv, struct ssb_driver, drv) + +extern int __ssb_driver_register(struct ssb_driver *drv, struct module *owner); +static inline int ssb_driver_register(struct ssb_driver *drv) +{ + return __ssb_driver_register(drv, THIS_MODULE); +} +extern void ssb_driver_unregister(struct ssb_driver *drv); + + + + +enum ssb_bustype { + SSB_BUSTYPE_SSB, /* This SSB bus is the system bus */ + SSB_BUSTYPE_PCI, /* SSB is connected to PCI bus */ + SSB_BUSTYPE_PCMCIA, /* SSB is connected to PCMCIA bus */ +}; + +/* board_vendor */ +#define SSB_BOARDVENDOR_BCM 0x14E4 /* Broadcom */ +#define SSB_BOARDVENDOR_DELL 0x1028 /* Dell */ +#define SSB_BOARDVENDOR_HP 0x0E11 /* HP */ +/* board_type */ +#define SSB_BOARD_BCM94306MP 0x0418 +#define SSB_BOARD_BCM4309G 0x0421 +#define SSB_BOARD_BCM4306CB 0x0417 +#define SSB_BOARD_BCM4309MP 0x040C +#define SSB_BOARD_MP4318 0x044A +#define SSB_BOARD_BU4306 0x0416 +#define SSB_BOARD_BU4309 0x040A +/* chip_package */ +#define SSB_CHIPPACK_BCM4712S 1 /* Small 200pin 4712 */ +#define SSB_CHIPPACK_BCM4712M 2 /* Medium 225pin 4712 */ +#define SSB_CHIPPACK_BCM4712L 0 /* Large 340pin 4712 */ + +#include +#include +#include +#include + +struct ssb_bus { + /* The MMIO area. */ + void __iomem *mmio; + + const struct ssb_bus_ops *ops; + + /* The core in the basic address register window. (PCI bus only) */ + struct ssb_device *mapped_device; + /* Currently mapped PCMCIA segment. (bustype == SSB_BUSTYPE_PCMCIA only) */ + u8 mapped_pcmcia_seg; + /* Lock for core and segment switching. */ + spinlock_t bar_lock; + + /* The bus this backplane is running on. */ + enum ssb_bustype bustype; + /* Pointer to the PCI bus (only valid if bustype == SSB_BUSTYPE_PCI). */ + struct pci_dev *host_pci; + /* Pointer to the PCMCIA device (only if bustype == SSB_BUSTYPE_PCMCIA). */ + struct pcmcia_device *host_pcmcia; + +#ifdef CONFIG_SSB_PCIHOST + struct mutex pci_sprom_mutex; +#endif + + /* ID information about the PCB. */ + u16 board_vendor; + u16 board_type; + u16 board_rev; + /* ID information about the Chip. */ + u16 chip_id; + u16 chip_rev; + u8 chip_package; + + /* Contents of the SPROM. + * If there is no sprom (not on PCI-bus), this is emulated. */ + struct ssb_sprom sprom; + + /* List of devices (cores) on the backplane. */ + struct ssb_device devices[SSB_MAX_NR_CORES]; + u8 nr_devices; + + /* Reference count. Number of suspended devices. */ + u8 suspend_cnt; + + /* Software ID number for this bus. */ + int busnumber; + + /* The ChipCommon device (if available). */ + struct ssb_chipcommon chipco; + /* The PCI-core device (if available). */ + struct ssb_pcicore pcicore; + /* The MIPS-core device (if available). */ + struct ssb_mipscore mipscore; + /* The EXTif-core device (if available). */ + struct ssb_extif extif; + + /* Internal. */ + struct list_head list; +}; + +extern int ssb_bus_ssbbus_register(struct ssb_bus *bus, + unsigned long baseaddr, + void (*fill_sprom)(struct ssb_sprom *sprom)); +#ifdef CONFIG_SSB_PCIHOST +extern int ssb_bus_pcibus_register(struct ssb_bus *bus, + struct pci_dev *host_pci); +#endif /* CONFIG_SSB_PCIHOST */ +#ifdef CONFIG_SSB_PCMCIAHOST +extern int ssb_bus_pcmciabus_register(struct ssb_bus *bus, + struct pcmcia_device *pcmcia_dev, + unsigned long baseaddr, + void (*fill_sprom)(struct ssb_sprom *sprom)); +#endif /* CONFIG_SSB_PCMCIAHOST */ + +extern void ssb_bus_unregister(struct ssb_bus *bus); + +extern u32 ssb_clockspeed(struct ssb_bus *bus); + +int ssb_device_is_enabled(struct ssb_device *dev); +void ssb_device_enable(struct ssb_device *dev, u32 core_specific_flags); +void ssb_device_disable(struct ssb_device *dev, u32 core_specific_flags); + + +/* Device MMIO register read/write functions. */ +static inline u16 ssb_read16(struct ssb_device *dev, u16 offset) +{ + return dev->ops->read16(dev, offset); +} +static inline u32 ssb_read32(struct ssb_device *dev, u16 offset) +{ + return dev->ops->read32(dev, offset); +} +static inline void ssb_write16(struct ssb_device *dev, u16 offset, u16 value) +{ + dev->ops->write16(dev, offset, value); +} +static inline void ssb_write32(struct ssb_device *dev, u16 offset, u32 value) +{ + dev->ops->write32(dev, offset, value); +} + + +/* Translation (routing) bits that need to be ORed to DMA + * addresses before they are given to a device. */ +extern u32 ssb_dma_translation(struct ssb_device *dev); +#define SSB_DMA_TRANSLATION_MASK 0xC0000000 +#define SSB_DMA_TRANSLATION_SHIFT 30 + +extern int ssb_dma_set_mask(struct ssb_device *ssb_dev, u64 mask); + + +#ifdef CONFIG_SSB_PCIHOST +/* PCI-host wrapper driver */ +extern int ssb_pcihost_register(struct pci_driver *driver); +static inline void ssb_pcihost_unregister(struct pci_driver *driver) +{ + pci_unregister_driver(driver); +} +#endif /* CONFIG_SSB_PCIHOST */ + + +/* Bus-Power handling functions. */ +extern int ssb_bus_may_powerdown(struct ssb_bus *bus); +extern int ssb_bus_powerup(struct ssb_bus *bus, int dynamic_pctl); + + +/* Various helper functions */ +extern u32 ssb_admatch_base(u32 adm); +extern u32 ssb_admatch_size(u32 adm); + + +#endif /* __KERNEL__ */ +#endif /* LINUX_SSB_H_ */ diff --git a/include/linux/ssb/ssb_driver_chipcommon.h b/include/linux/ssb/ssb_driver_chipcommon.h new file mode 100644 index 0000000..8856590 --- /dev/null +++ b/include/linux/ssb/ssb_driver_chipcommon.h @@ -0,0 +1,387 @@ +#ifndef LINUX_SSB_CHIPCO_H_ +#define LINUX_SSB_CHIPCO_H_ + +/* SonicsSiliconBackplane CHIPCOMMON core hardware definitions + * + * The chipcommon core provides chip identification, SB control, + * jtag, 0/1/2 uarts, clock frequency control, a watchdog interrupt timer, + * gpio interface, extbus, and support for serial and parallel flashes. + * + * Copyright 2005, Broadcom Corporation + * Copyright 2006, Michael Buesch + * + * Licensed under the GPL version 2. See COPYING for details. + */ +#ifdef __KERNEL__ + +/** ChipCommon core registers. **/ + +#define SSB_CHIPCO_CHIPID 0x0000 +#define SSB_CHIPCO_IDMASK 0x0000FFFF +#define SSB_CHIPCO_REVMASK 0x000F0000 +#define SSB_CHIPCO_REVSHIFT 16 +#define SSB_CHIPCO_PACKMASK 0x00F00000 +#define SSB_CHIPCO_PACKSHIFT 20 +#define SSB_CHIPCO_NRCORESMASK 0x0F000000 +#define SSB_CHIPCO_NRCORESSHIFT 24 +#define SSB_CHIPCO_CAP 0x0004 /* Capabilities */ +#define SSB_CHIPCO_CAP_NRUART 0x00000003 /* # of UARTs */ +#define SSB_CHIPCO_CAP_MIPSEB 0x00000004 /* MIPS in BigEndian Mode */ +#define SSB_CHIPCO_CAP_UARTCLK 0x00000018 /* UART clock select */ +#define SSB_CHIPCO_CAP_UARTCLK_INT 0x00000008 /* UARTs are driven by internal divided clock */ +#define SSB_CHIPCO_CAP_UARTGPIO 0x00000020 /* UARTs on GPIO 15-12 */ +#define SSB_CHIPCO_CAP_EXTBUS 0x000000C0 /* External buses present */ +#define SSB_CHIPCO_CAP_FLASHT 0x00000700 /* Flash Type */ +#define SSB_CHIPCO_FLASHT_NONE 0x00000000 /* No flash */ +#define SSB_CHIPCO_FLASHT_STSER 0x00000100 /* ST serial flash */ +#define SSB_CHIPCO_FLASHT_ATSER 0x00000200 /* Atmel serial flash */ +#define SSB_CHIPCO_FLASHT_PARA 0x00000700 /* Parallel flash */ +#define SSB_CHIPCO_CAP_PLLT 0x00038000 /* PLL Type */ +#define SSB_PLLTYPE_NONE 0x00000000 +#define SSB_PLLTYPE_1 0x00010000 /* 48Mhz base, 3 dividers */ +#define SSB_PLLTYPE_2 0x00020000 /* 48Mhz, 4 dividers */ +#define SSB_PLLTYPE_3 0x00030000 /* 25Mhz, 2 dividers */ +#define SSB_PLLTYPE_4 0x00008000 /* 48Mhz, 4 dividers */ +#define SSB_PLLTYPE_5 0x00018000 /* 25Mhz, 4 dividers */ +#define SSB_PLLTYPE_6 0x00028000 /* 100/200 or 120/240 only */ +#define SSB_PLLTYPE_7 0x00038000 /* 25Mhz, 4 dividers */ +#define SSB_CHIPCO_CAP_PCTL 0x00040000 /* Power Control */ +#define SSB_CHIPCO_CAP_OTPS 0x00380000 /* OTP size */ +#define SSB_CHIPCO_CAP_OTPS_SHIFT 19 +#define SSB_CHIPCO_CAP_OTPS_BASE 5 +#define SSB_CHIPCO_CAP_JTAGM 0x00400000 /* JTAG master present */ +#define SSB_CHIPCO_CAP_BROM 0x00800000 /* Internal boot ROM active */ +#define SSB_CHIPCO_CAP_64BIT 0x08000000 /* 64-bit Backplane */ +#define SSB_CHIPCO_CORECTL 0x0008 +#define SSB_CHIPCO_CORECTL_UARTCLK0 0x00000001 /* Drive UART with internal clock */ +#define SSB_CHIPCO_CORECTL_SE 0x00000002 /* sync clk out enable (corerev >= 3) */ +#define SSB_CHIPCO_BIST 0x000C +#define SSB_CHIPCO_OTPS 0x0010 /* OTP status */ +#define SSB_CHIPCO_OTPS_PROGFAIL 0x80000000 +#define SSB_CHIPCO_OTPS_PROTECT 0x00000007 +#define SSB_CHIPCO_OTPS_HW_PROTECT 0x00000001 +#define SSB_CHIPCO_OTPS_SW_PROTECT 0x00000002 +#define SSB_CHIPCO_OTPS_CID_PROTECT 0x00000004 +#define SSB_CHIPCO_OTPC 0x0014 /* OTP control */ +#define SSB_CHIPCO_OTPC_RECWAIT 0xFF000000 +#define SSB_CHIPCO_OTPC_PROGWAIT 0x00FFFF00 +#define SSB_CHIPCO_OTPC_PRW_SHIFT 8 +#define SSB_CHIPCO_OTPC_MAXFAIL 0x00000038 +#define SSB_CHIPCO_OTPC_VSEL 0x00000006 +#define SSB_CHIPCO_OTPC_SELVL 0x00000001 +#define SSB_CHIPCO_OTPP 0x0018 /* OTP prog */ +#define SSB_CHIPCO_OTPP_COL 0x000000FF +#define SSB_CHIPCO_OTPP_ROW 0x0000FF00 +#define SSB_CHIPCO_OTPP_ROW_SHIFT 8 +#define SSB_CHIPCO_OTPP_READERR 0x10000000 +#define SSB_CHIPCO_OTPP_VALUE 0x20000000 +#define SSB_CHIPCO_OTPP_READ 0x40000000 +#define SSB_CHIPCO_OTPP_START 0x80000000 +#define SSB_CHIPCO_OTPP_BUSY 0x80000000 +#define SSB_CHIPCO_IRQSTAT 0x0020 +#define SSB_CHIPCO_IRQMASK 0x0024 +#define SSB_CHIPCO_IRQ_GPIO 0x00000001 /* gpio intr */ +#define SSB_CHIPCO_IRQ_EXT 0x00000002 /* ro: ext intr pin (corerev >= 3) */ +#define SSB_CHIPCO_IRQ_WDRESET 0x80000000 /* watchdog reset occurred */ +#define SSB_CHIPCO_CHIPCTL 0x0028 /* Rev >= 11 only */ +#define SSB_CHIPCO_CHIPSTAT 0x002C /* Rev >= 11 only */ +#define SSB_CHIPCO_JCMD 0x0030 /* Rev >= 10 only */ +#define SSB_CHIPCO_JCMD_START 0x80000000 +#define SSB_CHIPCO_JCMD_BUSY 0x80000000 +#define SSB_CHIPCO_JCMD_PAUSE 0x40000000 +#define SSB_CHIPCO_JCMD0_ACC_MASK 0x0000F000 +#define SSB_CHIPCO_JCMD0_ACC_IRDR 0x00000000 +#define SSB_CHIPCO_JCMD0_ACC_DR 0x00001000 +#define SSB_CHIPCO_JCMD0_ACC_IR 0x00002000 +#define SSB_CHIPCO_JCMD0_ACC_RESET 0x00003000 +#define SSB_CHIPCO_JCMD0_ACC_IRPDR 0x00004000 +#define SSB_CHIPCO_JCMD0_ACC_PDR 0x00005000 +#define SSB_CHIPCO_JCMD0_IRW_MASK 0x00000F00 +#define SSB_CHIPCO_JCMD_ACC_MASK 0x000F0000 /* Changes for corerev 11 */ +#define SSB_CHIPCO_JCMD_ACC_IRDR 0x00000000 +#define SSB_CHIPCO_JCMD_ACC_DR 0x00010000 +#define SSB_CHIPCO_JCMD_ACC_IR 0x00020000 +#define SSB_CHIPCO_JCMD_ACC_RESET 0x00030000 +#define SSB_CHIPCO_JCMD_ACC_IRPDR 0x00040000 +#define SSB_CHIPCO_JCMD_ACC_PDR 0x00050000 +#define SSB_CHIPCO_JCMD_IRW_MASK 0x00001F00 +#define SSB_CHIPCO_JCMD_IRW_SHIFT 8 +#define SSB_CHIPCO_JCMD_DRW_MASK 0x0000003F +#define SSB_CHIPCO_JIR 0x0034 /* Rev >= 10 only */ +#define SSB_CHIPCO_JDR 0x0038 /* Rev >= 10 only */ +#define SSB_CHIPCO_JCTL 0x003C /* Rev >= 10 only */ +#define SSB_CHIPCO_JCTL_FORCE_CLK 4 /* Force clock */ +#define SSB_CHIPCO_JCTL_EXT_EN 2 /* Enable external targets */ +#define SSB_CHIPCO_JCTL_EN 1 /* Enable Jtag master */ +#define SSB_CHIPCO_FLASHCTL 0x0040 +#define SSB_CHIPCO_FLASHCTL_START 0x80000000 +#define SSB_CHIPCO_FLASHCTL_BUSY SSB_CHIPCO_FLASHCTL_START +#define SSB_CHIPCO_FLASHADDR 0x0044 +#define SSB_CHIPCO_FLASHDATA 0x0048 +#define SSB_CHIPCO_BCAST_ADDR 0x0050 +#define SSB_CHIPCO_BCAST_DATA 0x0054 +#define SSB_CHIPCO_GPIOIN 0x0060 +#define SSB_CHIPCO_GPIOOUT 0x0064 +#define SSB_CHIPCO_GPIOOUTEN 0x0068 +#define SSB_CHIPCO_GPIOCTL 0x006C +#define SSB_CHIPCO_GPIOPOL 0x0070 +#define SSB_CHIPCO_GPIOIRQ 0x0074 +#define SSB_CHIPCO_WATCHDOG 0x0080 +#define SSB_CHIPCO_GPIOTIMER 0x0088 /* LED powersave (corerev >= 16) */ +#define SSB_CHIPCO_GPIOTIMER_ONTIME_SHIFT 16 +#define SSB_CHIPCO_GPIOTOUTM 0x008C /* LED powersave (corerev >= 16) */ +#define SSB_CHIPCO_CLOCK_N 0x0090 +#define SSB_CHIPCO_CLOCK_SB 0x0094 +#define SSB_CHIPCO_CLOCK_PCI 0x0098 +#define SSB_CHIPCO_CLOCK_M2 0x009C +#define SSB_CHIPCO_CLOCK_MIPS 0x00A0 +#define SSB_CHIPCO_CLKDIV 0x00A4 /* Rev >= 3 only */ +#define SSB_CHIPCO_CLKDIV_SFLASH 0x0F000000 +#define SSB_CHIPCO_CLKDIV_SFLASH_SHIFT 24 +#define SSB_CHIPCO_CLKDIV_OTP 0x000F0000 +#define SSB_CHIPCO_CLKDIV_OTP_SHIFT 16 +#define SSB_CHIPCO_CLKDIV_JTAG 0x00000F00 +#define SSB_CHIPCO_CLKDIV_JTAG_SHIFT 8 +#define SSB_CHIPCO_CLKDIV_UART 0x000000FF +#define SSB_CHIPCO_PLLONDELAY 0x00B0 /* Rev >= 4 only */ +#define SSB_CHIPCO_FREFSELDELAY 0x00B4 /* Rev >= 4 only */ +#define SSB_CHIPCO_SLOWCLKCTL 0x00B8 /* 6 <= Rev <= 9 only */ +#define SSB_CHIPCO_SLOWCLKCTL_SRC 0x00000007 /* slow clock source mask */ +#define SSB_CHIPCO_SLOWCLKCTL_SRC_LPO 0x00000000 /* source of slow clock is LPO */ +#define SSB_CHIPCO_SLOWCLKCTL_SRC_XTAL 0x00000001 /* source of slow clock is crystal */ +#define SSB_CHIPCO_SLOECLKCTL_SRC_PCI 0x00000002 /* source of slow clock is PCI */ +#define SSB_CHIPCO_SLOWCLKCTL_LPOFREQ 0x00000200 /* LPOFreqSel, 1: 160Khz, 0: 32KHz */ +#define SSB_CHIPCO_SLOWCLKCTL_LPOPD 0x00000400 /* LPOPowerDown, 1: LPO is disabled, 0: LPO is enabled */ +#define SSB_CHIPCO_SLOWCLKCTL_FSLOW 0x00000800 /* ForceSlowClk, 1: sb/cores running on slow clock, 0: power logic control */ +#define SSB_CHIPCO_SLOWCLKCTL_IPLL 0x00001000 /* IgnorePllOffReq, 1/0: power logic ignores/honors PLL clock disable requests from core */ +#define SSB_CHIPCO_SLOWCLKCTL_ENXTAL 0x00002000 /* XtalControlEn, 1/0: power logic does/doesn't disable crystal when appropriate */ +#define SSB_CHIPCO_SLOWCLKCTL_XTALPU 0x00004000 /* XtalPU (RO), 1/0: crystal running/disabled */ +#define SSB_CHIPCO_SLOWCLKCTL_CLKDIV 0xFFFF0000 /* ClockDivider (SlowClk = 1/(4+divisor)) */ +#define SSB_CHIPCO_SLOWCLKCTL_CLKDIV_SHIFT 16 +#define SSB_CHIPCO_SYSCLKCTL 0x00C0 /* Rev >= 3 only */ +#define SSB_CHIPCO_SYSCLKCTL_IDLPEN 0x00000001 /* ILPen: Enable Idle Low Power */ +#define SSB_CHIPCO_SYSCLKCTL_ALPEN 0x00000002 /* ALPen: Enable Active Low Power */ +#define SSB_CHIPCO_SYSCLKCTL_PLLEN 0x00000004 /* ForcePLLOn */ +#define SSB_CHIPCO_SYSCLKCTL_FORCEALP 0x00000008 /* Force ALP (or HT if ALPen is not set */ +#define SSB_CHIPCO_SYSCLKCTL_FORCEHT 0x00000010 /* Force HT */ +#define SSB_CHIPCO_SYSCLKCTL_CLKDIV 0xFFFF0000 /* ClkDiv (ILP = 1/(4+divisor)) */ +#define SSB_CHIPCO_SYSCLKCTL_CLKDIV_SHIFT 16 +#define SSB_CHIPCO_CLKSTSTR 0x00C4 /* Rev >= 3 only */ +#define SSB_CHIPCO_PCMCIA_CFG 0x0100 +#define SSB_CHIPCO_PCMCIA_MEMWAIT 0x0104 +#define SSB_CHIPCO_PCMCIA_ATTRWAIT 0x0108 +#define SSB_CHIPCO_PCMCIA_IOWAIT 0x010C +#define SSB_CHIPCO_IDE_CFG 0x0110 +#define SSB_CHIPCO_IDE_MEMWAIT 0x0114 +#define SSB_CHIPCO_IDE_ATTRWAIT 0x0118 +#define SSB_CHIPCO_IDE_IOWAIT 0x011C +#define SSB_CHIPCO_PROG_CFG 0x0120 +#define SSB_CHIPCO_PROG_WAITCNT 0x0124 +#define SSB_CHIPCO_FLASH_CFG 0x0128 +#define SSB_CHIPCO_FLASH_WAITCNT 0x012C +#define SSB_CHIPCO_UART0_DATA 0x0300 +#define SSB_CHIPCO_UART0_IMR 0x0304 +#define SSB_CHIPCO_UART0_FCR 0x0308 +#define SSB_CHIPCO_UART0_LCR 0x030C +#define SSB_CHIPCO_UART0_MCR 0x0310 +#define SSB_CHIPCO_UART0_LSR 0x0314 +#define SSB_CHIPCO_UART0_MSR 0x0318 +#define SSB_CHIPCO_UART0_SCRATCH 0x031C +#define SSB_CHIPCO_UART1_DATA 0x0400 +#define SSB_CHIPCO_UART1_IMR 0x0404 +#define SSB_CHIPCO_UART1_FCR 0x0408 +#define SSB_CHIPCO_UART1_LCR 0x040C +#define SSB_CHIPCO_UART1_MCR 0x0410 +#define SSB_CHIPCO_UART1_LSR 0x0414 +#define SSB_CHIPCO_UART1_MSR 0x0418 +#define SSB_CHIPCO_UART1_SCRATCH 0x041C + + + +/** Clockcontrol masks and values **/ + +/* SSB_CHIPCO_CLOCK_N */ +#define SSB_CHIPCO_CLK_N1 0x0000003F /* n1 control */ +#define SSB_CHIPCO_CLK_N2 0x00003F00 /* n2 control */ +#define SSB_CHIPCO_CLK_N2_SHIFT 8 +#define SSB_CHIPCO_CLK_PLLC 0x000F0000 /* pll control */ +#define SSB_CHIPCO_CLK_PLLC_SHIFT 16 + +/* SSB_CHIPCO_CLOCK_SB/PCI/UART */ +#define SSB_CHIPCO_CLK_M1 0x0000003F /* m1 control */ +#define SSB_CHIPCO_CLK_M2 0x00003F00 /* m2 control */ +#define SSB_CHIPCO_CLK_M2_SHIFT 8 +#define SSB_CHIPCO_CLK_M3 0x003F0000 /* m3 control */ +#define SSB_CHIPCO_CLK_M3_SHIFT 16 +#define SSB_CHIPCO_CLK_MC 0x1F000000 /* mux control */ +#define SSB_CHIPCO_CLK_MC_SHIFT 24 + +/* N3M Clock control magic field values */ +#define SSB_CHIPCO_CLK_F6_2 0x02 /* A factor of 2 in */ +#define SSB_CHIPCO_CLK_F6_3 0x03 /* 6-bit fields like */ +#define SSB_CHIPCO_CLK_F6_4 0x05 /* N1, M1 or M3 */ +#define SSB_CHIPCO_CLK_F6_5 0x09 +#define SSB_CHIPCO_CLK_F6_6 0x11 +#define SSB_CHIPCO_CLK_F6_7 0x21 + +#define SSB_CHIPCO_CLK_F5_BIAS 5 /* 5-bit fields get this added */ + +#define SSB_CHIPCO_CLK_MC_BYPASS 0x08 +#define SSB_CHIPCO_CLK_MC_M1 0x04 +#define SSB_CHIPCO_CLK_MC_M1M2 0x02 +#define SSB_CHIPCO_CLK_MC_M1M2M3 0x01 +#define SSB_CHIPCO_CLK_MC_M1M3 0x11 + +/* Type 2 Clock control magic field values */ +#define SSB_CHIPCO_CLK_T2_BIAS 2 /* n1, n2, m1 & m3 bias */ +#define SSB_CHIPCO_CLK_T2M2_BIAS 3 /* m2 bias */ + +#define SSB_CHIPCO_CLK_T2MC_M1BYP 1 +#define SSB_CHIPCO_CLK_T2MC_M2BYP 2 +#define SSB_CHIPCO_CLK_T2MC_M3BYP 4 + +/* Type 6 Clock control magic field values */ +#define SSB_CHIPCO_CLK_T6_MMASK 1 /* bits of interest in m */ +#define SSB_CHIPCO_CLK_T6_M0 120000000 /* sb clock for m = 0 */ +#define SSB_CHIPCO_CLK_T6_M1 100000000 /* sb clock for m = 1 */ +#define SSB_CHIPCO_CLK_SB2MIPS_T6(sb) (2 * (sb)) + +/* Common clock base */ +#define SSB_CHIPCO_CLK_BASE1 24000000 /* Half the clock freq */ +#define SSB_CHIPCO_CLK_BASE2 12500000 /* Alternate crystal on some PLL's */ + +/* Clock control values for 200Mhz in 5350 */ +#define SSB_CHIPCO_CLK_5350_N 0x0311 +#define SSB_CHIPCO_CLK_5350_M 0x04020009 + + +/** Bits in the config registers **/ + +#define SSB_CHIPCO_CFG_EN 0x0001 /* Enable */ +#define SSB_CHIPCO_CFG_EXTM 0x000E /* Extif Mode */ +#define SSB_CHIPCO_CFG_EXTM_ASYNC 0x0002 /* Async/Parallel flash */ +#define SSB_CHIPCO_CFG_EXTM_SYNC 0x0004 /* Synchronous */ +#define SSB_CHIPCO_CFG_EXTM_PCMCIA 0x0008 /* PCMCIA */ +#define SSB_CHIPCO_CFG_EXTM_IDE 0x000A /* IDE */ +#define SSB_CHIPCO_CFG_DS16 0x0010 /* Data size, 0=8bit, 1=16bit */ +#define SSB_CHIPCO_CFG_CLKDIV 0x0060 /* Sync: Clock divisor */ +#define SSB_CHIPCO_CFG_CLKEN 0x0080 /* Sync: Clock enable */ +#define SSB_CHIPCO_CFG_BSTRO 0x0100 /* Sync: Size/Bytestrobe */ + + +/** Flash-specific control/status values */ + +/* flashcontrol opcodes for ST flashes */ +#define SSB_CHIPCO_FLASHCTL_ST_WREN 0x0006 /* Write Enable */ +#define SSB_CHIPCO_FLASHCTL_ST_WRDIS 0x0004 /* Write Disable */ +#define SSB_CHIPCO_FLASHCTL_ST_RDSR 0x0105 /* Read Status Register */ +#define SSB_CHIPCO_FLASHCTL_ST_WRSR 0x0101 /* Write Status Register */ +#define SSB_CHIPCO_FLASHCTL_ST_READ 0x0303 /* Read Data Bytes */ +#define SSB_CHIPCO_FLASHCTL_ST_PP 0x0302 /* Page Program */ +#define SSB_CHIPCO_FLASHCTL_ST_SE 0x02D8 /* Sector Erase */ +#define SSB_CHIPCO_FLASHCTL_ST_BE 0x00C7 /* Bulk Erase */ +#define SSB_CHIPCO_FLASHCTL_ST_DP 0x00B9 /* Deep Power-down */ +#define SSB_CHIPCO_FLASHCTL_ST_RSIG 0x03AB /* Read Electronic Signature */ + +/* Status register bits for ST flashes */ +#define SSB_CHIPCO_FLASHSTA_ST_WIP 0x01 /* Write In Progress */ +#define SSB_CHIPCO_FLASHSTA_ST_WEL 0x02 /* Write Enable Latch */ +#define SSB_CHIPCO_FLASHSTA_ST_BP 0x1C /* Block Protect */ +#define SSB_CHIPCO_FLASHSTA_ST_BP_SHIFT 2 +#define SSB_CHIPCO_FLASHSTA_ST_SRWD 0x80 /* Status Register Write Disable */ + +/* flashcontrol opcodes for Atmel flashes */ +#define SSB_CHIPCO_FLASHCTL_AT_READ 0x07E8 +#define SSB_CHIPCO_FLASHCTL_AT_PAGE_READ 0x07D2 +#define SSB_CHIPCO_FLASHCTL_AT_BUF1_READ /* FIXME */ +#define SSB_CHIPCO_FLASHCTL_AT_BUF2_READ /* FIXME */ +#define SSB_CHIPCO_FLASHCTL_AT_STATUS 0x01D7 +#define SSB_CHIPCO_FLASHCTL_AT_BUF1_WRITE 0x0384 +#define SSB_CHIPCO_FLASHCTL_AT_BUF2_WRITE 0x0387 +#define SSB_CHIPCO_FLASHCTL_AT_BUF1_ERASE_PRGM 0x0283 /* Erase program */ +#define SSB_CHIPCO_FLASHCTL_AT_BUF2_ERASE_PRGM 0x0286 /* Erase program */ +#define SSB_CHIPCO_FLASHCTL_AT_BUF1_PROGRAM 0x0288 +#define SSB_CHIPCO_FLASHCTL_AT_BUF2_PROGRAM 0x0289 +#define SSB_CHIPCO_FLASHCTL_AT_PAGE_ERASE 0x0281 +#define SSB_CHIPCO_FLASHCTL_AT_BLOCK_ERASE 0x0250 +#define SSB_CHIPCO_FLASHCTL_AT_BUF1_WRER_PRGM 0x0382 /* Write erase program */ +#define SSB_CHIPCO_FLASHCTL_AT_BUF2_WRER_PRGM 0x0385 /* Write erase program */ +#define SSB_CHIPCO_FLASHCTL_AT_BUF1_LOAD 0x0253 +#define SSB_CHIPCO_FLASHCTL_AT_BUF2_LOAD 0x0255 +#define SSB_CHIPCO_FLASHCTL_AT_BUF1_COMPARE 0x0260 +#define SSB_CHIPCO_FLASHCTL_AT_BUF2_COMPARE 0x0261 +#define SSB_CHIPCO_FLASHCTL_AT_BUF1_REPROGRAM 0x0258 +#define SSB_CHIPCO_FLASHCTL_AT_BUF2_REPROGRAM 0x0259 + +/* Status register bits for Atmel flashes */ +#define SSB_CHIPCO_FLASHSTA_AT_READY 0x80 +#define SSB_CHIPCO_FLASHSTA_AT_MISMATCH 0x40 +#define SSB_CHIPCO_FLASHSTA_AT_ID 0x38 +#define SSB_CHIPCO_FLASHSTA_AT_ID_SHIFT 3 + + +/** OTP **/ + +/* OTP regions */ +#define SSB_CHIPCO_OTP_HW_REGION SSB_CHIPCO_OTPS_HW_PROTECT +#define SSB_CHIPCO_OTP_SW_REGION SSB_CHIPCO_OTPS_SW_PROTECT +#define SSB_CHIPCO_OTP_CID_REGION SSB_CHIPCO_OTPS_CID_PROTECT + +/* OTP regions (Byte offsets from otp size) */ +#define SSB_CHIPCO_OTP_SWLIM_OFF (-8) +#define SSB_CHIPCO_OTP_CIDBASE_OFF 0 +#define SSB_CHIPCO_OTP_CIDLIM_OFF 8 + +/* Predefined OTP words (Word offset from otp size) */ +#define SSB_CHIPCO_OTP_BOUNDARY_OFF (-4) +#define SSB_CHIPCO_OTP_HWSIGN_OFF (-3) +#define SSB_CHIPCO_OTP_SWSIGN_OFF (-2) +#define SSB_CHIPCO_OTP_CIDSIGN_OFF (-1) + +#define SSB_CHIPCO_OTP_CID_OFF 0 +#define SSB_CHIPCO_OTP_PKG_OFF 1 +#define SSB_CHIPCO_OTP_FID_OFF 2 +#define SSB_CHIPCO_OTP_RSV_OFF 3 +#define SSB_CHIPCO_OTP_LIM_OFF 4 + +#define SSB_CHIPCO_OTP_SIGNATURE 0x578A +#define SSB_CHIPCO_OTP_MAGIC 0x4E56 + + +struct ssb_device; +struct ssb_serial_port; + +struct ssb_chipcommon { + struct ssb_device *dev; + u32 capabilities; + /* Fast Powerup Delay constant */ + u16 fast_pwrup_delay; +}; + +extern void ssb_chipcommon_init(struct ssb_chipcommon *cc); + +#include +extern void ssb_chipco_suspend(struct ssb_chipcommon *cc, pm_message_t state); +extern void ssb_chipco_resume(struct ssb_chipcommon *cc); + +extern void ssb_chipco_get_clockcontrol(struct ssb_chipcommon *cc, + u32 *plltype, u32 *n, u32 *m); +extern void ssb_chipco_timing_init(struct ssb_chipcommon *cc, + unsigned long ns_per_cycle); + +enum ssb_clkmode { + SSB_CLKMODE_SLOW, + SSB_CLKMODE_FAST, + SSB_CLKMODE_DYNAMIC, +}; + +extern void ssb_chipco_set_clockmode(struct ssb_chipcommon *cc, + enum ssb_clkmode mode); + +#ifdef CONFIG_SSB_SERIAL +extern int ssb_chipco_serial_init(struct ssb_chipcommon *cc, + struct ssb_serial_port *ports); +#endif /* CONFIG_SSB_SERIAL */ + +#endif /* __KERNEL__ */ +#endif /* LINUX_SSB_CHIPCO_H_ */ diff --git a/include/linux/ssb/ssb_driver_extif.h b/include/linux/ssb/ssb_driver_extif.h new file mode 100644 index 0000000..278a637 --- /dev/null +++ b/include/linux/ssb/ssb_driver_extif.h @@ -0,0 +1,163 @@ +/* + * Hardware-specific External Interface I/O core definitions + * for the BCM47xx family of SiliconBackplane-based chips. + * + * The External Interface core supports a total of three external chip selects + * supporting external interfaces. One of the external chip selects is + * used for Flash, one is used for PCMCIA, and the other may be + * programmed to support either a synchronous interface or an + * asynchronous interface. The asynchronous interface can be used to + * support external devices such as UARTs and the BCM2019 Bluetooth + * baseband processor. + * The external interface core also contains 2 on-chip 16550 UARTs, clock + * frequency control, a watchdog interrupt timer, and a GPIO interface. + * + * Copyright 2005, Broadcom Corporation + * Copyright 2006, Michael Buesch + * + * Licensed under the GPL version 2. See COPYING for details. + */ +#ifndef LINUX_SSB_EXTIFCORE_H_ +#define LINUX_SSB_EXTIFCORE_H_ + +#ifdef __KERNEL__ + +struct ssb_extif { + struct ssb_device *dev; +}; + +/* external interface address space */ +#define SSB_EXTIF_PCMCIA_MEMBASE(x) (x) +#define SSB_EXTIF_PCMCIA_IOBASE(x) ((x) + 0x100000) +#define SSB_EXTIF_PCMCIA_CFGBASE(x) ((x) + 0x200000) +#define SSB_EXTIF_CFGIF_BASE(x) ((x) + 0x800000) +#define SSB_EXTIF_FLASH_BASE(x) ((x) + 0xc00000) + +#define SSB_EXTIF_NR_GPIOOUT 5 +/* GPIO NOTE: + * The multiple instances of output and output enable registers + * are present to allow driver software for multiple cores to control + * gpio outputs without needing to share a single register pair. + * Use the following helper macro to get a register offset value. + */ +#define SSB_EXTIF_GPIO_OUT(index) ({ \ + BUILD_BUG_ON(index >= SSB_EXTIF_NR_GPIOOUT); \ + SSB_EXTIF_GPIO_OUT_BASE + ((index) * 8); \ + }) +#define SSB_EXTIF_GPIO_OUTEN(index) ({ \ + BUILD_BUG_ON(index >= SSB_EXTIF_NR_GPIOOUT); \ + SSB_EXTIF_GPIO_OUTEN_BASE + ((index) * 8); \ + }) + +/** EXTIF core registers **/ + +#define SSB_EXTIF_CTL 0x0000 +#define SSB_EXTIF_CTL_UARTEN (1 << 0) /* UART enable */ +#define SSB_EXTIF_EXTSTAT 0x0004 +#define SSB_EXTIF_EXTSTAT_EMODE (1 << 0) /* Endian mode (ro) */ +#define SSB_EXTIF_EXTSTAT_EIRQPIN (1 << 1) /* External interrupt pin (ro) */ +#define SSB_EXTIF_EXTSTAT_GPIOIRQPIN (1 << 2) /* GPIO interrupt pin (ro) */ +#define SSB_EXTIF_PCMCIA_CFG 0x0010 +#define SSB_EXTIF_PCMCIA_MEMWAIT 0x0014 +#define SSB_EXTIF_PCMCIA_ATTRWAIT 0x0018 +#define SSB_EXTIF_PCMCIA_IOWAIT 0x001C +#define SSB_EXTIF_PROG_CFG 0x0020 +#define SSB_EXTIF_PROG_WAITCNT 0x0024 +#define SSB_EXTIF_FLASH_CFG 0x0028 +#define SSB_EXTIF_FLASH_WAITCNT 0x002C +#define SSB_EXTIF_WATCHDOG 0x0040 +#define SSB_EXTIF_CLOCK_N 0x0044 +#define SSB_EXTIF_CLOCK_SB 0x0048 +#define SSB_EXTIF_CLOCK_PCI 0x004C +#define SSB_EXTIF_CLOCK_MII 0x0050 +#define SSB_EXTIF_GPIO_IN 0x0060 +#define SSB_EXTIF_GPIO_OUT_BASE 0x0064 +#define SSB_EXTIF_GPIO_OUTEN_BASE 0x0068 +#define SSB_EXTIF_EJTAG_OUTEN 0x0090 +#define SSB_EXTIF_GPIO_INTPOL 0x0094 +#define SSB_EXTIF_GPIO_INTMASK 0x0098 +#define SSB_EXTIF_UART_DATA 0x0300 +#define SSB_EXTIF_UART_TIMER 0x0310 +#define SSB_EXTIF_UART_FCR 0x0320 +#define SSB_EXTIF_UART_LCR 0x0330 +#define SSB_EXTIF_UART_MCR 0x0340 +#define SSB_EXTIF_UART_LSR 0x0350 +#define SSB_EXTIF_UART_MSR 0x0360 +#define SSB_EXTIF_UART_SCRATCH 0x0370 + + + + +/* pcmcia/prog/flash_config */ +#define SSB_EXTCFG_EN (1 << 0) /* enable */ +#define SSB_EXTCFG_MODE 0xE /* mode */ +#define SSB_EXTCFG_MODE_SHIFT 1 +#define SSB_EXTCFG_MODE_FLASH 0x0 /* flash/asynchronous mode */ +#define SSB_EXTCFG_MODE_SYNC 0x2 /* synchronous mode */ +#define SSB_EXTCFG_MODE_PCMCIA 0x4 /* pcmcia mode */ +#define SSB_EXTCFG_DS16 (1 << 4) /* destsize: 0=8bit, 1=16bit */ +#define SSB_EXTCFG_BSWAP (1 << 5) /* byteswap */ +#define SSB_EXTCFG_CLKDIV 0xC0 /* clock divider */ +#define SSB_EXTCFG_CLKDIV_SHIFT 6 +#define SSB_EXTCFG_CLKDIV_2 0x0 /* backplane/2 */ +#define SSB_EXTCFG_CLKDIV_3 0x40 /* backplane/3 */ +#define SSB_EXTCFG_CLKDIV_4 0x80 /* backplane/4 */ +#define SSB_EXTCFG_CLKEN (1 << 8) /* clock enable */ +#define SSB_EXTCFG_STROBE (1 << 9) /* size/bytestrobe (synch only) */ + +/* pcmcia_memwait */ +#define SSB_PCMCIA_MEMW_0 0x0000003F /* waitcount0 */ +#define SSB_PCMCIA_MEMW_1 0x00001F00 /* waitcount1 */ +#define SSB_PCMCIA_MEMW_1_SHIFT 8 +#define SSB_PCMCIA_MEMW_2 0x001F0000 /* waitcount2 */ +#define SSB_PCMCIA_MEMW_2_SHIFT 16 +#define SSB_PCMCIA_MEMW_3 0x1F000000 /* waitcount3 */ +#define SSB_PCMCIA_MEMW_3_SHIFT 24 + +/* pcmcia_attrwait */ +#define SSB_PCMCIA_ATTW_0 0x0000003F /* waitcount0 */ +#define SSB_PCMCIA_ATTW_1 0x00001F00 /* waitcount1 */ +#define SSB_PCMCIA_ATTW_1_SHIFT 8 +#define SSB_PCMCIA_ATTW_2 0x001F0000 /* waitcount2 */ +#define SSB_PCMCIA_ATTW_2_SHIFT 16 +#define SSB_PCMCIA_ATTW_3 0x1F000000 /* waitcount3 */ +#define SSB_PCMCIA_ATTW_3_SHIFT 24 + +/* pcmcia_iowait */ +#define SSB_PCMCIA_IOW_0 0x0000003F /* waitcount0 */ +#define SSB_PCMCIA_IOW_1 0x00001F00 /* waitcount1 */ +#define SSB_PCMCIA_IOW_1_SHIFT 8 +#define SSB_PCMCIA_IOW_2 0x001F0000 /* waitcount2 */ +#define SSB_PCMCIA_IOW_2_SHIFT 16 +#define SSB_PCMCIA_IOW_3 0x1F000000 /* waitcount3 */ +#define SSB_PCMCIA_IOW_3_SHIFT 24 + +/* prog_waitcount */ +#define SSB_PROG_WCNT_0 0x0000001F /* waitcount0 */ +#define SSB_PROG_WCNT_1 0x00001F00 /* waitcount1 */ +#define SSB_PROG_WCNT_1_SHIFT 8 +#define SSB_PROG_WCNT_2 0x001F0000 /* waitcount2 */ +#define SSB_PROG_WCNT_2_SHIFT 16 +#define SSB_PROG_WCNT_3 0x1F000000 /* waitcount3 */ +#define SSB_PROG_WCNT_3_SHIFT 24 + +#define SSB_PROG_W0 0x0000000C +#define SSB_PROG_W1 0x00000A00 +#define SSB_PROG_W2 0x00020000 +#define SSB_PROG_W3 0x01000000 + +/* flash_waitcount */ +#define SSB_FLASH_WCNT_0 0x0000001F /* waitcount0 */ +#define SSB_FLASH_WCNT_1 0x00001F00 /* waitcount1 */ +#define SSB_FLASH_WCNT_1_SHIFT 8 +#define SSB_FLASH_WCNT_2 0x001F0000 /* waitcount2 */ +#define SSB_FLASH_WCNT_2_SHIFT 16 +#define SSB_FLASH_WCNT_3 0x1F000000 /* waitcount3 */ +#define SSB_FLASH_WCNT_3_SHIFT 24 + +/* watchdog */ +#define SSB_EXTIF_WATCHDOG_CLK 48000000 /* Hz */ + + +#endif /* __KERNEL__ */ +#endif /* LINUX_SSB_EXTIFCORE_H_ */ diff --git a/include/linux/ssb/ssb_driver_mips.h b/include/linux/ssb/ssb_driver_mips.h new file mode 100644 index 0000000..91f2373 --- /dev/null +++ b/include/linux/ssb/ssb_driver_mips.h @@ -0,0 +1,47 @@ +#ifndef LINUX_SSB_MIPSCORE_H_ +#define LINUX_SSB_MIPSCORE_H_ + +#ifdef __KERNEL__ + +#ifdef CONFIG_SSB_DRIVER_MIPS + +struct ssb_device; + +struct ssb_serial_port { + void *regs; + unsigned long clockspeed; + unsigned int irq; + unsigned int baud_base; + unsigned int reg_shift; +}; + + +struct ssb_mipscore { + struct ssb_device *dev; + + int nr_serial_ports; + struct ssb_serial_port serial_ports[4]; + + u32 flash_window; + u32 flash_window_size; +}; + +extern void ssb_mipscore_init(struct ssb_mipscore *mcore); + +extern unsigned int ssb_mips_irq(struct ssb_device *dev); + + +#else /* CONFIG_SSB_DRIVER_MIPS */ + +struct ssb_mipscore { +}; + +static inline +void ssb_mipscore_init(struct ssb_mipscore *mcore) +{ +} + +#endif /* CONFIG_SSB_DRIVER_MIPS */ + +#endif /* __KERNEL__ */ +#endif /* LINUX_SSB_MIPSCORE_H_ */ diff --git a/include/linux/ssb/ssb_driver_pci.h b/include/linux/ssb/ssb_driver_pci.h new file mode 100644 index 0000000..5132f26 --- /dev/null +++ b/include/linux/ssb/ssb_driver_pci.h @@ -0,0 +1,108 @@ +#ifndef LINUX_SSB_PCICORE_H_ +#define LINUX_SSB_PCICORE_H_ +#ifdef __KERNEL__ + +#ifdef CONFIG_SSB_DRIVER_PCICORE + +/* PCI core registers. */ +#define SSB_PCICORE_CTL 0x0000 /* PCI Control */ +#define SSB_PCICORE_CTL_RST_OE 0x00000001 /* PCI_RESET Output Enable */ +#define SSB_PCICORE_CTL_RST 0x00000002 /* PCI_RESET driven out to pin */ +#define SSB_PCICORE_CTL_CLK_OE 0x00000004 /* Clock gate Output Enable */ +#define SSB_PCICORE_CTL_CLK 0x00000008 /* Gate for clock driven out to pin */ +#define SSB_PCICORE_ARBCTL 0x0010 /* PCI Arbiter Control */ +#define SSB_PCICORE_ARBCTL_INTERN 0x00000001 /* Use internal arbiter */ +#define SSB_PCICORE_ARBCTL_EXTERN 0x00000002 /* Use external arbiter */ +#define SSB_PCICORE_ARBCTL_PARKID 0x00000006 /* Mask, selects which agent is parked on an idle bus */ +#define SSB_PCICORE_ARBCTL_PARKID_LAST 0x00000000 /* Last requestor */ +#define SSB_PCICORE_ARBCTL_PARKID_4710 0x00000002 /* 4710 */ +#define SSB_PCICORE_ARBCTL_PARKID_EXT0 0x00000004 /* External requestor 0 */ +#define SSB_PCICORE_ARBCTL_PARKID_EXT1 0x00000006 /* External requestor 1 */ +#define SSB_PCICORE_ISTAT 0x0020 /* Interrupt status */ +#define SSB_PCICORE_ISTAT_INTA 0x00000001 /* PCI INTA# */ +#define SSB_PCICORE_ISTAT_INTB 0x00000002 /* PCI INTB# */ +#define SSB_PCICORE_ISTAT_SERR 0x00000004 /* PCI SERR# (write to clear) */ +#define SSB_PCICORE_ISTAT_PERR 0x00000008 /* PCI PERR# (write to clear) */ +#define SSB_PCICORE_ISTAT_PME 0x00000010 /* PCI PME# */ +#define SSB_PCICORE_IMASK 0x0024 /* Interrupt mask */ +#define SSB_PCICORE_IMASK_INTA 0x00000001 /* PCI INTA# */ +#define SSB_PCICORE_IMASK_INTB 0x00000002 /* PCI INTB# */ +#define SSB_PCICORE_IMASK_SERR 0x00000004 /* PCI SERR# */ +#define SSB_PCICORE_IMASK_PERR 0x00000008 /* PCI PERR# */ +#define SSB_PCICORE_IMASK_PME 0x00000010 /* PCI PME# */ +#define SSB_PCICORE_MBOX 0x0028 /* Backplane to PCI Mailbox */ +#define SSB_PCICORE_MBOX_F0_0 0x00000100 /* PCI function 0, INT 0 */ +#define SSB_PCICORE_MBOX_F0_1 0x00000200 /* PCI function 0, INT 1 */ +#define SSB_PCICORE_MBOX_F1_0 0x00000400 /* PCI function 1, INT 0 */ +#define SSB_PCICORE_MBOX_F1_1 0x00000800 /* PCI function 1, INT 1 */ +#define SSB_PCICORE_MBOX_F2_0 0x00001000 /* PCI function 2, INT 0 */ +#define SSB_PCICORE_MBOX_F2_1 0x00002000 /* PCI function 2, INT 1 */ +#define SSB_PCICORE_MBOX_F3_0 0x00004000 /* PCI function 3, INT 0 */ +#define SSB_PCICORE_MBOX_F3_1 0x00008000 /* PCI function 3, INT 1 */ +#define SSB_PCICORE_BCAST_ADDR 0x0050 /* Backplane Broadcast Address */ +#define SSB_PCICORE_BCAST_ADDR_MASK 0x000000FF +#define SSB_PCICORE_BCAST_DATA 0x0054 /* Backplane Broadcast Data */ +#define SSB_PCICORE_GPIO_IN 0x0060 /* rev >= 2 only */ +#define SSB_PCICORE_GPIO_OUT 0x0064 /* rev >= 2 only */ +#define SSB_PCICORE_GPIO_ENABLE 0x0068 /* rev >= 2 only */ +#define SSB_PCICORE_GPIO_CTL 0x006C /* rev >= 2 only */ +#define SSB_PCICORE_SBTOPCI0 0x0100 /* Backplane to PCI translation 0 (sbtopci0) */ +#define SSB_PCICORE_SBTOPCI0_MASK 0xFC000000 +#define SSB_PCICORE_SBTOPCI1 0x0104 /* Backplane to PCI translation 1 (sbtopci1) */ +#define SSB_PCICORE_SBTOPCI1_MASK 0xFC000000 +#define SSB_PCICORE_SBTOPCI2 0x0108 /* Backplane to PCI translation 2 (sbtopci2) */ +#define SSB_PCICORE_SBTOPCI2_MASK 0xC0000000 + +/* SBtoPCIx */ +#define SSB_PCICORE_SBTOPCI_MEM 0x00000000 +#define SSB_PCICORE_SBTOPCI_IO 0x00000001 +#define SSB_PCICORE_SBTOPCI_CFG0 0x00000002 +#define SSB_PCICORE_SBTOPCI_CFG1 0x00000003 +#define SSB_PCICORE_SBTOPCI_PREF 0x00000004 /* Prefetch enable */ +#define SSB_PCICORE_SBTOPCI_BURST 0x00000008 /* Burst enable */ +#define SSB_PCICORE_SBTOPCI_MRM 0x00000020 /* Memory Read Multiple */ +#define SSB_PCICORE_SBTOPCI_RC 0x00000030 /* Read Command mask (rev >= 11) */ +#define SSB_PCICORE_SBTOPCI_RC_READ 0x00000000 /* Memory read */ +#define SSB_PCICORE_SBTOPCI_RC_READL 0x00000010 /* Memory read line */ +#define SSB_PCICORE_SBTOPCI_RC_READM 0x00000020 /* Memory read multiple */ + + +/* PCIcore specific boardflags */ +#define SSB_PCICORE_BFL_NOPCI 0x00000400 /* Board leaves PCI floating */ + + +struct ssb_pcicore { + struct ssb_device *dev; + u8 setup_done:1; + u8 hostmode:1; + u8 cardbusmode:1; +}; + +extern void ssb_pcicore_init(struct ssb_pcicore *pc); + +/* Enable IRQ routing for a specific device */ +extern int ssb_pcicore_dev_irqvecs_enable(struct ssb_pcicore *pc, + struct ssb_device *dev); + + +#else /* CONFIG_SSB_DRIVER_PCICORE */ + + +struct ssb_pcicore { +}; + +static inline +void ssb_pcicore_init(struct ssb_pcicore *pc) +{ +} + +static inline +int ssb_pcicore_dev_irqvecs_enable(struct ssb_pcicore *pc, + struct ssb_device *dev) +{ + return 0; +} + +#endif /* CONFIG_SSB_DRIVER_PCICORE */ +#endif /* __KERNEL__ */ +#endif /* LINUX_SSB_PCICORE_H_ */ diff --git a/include/linux/ssb/ssb_regs.h b/include/linux/ssb/ssb_regs.h new file mode 100644 index 0000000..e1c7ff7 --- /dev/null +++ b/include/linux/ssb/ssb_regs.h @@ -0,0 +1,294 @@ +#ifndef LINUX_SSB_REGS_H_ +#define LINUX_SSB_REGS_H_ +#ifdef __KERNEL__ + + +/* SiliconBackplane Address Map. + * All regions may not exist on all chips. + */ +#define SSB_SDRAM_BASE 0x00000000 /* Physical SDRAM */ +#define SSB_PCI_MEM 0x08000000 /* Host Mode sb2pcitranslation0 (64 MB) */ +#define SSB_PCI_CFG 0x0c000000 /* Host Mode sb2pcitranslation1 (64 MB) */ +#define SSB_SDRAM_SWAPPED 0x10000000 /* Byteswapped Physical SDRAM */ +#define SSB_ENUM_BASE 0x18000000 /* Enumeration space base */ +#define SSB_ENUM_LIMIT 0x18010000 /* Enumeration space limit */ + +#define SSB_FLASH2 0x1c000000 /* Flash Region 2 (region 1 shadowed here) */ +#define SSB_FLASH2_SZ 0x02000000 /* Size of Flash Region 2 */ + +#define SSB_EXTIF_BASE 0x1f000000 /* External Interface region base address */ +#define SSB_FLASH1 0x1fc00000 /* Flash Region 1 */ +#define SSB_FLASH1_SZ 0x00400000 /* Size of Flash Region 1 */ + +#define SSB_PCI_DMA 0x40000000 /* Client Mode sb2pcitranslation2 (1 GB) */ +#define SSB_PCI_DMA_SZ 0x40000000 /* Client Mode sb2pcitranslation2 size in bytes */ +#define SSB_PCIE_DMA_L32 0x00000000 /* PCIE Client Mode sb2pcitranslation2 (2 ZettaBytes), low 32 bits */ +#define SSB_PCIE_DMA_H32 0x80000000 /* PCIE Client Mode sb2pcitranslation2 (2 ZettaBytes), high 32 bits */ +#define SSB_EUART (SB_EXTIF_BASE + 0x00800000) +#define SSB_LED (SB_EXTIF_BASE + 0x00900000) + + +/* Enumeration space constants */ +#define SSB_CORE_SIZE 0x1000 /* Size of a core MMIO area */ +#define SSB_MAX_NR_CORES ((SSB_ENUM_LIMIT - SSB_ENUM_BASE) / SSB_CORE_SIZE) + + +/* mips address */ +#define SSB_EJTAG 0xff200000 /* MIPS EJTAG space (2M) */ + + +/* SSB PCI config space registers. */ +#define SSB_PMCSR 0x44 +#define SSB_PE 0x100 +#define SSB_BAR0_WIN 0x80 /* Backplane address space 0 */ +#define SSB_BAR1_WIN 0x84 /* Backplane address space 1 */ +#define SSB_SPROMCTL 0x88 /* SPROM control */ +#define SSB_SPROMCTL_WE 0x10 /* SPROM write enable */ +#define SSB_BAR1_CONTROL 0x8c /* Address space 1 burst control */ +#define SSB_PCI_IRQS 0x90 /* PCI interrupts */ +#define SSB_PCI_IRQMASK 0x94 /* PCI IRQ control and mask (pcirev >= 6 only) */ +#define SSB_BACKPLANE_IRQS 0x98 /* Backplane Interrupts */ +#define SSB_GPIO_IN 0xB0 /* GPIO Input (pcirev >= 3 only) */ +#define SSB_GPIO_OUT 0xB4 /* GPIO Output (pcirev >= 3 only) */ +#define SSB_GPIO_OUT_ENABLE 0xB8 /* GPIO Output Enable/Disable (pcirev >= 3 only) */ +#define SSB_GPIO_SCS 0x10 /* PCI config space bit 4 for 4306c0 slow clock source */ +#define SSB_GPIO_HWRAD 0x20 /* PCI config space GPIO 13 for hw radio disable */ +#define SSB_GPIO_XTAL 0x40 /* PCI config space GPIO 14 for Xtal powerup */ +#define SSB_GPIO_PLL 0x80 /* PCI config space GPIO 15 for PLL powerdown */ + + +#define SSB_BAR0_MAX_RETRIES 50 + +/* Silicon backplane configuration register definitions */ +#define SSB_IPSFLAG 0x0F08 +#define SSB_IPSFLAG_IRQ1 0x0000003F /* which sbflags get routed to mips interrupt 1 */ +#define SSB_IPSFLAG_IRQ1_SHIFT 0 +#define SSB_IPSFLAG_IRQ2 0x00003F00 /* which sbflags get routed to mips interrupt 2 */ +#define SSB_IPSFLAG_IRQ2_SHIFT 8 +#define SSB_IPSFLAG_IRQ3 0x003F0000 /* which sbflags get routed to mips interrupt 3 */ +#define SSB_IPSFLAG_IRQ3_SHIFT 16 +#define SSB_IPSFLAG_IRQ4 0x3F000000 /* which sbflags get routed to mips interrupt 4 */ +#define SSB_IPSFLAG_IRQ4_SHIFT 24 +#define SSB_TPSFLAG 0x0F18 +#define SSB_TPSFLAG_BPFLAG 0x0000003F /* Backplane flag # */ +#define SSB_TPSFLAG_ALWAYSIRQ 0x00000040 /* IRQ is always sent on the Backplane */ +#define SSB_TMERRLOGA 0x0F48 +#define SSB_TMERRLOG 0x0F50 +#define SSB_ADMATCH3 0x0F60 +#define SSB_ADMATCH2 0x0F68 +#define SSB_ADMATCH1 0x0F70 +#define SSB_IMSTATE 0x0F90 /* SB Initiator Agent State */ +#define SSB_IMSTATE_PC 0x0000000f /* Pipe Count */ +#define SSB_IMSTATE_AP_MASK 0x00000030 /* Arbitration Priority */ +#define SSB_IMSTATE_AP_BOTH 0x00000000 /* Use both timeslices and token */ +#define SSB_IMSTATE_AP_TS 0x00000010 /* Use timeslices only */ +#define SSB_IMSTATE_AP_TK 0x00000020 /* Use token only */ +#define SSB_IMSTATE_AP_RSV 0x00000030 /* Reserved */ +#define SSB_IMSTATE_IBE 0x00020000 /* In Band Error */ +#define SSB_IMSTATE_TO 0x00040000 /* Timeout */ +#define SSB_INTVEC 0x0F94 /* SB Interrupt Mask */ +#define SSB_INTVEC_PCI 0x00000001 /* Enable interrupts for PCI */ +#define SSB_INTVEC_ENET0 0x00000002 /* Enable interrupts for enet 0 */ +#define SSB_INTVEC_ILINE20 0x00000004 /* Enable interrupts for iline20 */ +#define SSB_INTVEC_CODEC 0x00000008 /* Enable interrupts for v90 codec */ +#define SSB_INTVEC_USB 0x00000010 /* Enable interrupts for usb */ +#define SSB_INTVEC_EXTIF 0x00000020 /* Enable interrupts for external i/f */ +#define SSB_INTVEC_ENET1 0x00000040 /* Enable interrupts for enet 1 */ +#define SSB_TMSLOW 0x0F98 /* SB Target State Low */ +#define SSB_TMSLOW_RESET 0x00000001 /* Reset */ +#define SSB_TMSLOW_REJECT_22 0x00000002 /* Reject (Backplane rev 2.2) */ +#define SSB_TMSLOW_REJECT_23 0x00000004 /* Reject (Backplane rev 2.3) */ +#define SSB_TMSLOW_CLOCK 0x00010000 /* Clock Enable */ +#define SSB_TMSLOW_FGC 0x00020000 /* Force Gated Clocks On */ +#define SSB_TMSLOW_PE 0x40000000 /* Power Management Enable */ +#define SSB_TMSLOW_BE 0x80000000 /* BIST Enable */ +#define SSB_TMSHIGH 0x0F9C /* SB Target State High */ +#define SSB_TMSHIGH_SERR 0x00000001 /* S-error */ +#define SSB_TMSHIGH_INT 0x00000002 /* Interrupt */ +#define SSB_TMSHIGH_BUSY 0x00000004 /* Busy */ +#define SSB_TMSHIGH_TO 0x00000020 /* Timeout. Backplane rev >= 2.3 only */ +#define SSB_TMSHIGH_COREFL 0x1FFF0000 /* Core specific flags */ +#define SSB_TMSHIGH_COREFL_SHIFT 16 +#define SSB_TMSHIGH_DMA64 0x10000000 /* 64bit DMA supported */ +#define SSB_TMSHIGH_GCR 0x20000000 /* Gated Clock Request */ +#define SSB_TMSHIGH_BISTF 0x40000000 /* BIST Failed */ +#define SSB_TMSHIGH_BISTD 0x80000000 /* BIST Done */ +#define SSB_BWA0 0x0FA0 +#define SSB_IMCFGLO 0x0FA8 +#define SSB_IMCFGLO_SERTO 0x00000007 /* Service timeout */ +#define SSB_IMCFGLO_REQTO 0x00000070 /* Request timeout */ +#define SSB_IMCFGLO_REQTO_SHIFT 4 +#define SSB_IMCFGLO_CONNID 0x00FF0000 /* Connection ID */ +#define SSB_IMCFGLO_CONNID_SHIFT 16 +#define SSB_IMCFGHI 0x0FAC +#define SSB_ADMATCH0 0x0FB0 +#define SSB_TMCFGLO 0x0FB8 +#define SSB_TMCFGHI 0x0FBC +#define SSB_BCONFIG 0x0FC0 +#define SSB_BSTATE 0x0FC8 +#define SSB_ACTCFG 0x0FD8 +#define SSB_FLAGST 0x0FE8 +#define SSB_IDLOW 0x0FF8 +#define SSB_IDLOW_CFGSP 0x00000003 /* Config Space */ +#define SSB_IDLOW_ADDRNGE 0x00000038 /* Address Ranges supported */ +#define SSB_IDLOW_ADDRNGE_SHIFT 3 +#define SSB_IDLOW_SYNC 0x00000040 +#define SSB_IDLOW_INITIATOR 0x00000080 +#define SSB_IDLOW_MIBL 0x00000F00 /* Minimum Backplane latency */ +#define SSB_IDLOW_MIBL_SHIFT 8 +#define SSB_IDLOW_MABL 0x0000F000 /* Maximum Backplane latency */ +#define SSB_IDLOW_MABL_SHIFT 12 +#define SSB_IDLOW_TIF 0x00010000 /* This Initiator is first */ +#define SSB_IDLOW_CCW 0x000C0000 /* Cycle counter width */ +#define SSB_IDLOW_CCW_SHIFT 18 +#define SSB_IDLOW_TPT 0x00F00000 /* Target ports */ +#define SSB_IDLOW_TPT_SHIFT 20 +#define SSB_IDLOW_INITP 0x0F000000 /* Initiator ports */ +#define SSB_IDLOW_INITP_SHIFT 24 +#define SSB_IDLOW_SSBREV 0xF0000000 /* Sonics Backplane Revision code */ +#define SSB_IDLOW_SSBREV_22 0x00000000 /* <= 2.2 */ +#define SSB_IDLOW_SSBREV_23 0x10000000 /* 2.3 */ +#define SSB_IDHIGH 0x0FFC /* SB Identification High */ +#define SSB_IDHIGH_RCLO 0x0000000F /* Revision Code (low part) */ +#define SSB_IDHIGH_CC 0x00008FF0 /* Core Code */ +#define SSB_IDHIGH_CC_SHIFT 4 +#define SSB_IDHIGH_RCHI 0x00007000 /* Revision Code (high part) */ +#define SSB_IDHIGH_RCHI_SHIFT 8 /* yes, shift 8 is right */ +#define SSB_IDHIGH_VC 0xFFFF0000 /* Vendor Code */ +#define SSB_IDHIGH_VC_SHIFT 16 + +/* SPROM shadow area. If not otherwise noted, fields are + * two bytes wide. Note that the SPROM can _only_ be read + * in two-byte quantinies. + */ +#define SSB_SPROMSIZE_WORDS 64 +#define SSB_SPROMSIZE_BYTES (SSB_SPROMSIZE_WORDS * sizeof(u16)) +#define SSB_SPROM_BASE 0x1000 +#define SSB_SPROM_REVISION 0x107E +#define SSB_SPROM_REVISION_REV 0x00FF /* SPROM Revision number */ +#define SSB_SPROM_REVISION_CRC 0xFF00 /* SPROM CRC8 value */ +#define SSB_SPROM_REVISION_CRC_SHIFT 8 +/* SPROM Revision 1 */ +#define SSB_SPROM1_SPID 0x1004 /* Subsystem Product ID for PCI */ +#define SSB_SPROM1_SVID 0x1006 /* Subsystem Vendor ID for PCI */ +#define SSB_SPROM1_PID 0x1008 /* Product ID for PCI */ +#define SSB_SPROM1_IL0MAC 0x1048 /* 6 bytes MAC address for 802.11b/g */ +#define SSB_SPROM1_ET0MAC 0x104E /* 6 bytes MAC address for Ethernet */ +#define SSB_SPROM1_ET1MAC 0x1054 /* 6 bytes MAC address for 802.11a */ +#define SSB_SPROM1_ETHPHY 0x105A /* Ethernet PHY settings */ +#define SSB_SPROM1_ETHPHY_ET0A 0x001F /* MII Address for enet0 */ +#define SSB_SPROM1_ETHPHY_ET1A 0x03E0 /* MII Address for enet1 */ +#define SSB_SPROM1_ETHPHY_ET1A_SHIFT 5 +#define SSB_SPROM1_ETHPHY_ET0M (1<<14) /* MDIO for enet0 */ +#define SSB_SPROM1_ETHPHY_ET1M (1<<15) /* MDIO for enet1 */ +#define SSB_SPROM1_BINF 0x105C /* Board info */ +#define SSB_SPROM1_BINF_BREV 0x00FF /* Board Revision */ +#define SSB_SPROM1_BINF_CCODE 0x0F00 /* Country Code */ +#define SSB_SPROM1_BINF_CCODE_SHIFT 8 +#define SSB_SPROM1_BINF_ANTA 0x3000 /* Available A-PHY antennas */ +#define SSB_SPROM1_BINF_ANTA_SHIFT 12 +#define SSB_SPROM1_BINF_ANTBG 0xC000 /* Available B-PHY antennas */ +#define SSB_SPROM1_BINF_ANTBG_SHIFT 14 +#define SSB_SPROM1_PA0B0 0x105E +#define SSB_SPROM1_PA0B1 0x1060 +#define SSB_SPROM1_PA0B2 0x1062 +#define SSB_SPROM1_GPIOA 0x1064 /* General Purpose IO pins 0 and 1 */ +#define SSB_SPROM1_GPIOA_P0 0x00FF /* Pin 0 */ +#define SSB_SPROM1_GPIOA_P1 0xFF00 /* Pin 1 */ +#define SSB_SPROM1_GPIOA_P1_SHIFT 8 +#define SSB_SPROM1_GPIOB 0x1066 /* General Purpuse IO pins 2 and 3 */ +#define SSB_SPROM1_GPIOB_P2 0x00FF /* Pin 2 */ +#define SSB_SPROM1_GPIOB_P3 0xFF00 /* Pin 3 */ +#define SSB_SPROM1_GPIOB_P3_SHIFT 8 +#define SSB_SPROM1_MAXPWR 0x1068 /* Power Amplifier Max Power */ +#define SSB_SPROM1_MAXPWR_A 0x00FF /* A-PHY (in dBm Q5.2) */ +#define SSB_SPROM1_MAXPWR_BG 0xFF00 /* B-PHY and G-PHY (in dBm Q5.2) */ +#define SSB_SPROM1_MAXPWR_BG_SHIFT 8 +#define SSB_SPROM1_PA1B0 0x106A +#define SSB_SPROM1_PA1B1 0x106C +#define SSB_SPROM1_PA1B2 0x106E +#define SSB_SPROM1_ITSSI 0x1070 /* Idle TSSI Target */ +#define SSB_SPROM1_ITSSI_A 0x00FF /* A-PHY */ +#define SSB_SPROM1_ITSSI_BG 0xFF00 /* B-PHY and G-PHY */ +#define SSB_SPROM1_ITSSI_BG_SHIFT 8 +#define SSB_SPROM1_BFLLO 0x1072 /* Boardflags (low 16 bits) */ +#define SSB_SPROM1_AGAIN 0x1074 /* Antenna Gain (in dBm Q5.2) */ +#define SSB_SPROM1_AGAIN_A 0x00FF /* A-PHY */ +#define SSB_SPROM1_AGAIN_BG 0xFF00 /* B-PHY and G-PHY */ +#define SSB_SPROM1_AGAIN_BG_SHIFT 8 +#define SSB_SPROM1_OEM 0x1076 /* 8 bytes OEM string (rev 1 only) */ +/* SPROM Revision 2 (inherits from rev 1) */ +#define SSB_SPROM2_BFLHI 0x1038 /* Boardflags (high 16 bits) */ +#define SSB_SPROM2_MAXP_A 0x103A /* A-PHY Max Power */ +#define SSB_SPROM2_MAXP_A_HI 0x00FF /* Max Power High */ +#define SSB_SPROM2_MAXP_A_LO 0xFF00 /* Max Power Low */ +#define SSB_SPROM2_MAXP_A_LO_SHIFT 8 +#define SSB_SPROM2_PA1LOB0 0x103C /* A-PHY PowerAmplifier Low Settings */ +#define SSB_SPROM2_PA1LOB1 0x103E /* A-PHY PowerAmplifier Low Settings */ +#define SSB_SPROM2_PA1LOB2 0x1040 /* A-PHY PowerAmplifier Low Settings */ +#define SSB_SPROM2_PA1HIB0 0x1042 /* A-PHY PowerAmplifier High Settings */ +#define SSB_SPROM2_PA1HIB1 0x1044 /* A-PHY PowerAmplifier High Settings */ +#define SSB_SPROM2_PA1HIB2 0x1046 /* A-PHY PowerAmplifier High Settings */ +#define SSB_SPROM2_OPO 0x1078 /* OFDM Power Offset from CCK Level */ +#define SSB_SPROM2_OPO_VALUE 0x00FF +#define SSB_SPROM2_OPO_UNUSED 0xFF00 +#define SSB_SPROM2_CCODE 0x107C /* Two char Country Code */ +/* SPROM Revision 3 (inherits from rev 2) */ +#define SSB_SPROM3_OFDMAPO 0x102C /* A-PHY OFDM Mid Power Offset (4 bytes, BigEndian) */ +#define SSB_SPROM3_OFDMALPO 0x1030 /* A-PHY OFDM Low Power Offset (4 bytes, BigEndian) */ +#define SSB_SPROM3_OFDMAHPO 0x1034 /* A-PHY OFDM High Power Offset (4 bytes, BigEndian) */ +#define SSB_SPROM3_GPIOLDC 0x1042 /* GPIO LED Powersave Duty Cycle (4 bytes, BigEndian) */ +#define SSB_SPROM3_GPIOLDC_OFF 0x0000FF00 /* Off Count */ +#define SSB_SPROM3_GPIOLDC_OFF_SHIFT 8 +#define SSB_SPROM3_GPIOLDC_ON 0x00FF0000 /* On Count */ +#define SSB_SPROM3_GPIOLDC_ON_SHIFT 16 +#define SSB_SPROM3_CCKPO 0x1078 /* CCK Power Offset */ +#define SSB_SPROM3_CCKPO_1M 0x000F /* 1M Rate PO */ +#define SSB_SPROM3_CCKPO_2M 0x00F0 /* 2M Rate PO */ +#define SSB_SPROM3_CCKPO_2M_SHIFT 4 +#define SSB_SPROM3_CCKPO_55M 0x0F00 /* 5.5M Rate PO */ +#define SSB_SPROM3_CCKPO_55M_SHIFT 8 +#define SSB_SPROM3_CCKPO_11M 0xF000 /* 11M Rate PO */ +#define SSB_SPROM3_CCKPO_11M_SHIFT 12 +#define SSB_SPROM3_OFDMGPO 0x107A /* G-PHY OFDM Power Offset (4 bytes, BigEndian) */ + +/* Values for SSB_SPROM1_BINF_CCODE */ +enum { + SSB_SPROM1CCODE_WORLD = 0, + SSB_SPROM1CCODE_THAILAND, + SSB_SPROM1CCODE_ISRAEL, + SSB_SPROM1CCODE_JORDAN, + SSB_SPROM1CCODE_CHINA, + SSB_SPROM1CCODE_JAPAN, + SSB_SPROM1CCODE_USA_CANADA_ANZ, + SSB_SPROM1CCODE_EUROPE, + SSB_SPROM1CCODE_USA_LOW, + SSB_SPROM1CCODE_JAPAN_HIGH, + SSB_SPROM1CCODE_ALL, + SSB_SPROM1CCODE_NONE, +}; + +/* Address-Match values and masks (SSB_ADMATCH?) */ +#define SSB_ADM_TYPE 0x00000003 /* Address type */ +#define SSB_ADM_TYPE0 0 +#define SSB_ADM_TYPE1 1 +#define SSB_ADM_TYPE2 2 +#define SSB_ADM_AD64 0x00000004 +#define SSB_ADM_SZ0 0x000000F8 /* Type0 size */ +#define SSB_ADM_SZ0_SHIFT 3 +#define SSB_ADM_SZ1 0x000001F8 /* Type1 size */ +#define SSB_ADM_SZ1_SHIFT 3 +#define SSB_ADM_SZ2 0x000001F8 /* Type2 size */ +#define SSB_ADM_SZ2_SHIFT 3 +#define SSB_ADM_EN 0x00000400 /* Enable */ +#define SSB_ADM_NEG 0x00000800 /* Negative decode */ +#define SSB_ADM_BASE0 0xFFFFFF00 /* Type0 base address */ +#define SSB_ADM_BASE0_SHIFT 8 +#define SSB_ADM_BASE1 0xFFFFF000 /* Type1 base address for the core */ +#define SSB_ADM_BASE1_SHIFT 12 +#define SSB_ADM_BASE2 0xFFFF0000 /* Type2 base address for the core */ +#define SSB_ADM_BASE2_SHIFT 16 + + +#endif /* __KERNEL__ */ +#endif /* LINUX_SSB_REGS_H_ */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 88171f8..813706a 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -11,6 +11,69 @@ #include * Copyright 2006 Johannes Berg */ +/** + * struct scan_channel - describes a single channel to scan + * @phymode: PHY mode for this channel + * @channel: channel number (1-14, ...) + * @active: scan actively or passively on this channel + */ +struct scan_channel { + int phymode; + u32 channel; + int active; +}; + +/** + * struct scan_params - describes scan parameters + * @n_channels: number of items in @channels array or -1 to indicate all + * channels should be scanned (in that case @channels will be %NULL) + * @active: when n_channels is -1 this determines active/passive scanning. + * @phymode: when n_channels is -1 this determines PHY mode to scan. It is + * not possible to scan different PHY modes in one request w/o giving + * a channel list. + * @channels: array containing @n_channels &struct scan_channel items + */ +struct scan_params { + int n_channels; + int active; + int phymode; + struct scan_channel *channels; +}; + +/** + * struct association_params - describes association parameters + * @valid: this member contains flags which items are valid + * @bssid: the BSSID of the BSS to associate [%ASSOC_PARAMS_BSSID] + * @timeout: timeout (in TU) [%ASSOC_PARAMS_TIMEOUT] + * @ie: information element(s) to include in the association frames [%ASSOC_PARAMS_IE] + * @ie_len: length of the information element(s) + * @ssid: the SSID, always valid. + * @ssid_len: length of the SSID + */ +struct association_params { + u8 *bssid; + u32 timeout; + u8 *ie; + int ie_len; + u8 *ssid; + int ssid_len; + + unsigned int valid; +}; +#define ASSOC_PARAMS_TIMEOUT (1<<0) + +/** + * struct key_params - key information + */ +struct key_params { + u8 *key; + int key_len; + int key_id; + u32 key_type; + u8 *macaddress; + u32 cipher; +}; + /* from net/wireless.h */ struct wiphy; @@ -30,11 +93,62 @@ struct wiphy; * @add_virtual_intf: create a new virtual interface with the given name * * @del_virtual_intf: remove the virtual interface determined by ifindex. + * + * @change_virtual_intf: change type of virtual interface + * + * @associate: associate with given parameters + * + * @disassociate: disassociate from current AP + * + * @deauth: deauth from current AP + * + * @initiate_scan: scan with the given information (see &struct scan_params above) + * + * @get_association: get BSSID of the BSS that the device is currently + * associated to and return 1, or return 0 if not + * associated (or a negative error code) + * @get_auth_list: get list of BSSIDs of all BSSs the device has + * authenticated with, must call next_bssid for each, + * next_bssid returns non-zero on error, the given data + * is to be passed to that callback + * @add_key: add a key using &struct key_params + * @del_key: delete a key using info from &struct key_params */ struct cfg80211_ops { int (*add_virtual_intf)(struct wiphy *wiphy, char *name, unsigned int type); int (*del_virtual_intf)(struct wiphy *wiphy, int ifindex); + int (*change_virtual_intf)(struct wiphy *wiphy, int ifindex, + unsigned int type); + + int (*associate)(struct wiphy *wiphy, struct net_device *dev, + struct association_params *params); + int (*disassociate)(struct wiphy *wiphy, struct net_device *dev); + int (*deauth)(struct wiphy *wiphy, struct net_device *dev); + + + int (*initiate_scan)(struct wiphy *wiphy, struct net_device *dev, + struct scan_params *params); + + + int (*get_association)(struct wiphy *wiphy, struct net_device *dev, + u8 *bssid); + + int (*get_auth_list)(struct wiphy *wiphy, struct net_device *dev, + void *data, + int (*next_bssid)(void *data, u8 *bssid)); + + int (*add_key)(struct wiphy *wiphy, struct net_device *dev, + struct key_params *params); + int (*del_key)(struct wiphy *wiphy, struct net_device *dev, + struct key_params *params); }; + +/* helper functions specific to nl80211 */ +extern void *nl80211hdr_put(struct sk_buff *skb, u32 pid, + u32 seq, int flags, u8 cmd); +extern void *nl80211msg_new(struct sk_buff **skb, u32 pid, + u32 seq, int flags, u8 cmd); + #endif /* __NET_CFG80211_H */ diff --git a/include/net/iw_handler.h b/include/net/iw_handler.h index f23d07c..369d50e 100644 --- a/include/net/iw_handler.h +++ b/include/net/iw_handler.h @@ -431,7 +431,13 @@ struct iw_public_data { * Those may be called only within the kernel. */ -/* functions that may be called by driver modules */ +/* First : function strictly used inside the kernel */ + +/* Handle /proc/net/wireless, called in net/code/dev.c */ +extern int dev_get_wireless_info(char * buffer, char **start, off_t offset, + int length); + +/* Second : functions that may be called by driver modules */ /* Send a single event to user space */ extern void wireless_send_event(struct net_device * dev, diff --git a/include/net/mac80211.h b/include/net/mac80211.h new file mode 100644 index 0000000..ef9b613 --- /dev/null +++ b/include/net/mac80211.h @@ -0,0 +1,1060 @@ +/* + * Low-level hardware driver -- IEEE 802.11 driver (80211.o) interface + * Copyright 2002-2005, Devicescape Software, Inc. + * Copyright 2006-2007 Jiri Benc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef MAC80211_H +#define MAC80211_H + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Note! Only ieee80211_tx_status_irqsafe() and ieee80211_rx_irqsafe() can be + * called in hardware interrupt context. The low-level driver must not call any + * other functions in hardware interrupt context. If there is a need for such + * call, the low-level driver should first ACK the interrupt and perform the + * IEEE 802.11 code call after this, e.g., from a scheduled tasklet (in + * software interrupt context). + */ + +/* + * Frame format used when passing frame between low-level hardware drivers + * and IEEE 802.11 driver the same as used in the wireless media, i.e., + * buffers start with IEEE 802.11 header and include the same octets that + * are sent over air. + * + * If hardware uses IEEE 802.3 headers (and perform 802.3 <-> 802.11 + * conversion in firmware), upper layer 802.11 code needs to be changed to + * support this. + * + * If the receive frame format is not the same as the real frame sent + * on the wireless media (e.g., due to padding etc.), upper layer 802.11 code + * could be updated to provide support for such format assuming this would + * optimize the performance, e.g., by removing need to re-allocation and + * copying of the data. + */ + +#define IEEE80211_CHAN_W_SCAN 0x00000001 +#define IEEE80211_CHAN_W_ACTIVE_SCAN 0x00000002 +#define IEEE80211_CHAN_W_IBSS 0x00000004 + +/* Channel information structure. Low-level driver is expected to fill in chan, + * freq, and val fields. Other fields will be filled in by 80211.o based on + * hostapd information and low-level driver does not need to use them. The + * limits for each channel will be provided in 'struct ieee80211_conf' when + * configuring the low-level driver with hw->config callback. If a device has + * a default regulatory domain, IEEE80211_HW_DEFAULT_REG_DOMAIN_CONFIGURED + * can be set to let the driver configure all fields */ +struct ieee80211_channel { + short chan; /* channel number (IEEE 802.11) */ + short freq; /* frequency in MHz */ + int val; /* hw specific value for the channel */ + int flag; /* flag for hostapd use (IEEE80211_CHAN_*) */ + unsigned char power_level; + unsigned char antenna_max; +}; + +#define IEEE80211_RATE_ERP 0x00000001 +#define IEEE80211_RATE_BASIC 0x00000002 +#define IEEE80211_RATE_PREAMBLE2 0x00000004 +#define IEEE80211_RATE_SUPPORTED 0x00000010 +#define IEEE80211_RATE_OFDM 0x00000020 +#define IEEE80211_RATE_CCK 0x00000040 +#define IEEE80211_RATE_TURBO 0x00000080 +#define IEEE80211_RATE_MANDATORY 0x00000100 + +#define IEEE80211_RATE_CCK_2 (IEEE80211_RATE_CCK | IEEE80211_RATE_PREAMBLE2) +#define IEEE80211_RATE_MODULATION(f) \ + (f & (IEEE80211_RATE_CCK | IEEE80211_RATE_OFDM)) + +/* Low-level driver should set PREAMBLE2, OFDM, CCK, and TURBO flags. + * BASIC, SUPPORTED, ERP, and MANDATORY flags are set in 80211.o based on the + * configuration. */ +struct ieee80211_rate { + int rate; /* rate in 100 kbps */ + int val; /* hw specific value for the rate */ + int flags; /* IEEE80211_RATE_ flags */ + int val2; /* hw specific value for the rate when using short preamble + * (only when IEEE80211_RATE_PREAMBLE2 flag is set, i.e., for + * 2, 5.5, and 11 Mbps) */ + signed char min_rssi_ack; + unsigned char min_rssi_ack_delta; + + /* following fields are set by 80211.o and need not be filled by the + * low-level driver */ + int rate_inv; /* inverse of the rate (LCM(all rates) / rate) for + * optimizing channel utilization estimates */ +}; + +/* 802.11g is backwards-compatible with 802.11b, so a wlan card can + * actually be both in 11b and 11g modes at the same time. */ +enum { + MODE_IEEE80211A, /* IEEE 802.11a */ + MODE_IEEE80211B, /* IEEE 802.11b only */ + MODE_ATHEROS_TURBO, /* Atheros Turbo mode (2x.11a at 5 GHz) */ + MODE_IEEE80211G, /* IEEE 802.11g (and 802.11b compatibility) */ + MODE_ATHEROS_TURBOG, /* Atheros Turbo mode (2x.11g at 2.4 GHz) */ + + /* keep last */ + NUM_IEEE80211_MODES +}; + +struct ieee80211_hw_mode { + int mode; /* MODE_IEEE80211... */ + int num_channels; /* Number of channels (below) */ + struct ieee80211_channel *channels; /* Array of supported channels */ + int num_rates; /* Number of rates (below) */ + struct ieee80211_rate *rates; /* Array of supported rates */ + + struct list_head list; /* Internal, don't touch */ +}; + +struct ieee80211_tx_queue_params { + int aifs; /* 0 .. 255; -1 = use default */ + int cw_min; /* 2^n-1: 1, 3, 7, .. , 1023; 0 = use default */ + int cw_max; /* 2^n-1: 1, 3, 7, .. , 1023; 0 = use default */ + int burst_time; /* maximum burst time in 0.1 ms (i.e., 10 = 1 ms); + * 0 = disabled */ +}; + +struct ieee80211_tx_queue_stats_data { + unsigned int len; /* num packets in queue */ + unsigned int limit; /* queue len (soft) limit */ + unsigned int count; /* total num frames sent */ +}; + +enum { + IEEE80211_TX_QUEUE_DATA0, + IEEE80211_TX_QUEUE_DATA1, + IEEE80211_TX_QUEUE_DATA2, + IEEE80211_TX_QUEUE_DATA3, + IEEE80211_TX_QUEUE_DATA4, + IEEE80211_TX_QUEUE_SVP, + + NUM_TX_DATA_QUEUES, + +/* due to stupidity in the sub-ioctl userspace interface, the items in + * this struct need to have fixed values. As soon as it is removed, we can + * fix these entries. */ + IEEE80211_TX_QUEUE_AFTER_BEACON = 6, + IEEE80211_TX_QUEUE_BEACON = 7 +}; + +struct ieee80211_tx_queue_stats { + struct ieee80211_tx_queue_stats_data data[NUM_TX_DATA_QUEUES]; +}; + +struct ieee80211_low_level_stats { + unsigned int dot11ACKFailureCount; + unsigned int dot11RTSFailureCount; + unsigned int dot11FCSErrorCount; + unsigned int dot11RTSSuccessCount; +}; + +/* Transmit control fields. This data structure is passed to low-level driver + * with each TX frame. The low-level driver is responsible for configuring + * the hardware to use given values (depending on what is supported). */ +#define HW_KEY_IDX_INVALID -1 + +struct ieee80211_tx_control { + int tx_rate; /* Transmit rate, given as the hw specific value for the + * rate (from struct ieee80211_rate) */ + int rts_cts_rate; /* Transmit rate for RTS/CTS frame, given as the hw + * specific value for the rate (from + * struct ieee80211_rate) */ + +#define IEEE80211_TXCTL_REQ_TX_STATUS (1<<0)/* request TX status callback for + * this frame */ +#define IEEE80211_TXCTL_DO_NOT_ENCRYPT (1<<1) /* send this frame without + * encryption; e.g., for EAPOL + * frames */ +#define IEEE80211_TXCTL_USE_RTS_CTS (1<<2) /* use RTS-CTS before sending + * frame */ +#define IEEE80211_TXCTL_USE_CTS_PROTECT (1<<3) /* use CTS protection for the + * frame (e.g., for combined + * 802.11g / 802.11b networks) */ +#define IEEE80211_TXCTL_NO_ACK (1<<4) /* tell the low level not to + * wait for an ack */ +#define IEEE80211_TXCTL_RATE_CTRL_PROBE (1<<5) +#define IEEE80211_TXCTL_CLEAR_DST_MASK (1<<6) +#define IEEE80211_TXCTL_REQUEUE (1<<7) +#define IEEE80211_TXCTL_FIRST_FRAGMENT (1<<8) /* this is a first fragment of + * the frame */ +#define IEEE80211_TXCTL_TKIP_NEW_PHASE1_KEY (1<<9) + u32 flags; /* tx control flags defined + * above */ + u8 retry_limit; /* 1 = only first attempt, 2 = one retry, .. */ + u8 power_level; /* per-packet transmit power level, in dBm */ + u8 antenna_sel_tx; /* 0 = default/diversity, 1 = Ant0, 2 = Ant1 */ + s8 key_idx; /* -1 = do not encrypt, >= 0 keyidx from + * hw->set_key() */ + u8 icv_len; /* length of the ICV/MIC field in octets */ + u8 iv_len; /* length of the IV field in octets */ + u8 tkip_key[16]; /* generated phase2/phase1 key for hw TKIP */ + u8 queue; /* hardware queue to use for this frame; + * 0 = highest, hw->queues-1 = lowest */ + u8 sw_retry_attempt; /* number of times hw has tried to + * transmit frame (not incl. hw retries) */ + + struct ieee80211_rate *rate; /* internal 80211.o rate */ + struct ieee80211_rate *rts_rate; /* internal 80211.o rate + * for RTS/CTS */ + int alt_retry_rate; /* retry rate for the last retries, given as the + * hw specific value for the rate (from + * struct ieee80211_rate). To be used to limit + * packet dropping when probing higher rates, if hw + * supports multiple retry rates. -1 = not used */ + int type; /* internal */ + int ifindex; /* internal */ +}; + +/* Receive status. The low-level driver should provide this information + * (the subset supported by hardware) to the 802.11 code with each received + * frame. */ +struct ieee80211_rx_status { + u64 mactime; + int freq; /* receive frequency in Mhz */ + int channel; + int phymode; + int ssi; + int signal; /* used as qual in statistics reporting */ + int noise; + int antenna; + int rate; +#define RX_FLAG_MMIC_ERROR (1<<0) +#define RX_FLAG_DECRYPTED (1<<1) +#define RX_FLAG_RADIOTAP (1<<2) + int flag; +}; + +/* Transmit status. The low-level driver should provide this information + * (the subset supported by hardware) to the 802.11 code for each transmit + * frame. */ +struct ieee80211_tx_status { + /* copied ieee80211_tx_control structure */ + struct ieee80211_tx_control control; + +#define IEEE80211_TX_STATUS_TX_FILTERED (1<<0) +#define IEEE80211_TX_STATUS_ACK (1<<1) /* whether the TX frame was ACKed */ + u32 flags; /* tx staus flags defined above */ + + int ack_signal; /* measured signal strength of the ACK frame */ + int excessive_retries; + int retry_count; + + int queue_length; /* information about TX queue */ + int queue_number; +}; + + +/** + * struct ieee80211_conf - configuration of the device + * + * This struct indicates how the driver shall configure the hardware. + * + * @radio_enabled: when zero, driver is required to switch off the radio. + */ +struct ieee80211_conf { + int channel; /* IEEE 802.11 channel number */ + int freq; /* MHz */ + int channel_val; /* hw specific value for the channel */ + + int phymode; /* MODE_IEEE80211A, .. */ + struct ieee80211_channel *chan; + struct ieee80211_hw_mode *mode; + unsigned int regulatory_domain; + int radio_enabled; + + int beacon_int; + +#define IEEE80211_CONF_SHORT_SLOT_TIME (1<<0) /* use IEEE 802.11g Short Slot + * Time */ +#define IEEE80211_CONF_SSID_HIDDEN (1<<1) /* do not broadcast the ssid */ +#define IEEE80211_CONF_RADIOTAP (1<<2) /* use radiotap if supported + check this bit at RX time */ + u32 flags; /* configuration flags defined above */ + + u8 power_level; /* transmit power limit for current + * regulatory domain; in dBm */ + u8 antenna_max; /* maximum antenna gain */ + short tx_power_reduction; /* in 0.1 dBm */ + + /* 0 = default/diversity, 1 = Ant0, 2 = Ant1 */ + u8 antenna_sel_tx; + u8 antenna_sel_rx; + + int antenna_def; + int antenna_mode; + + /* Following five fields are used for IEEE 802.11H */ + unsigned int radar_detect; + unsigned int spect_mgmt; + unsigned int quiet_duration; /* duration of quiet period */ + unsigned int quiet_offset; /* how far into the beacon is the quiet + * period */ + unsigned int quiet_period; + u8 radar_firpwr_threshold; + u8 radar_rssi_threshold; + u8 pulse_height_threshold; + u8 pulse_rssi_threshold; + u8 pulse_inband_threshold; +}; + +/** + * enum ieee80211_if_types - types of 802.11 network interfaces + * + * @IEEE80211_IF_TYPE_AP: interface in AP mode. + * @IEEE80211_IF_TYPE_MGMT: special interface for communication with hostap + * daemon. Drivers should never see this type. + * @IEEE80211_IF_TYPE_STA: interface in STA (client) mode. + * @IEEE80211_IF_TYPE_IBSS: interface in IBSS (ad-hoc) mode. + * @IEEE80211_IF_TYPE_MNTR: interface in monitor (rfmon) mode. + * @IEEE80211_IF_TYPE_WDS: interface in WDS mode. + * @IEEE80211_IF_TYPE_VLAN: not used. + */ +enum ieee80211_if_types { + IEEE80211_IF_TYPE_AP = 0x00000000, + IEEE80211_IF_TYPE_MGMT = 0x00000001, + IEEE80211_IF_TYPE_STA = 0x00000002, + IEEE80211_IF_TYPE_IBSS = 0x00000003, + IEEE80211_IF_TYPE_MNTR = 0x00000004, + IEEE80211_IF_TYPE_WDS = 0x5A580211, + IEEE80211_IF_TYPE_VLAN = 0x00080211, +}; + +/** + * struct ieee80211_if_init_conf - initial configuration of an interface + * + * @if_id: internal interface ID. This number has no particular meaning to + * drivers and the only allowed usage is to pass it to + * ieee80211_beacon_get() and ieee80211_get_buffered_bc() functions. + * This field is not valid for monitor interfaces + * (interfaces of %IEEE80211_IF_TYPE_MNTR type). + * @type: one of &enum ieee80211_if_types constants. Determines the type of + * added/removed interface. + * @mac_addr: pointer to MAC address of the interface. This pointer is valid + * until the interface is removed (i.e. it cannot be used after + * remove_interface() callback was called for this interface). + * + * This structure is used in add_interface() and remove_interface() + * callbacks of &struct ieee80211_hw. + */ +struct ieee80211_if_init_conf { + int if_id; + int type; + void *mac_addr; +}; + +/** + * struct ieee80211_if_conf - configuration of an interface + * + * @type: type of the interface. This is always the same as was specified in + * &struct ieee80211_if_init_conf. The type of an interface never changes + * during the life of the interface; this field is present only for + * convenience. + * @bssid: BSSID of the network we are associated to/creating. + * @ssid: used (together with @ssid_len) by drivers for hardware that + * generate beacons independently. The pointer is valid only during the + * config_interface() call, so copy the value somewhere if you need + * it. + * @ssid_len: length of the @ssid field. + * @generic_elem: used (together with @generic_elem_len) by drivers for + * hardware that generate beacons independently. The pointer is valid + * only during the config_interface() call, so copy the value somewhere + * if you need it. + * @generic_elem_len: length of the generic element. + * @beacon: beacon template. Valid only if @host_gen_beacon_template in + * &struct ieee80211_hw is set. The driver is responsible of freeing + * the sk_buff. + * @beacon_control: tx_control for the beacon template, this field is only + * valid when the @beacon field was set. + * + * This structure is passed to the config_interface() callback of + * &struct ieee80211_hw. + */ +struct ieee80211_if_conf { + int type; + u8 *bssid; + u8 *ssid; + size_t ssid_len; + u8 *generic_elem; + size_t generic_elem_len; + struct sk_buff *beacon; + struct ieee80211_tx_control *beacon_control; +}; + +typedef enum { ALG_NONE, ALG_WEP, ALG_TKIP, ALG_CCMP, ALG_NULL } +ieee80211_key_alg; + + +struct ieee80211_key_conf { + + int hw_key_idx; /* filled + used by low-level driver */ + ieee80211_key_alg alg; + int keylen; + +#define IEEE80211_KEY_FORCE_SW_ENCRYPT (1<<0) /* to be cleared by low-level + driver */ +#define IEEE80211_KEY_DEFAULT_TX_KEY (1<<1) /* This key is the new default TX + key (used only for broadcast + keys). */ +#define IEEE80211_KEY_DEFAULT_WEP_ONLY (1<<2) /* static WEP is the only + configured security policy; + this allows some low-level + drivers to determine when + hwaccel can be used */ + u32 flags; /* key configuration flags defined above */ + + s8 keyidx; /* WEP key index */ + u8 key[0]; +}; + +#define IEEE80211_SEQ_COUNTER_RX 0 +#define IEEE80211_SEQ_COUNTER_TX 1 + +typedef enum { + SET_KEY, DISABLE_KEY, REMOVE_ALL_KEYS, +} set_key_cmd; + +/* This is driver-visible part of the per-hw state the stack keeps. */ +struct ieee80211_hw { + /* points to the cfg80211 wiphy for this piece. Note + * that you must fill in the perm_addr and dev fields + * of this structure, use the macros provided below. */ + struct wiphy *wiphy; + + /* assigned by mac80211, don't write */ + struct ieee80211_conf conf; + + /* Single thread workqueue available for driver use + * Allocated by mac80211 on registration */ + struct workqueue_struct *workqueue; + + /* Pointer to the private area that was + * allocated with this struct for you. */ + void *priv; + + /* The rest is information about your hardware */ + + /* TODO: frame_type 802.11/802.3, sw_encryption requirements */ + + /* Some wireless LAN chipsets generate beacons in the hardware/firmware + * and others rely on host generated beacons. This option is used to + * configure the upper layer IEEE 802.11 module to generate beacons. + * The low-level driver can use ieee80211_beacon_get() to fetch the + * next beacon frame. */ +#define IEEE80211_HW_HOST_GEN_BEACON (1<<0) + + /* The device needs to be supplied with a beacon template only. */ +#define IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE (1<<1) + + /* Some devices handle decryption internally and do not + * indicate whether the frame was encrypted (unencrypted frames + * will be dropped by the hardware, unless specifically allowed + * through) */ +#define IEEE80211_HW_DEVICE_HIDES_WEP (1<<2) + + /* Whether RX frames passed to ieee80211_rx() include FCS in the end */ +#define IEEE80211_HW_RX_INCLUDES_FCS (1<<3) + + /* Some wireless LAN chipsets buffer broadcast/multicast frames for + * power saving stations in the hardware/firmware and others rely on + * the host system for such buffering. This option is used to + * configure the IEEE 802.11 upper layer to buffer broadcast/multicast + * frames when there are power saving stations so that low-level driver + * can fetch them with ieee80211_get_buffered_bc(). */ +#define IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING (1<<4) + +#define IEEE80211_HW_WEP_INCLUDE_IV (1<<5) + + /* will data nullfunc frames get proper TX status callback */ +#define IEEE80211_HW_DATA_NULLFUNC_ACK (1<<6) + + /* Force software encryption for TKIP packets if WMM is enabled. */ +#define IEEE80211_HW_NO_TKIP_WMM_HWACCEL (1<<7) + + /* Some devices handle Michael MIC internally and do not include MIC in + * the received packets passed up. device_strips_mic must be set + * for such devices. The 'encryption' frame control bit is expected to + * be still set in the IEEE 802.11 header with this option unlike with + * the device_hides_wep configuration option. + */ +#define IEEE80211_HW_DEVICE_STRIPS_MIC (1<<8) + + /* Device is capable of performing full monitor mode even during + * normal operation. */ +#define IEEE80211_HW_MONITOR_DURING_OPER (1<<9) + + /* Device does not need BSSID filter set to broadcast in order to + * receive all probe responses while scanning */ +#define IEEE80211_HW_NO_PROBE_FILTERING (1<<10) + + /* Channels are already configured to the default regulatory domain + * specified in the device's EEPROM */ +#define IEEE80211_HW_DEFAULT_REG_DOMAIN_CONFIGURED (1<<11) + + /* calculate Michael MIC for an MSDU when doing hwcrypto */ +#define IEEE80211_HW_TKIP_INCLUDE_MMIC (1<<12) + /* Do TKIP phase1 key mixing in stack to support cards only do + * phase2 key mixing when doing hwcrypto */ +#define IEEE80211_HW_TKIP_REQ_PHASE1_KEY (1<<13) + /* Do TKIP phase1 and phase2 key mixing in stack and send the generated + * per-packet RC4 key with each TX frame when doing hwcrypto */ +#define IEEE80211_HW_TKIP_REQ_PHASE2_KEY (1<<14) + + /* The device capable of supporting 11n */ +#define IEEE80211_HW_SUPPORT_HT_MODE (1<<15) + + u32 flags; /* hardware flags defined above */ + + /* Set to the size of a needed device specific skb headroom for TX skbs. */ + unsigned int extra_tx_headroom; + + /* This is the time in us to change channels + */ + int channel_change_time; + /* Maximum values for various statistics. + * Leave at 0 to indicate no support. Use negative numbers for dBm. */ + s8 max_rssi; + s8 max_signal; + s8 max_noise; + + /* Number of available hardware TX queues for data packets. + * WMM requires at least four queues. */ + int queues; +}; + +static inline void SET_IEEE80211_DEV(struct ieee80211_hw *hw, struct device *dev) +{ + set_wiphy_dev(hw->wiphy, dev); +} + +static inline void SET_IEEE80211_PERM_ADDR(struct ieee80211_hw *hw, u8 *addr) +{ + memcpy(hw->wiphy->perm_addr, addr, ETH_ALEN); +} + +/* Configuration block used by the low-level driver to tell the 802.11 code + * about supported hardware features and to pass function pointers to callback + * functions. */ +struct ieee80211_ops { + /* Handler that 802.11 module calls for each transmitted frame. + * skb contains the buffer starting from the IEEE 802.11 header. + * The low-level driver should send the frame out based on + * configuration in the TX control data. + * Must be atomic. */ + int (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb, + struct ieee80211_tx_control *control); + + /* Handler for performing hardware reset. */ + int (*reset)(struct ieee80211_hw *hw); + + /* Handler that is called when any netdevice attached to the hardware + * device is set UP for the first time. This can be used, e.g., to + * enable interrupts and beacon sending. */ + int (*open)(struct ieee80211_hw *hw); + + /* Handler that is called when the last netdevice attached to the + * hardware device is set DOWN. This can be used, e.g., to disable + * interrupts and beacon sending. */ + int (*stop)(struct ieee80211_hw *hw); + + /* Handler for asking a driver if a new interface can be added (or, + * more exactly, set UP). If the handler returns zero, the interface + * is added. Driver should perform any initialization it needs prior + * to returning zero. By returning non-zero addition of the interface + * is inhibited. Unless monitor_during_oper is set, it is guaranteed + * that monitor interfaces and normal interfaces are mutually + * exclusive. The open() handler is called after add_interface() + * if this is the first device added. At least one of the open() + * open() and add_interface() callbacks has to be assigned. If + * add_interface() is NULL, one STA interface is permitted only. */ + int (*add_interface)(struct ieee80211_hw *hw, + struct ieee80211_if_init_conf *conf); + + /* Notify a driver that an interface is going down. The stop() handler + * is called prior to this if this is a last interface. */ + void (*remove_interface)(struct ieee80211_hw *hw, + struct ieee80211_if_init_conf *conf); + + /* Handler for configuration requests. IEEE 802.11 code calls this + * function to change hardware configuration, e.g., channel. */ + int (*config)(struct ieee80211_hw *hw, struct ieee80211_conf *conf); + + /* Handler for configuration requests related to interfaces (e.g. + * BSSID). */ + int (*config_interface)(struct ieee80211_hw *hw, + int if_id, struct ieee80211_if_conf *conf); + + /* ieee80211 drivers do not have access to the &struct net_device + * that is (are) connected with their device. Hence (and because + * we need to combine the multicast lists and flags for multiple + * virtual interfaces), they cannot assign set_multicast_list. + * The parameters here replace dev->flags and dev->mc_count, + * dev->mc_list is replaced by calling ieee80211_get_mc_list_item. + * Must be atomic. */ + void (*set_multicast_list)(struct ieee80211_hw *hw, + unsigned short flags, int mc_count); + + /* Set TIM bit handler. If the hardware/firmware takes care of beacon + * generation, IEEE 802.11 code uses this function to tell the + * low-level to set (or clear if set==0) TIM bit for the given aid. If + * host system is used to generate beacons, this handler is not used + * and low-level driver should set it to NULL. + * Must be atomic. */ + int (*set_tim)(struct ieee80211_hw *hw, int aid, int set); + + /* Set encryption key. IEEE 802.11 module calls this function to set + * encryption keys. addr is ff:ff:ff:ff:ff:ff for default keys and + * station hwaddr for individual keys. aid of the station is given + * to help low-level driver in selecting which key->hw_key_idx to use + * for this key. TX control data will use the hw_key_idx selected by + * the low-level driver. + * Must be atomic. */ + int (*set_key)(struct ieee80211_hw *hw, set_key_cmd cmd, + u8 *addr, struct ieee80211_key_conf *key, int aid); + + /* Set TX key index for default/broadcast keys. This is needed in cases + * where wlan card is doing full WEP/TKIP encapsulation (wep_include_iv + * is not set), in other cases, this function pointer can be set to + * NULL since the IEEE 802. 11 module takes care of selecting the key + * index for each TX frame. */ + int (*set_key_idx)(struct ieee80211_hw *hw, int idx); + + /* Enable/disable IEEE 802.1X. This item requests wlan card to pass + * unencrypted EAPOL-Key frames even when encryption is configured. + * If the wlan card does not require such a configuration, this + * function pointer can be set to NULL. */ + int (*set_ieee8021x)(struct ieee80211_hw *hw, int use_ieee8021x); + + /* Set port authorization state (IEEE 802.1X PAE) to be authorized + * (authorized=1) or unauthorized (authorized=0). This function can be + * used if the wlan hardware or low-level driver implements PAE. + * 80211.o module will anyway filter frames based on authorization + * state, so this function pointer can be NULL if low-level driver does + * not require event notification about port state changes. */ + int (*set_port_auth)(struct ieee80211_hw *hw, u8 *addr, + int authorized); + + /* Ask the hardware to service the scan request, no need to start + * the scan state machine in stack. */ + int (*hw_scan)(struct ieee80211_hw *hw, u8 *ssid, size_t len); + + /* return low-level statistics */ + int (*get_stats)(struct ieee80211_hw *hw, + struct ieee80211_low_level_stats *stats); + + /* For devices that generate their own beacons and probe response + * or association responses this updates the state of privacy_invoked + * returns 0 for success or an error number */ + int (*set_privacy_invoked)(struct ieee80211_hw *hw, + int privacy_invoked); + + /* For devices that have internal sequence counters, allow 802.11 + * code to access the current value of a counter */ + int (*get_sequence_counter)(struct ieee80211_hw *hw, + u8* addr, u8 keyidx, u8 txrx, + u32* iv32, u16* iv16); + + /* Configuration of RTS threshold (if device needs it) */ + int (*set_rts_threshold)(struct ieee80211_hw *hw, u32 value); + + /* Configuration of fragmentation threshold. + * Assign this if the device does fragmentation by itself, + * if this method is assigned then the stack will not do + * fragmentation. */ + int (*set_frag_threshold)(struct ieee80211_hw *hw, u32 value); + + /* Configuration of retry limits (if device needs it) */ + int (*set_retry_limit)(struct ieee80211_hw *hw, + u32 short_retry, u32 long_retr); + + /* Number of STAs in STA table notification (NULL = disabled). + * Must be atomic. */ + void (*sta_table_notification)(struct ieee80211_hw *hw, + int num_sta); + + /* Configure TX queue parameters (EDCF (aifs, cw_min, cw_max), + * bursting) for a hardware TX queue. + * queue = IEEE80211_TX_QUEUE_*. + * Must be atomic. */ + int (*conf_tx)(struct ieee80211_hw *hw, int queue, + const struct ieee80211_tx_queue_params *params); + + /* Get statistics of the current TX queue status. This is used to get + * number of currently queued packets (queue length), maximum queue + * size (limit), and total number of packets sent using each TX queue + * (count). This information is used for WMM to find out which TX + * queues have room for more packets and by hostapd to provide + * statistics about the current queueing state to external programs. */ + int (*get_tx_stats)(struct ieee80211_hw *hw, + struct ieee80211_tx_queue_stats *stats); + + /* Get the current TSF timer value from firmware/hardware. Currently, + * this is only used for IBSS mode debugging and, as such, is not a + * required function. + * Must be atomic. */ + u64 (*get_tsf)(struct ieee80211_hw *hw); + + /* Call low level driver with 11n Block Ack action */ + int (*handle_ba_action)(struct ieee80211_hw *hw, + struct ieee80211_mgmt *mgmt); + + /* Reset the TSF timer and allow firmware/hardware to synchronize with + * other STAs in the IBSS. This is only used in IBSS mode. This + * function is optional if the firmware/hardware takes full care of + * TSF synchronization. */ + void (*reset_tsf)(struct ieee80211_hw *hw); + + /* Configure ht parameters. */ + int (*conf_ht)(struct ieee80211_hw *hw, + struct ieee80211_ht_capability *ht_cap_param, + struct ieee80211_ht_additional_info *ht_extra_param); + + /* Get ht capabilities from the device */ + void (*get_ht_capab)(struct ieee80211_hw *hw, + struct ieee80211_ht_capability *ht_cap_param); + + /* Setup beacon data for IBSS beacons. Unlike access point (Master), + * IBSS uses a fixed beacon frame which is configured using this + * function. This handler is required only for IBSS mode. */ + int (*beacon_update)(struct ieee80211_hw *hw, + struct sk_buff *skb, + struct ieee80211_tx_control *control); + + /* Determine whether the last IBSS beacon was sent by us. This is + * needed only for IBSS mode and the result of this function is used to + * determine whether to reply to Probe Requests. */ + int (*tx_last_beacon)(struct ieee80211_hw *hw); +}; + +/* Allocate a new hardware device. This must be called once for each + * hardware device. The returned pointer must be used to refer to this + * device when calling other functions. 802.11 code allocates a private data + * area for the low-level driver. The size of this area is given as + * priv_data_len. + */ +struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, + const struct ieee80211_ops *ops); + +/* Register hardware device to the IEEE 802.11 code and kernel. Low-level + * drivers must call this function before using any other IEEE 802.11 + * function except ieee80211_register_hwmode. */ +int ieee80211_register_hw(struct ieee80211_hw *hw); + +/* driver can use this and ieee80211_get_rx_led_name to get the + * name of the registered LEDs after ieee80211_register_hw + * was called. + * This is useful to set the default trigger on the LED class + * device that your driver should export for each LED the device + * has, that way the default behaviour will be as expected but + * the user can still change it/turn off the LED etc. + */ +#ifdef CONFIG_MAC80211_LEDS +extern char *__ieee80211_get_tx_led_name(struct ieee80211_hw *hw); +extern char *__ieee80211_get_rx_led_name(struct ieee80211_hw *hw); +#endif +static inline char *ieee80211_get_tx_led_name(struct ieee80211_hw *hw) +{ +#ifdef CONFIG_MAC80211_LEDS + return __ieee80211_get_tx_led_name(hw); +#else + return NULL; +#endif +} + +static inline char *ieee80211_get_rx_led_name(struct ieee80211_hw *hw) +{ +#ifdef CONFIG_MAC80211_LEDS + return __ieee80211_get_rx_led_name(hw); +#else + return NULL; +#endif +} + +/* Register a new hardware PHYMODE capability to the stack. */ +int ieee80211_register_hwmode(struct ieee80211_hw *hw, + struct ieee80211_hw_mode *mode); + +/* Unregister a hardware device. This function instructs 802.11 code to free + * allocated resources and unregister netdevices from the kernel. */ +void ieee80211_unregister_hw(struct ieee80211_hw *hw); + +/* Free everything that was allocated including private data of a driver. */ +void ieee80211_free_hw(struct ieee80211_hw *hw); + +/* Receive frame callback function. The low-level driver uses this function to + * send received frames to the IEEE 802.11 code. Receive buffer (skb) must + * start with IEEE 802.11 header. */ +void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb, + struct ieee80211_rx_status *status); +void ieee80211_rx_irqsafe(struct ieee80211_hw *hw, + struct sk_buff *skb, + struct ieee80211_rx_status *status); + +/* Transmit status callback function. The low-level driver must call this + * function to report transmit status for all the TX frames that had + * req_tx_status set in the transmit control fields. In addition, this should + * be called at least for all unicast frames to provide information for TX rate + * control algorithm. In order to maintain all statistics, this function is + * recommended to be called after each frame, including multicast/broadcast, is + * sent. */ +void ieee80211_tx_status(struct ieee80211_hw *hw, + struct sk_buff *skb, + struct ieee80211_tx_status *status); +void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw, + struct sk_buff *skb, + struct ieee80211_tx_status *status); + +/** + * ieee80211_beacon_get - beacon generation function + * @hw: pointer obtained from ieee80211_alloc_hw(). + * @if_id: interface ID from &struct ieee80211_if_init_conf. + * @control: will be filled with information needed to send this beacon. + * + * If the beacon frames are generated by the host system (i.e., not in + * hardware/firmware), the low-level driver uses this function to receive + * the next beacon frame from the 802.11 code. The low-level is responsible + * for calling this function before beacon data is needed (e.g., based on + * hardware interrupt). Returned skb is used only once and low-level driver + * is responsible of freeing it. + */ +struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw, + int if_id, + struct ieee80211_tx_control *control); + +/** + * ieee80211_rts_get - RTS frame generation function + * @hw: pointer obtained from ieee80211_alloc_hw(). + * @frame: pointer to the frame that is going to be protected by the RTS. + * @frame_len: the frame length (in octets). + * @frame_txctl: &struct ieee80211_tx_control of the frame. + * @rts: The buffer where to store the RTS frame. + * + * If the RTS frames are generated by the host system (i.e., not in + * hardware/firmware), the low-level driver uses this function to receive + * the next RTS frame from the 802.11 code. The low-level is responsible + * for calling this function before and RTS frame is needed. + */ +void ieee80211_rts_get(struct ieee80211_hw *hw, + const void *frame, size_t frame_len, + const struct ieee80211_tx_control *frame_txctl, + struct ieee80211_rts *rts); + +/** + * ieee80211_rts_duration - Get the duration field for an RTS frame + * @hw: pointer obtained from ieee80211_alloc_hw(). + * @frame_len: the length of the frame that is going to be protected by the RTS. + * @frame_txctl: &struct ieee80211_tx_control of the frame. + * + * If the RTS is generated in firmware, but the host system must provide + * the duration field, the low-level driver uses this function to receive + * the duration field value in little-endian byteorder. + */ +__le16 ieee80211_rts_duration(struct ieee80211_hw *hw, + size_t frame_len, + const struct ieee80211_tx_control *frame_txctl); + +/** + * ieee80211_ctstoself_get - CTS-to-self frame generation function + * @hw: pointer obtained from ieee80211_alloc_hw(). + * @frame: pointer to the frame that is going to be protected by the CTS-to-self. + * @frame_len: the frame length (in octets). + * @frame_txctl: &struct ieee80211_tx_control of the frame. + * @cts: The buffer where to store the CTS-to-self frame. + * + * If the CTS-to-self frames are generated by the host system (i.e., not in + * hardware/firmware), the low-level driver uses this function to receive + * the next CTS-to-self frame from the 802.11 code. The low-level is responsible + * for calling this function before and CTS-to-self frame is needed. + */ +void ieee80211_ctstoself_get(struct ieee80211_hw *hw, + const void *frame, size_t frame_len, + const struct ieee80211_tx_control *frame_txctl, + struct ieee80211_cts *cts); + +/** + * ieee80211_ctstoself_duration - Get the duration field for a CTS-to-self frame + * @hw: pointer obtained from ieee80211_alloc_hw(). + * @frame_len: the length of the frame that is going to be protected by the CTS-to-self. + * @frame_txctl: &struct ieee80211_tx_control of the frame. + * + * If the CTS-to-self is generated in firmware, but the host system must provide + * the duration field, the low-level driver uses this function to receive + * the duration field value in little-endian byteorder. + */ +__le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw, + size_t frame_len, + const struct ieee80211_tx_control *frame_txctl); + +/** + * ieee80211_generic_frame_duration - Calculate the duration field for a frame + * @hw: pointer obtained from ieee80211_alloc_hw(). + * @frame_len: the length of the frame. + * @rate: the rate (in 100kbps) at which the frame is going to be transmitted. + * + * Calculate the duration field of some generic frame, given its + * length and transmission rate (in 100kbps). + */ +__le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw, + size_t frame_len, + int rate); + +/** + * ieee80211_get_buffered_bc - accessing buffered broadcast and multicast frames + * @hw: pointer as obtained from ieee80211_alloc_hw(). + * @if_id: interface ID from &struct ieee80211_if_init_conf. + * @control: will be filled with information needed to send returned frame. + * + * Function for accessing buffered broadcast and multicast frames. If + * hardware/firmware does not implement buffering of broadcast/multicast + * frames when power saving is used, 802.11 code buffers them in the host + * memory. The low-level driver uses this function to fetch next buffered + * frame. In most cases, this is used when generating beacon frame. This + * function returns a pointer to the next buffered skb or NULL if no more + * buffered frames are available. + * + * Note: buffered frames are returned only after DTIM beacon frame was + * generated with ieee80211_beacon_get() and the low-level driver must thus + * call ieee80211_beacon_get() first. ieee80211_get_buffered_bc() returns + * NULL if the previous generated beacon was not DTIM, so the low-level driver + * does not need to check for DTIM beacons separately and should be able to + * use common code for all beacons. + */ +struct sk_buff * +ieee80211_get_buffered_bc(struct ieee80211_hw *hw, int if_id, + struct ieee80211_tx_control *control); + +/* Low level drivers that have their own MLME and MAC indicate + * the aid for an associating station with this call */ +int ieee80211_set_aid_for_sta(struct ieee80211_hw *hw, + u8 *peer_address, u16 aid); + + +/* Given an sk_buff with a raw 802.11 header at the data pointer this function + * returns the 802.11 header length in bytes (not including encryption + * headers). If the data in the sk_buff is too short to contain a valid 802.11 + * header the function returns 0. + */ +int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb); + +/* Like ieee80211_get_hdrlen_from_skb() but takes a FC in CPU order. */ +int ieee80211_get_hdrlen(u16 fc); + +/** + * ieee80211_wake_queue - wake specific queue + * @hw: pointer as obtained from ieee80211_alloc_hw(). + * @queue: queue number (counted from zero). + * + * Drivers should use this function instead of netif_wake_queue. + */ +void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue); + +/** + * ieee80211_stop_queue - stop specific queue + * @hw: pointer as obtained from ieee80211_alloc_hw(). + * @queue: queue number (counted from zero). + * + * Drivers should use this function instead of netif_stop_queue. + */ +void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue); + +/** + * ieee80211_start_queues - start all queues + * @hw: pointer to as obtained from ieee80211_alloc_hw(). + * + * Drivers should use this function instead of netif_start_queue. + */ +void ieee80211_start_queues(struct ieee80211_hw *hw); + +/** + * ieee80211_stop_queues - stop all queues + * @hw: pointer as obtained from ieee80211_alloc_hw(). + * + * Drivers should use this function instead of netif_stop_queue. + */ +void ieee80211_stop_queues(struct ieee80211_hw *hw); + +/** + * ieee80211_wake_queues - wake all queues + * @hw: pointer as obtained from ieee80211_alloc_hw(). + * + * Drivers should use this function instead of netif_wake_queue. + */ +void ieee80211_wake_queues(struct ieee80211_hw *hw); + +/** + * ieee80211_get_mc_list_item - iteration over items in multicast list + * @hw: pointer as obtained from ieee80211_alloc_hw(). + * @prev: value returned by previous call to ieee80211_get_mc_list_item() or + * NULL to start a new iteration. + * @ptr: pointer to buffer of void * type for internal usage of + * ieee80211_get_mc_list_item(). + * + * Iterates over items in multicast list of given device. To get the first + * item, pass NULL in @prev and in *@ptr. In subsequent calls, pass the + * value returned by previous call in @prev. Don't alter *@ptr during + * iteration. When there are no more items, NULL is returned. + */ +struct dev_mc_list * +ieee80211_get_mc_list_item(struct ieee80211_hw *hw, + struct dev_mc_list *prev, + void **ptr); + +/* called by driver to notify scan status completed */ +void ieee80211_scan_completed(struct ieee80211_hw *hw); + +/* Function to indicate Radar Detection. The low level driver must call this + * function to indicate the presence of radar in the current channel. + * Additionally the radar type also could be sent */ +int ieee80211_radar_status(struct ieee80211_hw *hw, int channel, + int radar, int radar_type); + +/* return a pointer to the source address (SA) */ +static inline u8 *ieee80211_get_SA(struct ieee80211_hdr *hdr) +{ + u8 *raw = (u8 *) hdr; + u8 tofrom = (*(raw+1)) & 3; /* get the TODS and FROMDS bits */ + + switch (tofrom) { + case 2: + return hdr->addr3; + case 3: + return hdr->addr4; + } + return hdr->addr2; +} + +/* return a pointer to the destination address (DA) */ +static inline u8 *ieee80211_get_DA(struct ieee80211_hdr *hdr) +{ + u8 *raw = (u8 *) hdr; + u8 to_ds = (*(raw+1)) & 1; /* get the TODS bit */ + + if (to_ds) + return hdr->addr3; + return hdr->addr1; +} + +static inline int ieee80211_get_morefrag(struct ieee80211_hdr *hdr) +{ + return (le16_to_cpu(hdr->frame_control) & + IEEE80211_FCTL_MOREFRAGS) != 0; +} + +#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x" +#define MAC_ARG(x) ((u8*)(x))[0], ((u8*)(x))[1], ((u8*)(x))[2], \ + ((u8*)(x))[3], ((u8*)(x))[4], ((u8*)(x))[5] + +#endif /* MAC80211_H */ diff --git a/lib/Kconfig b/lib/Kconfig index 3842499..8e91748 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -33,6 +33,14 @@ config CRC32 kernel tree does. Such modules that use library CRC32 functions require M here. +config CRC_ITU_T + tristate "CRC ITU-T V.41 functions" + help + This option is provided for the case where no in-kernel-tree + modules require CRC ITU-T V.41 functions, but a module built outside + the kernel tree does. Such modules that use library CRC ITU-T V.41 + functions require M here. + config LIBCRC32C tristate "CRC32c (Castagnoli, et al) Cyclic Redundancy-Check" help diff --git a/lib/Makefile b/lib/Makefile index ae57f35..53b5da5 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_BITREVERSE) += bitrev.o obj-$(CONFIG_CRC_CCITT) += crc-ccitt.o obj-$(CONFIG_CRC16) += crc16.o obj-$(CONFIG_CRC32) += crc32.o +obj-$(CONFIG_CRC_ITU_T) += crc-itu-t.o obj-$(CONFIG_LIBCRC32C) += libcrc32c.o obj-$(CONFIG_GENERIC_ALLOCATOR) += genalloc.o diff --git a/lib/crc-itu-t.c b/lib/crc-itu-t.c new file mode 100644 index 0000000..0ca977c --- /dev/null +++ b/lib/crc-itu-t.c @@ -0,0 +1,64 @@ +/* + * crc-itu-t.c + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include +#include +#include + +/** CRC table for the CRC ITU-T V.41 0x0x1021 (x^16 + x^12 + x^15 + 1) */ +const u16 crc_itu_t_table[256] = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 +}; +EXPORT_SYMBOL(crc_itu_t_table); + +/** + * crc_itu_t - Compute the CRC-ITU-T for the data buffer + * @crc: previous CRC value + * @buffer: data pointer + * @len: number of bytes in the buffer + */ +u16 crc_itu_t(u16 crc, const u8 *buffer, size_t len) +{ + while (len--) + crc = crc_itu_t_byte(crc, *buffer++); + return crc; +} +EXPORT_SYMBOL(crc_itu_t); + +MODULE_DESCRIPTION("CRC ITU-T V.41 calculations"); +MODULE_LICENSE("GPL"); diff --git a/net/Kconfig b/net/Kconfig index 2fc8e77..5b22278 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -220,6 +220,7 @@ config FIB_RULES menu "Wireless" source "net/wireless/Kconfig" +source "net/mac80211/Kconfig" source "net/ieee80211/Kconfig" endmenu diff --git a/net/Makefile b/net/Makefile index 6b74d41..9fdb60c 100644 --- a/net/Makefile +++ b/net/Makefile @@ -45,6 +45,8 @@ obj-$(CONFIG_ECONET) += econet/ obj-$(CONFIG_VLAN_8021Q) += 8021q/ obj-$(CONFIG_IP_DCCP) += dccp/ obj-$(CONFIG_IP_SCTP) += sctp/ +obj-y += wireless/ +obj-$(CONFIG_MAC80211) += mac80211/ obj-$(CONFIG_IEEE80211) += ieee80211/ obj-$(CONFIG_TIPC) += tipc/ obj-$(CONFIG_NETLABEL) += netlabel/ @@ -53,5 +55,3 @@ obj-$(CONFIG_IUCV) += iucv/ ifeq ($(CONFIG_NET),y) obj-$(CONFIG_SYSCTL) += sysctl_net.o endif - -obj-y += wireless/ diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig new file mode 100644 index 0000000..a03c886 --- /dev/null +++ b/net/mac80211/Kconfig @@ -0,0 +1,82 @@ +config MAC80211 + tristate "Generic IEEE 802.11 Networking Stack (mac80211)" + depends on EXPERIMENTAL + select CRYPTO + select CRYPTO_ECB + select CRYPTO_ARC4 + select CRYPTO_AES + select CRC32 + select WIRELESS_EXT + select CFG80211 + select NET_SCH_FIFO + ---help--- + This option enables the hardware independent IEEE 802.11 + networking stack. + +config MAC80211_LEDS + bool "Enable LED triggers" + depends on MAC80211 && LEDS_TRIGGERS + ---help--- + This option enables a few LED triggers for different + packet receive/transmit events. + +config MAC80211_DEBUGFS + bool "Export mac80211 internals in DebugFS" + depends on MAC80211 && DEBUG_FS + ---help--- + Select this to see extensive information about + the internal state of mac80211 in debugfs. + + Say N unless you know you need this. + +config MAC80211_DEBUG + bool "Enable debugging output" + depends on MAC80211 + ---help--- + This option will enable debug tracing output for the + ieee80211 network stack. + + If you are not trying to debug or develop the ieee80211 + subsystem, you most likely want to say N here. + +config MAC80211_VERBOSE_DEBUG + bool "Verbose debugging output" + depends on MAC80211_DEBUG + +config MAC80211_LOWTX_FRAME_DUMP + bool "Debug frame dumping" + depends on MAC80211_DEBUG + ---help--- + Selecting this option will cause the stack to + print a message for each frame that is handed + to the lowlevel driver for transmission. This + message includes all MAC addresses and the + frame control field. + + If unsure, say N and insert the debugging code + you require into the driver you are debugging. + +config TKIP_DEBUG + bool "TKIP debugging" + depends on MAC80211_DEBUG + +config MAC80211_DEBUG_COUNTERS + bool "Extra statistics for TX/RX debugging" + depends on MAC80211_DEBUG + +config HOSTAPD_WPA_TESTING + bool "Support for TKIP countermeasures testing" + depends on MAC80211_DEBUG + +config MAC80211_IBSS_DEBUG + bool "Support for IBSS testing" + depends on MAC80211_DEBUG + ---help--- + Say Y here if you intend to debug the IBSS code. + +config MAC80211_VERBOSE_PS_DEBUG + bool "Verbose powersave mode debugging" + depends on MAC80211_DEBUG + ---help--- + Say Y here to print out verbose powersave + mode debug messages. diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile new file mode 100644 index 0000000..e9738da --- /dev/null +++ b/net/mac80211/Makefile @@ -0,0 +1,20 @@ +obj-$(CONFIG_MAC80211) += mac80211.o rc80211_simple.o + +mac80211-objs-$(CONFIG_MAC80211_LEDS) += ieee80211_led.o +mac80211-objs-$(CONFIG_MAC80211_DEBUGFS) += debugfs.o debugfs_sta.o debugfs_netdev.o debugfs_key.o + +mac80211-objs := \ + ieee80211.o \ + ieee80211_ioctl.o \ + sta_info.o \ + wep.o \ + wpa.o \ + ieee80211_sta.o \ + ieee80211_iface.o \ + ieee80211_rate.o \ + michael.o \ + tkip.o \ + aes_ccm.o \ + wme.o \ + ieee80211_cfg.o \ + $(mac80211-objs-y) diff --git a/net/mac80211/aes_ccm.c b/net/mac80211/aes_ccm.c new file mode 100644 index 0000000..e55569b --- /dev/null +++ b/net/mac80211/aes_ccm.c @@ -0,0 +1,155 @@ +/* + * Copyright 2003-2004, Instant802 Networks, Inc. + * Copyright 2005-2006, Devicescape Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include +#include "ieee80211_key.h" +#include "aes_ccm.h" + + +static void ieee80211_aes_encrypt(struct crypto_cipher *tfm, + const u8 pt[16], u8 ct[16]) +{ + crypto_cipher_encrypt_one(tfm, ct, pt); +} + + +static inline void aes_ccm_prepare(struct crypto_cipher *tfm, u8 *b_0, u8 *aad, + u8 *b, u8 *s_0, u8 *a) +{ + int i; + + ieee80211_aes_encrypt(tfm, b_0, b); + + /* Extra Authenticate-only data (always two AES blocks) */ + for (i = 0; i < AES_BLOCK_LEN; i++) + aad[i] ^= b[i]; + ieee80211_aes_encrypt(tfm, aad, b); + + aad += AES_BLOCK_LEN; + + for (i = 0; i < AES_BLOCK_LEN; i++) + aad[i] ^= b[i]; + ieee80211_aes_encrypt(tfm, aad, a); + + /* Mask out bits from auth-only-b_0 */ + b_0[0] &= 0x07; + + /* S_0 is used to encrypt T (= MIC) */ + b_0[14] = 0; + b_0[15] = 0; + ieee80211_aes_encrypt(tfm, b_0, s_0); +} + + +void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch, + u8 *b_0, u8 *aad, u8 *data, size_t data_len, + u8 *cdata, u8 *mic) +{ + int i, j, last_len, num_blocks; + u8 *pos, *cpos, *b, *s_0, *e; + + b = scratch; + s_0 = scratch + AES_BLOCK_LEN; + e = scratch + 2 * AES_BLOCK_LEN; + + num_blocks = (data_len + AES_BLOCK_LEN - 1) / AES_BLOCK_LEN; + last_len = data_len % AES_BLOCK_LEN; + aes_ccm_prepare(tfm, b_0, aad, b, s_0, b); + + /* Process payload blocks */ + pos = data; + cpos = cdata; + for (j = 1; j <= num_blocks; j++) { + int blen = (j == num_blocks && last_len) ? + last_len : AES_BLOCK_LEN; + + /* Authentication followed by encryption */ + for (i = 0; i < blen; i++) + b[i] ^= pos[i]; + ieee80211_aes_encrypt(tfm, b, b); + + b_0[14] = (j >> 8) & 0xff; + b_0[15] = j & 0xff; + ieee80211_aes_encrypt(tfm, b_0, e); + for (i = 0; i < blen; i++) + *cpos++ = *pos++ ^ e[i]; + } + + for (i = 0; i < CCMP_MIC_LEN; i++) + mic[i] = b[i] ^ s_0[i]; +} + + +int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch, + u8 *b_0, u8 *aad, u8 *cdata, size_t data_len, + u8 *mic, u8 *data) +{ + int i, j, last_len, num_blocks; + u8 *pos, *cpos, *b, *s_0, *a; + + b = scratch; + s_0 = scratch + AES_BLOCK_LEN; + a = scratch + 2 * AES_BLOCK_LEN; + + num_blocks = (data_len + AES_BLOCK_LEN - 1) / AES_BLOCK_LEN; + last_len = data_len % AES_BLOCK_LEN; + aes_ccm_prepare(tfm, b_0, aad, b, s_0, a); + + /* Process payload blocks */ + cpos = cdata; + pos = data; + for (j = 1; j <= num_blocks; j++) { + int blen = (j == num_blocks && last_len) ? + last_len : AES_BLOCK_LEN; + + /* Decryption followed by authentication */ + b_0[14] = (j >> 8) & 0xff; + b_0[15] = j & 0xff; + ieee80211_aes_encrypt(tfm, b_0, b); + for (i = 0; i < blen; i++) { + *pos = *cpos++ ^ b[i]; + a[i] ^= *pos++; + } + + ieee80211_aes_encrypt(tfm, a, a); + } + + for (i = 0; i < CCMP_MIC_LEN; i++) { + if ((mic[i] ^ s_0[i]) != a[i]) + return -1; + } + + return 0; +} + + +struct crypto_cipher * ieee80211_aes_key_setup_encrypt(const u8 key[]) +{ + struct crypto_cipher *tfm; + + tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(tfm)) + return NULL; + + crypto_cipher_setkey(tfm, key, ALG_CCMP_KEY_LEN); + + return tfm; +} + + +void ieee80211_aes_key_free(struct crypto_cipher *tfm) +{ + if (tfm) + crypto_free_cipher(tfm); +} diff --git a/net/mac80211/aes_ccm.h b/net/mac80211/aes_ccm.h new file mode 100644 index 0000000..885f190 --- /dev/null +++ b/net/mac80211/aes_ccm.h @@ -0,0 +1,26 @@ +/* + * Copyright 2003-2004, Instant802 Networks, Inc. + * Copyright 2006, Devicescape Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef AES_CCM_H +#define AES_CCM_H + +#include + +#define AES_BLOCK_LEN 16 + +struct crypto_cipher * ieee80211_aes_key_setup_encrypt(const u8 key[]); +void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch, + u8 *b_0, u8 *aad, u8 *data, size_t data_len, + u8 *cdata, u8 *mic); +int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch, + u8 *b_0, u8 *aad, u8 *cdata, size_t data_len, + u8 *mic, u8 *data); +void ieee80211_aes_key_free(struct crypto_cipher *tfm); + +#endif /* AES_CCM_H */ diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c new file mode 100644 index 0000000..bb6c0fe --- /dev/null +++ b/net/mac80211/debugfs.c @@ -0,0 +1,433 @@ +/* + * mac80211 debugfs for wireless PHYs + * + * Copyright 2007 Johannes Berg + * + * GPLv2 + * + */ + +#include +#include +#include "ieee80211_i.h" +#include "ieee80211_rate.h" +#include "debugfs.h" + +int mac80211_open_file_generic(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static const char *ieee80211_mode_str(int mode) +{ + switch (mode) { + case MODE_IEEE80211A: + return "IEEE 802.11a"; + case MODE_IEEE80211B: + return "IEEE 802.11b"; + case MODE_IEEE80211G: + return "IEEE 802.11g"; + case MODE_ATHEROS_TURBO: + return "Atheros Turbo (5 GHz)"; + default: + return "UNKNOWN"; + } +} + +static ssize_t modes_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct ieee80211_local *local = file->private_data; + struct ieee80211_hw_mode *mode; + char buf[150], *p = buf; + + /* FIXME: locking! */ + list_for_each_entry(mode, &local->modes_list, list) { + p += scnprintf(p, sizeof(buf)+buf-p, + "%s\n", ieee80211_mode_str(mode->mode)); + } + + return simple_read_from_buffer(userbuf, count, ppos, buf, p-buf); +} + +static const struct file_operations modes_ops = { + .read = modes_read, + .open = mac80211_open_file_generic, +}; + +#define DEBUGFS_READONLY_FILE(name, buflen, fmt, value...) \ +static ssize_t name## _read(struct file *file, char __user *userbuf, \ + size_t count, loff_t *ppos) \ +{ \ + struct ieee80211_local *local = file->private_data; \ + char buf[buflen]; \ + int res; \ + \ + res = scnprintf(buf, buflen, fmt "\n", ##value); \ + return simple_read_from_buffer(userbuf, count, ppos, buf, res); \ +} \ + \ +static const struct file_operations name## _ops = { \ + .read = name## _read, \ + .open = mac80211_open_file_generic, \ +}; + +#define DEBUGFS_ADD(name) \ + local->debugfs.name = debugfs_create_file(#name, 0444, phyd, \ + local, &name## _ops); + +#define DEBUGFS_DEL(name) \ + debugfs_remove(local->debugfs.name); \ + local->debugfs.name = NULL; + + +DEBUGFS_READONLY_FILE(channel, 20, "%d", + local->hw.conf.channel); +DEBUGFS_READONLY_FILE(frequency, 20, "%d", + local->hw.conf.freq); +DEBUGFS_READONLY_FILE(radar_detect, 20, "%d", + local->hw.conf.radar_detect); +DEBUGFS_READONLY_FILE(antenna_sel_tx, 20, "%d", + local->hw.conf.antenna_sel_tx); +DEBUGFS_READONLY_FILE(antenna_sel_rx, 20, "%d", + local->hw.conf.antenna_sel_rx); +DEBUGFS_READONLY_FILE(bridge_packets, 20, "%d", + local->bridge_packets); +DEBUGFS_READONLY_FILE(key_tx_rx_threshold, 20, "%d", + local->key_tx_rx_threshold); +DEBUGFS_READONLY_FILE(rts_threshold, 20, "%d", + local->rts_threshold); +DEBUGFS_READONLY_FILE(fragmentation_threshold, 20, "%d", + local->fragmentation_threshold); +DEBUGFS_READONLY_FILE(short_retry_limit, 20, "%d", + local->short_retry_limit); +DEBUGFS_READONLY_FILE(long_retry_limit, 20, "%d", + local->long_retry_limit); +DEBUGFS_READONLY_FILE(total_ps_buffered, 20, "%d", + local->total_ps_buffered); +DEBUGFS_READONLY_FILE(mode, 20, "%s", + ieee80211_mode_str(local->hw.conf.phymode)); +DEBUGFS_READONLY_FILE(wep_iv, 20, "%#06x", + local->wep_iv & 0xffffff); +DEBUGFS_READONLY_FILE(tx_power_reduction, 20, "%d.%d dBm", + local->hw.conf.tx_power_reduction / 10, + local->hw.conf.tx_power_reduction & 10); +DEBUGFS_READONLY_FILE(rate_ctrl_alg, 100, "%s", + local->rate_ctrl ? local->rate_ctrl->ops->name : ""); + +/* statistics stuff */ + +static inline int rtnl_lock_local(struct ieee80211_local *local) +{ + rtnl_lock(); + if (unlikely(local->reg_state != IEEE80211_DEV_REGISTERED)) { + rtnl_unlock(); + return -ENODEV; + } + return 0; +} + +#define DEBUGFS_STATS_FILE(name, buflen, fmt, value...) \ + DEBUGFS_READONLY_FILE(stats_ ##name, buflen, fmt, ##value) + +static ssize_t format_devstat_counter(struct ieee80211_local *local, + char __user *userbuf, + size_t count, loff_t *ppos, + int (*printvalue)(struct ieee80211_low_level_stats *stats, char *buf, + int buflen)) +{ + struct ieee80211_low_level_stats stats; + char buf[20]; + int res; + + if (!local->ops->get_stats) + return -EOPNOTSUPP; + + res = rtnl_lock_local(local); + if (res) + return res; + + res = local->ops->get_stats(local_to_hw(local), &stats); + rtnl_unlock(); + if (!res) + res = printvalue(&stats, buf, sizeof(buf)); + return simple_read_from_buffer(userbuf, count, ppos, buf, res); +} + +#define DEBUGFS_DEVSTATS_FILE(name) \ +static int print_devstats_##name(struct ieee80211_low_level_stats *stats,\ + char *buf, int buflen) \ +{ \ + return scnprintf(buf, buflen, "%u\n", stats->name); \ +} \ +static ssize_t stats_ ##name## _read(struct file *file, \ + char __user *userbuf, \ + size_t count, loff_t *ppos) \ +{ \ + return format_devstat_counter(file->private_data, \ + userbuf, \ + count, \ + ppos, \ + print_devstats_##name); \ +} \ + \ +static const struct file_operations stats_ ##name## _ops = { \ + .read = stats_ ##name## _read, \ + .open = mac80211_open_file_generic, \ +}; + +#define DEBUGFS_STATS_ADD(name) \ + local->debugfs.stats.name = debugfs_create_file(#name, 0444, statsd,\ + local, &stats_ ##name## _ops); + +#define DEBUGFS_STATS_DEL(name) \ + debugfs_remove(local->debugfs.stats.name); \ + local->debugfs.stats.name = NULL; + +DEBUGFS_STATS_FILE(transmitted_fragment_count, 20, "%u", + local->dot11TransmittedFragmentCount); +DEBUGFS_STATS_FILE(multicast_transmitted_frame_count, 20, "%u", + local->dot11MulticastTransmittedFrameCount); +DEBUGFS_STATS_FILE(failed_count, 20, "%u", + local->dot11FailedCount); +DEBUGFS_STATS_FILE(retry_count, 20, "%u", + local->dot11RetryCount); +DEBUGFS_STATS_FILE(multiple_retry_count, 20, "%u", + local->dot11MultipleRetryCount); +DEBUGFS_STATS_FILE(frame_duplicate_count, 20, "%u", + local->dot11FrameDuplicateCount); +DEBUGFS_STATS_FILE(received_fragment_count, 20, "%u", + local->dot11ReceivedFragmentCount); +DEBUGFS_STATS_FILE(multicast_received_frame_count, 20, "%u", + local->dot11MulticastReceivedFrameCount); +DEBUGFS_STATS_FILE(transmitted_frame_count, 20, "%u", + local->dot11TransmittedFrameCount); +DEBUGFS_STATS_FILE(wep_undecryptable_count, 20, "%u", + local->dot11WEPUndecryptableCount); +#ifdef CONFIG_MAC80211_DEBUG_COUNTERS +DEBUGFS_STATS_FILE(tx_handlers_drop, 20, "%u", + local->tx_handlers_drop); +DEBUGFS_STATS_FILE(tx_handlers_queued, 20, "%u", + local->tx_handlers_queued); +DEBUGFS_STATS_FILE(tx_handlers_drop_unencrypted, 20, "%u", + local->tx_handlers_drop_unencrypted); +DEBUGFS_STATS_FILE(tx_handlers_drop_fragment, 20, "%u", + local->tx_handlers_drop_fragment); +DEBUGFS_STATS_FILE(tx_handlers_drop_wep, 20, "%u", + local->tx_handlers_drop_wep); +DEBUGFS_STATS_FILE(tx_handlers_drop_not_assoc, 20, "%u", + local->tx_handlers_drop_not_assoc); +DEBUGFS_STATS_FILE(tx_handlers_drop_unauth_port, 20, "%u", + local->tx_handlers_drop_unauth_port); +DEBUGFS_STATS_FILE(rx_handlers_drop, 20, "%u", + local->rx_handlers_drop); +DEBUGFS_STATS_FILE(rx_handlers_queued, 20, "%u", + local->rx_handlers_queued); +DEBUGFS_STATS_FILE(rx_handlers_drop_nullfunc, 20, "%u", + local->rx_handlers_drop_nullfunc); +DEBUGFS_STATS_FILE(rx_handlers_drop_defrag, 20, "%u", + local->rx_handlers_drop_defrag); +DEBUGFS_STATS_FILE(rx_handlers_drop_short, 20, "%u", + local->rx_handlers_drop_short); +DEBUGFS_STATS_FILE(rx_handlers_drop_passive_scan, 20, "%u", + local->rx_handlers_drop_passive_scan); +DEBUGFS_STATS_FILE(tx_expand_skb_head, 20, "%u", + local->tx_expand_skb_head); +DEBUGFS_STATS_FILE(tx_expand_skb_head_cloned, 20, "%u", + local->tx_expand_skb_head_cloned); +DEBUGFS_STATS_FILE(rx_expand_skb_head, 20, "%u", + local->rx_expand_skb_head); +DEBUGFS_STATS_FILE(rx_expand_skb_head2, 20, "%u", + local->rx_expand_skb_head2); +DEBUGFS_STATS_FILE(rx_handlers_fragments, 20, "%u", + local->rx_handlers_fragments); +DEBUGFS_STATS_FILE(tx_status_drop, 20, "%u", + local->tx_status_drop); + +static ssize_t stats_wme_rx_queue_read(struct file *file, + char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct ieee80211_local *local = file->private_data; + char buf[NUM_RX_DATA_QUEUES*15], *p = buf; + int i; + + for (i = 0; i < NUM_RX_DATA_QUEUES; i++) + p += scnprintf(p, sizeof(buf)+buf-p, + "%u\n", local->wme_rx_queue[i]); + + return simple_read_from_buffer(userbuf, count, ppos, buf, p-buf); +} + +static const struct file_operations stats_wme_rx_queue_ops = { + .read = stats_wme_rx_queue_read, + .open = mac80211_open_file_generic, +}; + +static ssize_t stats_wme_tx_queue_read(struct file *file, + char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct ieee80211_local *local = file->private_data; + char buf[NUM_TX_DATA_QUEUES*15], *p = buf; + int i; + + for (i = 0; i < NUM_TX_DATA_QUEUES; i++) + p += scnprintf(p, sizeof(buf)+buf-p, + "%u\n", local->wme_tx_queue[i]); + + return simple_read_from_buffer(userbuf, count, ppos, buf, p-buf); +} + +static const struct file_operations stats_wme_tx_queue_ops = { + .read = stats_wme_tx_queue_read, + .open = mac80211_open_file_generic, +}; +#endif + +DEBUGFS_DEVSTATS_FILE(dot11ACKFailureCount); +DEBUGFS_DEVSTATS_FILE(dot11RTSFailureCount); +DEBUGFS_DEVSTATS_FILE(dot11FCSErrorCount); +DEBUGFS_DEVSTATS_FILE(dot11RTSSuccessCount); + + +void debugfs_hw_add(struct ieee80211_local *local) +{ + struct dentry *phyd = local->hw.wiphy->debugfsdir; + struct dentry *statsd; + + if (!phyd) + return; + + local->debugfs.stations = debugfs_create_dir("stations", phyd); + local->debugfs.keys = debugfs_create_dir("keys", phyd); + + DEBUGFS_ADD(channel); + DEBUGFS_ADD(frequency); + DEBUGFS_ADD(radar_detect); + DEBUGFS_ADD(antenna_sel_tx); + DEBUGFS_ADD(antenna_sel_rx); + DEBUGFS_ADD(bridge_packets); + DEBUGFS_ADD(key_tx_rx_threshold); + DEBUGFS_ADD(rts_threshold); + DEBUGFS_ADD(fragmentation_threshold); + DEBUGFS_ADD(short_retry_limit); + DEBUGFS_ADD(long_retry_limit); + DEBUGFS_ADD(total_ps_buffered); + DEBUGFS_ADD(mode); + DEBUGFS_ADD(wep_iv); + DEBUGFS_ADD(tx_power_reduction); + DEBUGFS_ADD(modes); + + statsd = debugfs_create_dir("statistics", phyd); + local->debugfs.statistics = statsd; + + /* if the dir failed, don't put all the other things into the root! */ + if (!statsd) + return; + + DEBUGFS_STATS_ADD(transmitted_fragment_count); + DEBUGFS_STATS_ADD(multicast_transmitted_frame_count); + DEBUGFS_STATS_ADD(failed_count); + DEBUGFS_STATS_ADD(retry_count); + DEBUGFS_STATS_ADD(multiple_retry_count); + DEBUGFS_STATS_ADD(frame_duplicate_count); + DEBUGFS_STATS_ADD(received_fragment_count); + DEBUGFS_STATS_ADD(multicast_received_frame_count); + DEBUGFS_STATS_ADD(transmitted_frame_count); + DEBUGFS_STATS_ADD(wep_undecryptable_count); +#ifdef CONFIG_MAC80211_DEBUG_COUNTERS + DEBUGFS_STATS_ADD(tx_handlers_drop); + DEBUGFS_STATS_ADD(tx_handlers_queued); + DEBUGFS_STATS_ADD(tx_handlers_drop_unencrypted); + DEBUGFS_STATS_ADD(tx_handlers_drop_fragment); + DEBUGFS_STATS_ADD(tx_handlers_drop_wep); + DEBUGFS_STATS_ADD(tx_handlers_drop_not_assoc); + DEBUGFS_STATS_ADD(tx_handlers_drop_unauth_port); + DEBUGFS_STATS_ADD(rx_handlers_drop); + DEBUGFS_STATS_ADD(rx_handlers_queued); + DEBUGFS_STATS_ADD(rx_handlers_drop_nullfunc); + DEBUGFS_STATS_ADD(rx_handlers_drop_defrag); + DEBUGFS_STATS_ADD(rx_handlers_drop_short); + DEBUGFS_STATS_ADD(rx_handlers_drop_passive_scan); + DEBUGFS_STATS_ADD(tx_expand_skb_head); + DEBUGFS_STATS_ADD(tx_expand_skb_head_cloned); + DEBUGFS_STATS_ADD(rx_expand_skb_head); + DEBUGFS_STATS_ADD(rx_expand_skb_head2); + DEBUGFS_STATS_ADD(rx_handlers_fragments); + DEBUGFS_STATS_ADD(tx_status_drop); + DEBUGFS_STATS_ADD(wme_tx_queue); + DEBUGFS_STATS_ADD(wme_rx_queue); +#endif + DEBUGFS_STATS_ADD(dot11ACKFailureCount); + DEBUGFS_STATS_ADD(dot11RTSFailureCount); + DEBUGFS_STATS_ADD(dot11FCSErrorCount); + DEBUGFS_STATS_ADD(dot11RTSSuccessCount); +} + +void debugfs_hw_del(struct ieee80211_local *local) +{ + DEBUGFS_DEL(channel); + DEBUGFS_DEL(frequency); + DEBUGFS_DEL(radar_detect); + DEBUGFS_DEL(antenna_sel_tx); + DEBUGFS_DEL(antenna_sel_rx); + DEBUGFS_DEL(bridge_packets); + DEBUGFS_DEL(key_tx_rx_threshold); + DEBUGFS_DEL(rts_threshold); + DEBUGFS_DEL(fragmentation_threshold); + DEBUGFS_DEL(short_retry_limit); + DEBUGFS_DEL(long_retry_limit); + DEBUGFS_DEL(total_ps_buffered); + DEBUGFS_DEL(mode); + DEBUGFS_DEL(wep_iv); + DEBUGFS_DEL(tx_power_reduction); + DEBUGFS_DEL(modes); + + DEBUGFS_STATS_DEL(transmitted_fragment_count); + DEBUGFS_STATS_DEL(multicast_transmitted_frame_count); + DEBUGFS_STATS_DEL(failed_count); + DEBUGFS_STATS_DEL(retry_count); + DEBUGFS_STATS_DEL(multiple_retry_count); + DEBUGFS_STATS_DEL(frame_duplicate_count); + DEBUGFS_STATS_DEL(received_fragment_count); + DEBUGFS_STATS_DEL(multicast_received_frame_count); + DEBUGFS_STATS_DEL(transmitted_frame_count); + DEBUGFS_STATS_DEL(wep_undecryptable_count); + DEBUGFS_STATS_DEL(num_scans); +#ifdef CONFIG_MAC80211_DEBUG_COUNTERS + DEBUGFS_STATS_DEL(tx_handlers_drop); + DEBUGFS_STATS_DEL(tx_handlers_queued); + DEBUGFS_STATS_DEL(tx_handlers_drop_unencrypted); + DEBUGFS_STATS_DEL(tx_handlers_drop_fragment); + DEBUGFS_STATS_DEL(tx_handlers_drop_wep); + DEBUGFS_STATS_DEL(tx_handlers_drop_not_assoc); + DEBUGFS_STATS_DEL(tx_handlers_drop_unauth_port); + DEBUGFS_STATS_DEL(rx_handlers_drop); + DEBUGFS_STATS_DEL(rx_handlers_queued); + DEBUGFS_STATS_DEL(rx_handlers_drop_nullfunc); + DEBUGFS_STATS_DEL(rx_handlers_drop_defrag); + DEBUGFS_STATS_DEL(rx_handlers_drop_short); + DEBUGFS_STATS_DEL(rx_handlers_drop_passive_scan); + DEBUGFS_STATS_DEL(tx_expand_skb_head); + DEBUGFS_STATS_DEL(tx_expand_skb_head_cloned); + DEBUGFS_STATS_DEL(rx_expand_skb_head); + DEBUGFS_STATS_DEL(rx_expand_skb_head2); + DEBUGFS_STATS_DEL(rx_handlers_fragments); + DEBUGFS_STATS_DEL(tx_status_drop); + DEBUGFS_STATS_DEL(wme_tx_queue); + DEBUGFS_STATS_DEL(wme_rx_queue); +#endif + DEBUGFS_STATS_DEL(dot11ACKFailureCount); + DEBUGFS_STATS_DEL(dot11RTSFailureCount); + DEBUGFS_STATS_DEL(dot11FCSErrorCount); + DEBUGFS_STATS_DEL(dot11RTSSuccessCount); + + debugfs_remove(local->debugfs.statistics); + local->debugfs.statistics = NULL; + debugfs_remove(local->debugfs.stations); + local->debugfs.stations = NULL; + debugfs_remove(local->debugfs.keys); + local->debugfs.keys = NULL; +} diff --git a/net/mac80211/debugfs.h b/net/mac80211/debugfs.h new file mode 100644 index 0000000..dd25419 --- /dev/null +++ b/net/mac80211/debugfs.h @@ -0,0 +1,16 @@ +#ifndef __MAC80211_DEBUGFS_H +#define __MAC80211_DEBUGFS_H + +#ifdef CONFIG_MAC80211_DEBUGFS +extern void debugfs_hw_add(struct ieee80211_local *local); +extern void debugfs_hw_del(struct ieee80211_local *local); +extern int mac80211_open_file_generic(struct inode *inode, struct file *file); +#else +static inline void debugfs_hw_add(struct ieee80211_local *local) +{ + return; +} +static inline void debugfs_hw_del(struct ieee80211_local *local) {} +#endif + +#endif /* __MAC80211_DEBUGFS_H */ diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c new file mode 100644 index 0000000..7d56dc9 --- /dev/null +++ b/net/mac80211/debugfs_key.c @@ -0,0 +1,252 @@ +/* + * Copyright 2003-2005 Devicescape Software, Inc. + * Copyright (c) 2006 Jiri Benc + * Copyright 2007 Johannes Berg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include "ieee80211_i.h" +#include "ieee80211_key.h" +#include "debugfs.h" +#include "debugfs_key.h" + +#define KEY_READ(name, buflen, format_string) \ +static ssize_t key_##name##_read(struct file *file, \ + char __user *userbuf, \ + size_t count, loff_t *ppos) \ +{ \ + char buf[buflen]; \ + struct ieee80211_key *key = file->private_data; \ + int res = scnprintf(buf, buflen, format_string, key->name); \ + return simple_read_from_buffer(userbuf, count, ppos, buf, res); \ +} +#define KEY_READ_D(name) KEY_READ(name, 20, "%d\n") + +#define KEY_OPS(name) \ +static const struct file_operations key_ ##name## _ops = { \ + .read = key_##name##_read, \ + .open = mac80211_open_file_generic, \ +} + +#define KEY_FILE(name, format) \ + KEY_READ_##format(name) \ + KEY_OPS(name) + +KEY_FILE(keylen, D); +KEY_FILE(force_sw_encrypt, D); +KEY_FILE(keyidx, D); +KEY_FILE(hw_key_idx, D); +KEY_FILE(tx_rx_count, D); + +static ssize_t key_algorithm_read(struct file *file, + char __user *userbuf, + size_t count, loff_t *ppos) +{ + char *alg; + struct ieee80211_key *key = file->private_data; + + switch (key->alg) { + case ALG_WEP: + alg = "WEP\n"; + break; + case ALG_TKIP: + alg = "TKIP\n"; + break; + case ALG_CCMP: + alg = "CCMP\n"; + break; + default: + return 0; + } + return simple_read_from_buffer(userbuf, count, ppos, alg, strlen(alg)); +} +KEY_OPS(algorithm); + +static ssize_t key_tx_spec_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + const u8 *tpn; + char buf[20]; + int len; + struct ieee80211_key *key = file->private_data; + + switch (key->alg) { + case ALG_WEP: + len = scnprintf(buf, sizeof(buf), "\n"); + case ALG_TKIP: + len = scnprintf(buf, sizeof(buf), "%08x %04x\n", + key->u.tkip.iv32, + key->u.tkip.iv16); + case ALG_CCMP: + tpn = key->u.ccmp.tx_pn; + len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n", + tpn[0], tpn[1], tpn[2], tpn[3], tpn[4], tpn[5]); + default: + return 0; + } + return simple_read_from_buffer(userbuf, count, ppos, buf, len); +} +KEY_OPS(tx_spec); + +static ssize_t key_rx_spec_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct ieee80211_key *key = file->private_data; + char buf[14*NUM_RX_DATA_QUEUES+1], *p = buf; + int i, len; + const u8 *rpn; + + switch (key->alg) { + case ALG_WEP: + len = scnprintf(buf, sizeof(buf), "\n"); + case ALG_TKIP: + for (i = 0; i < NUM_RX_DATA_QUEUES; i++) + p += scnprintf(p, sizeof(buf)+buf-p, + "%08x %04x\n", + key->u.tkip.iv32_rx[i], + key->u.tkip.iv16_rx[i]); + len = p - buf; + case ALG_CCMP: + for (i = 0; i < NUM_RX_DATA_QUEUES; i++) { + rpn = key->u.ccmp.rx_pn[i]; + p += scnprintf(p, sizeof(buf)+buf-p, + "%02x%02x%02x%02x%02x%02x\n", + rpn[0], rpn[1], rpn[2], + rpn[3], rpn[4], rpn[5]); + } + len = p - buf; + default: + return 0; + } + return simple_read_from_buffer(userbuf, count, ppos, buf, len); +} +KEY_OPS(rx_spec); + +static ssize_t key_replays_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct ieee80211_key *key = file->private_data; + char buf[20]; + int len; + + if (key->alg != ALG_CCMP) + return 0; + len = scnprintf(buf, sizeof(buf), "%u\n", key->u.ccmp.replays); + return simple_read_from_buffer(userbuf, count, ppos, buf, len); +} +KEY_OPS(replays); + +static ssize_t key_key_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct ieee80211_key *key = file->private_data; + int i, res, bufsize = 2*key->keylen+2; + char *buf = kmalloc(bufsize, GFP_KERNEL); + char *p = buf; + + for (i = 0; i < key->keylen; i++) + p += scnprintf(p, bufsize+buf-p, "%02x", key->key[i]); + p += scnprintf(p, bufsize+buf-p, "\n"); + res = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf); + kfree(buf); + return res; +} +KEY_OPS(key); + +#define DEBUGFS_ADD(name) \ + key->debugfs.name = debugfs_create_file(#name, 0400,\ + key->debugfs.dir, key, &key_##name##_ops); + +void ieee80211_debugfs_key_add(struct ieee80211_local *local, + struct ieee80211_key *key) +{ + char buf[20]; + + if (!local->debugfs.keys) + return; + + sprintf(buf, "%d", key->keyidx); + key->debugfs.dir = debugfs_create_dir(buf, + local->debugfs.keys); + + if (!key->debugfs.dir) + return; + + DEBUGFS_ADD(keylen); + DEBUGFS_ADD(force_sw_encrypt); + DEBUGFS_ADD(keyidx); + DEBUGFS_ADD(hw_key_idx); + DEBUGFS_ADD(tx_rx_count); + DEBUGFS_ADD(algorithm); + DEBUGFS_ADD(tx_spec); + DEBUGFS_ADD(rx_spec); + DEBUGFS_ADD(replays); + DEBUGFS_ADD(key); +}; + +#define DEBUGFS_DEL(name) \ + debugfs_remove(key->debugfs.name); key->debugfs.name = NULL; + +void ieee80211_debugfs_key_remove(struct ieee80211_key *key) +{ + if (!key) + return; + + DEBUGFS_DEL(keylen); + DEBUGFS_DEL(force_sw_encrypt); + DEBUGFS_DEL(keyidx); + DEBUGFS_DEL(hw_key_idx); + DEBUGFS_DEL(tx_rx_count); + DEBUGFS_DEL(algorithm); + DEBUGFS_DEL(tx_spec); + DEBUGFS_DEL(rx_spec); + DEBUGFS_DEL(replays); + DEBUGFS_DEL(key); + + debugfs_remove(key->debugfs.stalink); + key->debugfs.stalink = NULL; + debugfs_remove(key->debugfs.dir); + key->debugfs.dir = NULL; +} +void ieee80211_debugfs_key_add_default(struct ieee80211_sub_if_data *sdata) +{ + char buf[50]; + + if (!sdata->debugfsdir) + return; + + sprintf(buf, "../keys/%d", sdata->default_key->keyidx); + sdata->debugfs.default_key = + debugfs_create_symlink("default_key", sdata->debugfsdir, buf); +} +void ieee80211_debugfs_key_remove_default(struct ieee80211_sub_if_data *sdata) +{ + if (!sdata) + return; + + debugfs_remove(sdata->debugfs.default_key); + sdata->debugfs.default_key = NULL; +} +void ieee80211_debugfs_key_sta_link(struct ieee80211_key *key, + struct sta_info *sta) +{ + char buf[50]; + + if (!key->debugfs.dir) + return; + + sprintf(buf, "../sta/" MAC_FMT, MAC_ARG(sta->addr)); + key->debugfs.stalink = + debugfs_create_symlink("station", key->debugfs.dir, buf); +} + +void ieee80211_debugfs_key_sta_del(struct ieee80211_key *key, + struct sta_info *sta) +{ + debugfs_remove(key->debugfs.stalink); + key->debugfs.stalink = NULL; +} diff --git a/net/mac80211/debugfs_key.h b/net/mac80211/debugfs_key.h new file mode 100644 index 0000000..aecfce3 --- /dev/null +++ b/net/mac80211/debugfs_key.h @@ -0,0 +1,34 @@ +#ifndef __MAC80211_DEBUGFS_KEY_H +#define __MAC80211_DEBUGFS_KEY_H + +#ifdef CONFIG_MAC80211_DEBUGFS +void ieee80211_debugfs_key_add(struct ieee80211_local *local, + struct ieee80211_key *key); +void ieee80211_debugfs_key_remove(struct ieee80211_key *key); +void ieee80211_debugfs_key_add_default(struct ieee80211_sub_if_data *sdata); +void ieee80211_debugfs_key_remove_default(struct ieee80211_sub_if_data *sdata); +void ieee80211_debugfs_key_sta_link(struct ieee80211_key *key, + struct sta_info *sta); +void ieee80211_debugfs_key_sta_del(struct ieee80211_key *key, + struct sta_info *sta); +#else +static inline void ieee80211_debugfs_key_add(struct ieee80211_local *local, + struct ieee80211_key *key) +{} +static inline void ieee80211_debugfs_key_remove(struct ieee80211_key *key) +{} +static inline void ieee80211_debugfs_key_add_default( + struct ieee80211_sub_if_data *sdata) +{} +static inline void ieee80211_debugfs_key_remove_default( + struct ieee80211_sub_if_data *sdata) +{} +static inline void ieee80211_debugfs_key_sta_link( + struct ieee80211_key *key, struct sta_info *sta) +{} +static inline void ieee80211_debugfs_key_sta_del(struct ieee80211_key *key, + struct sta_info *sta) +{} +#endif + +#endif /* __MAC80211_DEBUGFS_KEY_H */ diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c new file mode 100644 index 0000000..9e39646 --- /dev/null +++ b/net/mac80211/debugfs_netdev.c @@ -0,0 +1,440 @@ +/* + * Copyright (c) 2006 Jiri Benc + * Copyright 2007 Johannes Berg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ieee80211_i.h" +#include "ieee80211_rate.h" +#include "debugfs.h" +#include "debugfs_netdev.h" + +static ssize_t ieee80211_if_read( + struct ieee80211_sub_if_data *sdata, + char __user *userbuf, + size_t count, loff_t *ppos, + ssize_t (*format)(const struct ieee80211_sub_if_data *, char *, int)) +{ + char buf[70]; + ssize_t ret = -EINVAL; + + read_lock(&dev_base_lock); + if (sdata->dev->reg_state == NETREG_REGISTERED) { + ret = (*format)(sdata, buf, sizeof(buf)); + ret = simple_read_from_buffer(userbuf, count, ppos, buf, ret); + } + read_unlock(&dev_base_lock); + return ret; +} + +#define IEEE80211_IF_FMT(name, field, format_string) \ +static ssize_t ieee80211_if_fmt_##name( \ + const struct ieee80211_sub_if_data *sdata, char *buf, \ + int buflen) \ +{ \ + return scnprintf(buf, buflen, format_string, sdata->field); \ +} +#define IEEE80211_IF_FMT_DEC(name, field) \ + IEEE80211_IF_FMT(name, field, "%d\n") +#define IEEE80211_IF_FMT_HEX(name, field) \ + IEEE80211_IF_FMT(name, field, "%#x\n") +#define IEEE80211_IF_FMT_SIZE(name, field) \ + IEEE80211_IF_FMT(name, field, "%zd\n") + +#define IEEE80211_IF_FMT_ATOMIC(name, field) \ +static ssize_t ieee80211_if_fmt_##name( \ + const struct ieee80211_sub_if_data *sdata, \ + char *buf, int buflen) \ +{ \ + return scnprintf(buf, buflen, "%d\n", atomic_read(&sdata->field));\ +} + +#define IEEE80211_IF_FMT_MAC(name, field) \ +static ssize_t ieee80211_if_fmt_##name( \ + const struct ieee80211_sub_if_data *sdata, char *buf, \ + int buflen) \ +{ \ + return scnprintf(buf, buflen, MAC_FMT "\n", MAC_ARG(sdata->field));\ +} + +#define __IEEE80211_IF_FILE(name) \ +static ssize_t ieee80211_if_read_##name(struct file *file, \ + char __user *userbuf, \ + size_t count, loff_t *ppos) \ +{ \ + return ieee80211_if_read(file->private_data, \ + userbuf, count, ppos, \ + ieee80211_if_fmt_##name); \ +} \ +static const struct file_operations name##_ops = { \ + .read = ieee80211_if_read_##name, \ + .open = mac80211_open_file_generic, \ +} + +#define IEEE80211_IF_FILE(name, field, format) \ + IEEE80211_IF_FMT_##format(name, field) \ + __IEEE80211_IF_FILE(name) + +/* common attributes */ +IEEE80211_IF_FILE(channel_use, channel_use, DEC); +IEEE80211_IF_FILE(drop_unencrypted, drop_unencrypted, DEC); +IEEE80211_IF_FILE(eapol, eapol, DEC); +IEEE80211_IF_FILE(ieee8021_x, ieee802_1x, DEC); + +/* STA/IBSS attributes */ +IEEE80211_IF_FILE(state, u.sta.state, DEC); +IEEE80211_IF_FILE(bssid, u.sta.bssid, MAC); +IEEE80211_IF_FILE(prev_bssid, u.sta.prev_bssid, MAC); +IEEE80211_IF_FILE(ssid_len, u.sta.ssid_len, SIZE); +IEEE80211_IF_FILE(aid, u.sta.aid, DEC); +IEEE80211_IF_FILE(ap_capab, u.sta.ap_capab, HEX); +IEEE80211_IF_FILE(capab, u.sta.capab, HEX); +IEEE80211_IF_FILE(extra_ie_len, u.sta.extra_ie_len, SIZE); +IEEE80211_IF_FILE(auth_tries, u.sta.auth_tries, DEC); +IEEE80211_IF_FILE(assoc_tries, u.sta.assoc_tries, DEC); +IEEE80211_IF_FILE(auth_algs, u.sta.auth_algs, HEX); +IEEE80211_IF_FILE(auth_alg, u.sta.auth_alg, DEC); +IEEE80211_IF_FILE(auth_transaction, u.sta.auth_transaction, DEC); + +static ssize_t ieee80211_if_fmt_flags( + const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) +{ + return scnprintf(buf, buflen, "%s%s%s%s%s%s%s\n", + sdata->u.sta.ssid_set ? "SSID\n" : "", + sdata->u.sta.bssid_set ? "BSSID\n" : "", + sdata->u.sta.prev_bssid_set ? "prev BSSID\n" : "", + sdata->u.sta.authenticated ? "AUTH\n" : "", + sdata->u.sta.associated ? "ASSOC\n" : "", + sdata->u.sta.probereq_poll ? "PROBEREQ POLL\n" : "", + sdata->u.sta.use_protection ? "CTS prot\n" : ""); +} +__IEEE80211_IF_FILE(flags); + +/* AP attributes */ +IEEE80211_IF_FILE(num_sta_ps, u.ap.num_sta_ps, ATOMIC); +IEEE80211_IF_FILE(dtim_period, u.ap.dtim_period, DEC); +IEEE80211_IF_FILE(dtim_count, u.ap.dtim_count, DEC); +IEEE80211_IF_FILE(num_beacons, u.ap.num_beacons, DEC); +IEEE80211_IF_FILE(force_unicast_rateidx, u.ap.force_unicast_rateidx, DEC); +IEEE80211_IF_FILE(max_ratectrl_rateidx, u.ap.max_ratectrl_rateidx, DEC); + +static ssize_t ieee80211_if_fmt_num_buffered_multicast( + const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) +{ + return scnprintf(buf, buflen, "%u\n", + skb_queue_len(&sdata->u.ap.ps_bc_buf)); +} +__IEEE80211_IF_FILE(num_buffered_multicast); + +static ssize_t ieee80211_if_fmt_beacon_head_len( + const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) +{ + if (sdata->u.ap.beacon_head) + return scnprintf(buf, buflen, "%d\n", + sdata->u.ap.beacon_head_len); + return scnprintf(buf, buflen, "\n"); +} +__IEEE80211_IF_FILE(beacon_head_len); + +static ssize_t ieee80211_if_fmt_beacon_tail_len( + const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) +{ + if (sdata->u.ap.beacon_tail) + return scnprintf(buf, buflen, "%d\n", + sdata->u.ap.beacon_tail_len); + return scnprintf(buf, buflen, "\n"); +} +__IEEE80211_IF_FILE(beacon_tail_len); + +/* WDS attributes */ +IEEE80211_IF_FILE(peer, u.wds.remote_addr, MAC); + +/* VLAN attributes */ +IEEE80211_IF_FILE(vlan_id, u.vlan.id, DEC); + +/* MONITOR attributes */ +static ssize_t ieee80211_if_fmt_mode( + const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) +{ + struct ieee80211_local *local = sdata->local; + + return scnprintf(buf, buflen, "%s\n", + ((local->hw.flags & IEEE80211_HW_MONITOR_DURING_OPER) || + local->open_count == local->monitors) ? + "hard" : "soft"); +} +__IEEE80211_IF_FILE(mode); + + +#define DEBUGFS_ADD(name, type)\ + sdata->debugfs.type.name = debugfs_create_file(#name, 0444,\ + sdata->debugfsdir, sdata, &name##_ops); + +static void add_sta_files(struct ieee80211_sub_if_data *sdata) +{ + DEBUGFS_ADD(channel_use, sta); + DEBUGFS_ADD(drop_unencrypted, sta); + DEBUGFS_ADD(eapol, sta); + DEBUGFS_ADD(ieee8021_x, sta); + DEBUGFS_ADD(state, sta); + DEBUGFS_ADD(bssid, sta); + DEBUGFS_ADD(prev_bssid, sta); + DEBUGFS_ADD(ssid_len, sta); + DEBUGFS_ADD(aid, sta); + DEBUGFS_ADD(ap_capab, sta); + DEBUGFS_ADD(capab, sta); + DEBUGFS_ADD(extra_ie_len, sta); + DEBUGFS_ADD(auth_tries, sta); + DEBUGFS_ADD(assoc_tries, sta); + DEBUGFS_ADD(auth_algs, sta); + DEBUGFS_ADD(auth_alg, sta); + DEBUGFS_ADD(auth_transaction, sta); + DEBUGFS_ADD(flags, sta); +} + +static void add_ap_files(struct ieee80211_sub_if_data *sdata) +{ + DEBUGFS_ADD(channel_use, ap); + DEBUGFS_ADD(drop_unencrypted, ap); + DEBUGFS_ADD(eapol, ap); + DEBUGFS_ADD(ieee8021_x, ap); + DEBUGFS_ADD(num_sta_ps, ap); + DEBUGFS_ADD(dtim_period, ap); + DEBUGFS_ADD(dtim_count, ap); + DEBUGFS_ADD(num_beacons, ap); + DEBUGFS_ADD(force_unicast_rateidx, ap); + DEBUGFS_ADD(max_ratectrl_rateidx, ap); + DEBUGFS_ADD(num_buffered_multicast, ap); + DEBUGFS_ADD(beacon_head_len, ap); + DEBUGFS_ADD(beacon_tail_len, ap); +} + +static void add_wds_files(struct ieee80211_sub_if_data *sdata) +{ + DEBUGFS_ADD(channel_use, wds); + DEBUGFS_ADD(drop_unencrypted, wds); + DEBUGFS_ADD(eapol, wds); + DEBUGFS_ADD(ieee8021_x, wds); + DEBUGFS_ADD(peer, wds); +} + +static void add_vlan_files(struct ieee80211_sub_if_data *sdata) +{ + DEBUGFS_ADD(channel_use, vlan); + DEBUGFS_ADD(drop_unencrypted, vlan); + DEBUGFS_ADD(eapol, vlan); + DEBUGFS_ADD(ieee8021_x, vlan); + DEBUGFS_ADD(vlan_id, vlan); +} + +static void add_monitor_files(struct ieee80211_sub_if_data *sdata) +{ + DEBUGFS_ADD(mode, monitor); +} + +static void add_files(struct ieee80211_sub_if_data *sdata) +{ + if (!sdata->debugfsdir) + return; + + switch (sdata->type) { + case IEEE80211_IF_TYPE_STA: + case IEEE80211_IF_TYPE_IBSS: + add_sta_files(sdata); + break; + case IEEE80211_IF_TYPE_AP: + add_ap_files(sdata); + break; + case IEEE80211_IF_TYPE_WDS: + add_wds_files(sdata); + break; + case IEEE80211_IF_TYPE_MNTR: + add_monitor_files(sdata); + break; + case IEEE80211_IF_TYPE_VLAN: + add_vlan_files(sdata); + break; + default: + break; + } +} + +#define DEBUGFS_DEL(name, type)\ + debugfs_remove(sdata->debugfs.type.name);\ + sdata->debugfs.type.name = NULL; + +static void del_sta_files(struct ieee80211_sub_if_data *sdata) +{ + DEBUGFS_DEL(channel_use, sta); + DEBUGFS_DEL(drop_unencrypted, sta); + DEBUGFS_DEL(eapol, sta); + DEBUGFS_DEL(ieee8021_x, sta); + DEBUGFS_DEL(state, sta); + DEBUGFS_DEL(bssid, sta); + DEBUGFS_DEL(prev_bssid, sta); + DEBUGFS_DEL(ssid_len, sta); + DEBUGFS_DEL(aid, sta); + DEBUGFS_DEL(ap_capab, sta); + DEBUGFS_DEL(capab, sta); + DEBUGFS_DEL(extra_ie_len, sta); + DEBUGFS_DEL(auth_tries, sta); + DEBUGFS_DEL(assoc_tries, sta); + DEBUGFS_DEL(auth_algs, sta); + DEBUGFS_DEL(auth_alg, sta); + DEBUGFS_DEL(auth_transaction, sta); + DEBUGFS_DEL(flags, sta); +} + +static void del_ap_files(struct ieee80211_sub_if_data *sdata) +{ + DEBUGFS_DEL(channel_use, ap); + DEBUGFS_DEL(drop_unencrypted, ap); + DEBUGFS_DEL(eapol, ap); + DEBUGFS_DEL(ieee8021_x, ap); + DEBUGFS_DEL(num_sta_ps, ap); + DEBUGFS_DEL(dtim_period, ap); + DEBUGFS_DEL(dtim_count, ap); + DEBUGFS_DEL(num_beacons, ap); + DEBUGFS_DEL(force_unicast_rateidx, ap); + DEBUGFS_DEL(max_ratectrl_rateidx, ap); + DEBUGFS_DEL(num_buffered_multicast, ap); + DEBUGFS_DEL(beacon_head_len, ap); + DEBUGFS_DEL(beacon_tail_len, ap); +} + +static void del_wds_files(struct ieee80211_sub_if_data *sdata) +{ + DEBUGFS_DEL(channel_use, wds); + DEBUGFS_DEL(drop_unencrypted, wds); + DEBUGFS_DEL(eapol, wds); + DEBUGFS_DEL(ieee8021_x, wds); + DEBUGFS_DEL(peer, wds); +} + +static void del_vlan_files(struct ieee80211_sub_if_data *sdata) +{ + DEBUGFS_DEL(channel_use, vlan); + DEBUGFS_DEL(drop_unencrypted, vlan); + DEBUGFS_DEL(eapol, vlan); + DEBUGFS_DEL(ieee8021_x, vlan); + DEBUGFS_DEL(vlan_id, vlan); +} + +static void del_monitor_files(struct ieee80211_sub_if_data *sdata) +{ + DEBUGFS_DEL(mode, monitor); +} + +static void del_files(struct ieee80211_sub_if_data *sdata, int type) +{ + if (!sdata->debugfsdir) + return; + + switch (type) { + case IEEE80211_IF_TYPE_STA: + case IEEE80211_IF_TYPE_IBSS: + del_sta_files(sdata); + break; + case IEEE80211_IF_TYPE_AP: + del_ap_files(sdata); + break; + case IEEE80211_IF_TYPE_WDS: + del_wds_files(sdata); + break; + case IEEE80211_IF_TYPE_MNTR: + del_monitor_files(sdata); + break; + case IEEE80211_IF_TYPE_VLAN: + del_vlan_files(sdata); + break; + default: + break; + } +} + +static int notif_registered; + +void ieee80211_debugfs_add_netdev(struct ieee80211_sub_if_data *sdata) +{ + char buf[10+IFNAMSIZ]; + + if (!notif_registered) + return; + + sprintf(buf, "netdev:%s", sdata->dev->name); + sdata->debugfsdir = debugfs_create_dir(buf, + sdata->local->hw.wiphy->debugfsdir); +} + +void ieee80211_debugfs_remove_netdev(struct ieee80211_sub_if_data *sdata) +{ + del_files(sdata, sdata->type); + debugfs_remove(sdata->debugfsdir); + sdata->debugfsdir = NULL; +} + +void ieee80211_debugfs_change_if_type(struct ieee80211_sub_if_data *sdata, + int oldtype) +{ + del_files(sdata, oldtype); + add_files(sdata); +} + +static int netdev_notify(struct notifier_block * nb, + unsigned long state, + void *ndev) +{ + struct net_device *dev = ndev; + char buf[10+IFNAMSIZ]; + + if (state != NETDEV_CHANGENAME) + return 0; + + if (!dev->ieee80211_ptr || !dev->ieee80211_ptr->wiphy) + return 0; + + if (dev->ieee80211_ptr->wiphy->privid != mac80211_wiphy_privid) + return 0; + + /* TODO + sprintf(buf, "netdev:%s", dev->name); + debugfs_rename(IEEE80211_DEV_TO_SUB_IF(dev)->debugfsdir, buf); + */ + + return 0; +} + +static struct notifier_block mac80211_debugfs_netdev_notifier = { + .notifier_call = netdev_notify, +}; + +void ieee80211_debugfs_netdev_init(void) +{ + int err; + + err = register_netdevice_notifier(&mac80211_debugfs_netdev_notifier); + if (err) { + printk(KERN_ERR + "mac80211: failed to install netdev notifier," + " disabling per-netdev debugfs!\n"); + } else + notif_registered = 1; +} + +void ieee80211_debugfs_netdev_exit(void) +{ + unregister_netdevice_notifier(&mac80211_debugfs_netdev_notifier); + notif_registered = 0; +} diff --git a/net/mac80211/debugfs_netdev.h b/net/mac80211/debugfs_netdev.h new file mode 100644 index 0000000..a690071 --- /dev/null +++ b/net/mac80211/debugfs_netdev.h @@ -0,0 +1,30 @@ +/* routines exported for debugfs handling */ + +#ifndef __IEEE80211_DEBUGFS_NETDEV_H +#define __IEEE80211_DEBUGFS_NETDEV_H + +#ifdef CONFIG_MAC80211_DEBUGFS +void ieee80211_debugfs_add_netdev(struct ieee80211_sub_if_data *sdata); +void ieee80211_debugfs_remove_netdev(struct ieee80211_sub_if_data *sdata); +void ieee80211_debugfs_change_if_type(struct ieee80211_sub_if_data *sdata, + int oldtype); +void ieee80211_debugfs_netdev_init(void); +void ieee80211_debugfs_netdev_exit(void); +#else +static inline void ieee80211_debugfs_add_netdev( + struct ieee80211_sub_if_data *sdata) +{} +static inline void ieee80211_debugfs_remove_netdev( + struct ieee80211_sub_if_data *sdata) +{} +static inline void ieee80211_debugfs_change_if_type( + struct ieee80211_sub_if_data *sdata, int oldtype) +{} +static inline void ieee80211_debugfs_netdev_init(void) +{} + +static inline void ieee80211_debugfs_netdev_exit(void) +{} +#endif + +#endif /* __IEEE80211_DEBUGFS_NETDEV_H */ diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c new file mode 100644 index 0000000..fc1a024 --- /dev/null +++ b/net/mac80211/debugfs_sta.c @@ -0,0 +1,247 @@ +/* + * Copyright 2003-2005 Devicescape Software, Inc. + * Copyright (c) 2006 Jiri Benc + * Copyright 2007 Johannes Berg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include "ieee80211_i.h" +#include "debugfs.h" +#include "debugfs_sta.h" +#include "sta_info.h" + +/* sta attributtes */ + +#define STA_READ(name, buflen, field, format_string) \ +static ssize_t sta_ ##name## _read(struct file *file, \ + char __user *userbuf, \ + size_t count, loff_t *ppos) \ +{ \ + int res; \ + struct sta_info *sta = file->private_data; \ + char buf[buflen]; \ + res = scnprintf(buf, buflen, format_string, sta->field); \ + return simple_read_from_buffer(userbuf, count, ppos, buf, res); \ +} +#define STA_READ_D(name, field) STA_READ(name, 20, field, "%d\n") +#define STA_READ_U(name, field) STA_READ(name, 20, field, "%u\n") +#define STA_READ_LU(name, field) STA_READ(name, 20, field, "%lu\n") +#define STA_READ_S(name, field) STA_READ(name, 20, field, "%s\n") + +#define STA_READ_RATE(name, field) \ +static ssize_t sta_##name##_read(struct file *file, \ + char __user *userbuf, \ + size_t count, loff_t *ppos) \ +{ \ + struct sta_info *sta = file->private_data; \ + struct ieee80211_local *local = wdev_priv(sta->dev->ieee80211_ptr);\ + struct ieee80211_hw_mode *mode = local->oper_hw_mode; \ + char buf[20]; \ + int res = scnprintf(buf, sizeof(buf), "%d\n", \ + (sta->field >= 0 && \ + sta->field < mode->num_rates) ? \ + mode->rates[sta->field].rate : -1); \ + return simple_read_from_buffer(userbuf, count, ppos, buf, res); \ +} + +#define STA_OPS(name) \ +static const struct file_operations sta_ ##name## _ops = { \ + .read = sta_##name##_read, \ + .open = mac80211_open_file_generic, \ +} + +#define STA_FILE(name, field, format) \ + STA_READ_##format(name, field) \ + STA_OPS(name) + +STA_FILE(aid, aid, D); +STA_FILE(key_idx_compression, key_idx_compression, D); +STA_FILE(dev, dev->name, S); +STA_FILE(vlan_id, vlan_id, D); +STA_FILE(rx_packets, rx_packets, LU); +STA_FILE(tx_packets, tx_packets, LU); +STA_FILE(rx_bytes, rx_bytes, LU); +STA_FILE(tx_bytes, tx_bytes, LU); +STA_FILE(rx_duplicates, num_duplicates, LU); +STA_FILE(rx_fragments, rx_fragments, LU); +STA_FILE(rx_dropped, rx_dropped, LU); +STA_FILE(tx_fragments, tx_fragments, LU); +STA_FILE(tx_filtered, tx_filtered_count, LU); +STA_FILE(txrate, txrate, RATE); +STA_FILE(last_txrate, last_txrate, RATE); +STA_FILE(tx_retry_failed, tx_retry_failed, LU); +STA_FILE(tx_retry_count, tx_retry_count, LU); +STA_FILE(last_rssi, last_rssi, D); +STA_FILE(last_signal, last_signal, D); +STA_FILE(last_noise, last_noise, D); +STA_FILE(channel_use, channel_use, D); +STA_FILE(wep_weak_iv_count, wep_weak_iv_count, D); + +static ssize_t sta_flags_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + char buf[100]; + struct sta_info *sta = file->private_data; + int res = scnprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s%s%s%s", + sta->flags & WLAN_STA_AUTH ? "AUTH\n" : "", + sta->flags & WLAN_STA_ASSOC ? "ASSOC\n" : "", + sta->flags & WLAN_STA_PS ? "PS\n" : "", + sta->flags & WLAN_STA_TIM ? "TIM\n" : "", + sta->flags & WLAN_STA_PERM ? "PERM\n" : "", + sta->flags & WLAN_STA_AUTHORIZED ? "AUTHORIZED\n" : "", + sta->flags & WLAN_STA_SHORT_PREAMBLE ? "SHORT PREAMBLE\n" : "", + sta->flags & WLAN_STA_WME ? "WME\n" : "", + sta->flags & WLAN_STA_HT ? "HT\n" : "", + sta->flags & WLAN_STA_WDS ? "WDS\n" : ""); + return simple_read_from_buffer(userbuf, count, ppos, buf, res); +} +STA_OPS(flags); + +static ssize_t sta_num_ps_buf_frames_read(struct file *file, + char __user *userbuf, + size_t count, loff_t *ppos) +{ + char buf[20]; + struct sta_info *sta = file->private_data; + int res = scnprintf(buf, sizeof(buf), "%u\n", + skb_queue_len(&sta->ps_tx_buf)); + return simple_read_from_buffer(userbuf, count, ppos, buf, res); +} +STA_OPS(num_ps_buf_frames); + +static ssize_t sta_last_ack_rssi_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + char buf[100]; + struct sta_info *sta = file->private_data; + int res = scnprintf(buf, sizeof(buf), "%d %d %d\n", + sta->last_ack_rssi[0], + sta->last_ack_rssi[1], + sta->last_ack_rssi[2]); + return simple_read_from_buffer(userbuf, count, ppos, buf, res); +} +STA_OPS(last_ack_rssi); + +static ssize_t sta_last_ack_ms_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + char buf[20]; + struct sta_info *sta = file->private_data; + int res = scnprintf(buf, sizeof(buf), "%d\n", + sta->last_ack ? + jiffies_to_msecs(jiffies - sta->last_ack) : -1); + return simple_read_from_buffer(userbuf, count, ppos, buf, res); +} +STA_OPS(last_ack_ms); + +static ssize_t sta_inactive_ms_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + char buf[20]; + struct sta_info *sta = file->private_data; + int res = scnprintf(buf, sizeof(buf), "%d\n", + jiffies_to_msecs(jiffies - sta->last_rx)); + return simple_read_from_buffer(userbuf, count, ppos, buf, res); +} +STA_OPS(inactive_ms); + +static ssize_t sta_last_seq_ctrl_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + char buf[15*NUM_RX_DATA_QUEUES], *p = buf; + int i; + struct sta_info *sta = file->private_data; + for (i = 0; i < NUM_RX_DATA_QUEUES; i++) + p += scnprintf(p, sizeof(buf)+buf-p, "%x ", + sta->last_seq_ctrl[i]); + p += scnprintf(p, sizeof(buf)+buf-p, "\n"); + return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf); +} +STA_OPS(last_seq_ctrl); + +#ifdef CONFIG_MAC80211_DEBUG_COUNTERS +static ssize_t sta_wme_rx_queue_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + char buf[15*NUM_RX_DATA_QUEUES], *p = buf; + int i; + struct sta_info *sta = file->private_data; + for (i = 0; i < NUM_RX_DATA_QUEUES; i++) + p += scnprintf(p, sizeof(buf)+buf-p, "%u ", + sta->wme_rx_queue[i]); + p += scnprintf(p, sizeof(buf)+buf-p, "\n"); + return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf); +} +STA_OPS(wme_rx_queue); + +static ssize_t sta_wme_tx_queue_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + char buf[15*NUM_TX_DATA_QUEUES], *p = buf; + int i; + struct sta_info *sta = file->private_data; + for (i = 0; i < NUM_TX_DATA_QUEUES; i++) + p += scnprintf(p, sizeof(buf)+buf-p, "%u ", + sta->wme_tx_queue[i]); + p += scnprintf(p, sizeof(buf)+buf-p, "\n"); + return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf); +} +STA_OPS(wme_tx_queue); +#endif + +#define DEBUGFS_ADD(name) \ + sta->debugfs.name = debugfs_create_file(#name, 0444, \ + sta->debugfs.dir, sta, &sta_ ##name## _ops); + +#define DEBUGFS_DEL(name) \ + debugfs_remove(sta->debugfs.name);\ + sta->debugfs.name = NULL; + + +void ieee80211_sta_debugfs_add(struct sta_info *sta) +{ + char buf[3*6]; + struct dentry *stations_dir = sta->local->debugfs.stations; + + if (!stations_dir) + return; + + sprintf(buf, MAC_FMT, MAC_ARG(sta->addr)); + + sta->debugfs.dir = debugfs_create_dir(buf, stations_dir); + if (!sta->debugfs.dir) + return; + + DEBUGFS_ADD(flags); + DEBUGFS_ADD(num_ps_buf_frames); + DEBUGFS_ADD(last_ack_rssi); + DEBUGFS_ADD(last_ack_ms); + DEBUGFS_ADD(inactive_ms); + DEBUGFS_ADD(last_seq_ctrl); +#ifdef CONFIG_MAC80211_DEBUG_COUNTERS + DEBUGFS_ADD(wme_rx_queue); + DEBUGFS_ADD(wme_tx_queue); +#endif +} + +void ieee80211_sta_debugfs_remove(struct sta_info *sta) +{ + DEBUGFS_DEL(flags); + DEBUGFS_DEL(num_ps_buf_frames); + DEBUGFS_DEL(last_ack_rssi); + DEBUGFS_DEL(last_ack_ms); + DEBUGFS_DEL(inactive_ms); + DEBUGFS_DEL(last_seq_ctrl); +#ifdef CONFIG_MAC80211_DEBUG_COUNTERS + DEBUGFS_DEL(wme_rx_queue); + DEBUGFS_DEL(wme_tx_queue); +#endif + + debugfs_remove(sta->debugfs.dir); + sta->debugfs.dir = NULL; +} diff --git a/net/mac80211/debugfs_sta.h b/net/mac80211/debugfs_sta.h new file mode 100644 index 0000000..574a1cd --- /dev/null +++ b/net/mac80211/debugfs_sta.h @@ -0,0 +1,12 @@ +#ifndef __MAC80211_DEBUGFS_STA_H +#define __MAC80211_DEBUGFS_STA_H + +#ifdef CONFIG_MAC80211_DEBUGFS +void ieee80211_sta_debugfs_add(struct sta_info *sta); +void ieee80211_sta_debugfs_remove(struct sta_info *sta); +#else +static inline void ieee80211_sta_debugfs_add(struct sta_info *sta) {} +static inline void ieee80211_sta_debugfs_remove(struct sta_info *sta) {} +#endif + +#endif /* __MAC80211_DEBUGFS_STA_H */ diff --git a/net/mac80211/hostapd_ioctl.h b/net/mac80211/hostapd_ioctl.h new file mode 100644 index 0000000..e35233c --- /dev/null +++ b/net/mac80211/hostapd_ioctl.h @@ -0,0 +1,344 @@ +/* + * Host AP (software wireless LAN access point) user space daemon for + * Host AP kernel driver + * Copyright 2002-2003, Jouni Malinen + * Copyright 2002-2004, Instant802 Networks, Inc. + * Copyright 2005, Devicescape Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef HOSTAPD_IOCTL_H +#define HOSTAPD_IOCTL_H + +#ifdef __KERNEL__ +#include +#endif /* __KERNEL__ */ + +#define PRISM2_IOCTL_PRISM2_PARAM (SIOCIWFIRSTPRIV + 0) +#define PRISM2_IOCTL_GET_PRISM2_PARAM (SIOCIWFIRSTPRIV + 1) +#define PRISM2_IOCTL_HOSTAPD (SIOCIWFIRSTPRIV + 3) + +/* PRISM2_IOCTL_PRISM2_PARAM ioctl() subtypes: + * This table is no longer added to, the whole sub-ioctl + * mess shall be deleted completely. */ +enum { + PRISM2_PARAM_BEACON_INT = 3, + PRISM2_PARAM_AP_BRIDGE_PACKETS = 10, + PRISM2_PARAM_DTIM_PERIOD = 11, + PRISM2_PARAM_AP_AUTH_ALGS = 15, + PRISM2_PARAM_HOST_ENCRYPT = 17, + PRISM2_PARAM_HOST_DECRYPT = 18, + PRISM2_PARAM_IEEE_802_1X = 23, + PRISM2_PARAM_ANTSEL_TX = 24, + PRISM2_PARAM_ANTSEL_RX = 25, + + /* Instant802 additions */ + PRISM2_PARAM_CTS_PROTECT_ERP_FRAMES = 1001, + PRISM2_PARAM_DROP_UNENCRYPTED = 1002, + PRISM2_PARAM_PREAMBLE = 1003, + PRISM2_PARAM_SHORT_SLOT_TIME = 1006, + PRISM2_PARAM_NEXT_MODE = 1008, + PRISM2_PARAM_CLEAR_KEYS = 1009, + PRISM2_PARAM_RADIO_ENABLED = 1010, + PRISM2_PARAM_ANTENNA_MODE = 1013, + PRISM2_PARAM_PRIVACY_INVOKED = 1014, + PRISM2_PARAM_BROADCAST_SSID = 1015, + PRISM2_PARAM_STAT_TIME = 1016, + PRISM2_PARAM_STA_ANTENNA_SEL = 1017, + PRISM2_PARAM_FORCE_UNICAST_RATE = 1018, + PRISM2_PARAM_RATE_CTRL_NUM_UP = 1019, + PRISM2_PARAM_RATE_CTRL_NUM_DOWN = 1020, + PRISM2_PARAM_MAX_RATECTRL_RATE = 1021, + PRISM2_PARAM_TX_POWER_REDUCTION = 1022, + PRISM2_PARAM_EAPOL = 1023, + PRISM2_PARAM_KEY_TX_RX_THRESHOLD = 1024, + PRISM2_PARAM_KEY_INDEX = 1025, + PRISM2_PARAM_DEFAULT_WEP_ONLY = 1026, + PRISM2_PARAM_WIFI_WME_NOACK_TEST = 1033, + PRISM2_PARAM_ALLOW_BROADCAST_ALWAYS = 1034, + PRISM2_PARAM_SCAN_FLAGS = 1035, + PRISM2_PARAM_HW_MODES = 1036, + PRISM2_PARAM_CREATE_IBSS = 1037, + PRISM2_PARAM_WMM_ENABLED = 1038, + PRISM2_PARAM_MIXED_CELL = 1039, + PRISM2_PARAM_KEY_MGMT = 1040, + PRISM2_PARAM_RADAR_DETECT = 1043, + PRISM2_PARAM_SPECTRUM_MGMT = 1044, + PRISM2_PARAM_USER_SPACE_MLME = 1045, + PRISM2_PARAM_MGMT_IF = 1046, +}; + +/* PRISM2_IOCTL_HOSTAPD ioctl() cmd: + * This table is no longer added to, the hostapd ioctl + * shall be deleted completely. */ +enum { + PRISM2_HOSTAPD_FLUSH = 1, + PRISM2_HOSTAPD_ADD_STA = 2, + PRISM2_HOSTAPD_REMOVE_STA = 3, + PRISM2_HOSTAPD_GET_INFO_STA = 4, + PRISM2_SET_ENCRYPTION = 6, + PRISM2_GET_ENCRYPTION = 7, + PRISM2_HOSTAPD_SET_FLAGS_STA = 8, + PRISM2_HOSTAPD_MLME = 13, + + /* Instant802 additions */ + PRISM2_HOSTAPD_SET_BEACON = 1001, + PRISM2_HOSTAPD_GET_HW_FEATURES = 1002, + PRISM2_HOSTAPD_WPA_TRIGGER = 1004, + PRISM2_HOSTAPD_SET_RATE_SETS = 1005, + PRISM2_HOSTAPD_ADD_IF = 1006, + PRISM2_HOSTAPD_REMOVE_IF = 1007, + PRISM2_HOSTAPD_GET_DOT11COUNTERSTABLE = 1008, + PRISM2_HOSTAPD_GET_LOAD_STATS = 1009, + PRISM2_HOSTAPD_SET_STA_VLAN = 1010, + PRISM2_HOSTAPD_SET_GENERIC_INFO_ELEM = 1011, + PRISM2_HOSTAPD_SET_CHANNEL_FLAG = 1012, + PRISM2_HOSTAPD_SET_REGULATORY_DOMAIN = 1013, + PRISM2_HOSTAPD_SET_TX_QUEUE_PARAMS = 1014, + PRISM2_HOSTAPD_GET_TX_STATS = 1016, + PRISM2_HOSTAPD_UPDATE_IF = 1017, + PRISM2_HOSTAPD_SCAN_REQ = 1019, + PRISM2_STA_GET_STATE = 1020, + PRISM2_HOSTAPD_FLUSH_IFS = 1021, + PRISM2_HOSTAPD_SET_RADAR_PARAMS = 1023, + PRISM2_HOSTAPD_SET_QUIET_PARAMS = 1024, +}; + +#define PRISM2_HOSTAPD_MAX_BUF_SIZE 2048 +#define HOSTAP_CRYPT_ALG_NAME_LEN 16 + +#ifndef ALIGNED +#define ALIGNED __attribute__ ((aligned)) +#endif + +struct prism2_hostapd_param { + u32 cmd; + u8 sta_addr[ETH_ALEN]; + u8 pad[2]; + union { + struct { + u16 aid; + u16 capability; + u8 supp_rates[32]; + u8 wds_flags; +#define IEEE80211_STA_DYNAMIC_ENC BIT(0) + u8 enc_flags; + u16 listen_interval; + } add_sta; + struct { + u32 inactive_msec; + u32 rx_packets; + u32 tx_packets; + u32 rx_bytes; + u32 tx_bytes; + u32 current_tx_rate; /* in 100 kbps */ + u32 channel_use; + u32 flags; + u32 num_ps_buf_frames; + u32 tx_retry_failed; + u32 tx_retry_count; + u32 last_rssi; + u32 last_ack_rssi; + } get_info_sta; + struct { + char alg[HOSTAP_CRYPT_ALG_NAME_LEN]; + u32 flags; + u32 err; + u8 idx; +#define HOSTAP_SEQ_COUNTER_SIZE 8 + u8 seq_counter[HOSTAP_SEQ_COUNTER_SIZE]; + u16 key_len; + u8 key[0] ALIGNED; + } crypt; + struct { + u32 flags_and; + u32 flags_or; + } set_flags_sta; + struct { + u16 head_len; + u16 tail_len; + u8 data[0] ALIGNED; /* head_len + tail_len bytes */ + } beacon; + struct { + u16 num_modes; + u16 flags; + u8 data[0] ALIGNED; /* num_modes * feature data */ + } hw_features; + struct { + u8 now; + s8 our_mode_only; + s16 last_rx; + u16 channel; + s16 interval; /* seconds */ + s32 listen; /* microseconds */ + } scan; + struct { +#define WPA_TRIGGER_FAIL_TX_MIC BIT(0) +#define WPA_TRIGGER_FAIL_TX_ICV BIT(1) +#define WPA_TRIGGER_FAIL_RX_MIC BIT(2) +#define WPA_TRIGGER_FAIL_RX_ICV BIT(3) +#define WPA_TRIGGER_TX_REPLAY BIT(4) +#define WPA_TRIGGER_TX_REPLAY_FRAG BIT(5) +#define WPA_TRIGGER_TX_SKIP_SEQ BIT(6) + u32 trigger; + } wpa_trigger; + struct { + u16 mode; /* MODE_* */ + u16 num_supported_rates; + u16 num_basic_rates; + u8 data[0] ALIGNED; /* num_supported_rates * u16 + + * num_basic_rates * u16 */ + } set_rate_sets; + struct { + u8 type; /* WDS, VLAN, etc */ + u8 name[IFNAMSIZ]; + u8 data[0] ALIGNED; + } if_info; + struct dot11_counters { + u32 dot11TransmittedFragmentCount; + u32 dot11MulticastTransmittedFrameCount; + u32 dot11FailedCount; + u32 dot11ReceivedFragmentCount; + u32 dot11MulticastReceivedFrameCount; + u32 dot11FCSErrorCount; + u32 dot11TransmittedFrameCount; + u32 dot11WEPUndecryptableCount; + u32 dot11ACKFailureCount; + u32 dot11RTSFailureCount; + u32 dot11RTSSuccessCount; + } dot11CountersTable; + struct { +#define LOAD_STATS_CLEAR BIT(1) + u32 flags; + u32 channel_use; + } get_load_stats; + struct { + char vlan_name[IFNAMSIZ]; + int vlan_id; + } set_sta_vlan; + struct { + u8 len; + u8 data[0] ALIGNED; + } set_generic_info_elem; + struct { + u16 mode; /* MODE_* */ + u16 chan; + u32 flag; + u8 power_level; /* regulatory limit in dBm */ + u8 antenna_max; + } set_channel_flag; + struct { + u32 rd; + } set_regulatory_domain; + struct { + u32 queue; + s32 aifs; + u32 cw_min; + u32 cw_max; + u32 burst_time; /* maximum burst time in 0.1 ms, i.e., + * 10 = 1 ms */ + } tx_queue_params; + struct ieee80211_tx_stats { + struct { + unsigned int len; /* num packets in queue */ + unsigned int limit; /* queue len (soft) limit + */ + unsigned int count; /* total num frames sent */ + } data[4]; + } get_tx_stats; + struct { + u8 ssid_len; + u8 ssid[0] ALIGNED; + } scan_req; + struct { + u32 state; + } sta_get_state; + struct { +#define MLME_STA_DEAUTH 0 +#define MLME_STA_DISASSOC 1 + u16 cmd; + u16 reason_code; + } mlme; + struct { + u8 radar_firpwr_threshold; + u8 radar_rssi_threshold; + u8 pulse_height_threshold; + u8 pulse_rssi_threshold; + u8 pulse_inband_threshold; + } radar; + struct { + unsigned int period; + unsigned int offset; + unsigned int duration; + } quiet; + struct { + u8 dummy[80]; /* Make sizeof() this struct large enough + * with some compiler versions. */ + } dummy; + } u; +}; + +#define HOSTAP_CRYPT_FLAG_SET_TX_KEY BIT(0) +#define HOSTAP_CRYPT_FLAG_PERMANENT BIT(1) + +#define HOSTAP_CRYPT_ERR_UNKNOWN_ALG 2 +#define HOSTAP_CRYPT_ERR_UNKNOWN_ADDR 3 +#define HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED 4 +#define HOSTAP_CRYPT_ERR_KEY_SET_FAILED 5 +#define HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED 6 +#define HOSTAP_CRYPT_ERR_CARD_CONF_FAILED 7 + +#define HOSTAP_HW_FLAG_NULLFUNC_OK BIT(0) + +enum { + IEEE80211_KEY_MGMT_NONE = 0, + IEEE80211_KEY_MGMT_IEEE8021X = 1, + IEEE80211_KEY_MGMT_WPA_PSK = 2, + IEEE80211_KEY_MGMT_WPA_EAP = 3, +}; + + +/* Data structures used for get_hw_features ioctl */ +struct hostapd_ioctl_hw_modes_hdr { + int mode; + int num_channels; + int num_rates; +}; + +struct ieee80211_channel_data { + short chan; /* channel number (IEEE 802.11) */ + short freq; /* frequency in MHz */ + int flag; /* flag for hostapd use (IEEE80211_CHAN_*) */ +}; + +struct ieee80211_rate_data { + int rate; /* rate in 100 kbps */ + int flags; /* IEEE80211_RATE_ flags */ +}; + + +/* ADD_IF, REMOVE_IF, and UPDATE_IF 'type' argument */ +enum { + HOSTAP_IF_WDS = 1, HOSTAP_IF_VLAN = 2, HOSTAP_IF_BSS = 3, + HOSTAP_IF_STA = 4 +}; + +struct hostapd_if_wds { + u8 remote_addr[ETH_ALEN]; +}; + +struct hostapd_if_vlan { + u8 id; +}; + +struct hostapd_if_bss { + u8 bssid[ETH_ALEN]; +}; + +struct hostapd_if_sta { +}; + +#endif /* HOSTAPD_IOCTL_H */ diff --git a/net/mac80211/ieee80211.c b/net/mac80211/ieee80211.c new file mode 100644 index 0000000..3ec386b --- /dev/null +++ b/net/mac80211/ieee80211.c @@ -0,0 +1,5108 @@ +/* + * Copyright 2002-2005, Instant802 Networks, Inc. + * Copyright 2005-2006, Devicescape Software, Inc. + * Copyright 2006-2007 Jiri Benc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ieee80211_common.h" +#include "ieee80211_i.h" +#include "ieee80211_rate.h" +#include "wep.h" +#include "wpa.h" +#include "tkip.h" +#include "wme.h" +#include "aes_ccm.h" +#include "ieee80211_led.h" +#include "ieee80211_cfg.h" +#include "debugfs.h" +#include "debugfs_netdev.h" +#include "debugfs_key.h" + +/* privid for wiphys to determine whether they belong to us or not */ +void *mac80211_wiphy_privid = &mac80211_wiphy_privid; + +/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */ +/* Ethernet-II snap header (RFC1042 for most EtherTypes) */ +static const unsigned char rfc1042_header[] = + { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; + +/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */ +static const unsigned char bridge_tunnel_header[] = + { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 }; + +/* No encapsulation header if EtherType < 0x600 (=length) */ +static const unsigned char eapol_header[] = + { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00, 0x88, 0x8e }; + + +static inline void ieee80211_include_sequence(struct ieee80211_sub_if_data *sdata, + struct ieee80211_hdr *hdr) +{ + /* Set the sequence number for this frame. */ + hdr->seq_ctrl = cpu_to_le16(sdata->sequence); + + /* Increase the sequence number. */ + sdata->sequence = (sdata->sequence + 0x10) & IEEE80211_SCTL_SEQ; +} + +struct ieee80211_key_conf * +ieee80211_key_data2conf(struct ieee80211_local *local, + const struct ieee80211_key *data) +{ + struct ieee80211_key_conf *conf; + + conf = kmalloc(sizeof(*conf) + data->keylen, GFP_ATOMIC); + if (!conf) + return NULL; + + conf->hw_key_idx = data->hw_key_idx; + conf->alg = data->alg; + conf->keylen = data->keylen; + conf->flags = 0; + if (data->force_sw_encrypt) + conf->flags |= IEEE80211_KEY_FORCE_SW_ENCRYPT; + conf->keyidx = data->keyidx; + if (data->default_tx_key) + conf->flags |= IEEE80211_KEY_DEFAULT_TX_KEY; + if (local->default_wep_only) + conf->flags |= IEEE80211_KEY_DEFAULT_WEP_ONLY; + memcpy(conf->key, data->key, data->keylen); + + return conf; +} + +struct ieee80211_key *ieee80211_key_alloc(struct ieee80211_sub_if_data *sdata, + int idx, size_t key_len, gfp_t flags) +{ + struct ieee80211_key *key; + + key = kzalloc(sizeof(struct ieee80211_key) + key_len, flags); + if (!key) + return NULL; + kref_init(&key->kref); + return key; +} + +static void ieee80211_key_release(struct kref *kref) +{ + struct ieee80211_key *key; + + key = container_of(kref, struct ieee80211_key, kref); + if (key->alg == ALG_CCMP) + ieee80211_aes_key_free(key->u.ccmp.tfm); + ieee80211_debugfs_key_remove(key); + kfree(key); +} + +void ieee80211_key_free(struct ieee80211_key *key) +{ + if (key) + kref_put(&key->kref, ieee80211_key_release); +} + +static int rate_list_match(const int *rate_list, int rate) +{ + int i; + + if (!rate_list) + return 0; + + for (i = 0; rate_list[i] >= 0; i++) + if (rate_list[i] == rate) + return 1; + + return 0; +} + + +void ieee80211_prepare_rates(struct ieee80211_local *local, + struct ieee80211_hw_mode *mode) +{ + int i; + + for (i = 0; i < mode->num_rates; i++) { + struct ieee80211_rate *rate = &mode->rates[i]; + + rate->flags &= ~(IEEE80211_RATE_SUPPORTED | + IEEE80211_RATE_BASIC); + + if (local->supp_rates[mode->mode]) { + if (!rate_list_match(local->supp_rates[mode->mode], + rate->rate)) + continue; + } + + rate->flags |= IEEE80211_RATE_SUPPORTED; + + /* Use configured basic rate set if it is available. If not, + * use defaults that are sane for most cases. */ + if (local->basic_rates[mode->mode]) { + if (rate_list_match(local->basic_rates[mode->mode], + rate->rate)) + rate->flags |= IEEE80211_RATE_BASIC; + } else switch (mode->mode) { + case MODE_IEEE80211A: + if (rate->rate == 60 || rate->rate == 120 || + rate->rate == 240) + rate->flags |= IEEE80211_RATE_BASIC; + break; + case MODE_IEEE80211B: + if (rate->rate == 10 || rate->rate == 20) + rate->flags |= IEEE80211_RATE_BASIC; + break; + case MODE_ATHEROS_TURBO: + if (rate->rate == 120 || rate->rate == 240 || + rate->rate == 480) + rate->flags |= IEEE80211_RATE_BASIC; + break; + case MODE_IEEE80211G: + if (rate->rate == 10 || rate->rate == 20 || + rate->rate == 55 || rate->rate == 110) + rate->flags |= IEEE80211_RATE_BASIC; + break; + } + + /* Set ERP and MANDATORY flags based on phymode */ + switch (mode->mode) { + case MODE_IEEE80211A: + if (rate->rate == 60 || rate->rate == 120 || + rate->rate == 240) + rate->flags |= IEEE80211_RATE_MANDATORY; + break; + case MODE_IEEE80211B: + if (rate->rate == 10) + rate->flags |= IEEE80211_RATE_MANDATORY; + break; + case MODE_ATHEROS_TURBO: + break; + case MODE_IEEE80211G: + if (rate->rate == 10 || rate->rate == 20 || + rate->rate == 55 || rate->rate == 110 || + rate->rate == 60 || rate->rate == 120 || + rate->rate == 240) + rate->flags |= IEEE80211_RATE_MANDATORY; + break; + } + if (ieee80211_is_erp_rate(mode->mode, rate->rate)) + rate->flags |= IEEE80211_RATE_ERP; + } +} + + +static void ieee80211_key_threshold_notify(struct net_device *dev, + struct ieee80211_key *key, + struct sta_info *sta) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct sk_buff *skb; + struct ieee80211_msg_key_notification *msg; + + /* if no one will get it anyway, don't even allocate it. + * unlikely because this is only relevant for APs + * where the device must be open... */ + if (unlikely(!local->apdev)) + return; + + skb = dev_alloc_skb(sizeof(struct ieee80211_frame_info) + + sizeof(struct ieee80211_msg_key_notification)); + if (!skb) + return; + + skb_reserve(skb, sizeof(struct ieee80211_frame_info)); + msg = (struct ieee80211_msg_key_notification *) + skb_put(skb, sizeof(struct ieee80211_msg_key_notification)); + msg->tx_rx_count = key->tx_rx_count; + memcpy(msg->ifname, dev->name, IFNAMSIZ); + if (sta) + memcpy(msg->addr, sta->addr, ETH_ALEN); + else + memset(msg->addr, 0xff, ETH_ALEN); + + key->tx_rx_count = 0; + + ieee80211_rx_mgmt(local, skb, NULL, + ieee80211_msg_key_threshold_notification); +} + + +static u8 * ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len) +{ + u16 fc; + + if (len < 24) + return NULL; + + fc = le16_to_cpu(hdr->frame_control); + + switch (fc & IEEE80211_FCTL_FTYPE) { + case IEEE80211_FTYPE_DATA: + switch (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) { + case IEEE80211_FCTL_TODS: + return hdr->addr1; + case (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS): + return NULL; + case IEEE80211_FCTL_FROMDS: + return hdr->addr2; + case 0: + return hdr->addr3; + } + break; + case IEEE80211_FTYPE_MGMT: + return hdr->addr3; + case IEEE80211_FTYPE_CTL: + if ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PSPOLL) + return hdr->addr1; + else + return NULL; + } + + return NULL; +} + +int ieee80211_get_hdrlen(u16 fc) +{ + int hdrlen = 24; + + switch (fc & IEEE80211_FCTL_FTYPE) { + case IEEE80211_FTYPE_DATA: + if ((fc & IEEE80211_FCTL_FROMDS) && (fc & IEEE80211_FCTL_TODS)) + hdrlen = 30; /* Addr4 */ + /* + * The QoS Control field is two bytes and its presence is + * indicated by the IEEE80211_STYPE_QOS_DATA bit. Add 2 to + * hdrlen if that bit is set. + * This works by masking out the bit and shifting it to + * bit position 1 so the result has the value 0 or 2. + */ + hdrlen += (fc & IEEE80211_STYPE_QOS_DATA) + >> (ilog2(IEEE80211_STYPE_QOS_DATA)-1); + break; + case IEEE80211_FTYPE_CTL: + /* + * ACK and CTS are 10 bytes, all others 16. To see how + * to get this condition consider + * subtype mask: 0b0000000011110000 (0x00F0) + * ACK subtype: 0b0000000011010000 (0x00D0) + * CTS subtype: 0b0000000011000000 (0x00C0) + * bits that matter: ^^^ (0x00E0) + * value of those: 0b0000000011000000 (0x00C0) + */ + if ((fc & 0xE0) == 0xC0) + hdrlen = 10; + else + hdrlen = 16; + break; + } + + return hdrlen; +} +EXPORT_SYMBOL(ieee80211_get_hdrlen); + +int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb) +{ + const struct ieee80211_hdr *hdr = (const struct ieee80211_hdr *) skb->data; + int hdrlen; + + if (unlikely(skb->len < 10)) + return 0; + hdrlen = ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_control)); + if (unlikely(hdrlen > skb->len)) + return 0; + return hdrlen; +} +EXPORT_SYMBOL(ieee80211_get_hdrlen_from_skb); + +static int ieee80211_get_radiotap_len(struct sk_buff *skb) +{ + struct ieee80211_radiotap_header *hdr = + (struct ieee80211_radiotap_header *) skb->data; + + return le16_to_cpu(hdr->it_len); +} + +#ifdef CONFIG_MAC80211_LOWTX_FRAME_DUMP +static void ieee80211_dump_frame(const char *ifname, const char *title, + const struct sk_buff *skb) +{ + const struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + u16 fc; + int hdrlen; + + printk(KERN_DEBUG "%s: %s (len=%d)", ifname, title, skb->len); + if (skb->len < 4) { + printk("\n"); + return; + } + + fc = le16_to_cpu(hdr->frame_control); + hdrlen = ieee80211_get_hdrlen(fc); + if (hdrlen > skb->len) + hdrlen = skb->len; + if (hdrlen >= 4) + printk(" FC=0x%04x DUR=0x%04x", + fc, le16_to_cpu(hdr->duration_id)); + if (hdrlen >= 10) + printk(" A1=" MAC_FMT, MAC_ARG(hdr->addr1)); + if (hdrlen >= 16) + printk(" A2=" MAC_FMT, MAC_ARG(hdr->addr2)); + if (hdrlen >= 24) + printk(" A3=" MAC_FMT, MAC_ARG(hdr->addr3)); + if (hdrlen >= 30) + printk(" A4=" MAC_FMT, MAC_ARG(hdr->addr4)); + printk("\n"); +} +#else /* CONFIG_MAC80211_LOWTX_FRAME_DUMP */ +static inline void ieee80211_dump_frame(const char *ifname, const char *title, + struct sk_buff *skb) +{ +} +#endif /* CONFIG_MAC80211_LOWTX_FRAME_DUMP */ + + +static int ieee80211_is_eapol(const struct sk_buff *skb) +{ + const struct ieee80211_hdr *hdr; + u16 fc; + int hdrlen; + + if (unlikely(skb->len < 10)) + return 0; + + hdr = (const struct ieee80211_hdr *) skb->data; + fc = le16_to_cpu(hdr->frame_control); + + if (unlikely(!WLAN_FC_DATA_PRESENT(fc))) + return 0; + + hdrlen = ieee80211_get_hdrlen(fc); + + if (unlikely(skb->len >= hdrlen + sizeof(eapol_header) && + memcmp(skb->data + hdrlen, eapol_header, + sizeof(eapol_header)) == 0)) + return 1; + + return 0; +} + + +static ieee80211_txrx_result +ieee80211_tx_h_rate_ctrl(struct ieee80211_txrx_data *tx) +{ + struct rate_control_extra extra; + + memset(&extra, 0, sizeof(extra)); + extra.mode = tx->u.tx.mode; + extra.mgmt_data = tx->sdata && + tx->sdata->type == IEEE80211_IF_TYPE_MGMT; + extra.ethertype = tx->ethertype; + + tx->u.tx.rate = rate_control_get_rate(tx->local, tx->dev, tx->skb, + &extra); + if (unlikely(extra.probe != NULL)) { + tx->u.tx.control->flags |= IEEE80211_TXCTL_RATE_CTRL_PROBE; + tx->u.tx.probe_last_frag = 1; + tx->u.tx.control->alt_retry_rate = tx->u.tx.rate->val; + tx->u.tx.rate = extra.probe; + } else { + tx->u.tx.control->alt_retry_rate = -1; + } + if (!tx->u.tx.rate) + return TXRX_DROP; + if (tx->u.tx.mode->mode == MODE_IEEE80211G && + tx->local->cts_protect_erp_frames && tx->fragmented && + extra.nonerp) { + tx->u.tx.last_frag_rate = tx->u.tx.rate; + tx->u.tx.probe_last_frag = extra.probe ? 1 : 0; + + tx->u.tx.rate = extra.nonerp; + tx->u.tx.control->rate = extra.nonerp; + tx->u.tx.control->flags &= ~IEEE80211_TXCTL_RATE_CTRL_PROBE; + } else { + tx->u.tx.last_frag_rate = tx->u.tx.rate; + tx->u.tx.control->rate = tx->u.tx.rate; + } + tx->u.tx.control->tx_rate = tx->u.tx.rate->val; + if ((tx->u.tx.rate->flags & IEEE80211_RATE_PREAMBLE2) && + tx->local->short_preamble && + (!tx->sta || (tx->sta->flags & WLAN_STA_SHORT_PREAMBLE))) { + tx->u.tx.short_preamble = 1; + tx->u.tx.control->tx_rate = tx->u.tx.rate->val2; + } + + return TXRX_CONTINUE; +} + + +static ieee80211_txrx_result +ieee80211_tx_h_select_key(struct ieee80211_txrx_data *tx) +{ + if (tx->sta) + tx->u.tx.control->key_idx = tx->sta->key_idx_compression; + else + tx->u.tx.control->key_idx = HW_KEY_IDX_INVALID; + + if (unlikely(tx->u.tx.control->flags & IEEE80211_TXCTL_DO_NOT_ENCRYPT)) + tx->key = NULL; + else if (tx->sta && tx->sta->key) + tx->key = tx->sta->key; + else if (tx->sdata->default_key) + tx->key = tx->sdata->default_key; + else if (tx->sdata->drop_unencrypted && + !(tx->sdata->eapol && ieee80211_is_eapol(tx->skb))) { + I802_DEBUG_INC(tx->local->tx_handlers_drop_unencrypted); + return TXRX_DROP; + } else + tx->key = NULL; + + if (tx->key) { + tx->key->tx_rx_count++; + if (unlikely(tx->local->key_tx_rx_threshold && + tx->key->tx_rx_count > + tx->local->key_tx_rx_threshold)) { + ieee80211_key_threshold_notify(tx->dev, tx->key, + tx->sta); + } + } + + return TXRX_CONTINUE; +} + + +static ieee80211_txrx_result +ieee80211_tx_h_fragment(struct ieee80211_txrx_data *tx) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data; + size_t hdrlen, per_fragm, num_fragm, payload_len, left; + struct sk_buff **frags, *first, *frag; + int i; + u16 seq; + u8 *pos; + int frag_threshold = tx->local->fragmentation_threshold; + + if (!tx->fragmented) + return TXRX_CONTINUE; + + first = tx->skb; + + hdrlen = ieee80211_get_hdrlen(tx->fc); + payload_len = first->len - hdrlen; + per_fragm = frag_threshold - hdrlen - FCS_LEN; + num_fragm = (payload_len + per_fragm - 1) / per_fragm; + + frags = kzalloc(num_fragm * sizeof(struct sk_buff *), GFP_ATOMIC); + if (!frags) + goto fail; + + hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREFRAGS); + seq = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ; + pos = first->data + hdrlen + per_fragm; + left = payload_len - per_fragm; + for (i = 0; i < num_fragm - 1; i++) { + struct ieee80211_hdr *fhdr; + size_t copylen; + + if (left <= 0) + goto fail; + + /* reserve enough extra head and tail room for possible + * encryption */ + frag = frags[i] = + dev_alloc_skb(tx->local->hw.extra_tx_headroom + + frag_threshold + + IEEE80211_ENCRYPT_HEADROOM + + IEEE80211_ENCRYPT_TAILROOM); + if (!frag) + goto fail; + /* Make sure that all fragments use the same priority so + * that they end up using the same TX queue */ + frag->priority = first->priority; + skb_reserve(frag, tx->local->hw.extra_tx_headroom + + IEEE80211_ENCRYPT_HEADROOM); + fhdr = (struct ieee80211_hdr *) skb_put(frag, hdrlen); + memcpy(fhdr, first->data, hdrlen); + if (i == num_fragm - 2) + fhdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREFRAGS); + fhdr->seq_ctrl = cpu_to_le16(seq | ((i + 1) & IEEE80211_SCTL_FRAG)); + copylen = left > per_fragm ? per_fragm : left; + memcpy(skb_put(frag, copylen), pos, copylen); + + pos += copylen; + left -= copylen; + } + skb_trim(first, hdrlen + per_fragm); + + tx->u.tx.num_extra_frag = num_fragm - 1; + tx->u.tx.extra_frag = frags; + + return TXRX_CONTINUE; + + fail: + printk(KERN_DEBUG "%s: failed to fragment frame\n", tx->dev->name); + if (frags) { + for (i = 0; i < num_fragm - 1; i++) + if (frags[i]) + dev_kfree_skb(frags[i]); + kfree(frags); + } + I802_DEBUG_INC(tx->local->tx_handlers_drop_fragment); + return TXRX_DROP; +} + + +static int wep_encrypt_skb(struct ieee80211_txrx_data *tx, struct sk_buff *skb) +{ + if (tx->key->force_sw_encrypt) { + if (ieee80211_wep_encrypt(tx->local, skb, tx->key)) + return -1; + } else { + tx->u.tx.control->key_idx = tx->key->hw_key_idx; + if (tx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV) { + if (ieee80211_wep_add_iv(tx->local, skb, tx->key) == + NULL) + return -1; + } + } + return 0; +} + + +void ieee80211_tx_set_iswep(struct ieee80211_txrx_data *tx) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data; + + hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); + if (tx->u.tx.extra_frag) { + struct ieee80211_hdr *fhdr; + int i; + for (i = 0; i < tx->u.tx.num_extra_frag; i++) { + fhdr = (struct ieee80211_hdr *) + tx->u.tx.extra_frag[i]->data; + fhdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); + } + } +} + + +static ieee80211_txrx_result +ieee80211_tx_h_wep_encrypt(struct ieee80211_txrx_data *tx) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data; + u16 fc; + + fc = le16_to_cpu(hdr->frame_control); + + if (!tx->key || tx->key->alg != ALG_WEP || + ((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA && + ((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT || + (fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_AUTH))) + return TXRX_CONTINUE; + + tx->u.tx.control->iv_len = WEP_IV_LEN; + tx->u.tx.control->icv_len = WEP_ICV_LEN; + ieee80211_tx_set_iswep(tx); + + if (wep_encrypt_skb(tx, tx->skb) < 0) { + I802_DEBUG_INC(tx->local->tx_handlers_drop_wep); + return TXRX_DROP; + } + + if (tx->u.tx.extra_frag) { + int i; + for (i = 0; i < tx->u.tx.num_extra_frag; i++) { + if (wep_encrypt_skb(tx, tx->u.tx.extra_frag[i]) < 0) { + I802_DEBUG_INC(tx->local-> + tx_handlers_drop_wep); + return TXRX_DROP; + } + } + } + + return TXRX_CONTINUE; +} + + +static int ieee80211_frame_duration(struct ieee80211_local *local, size_t len, + int rate, int erp, int short_preamble) +{ + int dur; + + /* calculate duration (in microseconds, rounded up to next higher + * integer if it includes a fractional microsecond) to send frame of + * len bytes (does not include FCS) at the given rate. Duration will + * also include SIFS. + * + * rate is in 100 kbps, so divident is multiplied by 10 in the + * DIV_ROUND_UP() operations. + */ + + if (local->hw.conf.phymode == MODE_IEEE80211A || erp || + local->hw.conf.phymode == MODE_ATHEROS_TURBO) { + /* + * OFDM: + * + * N_DBPS = DATARATE x 4 + * N_SYM = Ceiling((16+8xLENGTH+6) / N_DBPS) + * (16 = SIGNAL time, 6 = tail bits) + * TXTIME = T_PREAMBLE + T_SIGNAL + T_SYM x N_SYM + Signal Ext + * + * T_SYM = 4 usec + * 802.11a - 17.5.2: aSIFSTime = 16 usec + * 802.11g - 19.8.4: aSIFSTime = 10 usec + + * signal ext = 6 usec + */ + /* FIX: Atheros Turbo may have different (shorter) duration? */ + dur = 16; /* SIFS + signal ext */ + dur += 16; /* 17.3.2.3: T_PREAMBLE = 16 usec */ + dur += 4; /* 17.3.2.3: T_SIGNAL = 4 usec */ + dur += 4 * DIV_ROUND_UP((16 + 8 * (len + 4) + 6) * 10, + 4 * rate); /* T_SYM x N_SYM */ + } else { + /* + * 802.11b or 802.11g with 802.11b compatibility: + * 18.3.4: TXTIME = PreambleLength + PLCPHeaderTime + + * Ceiling(((LENGTH+PBCC)x8)/DATARATE). PBCC=0. + * + * 802.11 (DS): 15.3.3, 802.11b: 18.3.4 + * aSIFSTime = 10 usec + * aPreambleLength = 144 usec or 72 usec with short preamble + * aPLCPHeaderLength = 48 usec or 24 usec with short preamble + */ + dur = 10; /* aSIFSTime = 10 usec */ + dur += short_preamble ? (72 + 24) : (144 + 48); + + dur += DIV_ROUND_UP(8 * (len + 4) * 10, rate); + } + + return dur; +} + + +/* Exported duration function for driver use */ +__le16 ieee80211_generic_frame_duration(struct ieee80211_hw *hw, + size_t frame_len, int rate) +{ + struct ieee80211_local *local = hw_to_local(hw); + u16 dur; + int erp; + + erp = ieee80211_is_erp_rate(hw->conf.phymode, rate); + dur = ieee80211_frame_duration(local, frame_len, rate, + erp, local->short_preamble); + + return cpu_to_le16(dur); +} +EXPORT_SYMBOL(ieee80211_generic_frame_duration); + + +static u16 ieee80211_duration(struct ieee80211_txrx_data *tx, int group_addr, + int next_frag_len) +{ + int rate, mrate, erp, dur, i; + struct ieee80211_rate *txrate = tx->u.tx.rate; + struct ieee80211_local *local = tx->local; + struct ieee80211_hw_mode *mode = tx->u.tx.mode; + + erp = txrate->flags & IEEE80211_RATE_ERP; + + /* + * data and mgmt (except PS Poll): + * - during CFP: 32768 + * - during contention period: + * if addr1 is group address: 0 + * if more fragments = 0 and addr1 is individual address: time to + * transmit one ACK plus SIFS + * if more fragments = 1 and addr1 is individual address: time to + * transmit next fragment plus 2 x ACK plus 3 x SIFS + * + * IEEE 802.11, 9.6: + * - control response frame (CTS or ACK) shall be transmitted using the + * same rate as the immediately previous frame in the frame exchange + * sequence, if this rate belongs to the PHY mandatory rates, or else + * at the highest possible rate belonging to the PHY rates in the + * BSSBasicRateSet + */ + + if ((tx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL) { + /* TODO: These control frames are not currently sent by + * 80211.o, but should they be implemented, this function + * needs to be updated to support duration field calculation. + * + * RTS: time needed to transmit pending data/mgmt frame plus + * one CTS frame plus one ACK frame plus 3 x SIFS + * CTS: duration of immediately previous RTS minus time + * required to transmit CTS and its SIFS + * ACK: 0 if immediately previous directed data/mgmt had + * more=0, with more=1 duration in ACK frame is duration + * from previous frame minus time needed to transmit ACK + * and its SIFS + * PS Poll: BIT(15) | BIT(14) | aid + */ + return 0; + } + + /* data/mgmt */ + if (0 /* FIX: data/mgmt during CFP */) + return 32768; + + if (group_addr) /* Group address as the destination - no ACK */ + return 0; + + /* Individual destination address: + * IEEE 802.11, Ch. 9.6 (after IEEE 802.11g changes) + * CTS and ACK frames shall be transmitted using the highest rate in + * basic rate set that is less than or equal to the rate of the + * immediately previous frame and that is using the same modulation + * (CCK or OFDM). If no basic rate set matches with these requirements, + * the highest mandatory rate of the PHY that is less than or equal to + * the rate of the previous frame is used. + * Mandatory rates for IEEE 802.11g PHY: 1, 2, 5.5, 11, 6, 12, 24 Mbps + */ + rate = -1; + mrate = 10; /* use 1 Mbps if everything fails */ + for (i = 0; i < mode->num_rates; i++) { + struct ieee80211_rate *r = &mode->rates[i]; + if (r->rate > txrate->rate) + break; + + if (IEEE80211_RATE_MODULATION(txrate->flags) != + IEEE80211_RATE_MODULATION(r->flags)) + continue; + + if (r->flags & IEEE80211_RATE_BASIC) + rate = r->rate; + else if (r->flags & IEEE80211_RATE_MANDATORY) + mrate = r->rate; + } + if (rate == -1) { + /* No matching basic rate found; use highest suitable mandatory + * PHY rate */ + rate = mrate; + } + + /* Time needed to transmit ACK + * (10 bytes + 4-byte FCS = 112 bits) plus SIFS; rounded up + * to closest integer */ + + dur = ieee80211_frame_duration(local, 10, rate, erp, + local->short_preamble); + + if (next_frag_len) { + /* Frame is fragmented: duration increases with time needed to + * transmit next fragment plus ACK and 2 x SIFS. */ + dur *= 2; /* ACK + SIFS */ + /* next fragment */ + dur += ieee80211_frame_duration(local, next_frag_len, + txrate->rate, erp, + local->short_preamble); + } + + return dur; +} + + +static ieee80211_txrx_result +ieee80211_tx_h_misc(struct ieee80211_txrx_data *tx) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data; + u16 dur; + struct ieee80211_tx_control *control = tx->u.tx.control; + struct ieee80211_hw_mode *mode = tx->u.tx.mode; + + if (!is_multicast_ether_addr(hdr->addr1)) { + if (tx->skb->len + FCS_LEN > tx->local->rts_threshold && + tx->local->rts_threshold < IEEE80211_MAX_RTS_THRESHOLD) { + control->flags |= IEEE80211_TXCTL_USE_RTS_CTS; + control->retry_limit = + tx->local->long_retry_limit; + } else { + control->retry_limit = + tx->local->short_retry_limit; + } + } else { + control->retry_limit = 1; + } + + if (tx->fragmented) { + /* Do not use multiple retry rates when sending fragmented + * frames. + * TODO: The last fragment could still use multiple retry + * rates. */ + control->alt_retry_rate = -1; + } + + /* Use CTS protection for unicast frames sent using extended rates if + * there are associated non-ERP stations and RTS/CTS is not configured + * for the frame. */ + if (mode->mode == MODE_IEEE80211G && + (tx->u.tx.rate->flags & IEEE80211_RATE_ERP) && + tx->u.tx.unicast && + tx->local->cts_protect_erp_frames && + !(control->flags & IEEE80211_TXCTL_USE_RTS_CTS)) + control->flags |= IEEE80211_TXCTL_USE_CTS_PROTECT; + + /* Setup duration field for the first fragment of the frame. Duration + * for remaining fragments will be updated when they are being sent + * to low-level driver in ieee80211_tx(). */ + dur = ieee80211_duration(tx, is_multicast_ether_addr(hdr->addr1), + tx->fragmented ? tx->u.tx.extra_frag[0]->len : + 0); + hdr->duration_id = cpu_to_le16(dur); + + if ((control->flags & IEEE80211_TXCTL_USE_RTS_CTS) || + (control->flags & IEEE80211_TXCTL_USE_CTS_PROTECT)) { + struct ieee80211_rate *rate; + + /* Do not use multiple retry rates when using RTS/CTS */ + control->alt_retry_rate = -1; + + /* Use min(data rate, max base rate) as CTS/RTS rate */ + rate = tx->u.tx.rate; + while (rate > mode->rates && + !(rate->flags & IEEE80211_RATE_BASIC)) + rate--; + + control->rts_cts_rate = rate->val; + control->rts_rate = rate; + } + + if (tx->sta) { + tx->sta->tx_packets++; + tx->sta->tx_fragments++; + tx->sta->tx_bytes += tx->skb->len; + if (tx->u.tx.extra_frag) { + int i; + tx->sta->tx_fragments += tx->u.tx.num_extra_frag; + for (i = 0; i < tx->u.tx.num_extra_frag; i++) { + tx->sta->tx_bytes += + tx->u.tx.extra_frag[i]->len; + } + } + } + + return TXRX_CONTINUE; +} + + +static ieee80211_txrx_result +ieee80211_tx_h_check_assoc(struct ieee80211_txrx_data *tx) +{ +#ifdef CONFIG_MAC80211_VERBOSE_DEBUG + struct sk_buff *skb = tx->skb; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; +#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ + u32 sta_flags; + + if (unlikely(tx->local->sta_scanning != 0) && + ((tx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT || + (tx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_PROBE_REQ)) + return TXRX_DROP; + + if (tx->u.tx.ps_buffered) + return TXRX_CONTINUE; + + sta_flags = tx->sta ? tx->sta->flags : 0; + + if (likely(tx->u.tx.unicast)) { + if (unlikely(!(sta_flags & WLAN_STA_ASSOC) && + tx->sdata->type != IEEE80211_IF_TYPE_IBSS && + (tx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA)) { +#ifdef CONFIG_MAC80211_VERBOSE_DEBUG + printk(KERN_DEBUG "%s: dropped data frame to not " + "associated station " MAC_FMT "\n", + tx->dev->name, MAC_ARG(hdr->addr1)); +#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ + I802_DEBUG_INC(tx->local->tx_handlers_drop_not_assoc); + return TXRX_DROP; + } + } else { + if (unlikely((tx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA && + tx->local->num_sta == 0 && + !tx->local->allow_broadcast_always && + tx->sdata->type != IEEE80211_IF_TYPE_IBSS)) { + /* + * No associated STAs - no need to send multicast + * frames. + */ + return TXRX_DROP; + } + return TXRX_CONTINUE; + } + + if (unlikely(!tx->u.tx.mgmt_interface && tx->sdata->ieee802_1x && + !(sta_flags & WLAN_STA_AUTHORIZED))) { +#ifdef CONFIG_MAC80211_VERBOSE_DEBUG + printk(KERN_DEBUG "%s: dropped frame to " MAC_FMT + " (unauthorized port)\n", tx->dev->name, + MAC_ARG(hdr->addr1)); +#endif + I802_DEBUG_INC(tx->local->tx_handlers_drop_unauth_port); + return TXRX_DROP; + } + + return TXRX_CONTINUE; +} + +static ieee80211_txrx_result +ieee80211_tx_h_sequence(struct ieee80211_txrx_data *tx) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data; + + if (ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_control)) >= 24) + ieee80211_include_sequence(tx->sdata, hdr); + + return TXRX_CONTINUE; +} + +/* This function is called whenever the AP is about to exceed the maximum limit + * of buffered frames for power saving STAs. This situation should not really + * happen often during normal operation, so dropping the oldest buffered packet + * from each queue should be OK to make some room for new frames. */ +static void purge_old_ps_buffers(struct ieee80211_local *local) +{ + int total = 0, purged = 0; + struct sk_buff *skb; + struct ieee80211_sub_if_data *sdata; + struct sta_info *sta; + + read_lock(&local->sub_if_lock); + list_for_each_entry(sdata, &local->sub_if_list, list) { + struct ieee80211_if_ap *ap; + if (sdata->dev == local->mdev || + sdata->type != IEEE80211_IF_TYPE_AP) + continue; + ap = &sdata->u.ap; + skb = skb_dequeue(&ap->ps_bc_buf); + if (skb) { + purged++; + dev_kfree_skb(skb); + } + total += skb_queue_len(&ap->ps_bc_buf); + } + read_unlock(&local->sub_if_lock); + + spin_lock_bh(&local->sta_lock); + list_for_each_entry(sta, &local->sta_list, list) { + skb = skb_dequeue(&sta->ps_tx_buf); + if (skb) { + purged++; + dev_kfree_skb(skb); + } + total += skb_queue_len(&sta->ps_tx_buf); + } + spin_unlock_bh(&local->sta_lock); + + local->total_ps_buffered = total; + printk(KERN_DEBUG "%s: PS buffers full - purged %d frames\n", + local->mdev->name, purged); +} + + +static inline ieee80211_txrx_result +ieee80211_tx_h_multicast_ps_buf(struct ieee80211_txrx_data *tx) +{ + /* broadcast/multicast frame */ + /* If any of the associated stations is in power save mode, + * the frame is buffered to be sent after DTIM beacon frame */ + if ((tx->local->hw.flags & IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING) && + tx->sdata->type != IEEE80211_IF_TYPE_WDS && + tx->sdata->bss && atomic_read(&tx->sdata->bss->num_sta_ps) && + !(tx->fc & IEEE80211_FCTL_ORDER)) { + if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER) + purge_old_ps_buffers(tx->local); + if (skb_queue_len(&tx->sdata->bss->ps_bc_buf) >= + AP_MAX_BC_BUFFER) { + if (net_ratelimit()) { + printk(KERN_DEBUG "%s: BC TX buffer full - " + "dropping the oldest frame\n", + tx->dev->name); + } + dev_kfree_skb(skb_dequeue(&tx->sdata->bss->ps_bc_buf)); + } else + tx->local->total_ps_buffered++; + skb_queue_tail(&tx->sdata->bss->ps_bc_buf, tx->skb); + return TXRX_QUEUED; + } + + return TXRX_CONTINUE; +} + + +static inline ieee80211_txrx_result +ieee80211_tx_h_unicast_ps_buf(struct ieee80211_txrx_data *tx) +{ + struct sta_info *sta = tx->sta; + + if (unlikely(!sta || + ((tx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT && + (tx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PROBE_RESP))) + return TXRX_CONTINUE; + + if (unlikely((sta->flags & WLAN_STA_PS) && !sta->pspoll)) { + struct ieee80211_tx_packet_data *pkt_data; +#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG + printk(KERN_DEBUG "STA " MAC_FMT " aid %d: PS buffer (entries " + "before %d)\n", + MAC_ARG(sta->addr), sta->aid, + skb_queue_len(&sta->ps_tx_buf)); +#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ + sta->flags |= WLAN_STA_TIM; + if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER) + purge_old_ps_buffers(tx->local); + if (skb_queue_len(&sta->ps_tx_buf) >= STA_MAX_TX_BUFFER) { + struct sk_buff *old = skb_dequeue(&sta->ps_tx_buf); + if (net_ratelimit()) { + printk(KERN_DEBUG "%s: STA " MAC_FMT " TX " + "buffer full - dropping oldest frame\n", + tx->dev->name, MAC_ARG(sta->addr)); + } + dev_kfree_skb(old); + } else + tx->local->total_ps_buffered++; + /* Queue frame to be sent after STA sends an PS Poll frame */ + if (skb_queue_empty(&sta->ps_tx_buf)) { + if (tx->local->ops->set_tim) + tx->local->ops->set_tim(local_to_hw(tx->local), + sta->aid, 1); + if (tx->sdata->bss) + bss_tim_set(tx->local, tx->sdata->bss, sta->aid); + } + pkt_data = (struct ieee80211_tx_packet_data *)tx->skb->cb; + pkt_data->jiffies = jiffies; + skb_queue_tail(&sta->ps_tx_buf, tx->skb); + return TXRX_QUEUED; + } +#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG + else if (unlikely(sta->flags & WLAN_STA_PS)) { + printk(KERN_DEBUG "%s: STA " MAC_FMT " in PS mode, but pspoll " + "set -> send frame\n", tx->dev->name, + MAC_ARG(sta->addr)); + } +#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ + sta->pspoll = 0; + + return TXRX_CONTINUE; +} + + +static ieee80211_txrx_result +ieee80211_tx_h_ps_buf(struct ieee80211_txrx_data *tx) +{ + if (unlikely(tx->u.tx.ps_buffered)) + return TXRX_CONTINUE; + + if (tx->u.tx.unicast) + return ieee80211_tx_h_unicast_ps_buf(tx); + else + return ieee80211_tx_h_multicast_ps_buf(tx); +} + + +static void inline +__ieee80211_tx_prepare(struct ieee80211_txrx_data *tx, + struct sk_buff *skb, + struct net_device *dev, + struct ieee80211_tx_control *control) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + int hdrlen; + + memset(tx, 0, sizeof(*tx)); + tx->skb = skb; + tx->dev = dev; /* use original interface */ + tx->local = local; + tx->sdata = IEEE80211_DEV_TO_SUB_IF(dev); + tx->sta = sta_info_get(local, hdr->addr1); + tx->fc = le16_to_cpu(hdr->frame_control); + control->power_level = local->hw.conf.power_level; + tx->u.tx.control = control; + tx->u.tx.unicast = !is_multicast_ether_addr(hdr->addr1); + if (is_multicast_ether_addr(hdr->addr1)) + control->flags |= IEEE80211_TXCTL_NO_ACK; + else + control->flags &= ~IEEE80211_TXCTL_NO_ACK; + tx->fragmented = local->fragmentation_threshold < + IEEE80211_MAX_FRAG_THRESHOLD && tx->u.tx.unicast && + skb->len + FCS_LEN > local->fragmentation_threshold && + (!local->ops->set_frag_threshold); + if (!tx->sta) + control->flags |= IEEE80211_TXCTL_CLEAR_DST_MASK; + else if (tx->sta->clear_dst_mask) { + control->flags |= IEEE80211_TXCTL_CLEAR_DST_MASK; + tx->sta->clear_dst_mask = 0; + } + control->antenna_sel_tx = local->hw.conf.antenna_sel_tx; + if (local->sta_antenna_sel != STA_ANTENNA_SEL_AUTO && tx->sta) + control->antenna_sel_tx = tx->sta->antenna_sel_tx; + hdrlen = ieee80211_get_hdrlen(tx->fc); + if (skb->len > hdrlen + sizeof(rfc1042_header) + 2) { + u8 *pos = &skb->data[hdrlen + sizeof(rfc1042_header)]; + tx->ethertype = (pos[0] << 8) | pos[1]; + } + control->flags |= IEEE80211_TXCTL_FIRST_FRAGMENT; + +} + +static int inline is_ieee80211_device(struct net_device *dev, + struct net_device *master) +{ + return (wdev_priv(dev->ieee80211_ptr) == + wdev_priv(master->ieee80211_ptr)); +} + +/* Device in tx->dev has a reference added; use dev_put(tx->dev) when + * finished with it. */ +static int inline ieee80211_tx_prepare(struct ieee80211_txrx_data *tx, + struct sk_buff *skb, + struct net_device *mdev, + struct ieee80211_tx_control *control) +{ + struct ieee80211_tx_packet_data *pkt_data; + struct net_device *dev; + + pkt_data = (struct ieee80211_tx_packet_data *)skb->cb; + dev = dev_get_by_index(pkt_data->ifindex); + if (unlikely(dev && !is_ieee80211_device(dev, mdev))) { + dev_put(dev); + dev = NULL; + } + if (unlikely(!dev)) + return -ENODEV; + __ieee80211_tx_prepare(tx, skb, dev, control); + return 0; +} + +static inline int __ieee80211_queue_stopped(const struct ieee80211_local *local, + int queue) +{ + return test_bit(IEEE80211_LINK_STATE_XOFF, &local->state[queue]); +} + +static inline int __ieee80211_queue_pending(const struct ieee80211_local *local, + int queue) +{ + return test_bit(IEEE80211_LINK_STATE_PENDING, &local->state[queue]); +} + +#define IEEE80211_TX_OK 0 +#define IEEE80211_TX_AGAIN 1 +#define IEEE80211_TX_FRAG_AGAIN 2 + +static int __ieee80211_tx(struct ieee80211_local *local, struct sk_buff *skb, + struct ieee80211_txrx_data *tx) +{ + struct ieee80211_tx_control *control = tx->u.tx.control; + int ret, i; + + if (skb) { + ieee80211_dump_frame(local->mdev->name, "TX to low-level driver", skb); + ret = local->ops->tx(local_to_hw(local), skb, control); + if (ret) + return IEEE80211_TX_AGAIN; + local->mdev->trans_start = jiffies; + ieee80211_led_tx(local, 1); + } + if (tx->u.tx.extra_frag) { + control->flags &= ~(IEEE80211_TXCTL_USE_RTS_CTS | + IEEE80211_TXCTL_USE_CTS_PROTECT | + IEEE80211_TXCTL_CLEAR_DST_MASK | + IEEE80211_TXCTL_FIRST_FRAGMENT); + for (i = 0; i < tx->u.tx.num_extra_frag; i++) { + if (!tx->u.tx.extra_frag[i]) + continue; + if (__ieee80211_queue_stopped(local, control->queue)) + return IEEE80211_TX_FRAG_AGAIN; + if (i == tx->u.tx.num_extra_frag) { + control->tx_rate = tx->u.tx.last_frag_hwrate; + control->rate = tx->u.tx.last_frag_rate; + if (tx->u.tx.probe_last_frag) + control->flags |= + IEEE80211_TXCTL_RATE_CTRL_PROBE; + else + control->flags &= + ~IEEE80211_TXCTL_RATE_CTRL_PROBE; + } + + ieee80211_dump_frame(local->mdev->name, + "TX to low-level driver", + tx->u.tx.extra_frag[i]); + ret = local->ops->tx(local_to_hw(local), + tx->u.tx.extra_frag[i], + control); + if (ret) + return IEEE80211_TX_FRAG_AGAIN; + local->mdev->trans_start = jiffies; + ieee80211_led_tx(local, 1); + tx->u.tx.extra_frag[i] = NULL; + } + kfree(tx->u.tx.extra_frag); + tx->u.tx.extra_frag = NULL; + } + return IEEE80211_TX_OK; +} + +static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb, + struct ieee80211_tx_control *control, int mgmt) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct sta_info *sta; + ieee80211_tx_handler *handler; + struct ieee80211_txrx_data tx; + ieee80211_txrx_result res = TXRX_DROP; + int ret, i; + + WARN_ON(__ieee80211_queue_pending(local, control->queue)); + + if (unlikely(skb->len < 10)) { + dev_kfree_skb(skb); + return 0; + } + + __ieee80211_tx_prepare(&tx, skb, dev, control); + sta = tx.sta; + tx.u.tx.mgmt_interface = mgmt; + tx.u.tx.mode = local->hw.conf.mode; + + for (handler = local->tx_handlers; *handler != NULL; handler++) { + res = (*handler)(&tx); + if (res != TXRX_CONTINUE) + break; + } + + skb = tx.skb; /* handlers are allowed to change skb */ + + if (sta) + sta_info_put(sta); + + if (unlikely(res == TXRX_DROP)) { + I802_DEBUG_INC(local->tx_handlers_drop); + goto drop; + } + + if (unlikely(res == TXRX_QUEUED)) { + I802_DEBUG_INC(local->tx_handlers_queued); + return 0; + } + + if (tx.u.tx.extra_frag) { + for (i = 0; i < tx.u.tx.num_extra_frag; i++) { + int next_len, dur; + struct ieee80211_hdr *hdr = + (struct ieee80211_hdr *) + tx.u.tx.extra_frag[i]->data; + + if (i + 1 < tx.u.tx.num_extra_frag) { + next_len = tx.u.tx.extra_frag[i + 1]->len; + } else { + next_len = 0; + tx.u.tx.rate = tx.u.tx.last_frag_rate; + tx.u.tx.last_frag_hwrate = tx.u.tx.rate->val; + } + dur = ieee80211_duration(&tx, 0, next_len); + hdr->duration_id = cpu_to_le16(dur); + } + } + +retry: + ret = __ieee80211_tx(local, skb, &tx); + if (ret) { + struct ieee80211_tx_stored_packet *store = + &local->pending_packet[control->queue]; + + if (ret == IEEE80211_TX_FRAG_AGAIN) + skb = NULL; + set_bit(IEEE80211_LINK_STATE_PENDING, + &local->state[control->queue]); + smp_mb(); + /* When the driver gets out of buffers during sending of + * fragments and calls ieee80211_stop_queue, there is + * a small window between IEEE80211_LINK_STATE_XOFF and + * IEEE80211_LINK_STATE_PENDING flags are set. If a buffer + * gets available in that window (i.e. driver calls + * ieee80211_wake_queue), we would end up with ieee80211_tx + * called with IEEE80211_LINK_STATE_PENDING. Prevent this by + * continuing transmitting here when that situation is + * possible to have happened. */ + if (!__ieee80211_queue_stopped(local, control->queue)) { + clear_bit(IEEE80211_LINK_STATE_PENDING, + &local->state[control->queue]); + goto retry; + } + memcpy(&store->control, control, + sizeof(struct ieee80211_tx_control)); + store->skb = skb; + store->extra_frag = tx.u.tx.extra_frag; + store->num_extra_frag = tx.u.tx.num_extra_frag; + store->last_frag_hwrate = tx.u.tx.last_frag_hwrate; + store->last_frag_rate = tx.u.tx.last_frag_rate; + store->last_frag_rate_ctrl_probe = tx.u.tx.probe_last_frag; + } + return 0; + + drop: + if (skb) + dev_kfree_skb(skb); + for (i = 0; i < tx.u.tx.num_extra_frag; i++) + if (tx.u.tx.extra_frag[i]) + dev_kfree_skb(tx.u.tx.extra_frag[i]); + kfree(tx.u.tx.extra_frag); + return 0; +} + +static void ieee80211_tx_pending(unsigned long data) +{ + struct ieee80211_local *local = (struct ieee80211_local *)data; + struct net_device *dev = local->mdev; + struct ieee80211_tx_stored_packet *store; + struct ieee80211_txrx_data tx; + int i, ret, reschedule = 0; + + netif_tx_lock_bh(dev); + for (i = 0; i < local->hw.queues; i++) { + if (__ieee80211_queue_stopped(local, i)) + continue; + if (!__ieee80211_queue_pending(local, i)) { + reschedule = 1; + continue; + } + store = &local->pending_packet[i]; + tx.u.tx.control = &store->control; + tx.u.tx.extra_frag = store->extra_frag; + tx.u.tx.num_extra_frag = store->num_extra_frag; + tx.u.tx.last_frag_hwrate = store->last_frag_hwrate; + tx.u.tx.last_frag_rate = store->last_frag_rate; + tx.u.tx.probe_last_frag = store->last_frag_rate_ctrl_probe; + ret = __ieee80211_tx(local, store->skb, &tx); + if (ret) { + if (ret == IEEE80211_TX_FRAG_AGAIN) + store->skb = NULL; + } else { + clear_bit(IEEE80211_LINK_STATE_PENDING, + &local->state[i]); + reschedule = 1; + } + } + netif_tx_unlock_bh(dev); + if (reschedule) + netif_schedule(dev); +} + +static void ieee80211_clear_tx_pending(struct ieee80211_local *local) +{ + int i, j; + struct ieee80211_tx_stored_packet *store; + + for (i = 0; i < local->hw.queues; i++) { + if (!__ieee80211_queue_pending(local, i)) + continue; + store = &local->pending_packet[i]; + kfree_skb(store->skb); + for (j = 0; j < store->num_extra_frag; j++) + kfree_skb(store->extra_frag[j]); + kfree(store->extra_frag); + clear_bit(IEEE80211_LINK_STATE_PENDING, &local->state[i]); + } +} + +static int ieee80211_master_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct ieee80211_tx_control control; + struct ieee80211_tx_packet_data *pkt_data; + struct net_device *odev = NULL; + struct ieee80211_sub_if_data *osdata; + int headroom; + int ret; + + /* + * copy control out of the skb so other people can use skb->cb + */ + pkt_data = (struct ieee80211_tx_packet_data *)skb->cb; + memset(&control, 0, sizeof(struct ieee80211_tx_control)); + + if (pkt_data->ifindex) + odev = dev_get_by_index(pkt_data->ifindex); + if (unlikely(odev && !is_ieee80211_device(odev, dev))) { + dev_put(odev); + odev = NULL; + } + if (unlikely(!odev)) { +#ifdef CONFIG_MAC80211_VERBOSE_DEBUG + printk(KERN_DEBUG "%s: Discarded packet with nonexistent " + "originating device\n", dev->name); +#endif + dev_kfree_skb(skb); + return 0; + } + osdata = IEEE80211_DEV_TO_SUB_IF(odev); + + headroom = osdata->local->hw.extra_tx_headroom + + IEEE80211_ENCRYPT_HEADROOM; + if (skb_headroom(skb) < headroom) { + if (pskb_expand_head(skb, headroom, 0, GFP_ATOMIC)) { + dev_kfree_skb(skb); + return 0; + } + } + + control.ifindex = odev->ifindex; + control.type = osdata->type; + if (pkt_data->req_tx_status) + control.flags |= IEEE80211_TXCTL_REQ_TX_STATUS; + if (pkt_data->do_not_encrypt) + control.flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT; + if (pkt_data->requeue) + control.flags |= IEEE80211_TXCTL_REQUEUE; + control.queue = pkt_data->queue; + + ret = ieee80211_tx(odev, skb, &control, + control.type == IEEE80211_IF_TYPE_MGMT); + dev_put(odev); + + return ret; +} + + +/** + * ieee80211_subif_start_xmit - netif start_xmit function for Ethernet-type + * subinterfaces (wlan#, WDS, and VLAN interfaces) + * @skb: packet to be sent + * @dev: incoming interface + * + * Returns: 0 on success (and frees skb in this case) or 1 on failure (skb will + * not be freed, and caller is responsible for either retrying later or freeing + * skb). + * + * This function takes in an Ethernet header and encapsulates it with suitable + * IEEE 802.11 header based on which interface the packet is coming in. The + * encapsulated packet will then be passed to master interface, wlan#.11, for + * transmission (through low-level driver). + */ +static int ieee80211_subif_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_tx_packet_data *pkt_data; + struct ieee80211_sub_if_data *sdata; + int ret = 1, head_need; + u16 ethertype, hdrlen, fc; + struct ieee80211_hdr hdr; + const u8 *encaps_data; + int encaps_len, skip_header_bytes; + int nh_pos, h_pos, no_encrypt = 0; + struct sta_info *sta; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + if (unlikely(skb->len < ETH_HLEN)) { + printk(KERN_DEBUG "%s: short skb (len=%d)\n", + dev->name, skb->len); + ret = 0; + goto fail; + } + + nh_pos = skb_network_header(skb) - skb->data; + h_pos = skb_transport_header(skb) - skb->data; + + /* convert Ethernet header to proper 802.11 header (based on + * operation mode) */ + ethertype = (skb->data[12] << 8) | skb->data[13]; + /* TODO: handling for 802.1x authorized/unauthorized port */ + fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA; + + if (likely(sdata->type == IEEE80211_IF_TYPE_AP || + sdata->type == IEEE80211_IF_TYPE_VLAN)) { + fc |= IEEE80211_FCTL_FROMDS; + /* DA BSSID SA */ + memcpy(hdr.addr1, skb->data, ETH_ALEN); + memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN); + memcpy(hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN); + hdrlen = 24; + } else if (sdata->type == IEEE80211_IF_TYPE_WDS) { + fc |= IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS; + /* RA TA DA SA */ + memcpy(hdr.addr1, sdata->u.wds.remote_addr, ETH_ALEN); + memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN); + memcpy(hdr.addr3, skb->data, ETH_ALEN); + memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN); + hdrlen = 30; + } else if (sdata->type == IEEE80211_IF_TYPE_STA) { + fc |= IEEE80211_FCTL_TODS; + /* BSSID SA DA */ + memcpy(hdr.addr1, sdata->u.sta.bssid, ETH_ALEN); + memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); + memcpy(hdr.addr3, skb->data, ETH_ALEN); + hdrlen = 24; + } else if (sdata->type == IEEE80211_IF_TYPE_IBSS) { + /* DA SA BSSID */ + memcpy(hdr.addr1, skb->data, ETH_ALEN); + memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); + memcpy(hdr.addr3, sdata->u.sta.bssid, ETH_ALEN); + hdrlen = 24; + } else { + ret = 0; + goto fail; + } + + /* receiver is QoS enabled, use a QoS type frame */ + sta = sta_info_get(local, hdr.addr1); + if (sta) { + if (sta->flags & WLAN_STA_WME) { + fc |= IEEE80211_STYPE_QOS_DATA; + hdrlen += 2; + } + sta_info_put(sta); + } + + hdr.frame_control = cpu_to_le16(fc); + hdr.duration_id = 0; + hdr.seq_ctrl = 0; + + skip_header_bytes = ETH_HLEN; + if (ethertype == ETH_P_AARP || ethertype == ETH_P_IPX) { + encaps_data = bridge_tunnel_header; + encaps_len = sizeof(bridge_tunnel_header); + skip_header_bytes -= 2; + } else if (ethertype >= 0x600) { + encaps_data = rfc1042_header; + encaps_len = sizeof(rfc1042_header); + skip_header_bytes -= 2; + } else { + encaps_data = NULL; + encaps_len = 0; + } + + skb_pull(skb, skip_header_bytes); + nh_pos -= skip_header_bytes; + h_pos -= skip_header_bytes; + + /* TODO: implement support for fragments so that there is no need to + * reallocate and copy payload; it might be enough to support one + * extra fragment that would be copied in the beginning of the frame + * data.. anyway, it would be nice to include this into skb structure + * somehow + * + * There are few options for this: + * use skb->cb as an extra space for 802.11 header + * allocate new buffer if not enough headroom + * make sure that there is enough headroom in every skb by increasing + * build in headroom in __dev_alloc_skb() (linux/skbuff.h) and + * alloc_skb() (net/core/skbuff.c) + */ + head_need = hdrlen + encaps_len + local->hw.extra_tx_headroom; + head_need -= skb_headroom(skb); + + /* We are going to modify skb data, so make a copy of it if happens to + * be cloned. This could happen, e.g., with Linux bridge code passing + * us broadcast frames. */ + + if (head_need > 0 || skb_cloned(skb)) { +#if 0 + printk(KERN_DEBUG "%s: need to reallocate buffer for %d bytes " + "of headroom\n", dev->name, head_need); +#endif + + if (skb_cloned(skb)) + I802_DEBUG_INC(local->tx_expand_skb_head_cloned); + else + I802_DEBUG_INC(local->tx_expand_skb_head); + /* Since we have to reallocate the buffer, make sure that there + * is enough room for possible WEP IV/ICV and TKIP (8 bytes + * before payload and 12 after). */ + if (pskb_expand_head(skb, (head_need > 0 ? head_need + 8 : 8), + 12, GFP_ATOMIC)) { + printk(KERN_DEBUG "%s: failed to reallocate TX buffer" + "\n", dev->name); + goto fail; + } + } + + if (encaps_data) { + memcpy(skb_push(skb, encaps_len), encaps_data, encaps_len); + nh_pos += encaps_len; + h_pos += encaps_len; + } + memcpy(skb_push(skb, hdrlen), &hdr, hdrlen); + nh_pos += hdrlen; + h_pos += hdrlen; + + pkt_data = (struct ieee80211_tx_packet_data *)skb->cb; + memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data)); + pkt_data->ifindex = sdata->dev->ifindex; + pkt_data->mgmt_iface = (sdata->type == IEEE80211_IF_TYPE_MGMT); + pkt_data->do_not_encrypt = no_encrypt; + + skb->dev = local->mdev; + sdata->stats.tx_packets++; + sdata->stats.tx_bytes += skb->len; + + /* Update skb pointers to various headers since this modified frame + * is going to go through Linux networking code that may potentially + * need things like pointer to IP header. */ + skb_set_mac_header(skb, 0); + skb_set_network_header(skb, nh_pos); + skb_set_transport_header(skb, h_pos); + + dev->trans_start = jiffies; + dev_queue_xmit(skb); + + return 0; + + fail: + if (!ret) + dev_kfree_skb(skb); + + return ret; +} + + +/* + * This is the transmit routine for the 802.11 type interfaces + * called by upper layers of the linux networking + * stack when it has a frame to transmit + */ +static int +ieee80211_mgmt_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct ieee80211_sub_if_data *sdata; + struct ieee80211_tx_packet_data *pkt_data; + struct ieee80211_hdr *hdr; + u16 fc; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + if (skb->len < 10) { + dev_kfree_skb(skb); + return 0; + } + + if (skb_headroom(skb) < sdata->local->hw.extra_tx_headroom) { + if (pskb_expand_head(skb, + sdata->local->hw.extra_tx_headroom, 0, GFP_ATOMIC)) { + dev_kfree_skb(skb); + return 0; + } + } + + hdr = (struct ieee80211_hdr *) skb->data; + fc = le16_to_cpu(hdr->frame_control); + + pkt_data = (struct ieee80211_tx_packet_data *) skb->cb; + memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data)); + pkt_data->ifindex = sdata->dev->ifindex; + pkt_data->mgmt_iface = (sdata->type == IEEE80211_IF_TYPE_MGMT); + + skb->priority = 20; /* use hardcoded priority for mgmt TX queue */ + skb->dev = sdata->local->mdev; + + /* + * We're using the protocol field of the the frame control header + * to request TX callback for hostapd. BIT(1) is checked. + */ + if ((fc & BIT(1)) == BIT(1)) { + pkt_data->req_tx_status = 1; + fc &= ~BIT(1); + hdr->frame_control = cpu_to_le16(fc); + } + + pkt_data->do_not_encrypt = !(fc & IEEE80211_FCTL_PROTECTED); + + sdata->stats.tx_packets++; + sdata->stats.tx_bytes += skb->len; + + dev_queue_xmit(skb); + + return 0; +} + + +static void ieee80211_beacon_add_tim(struct ieee80211_local *local, + struct ieee80211_if_ap *bss, + struct sk_buff *skb) +{ + u8 *pos, *tim; + int aid0 = 0; + int i, have_bits = 0, n1, n2; + + /* Generate bitmap for TIM only if there are any STAs in power save + * mode. */ + spin_lock_bh(&local->sta_lock); + if (atomic_read(&bss->num_sta_ps) > 0) + /* in the hope that this is faster than + * checking byte-for-byte */ + have_bits = !bitmap_empty((unsigned long*)bss->tim, + IEEE80211_MAX_AID+1); + + if (bss->dtim_count == 0) + bss->dtim_count = bss->dtim_period - 1; + else + bss->dtim_count--; + + tim = pos = (u8 *) skb_put(skb, 6); + *pos++ = WLAN_EID_TIM; + *pos++ = 4; + *pos++ = bss->dtim_count; + *pos++ = bss->dtim_period; + + if (bss->dtim_count == 0 && !skb_queue_empty(&bss->ps_bc_buf)) + aid0 = 1; + + if (have_bits) { + /* Find largest even number N1 so that bits numbered 1 through + * (N1 x 8) - 1 in the bitmap are 0 and number N2 so that bits + * (N2 + 1) x 8 through 2007 are 0. */ + n1 = 0; + for (i = 0; i < IEEE80211_MAX_TIM_LEN; i++) { + if (bss->tim[i]) { + n1 = i & 0xfe; + break; + } + } + n2 = n1; + for (i = IEEE80211_MAX_TIM_LEN - 1; i >= n1; i--) { + if (bss->tim[i]) { + n2 = i; + break; + } + } + + /* Bitmap control */ + *pos++ = n1 | aid0; + /* Part Virt Bitmap */ + memcpy(pos, bss->tim + n1, n2 - n1 + 1); + + tim[1] = n2 - n1 + 4; + skb_put(skb, n2 - n1); + } else { + *pos++ = aid0; /* Bitmap control */ + *pos++ = 0; /* Part Virt Bitmap */ + } + spin_unlock_bh(&local->sta_lock); +} + + +struct sk_buff * ieee80211_beacon_get(struct ieee80211_hw *hw, int if_id, + struct ieee80211_tx_control *control) +{ + struct ieee80211_local *local = hw_to_local(hw); + struct sk_buff *skb; + struct net_device *bdev; + struct ieee80211_sub_if_data *sdata = NULL; + struct ieee80211_if_ap *ap = NULL; + struct ieee80211_rate *rate; + struct rate_control_extra extra; + u8 *b_head, *b_tail; + int bh_len, bt_len; + + bdev = dev_get_by_index(if_id); + if (bdev) { + sdata = IEEE80211_DEV_TO_SUB_IF(bdev); + ap = &sdata->u.ap; + dev_put(bdev); + } + + if (!ap || sdata->type != IEEE80211_IF_TYPE_AP || + !ap->beacon_head) { +#ifdef CONFIG_MAC80211_VERBOSE_DEBUG + if (net_ratelimit()) + printk(KERN_DEBUG "no beacon data avail for idx=%d " + "(%s)\n", if_id, bdev ? bdev->name : "N/A"); +#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ + return NULL; + } + + /* Assume we are generating the normal beacon locally */ + b_head = ap->beacon_head; + b_tail = ap->beacon_tail; + bh_len = ap->beacon_head_len; + bt_len = ap->beacon_tail_len; + + skb = dev_alloc_skb(local->hw.extra_tx_headroom + + bh_len + bt_len + 256 /* maximum TIM len */); + if (!skb) + return NULL; + + skb_reserve(skb, local->hw.extra_tx_headroom); + memcpy(skb_put(skb, bh_len), b_head, bh_len); + + ieee80211_include_sequence(sdata, (struct ieee80211_hdr *)skb->data); + + ieee80211_beacon_add_tim(local, ap, skb); + + if (b_tail) { + memcpy(skb_put(skb, bt_len), b_tail, bt_len); + } + + if (control) { + memset(&extra, 0, sizeof(extra)); + extra.mode = local->oper_hw_mode; + + rate = rate_control_get_rate(local, local->mdev, skb, &extra); + if (!rate) { + if (net_ratelimit()) { + printk(KERN_DEBUG "%s: ieee80211_beacon_get: no rate " + "found\n", local->mdev->name); + } + dev_kfree_skb(skb); + return NULL; + } + + control->tx_rate = (local->short_preamble && + (rate->flags & IEEE80211_RATE_PREAMBLE2)) ? + rate->val2 : rate->val; + control->antenna_sel_tx = local->hw.conf.antenna_sel_tx; + control->power_level = local->hw.conf.power_level; + control->flags |= IEEE80211_TXCTL_NO_ACK; + control->retry_limit = 1; + control->flags |= IEEE80211_TXCTL_CLEAR_DST_MASK; + } + + ap->num_beacons++; + return skb; +} +EXPORT_SYMBOL(ieee80211_beacon_get); + +__le16 ieee80211_rts_duration(struct ieee80211_hw *hw, + size_t frame_len, + const struct ieee80211_tx_control *frame_txctl) +{ + struct ieee80211_local *local = hw_to_local(hw); + struct ieee80211_rate *rate; + int short_preamble = local->short_preamble; + int erp; + u16 dur; + + rate = frame_txctl->rts_rate; + erp = !!(rate->flags & IEEE80211_RATE_ERP); + + /* CTS duration */ + dur = ieee80211_frame_duration(local, 10, rate->rate, + erp, short_preamble); + /* Data frame duration */ + dur += ieee80211_frame_duration(local, frame_len, rate->rate, + erp, short_preamble); + /* ACK duration */ + dur += ieee80211_frame_duration(local, 10, rate->rate, + erp, short_preamble); + + return cpu_to_le16(dur); +} +EXPORT_SYMBOL(ieee80211_rts_duration); + + +__le16 ieee80211_ctstoself_duration(struct ieee80211_hw *hw, + size_t frame_len, + const struct ieee80211_tx_control *frame_txctl) +{ + struct ieee80211_local *local = hw_to_local(hw); + struct ieee80211_rate *rate; + int short_preamble = local->short_preamble; + int erp; + u16 dur; + + rate = frame_txctl->rts_rate; + erp = !!(rate->flags & IEEE80211_RATE_ERP); + + /* Data frame duration */ + dur = ieee80211_frame_duration(local, frame_len, rate->rate, + erp, short_preamble); + if (!(frame_txctl->flags & IEEE80211_TXCTL_NO_ACK)) { + /* ACK duration */ + dur += ieee80211_frame_duration(local, 10, rate->rate, + erp, short_preamble); + } + + return cpu_to_le16(dur); +} +EXPORT_SYMBOL(ieee80211_ctstoself_duration); + +void ieee80211_rts_get(struct ieee80211_hw *hw, + const void *frame, size_t frame_len, + const struct ieee80211_tx_control *frame_txctl, + struct ieee80211_rts *rts) +{ + const struct ieee80211_hdr *hdr = frame; + u16 fctl; + + fctl = IEEE80211_FTYPE_CTL | IEEE80211_STYPE_RTS; + rts->frame_control = cpu_to_le16(fctl); + rts->duration = ieee80211_rts_duration(hw, frame_len, frame_txctl); + memcpy(rts->ra, hdr->addr1, sizeof(rts->ra)); + memcpy(rts->ta, hdr->addr2, sizeof(rts->ta)); +} +EXPORT_SYMBOL(ieee80211_rts_get); + +void ieee80211_ctstoself_get(struct ieee80211_hw *hw, + const void *frame, size_t frame_len, + const struct ieee80211_tx_control *frame_txctl, + struct ieee80211_cts *cts) +{ + const struct ieee80211_hdr *hdr = frame; + u16 fctl; + + fctl = IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CTS; + cts->frame_control = cpu_to_le16(fctl); + cts->duration = ieee80211_ctstoself_duration(hw, frame_len, frame_txctl); + memcpy(cts->ra, hdr->addr1, sizeof(cts->ra)); +} +EXPORT_SYMBOL(ieee80211_ctstoself_get); + +struct sk_buff * +ieee80211_get_buffered_bc(struct ieee80211_hw *hw, int if_id, + struct ieee80211_tx_control *control) +{ + struct ieee80211_local *local = hw_to_local(hw); + struct sk_buff *skb; + struct sta_info *sta; + ieee80211_tx_handler *handler; + struct ieee80211_txrx_data tx; + ieee80211_txrx_result res = TXRX_DROP; + struct net_device *bdev; + struct ieee80211_sub_if_data *sdata; + struct ieee80211_if_ap *bss = NULL; + + bdev = dev_get_by_index(if_id); + if (bdev) { + sdata = IEEE80211_DEV_TO_SUB_IF(bdev); + bss = &sdata->u.ap; + dev_put(bdev); + } + if (!bss || sdata->type != IEEE80211_IF_TYPE_AP || !bss->beacon_head) + return NULL; + + if (bss->dtim_count != 0) + return NULL; /* send buffered bc/mc only after DTIM beacon */ + memset(control, 0, sizeof(*control)); + while (1) { + skb = skb_dequeue(&bss->ps_bc_buf); + if (!skb) + return NULL; + local->total_ps_buffered--; + + if (!skb_queue_empty(&bss->ps_bc_buf) && skb->len >= 2) { + struct ieee80211_hdr *hdr = + (struct ieee80211_hdr *) skb->data; + /* more buffered multicast/broadcast frames ==> set + * MoreData flag in IEEE 802.11 header to inform PS + * STAs */ + hdr->frame_control |= + cpu_to_le16(IEEE80211_FCTL_MOREDATA); + } + + if (ieee80211_tx_prepare(&tx, skb, local->mdev, control) == 0) + break; + dev_kfree_skb_any(skb); + } + sta = tx.sta; + tx.u.tx.ps_buffered = 1; + + for (handler = local->tx_handlers; *handler != NULL; handler++) { + res = (*handler)(&tx); + if (res == TXRX_DROP || res == TXRX_QUEUED) + break; + } + dev_put(tx.dev); + skb = tx.skb; /* handlers are allowed to change skb */ + + if (res == TXRX_DROP) { + I802_DEBUG_INC(local->tx_handlers_drop); + dev_kfree_skb(skb); + skb = NULL; + } else if (res == TXRX_QUEUED) { + I802_DEBUG_INC(local->tx_handlers_queued); + skb = NULL; + } + + if (sta) + sta_info_put(sta); + + return skb; +} +EXPORT_SYMBOL(ieee80211_get_buffered_bc); + +static int __ieee80211_if_config(struct net_device *dev, + struct sk_buff *beacon, + struct ieee80211_tx_control *control) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_if_conf conf; + static u8 scan_bssid[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + if (!local->ops->config_interface || !netif_running(dev)) + return 0; + + memset(&conf, 0, sizeof(conf)); + conf.type = sdata->type; + if (sdata->type == IEEE80211_IF_TYPE_STA || + sdata->type == IEEE80211_IF_TYPE_IBSS) { + if (local->sta_scanning && + local->scan_dev == dev) + conf.bssid = scan_bssid; + else + conf.bssid = sdata->u.sta.bssid; + conf.ssid = sdata->u.sta.ssid; + conf.ssid_len = sdata->u.sta.ssid_len; + conf.generic_elem = sdata->u.sta.extra_ie; + conf.generic_elem_len = sdata->u.sta.extra_ie_len; + } else if (sdata->type == IEEE80211_IF_TYPE_AP) { + conf.ssid = sdata->u.ap.ssid; + conf.ssid_len = sdata->u.ap.ssid_len; + conf.generic_elem = sdata->u.ap.generic_elem; + conf.generic_elem_len = sdata->u.ap.generic_elem_len; + conf.beacon = beacon; + conf.beacon_control = control; + } + return local->ops->config_interface(local_to_hw(local), + dev->ifindex, &conf); +} + +int ieee80211_if_config(struct net_device *dev) +{ + return __ieee80211_if_config(dev, NULL, NULL); +} + +int ieee80211_if_config_beacon(struct net_device *dev) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_tx_control control; + struct sk_buff *skb; + + if (!(local->hw.flags & IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE)) + return 0; + skb = ieee80211_beacon_get(local_to_hw(local), dev->ifindex, &control); + if (!skb) + return -ENOMEM; + return __ieee80211_if_config(dev, skb, &control); +} + +int ieee80211_hw_config(struct ieee80211_local *local) +{ + struct ieee80211_hw_mode *mode; + struct ieee80211_channel *chan; + int ret = 0; + + if (local->sta_scanning) { + chan = local->scan_channel; + mode = local->scan_hw_mode; + } else { + chan = local->oper_channel; + mode = local->oper_hw_mode; + } + + local->hw.conf.channel = chan->chan; + local->hw.conf.channel_val = chan->val; + local->hw.conf.power_level = chan->power_level; + local->hw.conf.freq = chan->freq; + local->hw.conf.phymode = mode->mode; + local->hw.conf.antenna_max = chan->antenna_max; + local->hw.conf.chan = chan; + local->hw.conf.mode = mode; + +#ifdef CONFIG_MAC80211_VERBOSE_DEBUG + printk(KERN_DEBUG "HW CONFIG: channel=%d freq=%d " + "phymode=%d\n", local->hw.conf.channel, local->hw.conf.freq, + local->hw.conf.phymode); +#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ + + if (local->ops->config) + ret = local->ops->config(local_to_hw(local), &local->hw.conf); + + return ret; +} + + +static int ieee80211_change_mtu(struct net_device *dev, int new_mtu) +{ + /* FIX: what would be proper limits for MTU? + * This interface uses 802.3 frames. */ + if (new_mtu < 256 || new_mtu > IEEE80211_MAX_DATA_LEN - 24 - 6) { + printk(KERN_WARNING "%s: invalid MTU %d\n", + dev->name, new_mtu); + return -EINVAL; + } + +#ifdef CONFIG_MAC80211_VERBOSE_DEBUG + printk(KERN_DEBUG "%s: setting MTU %d\n", dev->name, new_mtu); +#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ + dev->mtu = new_mtu; + return 0; +} + + +static int ieee80211_change_mtu_apdev(struct net_device *dev, int new_mtu) +{ + /* FIX: what would be proper limits for MTU? + * This interface uses 802.11 frames. */ + if (new_mtu < 256 || new_mtu > IEEE80211_MAX_DATA_LEN) { + printk(KERN_WARNING "%s: invalid MTU %d\n", + dev->name, new_mtu); + return -EINVAL; + } + +#ifdef CONFIG_MAC80211_VERBOSE_DEBUG + printk(KERN_DEBUG "%s: setting MTU %d\n", dev->name, new_mtu); +#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ + dev->mtu = new_mtu; + return 0; +} + +enum netif_tx_lock_class { + TX_LOCK_NORMAL, + TX_LOCK_MASTER, +} + +static inline void netif_tx_lock_nested(struct net_device *dev, int subclass) +{ + spin_lock_nested(&dev->_xmit_lock, subclass); + dev->xmit_lock_owner = smp_processor_id(); +} + +static void ieee80211_set_multicast_list(struct net_device *dev) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + unsigned short flags; + + netif_tx_lock_nested(local->mdev, TX_LOCK_MASTER); + if (((dev->flags & IFF_ALLMULTI) != 0) ^ (sdata->allmulti != 0)) { + if (sdata->allmulti) { + sdata->allmulti = 0; + local->iff_allmultis--; + } else { + sdata->allmulti = 1; + local->iff_allmultis++; + } + } + if (((dev->flags & IFF_PROMISC) != 0) ^ (sdata->promisc != 0)) { + if (sdata->promisc) { + sdata->promisc = 0; + local->iff_promiscs--; + } else { + sdata->promisc = 1; + local->iff_promiscs++; + } + } + if (dev->mc_count != sdata->mc_count) { + local->mc_count = local->mc_count - sdata->mc_count + + dev->mc_count; + sdata->mc_count = dev->mc_count; + } + if (local->ops->set_multicast_list) { + flags = local->mdev->flags; + if (local->iff_allmultis) + flags |= IFF_ALLMULTI; + if (local->iff_promiscs) + flags |= IFF_PROMISC; + read_lock(&local->sub_if_lock); + local->ops->set_multicast_list(local_to_hw(local), flags, + local->mc_count); + read_unlock(&local->sub_if_lock); + } + netif_tx_unlock(local->mdev); +} + +struct dev_mc_list *ieee80211_get_mc_list_item(struct ieee80211_hw *hw, + struct dev_mc_list *prev, + void **ptr) +{ + struct ieee80211_local *local = hw_to_local(hw); + struct ieee80211_sub_if_data *sdata = *ptr; + struct dev_mc_list *mc; + + if (!prev) { + WARN_ON(sdata); + sdata = NULL; + } + if (!prev || !prev->next) { + if (sdata) + sdata = list_entry(sdata->list.next, + struct ieee80211_sub_if_data, list); + else + sdata = list_entry(local->sub_if_list.next, + struct ieee80211_sub_if_data, list); + if (&sdata->list != &local->sub_if_list) + mc = sdata->dev->mc_list; + else + mc = NULL; + } else + mc = prev->next; + + *ptr = sdata; + return mc; +} +EXPORT_SYMBOL(ieee80211_get_mc_list_item); + +static struct net_device_stats *ieee80211_get_stats(struct net_device *dev) +{ + struct ieee80211_sub_if_data *sdata; + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + return &(sdata->stats); +} + +static void ieee80211_if_shutdown(struct net_device *dev) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + ASSERT_RTNL(); + switch (sdata->type) { + case IEEE80211_IF_TYPE_STA: + case IEEE80211_IF_TYPE_IBSS: + sdata->u.sta.state = IEEE80211_DISABLED; + del_timer_sync(&sdata->u.sta.timer); + skb_queue_purge(&sdata->u.sta.skb_queue); + if (!local->ops->hw_scan && + local->scan_dev == sdata->dev) { + local->sta_scanning = 0; + cancel_delayed_work(&local->scan_work); + } + flush_workqueue(local->hw.workqueue); + break; + } +} + +static inline int identical_mac_addr_allowed(int type1, int type2) +{ + return (type1 == IEEE80211_IF_TYPE_MNTR || + type2 == IEEE80211_IF_TYPE_MNTR || + (type1 == IEEE80211_IF_TYPE_AP && + type2 == IEEE80211_IF_TYPE_WDS) || + (type1 == IEEE80211_IF_TYPE_WDS && + (type2 == IEEE80211_IF_TYPE_WDS || + type2 == IEEE80211_IF_TYPE_AP)) || + (type1 == IEEE80211_IF_TYPE_AP && + type2 == IEEE80211_IF_TYPE_VLAN) || + (type1 == IEEE80211_IF_TYPE_VLAN && + (type2 == IEEE80211_IF_TYPE_AP || + type2 == IEEE80211_IF_TYPE_VLAN))); +} + +static int ieee80211_master_open(struct net_device *dev) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_sub_if_data *sdata; + int res = -EOPNOTSUPP; + + if (!ieee80211_qdisc_installed(dev)) { + printk(KERN_ERR "%s: ieee80211 qdisc not installed\n", + dev->name); + return res; + } + + read_lock(&local->sub_if_lock); + list_for_each_entry(sdata, &local->sub_if_list, list) { + if (sdata->dev != dev && netif_running(sdata->dev)) { + res = 0; + break; + } + } + read_unlock(&local->sub_if_lock); + return res; +} + +static int ieee80211_master_stop(struct net_device *dev) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_sub_if_data *sdata; + + read_lock(&local->sub_if_lock); + list_for_each_entry(sdata, &local->sub_if_list, list) + if (sdata->dev != dev && netif_running(sdata->dev)) + dev_close(sdata->dev); + read_unlock(&local->sub_if_lock); + + return 0; +} + +static int ieee80211_mgmt_open(struct net_device *dev) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + + if (!netif_running(local->mdev)) + return -EOPNOTSUPP; + return 0; +} + +static int ieee80211_mgmt_stop(struct net_device *dev) +{ + return 0; +} + +/* Check if running monitor interfaces should go to a "soft monitor" mode + * and switch them if necessary. */ +static inline void ieee80211_start_soft_monitor(struct ieee80211_local *local) +{ + struct ieee80211_if_init_conf conf; + + if (local->open_count && local->open_count == local->monitors && + !(local->hw.flags & IEEE80211_HW_MONITOR_DURING_OPER) && + local->ops->remove_interface) { + conf.if_id = -1; + conf.type = IEEE80211_IF_TYPE_MNTR; + conf.mac_addr = NULL; + local->ops->remove_interface(local_to_hw(local), &conf); + } +} + +/* Check if running monitor interfaces should go to a "hard monitor" mode + * and switch them if necessary. */ +static void ieee80211_start_hard_monitor(struct ieee80211_local *local) +{ + struct ieee80211_if_init_conf conf; + + if (local->open_count && local->open_count == local->monitors && + !(local->hw.flags & IEEE80211_HW_MONITOR_DURING_OPER) && + local->ops->add_interface) { + conf.if_id = -1; + conf.type = IEEE80211_IF_TYPE_MNTR; + conf.mac_addr = NULL; + local->ops->add_interface(local_to_hw(local), &conf); + } +} + +static int ieee80211_open(struct net_device *dev) +{ + struct ieee80211_sub_if_data *sdata, *nsdata; + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_if_init_conf conf; + int res; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + read_lock(&local->sub_if_lock); + list_for_each_entry(nsdata, &local->sub_if_list, list) { + struct net_device *ndev = nsdata->dev; + + if (ndev != dev && ndev != local->mdev && netif_running(ndev) && + compare_ether_addr(dev->dev_addr, ndev->dev_addr) == 0 && + !identical_mac_addr_allowed(sdata->type, nsdata->type)) { + read_unlock(&local->sub_if_lock); + return -ENOTUNIQ; + } + } + read_unlock(&local->sub_if_lock); + + if (sdata->type == IEEE80211_IF_TYPE_WDS && + is_zero_ether_addr(sdata->u.wds.remote_addr)) + return -ENOLINK; + + if (sdata->type == IEEE80211_IF_TYPE_MNTR && local->open_count && + !(local->hw.flags & IEEE80211_HW_MONITOR_DURING_OPER)) { + /* run the interface in a "soft monitor" mode */ + local->monitors++; + local->open_count++; + local->hw.conf.flags |= IEEE80211_CONF_RADIOTAP; + return 0; + } + ieee80211_start_soft_monitor(local); + + if (local->ops->add_interface) { + conf.if_id = dev->ifindex; + conf.type = sdata->type; + conf.mac_addr = dev->dev_addr; + res = local->ops->add_interface(local_to_hw(local), &conf); + if (res) { + if (sdata->type == IEEE80211_IF_TYPE_MNTR) + ieee80211_start_hard_monitor(local); + return res; + } + } else { + if (sdata->type != IEEE80211_IF_TYPE_STA) + return -EOPNOTSUPP; + if (local->open_count > 0) + return -ENOBUFS; + } + + if (local->open_count == 0) { + res = 0; + tasklet_enable(&local->tx_pending_tasklet); + tasklet_enable(&local->tasklet); + if (local->ops->open) + res = local->ops->open(local_to_hw(local)); + if (res == 0) { + res = dev_open(local->mdev); + if (res) { + if (local->ops->stop) + local->ops->stop(local_to_hw(local)); + } else { + res = ieee80211_hw_config(local); + if (res && local->ops->stop) + local->ops->stop(local_to_hw(local)); + else if (!res && local->apdev) + dev_open(local->apdev); + } + } + if (res) { + if (local->ops->remove_interface) + local->ops->remove_interface(local_to_hw(local), + &conf); + return res; + } + } + local->open_count++; + + if (sdata->type == IEEE80211_IF_TYPE_MNTR) { + local->monitors++; + local->hw.conf.flags |= IEEE80211_CONF_RADIOTAP; + } else + ieee80211_if_config(dev); + + if (sdata->type == IEEE80211_IF_TYPE_STA && + !local->user_space_mlme) + netif_carrier_off(dev); + + netif_start_queue(dev); + return 0; +} + + +static int ieee80211_stop(struct net_device *dev) +{ + struct ieee80211_sub_if_data *sdata; + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + if (sdata->type == IEEE80211_IF_TYPE_MNTR && + local->open_count > 1 && + !(local->hw.flags & IEEE80211_HW_MONITOR_DURING_OPER)) { + /* remove "soft monitor" interface */ + local->open_count--; + local->monitors--; + if (!local->monitors) + local->hw.conf.flags &= ~IEEE80211_CONF_RADIOTAP; + return 0; + } + + netif_stop_queue(dev); + ieee80211_if_shutdown(dev); + + if (sdata->type == IEEE80211_IF_TYPE_MNTR) { + local->monitors--; + if (!local->monitors) + local->hw.conf.flags &= ~IEEE80211_CONF_RADIOTAP; + } + + local->open_count--; + if (local->open_count == 0) { + if (netif_running(local->mdev)) + dev_close(local->mdev); + if (local->apdev) + dev_close(local->apdev); + if (local->ops->stop) + local->ops->stop(local_to_hw(local)); + tasklet_disable(&local->tx_pending_tasklet); + tasklet_disable(&local->tasklet); + } + if (local->ops->remove_interface) { + struct ieee80211_if_init_conf conf; + + conf.if_id = dev->ifindex; + conf.type = sdata->type; + conf.mac_addr = dev->dev_addr; + local->ops->remove_interface(local_to_hw(local), &conf); + } + + ieee80211_start_hard_monitor(local); + + return 0; +} + + +static int header_parse_80211(struct sk_buff *skb, unsigned char *haddr) +{ + memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN); /* addr2 */ + return ETH_ALEN; +} + +static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr) +{ + return compare_ether_addr(raddr, addr) == 0 || + is_broadcast_ether_addr(raddr); +} + + +inline static unsigned int calc_pad_len(unsigned int len) +{ + return ((4 - len) & 0x3); +} + +static ieee80211_txrx_result +ieee80211_rx_h_data_agg(struct ieee80211_txrx_data *rx) +{ + struct net_device *dev = rx->dev; + struct ieee80211_local *local = rx->local; + u16 fc, hdrlen, ethertype; + u8 *payload; + struct sk_buff *skb = rx->skb, *skb2, *frame; + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + const struct ethhdr* eth; + int remaining; + + fc = rx->fc; + if (unlikely((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA)) + return TXRX_CONTINUE; + + if (unlikely(!WLAN_FC_DATA_PRESENT(fc))) + return TXRX_DROP; + + if (!rx->u.rx.is_agg_frame) + return TXRX_CONTINUE; + + hdrlen = ieee80211_get_hdrlen(fc); + + payload = skb->data + hdrlen; + + if (unlikely((skb->len - hdrlen) < 8)) { + if (net_ratelimit()) + printk(KERN_DEBUG "%s: RX too short data frame " + "payload\n", dev->name); + return TXRX_DROP; + } + + ethertype = (payload[6] << 8) | payload[7]; + + if (likely((compare_ether_addr(payload, rfc1042_header) == 0 && + ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) || + compare_ether_addr(payload, bridge_tunnel_header) == 0)) { + /* remove RFC1042 or Bridge-Tunnel encapsulation and + * replace EtherType */ + eth = (struct ethhdr*) (skb->data + hdrlen + 6); + remaining = skb->len - (hdrlen + 6); + } else { + eth = (struct ethhdr*) (skb->data + hdrlen); + remaining = skb->len - hdrlen; + } + + while ((u8*)eth < skb->data + skb->len) { + u8 padding; + unsigned int subframe_len = sizeof(struct ethhdr) + + ntohs(eth->h_proto); + + padding = calc_pad_len(subframe_len); + /* the last MSDU has no padding */ + if (subframe_len > remaining) + return TXRX_DROP; + + frame = dev_alloc_skb(local->hw.extra_tx_headroom + + subframe_len); + + if (frame == NULL) + return TXRX_DROP; + + memcpy(skb_put(frame, subframe_len), (u8*)eth, subframe_len); + skb_set_mac_header(frame, 0); + skb2 = NULL; + + sdata->stats.rx_packets++; + sdata->stats.rx_bytes += frame->len; + + if (local->bridge_packets && + (sdata->type == IEEE80211_IF_TYPE_AP || + sdata->type == IEEE80211_IF_TYPE_VLAN) && + rx->u.rx.ra_match) { + if (is_multicast_ether_addr(frame->data)) { + /* send multicast frames both to higher layers + * in local net stack and back to the wireless + * media */ + skb2 = skb_copy(frame, GFP_ATOMIC); + if (!skb2) + printk(KERN_DEBUG "%s: failed to clone" + " multicast frame\n", dev->name); + } else { + struct sta_info *dsta; + + dsta = sta_info_get(local, frame->data); + if (dsta && !dsta->dev) + printk(KERN_DEBUG "Station with null " + "dev structure!\n"); + else if (dsta && dsta->dev == dev) { + /* Destination station is associated + * to this AP, so send the frame + * directly to it and do not pass + * the frame to local net stack. + */ + skb2 = frame; + frame = NULL; + } + if (dsta) + sta_info_put(dsta); + } + } + if (frame) { + /* deliver to local stack */ + frame->protocol = eth_type_trans(frame, dev); + frame->priority = skb->priority; + frame->dev = dev; + netif_rx(frame); + } + + if (skb2) { + /* send to wireless media */ + skb2->protocol = __constant_htons(ETH_P_802_3); + skb_set_network_header(skb2, 0); + skb_set_mac_header(skb2, 0); + skb2->priority = skb->priority; + skb2->dev = dev; + dev_queue_xmit(skb2); + } + + eth = (struct ethhdr*)((u8*)eth + subframe_len + padding); + + remaining -= (subframe_len + padding); + } + + dev_kfree_skb(skb); + return TXRX_QUEUED; +} + +static ieee80211_txrx_result +ieee80211_rx_h_data(struct ieee80211_txrx_data *rx) +{ + struct net_device *dev = rx->dev; + struct ieee80211_local *local = rx->local; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data; + u16 fc, hdrlen, ethertype; + u8 *payload; + u8 dst[ETH_ALEN]; + u8 src[ETH_ALEN]; + struct sk_buff *skb = rx->skb, *skb2; + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + fc = rx->fc; + if (unlikely((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA)) + return TXRX_CONTINUE; + + if (unlikely(!WLAN_FC_DATA_PRESENT(fc))) + return TXRX_DROP; + + hdrlen = ieee80211_get_hdrlen(fc); + + /* convert IEEE 802.11 header + possible LLC headers into Ethernet + * header + * IEEE 802.11 address fields: + * ToDS FromDS Addr1 Addr2 Addr3 Addr4 + * 0 0 DA SA BSSID n/a + * 0 1 DA BSSID SA n/a + * 1 0 BSSID SA DA n/a + * 1 1 RA TA DA SA + */ + + switch (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) { + case IEEE80211_FCTL_TODS: + /* BSSID SA DA */ + memcpy(dst, hdr->addr3, ETH_ALEN); + memcpy(src, hdr->addr2, ETH_ALEN); + + if (unlikely(sdata->type != IEEE80211_IF_TYPE_AP && + sdata->type != IEEE80211_IF_TYPE_VLAN)) { + printk(KERN_DEBUG "%s: dropped ToDS frame (BSSID=" + MAC_FMT " SA=" MAC_FMT " DA=" MAC_FMT ")\n", + dev->name, MAC_ARG(hdr->addr1), + MAC_ARG(hdr->addr2), MAC_ARG(hdr->addr3)); + return TXRX_DROP; + } + break; + case (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS): + /* RA TA DA SA */ + memcpy(dst, hdr->addr3, ETH_ALEN); + memcpy(src, hdr->addr4, ETH_ALEN); + + if (unlikely(sdata->type != IEEE80211_IF_TYPE_WDS)) { + printk(KERN_DEBUG "%s: dropped FromDS&ToDS frame (RA=" + MAC_FMT " TA=" MAC_FMT " DA=" MAC_FMT " SA=" + MAC_FMT ")\n", + rx->dev->name, MAC_ARG(hdr->addr1), + MAC_ARG(hdr->addr2), MAC_ARG(hdr->addr3), + MAC_ARG(hdr->addr4)); + return TXRX_DROP; + } + break; + case IEEE80211_FCTL_FROMDS: + /* DA BSSID SA */ + memcpy(dst, hdr->addr1, ETH_ALEN); + memcpy(src, hdr->addr3, ETH_ALEN); + + if (sdata->type != IEEE80211_IF_TYPE_STA) { + return TXRX_DROP; + } + break; + case 0: + /* DA SA BSSID */ + memcpy(dst, hdr->addr1, ETH_ALEN); + memcpy(src, hdr->addr2, ETH_ALEN); + + if (sdata->type != IEEE80211_IF_TYPE_IBSS) { + if (net_ratelimit()) { + printk(KERN_DEBUG "%s: dropped IBSS frame (DA=" + MAC_FMT " SA=" MAC_FMT " BSSID=" MAC_FMT + ")\n", + dev->name, MAC_ARG(hdr->addr1), + MAC_ARG(hdr->addr2), + MAC_ARG(hdr->addr3)); + } + return TXRX_DROP; + } + break; + } + + payload = skb->data + hdrlen; + + if (unlikely(skb->len - hdrlen < 8)) { + if (net_ratelimit()) { + printk(KERN_DEBUG "%s: RX too short data frame " + "payload\n", dev->name); + } + return TXRX_DROP; + } + + ethertype = (payload[6] << 8) | payload[7]; + + if (likely((compare_ether_addr(payload, rfc1042_header) == 0 && + ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) || + compare_ether_addr(payload, bridge_tunnel_header) == 0)) { + /* remove RFC1042 or Bridge-Tunnel encapsulation and + * replace EtherType */ + skb_pull(skb, hdrlen + 6); + memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN); + memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN); + } else { + struct ethhdr *ehdr; + __be16 len; + skb_pull(skb, hdrlen); + len = htons(skb->len); + ehdr = (struct ethhdr *) skb_push(skb, sizeof(struct ethhdr)); + memcpy(ehdr->h_dest, dst, ETH_ALEN); + memcpy(ehdr->h_source, src, ETH_ALEN); + ehdr->h_proto = len; + } + skb->dev = dev; + + skb2 = NULL; + + sdata->stats.rx_packets++; + sdata->stats.rx_bytes += skb->len; + + if (local->bridge_packets && (sdata->type == IEEE80211_IF_TYPE_AP + || sdata->type == IEEE80211_IF_TYPE_VLAN) && rx->u.rx.ra_match) { + if (is_multicast_ether_addr(skb->data)) { + /* send multicast frames both to higher layers in + * local net stack and back to the wireless media */ + skb2 = skb_copy(skb, GFP_ATOMIC); + if (!skb2) + printk(KERN_DEBUG "%s: failed to clone " + "multicast frame\n", dev->name); + } else { + struct sta_info *dsta; + dsta = sta_info_get(local, skb->data); + if (dsta && !dsta->dev) { + printk(KERN_DEBUG "Station with null dev " + "structure!\n"); + } else if (dsta && dsta->dev == dev) { + /* Destination station is associated to this + * AP, so send the frame directly to it and + * do not pass the frame to local net stack. + */ + skb2 = skb; + skb = NULL; + } + if (dsta) + sta_info_put(dsta); + } + } + + if (skb) { + /* deliver to local stack */ + skb->protocol = eth_type_trans(skb, dev); + memset(skb->cb, 0, sizeof(skb->cb)); + netif_rx(skb); + } + + if (skb2) { + /* send to wireless media */ + skb2->protocol = __constant_htons(ETH_P_802_3); + skb_set_network_header(skb2, 0); + skb_set_mac_header(skb2, 0); + dev_queue_xmit(skb2); + } + + return TXRX_QUEUED; +} + + +static struct ieee80211_rate * +ieee80211_get_rate(struct ieee80211_local *local, int phymode, int hw_rate) +{ + struct ieee80211_hw_mode *mode; + int r; + + list_for_each_entry(mode, &local->modes_list, list) { + if (mode->mode != phymode) + continue; + for (r = 0; r < mode->num_rates; r++) { + struct ieee80211_rate *rate = &mode->rates[r]; + if (rate->val == hw_rate || + (rate->flags & IEEE80211_RATE_PREAMBLE2 && + rate->val2 == hw_rate)) + return rate; + } + } + + return NULL; +} + +static void +ieee80211_fill_frame_info(struct ieee80211_local *local, + struct ieee80211_frame_info *fi, + struct ieee80211_rx_status *status) +{ + if (status) { + struct timespec ts; + struct ieee80211_rate *rate; + + jiffies_to_timespec(jiffies, &ts); + fi->hosttime = cpu_to_be64((u64) ts.tv_sec * 1000000 + + ts.tv_nsec / 1000); + fi->mactime = cpu_to_be64(status->mactime); + switch (status->phymode) { + case MODE_IEEE80211A: + fi->phytype = htonl(ieee80211_phytype_ofdm_dot11_a); + break; + case MODE_IEEE80211B: + fi->phytype = htonl(ieee80211_phytype_dsss_dot11_b); + break; + case MODE_IEEE80211G: + fi->phytype = htonl(ieee80211_phytype_pbcc_dot11_g); + break; + case MODE_ATHEROS_TURBO: + fi->phytype = + htonl(ieee80211_phytype_dsss_dot11_turbo); + break; + default: + fi->phytype = htonl(0xAAAAAAAA); + break; + } + fi->channel = htonl(status->channel); + rate = ieee80211_get_rate(local, status->phymode, + status->rate); + if (rate) { + fi->datarate = htonl(rate->rate); + if (rate->flags & IEEE80211_RATE_PREAMBLE2) { + if (status->rate == rate->val) + fi->preamble = htonl(2); /* long */ + else if (status->rate == rate->val2) + fi->preamble = htonl(1); /* short */ + } else + fi->preamble = htonl(0); + } else { + fi->datarate = htonl(0); + fi->preamble = htonl(0); + } + + fi->antenna = htonl(status->antenna); + fi->priority = htonl(0xffffffff); /* no clue */ + fi->ssi_type = htonl(ieee80211_ssi_raw); + fi->ssi_signal = htonl(status->ssi); + fi->ssi_noise = 0x00000000; + fi->encoding = 0; + } else { + /* clear everything because we really don't know. + * the msg_type field isn't present on monitor frames + * so we don't know whether it will be present or not, + * but it's ok to not clear it since it'll be assigned + * anyway */ + memset(fi, 0, sizeof(*fi) - sizeof(fi->msg_type)); + + fi->ssi_type = htonl(ieee80211_ssi_none); + } + fi->version = htonl(IEEE80211_FI_VERSION); + fi->length = cpu_to_be32(sizeof(*fi) - sizeof(fi->msg_type)); +} + +/* this routine is actually not just for this, but also + * for pushing fake 'management' frames into userspace. + * it shall be replaced by a netlink-based system. */ +void +ieee80211_rx_mgmt(struct ieee80211_local *local, struct sk_buff *skb, + struct ieee80211_rx_status *status, u32 msg_type) +{ + struct ieee80211_frame_info *fi; + const size_t hlen = sizeof(struct ieee80211_frame_info); + struct ieee80211_sub_if_data *sdata; + + skb->dev = local->apdev; + + sdata = IEEE80211_DEV_TO_SUB_IF(local->apdev); + + if (skb_headroom(skb) < hlen) { + I802_DEBUG_INC(local->rx_expand_skb_head); + if (pskb_expand_head(skb, hlen, 0, GFP_ATOMIC)) { + dev_kfree_skb(skb); + return; + } + } + + fi = (struct ieee80211_frame_info *) skb_push(skb, hlen); + + ieee80211_fill_frame_info(local, fi, status); + fi->msg_type = htonl(msg_type); + + sdata->stats.rx_packets++; + sdata->stats.rx_bytes += skb->len; + + skb_set_mac_header(skb, 0); + skb->ip_summed = CHECKSUM_UNNECESSARY; + skb->pkt_type = PACKET_OTHERHOST; + skb->protocol = htons(ETH_P_802_2); + memset(skb->cb, 0, sizeof(skb->cb)); + netif_rx(skb); +} + +static void +ieee80211_rx_monitor(struct net_device *dev, struct sk_buff *skb, + struct ieee80211_rx_status *status) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_sub_if_data *sdata; + struct ieee80211_rate *rate; + struct ieee80211_rtap_hdr { + struct ieee80211_radiotap_header hdr; + u8 flags; + u8 rate; + __le16 chan_freq; + __le16 chan_flags; + u8 antsignal; + } __attribute__ ((packed)) *rthdr; + + skb->dev = dev; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + if (status->flag & RX_FLAG_RADIOTAP) + goto out; + + if (skb_headroom(skb) < sizeof(*rthdr)) { + I802_DEBUG_INC(local->rx_expand_skb_head); + if (pskb_expand_head(skb, sizeof(*rthdr), 0, GFP_ATOMIC)) { + dev_kfree_skb(skb); + return; + } + } + + rthdr = (struct ieee80211_rtap_hdr *) skb_push(skb, sizeof(*rthdr)); + memset(rthdr, 0, sizeof(*rthdr)); + rthdr->hdr.it_len = cpu_to_le16(sizeof(*rthdr)); + rthdr->hdr.it_present = + cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) | + (1 << IEEE80211_RADIOTAP_RATE) | + (1 << IEEE80211_RADIOTAP_CHANNEL) | + (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL)); + rthdr->flags = local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS ? + IEEE80211_RADIOTAP_F_FCS : 0; + rate = ieee80211_get_rate(local, status->phymode, status->rate); + if (rate) + rthdr->rate = rate->rate / 5; + rthdr->chan_freq = cpu_to_le16(status->freq); + rthdr->chan_flags = + status->phymode == MODE_IEEE80211A ? + cpu_to_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ) : + cpu_to_le16(IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ); + rthdr->antsignal = status->ssi; + + out: + sdata->stats.rx_packets++; + sdata->stats.rx_bytes += skb->len; + + skb_set_mac_header(skb, 0); + skb->ip_summed = CHECKSUM_UNNECESSARY; + skb->pkt_type = PACKET_OTHERHOST; + skb->protocol = htons(ETH_P_802_2); + memset(skb->cb, 0, sizeof(skb->cb)); + netif_rx(skb); +} + +int ieee80211_radar_status(struct ieee80211_hw *hw, int channel, + int radar, int radar_type) +{ + struct sk_buff *skb; + struct ieee80211_radar_info *msg; + struct ieee80211_local *local = hw_to_local(hw); + + if (!local->apdev) + return 0; + + skb = dev_alloc_skb(sizeof(struct ieee80211_frame_info) + + sizeof(struct ieee80211_radar_info)); + + if (!skb) + return -ENOMEM; + skb_reserve(skb, sizeof(struct ieee80211_frame_info)); + + msg = (struct ieee80211_radar_info *) + skb_put(skb, sizeof(struct ieee80211_radar_info)); + msg->channel = channel; + msg->radar = radar; + msg->radar_type = radar_type; + + ieee80211_rx_mgmt(local, skb, NULL, ieee80211_msg_radar); + return 0; +} +EXPORT_SYMBOL(ieee80211_radar_status); + +int ieee80211_set_aid_for_sta(struct ieee80211_hw *hw, u8 *peer_address, + u16 aid) +{ + struct sk_buff *skb; + struct ieee80211_msg_set_aid_for_sta *msg; + struct ieee80211_local *local = hw_to_local(hw); + + /* unlikely because if this event only happens for APs, + * which require an open ap device. */ + if (unlikely(!local->apdev)) + return 0; + + skb = dev_alloc_skb(sizeof(struct ieee80211_frame_info) + + sizeof(struct ieee80211_msg_set_aid_for_sta)); + + if (!skb) + return -ENOMEM; + skb_reserve(skb, sizeof(struct ieee80211_frame_info)); + + msg = (struct ieee80211_msg_set_aid_for_sta *) + skb_put(skb, sizeof(struct ieee80211_msg_set_aid_for_sta)); + memcpy(msg->sta_address, peer_address, ETH_ALEN); + msg->aid = aid; + + ieee80211_rx_mgmt(local, skb, NULL, ieee80211_msg_set_aid_for_sta); + return 0; +} +EXPORT_SYMBOL(ieee80211_set_aid_for_sta); + +static void ap_sta_ps_start(struct net_device *dev, struct sta_info *sta) +{ + struct ieee80211_sub_if_data *sdata; + sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); + + if (sdata->bss) + atomic_inc(&sdata->bss->num_sta_ps); + sta->flags |= WLAN_STA_PS; + sta->pspoll = 0; +#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG + printk(KERN_DEBUG "%s: STA " MAC_FMT " aid %d enters power " + "save mode\n", dev->name, MAC_ARG(sta->addr), sta->aid); +#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ +} + + +static int ap_sta_ps_end(struct net_device *dev, struct sta_info *sta) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct sk_buff *skb; + int sent = 0; + struct ieee80211_sub_if_data *sdata; + struct ieee80211_tx_packet_data *pkt_data; + + sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); + if (sdata->bss) + atomic_dec(&sdata->bss->num_sta_ps); + sta->flags &= ~(WLAN_STA_PS | WLAN_STA_TIM); + sta->pspoll = 0; + if (!skb_queue_empty(&sta->ps_tx_buf)) { + if (local->ops->set_tim) + local->ops->set_tim(local_to_hw(local), sta->aid, 0); + if (sdata->bss) + bss_tim_clear(local, sdata->bss, sta->aid); + } +#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG + printk(KERN_DEBUG "%s: STA " MAC_FMT " aid %d exits power " + "save mode\n", dev->name, MAC_ARG(sta->addr), sta->aid); +#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ + /* Send all buffered frames to the station */ + while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) { + pkt_data = (struct ieee80211_tx_packet_data *) skb->cb; + sent++; + pkt_data->requeue = 1; + dev_queue_xmit(skb); + } + while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) { + pkt_data = (struct ieee80211_tx_packet_data *) skb->cb; + local->total_ps_buffered--; + sent++; +#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG + printk(KERN_DEBUG "%s: STA " MAC_FMT " aid %d send PS frame " + "since STA not sleeping anymore\n", dev->name, + MAC_ARG(sta->addr), sta->aid); +#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ + pkt_data->requeue = 1; + dev_queue_xmit(skb); + } + + return sent; +} + + +static ieee80211_txrx_result +ieee80211_rx_h_ps_poll(struct ieee80211_txrx_data *rx) +{ + struct sk_buff *skb; + int no_pending_pkts; + + if (likely(!rx->sta || + (rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_CTL || + (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_PSPOLL || + !rx->u.rx.ra_match)) + return TXRX_CONTINUE; + + skb = skb_dequeue(&rx->sta->tx_filtered); + if (!skb) { + skb = skb_dequeue(&rx->sta->ps_tx_buf); + if (skb) + rx->local->total_ps_buffered--; + } + no_pending_pkts = skb_queue_empty(&rx->sta->tx_filtered) && + skb_queue_empty(&rx->sta->ps_tx_buf); + + if (skb) { + struct ieee80211_hdr *hdr = + (struct ieee80211_hdr *) skb->data; + + /* tell TX path to send one frame even though the STA may + * still remain is PS mode after this frame exchange */ + rx->sta->pspoll = 1; + +#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG + printk(KERN_DEBUG "STA " MAC_FMT " aid %d: PS Poll (entries " + "after %d)\n", + MAC_ARG(rx->sta->addr), rx->sta->aid, + skb_queue_len(&rx->sta->ps_tx_buf)); +#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ + + /* Use MoreData flag to indicate whether there are more + * buffered frames for this STA */ + if (no_pending_pkts) { + hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREDATA); + rx->sta->flags &= ~WLAN_STA_TIM; + } else + hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA); + + dev_queue_xmit(skb); + + if (no_pending_pkts) { + if (rx->local->ops->set_tim) + rx->local->ops->set_tim(local_to_hw(rx->local), + rx->sta->aid, 0); + if (rx->sdata->bss) + bss_tim_clear(rx->local, rx->sdata->bss, rx->sta->aid); + } +#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG + } else if (!rx->u.rx.sent_ps_buffered) { + printk(KERN_DEBUG "%s: STA " MAC_FMT " sent PS Poll even " + "though there is no buffered frames for it\n", + rx->dev->name, MAC_ARG(rx->sta->addr)); +#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */ + + } + + /* Free PS Poll skb here instead of returning TXRX_DROP that would + * count as an dropped frame. */ + dev_kfree_skb(rx->skb); + + return TXRX_QUEUED; +} + + +static inline struct ieee80211_fragment_entry * +ieee80211_reassemble_add(struct ieee80211_sub_if_data *sdata, + unsigned int frag, unsigned int seq, int rx_queue, + struct sk_buff **skb) +{ + struct ieee80211_fragment_entry *entry; + int idx; + + idx = sdata->fragment_next; + entry = &sdata->fragments[sdata->fragment_next++]; + if (sdata->fragment_next >= IEEE80211_FRAGMENT_MAX) + sdata->fragment_next = 0; + + if (!skb_queue_empty(&entry->skb_list)) { +#ifdef CONFIG_MAC80211_DEBUG + struct ieee80211_hdr *hdr = + (struct ieee80211_hdr *) entry->skb_list.next->data; + printk(KERN_DEBUG "%s: RX reassembly removed oldest " + "fragment entry (idx=%d age=%lu seq=%d last_frag=%d " + "addr1=" MAC_FMT " addr2=" MAC_FMT "\n", + sdata->dev->name, idx, + jiffies - entry->first_frag_time, entry->seq, + entry->last_frag, MAC_ARG(hdr->addr1), + MAC_ARG(hdr->addr2)); +#endif /* CONFIG_MAC80211_DEBUG */ + __skb_queue_purge(&entry->skb_list); + } + + __skb_queue_tail(&entry->skb_list, *skb); /* no need for locking */ + *skb = NULL; + entry->first_frag_time = jiffies; + entry->seq = seq; + entry->rx_queue = rx_queue; + entry->last_frag = frag; + entry->ccmp = 0; + entry->extra_len = 0; + + return entry; +} + + +static inline struct ieee80211_fragment_entry * +ieee80211_reassemble_find(struct ieee80211_sub_if_data *sdata, + u16 fc, unsigned int frag, unsigned int seq, + int rx_queue, struct ieee80211_hdr *hdr) +{ + struct ieee80211_fragment_entry *entry; + int i, idx; + + idx = sdata->fragment_next; + for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++) { + struct ieee80211_hdr *f_hdr; + u16 f_fc; + + idx--; + if (idx < 0) + idx = IEEE80211_FRAGMENT_MAX - 1; + + entry = &sdata->fragments[idx]; + if (skb_queue_empty(&entry->skb_list) || entry->seq != seq || + entry->rx_queue != rx_queue || + entry->last_frag + 1 != frag) + continue; + + f_hdr = (struct ieee80211_hdr *) entry->skb_list.next->data; + f_fc = le16_to_cpu(f_hdr->frame_control); + + if ((fc & IEEE80211_FCTL_FTYPE) != (f_fc & IEEE80211_FCTL_FTYPE) || + compare_ether_addr(hdr->addr1, f_hdr->addr1) != 0 || + compare_ether_addr(hdr->addr2, f_hdr->addr2) != 0) + continue; + + if (entry->first_frag_time + 2 * HZ < jiffies) { + __skb_queue_purge(&entry->skb_list); + continue; + } + return entry; + } + + return NULL; +} + + +static ieee80211_txrx_result +ieee80211_rx_h_defragment(struct ieee80211_txrx_data *rx) +{ + struct ieee80211_hdr *hdr; + u16 sc; + unsigned int frag, seq; + struct ieee80211_fragment_entry *entry; + struct sk_buff *skb; + + hdr = (struct ieee80211_hdr *) rx->skb->data; + sc = le16_to_cpu(hdr->seq_ctrl); + frag = sc & IEEE80211_SCTL_FRAG; + + if (likely((!(rx->fc & IEEE80211_FCTL_MOREFRAGS) && frag == 0) || + (rx->skb)->len < 24 || + is_multicast_ether_addr(hdr->addr1))) { + /* not fragmented */ + goto out; + } + I802_DEBUG_INC(rx->local->rx_handlers_fragments); + + seq = (sc & IEEE80211_SCTL_SEQ) >> 4; + + if (frag == 0) { + /* This is the first fragment of a new frame. */ + entry = ieee80211_reassemble_add(rx->sdata, frag, seq, + rx->u.rx.queue, &(rx->skb)); + if (rx->key && rx->key->alg == ALG_CCMP && + (rx->fc & IEEE80211_FCTL_PROTECTED)) { + /* Store CCMP PN so that we can verify that the next + * fragment has a sequential PN value. */ + entry->ccmp = 1; + memcpy(entry->last_pn, + rx->key->u.ccmp.rx_pn[rx->u.rx.queue], + CCMP_PN_LEN); + } + return TXRX_QUEUED; + } + + /* This is a fragment for a frame that should already be pending in + * fragment cache. Add this fragment to the end of the pending entry. + */ + entry = ieee80211_reassemble_find(rx->sdata, rx->fc, frag, seq, + rx->u.rx.queue, hdr); + if (!entry) { + I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag); + return TXRX_DROP; + } + + /* Verify that MPDUs within one MSDU have sequential PN values. + * (IEEE 802.11i, 8.3.3.4.5) */ + if (entry->ccmp) { + int i; + u8 pn[CCMP_PN_LEN], *rpn; + if (!rx->key || rx->key->alg != ALG_CCMP) + return TXRX_DROP; + memcpy(pn, entry->last_pn, CCMP_PN_LEN); + for (i = CCMP_PN_LEN - 1; i >= 0; i--) { + pn[i]++; + if (pn[i]) + break; + } + rpn = rx->key->u.ccmp.rx_pn[rx->u.rx.queue]; + if (memcmp(pn, rpn, CCMP_PN_LEN) != 0) { + printk(KERN_DEBUG "%s: defrag: CCMP PN not sequential" + " A2=" MAC_FMT " PN=%02x%02x%02x%02x%02x%02x " + "(expected %02x%02x%02x%02x%02x%02x)\n", + rx->dev->name, MAC_ARG(hdr->addr2), + rpn[0], rpn[1], rpn[2], rpn[3], rpn[4], rpn[5], + pn[0], pn[1], pn[2], pn[3], pn[4], pn[5]); + return TXRX_DROP; + } + memcpy(entry->last_pn, pn, CCMP_PN_LEN); + } + + skb_pull(rx->skb, ieee80211_get_hdrlen(rx->fc)); + __skb_queue_tail(&entry->skb_list, rx->skb); + entry->last_frag = frag; + entry->extra_len += rx->skb->len; + if (rx->fc & IEEE80211_FCTL_MOREFRAGS) { + rx->skb = NULL; + return TXRX_QUEUED; + } + + rx->skb = __skb_dequeue(&entry->skb_list); + if (skb_tailroom(rx->skb) < entry->extra_len) { + I802_DEBUG_INC(rx->local->rx_expand_skb_head2); + if (unlikely(pskb_expand_head(rx->skb, 0, entry->extra_len, + GFP_ATOMIC))) { + I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag); + __skb_queue_purge(&entry->skb_list); + return TXRX_DROP; + } + } + while ((skb = __skb_dequeue(&entry->skb_list))) + memcpy(skb_put(rx->skb, skb->len), skb->data, skb->len); + + /* Complete frame has been reassembled - process it now */ + rx->fragmented = 1; + + out: + if (rx->sta) + rx->sta->rx_packets++; + if (is_multicast_ether_addr(hdr->addr1)) + rx->local->dot11MulticastReceivedFrameCount++; + else + ieee80211_led_rx(rx->local); + return TXRX_CONTINUE; +} + + +static ieee80211_txrx_result +ieee80211_rx_h_monitor(struct ieee80211_txrx_data *rx) +{ + if (rx->sdata->type == IEEE80211_IF_TYPE_MNTR) { + ieee80211_rx_monitor(rx->dev, rx->skb, rx->u.rx.status); + return TXRX_QUEUED; + } + + if (rx->u.rx.status->flag & RX_FLAG_RADIOTAP) + skb_pull(rx->skb, ieee80211_get_radiotap_len(rx->skb)); + + return TXRX_CONTINUE; +} + + +static ieee80211_txrx_result +ieee80211_rx_h_check(struct ieee80211_txrx_data *rx) +{ + struct ieee80211_hdr *hdr; + int always_sta_key; + hdr = (struct ieee80211_hdr *) rx->skb->data; + + /* Drop duplicate 802.11 retransmissions (IEEE 802.11 Chap. 9.2.9) */ + if (rx->sta && !is_multicast_ether_addr(hdr->addr1)) { + if (unlikely(rx->fc & IEEE80211_FCTL_RETRY && + rx->sta->last_seq_ctrl[rx->u.rx.queue] == + hdr->seq_ctrl)) { + if (rx->u.rx.ra_match) { + rx->local->dot11FrameDuplicateCount++; + rx->sta->num_duplicates++; + } + return TXRX_DROP; + } else + rx->sta->last_seq_ctrl[rx->u.rx.queue] = hdr->seq_ctrl; + } + + if ((rx->local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) && + rx->skb->len > FCS_LEN) + skb_trim(rx->skb, rx->skb->len - FCS_LEN); + + if (unlikely(rx->skb->len < 16)) { + I802_DEBUG_INC(rx->local->rx_handlers_drop_short); + return TXRX_DROP; + } + + if (!rx->u.rx.ra_match) + rx->skb->pkt_type = PACKET_OTHERHOST; + else if (compare_ether_addr(rx->dev->dev_addr, hdr->addr1) == 0) + rx->skb->pkt_type = PACKET_HOST; + else if (is_multicast_ether_addr(hdr->addr1)) { + if (is_broadcast_ether_addr(hdr->addr1)) + rx->skb->pkt_type = PACKET_BROADCAST; + else + rx->skb->pkt_type = PACKET_MULTICAST; + } else + rx->skb->pkt_type = PACKET_OTHERHOST; + + /* Drop disallowed frame classes based on STA auth/assoc state; + * IEEE 802.11, Chap 5.5. + * + * 80211.o does filtering only based on association state, i.e., it + * drops Class 3 frames from not associated stations. hostapd sends + * deauth/disassoc frames when needed. In addition, hostapd is + * responsible for filtering on both auth and assoc states. + */ + if (unlikely(((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA || + ((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL && + (rx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PSPOLL)) && + rx->sdata->type != IEEE80211_IF_TYPE_IBSS && + (!rx->sta || !(rx->sta->flags & WLAN_STA_ASSOC)))) { + if ((!(rx->fc & IEEE80211_FCTL_FROMDS) && + !(rx->fc & IEEE80211_FCTL_TODS) && + (rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) + || !rx->u.rx.ra_match) { + /* Drop IBSS frames and frames for other hosts + * silently. */ + return TXRX_DROP; + } + + if (!rx->local->apdev) + return TXRX_DROP; + + ieee80211_rx_mgmt(rx->local, rx->skb, rx->u.rx.status, + ieee80211_msg_sta_not_assoc); + return TXRX_QUEUED; + } + + if (rx->sdata->type == IEEE80211_IF_TYPE_STA) + always_sta_key = 0; + else + always_sta_key = 1; + + if (rx->sta && rx->sta->key && always_sta_key) { + rx->key = rx->sta->key; + } else { + if (rx->sta && rx->sta->key) + rx->key = rx->sta->key; + else + rx->key = rx->sdata->default_key; + + if ((rx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV) && + rx->fc & IEEE80211_FCTL_PROTECTED) { + int keyidx = ieee80211_wep_get_keyidx(rx->skb); + + if (keyidx >= 0 && keyidx < NUM_DEFAULT_KEYS && + (!rx->sta || !rx->sta->key || keyidx > 0)) + rx->key = rx->sdata->keys[keyidx]; + + if (!rx->key) { + if (!rx->u.rx.ra_match) + return TXRX_DROP; + printk(KERN_DEBUG "%s: RX WEP frame with " + "unknown keyidx %d (A1=" MAC_FMT " A2=" + MAC_FMT " A3=" MAC_FMT ")\n", + rx->dev->name, keyidx, + MAC_ARG(hdr->addr1), + MAC_ARG(hdr->addr2), + MAC_ARG(hdr->addr3)); + if (!rx->local->apdev) + return TXRX_DROP; + ieee80211_rx_mgmt( + rx->local, rx->skb, rx->u.rx.status, + ieee80211_msg_wep_frame_unknown_key); + return TXRX_QUEUED; + } + } + } + + if (rx->fc & IEEE80211_FCTL_PROTECTED && rx->key && rx->u.rx.ra_match) { + rx->key->tx_rx_count++; + if (unlikely(rx->local->key_tx_rx_threshold && + rx->key->tx_rx_count > + rx->local->key_tx_rx_threshold)) { + ieee80211_key_threshold_notify(rx->dev, rx->key, + rx->sta); + } + } + + return TXRX_CONTINUE; +} + + +static ieee80211_txrx_result +ieee80211_rx_h_sta_process(struct ieee80211_txrx_data *rx) +{ + struct sta_info *sta = rx->sta; + struct net_device *dev = rx->dev; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data; + + if (!sta) + return TXRX_CONTINUE; + + /* Update last_rx only for IBSS packets which are for the current + * BSSID to avoid keeping the current IBSS network alive in cases where + * other STAs are using different BSSID. */ + if (rx->sdata->type == IEEE80211_IF_TYPE_IBSS) { + u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len); + if (compare_ether_addr(bssid, rx->sdata->u.sta.bssid) == 0) + sta->last_rx = jiffies; + } else + if (!is_multicast_ether_addr(hdr->addr1) || + rx->sdata->type == IEEE80211_IF_TYPE_STA) { + /* Update last_rx only for unicast frames in order to prevent + * the Probe Request frames (the only broadcast frames from a + * STA in infrastructure mode) from keeping a connection alive. + */ + sta->last_rx = jiffies; + } + + if (!rx->u.rx.ra_match) + return TXRX_CONTINUE; + + sta->rx_fragments++; + sta->rx_bytes += rx->skb->len; + sta->last_rssi = (sta->last_rssi * 15 + + rx->u.rx.status->ssi) / 16; + sta->last_signal = (sta->last_signal * 15 + + rx->u.rx.status->signal) / 16; + sta->last_noise = (sta->last_noise * 15 + + rx->u.rx.status->noise) / 16; + + if (!(rx->fc & IEEE80211_FCTL_MOREFRAGS)) { + /* Change STA power saving mode only in the end of a frame + * exchange sequence */ + if ((sta->flags & WLAN_STA_PS) && !(rx->fc & IEEE80211_FCTL_PM)) + rx->u.rx.sent_ps_buffered += ap_sta_ps_end(dev, sta); + else if (!(sta->flags & WLAN_STA_PS) && + (rx->fc & IEEE80211_FCTL_PM)) + ap_sta_ps_start(dev, sta); + } + + /* Drop data::nullfunc frames silently, since they are used only to + * control station power saving mode. */ + if ((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA && + (rx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_NULLFUNC) { + I802_DEBUG_INC(rx->local->rx_handlers_drop_nullfunc); + /* Update counter and free packet here to avoid counting this + * as a dropped packed. */ + sta->rx_packets++; + dev_kfree_skb(rx->skb); + return TXRX_QUEUED; + } + + return TXRX_CONTINUE; +} /* ieee80211_rx_h_sta_process */ + + +static ieee80211_txrx_result +ieee80211_rx_h_wep_weak_iv_detection(struct ieee80211_txrx_data *rx) +{ + if (!rx->sta || !(rx->fc & IEEE80211_FCTL_PROTECTED) || + (rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA || + !rx->key || rx->key->alg != ALG_WEP || !rx->u.rx.ra_match) + return TXRX_CONTINUE; + + /* Check for weak IVs, if hwaccel did not remove IV from the frame */ + if ((rx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV) || + rx->key->force_sw_encrypt) { + u8 *iv = ieee80211_wep_is_weak_iv(rx->skb, rx->key); + if (iv) { + rx->sta->wep_weak_iv_count++; + } + } + + return TXRX_CONTINUE; +} + + +static ieee80211_txrx_result +ieee80211_rx_h_wep_decrypt(struct ieee80211_txrx_data *rx) +{ + /* If the device handles decryption totally, skip this test */ + if (rx->local->hw.flags & IEEE80211_HW_DEVICE_HIDES_WEP) + return TXRX_CONTINUE; + + if ((rx->key && rx->key->alg != ALG_WEP) || + !(rx->fc & IEEE80211_FCTL_PROTECTED) || + ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA && + ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT || + (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_AUTH))) + return TXRX_CONTINUE; + + if (!rx->key) { + printk(KERN_DEBUG "%s: RX WEP frame, but no key set\n", + rx->dev->name); + return TXRX_DROP; + } + + if (!(rx->u.rx.status->flag & RX_FLAG_DECRYPTED) || + rx->key->force_sw_encrypt) { + if (ieee80211_wep_decrypt(rx->local, rx->skb, rx->key)) { + printk(KERN_DEBUG "%s: RX WEP frame, decrypt " + "failed\n", rx->dev->name); + return TXRX_DROP; + } + } else if (rx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV) { + ieee80211_wep_remove_iv(rx->local, rx->skb, rx->key); + /* remove ICV */ + skb_trim(rx->skb, rx->skb->len - 4); + } + + return TXRX_CONTINUE; +} + + +static ieee80211_txrx_result +ieee80211_rx_h_802_1x_pae(struct ieee80211_txrx_data *rx) +{ + if (rx->sdata->eapol && ieee80211_is_eapol(rx->skb) && + rx->sdata->type != IEEE80211_IF_TYPE_STA && rx->u.rx.ra_match) { + /* Pass both encrypted and unencrypted EAPOL frames to user + * space for processing. */ + if (!rx->local->apdev) + return TXRX_DROP; + ieee80211_rx_mgmt(rx->local, rx->skb, rx->u.rx.status, + ieee80211_msg_normal); + return TXRX_QUEUED; + } + + if (unlikely(rx->sdata->ieee802_1x && + (rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA && + (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_NULLFUNC && + (!rx->sta || !(rx->sta->flags & WLAN_STA_AUTHORIZED)) && + !ieee80211_is_eapol(rx->skb))) { +#ifdef CONFIG_MAC80211_DEBUG + struct ieee80211_hdr *hdr = + (struct ieee80211_hdr *) rx->skb->data; + printk(KERN_DEBUG "%s: dropped frame from " MAC_FMT + " (unauthorized port)\n", rx->dev->name, + MAC_ARG(hdr->addr2)); +#endif /* CONFIG_MAC80211_DEBUG */ + return TXRX_DROP; + } + + return TXRX_CONTINUE; +} + + +static ieee80211_txrx_result +ieee80211_rx_h_drop_unencrypted(struct ieee80211_txrx_data *rx) +{ + /* If the device handles decryption totally, skip this test */ + if (rx->local->hw.flags & IEEE80211_HW_DEVICE_HIDES_WEP) + return TXRX_CONTINUE; + + /* Drop unencrypted frames if key is set. */ + if (unlikely(!(rx->fc & IEEE80211_FCTL_PROTECTED) && + (rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA && + (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_NULLFUNC && + (rx->key || rx->sdata->drop_unencrypted) && + (rx->sdata->eapol == 0 || + !ieee80211_is_eapol(rx->skb)))) { + printk(KERN_DEBUG "%s: RX non-WEP frame, but expected " + "encryption\n", rx->dev->name); + return TXRX_DROP; + } + return TXRX_CONTINUE; +} + + +static ieee80211_txrx_result +ieee80211_rx_h_mgmt(struct ieee80211_txrx_data *rx) +{ + struct ieee80211_sub_if_data *sdata; + + if (!rx->u.rx.ra_match) + return TXRX_DROP; + + sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev); + if ((sdata->type == IEEE80211_IF_TYPE_STA || + sdata->type == IEEE80211_IF_TYPE_IBSS) && + !rx->local->user_space_mlme) { + ieee80211_sta_rx_mgmt(rx->dev, rx->skb, rx->u.rx.status); + } else { + /* Management frames are sent to hostapd for processing */ + if (!rx->local->apdev) + return TXRX_DROP; + ieee80211_rx_mgmt(rx->local, rx->skb, rx->u.rx.status, + ieee80211_msg_normal); + } + return TXRX_QUEUED; +} + + +static ieee80211_txrx_result +ieee80211_rx_h_passive_scan(struct ieee80211_txrx_data *rx) +{ + struct ieee80211_local *local = rx->local; + struct sk_buff *skb = rx->skb; + + if (unlikely(local->sta_scanning != 0)) { + ieee80211_sta_rx_scan(rx->dev, skb, rx->u.rx.status); + return TXRX_QUEUED; + } + + if (unlikely(rx->u.rx.in_scan)) { + /* scanning finished during invoking of handlers */ + I802_DEBUG_INC(local->rx_handlers_drop_passive_scan); + return TXRX_DROP; + } + + return TXRX_CONTINUE; +} + + +static void ieee80211_rx_michael_mic_report(struct net_device *dev, + struct ieee80211_hdr *hdr, + struct sta_info *sta, + struct ieee80211_txrx_data *rx) +{ + int keyidx, hdrlen; + + hdrlen = ieee80211_get_hdrlen_from_skb(rx->skb); + if (rx->skb->len >= hdrlen + 4) + keyidx = rx->skb->data[hdrlen + 3] >> 6; + else + keyidx = -1; + + /* TODO: verify that this is not triggered by fragmented + * frames (hw does not verify MIC for them). */ + printk(KERN_DEBUG "%s: TKIP hwaccel reported Michael MIC " + "failure from " MAC_FMT " to " MAC_FMT " keyidx=%d\n", + dev->name, MAC_ARG(hdr->addr2), MAC_ARG(hdr->addr1), keyidx); + + if (!sta) { + /* Some hardware versions seem to generate incorrect + * Michael MIC reports; ignore them to avoid triggering + * countermeasures. */ + printk(KERN_DEBUG "%s: ignored spurious Michael MIC " + "error for unknown address " MAC_FMT "\n", + dev->name, MAC_ARG(hdr->addr2)); + goto ignore; + } + + if (!(rx->fc & IEEE80211_FCTL_PROTECTED)) { + printk(KERN_DEBUG "%s: ignored spurious Michael MIC " + "error for a frame with no ISWEP flag (src " + MAC_FMT ")\n", dev->name, MAC_ARG(hdr->addr2)); + goto ignore; + } + + if ((rx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV) && + rx->sdata->type == IEEE80211_IF_TYPE_AP) { + keyidx = ieee80211_wep_get_keyidx(rx->skb); + /* AP with Pairwise keys support should never receive Michael + * MIC errors for non-zero keyidx because these are reserved + * for group keys and only the AP is sending real multicast + * frames in BSS. */ + if (keyidx) { + printk(KERN_DEBUG "%s: ignored Michael MIC error for " + "a frame with non-zero keyidx (%d) (src " MAC_FMT + ")\n", dev->name, keyidx, MAC_ARG(hdr->addr2)); + goto ignore; + } + } + + if ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA && + ((rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT || + (rx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_AUTH)) { + printk(KERN_DEBUG "%s: ignored spurious Michael MIC " + "error for a frame that cannot be encrypted " + "(fc=0x%04x) (src " MAC_FMT ")\n", + dev->name, rx->fc, MAC_ARG(hdr->addr2)); + goto ignore; + } + + do { + union iwreq_data wrqu; + char *buf = kmalloc(128, GFP_ATOMIC); + if (!buf) + break; + + /* TODO: needed parameters: count, key type, TSC */ + sprintf(buf, "MLME-MICHAELMICFAILURE.indication(" + "keyid=%d %scast addr=" MAC_FMT ")", + keyidx, hdr->addr1[0] & 0x01 ? "broad" : "uni", + MAC_ARG(hdr->addr2)); + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = strlen(buf); + wireless_send_event(rx->dev, IWEVCUSTOM, &wrqu, buf); + kfree(buf); + } while (0); + + /* TODO: consider verifying the MIC error report with software + * implementation if we get too many spurious reports from the + * hardware. */ + if (!rx->local->apdev) + goto ignore; + ieee80211_rx_mgmt(rx->local, rx->skb, rx->u.rx.status, + ieee80211_msg_michael_mic_failure); + return; + + ignore: + dev_kfree_skb(rx->skb); + rx->skb = NULL; +} + +static inline ieee80211_txrx_result __ieee80211_invoke_rx_handlers( + struct ieee80211_local *local, + ieee80211_rx_handler *handlers, + struct ieee80211_txrx_data *rx, + struct sta_info *sta) +{ + ieee80211_rx_handler *handler; + ieee80211_txrx_result res = TXRX_DROP; + + for (handler = handlers; *handler != NULL; handler++) { + res = (*handler)(rx); + if (res != TXRX_CONTINUE) { + if (res == TXRX_DROP) { + I802_DEBUG_INC(local->rx_handlers_drop); + if (sta) + sta->rx_dropped++; + } + if (res == TXRX_QUEUED) + I802_DEBUG_INC(local->rx_handlers_queued); + break; + } + } + + if (res == TXRX_DROP) { + dev_kfree_skb(rx->skb); + } + return res; +} + +static inline void ieee80211_invoke_rx_handlers(struct ieee80211_local *local, + ieee80211_rx_handler *handlers, + struct ieee80211_txrx_data *rx, + struct sta_info *sta) +{ + if (__ieee80211_invoke_rx_handlers(local, handlers, rx, sta) == + TXRX_CONTINUE) + dev_kfree_skb(rx->skb); +} + +/* + * This is the receive path handler. It is called by a low level driver when an + * 802.11 MPDU is received from the hardware. + */ +void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb, + struct ieee80211_rx_status *status) +{ + struct ieee80211_local *local = hw_to_local(hw); + struct ieee80211_sub_if_data *sdata; + struct sta_info *sta; + struct ieee80211_hdr *hdr; + struct ieee80211_txrx_data rx; + u16 type; + int multicast; + int radiotap_len = 0; + + if (status->flag & RX_FLAG_RADIOTAP) { + radiotap_len = ieee80211_get_radiotap_len(skb); + skb_pull(skb, radiotap_len); + } + + hdr = (struct ieee80211_hdr *) skb->data; + memset(&rx, 0, sizeof(rx)); + rx.skb = skb; + rx.local = local; + + rx.u.rx.status = status; + rx.fc = skb->len >= 2 ? le16_to_cpu(hdr->frame_control) : 0; + type = rx.fc & IEEE80211_FCTL_FTYPE; + if (type == IEEE80211_FTYPE_DATA || type == IEEE80211_FTYPE_MGMT) + local->dot11ReceivedFragmentCount++; + multicast = is_multicast_ether_addr(hdr->addr1); + + if (skb->len >= 16) + sta = rx.sta = sta_info_get(local, hdr->addr2); + else + sta = rx.sta = NULL; + + if (sta) { + rx.dev = sta->dev; + rx.sdata = IEEE80211_DEV_TO_SUB_IF(rx.dev); + } + + if ((status->flag & RX_FLAG_MMIC_ERROR)) { + ieee80211_rx_michael_mic_report(local->mdev, hdr, sta, &rx); + goto end; + } + + if (unlikely(local->sta_scanning)) + rx.u.rx.in_scan = 1; + + if (__ieee80211_invoke_rx_handlers(local, local->rx_pre_handlers, &rx, + sta) != TXRX_CONTINUE) + goto end; + skb = rx.skb; + + skb_push(skb, radiotap_len); + if (sta && !sta->assoc_ap && !(sta->flags & WLAN_STA_WDS) && + !local->iff_promiscs && !multicast) { + rx.u.rx.ra_match = 1; + ieee80211_invoke_rx_handlers(local, local->rx_handlers, &rx, + sta); + } else { + struct ieee80211_sub_if_data *prev = NULL; + struct sk_buff *skb_new; + u8 *bssid = ieee80211_get_bssid(hdr, skb->len - radiotap_len); + + read_lock(&local->sub_if_lock); + list_for_each_entry(sdata, &local->sub_if_list, list) { + rx.u.rx.ra_match = 1; + switch (sdata->type) { + case IEEE80211_IF_TYPE_STA: + if (!bssid) + continue; + if (!ieee80211_bssid_match(bssid, + sdata->u.sta.bssid)) { + if (!rx.u.rx.in_scan) + continue; + rx.u.rx.ra_match = 0; + } else if (!multicast && + compare_ether_addr(sdata->dev->dev_addr, + hdr->addr1) != 0) { + if (!sdata->promisc) + continue; + rx.u.rx.ra_match = 0; + } + break; + case IEEE80211_IF_TYPE_IBSS: + if (!bssid) + continue; + if (!ieee80211_bssid_match(bssid, + sdata->u.sta.bssid)) { + if (!rx.u.rx.in_scan) + continue; + rx.u.rx.ra_match = 0; + } else if (!multicast && + compare_ether_addr(sdata->dev->dev_addr, + hdr->addr1) != 0) { + if (!sdata->promisc) + continue; + rx.u.rx.ra_match = 0; + } else if (!sta) + sta = rx.sta = + ieee80211_ibss_add_sta(sdata->dev, + skb, bssid, + hdr->addr2); + break; + case IEEE80211_IF_TYPE_AP: + if (!bssid) { + if (compare_ether_addr(sdata->dev->dev_addr, + hdr->addr1) != 0) + continue; + } else if (!ieee80211_bssid_match(bssid, + sdata->dev->dev_addr)) { + if (!rx.u.rx.in_scan) + continue; + rx.u.rx.ra_match = 0; + } + if (sdata->dev == local->mdev && + !rx.u.rx.in_scan) + /* do not receive anything via + * master device when not scanning */ + continue; + break; + case IEEE80211_IF_TYPE_WDS: + if (bssid || + (rx.fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA) + continue; + if (compare_ether_addr(sdata->u.wds.remote_addr, + hdr->addr2) != 0) + continue; + break; + } + + if (prev) { + skb_new = skb_copy(skb, GFP_ATOMIC); + if (!skb_new) { + if (net_ratelimit()) + printk(KERN_DEBUG "%s: failed to copy " + "multicast frame for %s", + local->mdev->name, prev->dev->name); + continue; + } + rx.skb = skb_new; + rx.dev = prev->dev; + rx.sdata = prev; + ieee80211_invoke_rx_handlers(local, + local->rx_handlers, + &rx, sta); + } + prev = sdata; + } + if (prev) { + rx.skb = skb; + rx.dev = prev->dev; + rx.sdata = prev; + ieee80211_invoke_rx_handlers(local, local->rx_handlers, + &rx, sta); + } else + dev_kfree_skb(skb); + read_unlock(&local->sub_if_lock); + } + + end: + if (sta) + sta_info_put(sta); +} +EXPORT_SYMBOL(__ieee80211_rx); + +static ieee80211_txrx_result +ieee80211_tx_h_load_stats(struct ieee80211_txrx_data *tx) +{ + struct ieee80211_local *local = tx->local; + struct ieee80211_hw_mode *mode = tx->u.tx.mode; + struct sk_buff *skb = tx->skb; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + u32 load = 0, hdrtime; + + /* TODO: this could be part of tx_status handling, so that the number + * of retries would be known; TX rate should in that case be stored + * somewhere with the packet */ + + /* Estimate total channel use caused by this frame */ + + /* 1 bit at 1 Mbit/s takes 1 usec; in channel_use values, + * 1 usec = 1/8 * (1080 / 10) = 13.5 */ + + if (mode->mode == MODE_IEEE80211A || + mode->mode == MODE_ATHEROS_TURBO || + mode->mode == MODE_ATHEROS_TURBOG || + (mode->mode == MODE_IEEE80211G && + tx->u.tx.rate->flags & IEEE80211_RATE_ERP)) + hdrtime = CHAN_UTIL_HDR_SHORT; + else + hdrtime = CHAN_UTIL_HDR_LONG; + + load = hdrtime; + if (!is_multicast_ether_addr(hdr->addr1)) + load += hdrtime; + + if (tx->u.tx.control->flags & IEEE80211_TXCTL_USE_RTS_CTS) + load += 2 * hdrtime; + else if (tx->u.tx.control->flags & IEEE80211_TXCTL_USE_CTS_PROTECT) + load += hdrtime; + + load += skb->len * tx->u.tx.rate->rate_inv; + + if (tx->u.tx.extra_frag) { + int i; + for (i = 0; i < tx->u.tx.num_extra_frag; i++) { + load += 2 * hdrtime; + load += tx->u.tx.extra_frag[i]->len * + tx->u.tx.rate->rate; + } + } + + /* Divide channel_use by 8 to avoid wrapping around the counter */ + load >>= CHAN_UTIL_SHIFT; + local->channel_use_raw += load; + if (tx->sta) + tx->sta->channel_use_raw += load; + tx->sdata->channel_use_raw += load; + + return TXRX_CONTINUE; +} + + +static ieee80211_txrx_result +ieee80211_rx_h_load_stats(struct ieee80211_txrx_data *rx) +{ + struct ieee80211_local *local = rx->local; + struct sk_buff *skb = rx->skb; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + u32 load = 0, hdrtime; + struct ieee80211_rate *rate; + struct ieee80211_hw_mode *mode = local->hw.conf.mode; + int i; + + /* Estimate total channel use caused by this frame */ + + if (unlikely(mode->num_rates < 0)) + return TXRX_CONTINUE; + + rate = &mode->rates[0]; + for (i = 0; i < mode->num_rates; i++) { + if (mode->rates[i].val == rx->u.rx.status->rate) { + rate = &mode->rates[i]; + break; + } + } + + /* 1 bit at 1 Mbit/s takes 1 usec; in channel_use values, + * 1 usec = 1/8 * (1080 / 10) = 13.5 */ + + if (mode->mode == MODE_IEEE80211A || + mode->mode == MODE_ATHEROS_TURBO || + mode->mode == MODE_ATHEROS_TURBOG || + (mode->mode == MODE_IEEE80211G && + rate->flags & IEEE80211_RATE_ERP)) + hdrtime = CHAN_UTIL_HDR_SHORT; + else + hdrtime = CHAN_UTIL_HDR_LONG; + + load = hdrtime; + if (!is_multicast_ether_addr(hdr->addr1)) + load += hdrtime; + + load += skb->len * rate->rate_inv; + + /* Divide channel_use by 8 to avoid wrapping around the counter */ + load >>= CHAN_UTIL_SHIFT; + local->channel_use_raw += load; + if (rx->sta) + rx->sta->channel_use_raw += load; + rx->u.rx.load = load; + + return TXRX_CONTINUE; +} + +static ieee80211_txrx_result +ieee80211_rx_h_if_stats(struct ieee80211_txrx_data *rx) +{ + rx->sdata->channel_use_raw += rx->u.rx.load; + return TXRX_CONTINUE; +} + +static void ieee80211_stat_refresh(unsigned long data) +{ + struct ieee80211_local *local = (struct ieee80211_local *) data; + struct sta_info *sta; + struct ieee80211_sub_if_data *sdata; + + if (!local->stat_time) + return; + + /* go through all stations */ + spin_lock_bh(&local->sta_lock); + list_for_each_entry(sta, &local->sta_list, list) { + sta->channel_use = (sta->channel_use_raw / local->stat_time) / + CHAN_UTIL_PER_10MS; + sta->channel_use_raw = 0; + } + spin_unlock_bh(&local->sta_lock); + + /* go through all subinterfaces */ + read_lock(&local->sub_if_lock); + list_for_each_entry(sdata, &local->sub_if_list, list) { + sdata->channel_use = (sdata->channel_use_raw / + local->stat_time) / CHAN_UTIL_PER_10MS; + sdata->channel_use_raw = 0; + } + read_unlock(&local->sub_if_lock); + + /* hardware interface */ + local->channel_use = (local->channel_use_raw / + local->stat_time) / CHAN_UTIL_PER_10MS; + local->channel_use_raw = 0; + + local->stat_timer.expires = jiffies + HZ * local->stat_time / 100; + add_timer(&local->stat_timer); +} + + +/* This is a version of the rx handler that can be called from hard irq + * context. Post the skb on the queue and schedule the tasklet */ +void ieee80211_rx_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb, + struct ieee80211_rx_status *status) +{ + struct ieee80211_local *local = hw_to_local(hw); + + BUILD_BUG_ON(sizeof(struct ieee80211_rx_status) > sizeof(skb->cb)); + + skb->dev = local->mdev; + /* copy status into skb->cb for use by tasklet */ + memcpy(skb->cb, status, sizeof(*status)); + skb->pkt_type = IEEE80211_RX_MSG; + skb_queue_tail(&local->skb_queue, skb); + tasklet_schedule(&local->tasklet); +} +EXPORT_SYMBOL(ieee80211_rx_irqsafe); + +void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw, + struct sk_buff *skb, + struct ieee80211_tx_status *status) +{ + struct ieee80211_local *local = hw_to_local(hw); + struct ieee80211_tx_status *saved; + int tmp; + + skb->dev = local->mdev; + saved = kmalloc(sizeof(struct ieee80211_tx_status), GFP_ATOMIC); + if (unlikely(!saved)) { + if (net_ratelimit()) + printk(KERN_WARNING "%s: Not enough memory, " + "dropping tx status", skb->dev->name); + /* should be dev_kfree_skb_irq, but due to this function being + * named _irqsafe instead of just _irq we can't be sure that + * people won't call it from non-irq contexts */ + dev_kfree_skb_any(skb); + return; + } + memcpy(saved, status, sizeof(struct ieee80211_tx_status)); + /* copy pointer to saved status into skb->cb for use by tasklet */ + memcpy(skb->cb, &saved, sizeof(saved)); + + skb->pkt_type = IEEE80211_TX_STATUS_MSG; + skb_queue_tail(status->control.flags & IEEE80211_TXCTL_REQ_TX_STATUS ? + &local->skb_queue : &local->skb_queue_unreliable, skb); + tmp = skb_queue_len(&local->skb_queue) + + skb_queue_len(&local->skb_queue_unreliable); + while (tmp > IEEE80211_IRQSAFE_QUEUE_LIMIT && + (skb = skb_dequeue(&local->skb_queue_unreliable))) { + memcpy(&saved, skb->cb, sizeof(saved)); + kfree(saved); + dev_kfree_skb_irq(skb); + tmp--; + I802_DEBUG_INC(local->tx_status_drop); + } + tasklet_schedule(&local->tasklet); +} +EXPORT_SYMBOL(ieee80211_tx_status_irqsafe); + +static void ieee80211_tasklet_handler(unsigned long data) +{ + struct ieee80211_local *local = (struct ieee80211_local *) data; + struct sk_buff *skb; + struct ieee80211_rx_status rx_status; + struct ieee80211_tx_status *tx_status; + + while ((skb = skb_dequeue(&local->skb_queue)) || + (skb = skb_dequeue(&local->skb_queue_unreliable))) { + switch (skb->pkt_type) { + case IEEE80211_RX_MSG: + /* status is in skb->cb */ + memcpy(&rx_status, skb->cb, sizeof(rx_status)); + /* Clear skb->type in order to not confuse kernel + * netstack. */ + skb->pkt_type = 0; + __ieee80211_rx(local_to_hw(local), skb, &rx_status); + break; + case IEEE80211_TX_STATUS_MSG: + /* get pointer to saved status out of skb->cb */ + memcpy(&tx_status, skb->cb, sizeof(tx_status)); + skb->pkt_type = 0; + ieee80211_tx_status(local_to_hw(local), + skb, tx_status); + kfree(tx_status); + break; + default: /* should never get here! */ + printk(KERN_ERR "%s: Unknown message type (%d)\n", + local->mdev->name, skb->pkt_type); + dev_kfree_skb(skb); + break; + } + } +} + + +/* Remove added headers (e.g., QoS control), encryption header/MIC, etc. to + * make a prepared TX frame (one that has been given to hw) to look like brand + * new IEEE 802.11 frame that is ready to go through TX processing again. + * Also, tx_packet_data in cb is restored from tx_control. */ +static void ieee80211_remove_tx_extra(struct ieee80211_local *local, + struct ieee80211_key *key, + struct sk_buff *skb, + struct ieee80211_tx_control *control) +{ + int hdrlen, iv_len, mic_len; + struct ieee80211_tx_packet_data *pkt_data; + + pkt_data = (struct ieee80211_tx_packet_data *)skb->cb; + pkt_data->ifindex = control->ifindex; + pkt_data->mgmt_iface = (control->type == IEEE80211_IF_TYPE_MGMT); + pkt_data->req_tx_status = !!(control->flags & IEEE80211_TXCTL_REQ_TX_STATUS); + pkt_data->do_not_encrypt = !!(control->flags & IEEE80211_TXCTL_DO_NOT_ENCRYPT); + pkt_data->requeue = !!(control->flags & IEEE80211_TXCTL_REQUEUE); + pkt_data->queue = control->queue; + + hdrlen = ieee80211_get_hdrlen_from_skb(skb); + + if (!key) + goto no_key; + + switch (key->alg) { + case ALG_WEP: + iv_len = WEP_IV_LEN; + mic_len = WEP_ICV_LEN; + break; + case ALG_TKIP: + iv_len = TKIP_IV_LEN; + mic_len = TKIP_ICV_LEN; + break; + case ALG_CCMP: + iv_len = CCMP_HDR_LEN; + mic_len = CCMP_MIC_LEN; + break; + default: + goto no_key; + } + + if (skb->len >= mic_len && key->force_sw_encrypt) + skb_trim(skb, skb->len - mic_len); + if (skb->len >= iv_len && skb->len > hdrlen) { + memmove(skb->data + iv_len, skb->data, hdrlen); + skb_pull(skb, iv_len); + } + +no_key: + { + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + u16 fc = le16_to_cpu(hdr->frame_control); + if ((fc & 0x8C) == 0x88) /* QoS Control Field */ { + fc &= ~IEEE80211_STYPE_QOS_DATA; + hdr->frame_control = cpu_to_le16(fc); + memmove(skb->data + 2, skb->data, hdrlen - 2); + skb_pull(skb, 2); + } + } +} + + +void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb, + struct ieee80211_tx_status *status) +{ + struct sk_buff *skb2; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + struct ieee80211_local *local = hw_to_local(hw); + u16 frag, type; + u32 msg_type; + + if (!status) { + printk(KERN_ERR + "%s: ieee80211_tx_status called with NULL status\n", + local->mdev->name); + dev_kfree_skb(skb); + return; + } + + if (status->excessive_retries) { + struct sta_info *sta; + sta = sta_info_get(local, hdr->addr1); + if (sta) { + if (sta->flags & WLAN_STA_PS) { + /* The STA is in power save mode, so assume + * that this TX packet failed because of that. + */ + status->excessive_retries = 0; + status->flags |= IEEE80211_TX_STATUS_TX_FILTERED; + } + sta_info_put(sta); + } + } + + if (status->flags & IEEE80211_TX_STATUS_TX_FILTERED) { + struct sta_info *sta; + sta = sta_info_get(local, hdr->addr1); + if (sta) { + sta->tx_filtered_count++; + + /* Clear the TX filter mask for this STA when sending + * the next packet. If the STA went to power save mode, + * this will happen when it is waking up for the next + * time. */ + sta->clear_dst_mask = 1; + + /* TODO: Is the WLAN_STA_PS flag always set here or is + * the race between RX and TX status causing some + * packets to be filtered out before 80211.o gets an + * update for PS status? This seems to be the case, so + * no changes are likely to be needed. */ + if (sta->flags & WLAN_STA_PS && + skb_queue_len(&sta->tx_filtered) < + STA_MAX_TX_BUFFER) { + ieee80211_remove_tx_extra(local, sta->key, + skb, + &status->control); + skb_queue_tail(&sta->tx_filtered, skb); + } else if (!(sta->flags & WLAN_STA_PS) && + !(status->control.flags & IEEE80211_TXCTL_REQUEUE)) { + /* Software retry the packet once */ + status->control.flags |= IEEE80211_TXCTL_REQUEUE; + ieee80211_remove_tx_extra(local, sta->key, + skb, + &status->control); + dev_queue_xmit(skb); + } else { + if (net_ratelimit()) { + printk(KERN_DEBUG "%s: dropped TX " + "filtered frame queue_len=%d " + "PS=%d @%lu\n", + local->mdev->name, + skb_queue_len( + &sta->tx_filtered), + !!(sta->flags & WLAN_STA_PS), + jiffies); + } + dev_kfree_skb(skb); + } + sta_info_put(sta); + return; + } + } else { + /* FIXME: STUPID to call this with both local and local->mdev */ + rate_control_tx_status(local, local->mdev, skb, status); + } + + ieee80211_led_tx(local, 0); + + /* SNMP counters + * Fragments are passed to low-level drivers as separate skbs, so these + * are actually fragments, not frames. Update frame counters only for + * the first fragment of the frame. */ + + frag = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG; + type = le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_FTYPE; + + if (status->flags & IEEE80211_TX_STATUS_ACK) { + if (frag == 0) { + local->dot11TransmittedFrameCount++; + if (is_multicast_ether_addr(hdr->addr1)) + local->dot11MulticastTransmittedFrameCount++; + if (status->retry_count > 0) + local->dot11RetryCount++; + if (status->retry_count > 1) + local->dot11MultipleRetryCount++; + } + + /* This counter shall be incremented for an acknowledged MPDU + * with an individual address in the address 1 field or an MPDU + * with a multicast address in the address 1 field of type Data + * or Management. */ + if (!is_multicast_ether_addr(hdr->addr1) || + type == IEEE80211_FTYPE_DATA || + type == IEEE80211_FTYPE_MGMT) + local->dot11TransmittedFragmentCount++; + } else { + if (frag == 0) + local->dot11FailedCount++; + } + + if (!(status->control.flags & IEEE80211_TXCTL_REQ_TX_STATUS) + || unlikely(!local->apdev)) { + dev_kfree_skb(skb); + return; + } + + msg_type = (status->flags & IEEE80211_TX_STATUS_ACK) ? + ieee80211_msg_tx_callback_ack : ieee80211_msg_tx_callback_fail; + + /* skb was the original skb used for TX. Clone it and give the clone + * to netif_rx(). Free original skb. */ + skb2 = skb_copy(skb, GFP_ATOMIC); + if (!skb2) { + dev_kfree_skb(skb); + return; + } + dev_kfree_skb(skb); + skb = skb2; + + /* Send frame to hostapd */ + ieee80211_rx_mgmt(local, skb, NULL, msg_type); +} +EXPORT_SYMBOL(ieee80211_tx_status); + +/* TODO: implement register/unregister functions for adding TX/RX handlers + * into ordered list */ + +/* rx_pre handlers don't have dev and sdata fields available in + * ieee80211_txrx_data */ +static ieee80211_rx_handler ieee80211_rx_pre_handlers[] = +{ + ieee80211_rx_h_parse_qos, + ieee80211_rx_h_load_stats, + NULL +}; + +static ieee80211_rx_handler ieee80211_rx_handlers[] = +{ + ieee80211_rx_h_if_stats, + ieee80211_rx_h_monitor, + ieee80211_rx_h_passive_scan, + ieee80211_rx_h_check, + ieee80211_rx_h_sta_process, + ieee80211_rx_h_ccmp_decrypt, + ieee80211_rx_h_tkip_decrypt, + ieee80211_rx_h_wep_weak_iv_detection, + ieee80211_rx_h_wep_decrypt, + ieee80211_rx_h_defragment, + ieee80211_rx_h_ps_poll, + ieee80211_rx_h_michael_mic_verify, + /* this must be after decryption - so header is counted in MPDU mic + * must be before pae and data, so QOS_DATA format frames + * are not passed to user space by these functions + */ + ieee80211_rx_h_remove_qos_control, + ieee80211_rx_h_802_1x_pae, + ieee80211_rx_h_drop_unencrypted, + ieee80211_rx_h_data_agg, + ieee80211_rx_h_data, + ieee80211_rx_h_mgmt, + NULL +}; + +static ieee80211_tx_handler ieee80211_tx_handlers[] = +{ + ieee80211_tx_h_check_assoc, + ieee80211_tx_h_sequence, + ieee80211_tx_h_ps_buf, + ieee80211_tx_h_select_key, + ieee80211_tx_h_michael_mic_add, + ieee80211_tx_h_fragment, + ieee80211_tx_h_tkip_encrypt, + ieee80211_tx_h_ccmp_encrypt, + ieee80211_tx_h_wep_encrypt, + ieee80211_tx_h_rate_ctrl, + ieee80211_tx_h_misc, + ieee80211_tx_h_load_stats, + NULL +}; + + +int ieee80211_if_update_wds(struct net_device *dev, u8 *remote_addr) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct sta_info *sta; + + if (compare_ether_addr(remote_addr, sdata->u.wds.remote_addr) == 0) + return 0; + + /* Create STA entry for the new peer */ + sta = sta_info_add(local, dev, remote_addr, GFP_KERNEL); + if (!sta) + return -ENOMEM; + sta_info_put(sta); + + /* Remove STA entry for the old peer */ + sta = sta_info_get(local, sdata->u.wds.remote_addr); + if (sta) { + sta_info_put(sta); + sta_info_free(sta, 0); + } else { + printk(KERN_DEBUG "%s: could not find STA entry for WDS link " + "peer " MAC_FMT "\n", + dev->name, MAC_ARG(sdata->u.wds.remote_addr)); + } + + /* Update WDS link data */ + memcpy(&sdata->u.wds.remote_addr, remote_addr, ETH_ALEN); + + return 0; +} + +/* Must not be called for mdev and apdev */ +void ieee80211_if_setup(struct net_device *dev) +{ + ether_setup(dev); + dev->hard_start_xmit = ieee80211_subif_start_xmit; + dev->wireless_handlers = &ieee80211_iw_handler_def; + dev->do_ioctl = ieee80211_ioctl; + dev->set_multicast_list = ieee80211_set_multicast_list; + dev->change_mtu = ieee80211_change_mtu; + dev->get_stats = ieee80211_get_stats; + dev->open = ieee80211_open; + dev->stop = ieee80211_stop; + dev->uninit = ieee80211_if_reinit; + dev->destructor = ieee80211_if_free; +} + +void ieee80211_if_mgmt_setup(struct net_device *dev) +{ + ether_setup(dev); + dev->hard_start_xmit = ieee80211_mgmt_start_xmit; + dev->change_mtu = ieee80211_change_mtu_apdev; + dev->get_stats = ieee80211_get_stats; + dev->open = ieee80211_mgmt_open; + dev->stop = ieee80211_mgmt_stop; + dev->type = ARPHRD_IEEE80211_PRISM; + dev->hard_header_parse = header_parse_80211; + dev->uninit = ieee80211_if_reinit; + dev->destructor = ieee80211_if_free; +} + +int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local, + const char *name) +{ + struct rate_control_ref *ref, *old; + + ASSERT_RTNL(); + if (local->open_count || netif_running(local->mdev) || + (local->apdev && netif_running(local->apdev))) + return -EBUSY; + + ref = rate_control_alloc(name, local); + if (!ref) { + printk(KERN_WARNING "%s: Failed to select rate control " + "algorithm\n", local->mdev->name); + return -ENOENT; + } + + old = local->rate_ctrl; + local->rate_ctrl = ref; + if (old) { + rate_control_put(old); + sta_info_flush(local, NULL); + } + + printk(KERN_DEBUG "%s: Selected rate control " + "algorithm '%s'\n", local->mdev->name, + ref->ops->name); + + + return 0; +} + +static void rate_control_deinitialize(struct ieee80211_local *local) +{ + struct rate_control_ref *ref; + + ref = local->rate_ctrl; + local->rate_ctrl = NULL; + rate_control_put(ref); +} + +struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, + const struct ieee80211_ops *ops) +{ + struct net_device *mdev; + struct ieee80211_local *local; + struct ieee80211_sub_if_data *sdata; + int priv_size; + struct wiphy *wiphy; + + /* Ensure 32-byte alignment of our private data and hw private data. + * We use the wiphy priv data for both our ieee80211_local and for + * the driver's private data + * + * In memory it'll be like this: + * + * +-------------------------+ + * | struct wiphy | + * +-------------------------+ + * | struct ieee80211_local | + * +-------------------------+ + * | driver's private data | + * +-------------------------+ + * + */ + priv_size = ((sizeof(struct ieee80211_local) + + NETDEV_ALIGN_CONST) & ~NETDEV_ALIGN_CONST) + + priv_data_len; + + wiphy = wiphy_new(&mac80211_config_ops, priv_size); + + if (!wiphy) + return NULL; + + wiphy->privid = mac80211_wiphy_privid; + + local = wiphy_priv(wiphy); + local->hw.wiphy = wiphy; + + local->hw.priv = (char *)local + + ((sizeof(struct ieee80211_local) + + NETDEV_ALIGN_CONST) & ~NETDEV_ALIGN_CONST); + + local->ops = ops; + + /* for now, mdev needs sub_if_data :/ */ + mdev = alloc_netdev(sizeof(struct ieee80211_sub_if_data), + "wmaster%d", ether_setup); + if (!mdev) { + wiphy_free(wiphy); + return NULL; + } + + sdata = IEEE80211_DEV_TO_SUB_IF(mdev); + mdev->ieee80211_ptr = &sdata->wdev; + sdata->wdev.wiphy = wiphy; + + local->hw.queues = 1; /* default */ + + local->mdev = mdev; + local->rx_pre_handlers = ieee80211_rx_pre_handlers; + local->rx_handlers = ieee80211_rx_handlers; + local->tx_handlers = ieee80211_tx_handlers; + + local->bridge_packets = 1; + + local->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD; + local->fragmentation_threshold = IEEE80211_MAX_FRAG_THRESHOLD; + local->short_retry_limit = 7; + local->long_retry_limit = 4; + local->hw.conf.radio_enabled = 1; + local->rate_ctrl_num_up = RATE_CONTROL_NUM_UP; + local->rate_ctrl_num_down = RATE_CONTROL_NUM_DOWN; + + local->enabled_modes = (unsigned int) -1; + + INIT_LIST_HEAD(&local->modes_list); + + rwlock_init(&local->sub_if_lock); + INIT_LIST_HEAD(&local->sub_if_list); + + INIT_DELAYED_WORK(&local->scan_work, ieee80211_sta_scan_work); + init_timer(&local->stat_timer); + local->stat_timer.function = ieee80211_stat_refresh; + local->stat_timer.data = (unsigned long) local; + ieee80211_rx_bss_list_init(mdev); + + sta_info_init(local); + + mdev->hard_start_xmit = ieee80211_master_start_xmit; + mdev->open = ieee80211_master_open; + mdev->stop = ieee80211_master_stop; + mdev->type = ARPHRD_IEEE80211; + mdev->hard_header_parse = header_parse_80211; + + sdata->type = IEEE80211_IF_TYPE_AP; + sdata->dev = mdev; + sdata->local = local; + sdata->u.ap.force_unicast_rateidx = -1; + sdata->u.ap.max_ratectrl_rateidx = -1; + ieee80211_if_sdata_init(sdata); + list_add_tail(&sdata->list, &local->sub_if_list); + + tasklet_init(&local->tx_pending_tasklet, ieee80211_tx_pending, + (unsigned long)local); + tasklet_disable(&local->tx_pending_tasklet); + + tasklet_init(&local->tasklet, + ieee80211_tasklet_handler, + (unsigned long) local); + tasklet_disable(&local->tasklet); + + skb_queue_head_init(&local->skb_queue); + skb_queue_head_init(&local->skb_queue_unreliable); + + return local_to_hw(local); +} +EXPORT_SYMBOL(ieee80211_alloc_hw); + +int ieee80211_register_hw(struct ieee80211_hw *hw) +{ + struct ieee80211_local *local = hw_to_local(hw); + const char *name; + int result; + + result = wiphy_register(local->hw.wiphy); + if (result < 0) + return result; + + name = wiphy_dev(local->hw.wiphy)->driver->name; + local->hw.workqueue = create_singlethread_workqueue(name); + if (!local->hw.workqueue) { + result = -ENOMEM; + goto fail_workqueue; + } + + debugfs_hw_add(local); + + local->hw.conf.beacon_int = 1000; + + local->wstats_flags |= local->hw.max_rssi ? + IW_QUAL_LEVEL_UPDATED : IW_QUAL_LEVEL_INVALID; + local->wstats_flags |= local->hw.max_signal ? + IW_QUAL_QUAL_UPDATED : IW_QUAL_QUAL_INVALID; + local->wstats_flags |= local->hw.max_noise ? + IW_QUAL_NOISE_UPDATED : IW_QUAL_NOISE_INVALID; + if (local->hw.max_rssi < 0 || local->hw.max_noise < 0) + local->wstats_flags |= IW_QUAL_DBM; + + result = sta_info_start(local); + if (result < 0) + goto fail_sta_info; + + rtnl_lock(); + result = dev_alloc_name(local->mdev, local->mdev->name); + if (result < 0) + goto fail_dev; + + memcpy(local->mdev->dev_addr, local->hw.wiphy->perm_addr, ETH_ALEN); + SET_NETDEV_DEV(local->mdev, wiphy_dev(local->hw.wiphy)); + + result = register_netdevice(local->mdev); + if (result < 0) + goto fail_dev; + + ieee80211_debugfs_add_netdev(IEEE80211_DEV_TO_SUB_IF(local->mdev)); + + result = ieee80211_init_rate_ctrl_alg(local, NULL); + if (result < 0) { + printk(KERN_DEBUG "%s: Failed to initialize rate control " + "algorithm\n", local->mdev->name); + goto fail_rate; + } + + result = ieee80211_wep_init(local); + + if (result < 0) { + printk(KERN_DEBUG "%s: Failed to initialize wep\n", + local->mdev->name); + goto fail_wep; + } + + ieee80211_install_qdisc(local->mdev); + + /* add one default STA interface */ + result = ieee80211_if_add(local->mdev, "wlan%d", NULL, + IEEE80211_IF_TYPE_STA); + if (result) + printk(KERN_WARNING "%s: Failed to add default virtual iface\n", + local->mdev->name); + + local->reg_state = IEEE80211_DEV_REGISTERED; + rtnl_unlock(); + + ieee80211_led_init(local); + + return 0; + +fail_wep: + rate_control_deinitialize(local); +fail_rate: + ieee80211_debugfs_remove_netdev(IEEE80211_DEV_TO_SUB_IF(local->mdev)); + unregister_netdevice(local->mdev); +fail_dev: + rtnl_unlock(); + sta_info_stop(local); +fail_sta_info: + debugfs_hw_del(local); + destroy_workqueue(local->hw.workqueue); +fail_workqueue: + wiphy_unregister(local->hw.wiphy); + return result; +} +EXPORT_SYMBOL(ieee80211_register_hw); + +int ieee80211_register_hwmode(struct ieee80211_hw *hw, + struct ieee80211_hw_mode *mode) +{ + struct ieee80211_local *local = hw_to_local(hw); + struct ieee80211_rate *rate; + int i; + + INIT_LIST_HEAD(&mode->list); + list_add_tail(&mode->list, &local->modes_list); + + local->hw_modes |= (1 << mode->mode); + for (i = 0; i < mode->num_rates; i++) { + rate = &(mode->rates[i]); + rate->rate_inv = CHAN_UTIL_RATE_LCM / rate->rate; + } + ieee80211_prepare_rates(local, mode); + + if (!local->oper_hw_mode) { + /* Default to this mode */ + local->hw.conf.phymode = mode->mode; + local->oper_hw_mode = local->scan_hw_mode = mode; + local->oper_channel = local->scan_channel = &mode->channels[0]; + local->hw.conf.mode = local->oper_hw_mode; + local->hw.conf.chan = local->oper_channel; + } + + if (!(hw->flags & IEEE80211_HW_DEFAULT_REG_DOMAIN_CONFIGURED)) + ieee80211_init_client(local->mdev); + + return 0; +} +EXPORT_SYMBOL(ieee80211_register_hwmode); + +void ieee80211_unregister_hw(struct ieee80211_hw *hw) +{ + struct ieee80211_local *local = hw_to_local(hw); + struct ieee80211_sub_if_data *sdata, *tmp; + struct list_head tmp_list; + int i; + + tasklet_kill(&local->tx_pending_tasklet); + tasklet_kill(&local->tasklet); + + rtnl_lock(); + + BUG_ON(local->reg_state != IEEE80211_DEV_REGISTERED); + + local->reg_state = IEEE80211_DEV_UNREGISTERED; + if (local->apdev) + ieee80211_if_del_mgmt(local); + + write_lock_bh(&local->sub_if_lock); + list_replace_init(&local->sub_if_list, &tmp_list); + write_unlock_bh(&local->sub_if_lock); + + list_for_each_entry_safe(sdata, tmp, &tmp_list, list) + __ieee80211_if_del(local, sdata); + + rtnl_unlock(); + + if (local->stat_time) + del_timer_sync(&local->stat_timer); + + ieee80211_rx_bss_list_deinit(local->mdev); + ieee80211_clear_tx_pending(local); + sta_info_stop(local); + rate_control_deinitialize(local); + debugfs_hw_del(local); + + for (i = 0; i < NUM_IEEE80211_MODES; i++) { + kfree(local->supp_rates[i]); + kfree(local->basic_rates[i]); + } + + if (skb_queue_len(&local->skb_queue) + || skb_queue_len(&local->skb_queue_unreliable)) + printk(KERN_WARNING "%s: skb_queue not empty\n", + local->mdev->name); + skb_queue_purge(&local->skb_queue); + skb_queue_purge(&local->skb_queue_unreliable); + + destroy_workqueue(local->hw.workqueue); + wiphy_unregister(local->hw.wiphy); + ieee80211_wep_free(local); + ieee80211_led_exit(local); +} +EXPORT_SYMBOL(ieee80211_unregister_hw); + +void ieee80211_free_hw(struct ieee80211_hw *hw) +{ + struct ieee80211_local *local = hw_to_local(hw); + + ieee80211_if_free(local->mdev); + wiphy_free(local->hw.wiphy); +} +EXPORT_SYMBOL(ieee80211_free_hw); + +void ieee80211_wake_queue(struct ieee80211_hw *hw, int queue) +{ + struct ieee80211_local *local = hw_to_local(hw); + + if (test_and_clear_bit(IEEE80211_LINK_STATE_XOFF, + &local->state[queue])) { + if (test_bit(IEEE80211_LINK_STATE_PENDING, + &local->state[queue])) + tasklet_schedule(&local->tx_pending_tasklet); + else + __netif_schedule(local->mdev); + } +} +EXPORT_SYMBOL(ieee80211_wake_queue); + +void ieee80211_stop_queue(struct ieee80211_hw *hw, int queue) +{ + struct ieee80211_local *local = hw_to_local(hw); + + set_bit(IEEE80211_LINK_STATE_XOFF, &local->state[queue]); +} +EXPORT_SYMBOL(ieee80211_stop_queue); + +void ieee80211_start_queues(struct ieee80211_hw *hw) +{ + struct ieee80211_local *local = hw_to_local(hw); + int i; + + for (i = 0; i < local->hw.queues; i++) + clear_bit(IEEE80211_LINK_STATE_XOFF, &local->state[i]); +} +EXPORT_SYMBOL(ieee80211_start_queues); + +void ieee80211_stop_queues(struct ieee80211_hw *hw) +{ + int i; + + for (i = 0; i < hw->queues; i++) + ieee80211_stop_queue(hw, i); +} +EXPORT_SYMBOL(ieee80211_stop_queues); + +void ieee80211_wake_queues(struct ieee80211_hw *hw) +{ + int i; + + for (i = 0; i < hw->queues; i++) + ieee80211_wake_queue(hw, i); +} +EXPORT_SYMBOL(ieee80211_wake_queues); + +struct net_device_stats *ieee80211_dev_stats(struct net_device *dev) +{ + struct ieee80211_sub_if_data *sdata; + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + return &sdata->stats; +} + +static int __init ieee80211_init(void) +{ + struct sk_buff *skb; + int ret; + + BUILD_BUG_ON(sizeof(struct ieee80211_tx_packet_data) > sizeof(skb->cb)); + + ret = ieee80211_wme_register(); + if (ret) { + printk(KERN_DEBUG "ieee80211_init: failed to " + "initialize WME (err=%d)\n", ret); + return ret; + } + + ieee80211_debugfs_netdev_init(); + + return 0; +} + + +static void __exit ieee80211_exit(void) +{ + ieee80211_wme_unregister(); + ieee80211_debugfs_netdev_exit(); +} + + +module_init(ieee80211_init); +module_exit(ieee80211_exit); + +MODULE_DESCRIPTION("IEEE 802.11 subsystem"); +MODULE_LICENSE("GPL"); diff --git a/net/mac80211/ieee80211_cfg.c b/net/mac80211/ieee80211_cfg.c new file mode 100644 index 0000000..0069826 --- /dev/null +++ b/net/mac80211/ieee80211_cfg.c @@ -0,0 +1,72 @@ +/* + * mac80211 configuration hooks for cfg80211 + * + * Copyright 2006 Johannes Berg + * + * This file is GPLv2 as found in COPYING. + */ + +#include +#include +#include +#include "ieee80211_i.h" +#include "ieee80211_cfg.h" + +static int ieee80211_add_iface(struct wiphy *wiphy, char *name, + unsigned int type) +{ + struct ieee80211_local *local = wiphy_priv(wiphy); + int itype; + + if (unlikely(local->reg_state != IEEE80211_DEV_REGISTERED)) + return -ENODEV; + + switch (type) { + case NL80211_IFTYPE_UNSPECIFIED: + itype = IEEE80211_IF_TYPE_STA; + break; + case NL80211_IFTYPE_ADHOC: + itype = IEEE80211_IF_TYPE_IBSS; + break; + case NL80211_IFTYPE_STATION: + itype = IEEE80211_IF_TYPE_STA; + break; + case NL80211_IFTYPE_AP: + itype = IEEE80211_IF_TYPE_AP; + break; + case NL80211_IFTYPE_WDS: + itype = IEEE80211_IF_TYPE_WDS; + break; + case NL80211_IFTYPE_MONITOR: + itype = IEEE80211_IF_TYPE_MNTR; + break; + default: + return -EINVAL; + } + + return ieee80211_if_add(local->mdev, name, NULL, itype); +} + +static int ieee80211_del_iface(struct wiphy *wiphy, int ifindex) +{ + struct ieee80211_local *local = wiphy_priv(wiphy); + struct net_device *dev; + char *name; + + if (unlikely(local->reg_state != IEEE80211_DEV_REGISTERED)) + return -ENODEV; + + dev = dev_get_by_index(ifindex); + if (!dev) + return 0; + + name = dev->name; + dev_put(dev); + + return ieee80211_if_remove(local->mdev, name, -1); +} + +struct cfg80211_ops mac80211_config_ops = { + .add_virtual_intf = ieee80211_add_iface, + .del_virtual_intf = ieee80211_del_iface, +}; diff --git a/net/mac80211/ieee80211_cfg.h b/net/mac80211/ieee80211_cfg.h new file mode 100644 index 0000000..85ed2c9 --- /dev/null +++ b/net/mac80211/ieee80211_cfg.h @@ -0,0 +1,9 @@ +/* + * mac80211 configuration hooks for cfg80211 + */ +#ifndef __IEEE80211_CFG_H +#define __IEEE80211_CFG_H + +extern struct cfg80211_ops mac80211_config_ops; + +#endif /* __IEEE80211_CFG_H */ diff --git a/net/mac80211/ieee80211_common.h b/net/mac80211/ieee80211_common.h new file mode 100644 index 0000000..b9a73e7 --- /dev/null +++ b/net/mac80211/ieee80211_common.h @@ -0,0 +1,98 @@ +/* + * IEEE 802.11 driver (80211.o) -- hostapd interface + * Copyright 2002-2004, Instant802 Networks, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef IEEE80211_COMMON_H +#define IEEE80211_COMMON_H + +#include + +/* + * This is common header information with user space. It is used on all + * frames sent to wlan#ap interface. + */ + +#define IEEE80211_FI_VERSION 0x80211001 + +struct ieee80211_frame_info { + __be32 version; + __be32 length; + __be64 mactime; + __be64 hosttime; + __be32 phytype; + __be32 channel; + __be32 datarate; + __be32 antenna; + __be32 priority; + __be32 ssi_type; + __be32 ssi_signal; + __be32 ssi_noise; + __be32 preamble; + __be32 encoding; + + /* Note: this structure is otherwise identical to capture format used + * in linux-wlan-ng, but this additional field is used to provide meta + * data about the frame to hostapd. This was the easiest method for + * providing this information, but this might change in the future. */ + __be32 msg_type; +} __attribute__ ((packed)); + + +enum ieee80211_msg_type { + ieee80211_msg_normal = 0, + ieee80211_msg_tx_callback_ack = 1, + ieee80211_msg_tx_callback_fail = 2, + ieee80211_msg_passive_scan = 3, + ieee80211_msg_wep_frame_unknown_key = 4, + ieee80211_msg_michael_mic_failure = 5, + /* hole at 6, was monitor but never sent to userspace */ + ieee80211_msg_sta_not_assoc = 7, + ieee80211_msg_set_aid_for_sta = 8 /* used by Intersil MVC driver */, + ieee80211_msg_key_threshold_notification = 9, + ieee80211_msg_radar = 11, +}; + +struct ieee80211_msg_set_aid_for_sta { + char sta_address[ETH_ALEN]; + u16 aid; +}; + +struct ieee80211_msg_key_notification { + int tx_rx_count; + char ifname[IFNAMSIZ]; + u8 addr[ETH_ALEN]; /* ff:ff:ff:ff:ff:ff for broadcast keys */ +}; + + +enum ieee80211_phytype { + ieee80211_phytype_fhss_dot11_97 = 1, + ieee80211_phytype_dsss_dot11_97 = 2, + ieee80211_phytype_irbaseband = 3, + ieee80211_phytype_dsss_dot11_b = 4, + ieee80211_phytype_pbcc_dot11_b = 5, + ieee80211_phytype_ofdm_dot11_g = 6, + ieee80211_phytype_pbcc_dot11_g = 7, + ieee80211_phytype_ofdm_dot11_a = 8, + ieee80211_phytype_dsss_dot11_turbog = 255, + ieee80211_phytype_dsss_dot11_turbo = 256, +}; + +enum ieee80211_ssi_type { + ieee80211_ssi_none = 0, + ieee80211_ssi_norm = 1, /* normalized, 0-1000 */ + ieee80211_ssi_dbm = 2, + ieee80211_ssi_raw = 3, /* raw SSI */ +}; + +struct ieee80211_radar_info { + int channel; + int radar; + int radar_type; +}; + +#endif /* IEEE80211_COMMON_H */ diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h new file mode 100644 index 0000000..8b939d0 --- /dev/null +++ b/net/mac80211/ieee80211_i.h @@ -0,0 +1,814 @@ +/* + * Copyright 2002-2005, Instant802 Networks, Inc. + * Copyright 2005, Devicescape Software, Inc. + * Copyright 2006-2007 Jiri Benc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef IEEE80211_I_H +#define IEEE80211_I_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ieee80211_key.h" +#include "sta_info.h" + +/* ieee80211.o internal definitions, etc. These are not included into + * low-level drivers. */ + +#ifndef ETH_P_PAE +#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ +#endif /* ETH_P_PAE */ + +#define WLAN_FC_DATA_PRESENT(fc) (((fc) & 0x4c) == 0x08) + +struct ieee80211_local; + +#define BIT(x) (1 << (x)) + +#define IEEE80211_ALIGN32_PAD(a) ((4 - ((a) & 3)) & 3) + +/* Maximum number of broadcast/multicast frames to buffer when some of the + * associated stations are using power saving. */ +#define AP_MAX_BC_BUFFER 128 + +/* Maximum number of frames buffered to all STAs, including multicast frames. + * Note: increasing this limit increases the potential memory requirement. Each + * frame can be up to about 2 kB long. */ +#define TOTAL_MAX_TX_BUFFER 512 + +/* Required encryption head and tailroom */ +#define IEEE80211_ENCRYPT_HEADROOM 8 +#define IEEE80211_ENCRYPT_TAILROOM 12 + +/* IEEE 802.11 (Ch. 9.5 Defragmentation) requires support for concurrent + * reception of at least three fragmented frames. This limit can be increased + * by changing this define, at the cost of slower frame reassembly and + * increased memory use (about 2 kB of RAM per entry). */ +#define IEEE80211_FRAGMENT_MAX 4 + +struct ieee80211_fragment_entry { + unsigned long first_frag_time; + unsigned int seq; + unsigned int rx_queue; + unsigned int last_frag; + unsigned int extra_len; + struct sk_buff_head skb_list; + int ccmp; /* Whether fragments were encrypted with CCMP */ + u8 last_pn[6]; /* PN of the last fragment if CCMP was used */ +}; + + +struct ieee80211_sta_bss { + struct list_head list; + struct ieee80211_sta_bss *hnext; + atomic_t users; + + u8 bssid[ETH_ALEN]; + u8 ssid[IEEE80211_MAX_SSID_LEN]; + size_t ssid_len; + u16 capability; /* host byte order */ + int hw_mode; + int channel; + int freq; + int rssi, signal, noise; + u8 *wpa_ie; + size_t wpa_ie_len; + u8 *rsn_ie; + size_t rsn_ie_len; + u8 *wmm_ie; + size_t wmm_ie_len; + u8 *ht_ie; + size_t ht_ie_len; +#define IEEE80211_MAX_SUPP_RATES 32 + u8 supp_rates[IEEE80211_MAX_SUPP_RATES]; + size_t supp_rates_len; + int beacon_int; + u64 timestamp; + + int probe_resp; + unsigned long last_update; + +}; + + +typedef enum { + TXRX_CONTINUE, TXRX_DROP, TXRX_QUEUED +} ieee80211_txrx_result; + +struct ieee80211_txrx_data { + struct sk_buff *skb; + struct net_device *dev; + struct ieee80211_local *local; + struct ieee80211_sub_if_data *sdata; + struct sta_info *sta; + u16 fc, ethertype; + struct ieee80211_key *key; + unsigned int fragmented:1; /* whether the MSDU was fragmented */ + union { + struct { + struct ieee80211_tx_control *control; + unsigned int unicast:1; + unsigned int ps_buffered:1; + unsigned int short_preamble:1; + unsigned int probe_last_frag:1; + struct ieee80211_hw_mode *mode; + struct ieee80211_rate *rate; + /* use this rate (if set) for last fragment; rate can + * be set to lower rate for the first fragments, e.g., + * when using CTS protection with IEEE 802.11g. */ + struct ieee80211_rate *last_frag_rate; + int last_frag_hwrate; + int mgmt_interface; + + /* Extra fragments (in addition to the first fragment + * in skb) */ + int num_extra_frag; + struct sk_buff **extra_frag; + } tx; + struct { + struct ieee80211_rx_status *status; + int sent_ps_buffered; + int queue; + int load; + u16 qos_control; + unsigned int in_scan:1; + /* frame is destined to interface currently processed + * (including multicast frames) */ + unsigned int ra_match:1; + unsigned int is_agg_frame:1; + } rx; + } u; +#ifdef CONFIG_HOSTAPD_WPA_TESTING + int wpa_test; +#endif /* CONFIG_HOSTAPD_WPA_TESTING */ +}; + +/* Stored in sk_buff->cb */ +struct ieee80211_tx_packet_data { + int ifindex; + unsigned long jiffies; + unsigned int req_tx_status:1; + unsigned int do_not_encrypt:1; + unsigned int requeue:1; + unsigned int mgmt_iface:1; + unsigned int queue:4; +}; + +struct ieee80211_tx_stored_packet { + struct ieee80211_tx_control control; + struct sk_buff *skb; + int num_extra_frag; + struct sk_buff **extra_frag; + int last_frag_rateidx; + int last_frag_hwrate; + struct ieee80211_rate *last_frag_rate; + unsigned int last_frag_rate_ctrl_probe:1; +}; + +typedef ieee80211_txrx_result (*ieee80211_tx_handler) +(struct ieee80211_txrx_data *tx); + +typedef ieee80211_txrx_result (*ieee80211_rx_handler) +(struct ieee80211_txrx_data *rx); + +struct ieee80211_if_ap { + u8 *beacon_head, *beacon_tail; + int beacon_head_len, beacon_tail_len; + + u8 ssid[IEEE80211_MAX_SSID_LEN]; + size_t ssid_len; + u8 *generic_elem; + size_t generic_elem_len; + + /* yes, this looks ugly, but guarantees that we can later use + * bitmap_empty :) + * NB: don't ever use set_bit, use bss_tim_set/bss_tim_clear! */ + u8 tim[sizeof(unsigned long) * BITS_TO_LONGS(IEEE80211_MAX_AID + 1)]; + atomic_t num_sta_ps; /* number of stations in PS mode */ + struct sk_buff_head ps_bc_buf; + int dtim_period, dtim_count; + int force_unicast_rateidx; /* forced TX rateidx for unicast frames */ + int max_ratectrl_rateidx; /* max TX rateidx for rate control */ + int num_beacons; /* number of TXed beacon frames for this BSS */ +}; + +struct ieee80211_if_wds { + u8 remote_addr[ETH_ALEN]; + struct sta_info *sta; +}; + +struct ieee80211_if_vlan { + u8 id; +}; + +struct ieee80211_if_sta { + enum { + IEEE80211_DISABLED, IEEE80211_AUTHENTICATE, + IEEE80211_ASSOCIATE, IEEE80211_ASSOCIATED, + IEEE80211_IBSS_SEARCH, IEEE80211_IBSS_JOINED + } state; + struct timer_list timer; + struct work_struct work; + u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN]; + u8 ssid[IEEE80211_MAX_SSID_LEN]; + size_t ssid_len; + u16 aid; + u16 ap_capab, capab; + u8 *extra_ie; /* to be added to the end of AssocReq */ + size_t extra_ie_len; + + /* The last AssocReq/Resp IEs */ + u8 *assocreq_ies, *assocresp_ies; + size_t assocreq_ies_len, assocresp_ies_len; + + int auth_tries, assoc_tries; + + unsigned int ssid_set:1; + unsigned int bssid_set:1; + unsigned int prev_bssid_set:1; + unsigned int authenticated:1; + unsigned int associated:1; + unsigned int probereq_poll:1; + unsigned int use_protection:1; + unsigned int create_ibss:1; + unsigned int mixed_cell:1; + unsigned int wmm_enabled:1; + unsigned int ht_enabled:1; + unsigned int auto_ssid_sel:1; + unsigned int auto_bssid_sel:1; + unsigned int auto_channel_sel:1; +#define IEEE80211_STA_REQ_SCAN 0 +#define IEEE80211_STA_REQ_AUTH 1 +#define IEEE80211_STA_REQ_RUN 2 + unsigned long request; + struct sk_buff_head skb_queue; + + int key_mgmt; + unsigned long last_probe; + +#define IEEE80211_AUTH_ALG_OPEN BIT(0) +#define IEEE80211_AUTH_ALG_SHARED_KEY BIT(1) +#define IEEE80211_AUTH_ALG_LEAP BIT(2) + unsigned int auth_algs; /* bitfield of allowed auth algs */ + int auth_alg; /* currently used IEEE 802.11 authentication algorithm */ + int auth_transaction; + + unsigned long ibss_join_req; + struct sk_buff *probe_resp; /* ProbeResp template for IBSS */ + u32 supp_rates_bits; + + int wmm_last_param_set; +}; + + +struct ieee80211_sub_if_data { + struct list_head list; + unsigned int type; + + struct wireless_dev wdev; + + struct net_device *dev; + struct ieee80211_local *local; + + int mc_count; + unsigned int allmulti:1; + unsigned int promisc:1; + + struct net_device_stats stats; + int drop_unencrypted; + int eapol; /* 0 = process EAPOL frames as normal data frames, + * 1 = send EAPOL frames through wlan#ap to hostapd + * (default) */ + int ieee802_1x; /* IEEE 802.1X PAE - drop packet to/from unauthorized + * port */ + + u16 sequence; + + /* Fragment table for host-based reassembly */ + struct ieee80211_fragment_entry fragments[IEEE80211_FRAGMENT_MAX]; + unsigned int fragment_next; + +#define NUM_DEFAULT_KEYS 4 + struct ieee80211_key *keys[NUM_DEFAULT_KEYS]; + struct ieee80211_key *default_key; + + struct ieee80211_if_ap *bss; /* BSS that this device belongs to */ + + union { + struct ieee80211_if_ap ap; + struct ieee80211_if_wds wds; + struct ieee80211_if_vlan vlan; + struct ieee80211_if_sta sta; + } u; + int channel_use; + int channel_use_raw; + +#ifdef CONFIG_MAC80211_DEBUGFS + struct dentry *debugfsdir; + union { + struct { + struct dentry *channel_use; + struct dentry *drop_unencrypted; + struct dentry *eapol; + struct dentry *ieee8021_x; + struct dentry *state; + struct dentry *bssid; + struct dentry *prev_bssid; + struct dentry *ssid_len; + struct dentry *aid; + struct dentry *ap_capab; + struct dentry *capab; + struct dentry *extra_ie_len; + struct dentry *auth_tries; + struct dentry *assoc_tries; + struct dentry *auth_algs; + struct dentry *auth_alg; + struct dentry *auth_transaction; + struct dentry *flags; + } sta; + struct { + struct dentry *channel_use; + struct dentry *drop_unencrypted; + struct dentry *eapol; + struct dentry *ieee8021_x; + struct dentry *num_sta_ps; + struct dentry *dtim_period; + struct dentry *dtim_count; + struct dentry *num_beacons; + struct dentry *force_unicast_rateidx; + struct dentry *max_ratectrl_rateidx; + struct dentry *num_buffered_multicast; + struct dentry *beacon_head_len; + struct dentry *beacon_tail_len; + } ap; + struct { + struct dentry *channel_use; + struct dentry *drop_unencrypted; + struct dentry *eapol; + struct dentry *ieee8021_x; + struct dentry *peer; + } wds; + struct { + struct dentry *channel_use; + struct dentry *drop_unencrypted; + struct dentry *eapol; + struct dentry *ieee8021_x; + struct dentry *vlan_id; + } vlan; + struct { + struct dentry *mode; + } monitor; + struct dentry *default_key; + } debugfs; +#endif +}; + +#define IEEE80211_DEV_TO_SUB_IF(dev) netdev_priv(dev) + +enum { + IEEE80211_RX_MSG = 1, + IEEE80211_TX_STATUS_MSG = 2, +}; + +struct ieee80211_local { + /* embed the driver visible part. + * don't cast (use the static inlines below), but we keep + * it first anyway so they become a no-op */ + struct ieee80211_hw hw; + + const struct ieee80211_ops *ops; + + /* List of registered struct ieee80211_hw_mode */ + struct list_head modes_list; + + struct net_device *mdev; /* wmaster# - "master" 802.11 device */ + struct net_device *apdev; /* wlan#ap - management frames (hostapd) */ + int open_count; + int monitors; + struct iw_statistics wstats; + u8 wstats_flags; + + enum { + IEEE80211_DEV_UNINITIALIZED = 0, + IEEE80211_DEV_REGISTERED, + IEEE80211_DEV_UNREGISTERED, + } reg_state; + + /* Tasklet and skb queue to process calls from IRQ mode. All frames + * added to skb_queue will be processed, but frames in + * skb_queue_unreliable may be dropped if the total length of these + * queues increases over the limit. */ +#define IEEE80211_IRQSAFE_QUEUE_LIMIT 128 + struct tasklet_struct tasklet; + struct sk_buff_head skb_queue; + struct sk_buff_head skb_queue_unreliable; + + /* Station data structures */ + spinlock_t sta_lock; /* mutex for STA data structures */ + int num_sta; /* number of stations in sta_list */ + struct list_head sta_list; + struct list_head deleted_sta_list; + struct sta_info *sta_hash[STA_HASH_SIZE]; + struct timer_list sta_cleanup; + + unsigned long state[NUM_TX_DATA_QUEUES]; + struct ieee80211_tx_stored_packet pending_packet[NUM_TX_DATA_QUEUES]; + struct tasklet_struct tx_pending_tasklet; + + int mc_count; /* total count of multicast entries in all interfaces */ + int iff_allmultis, iff_promiscs; + /* number of interfaces with corresponding IFF_ flags */ + + struct rate_control_ref *rate_ctrl; + + int next_mode; /* MODE_IEEE80211* + * The mode preference for next channel change. This is + * used to select .11g vs. .11b channels (or 4.9 GHz vs. + * .11a) when the channel number is not unique. */ + + /* Supported and basic rate filters for different modes. These are + * pointers to -1 terminated lists and rates in 100 kbps units. */ + int *supp_rates[NUM_IEEE80211_MODES]; + int *basic_rates[NUM_IEEE80211_MODES]; + + int rts_threshold; + int cts_protect_erp_frames; + int fragmentation_threshold; + int short_retry_limit; /* dot11ShortRetryLimit */ + int long_retry_limit; /* dot11LongRetryLimit */ + int short_preamble; /* use short preamble with IEEE 802.11b */ + + struct crypto_blkcipher *wep_tx_tfm; + struct crypto_blkcipher *wep_rx_tfm; + u32 wep_iv; + int key_tx_rx_threshold; /* number of times any key can be used in TX + * or RX before generating a rekey + * notification; 0 = notification disabled. */ + + int bridge_packets; /* bridge packets between associated stations and + * deliver multicast frames both back to wireless + * media and to the local net stack */ + + ieee80211_rx_handler *rx_pre_handlers; + ieee80211_rx_handler *rx_handlers; + ieee80211_tx_handler *tx_handlers; + + rwlock_t sub_if_lock; /* Protects sub_if_list. Cannot be taken under + * sta_bss_lock or sta_lock. */ + struct list_head sub_if_list; + int sta_scanning; + int scan_channel_idx; + enum { SCAN_SET_CHANNEL, SCAN_SEND_PROBE } scan_state; + unsigned long last_scan_completed; + struct delayed_work scan_work; + struct net_device *scan_dev; + struct ieee80211_channel *oper_channel, *scan_channel; + struct ieee80211_hw_mode *oper_hw_mode, *scan_hw_mode; + u8 scan_ssid[IEEE80211_MAX_SSID_LEN]; + size_t scan_ssid_len; + struct list_head sta_bss_list; + struct ieee80211_sta_bss *sta_bss_hash[STA_HASH_SIZE]; + spinlock_t sta_bss_lock; +#define IEEE80211_SCAN_MATCH_SSID BIT(0) +#define IEEE80211_SCAN_WPA_ONLY BIT(1) +#define IEEE80211_SCAN_EXTRA_INFO BIT(2) + int scan_flags; + +#ifdef CONFIG_HOSTAPD_WPA_TESTING + u32 wpa_trigger; +#endif /* CONFIG_HOSTAPD_WPA_TESTING */ + /* SNMP counters */ + /* dot11CountersTable */ + u32 dot11TransmittedFragmentCount; + u32 dot11MulticastTransmittedFrameCount; + u32 dot11FailedCount; + u32 dot11RetryCount; + u32 dot11MultipleRetryCount; + u32 dot11FrameDuplicateCount; + u32 dot11ReceivedFragmentCount; + u32 dot11MulticastReceivedFrameCount; + u32 dot11TransmittedFrameCount; + u32 dot11WEPUndecryptableCount; + +#ifdef CONFIG_MAC80211_LEDS + int tx_led_counter, rx_led_counter; + struct led_trigger *tx_led, *rx_led; + char tx_led_name[32], rx_led_name[32]; +#endif + + u32 channel_use; + u32 channel_use_raw; + u32 stat_time; + struct timer_list stat_timer; + +#ifdef CONFIG_MAC80211_DEBUGFS + struct work_struct sta_debugfs_add; +#endif + + enum { + STA_ANTENNA_SEL_AUTO = 0, + STA_ANTENNA_SEL_SW_CTRL = 1, + STA_ANTENNA_SEL_SW_CTRL_DEBUG = 2 + } sta_antenna_sel; + + int rate_ctrl_num_up, rate_ctrl_num_down; + +#ifdef CONFIG_MAC80211_DEBUG_COUNTERS + /* TX/RX handler statistics */ + unsigned int tx_handlers_drop; + unsigned int tx_handlers_queued; + unsigned int tx_handlers_drop_unencrypted; + unsigned int tx_handlers_drop_fragment; + unsigned int tx_handlers_drop_wep; + unsigned int tx_handlers_drop_not_assoc; + unsigned int tx_handlers_drop_unauth_port; + unsigned int rx_handlers_drop; + unsigned int rx_handlers_queued; + unsigned int rx_handlers_drop_nullfunc; + unsigned int rx_handlers_drop_defrag; + unsigned int rx_handlers_drop_short; + unsigned int rx_handlers_drop_passive_scan; + unsigned int tx_expand_skb_head; + unsigned int tx_expand_skb_head_cloned; + unsigned int rx_expand_skb_head; + unsigned int rx_expand_skb_head2; + unsigned int rx_handlers_fragments; + unsigned int tx_status_drop; + unsigned int wme_rx_queue[NUM_RX_DATA_QUEUES]; + unsigned int wme_tx_queue[NUM_RX_DATA_QUEUES]; +#define I802_DEBUG_INC(c) (c)++ +#else /* CONFIG_MAC80211_DEBUG_COUNTERS */ +#define I802_DEBUG_INC(c) do { } while (0) +#endif /* CONFIG_MAC80211_DEBUG_COUNTERS */ + + + int default_wep_only; /* only default WEP keys are used with this + * interface; this is used to decide when hwaccel + * can be used with default keys */ + int total_ps_buffered; /* total number of all buffered unicast and + * multicast packets for power saving stations + */ + int allow_broadcast_always; /* whether to allow TX of broadcast frames + * even when there are no associated STAs + */ + + int wifi_wme_noack_test; + unsigned int wmm_acm; /* bit field of ACM bits (BIT(802.1D tag)) */ + + unsigned int enabled_modes; /* bitfield of allowed modes; + * (1 << MODE_*) */ + unsigned int hw_modes; /* bitfield of supported hardware modes; + * (1 << MODE_*) */ + + int user_space_mlme; + +#ifdef CONFIG_MAC80211_DEBUGFS + struct local_debugfsdentries { + struct dentry *channel; + struct dentry *frequency; + struct dentry *radar_detect; + struct dentry *antenna_sel_tx; + struct dentry *antenna_sel_rx; + struct dentry *bridge_packets; + struct dentry *key_tx_rx_threshold; + struct dentry *rts_threshold; + struct dentry *fragmentation_threshold; + struct dentry *short_retry_limit; + struct dentry *long_retry_limit; + struct dentry *total_ps_buffered; + struct dentry *mode; + struct dentry *wep_iv; + struct dentry *tx_power_reduction; + struct dentry *modes; + struct dentry *statistics; + struct local_debugfsdentries_statsdentries { + struct dentry *transmitted_fragment_count; + struct dentry *multicast_transmitted_frame_count; + struct dentry *failed_count; + struct dentry *retry_count; + struct dentry *multiple_retry_count; + struct dentry *frame_duplicate_count; + struct dentry *received_fragment_count; + struct dentry *multicast_received_frame_count; + struct dentry *transmitted_frame_count; + struct dentry *wep_undecryptable_count; + struct dentry *num_scans; +#ifdef CONFIG_MAC80211_DEBUG_COUNTERS + struct dentry *tx_handlers_drop; + struct dentry *tx_handlers_queued; + struct dentry *tx_handlers_drop_unencrypted; + struct dentry *tx_handlers_drop_fragment; + struct dentry *tx_handlers_drop_wep; + struct dentry *tx_handlers_drop_not_assoc; + struct dentry *tx_handlers_drop_unauth_port; + struct dentry *rx_handlers_drop; + struct dentry *rx_handlers_queued; + struct dentry *rx_handlers_drop_nullfunc; + struct dentry *rx_handlers_drop_defrag; + struct dentry *rx_handlers_drop_short; + struct dentry *rx_handlers_drop_passive_scan; + struct dentry *tx_expand_skb_head; + struct dentry *tx_expand_skb_head_cloned; + struct dentry *rx_expand_skb_head; + struct dentry *rx_expand_skb_head2; + struct dentry *rx_handlers_fragments; + struct dentry *tx_status_drop; + struct dentry *wme_tx_queue; + struct dentry *wme_rx_queue; +#endif + struct dentry *dot11ACKFailureCount; + struct dentry *dot11RTSFailureCount; + struct dentry *dot11FCSErrorCount; + struct dentry *dot11RTSSuccessCount; + } stats; + struct dentry *stations; + struct dentry *keys; + } debugfs; +#endif +}; + +static inline struct ieee80211_local *hw_to_local( + struct ieee80211_hw *hw) +{ + return container_of(hw, struct ieee80211_local, hw); +} + +static inline struct ieee80211_hw *local_to_hw( + struct ieee80211_local *local) +{ + return &local->hw; +} + +enum ieee80211_link_state_t { + IEEE80211_LINK_STATE_XOFF = 0, + IEEE80211_LINK_STATE_PENDING, +}; + +struct sta_attribute { + struct attribute attr; + ssize_t (*show)(const struct sta_info *, char *buf); + ssize_t (*store)(struct sta_info *, const char *buf, size_t count); +}; + +static inline void __bss_tim_set(struct ieee80211_if_ap *bss, int aid) +{ + /* + * This format has ben mandated by the IEEE specifications, + * so this line may not be changed to use the __set_bit() format. + */ + bss->tim[(aid)/8] |= 1<<((aid) % 8); +} + +static inline void bss_tim_set(struct ieee80211_local *local, + struct ieee80211_if_ap *bss, int aid) +{ + spin_lock_bh(&local->sta_lock); + __bss_tim_set(bss, aid); + spin_unlock_bh(&local->sta_lock); +} + +static inline void __bss_tim_clear(struct ieee80211_if_ap *bss, int aid) +{ + /* + * This format has ben mandated by the IEEE specifications, + * so this line may not be changed to use the __clear_bit() format. + */ + bss->tim[(aid)/8] &= !(1<<((aid) % 8)); +} + +static inline void bss_tim_clear(struct ieee80211_local *local, + struct ieee80211_if_ap *bss, int aid) +{ + spin_lock_bh(&local->sta_lock); + __bss_tim_clear(bss, aid); + spin_unlock_bh(&local->sta_lock); +} + +/** + * ieee80211_is_erp_rate - Check if a rate is an ERP rate + * @phymode: The PHY-mode for this rate (MODE_IEEE80211...) + * @rate: Transmission rate to check, in 100 kbps + * + * Check if a given rate is an Extended Rate PHY (ERP) rate. + */ +static inline int ieee80211_is_erp_rate(int phymode, int rate) +{ + if (phymode == MODE_IEEE80211G) { + if (rate != 10 && rate != 20 && + rate != 55 && rate != 110) + return 1; + } + return 0; +} + +/* ieee80211.c */ +int ieee80211_hw_config(struct ieee80211_local *local); +int ieee80211_if_config(struct net_device *dev); +int ieee80211_if_config_beacon(struct net_device *dev); +struct ieee80211_key_conf * +ieee80211_key_data2conf(struct ieee80211_local *local, + const struct ieee80211_key *data); +struct ieee80211_key *ieee80211_key_alloc(struct ieee80211_sub_if_data *sdata, + int idx, size_t key_len, gfp_t flags); +void ieee80211_key_free(struct ieee80211_key *key); +void ieee80211_rx_mgmt(struct ieee80211_local *local, struct sk_buff *skb, + struct ieee80211_rx_status *status, u32 msg_type); +void ieee80211_prepare_rates(struct ieee80211_local *local, + struct ieee80211_hw_mode *mode); +void ieee80211_tx_set_iswep(struct ieee80211_txrx_data *tx); +int ieee80211_if_update_wds(struct net_device *dev, u8 *remote_addr); +void ieee80211_if_setup(struct net_device *dev); +void ieee80211_if_mgmt_setup(struct net_device *dev); +int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local, + const char *name); +struct net_device_stats *ieee80211_dev_stats(struct net_device *dev); + +/* ieee80211_ioctl.c */ +int ieee80211_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +extern const struct iw_handler_def ieee80211_iw_handler_def; + +/* Set hw encryption from ieee80211 */ +int ieee80211_set_hw_encryption(struct net_device *dev, + struct sta_info *sta, u8 addr[ETH_ALEN], + struct ieee80211_key *key); +void ieee80211_update_default_wep_only(struct ieee80211_local *local); + + +/* Least common multiple of the used rates (in 100 kbps). This is used to + * calculate rate_inv values for each rate so that only integers are needed. */ +#define CHAN_UTIL_RATE_LCM 95040 +/* 1 usec is 1/8 * (95040/10) = 1188 */ +#define CHAN_UTIL_PER_USEC 1188 +/* Amount of bits to shift the result right to scale the total utilization + * to values that will not wrap around 32-bit integers. */ +#define CHAN_UTIL_SHIFT 9 +/* Theoretical maximum of channel utilization counter in 10 ms (stat_time=1): + * (CHAN_UTIL_PER_USEC * 10000) >> CHAN_UTIL_SHIFT = 23203. So dividing the + * raw value with about 23 should give utilization in 10th of a percentage + * (1/1000). However, utilization is only estimated and not all intervals + * between frames etc. are calculated. 18 seems to give numbers that are closer + * to the real maximum. */ +#define CHAN_UTIL_PER_10MS 18 +#define CHAN_UTIL_HDR_LONG (202 * CHAN_UTIL_PER_USEC) +#define CHAN_UTIL_HDR_SHORT (40 * CHAN_UTIL_PER_USEC) + + +/* ieee80211_ioctl.c */ +int ieee80211_set_compression(struct ieee80211_local *local, + struct net_device *dev, struct sta_info *sta); +int ieee80211_init_client(struct net_device *dev); +int ieee80211_set_channel(struct ieee80211_local *local, int channel, int freq); +/* ieee80211_sta.c */ +void ieee80211_sta_timer(unsigned long data); +void ieee80211_sta_work(struct work_struct *work); +void ieee80211_sta_scan_work(struct work_struct *work); +void ieee80211_sta_rx_mgmt(struct net_device *dev, struct sk_buff *skb, + struct ieee80211_rx_status *rx_status); +int ieee80211_sta_set_ssid(struct net_device *dev, char *ssid, size_t len); +int ieee80211_sta_get_ssid(struct net_device *dev, char *ssid, size_t *len); +int ieee80211_sta_set_bssid(struct net_device *dev, u8 *bssid); +int ieee80211_sta_req_scan(struct net_device *dev, u8 *ssid, size_t ssid_len); +void ieee80211_sta_req_auth(struct net_device *dev, + struct ieee80211_if_sta *ifsta); +int ieee80211_sta_scan_results(struct net_device *dev, char *buf, size_t len); +void ieee80211_sta_rx_scan(struct net_device *dev, struct sk_buff *skb, + struct ieee80211_rx_status *rx_status); +void ieee80211_rx_bss_list_init(struct net_device *dev); +void ieee80211_rx_bss_list_deinit(struct net_device *dev); +int ieee80211_sta_set_extra_ie(struct net_device *dev, char *ie, size_t len); +struct sta_info * ieee80211_ibss_add_sta(struct net_device *dev, + struct sk_buff *skb, u8 *bssid, + u8 *addr); +int ieee80211_sta_deauthenticate(struct net_device *dev, u16 reason); +int ieee80211_sta_disassociate(struct net_device *dev, u16 reason); + +/* ieee80211_iface.c */ +int ieee80211_if_add(struct net_device *dev, const char *name, + struct net_device **new_dev, int type); +void ieee80211_if_set_type(struct net_device *dev, int type); +void ieee80211_if_reinit(struct net_device *dev); +void __ieee80211_if_del(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata); +int ieee80211_if_remove(struct net_device *dev, const char *name, int id); +void ieee80211_if_free(struct net_device *dev); +void ieee80211_if_sdata_init(struct ieee80211_sub_if_data *sdata); +int ieee80211_if_add_mgmt(struct ieee80211_local *local); +void ieee80211_if_del_mgmt(struct ieee80211_local *local); + +/* for wiphy privid */ +extern void *mac80211_wiphy_privid; + +#endif /* IEEE80211_I_H */ diff --git a/net/mac80211/ieee80211_iface.c b/net/mac80211/ieee80211_iface.c new file mode 100644 index 0000000..a8612a8 --- /dev/null +++ b/net/mac80211/ieee80211_iface.c @@ -0,0 +1,353 @@ +/* + * Copyright 2002-2005, Instant802 Networks, Inc. + * Copyright 2005-2006, Devicescape Software, Inc. + * Copyright (c) 2006 Jiri Benc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include "ieee80211_i.h" +#include "sta_info.h" +#include "debugfs_netdev.h" + +void ieee80211_if_sdata_init(struct ieee80211_sub_if_data *sdata) +{ + int i; + + /* Default values for sub-interface parameters */ + sdata->drop_unencrypted = 0; + sdata->eapol = 1; + for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++) + skb_queue_head_init(&sdata->fragments[i].skb_list); +} + +static void ieee80211_if_sdata_deinit(struct ieee80211_sub_if_data *sdata) +{ + int i; + + for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++) { + __skb_queue_purge(&sdata->fragments[i].skb_list); + } +} + +/* Must be called with rtnl lock held. */ +int ieee80211_if_add(struct net_device *dev, const char *name, + struct net_device **new_dev, int type) +{ + struct net_device *ndev; + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_sub_if_data *sdata = NULL; + int ret; + + ASSERT_RTNL(); + ndev = alloc_netdev(sizeof(struct ieee80211_sub_if_data), + name, ieee80211_if_setup); + if (!ndev) + return -ENOMEM; + + ret = dev_alloc_name(ndev, ndev->name); + if (ret < 0) + goto fail; + + memcpy(ndev->dev_addr, local->hw.wiphy->perm_addr, ETH_ALEN); + ndev->base_addr = dev->base_addr; + ndev->irq = dev->irq; + ndev->mem_start = dev->mem_start; + ndev->mem_end = dev->mem_end; + SET_NETDEV_DEV(ndev, wiphy_dev(local->hw.wiphy)); + + sdata = IEEE80211_DEV_TO_SUB_IF(ndev); + ndev->ieee80211_ptr = &sdata->wdev; + sdata->wdev.wiphy = local->hw.wiphy; + sdata->type = IEEE80211_IF_TYPE_AP; + sdata->dev = ndev; + sdata->local = local; + ieee80211_if_sdata_init(sdata); + + ret = register_netdevice(ndev); + if (ret) + goto fail; + + ieee80211_debugfs_add_netdev(sdata); + ieee80211_if_set_type(ndev, type); + + write_lock_bh(&local->sub_if_lock); + if (unlikely(local->reg_state == IEEE80211_DEV_UNREGISTERED)) { + write_unlock_bh(&local->sub_if_lock); + __ieee80211_if_del(local, sdata); + return -ENODEV; + } + list_add(&sdata->list, &local->sub_if_list); + if (new_dev) + *new_dev = ndev; + write_unlock_bh(&local->sub_if_lock); + + ieee80211_update_default_wep_only(local); + + return 0; + +fail: + free_netdev(ndev); + return ret; +} + +int ieee80211_if_add_mgmt(struct ieee80211_local *local) +{ + struct net_device *ndev; + struct ieee80211_sub_if_data *nsdata; + int ret; + + ASSERT_RTNL(); + + ndev = alloc_netdev(sizeof(struct ieee80211_sub_if_data), "wmgmt%d", + ieee80211_if_mgmt_setup); + if (!ndev) + return -ENOMEM; + ret = dev_alloc_name(ndev, ndev->name); + if (ret < 0) + goto fail; + + memcpy(ndev->dev_addr, local->hw.wiphy->perm_addr, ETH_ALEN); + SET_NETDEV_DEV(ndev, wiphy_dev(local->hw.wiphy)); + + nsdata = IEEE80211_DEV_TO_SUB_IF(ndev); + ndev->ieee80211_ptr = &nsdata->wdev; + nsdata->wdev.wiphy = local->hw.wiphy; + nsdata->type = IEEE80211_IF_TYPE_MGMT; + nsdata->dev = ndev; + nsdata->local = local; + ieee80211_if_sdata_init(nsdata); + + ret = register_netdevice(ndev); + if (ret) + goto fail; + + ieee80211_debugfs_add_netdev(nsdata); + + if (local->open_count > 0) + dev_open(ndev); + local->apdev = ndev; + return 0; + +fail: + free_netdev(ndev); + return ret; +} + +void ieee80211_if_del_mgmt(struct ieee80211_local *local) +{ + struct net_device *apdev; + + ASSERT_RTNL(); + apdev = local->apdev; + ieee80211_debugfs_remove_netdev(IEEE80211_DEV_TO_SUB_IF(apdev)); + local->apdev = NULL; + unregister_netdevice(apdev); +} + +void ieee80211_if_set_type(struct net_device *dev, int type) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + int oldtype = sdata->type; + + sdata->type = type; + switch (type) { + case IEEE80211_IF_TYPE_WDS: + sdata->bss = NULL; + break; + case IEEE80211_IF_TYPE_VLAN: + break; + case IEEE80211_IF_TYPE_AP: + sdata->u.ap.dtim_period = 2; + sdata->u.ap.force_unicast_rateidx = -1; + sdata->u.ap.max_ratectrl_rateidx = -1; + skb_queue_head_init(&sdata->u.ap.ps_bc_buf); + sdata->bss = &sdata->u.ap; + break; + case IEEE80211_IF_TYPE_STA: + case IEEE80211_IF_TYPE_IBSS: { + struct ieee80211_sub_if_data *msdata; + struct ieee80211_if_sta *ifsta; + + ifsta = &sdata->u.sta; + INIT_WORK(&ifsta->work, ieee80211_sta_work); + setup_timer(&ifsta->timer, ieee80211_sta_timer, + (unsigned long) sdata); + skb_queue_head_init(&ifsta->skb_queue); + + ifsta->capab = WLAN_CAPABILITY_ESS; + ifsta->auth_algs = IEEE80211_AUTH_ALG_OPEN | + IEEE80211_AUTH_ALG_SHARED_KEY; + ifsta->create_ibss = 1; + ifsta->wmm_enabled = 1; + ifsta->ht_enabled = 1; + ifsta->auto_channel_sel = 1; + ifsta->auto_bssid_sel = 1; + + msdata = IEEE80211_DEV_TO_SUB_IF(sdata->local->mdev); + sdata->bss = &msdata->u.ap; + break; + } + case IEEE80211_IF_TYPE_MNTR: + dev->type = ARPHRD_IEEE80211_RADIOTAP; + break; + default: + printk(KERN_WARNING "%s: %s: Unknown interface type 0x%x", + dev->name, __FUNCTION__, type); + } + ieee80211_debugfs_change_if_type(sdata, oldtype); + ieee80211_update_default_wep_only(local); +} + +/* Must be called with rtnl lock held. */ +void ieee80211_if_reinit(struct net_device *dev) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct sta_info *sta; + int i; + + ASSERT_RTNL(); + ieee80211_if_sdata_deinit(sdata); + for (i = 0; i < NUM_DEFAULT_KEYS; i++) { + if (!sdata->keys[i]) + continue; +#if 0 + /* The interface is down at the moment, so there is not + * really much point in disabling the keys at this point. */ + memset(addr, 0xff, ETH_ALEN); + if (local->ops->set_key) + local->ops->set_key(local_to_hw(local), DISABLE_KEY, addr, + local->keys[i], 0); +#endif + ieee80211_key_free(sdata->keys[i]); + sdata->keys[i] = NULL; + } + + switch (sdata->type) { + case IEEE80211_IF_TYPE_AP: { + /* Remove all virtual interfaces that use this BSS + * as their sdata->bss */ + struct ieee80211_sub_if_data *tsdata, *n; + LIST_HEAD(tmp_list); + + write_lock_bh(&local->sub_if_lock); + list_for_each_entry_safe(tsdata, n, &local->sub_if_list, list) { + if (tsdata != sdata && tsdata->bss == &sdata->u.ap) { + printk(KERN_DEBUG "%s: removing virtual " + "interface %s because its BSS interface" + " is being removed\n", + sdata->dev->name, tsdata->dev->name); + list_move_tail(&tsdata->list, &tmp_list); + } + } + write_unlock_bh(&local->sub_if_lock); + + list_for_each_entry_safe(tsdata, n, &tmp_list, list) + __ieee80211_if_del(local, tsdata); + + kfree(sdata->u.ap.beacon_head); + kfree(sdata->u.ap.beacon_tail); + kfree(sdata->u.ap.generic_elem); + + if (dev != local->mdev) { + struct sk_buff *skb; + while ((skb = skb_dequeue(&sdata->u.ap.ps_bc_buf))) { + local->total_ps_buffered--; + dev_kfree_skb(skb); + } + } + + break; + } + case IEEE80211_IF_TYPE_WDS: + sta = sta_info_get(local, sdata->u.wds.remote_addr); + if (sta) { + sta_info_put(sta); + sta_info_free(sta, 0); + } else { +#ifdef CONFIG_MAC80211_VERBOSE_DEBUG + printk(KERN_DEBUG "%s: Someone had deleted my STA " + "entry for the WDS link\n", dev->name); +#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ + } + break; + case IEEE80211_IF_TYPE_STA: + case IEEE80211_IF_TYPE_IBSS: + kfree(sdata->u.sta.extra_ie); + sdata->u.sta.extra_ie = NULL; + kfree(sdata->u.sta.assocreq_ies); + sdata->u.sta.assocreq_ies = NULL; + kfree(sdata->u.sta.assocresp_ies); + sdata->u.sta.assocresp_ies = NULL; + if (sdata->u.sta.probe_resp) { + dev_kfree_skb(sdata->u.sta.probe_resp); + sdata->u.sta.probe_resp = NULL; + } + + break; + case IEEE80211_IF_TYPE_MNTR: + dev->type = ARPHRD_ETHER; + break; + } + + /* remove all STAs that are bound to this virtual interface */ + sta_info_flush(local, dev); + + memset(&sdata->u, 0, sizeof(sdata->u)); + ieee80211_if_sdata_init(sdata); +} + +/* Must be called with rtnl lock held. */ +void __ieee80211_if_del(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) +{ + struct net_device *dev = sdata->dev; + + ieee80211_debugfs_remove_netdev(sdata); + unregister_netdevice(dev); + /* Except master interface, the net_device will be freed by + * net_device->destructor (i. e. ieee80211_if_free). */ +} + +/* Must be called with rtnl lock held. */ +int ieee80211_if_remove(struct net_device *dev, const char *name, int id) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_sub_if_data *sdata, *n; + + ASSERT_RTNL(); + + write_lock_bh(&local->sub_if_lock); + list_for_each_entry_safe(sdata, n, &local->sub_if_list, list) { + if ((sdata->type == id || id == -1) && + strcmp(name, sdata->dev->name) == 0 && + sdata->dev != local->mdev) { + list_del(&sdata->list); + write_unlock_bh(&local->sub_if_lock); + __ieee80211_if_del(local, sdata); + ieee80211_update_default_wep_only(local); + return 0; + } + } + write_unlock_bh(&local->sub_if_lock); + return -ENODEV; +} + +void ieee80211_if_free(struct net_device *dev) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + /* local->apdev must be NULL when freeing management interface */ + BUG_ON(dev == local->apdev); + ieee80211_if_sdata_deinit(sdata); + free_netdev(dev); +} diff --git a/net/mac80211/ieee80211_ioctl.c b/net/mac80211/ieee80211_ioctl.c new file mode 100644 index 0000000..7736ae6 --- /dev/null +++ b/net/mac80211/ieee80211_ioctl.c @@ -0,0 +1,3180 @@ +/* + * Copyright 2002-2005, Instant802 Networks, Inc. + * Copyright 2005-2006, Devicescape Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "ieee80211_i.h" +#include "hostapd_ioctl.h" +#include "ieee80211_rate.h" +#include "wpa.h" +#include "aes_ccm.h" +#include "debugfs_key.h" + +static int ieee80211_regdom = 0x10; /* FCC */ +module_param(ieee80211_regdom, int, 0444); +MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain; 64=MKK"); + +/* + * If firmware is upgraded by the vendor, additional channels can be used based + * on the new Japanese regulatory rules. This is indicated by setting + * ieee80211_japan_5ghz module parameter to one when loading the 80211 kernel + * module. + */ +static int ieee80211_japan_5ghz /* = 0 */; +module_param(ieee80211_japan_5ghz, int, 0444); +MODULE_PARM_DESC(ieee80211_japan_5ghz, "Vendor-updated firmware for 5 GHz"); + + +static int ieee80211_ioctl_set_beacon(struct net_device *dev, + struct prism2_hostapd_param *param, + int param_len, + int flag) +{ + struct ieee80211_sub_if_data *sdata; + struct ieee80211_if_ap *ap; + u8 **b_head, **b_tail; + int *b_head_len, *b_tail_len; + int len; + + len = ((char *) param->u.beacon.data - (char *) param) + + param->u.beacon.head_len + param->u.beacon.tail_len; + + if (param_len > len) + param_len = len; + else if (param_len != len) + return -EINVAL; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + if (sdata->type != IEEE80211_IF_TYPE_AP) + return -EINVAL; + ap = &sdata->u.ap; + + switch (flag) { + case 0: + b_head = &ap->beacon_head; + b_tail = &ap->beacon_tail; + b_head_len = &ap->beacon_head_len; + b_tail_len = &ap->beacon_tail_len; + break; + default: + printk(KERN_DEBUG "%s: unknown beacon flag %d\n", + dev->name, flag); + return -EINVAL; + } + + kfree(*b_head); + kfree(*b_tail); + *b_head = NULL; + *b_tail = NULL; + + *b_head_len = param->u.beacon.head_len; + *b_tail_len = param->u.beacon.tail_len; + + *b_head = kmalloc(*b_head_len, GFP_KERNEL); + if (*b_head) + memcpy(*b_head, param->u.beacon.data, *b_head_len); + else { + printk(KERN_DEBUG "%s: failed to allocate beacon_head\n", + dev->name); + return -ENOMEM; + } + + if (*b_tail_len > 0) { + *b_tail = kmalloc(*b_tail_len, GFP_KERNEL); + if (*b_tail) + memcpy(*b_tail, param->u.beacon.data + (*b_head_len), + (*b_tail_len)); + else { + printk(KERN_DEBUG "%s: failed to allocate " + "beacon_tail\n", dev->name); + return -ENOMEM; + } + } + + return ieee80211_if_config_beacon(dev); +} + + +static int ieee80211_ioctl_get_hw_features(struct net_device *dev, + struct prism2_hostapd_param *param, + int param_len) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + u8 *pos = param->u.hw_features.data; + int left = param_len - (pos - (u8 *) param); + int i; + struct hostapd_ioctl_hw_modes_hdr *hdr; + struct ieee80211_rate_data *rate; + struct ieee80211_channel_data *chan; + struct ieee80211_hw_mode *mode; + + param->u.hw_features.flags = 0; + if (local->hw.flags & IEEE80211_HW_DATA_NULLFUNC_ACK) + param->u.hw_features.flags |= HOSTAP_HW_FLAG_NULLFUNC_OK; + + param->u.hw_features.num_modes = 0; + list_for_each_entry(mode, &local->modes_list, list) { + int clen, rlen; + + param->u.hw_features.num_modes++; + clen = mode->num_channels * sizeof(struct ieee80211_channel_data); + rlen = mode->num_rates * sizeof(struct ieee80211_rate_data); + if (left < sizeof(*hdr) + clen + rlen) + return -E2BIG; + left -= sizeof(*hdr) + clen + rlen; + + hdr = (struct hostapd_ioctl_hw_modes_hdr *) pos; + hdr->mode = mode->mode; + hdr->num_channels = mode->num_channels; + hdr->num_rates = mode->num_rates; + + pos = (u8 *) (hdr + 1); + chan = (struct ieee80211_channel_data *) pos; + for (i = 0; i < mode->num_channels; i++) { + chan[i].chan = mode->channels[i].chan; + chan[i].freq = mode->channels[i].freq; + chan[i].flag = mode->channels[i].flag; + } + pos += clen; + + rate = (struct ieee80211_rate_data *) pos; + for (i = 0; i < mode->num_rates; i++) { + rate[i].rate = mode->rates[i].rate; + rate[i].flags = mode->rates[i].flags; + } + pos += rlen; + } + + return 0; +} + +static int ieee80211_ioctl_flush(struct net_device *dev, + struct prism2_hostapd_param *param) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + sta_info_flush(local, NULL); + return 0; +} + + +/* Layer 2 Update frame (802.2 Type 1 LLC XID Update response) */ +struct iapp_layer2_update { + u8 da[ETH_ALEN]; /* broadcast */ + u8 sa[ETH_ALEN]; /* STA addr */ + __be16 len; /* 6 */ + u8 dsap; /* 0 */ + u8 ssap; /* 0 */ + u8 control; + u8 xid_info[3]; +} __attribute__ ((packed)); + +static void ieee80211_send_layer2_update(struct net_device *dev, + const u8 *addr) +{ + struct iapp_layer2_update *msg; + struct sk_buff *skb; + + /* Send Level 2 Update Frame to update forwarding tables in layer 2 + * bridge devices */ + + skb = dev_alloc_skb(sizeof(*msg)); + if (!skb) + return; + msg = (struct iapp_layer2_update *) skb_put(skb, sizeof(*msg)); + + /* 802.2 Type 1 Logical Link Control (LLC) Exchange Identifier (XID) + * Update response frame; IEEE Std 802.2-1998, 5.4.1.2.1 */ + + memset(msg->da, 0xff, ETH_ALEN); + memcpy(msg->sa, addr, ETH_ALEN); + msg->len = htons(6); + msg->dsap = 0; + msg->ssap = 0x01; /* NULL LSAP, CR Bit: Response */ + msg->control = 0xaf; /* XID response lsb.1111F101. + * F=0 (no poll command; unsolicited frame) */ + msg->xid_info[0] = 0x81; /* XID format identifier */ + msg->xid_info[1] = 1; /* LLC types/classes: Type 1 LLC */ + msg->xid_info[2] = 0; /* XID sender's receive window size (RW) */ + + skb->dev = dev; + skb->protocol = eth_type_trans(skb, dev); + memset(skb->cb, 0, sizeof(skb->cb)); + netif_rx(skb); +} + + +static int ieee80211_ioctl_add_sta(struct net_device *dev, + struct prism2_hostapd_param *param) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct sta_info *sta; + u32 rates; + int i, j; + struct ieee80211_sub_if_data *sdata; + struct ieee80211_hw_mode *mode; + int add_key_entry = 1; + + /* Prevent a race with changing the rate control algorithm */ + if (!netif_running(dev)) + return -ENETDOWN; + + sta = sta_info_get(local, param->sta_addr); + + if (!sta) { + sta = sta_info_add(local, dev, param->sta_addr, GFP_KERNEL); + if (!sta) + return -ENOMEM; + } + + if (sta->dev != dev) { + /* Binding STA to a new interface, so remove all references to + * the old BSS. */ + spin_lock_bh(&local->sta_lock); + sta_info_remove_aid_ptr(sta); + spin_unlock_bh(&local->sta_lock); + } + + /* TODO + * We "steal" the device in case someone owns it + * This will hurt WDS links and such when we have a + * WDS link and a client associating from the same station + */ + sta->dev = dev; + sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); + + sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC; + sta->aid = param->u.add_sta.aid; + if (sta->aid > IEEE80211_MAX_AID) + sta->aid = 0; + sta->listen_interval = param->u.add_sta.listen_interval; + + rates = 0; + mode = local->oper_hw_mode; + for (i = 0; i < sizeof(param->u.add_sta.supp_rates); i++) { + int rate = (param->u.add_sta.supp_rates[i] & 0x7f) * 5; + if (mode->mode == MODE_ATHEROS_TURBO || + mode->mode == MODE_ATHEROS_TURBOG) + rate *= 2; + for (j = 0; j < mode->num_rates; j++) { + if (mode->rates[j].rate == rate) + rates |= BIT(j); + } + + } + sta->supp_rates = rates; + + rate_control_rate_init(sta, local); + + if (param->u.add_sta.wds_flags & 0x01) + sta->flags |= WLAN_STA_WDS; + else + sta->flags &= ~WLAN_STA_WDS; + + if (add_key_entry && !sta->key && !sdata->default_key && + local->ops->set_key) { + struct ieee80211_key_conf conf; + /* Add key cache entry with NULL key type because this may used + * for TX filtering. */ + memset(&conf, 0, sizeof(conf)); + conf.hw_key_idx = HW_KEY_IDX_INVALID; + conf.alg = ALG_NULL; + conf.flags |= IEEE80211_KEY_FORCE_SW_ENCRYPT; + if (local->ops->set_key(local_to_hw(local), SET_KEY, + sta->addr, &conf, sta->aid)) { + sta->key_idx_compression = HW_KEY_IDX_INVALID; + } else { + sta->key_idx_compression = conf.hw_key_idx; + } + } + + sta_info_put(sta); + + if (sdata->type == IEEE80211_IF_TYPE_AP || + sdata->type == IEEE80211_IF_TYPE_VLAN) + ieee80211_send_layer2_update(dev, param->sta_addr); + + return 0; +} + + +static int ieee80211_ioctl_remove_sta(struct net_device *dev, + struct prism2_hostapd_param *param) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct sta_info *sta; + + sta = sta_info_get(local, param->sta_addr); + if (sta) { + sta_info_put(sta); + sta_info_free(sta, 0); + } + + return sta ? 0 : -ENOENT; +} + + +static int ieee80211_ioctl_get_dot11counterstable(struct net_device *dev, + struct prism2_hostapd_param *param) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_low_level_stats stats; + + memset(&stats, 0, sizeof(stats)); + if (local->ops->get_stats) + local->ops->get_stats(local_to_hw(local), &stats); + param->u.dot11CountersTable.dot11TransmittedFragmentCount = + local->dot11TransmittedFragmentCount; + param->u.dot11CountersTable.dot11MulticastTransmittedFrameCount = + local->dot11MulticastTransmittedFrameCount; + param->u.dot11CountersTable.dot11ReceivedFragmentCount = + local->dot11ReceivedFragmentCount; + param->u.dot11CountersTable.dot11MulticastReceivedFrameCount = + local->dot11MulticastReceivedFrameCount; + param->u.dot11CountersTable.dot11TransmittedFrameCount = + local->dot11TransmittedFrameCount; + param->u.dot11CountersTable.dot11FCSErrorCount = + stats.dot11FCSErrorCount; + param->u.dot11CountersTable.dot11ACKFailureCount = + stats.dot11ACKFailureCount; + param->u.dot11CountersTable.dot11RTSFailureCount = + stats.dot11RTSFailureCount; + param->u.dot11CountersTable.dot11RTSSuccessCount = + stats.dot11RTSSuccessCount; + + return 0; +} + + +static int ieee80211_ioctl_get_info_sta(struct net_device *dev, + struct prism2_hostapd_param *param) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_hw_mode *mode; + struct sta_info *sta; + + if (is_broadcast_ether_addr(param->sta_addr)) { + struct net_device_stats *stats; + + stats = ieee80211_dev_stats(local->mdev); + param->u.get_info_sta.rx_bytes = stats->rx_bytes; + param->u.get_info_sta.tx_bytes = stats->tx_bytes; + /* go through all STAs and get STA with lowest max. rate */ + param->u.get_info_sta.current_tx_rate = + sta_info_min_txrate_get(local); + return 0; + } + + sta = sta_info_get(local, param->sta_addr); + + if (!sta) + return -ENOENT; + + param->u.get_info_sta.inactive_msec = + jiffies_to_msecs(jiffies - sta->last_rx); + param->u.get_info_sta.rx_packets = sta->rx_packets; + param->u.get_info_sta.tx_packets = sta->tx_packets; + param->u.get_info_sta.rx_bytes = sta->rx_bytes; + param->u.get_info_sta.tx_bytes = sta->tx_bytes; + param->u.get_info_sta.channel_use = sta->channel_use; + param->u.get_info_sta.flags = sta->flags; + mode = local->oper_hw_mode; + if (sta->txrate >= 0 && sta->txrate < mode->num_rates) + param->u.get_info_sta.current_tx_rate = + mode->rates[sta->txrate].rate; + param->u.get_info_sta.num_ps_buf_frames = + skb_queue_len(&sta->ps_tx_buf); + param->u.get_info_sta.tx_retry_failed = sta->tx_retry_failed; + param->u.get_info_sta.tx_retry_count = sta->tx_retry_count; + param->u.get_info_sta.last_rssi = sta->last_rssi; + param->u.get_info_sta.last_ack_rssi = sta->last_ack_rssi[2]; + + sta_info_put(sta); + + return 0; +} + + +static int ieee80211_ioctl_set_flags_sta(struct net_device *dev, + struct prism2_hostapd_param *param) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct sta_info *sta; + + sta = sta_info_get(local, param->sta_addr); + if (sta) { + sta->flags |= param->u.set_flags_sta.flags_or; + sta->flags &= param->u.set_flags_sta.flags_and; + if (local->ops->set_port_auth && + (param->u.set_flags_sta.flags_or & WLAN_STA_AUTHORIZED) && + local->ops->set_port_auth(local_to_hw(local), sta->addr, 1)) + printk(KERN_DEBUG "%s: failed to set low-level driver " + "PAE state (authorized) for " MAC_FMT "\n", + dev->name, MAC_ARG(sta->addr)); + if (local->ops->set_port_auth && + !(param->u.set_flags_sta.flags_and & WLAN_STA_AUTHORIZED) && + local->ops->set_port_auth(local_to_hw(local), sta->addr, 0)) + printk(KERN_DEBUG "%s: failed to set low-level driver " + "PAE state (unauthorized) for " MAC_FMT "\n", + dev->name, MAC_ARG(sta->addr)); + sta_info_put(sta); + } + + return sta ? 0 : -ENOENT; +} + + +int ieee80211_set_hw_encryption(struct net_device *dev, + struct sta_info *sta, u8 addr[ETH_ALEN], + struct ieee80211_key *key) +{ + struct ieee80211_key_conf *keyconf = NULL; + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + int rc = 0; + + /* default to sw encryption; this will be cleared by low-level + * driver if the hw supports requested encryption */ + if (key) + key->force_sw_encrypt = 1; + + if (key && local->ops->set_key && + (keyconf = ieee80211_key_data2conf(local, key))) { + if (local->ops->set_key(local_to_hw(local), SET_KEY, addr, + keyconf, sta ? sta->aid : 0)) { + rc = HOSTAP_CRYPT_ERR_KEY_SET_FAILED; + key->force_sw_encrypt = 1; + key->hw_key_idx = HW_KEY_IDX_INVALID; + } else { + key->force_sw_encrypt = + !!(keyconf->flags & IEEE80211_KEY_FORCE_SW_ENCRYPT); + key->hw_key_idx = + keyconf->hw_key_idx; + + } + } + kfree(keyconf); + + return rc; +} + + +static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr, + int idx, int alg, int set_tx_key, u32 *err, + const u8 *_key, size_t key_len) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + int ret = 0; + struct sta_info *sta; + struct ieee80211_key *key, *old_key; + int try_hwaccel = 1; + struct ieee80211_key_conf *keyconf; + struct ieee80211_sub_if_data *sdata; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + if (is_broadcast_ether_addr(sta_addr)) { + sta = NULL; + if (idx >= NUM_DEFAULT_KEYS) { + printk(KERN_DEBUG "%s: set_encrypt - invalid idx=%d\n", + dev->name, idx); + return -EINVAL; + } + key = sdata->keys[idx]; + + /* TODO: consider adding hwaccel support for these; at least + * Atheros key cache should be able to handle this since AP is + * only transmitting frames with default keys. */ + /* FIX: hw key cache can be used when only one virtual + * STA is associated with each AP. If more than one STA + * is associated to the same AP, software encryption + * must be used. This should be done automatically + * based on configured station devices. For the time + * being, this can be only set at compile time. */ + } else { + set_tx_key = 0; + if (idx != 0) { + printk(KERN_DEBUG "%s: set_encrypt - non-zero idx for " + "individual key\n", dev->name); + return -EINVAL; + } + + sta = sta_info_get(local, sta_addr); + if (!sta) { + if (err) + *err = HOSTAP_CRYPT_ERR_UNKNOWN_ADDR; +#ifdef CONFIG_MAC80211_VERBOSE_DEBUG + printk(KERN_DEBUG "%s: set_encrypt - unknown addr " + MAC_FMT "\n", + dev->name, MAC_ARG(sta_addr)); +#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ + + return -ENOENT; + } + + key = sta->key; + } + + /* FIX: + * Cannot configure default hwaccel keys with WEP algorithm, if + * any of the virtual interfaces is using static WEP + * configuration because hwaccel would otherwise try to decrypt + * these frames. + * + * For now, just disable WEP hwaccel for broadcast when there is + * possibility of conflict with default keys. This can maybe later be + * optimized by using non-default keys (at least with Atheros ar521x). + */ + if (!sta && alg == ALG_WEP && !local->default_wep_only && + sdata->type != IEEE80211_IF_TYPE_IBSS && + sdata->type != IEEE80211_IF_TYPE_AP) { + try_hwaccel = 0; + } + + if (local->hw.flags & IEEE80211_HW_DEVICE_HIDES_WEP) { + /* Software encryption cannot be used with devices that hide + * encryption from the host system, so always try to use + * hardware acceleration with such devices. */ + try_hwaccel = 1; + } + + if ((local->hw.flags & IEEE80211_HW_NO_TKIP_WMM_HWACCEL) && + alg == ALG_TKIP) { + if (sta && (sta->flags & WLAN_STA_WME)) { + /* Hardware does not support hwaccel with TKIP when using WMM. + */ + try_hwaccel = 0; + } + else if (sdata->type == IEEE80211_IF_TYPE_STA) { + sta = sta_info_get(local, sdata->u.sta.bssid); + if (sta) { + if (sta->flags & WLAN_STA_WME) { + try_hwaccel = 0; + } + sta_info_put(sta); + sta = NULL; + } + } + } + + if (alg == ALG_NONE) { + keyconf = NULL; + if (try_hwaccel && key && + key->hw_key_idx != HW_KEY_IDX_INVALID && + local->ops->set_key && + (keyconf = ieee80211_key_data2conf(local, key)) != NULL && + local->ops->set_key(local_to_hw(local), DISABLE_KEY, + sta_addr, keyconf, sta ? sta->aid : 0)) { + if (err) + *err = HOSTAP_CRYPT_ERR_KEY_SET_FAILED; + printk(KERN_DEBUG "%s: set_encrypt - low-level disable" + " failed\n", dev->name); + ret = -EINVAL; + } + kfree(keyconf); + + if (set_tx_key || sdata->default_key == key) { + ieee80211_debugfs_key_remove_default(sdata); + sdata->default_key = NULL; + } + ieee80211_debugfs_key_remove(key); + if (sta) + sta->key = NULL; + else + sdata->keys[idx] = NULL; + ieee80211_key_free(key); + key = NULL; + } else { + old_key = key; + key = ieee80211_key_alloc(sta ? NULL : sdata, idx, key_len, + GFP_KERNEL); + if (!key) { + ret = -ENOMEM; + goto err_out; + } + + /* default to sw encryption; low-level driver sets these if the + * requested encryption is supported */ + key->hw_key_idx = HW_KEY_IDX_INVALID; + key->force_sw_encrypt = 1; + + key->alg = alg; + key->keyidx = idx; + key->keylen = key_len; + memcpy(key->key, _key, key_len); + if (set_tx_key) + key->default_tx_key = 1; + + if (alg == ALG_CCMP) { + /* Initialize AES key state here as an optimization + * so that it does not need to be initialized for every + * packet. */ + key->u.ccmp.tfm = ieee80211_aes_key_setup_encrypt( + key->key); + if (!key->u.ccmp.tfm) { + ret = -ENOMEM; + goto err_free; + } + } + + if (set_tx_key || sdata->default_key == old_key) { + ieee80211_debugfs_key_remove_default(sdata); + sdata->default_key = NULL; + } + ieee80211_debugfs_key_remove(old_key); + if (sta) + sta->key = key; + else + sdata->keys[idx] = key; + ieee80211_key_free(old_key); + ieee80211_debugfs_key_add(local, key); + if (sta) + ieee80211_debugfs_key_sta_link(key, sta); + + if (try_hwaccel && + (alg == ALG_WEP || alg == ALG_TKIP || alg == ALG_CCMP)) { + int e = ieee80211_set_hw_encryption(dev, sta, sta_addr, + key); + if (err) + *err = e; + } + } + + if (set_tx_key || (!sta && !sdata->default_key && key)) { + sdata->default_key = key; + if (key) + ieee80211_debugfs_key_add_default(sdata); + + if (local->ops->set_key_idx && + local->ops->set_key_idx(local_to_hw(local), idx)) + printk(KERN_DEBUG "%s: failed to set TX key idx for " + "low-level driver\n", dev->name); + } + + if (sta) + sta_info_put(sta); + + return 0; + +err_free: + ieee80211_key_free(key); +err_out: + if (sta) + sta_info_put(sta); + return ret; +} + + +static int ieee80211_ioctl_set_encryption(struct net_device *dev, + struct prism2_hostapd_param *param, + int param_len) +{ + int alg; + + param->u.crypt.err = 0; + param->u.crypt.alg[HOSTAP_CRYPT_ALG_NAME_LEN - 1] = '\0'; + + if (param_len < + (int) ((char *) param->u.crypt.key - (char *) param) + + param->u.crypt.key_len) { + printk(KERN_DEBUG "%s: set_encrypt - invalid param_lem\n", + dev->name); + return -EINVAL; + } + + if (strcmp(param->u.crypt.alg, "none") == 0) + alg = ALG_NONE; + else if (strcmp(param->u.crypt.alg, "WEP") == 0) + alg = ALG_WEP; + else if (strcmp(param->u.crypt.alg, "TKIP") == 0) { + if (param->u.crypt.key_len != ALG_TKIP_KEY_LEN) { + printk(KERN_DEBUG "%s: set_encrypt - invalid TKIP key " + "length %d\n", dev->name, + param->u.crypt.key_len); + return -EINVAL; + } + alg = ALG_TKIP; + } else if (strcmp(param->u.crypt.alg, "CCMP") == 0) { + if (param->u.crypt.key_len != ALG_CCMP_KEY_LEN) { + printk(KERN_DEBUG "%s: set_encrypt - invalid CCMP key " + "length %d\n", dev->name, + param->u.crypt.key_len); + return -EINVAL; + } + alg = ALG_CCMP; + } else { + param->u.crypt.err = HOSTAP_CRYPT_ERR_UNKNOWN_ALG; + printk(KERN_DEBUG "%s: set_encrypt - unknown alg\n", + dev->name); + return -EINVAL; + } + + return ieee80211_set_encryption( + dev, param->sta_addr, + param->u.crypt.idx, alg, + param->u.crypt.flags & HOSTAP_CRYPT_FLAG_SET_TX_KEY, + ¶m->u.crypt.err, param->u.crypt.key, + param->u.crypt.key_len); +} + + +static int ieee80211_ioctl_get_encryption(struct net_device *dev, + struct prism2_hostapd_param *param, + int param_len) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + int ret = 0; + struct sta_info *sta; + struct ieee80211_key **key; + int max_key_len; + struct ieee80211_sub_if_data *sdata; + u8 *pos; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + param->u.crypt.err = 0; + + max_key_len = param_len - + (int) ((char *) param->u.crypt.key - (char *) param); + if (max_key_len < 0) + return -EINVAL; + + if (is_broadcast_ether_addr(param->sta_addr)) { + sta = NULL; + if (param->u.crypt.idx >= NUM_DEFAULT_KEYS) { + param->u.crypt.idx = sdata->default_key ? + sdata->default_key->keyidx : 0; + return 0; + } else + key = &sdata->keys[param->u.crypt.idx]; + } else { + sta = sta_info_get(local, param->sta_addr); + if (!sta) { + param->u.crypt.err = HOSTAP_CRYPT_ERR_UNKNOWN_ADDR; + return -EINVAL; + } + + key = &sta->key; + } + + memset(param->u.crypt.seq_counter, 0, HOSTAP_SEQ_COUNTER_SIZE); + if (!*key) { + memcpy(param->u.crypt.alg, "none", 5); + param->u.crypt.key_len = 0; + param->u.crypt.idx = 0xff; + } else { + switch ((*key)->alg) { + case ALG_WEP: + memcpy(param->u.crypt.alg, "WEP", 4); + break; + case ALG_TKIP: + { + u32 iv32; + u16 iv16; + + memcpy(param->u.crypt.alg, "TKIP", 5); + if (local->ops->get_sequence_counter) { + /* Get transmit counter from low level driver */ + if (local->ops->get_sequence_counter( + local_to_hw(local), + param->sta_addr, + (*key)->keyidx, + IEEE80211_SEQ_COUNTER_TX, + &iv32, + &iv16)) { + /* Error getting value from device */ + return -EIO; + } + } else { + /* Get it from our own local data */ + iv32 = (*key)->u.tkip.iv32; + iv16 = (*key)->u.tkip.iv16; + } + pos = param->u.crypt.seq_counter; + *pos++ = iv16 & 0xff; + *pos++ = (iv16 >> 8) & 0xff; + *pos++ = iv32 & 0xff; + *pos++ = (iv32 >> 8) & 0xff; + *pos++ = (iv32 >> 16) & 0xff; + *pos++ = (iv32 >> 24) & 0xff; + break; + } + case ALG_CCMP: + { + u8 *pn; + memcpy(param->u.crypt.alg, "CCMP", 5); + pos = param->u.crypt.seq_counter; + pn = (*key)->u.ccmp.tx_pn; + *pos++ = pn[5]; + *pos++ = pn[4]; + *pos++ = pn[3]; + *pos++ = pn[2]; + *pos++ = pn[1]; + *pos++ = pn[0]; + break; + } + default: + memcpy(param->u.crypt.alg, "unknown", 8); + break; + } + + if (max_key_len < (*key)->keylen) + ret = -E2BIG; + else { + param->u.crypt.key_len = (*key)->keylen; + memcpy(param->u.crypt.key, (*key)->key, + (*key)->keylen); + } + } + + if (sta) + sta_info_put(sta); + + return ret; +} + + +#ifdef CONFIG_HOSTAPD_WPA_TESTING +static int ieee80211_ioctl_wpa_trigger(struct net_device *dev, + struct prism2_hostapd_param *param) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct sta_info *sta; + + if (is_broadcast_ether_addr(param->sta_addr)) { + local->wpa_trigger = param->u.wpa_trigger.trigger; + return 0; + } + + sta = sta_info_get(local, param->sta_addr); + if (!sta) { + printk(KERN_DEBUG "%s: wpa_trigger - unknown addr\n", + dev->name); + return -EINVAL; + } + + sta->wpa_trigger = param->u.wpa_trigger.trigger; + + sta_info_put(sta); + return 0; +} +#endif /* CONFIG_HOSTAPD_WPA_TESTING */ + + +static int ieee80211_ioctl_set_rate_sets(struct net_device *dev, + struct prism2_hostapd_param *param, + int param_len) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + u16 *pos = (u16 *) param->u.set_rate_sets.data; + int left = param_len - ((u8 *) pos - (u8 *) param); + int i, mode, num_supp, num_basic, *supp, *basic, *prev; + struct ieee80211_hw_mode *hw_mode; + + mode = param->u.set_rate_sets.mode; + num_supp = param->u.set_rate_sets.num_supported_rates; + num_basic = param->u.set_rate_sets.num_basic_rates; + + if (left < (num_supp + num_basic) * 2) { + printk(KERN_WARNING "%s: invalid length in hostapd set rate " + "sets ioctl (%d != %d)\n", dev->name, left, + (num_supp + num_basic) * 2); + return -EINVAL; + } + + supp = (int *) kmalloc((num_supp + 1) * sizeof(int), GFP_KERNEL); + basic = (int *) kmalloc((num_basic + 1) * sizeof(int), GFP_KERNEL); + + if (!supp || !basic) { + kfree(supp); + kfree(basic); + return -ENOMEM; + } + + for (i = 0; i < num_supp; i++) + supp[i] = *pos++; + supp[i] = -1; + + for (i = 0; i < num_basic; i++) + basic[i] = *pos++; + basic[i] = -1; + + if (num_supp == 0) { + kfree(supp); + supp = NULL; + } + + if (num_basic == 0) { + kfree(basic); + basic = NULL; + } + + prev = local->supp_rates[mode]; + local->supp_rates[mode] = supp; + kfree(prev); + + prev = local->basic_rates[mode]; + local->basic_rates[mode] = basic; + kfree(prev); + + /* TODO: should update STA TX rates and remove STAs if they + * do not have any remaining supported rates after the change + */ + list_for_each_entry(hw_mode, &local->modes_list, list) + if (hw_mode->mode == mode) + ieee80211_prepare_rates(local, hw_mode); + + return 0; +} + + +static int ieee80211_ioctl_add_if(struct net_device *dev, + struct prism2_hostapd_param *param, + int param_len) +{ + u8 *pos = param->u.if_info.data; + int left = param_len - ((u8 *) pos - (u8 *) param); + struct net_device *new_dev; + int res; + struct hostapd_if_wds *wds; + struct hostapd_if_bss *bss; + + printk(KERN_WARNING "PRISM2_HOSTAPD_ADD_IF ioctl is deprecated!"); + switch (param->u.if_info.type) { + case HOSTAP_IF_WDS: + wds = (struct hostapd_if_wds *) param->u.if_info.data; + + if (left < sizeof(struct hostapd_if_wds)) + return -EPROTO; + + res = ieee80211_if_add(dev, param->u.if_info.name, &new_dev, + IEEE80211_IF_TYPE_WDS); + if (res) + return res; + res = ieee80211_if_update_wds(new_dev, wds->remote_addr); + if (unlikely(res)) { + struct ieee80211_local *local = + wdev_priv(dev->ieee80211_ptr); + struct ieee80211_sub_if_data *sdata = + IEEE80211_DEV_TO_SUB_IF(new_dev); + write_lock_bh(&local->sub_if_lock); + list_del(&sdata->list); + write_unlock_bh(&local->sub_if_lock); + __ieee80211_if_del(local, sdata); + } + return res; + case HOSTAP_IF_VLAN: + if (left < sizeof(struct hostapd_if_vlan)) + return -EPROTO; + + res = ieee80211_if_add(dev, param->u.if_info.name, NULL, + IEEE80211_IF_TYPE_VLAN); + return res; + case HOSTAP_IF_BSS: + bss = (struct hostapd_if_bss *) param->u.if_info.data; + + if (left < sizeof(struct hostapd_if_bss)) + return -EPROTO; + + res = ieee80211_if_add(dev, param->u.if_info.name, &new_dev, + IEEE80211_IF_TYPE_AP); + if (res) + return res; + memcpy(new_dev->dev_addr, bss->bssid, ETH_ALEN); + return 0; + case HOSTAP_IF_STA: + if (left < sizeof(struct hostapd_if_sta)) + return -EPROTO; + + res = ieee80211_if_add(dev, param->u.if_info.name, NULL, + IEEE80211_IF_TYPE_STA); + return res; + default: + return -EINVAL; + } + + return 0; +} + +static int ieee80211_ioctl_remove_if(struct net_device *dev, + struct prism2_hostapd_param *param) +{ + unsigned int type; + + switch (param->u.if_info.type) { + case HOSTAP_IF_WDS: + type = IEEE80211_IF_TYPE_WDS; + break; + case HOSTAP_IF_VLAN: + type = IEEE80211_IF_TYPE_VLAN; + break; + case HOSTAP_IF_BSS: + type = IEEE80211_IF_TYPE_AP; + break; + case HOSTAP_IF_STA: + type = IEEE80211_IF_TYPE_STA; + break; + default: + return -EINVAL; + } + + return ieee80211_if_remove(dev, param->u.if_info.name, type); +} + +static int ieee80211_ioctl_update_if(struct net_device *dev, + struct prism2_hostapd_param *param, + int param_len) +{ + u8 *pos = param->u.if_info.data; + int left = param_len - ((u8 *) pos - (u8 *) param); + + if (param->u.if_info.type == HOSTAP_IF_WDS) { + struct hostapd_if_wds *wds = + (struct hostapd_if_wds *) param->u.if_info.data; + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct net_device *wds_dev = NULL; + struct ieee80211_sub_if_data *sdata; + + if (left < sizeof(struct ieee80211_if_wds)) + return -EPROTO; + + read_lock(&local->sub_if_lock); + list_for_each_entry(sdata, &local->sub_if_list, list) { + if (strcmp(param->u.if_info.name, + sdata->dev->name) == 0) { + wds_dev = sdata->dev; + break; + } + } + read_unlock(&local->sub_if_lock); + + if (!wds_dev || sdata->type != IEEE80211_IF_TYPE_WDS) + return -ENODEV; + + return ieee80211_if_update_wds(wds_dev, wds->remote_addr); + } else { + return -EOPNOTSUPP; + } +} + + +static int ieee80211_ioctl_scan_req(struct net_device *dev, + struct prism2_hostapd_param *param, + int param_len) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + u8 *pos = param->u.scan_req.ssid; + int left = param_len - ((u8 *) pos - (u8 *) param); + int len = param->u.scan_req.ssid_len; + + if (local->user_space_mlme) + return -EOPNOTSUPP; + + if (!netif_running(dev)) + return -ENETDOWN; + + if (left < len || len > IEEE80211_MAX_SSID_LEN) + return -EINVAL; + + return ieee80211_sta_req_scan(dev, pos, len); +} + + +static int ieee80211_ioctl_sta_get_state(struct net_device *dev, + struct prism2_hostapd_param *param) +{ + struct ieee80211_sub_if_data *sdata; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + if (sdata->type != IEEE80211_IF_TYPE_STA && + sdata->type != IEEE80211_IF_TYPE_IBSS) + return -EINVAL; + param->u.sta_get_state.state = sdata->u.sta.state; + return 0; +} + + +static int ieee80211_ioctl_mlme(struct net_device *dev, + struct prism2_hostapd_param *param) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_sub_if_data *sdata; + + if (local->user_space_mlme) + return -EOPNOTSUPP; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + if (sdata->type != IEEE80211_IF_TYPE_STA && + sdata->type != IEEE80211_IF_TYPE_IBSS) + return -EINVAL; + switch (param->u.mlme.cmd) { + case MLME_STA_DEAUTH: + return ieee80211_sta_deauthenticate(dev, param->u.mlme.reason_code); + case MLME_STA_DISASSOC: + return ieee80211_sta_disassociate(dev, param->u.mlme.reason_code); + } + return 0; +} + + +static int ieee80211_ioctl_get_load_stats(struct net_device *dev, + struct prism2_hostapd_param *param) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + + param->u.get_load_stats.channel_use = local->channel_use; +/* if (param->u.get_load_stats.flags & LOAD_STATS_CLEAR) + local->channel_use = 0; */ /* now it's not raw counter */ + + return 0; +} + + +static int ieee80211_ioctl_set_sta_vlan(struct net_device *dev, + struct prism2_hostapd_param *param) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct sta_info *sta; + + sta = sta_info_get(local, param->sta_addr); + if (sta) { + struct net_device *new_vlan_dev; + new_vlan_dev = + dev_get_by_name(param->u.set_sta_vlan.vlan_name); + if (new_vlan_dev) { +#if 0 + printk("%s: Station " MAC_FMT " moved to vlan: %s\n", + dev->name, MAC_ARG(param->sta_addr), + new_vlan_dev->name); +#endif + if (sta->dev != new_vlan_dev) { + ieee80211_send_layer2_update(new_vlan_dev, + sta->addr); + } + sta->dev = new_vlan_dev; + sta->vlan_id = param->u.set_sta_vlan.vlan_id; + dev_put(new_vlan_dev); + } + sta_info_put(sta); + } + + return sta ? 0 : -ENOENT; +} + + +static int ieee80211_set_gen_ie(struct net_device *dev, u8 *ie, size_t len) +{ + struct ieee80211_sub_if_data *sdata; + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + + if (local->user_space_mlme) + return -EOPNOTSUPP; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + if (sdata->type == IEEE80211_IF_TYPE_STA || + sdata->type == IEEE80211_IF_TYPE_IBSS) { + int ret = ieee80211_sta_set_extra_ie(dev, ie, len); + if (ret) + return ret; + sdata->u.sta.auto_bssid_sel = 0; + ieee80211_sta_req_auth(dev, &sdata->u.sta); + return 0; + } + + if (sdata->type == IEEE80211_IF_TYPE_AP) { + kfree(sdata->u.ap.generic_elem); + sdata->u.ap.generic_elem = kmalloc(len, GFP_KERNEL); + if (!sdata->u.ap.generic_elem) + return -ENOMEM; + memcpy(sdata->u.ap.generic_elem, ie, len); + sdata->u.ap.generic_elem_len = len; + return ieee80211_if_config(dev); + } + return -EOPNOTSUPP; +} + + +static int +ieee80211_ioctl_set_generic_info_elem(struct net_device *dev, + struct prism2_hostapd_param *param, + int param_len) +{ + u8 *pos = param->u.set_generic_info_elem.data; + int left = param_len - ((u8 *) pos - (u8 *) param); + int len = param->u.set_generic_info_elem.len; + + if (left < len) + return -EINVAL; + + return ieee80211_set_gen_ie(dev, pos, len); +} + + +static int ieee80211_ioctl_set_regulatory_domain(struct net_device *dev, + struct prism2_hostapd_param *param) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_conf *conf = &local->hw.conf; + conf->regulatory_domain = param->u.set_regulatory_domain.rd; + return 0; +} + + +static int ieee80211_ioctl_set_radio_enabled(struct net_device *dev, + int val) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_conf *conf = &local->hw.conf; + + conf->radio_enabled = val; + return ieee80211_hw_config(wdev_priv(dev->ieee80211_ptr)); +} + +static int +ieee80211_ioctl_set_tx_queue_params(struct net_device *dev, + struct prism2_hostapd_param *param) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_tx_queue_params qparam; + + if (!local->ops->conf_tx) { + printk(KERN_DEBUG "%s: low-level driver does not support TX " + "queue configuration\n", dev->name); + return -EOPNOTSUPP; + } + + memset(&qparam, 0, sizeof(qparam)); + qparam.aifs = param->u.tx_queue_params.aifs; + qparam.cw_min = param->u.tx_queue_params.cw_min; + qparam.cw_max = param->u.tx_queue_params.cw_max; + qparam.burst_time = param->u.tx_queue_params.burst_time; + + return local->ops->conf_tx(local_to_hw(local), + param->u.tx_queue_params.queue, + &qparam); +} + + +static int ieee80211_ioctl_get_tx_stats(struct net_device *dev, + struct prism2_hostapd_param *param) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_tx_queue_stats stats; + int ret, i; + + if (!local->ops->get_tx_stats) + return -EOPNOTSUPP; + + memset(&stats, 0, sizeof(stats)); + ret = local->ops->get_tx_stats(local_to_hw(local), &stats); + if (ret) + return ret; + + for (i = 0; i < 4; i++) { + param->u.get_tx_stats.data[i].len = stats.data[i].len; + param->u.get_tx_stats.data[i].limit = stats.data[i].limit; + param->u.get_tx_stats.data[i].count = stats.data[i].count; + } + + return 0; +} + + +static int ieee80211_ioctl_set_channel_flag(struct net_device *dev, + struct prism2_hostapd_param *param) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_hw_mode *mode; + struct ieee80211_channel *chan = NULL; + int i; + + list_for_each_entry(mode, &local->modes_list, list) { + if (mode->mode == param->u.set_channel_flag.mode) + goto found; + } + return -ENOENT; +found: + + for (i = 0; i < mode->num_channels; i++) { + chan = &mode->channels[i]; + if (chan->chan == param->u.set_channel_flag.chan) + break; + chan = NULL; + } + + if (!chan) + return -ENOENT; + + chan->flag = param->u.set_channel_flag.flag; + chan->power_level = param->u.set_channel_flag.power_level; + chan->antenna_max = param->u.set_channel_flag.antenna_max; + + return 0; +} + + +static int ieee80211_ioctl_set_quiet_params(struct net_device *dev, + struct prism2_hostapd_param *param) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_conf *conf = &local->hw.conf; + + conf->quiet_duration = param->u.quiet.duration; + conf->quiet_offset = param->u.quiet.offset; + conf->quiet_period = param->u.quiet.period; + return 0; +} + + +static int ieee80211_ioctl_set_radar_params(struct net_device *dev, + struct prism2_hostapd_param *param) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_conf *conf = &local->hw.conf; + + conf->radar_firpwr_threshold = param->u.radar.radar_firpwr_threshold; + conf->radar_rssi_threshold = param->u.radar.radar_rssi_threshold; + conf->pulse_height_threshold = param->u.radar.pulse_height_threshold; + conf->pulse_rssi_threshold = param->u.radar.pulse_rssi_threshold; + conf->pulse_inband_threshold = param->u.radar.pulse_inband_threshold; + return 0; +} + + +static int ieee80211_ioctl_priv_hostapd(struct net_device *dev, + struct iw_point *p) +{ + struct prism2_hostapd_param *param; + int ret = 0; + + if (p->length < sizeof(struct prism2_hostapd_param) || + p->length > PRISM2_HOSTAPD_MAX_BUF_SIZE || !p->pointer) { + printk(KERN_DEBUG "%s: hostapd ioctl: ptr=%p len=%d min=%d " + "max=%d\n", dev->name, p->pointer, p->length, + (int)sizeof(struct prism2_hostapd_param), + PRISM2_HOSTAPD_MAX_BUF_SIZE); + return -EINVAL; + } + + param = (struct prism2_hostapd_param *) kmalloc(p->length, GFP_KERNEL); + if (!param) + return -ENOMEM; + + if (copy_from_user(param, p->pointer, p->length)) { + ret = -EFAULT; + goto out; + } + + switch (param->cmd) { + case PRISM2_HOSTAPD_FLUSH: + ret = ieee80211_ioctl_flush(dev, param); + break; + case PRISM2_HOSTAPD_ADD_STA: + ret = ieee80211_ioctl_add_sta(dev, param); + break; + case PRISM2_HOSTAPD_REMOVE_STA: + ret = ieee80211_ioctl_remove_sta(dev, param); + break; + case PRISM2_HOSTAPD_GET_INFO_STA: + ret = ieee80211_ioctl_get_info_sta(dev, param); + break; + case PRISM2_SET_ENCRYPTION: + ret = ieee80211_ioctl_set_encryption(dev, param, p->length); + break; + case PRISM2_GET_ENCRYPTION: + ret = ieee80211_ioctl_get_encryption(dev, param, p->length); + break; + case PRISM2_HOSTAPD_SET_FLAGS_STA: + ret = ieee80211_ioctl_set_flags_sta(dev, param); + break; + case PRISM2_HOSTAPD_SET_BEACON: + ret = ieee80211_ioctl_set_beacon(dev, param, p->length, 0); + break; + case PRISM2_HOSTAPD_GET_HW_FEATURES: + ret = ieee80211_ioctl_get_hw_features(dev, param, p->length); + break; +#ifdef CONFIG_HOSTAPD_WPA_TESTING + case PRISM2_HOSTAPD_WPA_TRIGGER: + ret = ieee80211_ioctl_wpa_trigger(dev, param); + break; +#endif /* CONFIG_HOSTAPD_WPA_TESTING */ + case PRISM2_HOSTAPD_SET_RATE_SETS: + ret = ieee80211_ioctl_set_rate_sets(dev, param, p->length); + break; + case PRISM2_HOSTAPD_ADD_IF: + ret = ieee80211_ioctl_add_if(dev, param, p->length); + break; + case PRISM2_HOSTAPD_REMOVE_IF: + ret = ieee80211_ioctl_remove_if(dev, param); + break; + case PRISM2_HOSTAPD_GET_DOT11COUNTERSTABLE: + ret = ieee80211_ioctl_get_dot11counterstable(dev, param); + break; + case PRISM2_HOSTAPD_GET_LOAD_STATS: + ret = ieee80211_ioctl_get_load_stats(dev, param); + break; + case PRISM2_HOSTAPD_SET_STA_VLAN: + ret = ieee80211_ioctl_set_sta_vlan(dev, param); + break; + case PRISM2_HOSTAPD_SET_GENERIC_INFO_ELEM: + ret = ieee80211_ioctl_set_generic_info_elem(dev, param, + p->length); + break; + case PRISM2_HOSTAPD_SET_CHANNEL_FLAG: + ret = ieee80211_ioctl_set_channel_flag(dev, param); + break; + case PRISM2_HOSTAPD_SET_REGULATORY_DOMAIN: + ret = ieee80211_ioctl_set_regulatory_domain(dev, param); + break; + case PRISM2_HOSTAPD_SET_TX_QUEUE_PARAMS: + ret = ieee80211_ioctl_set_tx_queue_params(dev, param); + break; + case PRISM2_HOSTAPD_GET_TX_STATS: + ret = ieee80211_ioctl_get_tx_stats(dev, param); + break; + case PRISM2_HOSTAPD_UPDATE_IF: + ret = ieee80211_ioctl_update_if(dev, param, p->length); + break; + case PRISM2_HOSTAPD_SCAN_REQ: + ret = ieee80211_ioctl_scan_req(dev, param, p->length); + break; + case PRISM2_STA_GET_STATE: + ret = ieee80211_ioctl_sta_get_state(dev, param); + break; + case PRISM2_HOSTAPD_MLME: + ret = ieee80211_ioctl_mlme(dev, param); + break; + case PRISM2_HOSTAPD_SET_RADAR_PARAMS: + ret = ieee80211_ioctl_set_radar_params(dev, param); + break; + case PRISM2_HOSTAPD_SET_QUIET_PARAMS: + ret = ieee80211_ioctl_set_quiet_params(dev, param); + break; + default: + ret = -EOPNOTSUPP; + break; + } + + if (copy_to_user(p->pointer, param, p->length)) + ret = -EFAULT; + + out: + kfree(param); + + return ret; +} + + +static int ieee80211_ioctl_giwname(struct net_device *dev, + struct iw_request_info *info, + char *name, char *extra) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + + switch (local->hw.conf.phymode) { + case MODE_IEEE80211A: + strcpy(name, "IEEE 802.11a"); + break; + case MODE_IEEE80211B: + strcpy(name, "IEEE 802.11b"); + break; + case MODE_IEEE80211G: + strcpy(name, "IEEE 802.11g"); + break; + case MODE_ATHEROS_TURBO: + strcpy(name, "5GHz Turbo"); + break; + default: + strcpy(name, "IEEE 802.11"); + break; + } + + return 0; +} + + +static int ieee80211_ioctl_giwrange(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct iw_range *range = (struct iw_range *) extra; + + data->length = sizeof(struct iw_range); + memset(range, 0, sizeof(struct iw_range)); + + range->we_version_compiled = WIRELESS_EXT; + range->we_version_source = 21; + range->retry_capa = IW_RETRY_LIMIT; + range->retry_flags = IW_RETRY_LIMIT; + range->min_retry = 0; + range->max_retry = 255; + range->min_rts = 0; + range->max_rts = 2347; + range->min_frag = 256; + range->max_frag = 2346; + + range->encoding_size[0] = 5; + range->encoding_size[1] = 13; + range->num_encoding_sizes = 2; + range->max_encoding_tokens = NUM_DEFAULT_KEYS; + + range->max_qual.qual = local->hw.max_signal; + range->max_qual.level = local->hw.max_rssi; + range->max_qual.noise = local->hw.max_noise; + range->max_qual.updated = local->wstats_flags; + + range->avg_qual.qual = local->hw.max_signal/2; + range->avg_qual.level = 0; + range->avg_qual.noise = 0; + range->avg_qual.updated = local->wstats_flags; + + range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 | + IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP; + + IW_EVENT_CAPA_SET_KERNEL(range->event_capa); + IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWTHRSPY); + IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP); + IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN); + + return 0; +} + + +struct ieee80211_channel_range { + short start_freq; + short end_freq; + unsigned char power_level; + unsigned char antenna_max; +}; + +static const struct ieee80211_channel_range ieee80211_fcc_channels[] = { + { 2412, 2462, 27, 6 } /* IEEE 802.11b/g, channels 1..11 */, + { 5180, 5240, 17, 6 } /* IEEE 802.11a, channels 36..48 */, + { 5260, 5320, 23, 6 } /* IEEE 802.11a, channels 52..64 */, + { 5745, 5825, 30, 6 } /* IEEE 802.11a, channels 149..165, outdoor */, + { 0 } +}; + +static const struct ieee80211_channel_range ieee80211_mkk_channels[] = { + { 2412, 2472, 20, 6 } /* IEEE 802.11b/g, channels 1..13 */, + { 5170, 5240, 20, 6 } /* IEEE 802.11a, channels 34..48 */, + { 5260, 5320, 20, 6 } /* IEEE 802.11a, channels 52..64 */, + { 0 } +}; + + +static const struct ieee80211_channel_range *channel_range = + ieee80211_fcc_channels; + + +static void ieee80211_unmask_channel(struct net_device *dev, int mode, + struct ieee80211_channel *chan) +{ + int i; + + chan->flag = 0; + + if (ieee80211_regdom == 64 && + (mode == MODE_ATHEROS_TURBO || mode == MODE_ATHEROS_TURBOG)) { + /* Do not allow Turbo modes in Japan. */ + return; + } + + for (i = 0; channel_range[i].start_freq; i++) { + const struct ieee80211_channel_range *r = &channel_range[i]; + if (r->start_freq <= chan->freq && r->end_freq >= chan->freq) { + if (ieee80211_regdom == 64 && !ieee80211_japan_5ghz && + chan->freq >= 5260 && chan->freq <= 5320) { + /* + * Skip new channels in Japan since the + * firmware was not marked having been upgraded + * by the vendor. + */ + continue; + } + + if (ieee80211_regdom == 0x10 && + (chan->freq == 5190 || chan->freq == 5210 || + chan->freq == 5230)) { + /* Skip MKK channels when in FCC domain. */ + continue; + } + + chan->flag |= IEEE80211_CHAN_W_SCAN | + IEEE80211_CHAN_W_ACTIVE_SCAN | + IEEE80211_CHAN_W_IBSS; + chan->power_level = r->power_level; + chan->antenna_max = r->antenna_max; + + if (ieee80211_regdom == 64 && + (chan->freq == 5170 || chan->freq == 5190 || + chan->freq == 5210 || chan->freq == 5230)) { + /* + * New regulatory rules in Japan have backwards + * compatibility with old channels in 5.15-5.25 + * GHz band, but the station is not allowed to + * use active scan on these old channels. + */ + chan->flag &= ~IEEE80211_CHAN_W_ACTIVE_SCAN; + } + + if (ieee80211_regdom == 64 && + (chan->freq == 5260 || chan->freq == 5280 || + chan->freq == 5300 || chan->freq == 5320)) { + /* + * IBSS is not allowed on 5.25-5.35 GHz band + * due to radar detection requirements. + */ + chan->flag &= ~IEEE80211_CHAN_W_IBSS; + } + + break; + } + } +} + + +static int ieee80211_unmask_channels(struct net_device *dev) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_hw_mode *mode; + int c; + + list_for_each_entry(mode, &local->modes_list, list) { + for (c = 0; c < mode->num_channels; c++) { + ieee80211_unmask_channel(dev, mode->mode, + &mode->channels[c]); + } + } + return 0; +} + + +int ieee80211_init_client(struct net_device *dev) +{ + if (ieee80211_regdom == 0x40) + channel_range = ieee80211_mkk_channels; + ieee80211_unmask_channels(dev); + return 0; +} + + +static int ieee80211_ioctl_siwmode(struct net_device *dev, + struct iw_request_info *info, + __u32 *mode, char *extra) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + int type; + + if (sdata->type == IEEE80211_IF_TYPE_VLAN) + return -EOPNOTSUPP; + + switch (*mode) { + case IW_MODE_MASTER: + type = IEEE80211_IF_TYPE_AP; + break; + case IW_MODE_INFRA: + type = IEEE80211_IF_TYPE_STA; + break; + case IW_MODE_ADHOC: + type = IEEE80211_IF_TYPE_IBSS; + break; + case IW_MODE_MONITOR: + type = IEEE80211_IF_TYPE_MNTR; + break; + case IW_MODE_REPEAT: + type = IEEE80211_IF_TYPE_WDS; + break; + default: + return -EINVAL; + } + + if (type == sdata->type) + return 0; + if (netif_running(dev)) + return -EBUSY; + + ieee80211_if_reinit(dev); + ieee80211_if_set_type(dev, type); + + return 0; +} + + +static int ieee80211_ioctl_giwmode(struct net_device *dev, + struct iw_request_info *info, + __u32 *mode, char *extra) +{ + struct ieee80211_sub_if_data *sdata; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + switch (sdata->type) { + case IEEE80211_IF_TYPE_AP: + *mode = IW_MODE_MASTER; + break; + case IEEE80211_IF_TYPE_STA: + *mode = IW_MODE_INFRA; + break; + case IEEE80211_IF_TYPE_IBSS: + *mode = IW_MODE_ADHOC; + break; + case IEEE80211_IF_TYPE_MNTR: + *mode = IW_MODE_MONITOR; + break; + case IEEE80211_IF_TYPE_WDS: + *mode = IW_MODE_REPEAT; + break; + case IEEE80211_IF_TYPE_VLAN: + *mode = IW_MODE_SECOND; /* FIXME */ + break; + default: + *mode = IW_MODE_AUTO; + break; + } + return 0; +} + +int ieee80211_set_channel(struct ieee80211_local *local, int channel, int freq) +{ + struct ieee80211_hw_mode *mode; + int c, set = 0; + int ret = -EINVAL; + + list_for_each_entry(mode, &local->modes_list, list) { + if (!(local->enabled_modes & (1 << mode->mode))) + continue; + for (c = 0; c < mode->num_channels; c++) { + struct ieee80211_channel *chan = &mode->channels[c]; + if (chan->flag & IEEE80211_CHAN_W_SCAN && + ((chan->chan == channel) || (chan->freq == freq))) { + /* Use next_mode as the mode preference to + * resolve non-unique channel numbers. */ + if (set && mode->mode != local->next_mode) + continue; + + local->oper_channel = chan; + local->oper_hw_mode = mode; + set++; + } + } + } + + if (set) { + if (local->sta_scanning) + ret = 0; + else + ret = ieee80211_hw_config(local); + + rate_control_clear(local); + } + + return ret; +} + +static int ieee80211_ioctl_siwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + if (sdata->type == IEEE80211_IF_TYPE_STA) + sdata->u.sta.auto_channel_sel = 0; + + /* freq->e == 0: freq->m = channel; otherwise freq = m * 10^e */ + if (freq->e == 0) { + if (freq->m < 0) { + if (sdata->type == IEEE80211_IF_TYPE_STA) + sdata->u.sta.auto_channel_sel = 1; + return 0; + } else + return ieee80211_set_channel(local, freq->m, -1); + } else { + int i, div = 1000000; + for (i = 0; i < freq->e; i++) + div /= 10; + if (div > 0) + return ieee80211_set_channel(local, -1, freq->m / div); + else + return -EINVAL; + } +} + + +static int ieee80211_ioctl_giwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + + /* TODO: in station mode (Managed/Ad-hoc) might need to poll low-level + * driver for the current channel with firmware-based management */ + + freq->m = local->hw.conf.freq; + freq->e = 6; + + return 0; +} + + +static int ieee80211_ioctl_siwessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *ssid) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_sub_if_data *sdata; + size_t len = data->length; + + /* iwconfig uses nul termination in SSID.. */ + if (len > 0 && ssid[len - 1] == '\0') + len--; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + if (sdata->type == IEEE80211_IF_TYPE_STA || + sdata->type == IEEE80211_IF_TYPE_IBSS) { + int ret; + if (local->user_space_mlme) { + if (len > IEEE80211_MAX_SSID_LEN) + return -EINVAL; + memcpy(sdata->u.sta.ssid, ssid, len); + sdata->u.sta.ssid_len = len; + return 0; + } + sdata->u.sta.auto_ssid_sel = !data->flags; + ret = ieee80211_sta_set_ssid(dev, ssid, len); + if (ret) + return ret; + ieee80211_sta_req_auth(dev, &sdata->u.sta); + return 0; + } + + if (sdata->type == IEEE80211_IF_TYPE_AP) { + memcpy(sdata->u.ap.ssid, ssid, len); + memset(sdata->u.ap.ssid + len, 0, + IEEE80211_MAX_SSID_LEN - len); + sdata->u.ap.ssid_len = len; + return ieee80211_if_config(dev); + } + return -EOPNOTSUPP; +} + + +static int ieee80211_ioctl_giwessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *ssid) +{ + size_t len; + + struct ieee80211_sub_if_data *sdata; + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + if (sdata->type == IEEE80211_IF_TYPE_STA || + sdata->type == IEEE80211_IF_TYPE_IBSS) { + int res = ieee80211_sta_get_ssid(dev, ssid, &len); + if (res == 0) { + data->length = len; + data->flags = 1; + } else + data->flags = 0; + return res; + } + + if (sdata->type == IEEE80211_IF_TYPE_AP) { + len = sdata->u.ap.ssid_len; + if (len > IW_ESSID_MAX_SIZE) + len = IW_ESSID_MAX_SIZE; + memcpy(ssid, sdata->u.ap.ssid, len); + data->length = len; + data->flags = 1; + return 0; + } + return -EOPNOTSUPP; +} + + +static int ieee80211_ioctl_siwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_sub_if_data *sdata; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + if (sdata->type == IEEE80211_IF_TYPE_STA || + sdata->type == IEEE80211_IF_TYPE_IBSS) { + int ret; + if (local->user_space_mlme) { + memcpy(sdata->u.sta.bssid, (u8 *) &ap_addr->sa_data, + ETH_ALEN); + return 0; + } + if (is_zero_ether_addr((u8 *) &ap_addr->sa_data)) { + sdata->u.sta.auto_bssid_sel = 1; + sdata->u.sta.auto_channel_sel = 1; + } else if (is_broadcast_ether_addr((u8 *) &ap_addr->sa_data)) + sdata->u.sta.auto_bssid_sel = 1; + else + sdata->u.sta.auto_bssid_sel = 0; + ret = ieee80211_sta_set_bssid(dev, (u8 *) &ap_addr->sa_data); + if (ret) + return ret; + ieee80211_sta_req_auth(dev, &sdata->u.sta); + return 0; + } else if (sdata->type == IEEE80211_IF_TYPE_WDS) { + if (memcmp(sdata->u.wds.remote_addr, (u8 *) &ap_addr->sa_data, + ETH_ALEN) == 0) + return 0; + return ieee80211_if_update_wds(dev, (u8 *) &ap_addr->sa_data); + } + + return -EOPNOTSUPP; +} + + +static int ieee80211_ioctl_giwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra) +{ + struct ieee80211_sub_if_data *sdata; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + if (sdata->type == IEEE80211_IF_TYPE_STA || + sdata->type == IEEE80211_IF_TYPE_IBSS) { + ap_addr->sa_family = ARPHRD_ETHER; + memcpy(&ap_addr->sa_data, sdata->u.sta.bssid, ETH_ALEN); + return 0; + } else if (sdata->type == IEEE80211_IF_TYPE_WDS) { + ap_addr->sa_family = ARPHRD_ETHER; + memcpy(&ap_addr->sa_data, sdata->u.wds.remote_addr, ETH_ALEN); + return 0; + } + + return -EOPNOTSUPP; +} + + +static int ieee80211_ioctl_siwscan(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + u8 *ssid = NULL; + size_t ssid_len = 0; + + if (!netif_running(dev)) + return -ENETDOWN; + + if (local->scan_flags & IEEE80211_SCAN_MATCH_SSID) { + if (sdata->type == IEEE80211_IF_TYPE_STA || + sdata->type == IEEE80211_IF_TYPE_IBSS) { + ssid = sdata->u.sta.ssid; + ssid_len = sdata->u.sta.ssid_len; + } else if (sdata->type == IEEE80211_IF_TYPE_AP) { + ssid = sdata->u.ap.ssid; + ssid_len = sdata->u.ap.ssid_len; + } else + return -EINVAL; + } + return ieee80211_sta_req_scan(dev, ssid, ssid_len); +} + + +static int ieee80211_ioctl_giwscan(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + int res; + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + if (local->sta_scanning) + return -EAGAIN; + res = ieee80211_sta_scan_results(dev, extra, data->length); + if (res >= 0) { + data->length = res; + return 0; + } + data->length = 0; + return res; +} + + +static int ieee80211_ioctl_siwrts(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rts, char *extra) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + + if (rts->disabled) + local->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD; + else if (rts->value < 0 || rts->value > IEEE80211_MAX_RTS_THRESHOLD) + return -EINVAL; + else + local->rts_threshold = rts->value; + + /* If the wlan card performs RTS/CTS in hardware/firmware, + * configure it here */ + + if (local->ops->set_rts_threshold) + local->ops->set_rts_threshold(local_to_hw(local), + local->rts_threshold); + + return 0; +} + +static int ieee80211_ioctl_giwrts(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rts, char *extra) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + + rts->value = local->rts_threshold; + rts->disabled = (rts->value >= IEEE80211_MAX_RTS_THRESHOLD); + rts->fixed = 1; + + return 0; +} + + +static int ieee80211_ioctl_siwfrag(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *frag, char *extra) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + + if (frag->disabled) + local->fragmentation_threshold = IEEE80211_MAX_FRAG_THRESHOLD; + else if (frag->value < 256 || + frag->value > IEEE80211_MAX_FRAG_THRESHOLD) + return -EINVAL; + else { + /* Fragment length must be even, so strip LSB. */ + local->fragmentation_threshold = frag->value & ~0x1; + } + + /* If the wlan card performs fragmentation in hardware/firmware, + * configure it here */ + + if (local->ops->set_frag_threshold) + local->ops->set_frag_threshold( + local_to_hw(local), + local->fragmentation_threshold); + + return 0; +} + +static int ieee80211_ioctl_giwfrag(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *frag, char *extra) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + + frag->value = local->fragmentation_threshold; + frag->disabled = (frag->value >= IEEE80211_MAX_RTS_THRESHOLD); + frag->fixed = 1; + + return 0; +} + + +static int ieee80211_ioctl_siwretry(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *retry, char *extra) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + + if (retry->disabled || + (retry->flags & IW_RETRY_TYPE) != IW_RETRY_LIMIT) + return -EINVAL; + + if (retry->flags & IW_RETRY_MAX) + local->long_retry_limit = retry->value; + else if (retry->flags & IW_RETRY_MIN) + local->short_retry_limit = retry->value; + else { + local->long_retry_limit = retry->value; + local->short_retry_limit = retry->value; + } + + if (local->ops->set_retry_limit) { + return local->ops->set_retry_limit( + local_to_hw(local), + local->short_retry_limit, + local->long_retry_limit); + } + + return 0; +} + + +static int ieee80211_ioctl_giwretry(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *retry, char *extra) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + + retry->disabled = 0; + if (retry->flags == 0 || retry->flags & IW_RETRY_MIN) { + /* first return min value, iwconfig will ask max value + * later if needed */ + retry->flags |= IW_RETRY_LIMIT; + retry->value = local->short_retry_limit; + if (local->long_retry_limit != local->short_retry_limit) + retry->flags |= IW_RETRY_MIN; + return 0; + } + if (retry->flags & IW_RETRY_MAX) { + retry->flags = IW_RETRY_LIMIT | IW_RETRY_MAX; + retry->value = local->long_retry_limit; + } + + return 0; +} + +static int ieee80211_ioctl_clear_keys(struct net_device *dev) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_key_conf key; + int i; + u8 addr[ETH_ALEN]; + struct ieee80211_key_conf *keyconf; + struct ieee80211_sub_if_data *sdata; + struct sta_info *sta; + + memset(addr, 0xff, ETH_ALEN); + read_lock(&local->sub_if_lock); + list_for_each_entry(sdata, &local->sub_if_list, list) { + for (i = 0; i < NUM_DEFAULT_KEYS; i++) { + keyconf = NULL; + if (sdata->keys[i] && + !sdata->keys[i]->force_sw_encrypt && + local->ops->set_key && + (keyconf = ieee80211_key_data2conf(local, + sdata->keys[i]))) + local->ops->set_key(local_to_hw(local), + DISABLE_KEY, addr, + keyconf, 0); + kfree(keyconf); + ieee80211_key_free(sdata->keys[i]); + sdata->keys[i] = NULL; + } + sdata->default_key = NULL; + } + read_unlock(&local->sub_if_lock); + + spin_lock_bh(&local->sta_lock); + list_for_each_entry(sta, &local->sta_list, list) { + keyconf = NULL; + if (sta->key && !sta->key->force_sw_encrypt && + local->ops->set_key && + (keyconf = ieee80211_key_data2conf(local, sta->key))) + local->ops->set_key(local_to_hw(local), DISABLE_KEY, + sta->addr, keyconf, sta->aid); + kfree(keyconf); + ieee80211_key_free(sta->key); + sta->key = NULL; + } + spin_unlock_bh(&local->sta_lock); + + memset(&key, 0, sizeof(key)); + if (local->ops->set_key && + local->ops->set_key(local_to_hw(local), REMOVE_ALL_KEYS, + NULL, &key, 0)) + printk(KERN_DEBUG "%s: failed to remove hwaccel keys\n", + dev->name); + + return 0; +} + + +static int +ieee80211_ioctl_force_unicast_rate(struct net_device *dev, + struct ieee80211_sub_if_data *sdata, + int rate) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_hw_mode *mode; + int i; + + if (sdata->type != IEEE80211_IF_TYPE_AP) + return -ENOENT; + + if (rate == 0) { + sdata->u.ap.force_unicast_rateidx = -1; + return 0; + } + + mode = local->oper_hw_mode; + for (i = 0; i < mode->num_rates; i++) { + if (mode->rates[i].rate == rate) { + sdata->u.ap.force_unicast_rateidx = i; + return 0; + } + } + return -EINVAL; +} + + +static int +ieee80211_ioctl_max_ratectrl_rate(struct net_device *dev, + struct ieee80211_sub_if_data *sdata, + int rate) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_hw_mode *mode; + int i; + + if (sdata->type != IEEE80211_IF_TYPE_AP) + return -ENOENT; + + if (rate == 0) { + sdata->u.ap.max_ratectrl_rateidx = -1; + return 0; + } + + mode = local->oper_hw_mode; + for (i = 0; i < mode->num_rates; i++) { + if (mode->rates[i].rate == rate) { + sdata->u.ap.max_ratectrl_rateidx = i; + return 0; + } + } + return -EINVAL; +} + + +static void ieee80211_key_enable_hwaccel(struct ieee80211_local *local, + struct ieee80211_key *key) +{ + struct ieee80211_key_conf *keyconf; + u8 addr[ETH_ALEN]; + + if (!key || key->alg != ALG_WEP || !key->force_sw_encrypt || + (local->hw.flags & IEEE80211_HW_DEVICE_HIDES_WEP)) + return; + + memset(addr, 0xff, ETH_ALEN); + keyconf = ieee80211_key_data2conf(local, key); + if (keyconf && local->ops->set_key && + local->ops->set_key(local_to_hw(local), + SET_KEY, addr, keyconf, 0) == 0) { + key->force_sw_encrypt = + !!(keyconf->flags & IEEE80211_KEY_FORCE_SW_ENCRYPT); + key->hw_key_idx = keyconf->hw_key_idx; + } + kfree(keyconf); +} + + +static void ieee80211_key_disable_hwaccel(struct ieee80211_local *local, + struct ieee80211_key *key) +{ + struct ieee80211_key_conf *keyconf; + u8 addr[ETH_ALEN]; + + if (!key || key->alg != ALG_WEP || key->force_sw_encrypt || + (local->hw.flags & IEEE80211_HW_DEVICE_HIDES_WEP)) + return; + + memset(addr, 0xff, ETH_ALEN); + keyconf = ieee80211_key_data2conf(local, key); + if (keyconf && local->ops->set_key) + local->ops->set_key(local_to_hw(local), DISABLE_KEY, + addr, keyconf, 0); + kfree(keyconf); + key->force_sw_encrypt = 1; +} + + +static int ieee80211_ioctl_default_wep_only(struct ieee80211_local *local, + int value) +{ + int i; + struct ieee80211_sub_if_data *sdata; + + local->default_wep_only = value; + read_lock(&local->sub_if_lock); + list_for_each_entry(sdata, &local->sub_if_list, list) + for (i = 0; i < NUM_DEFAULT_KEYS; i++) + if (value) + ieee80211_key_enable_hwaccel(local, + sdata->keys[i]); + else + ieee80211_key_disable_hwaccel(local, + sdata->keys[i]); + read_unlock(&local->sub_if_lock); + + return 0; +} + + +void ieee80211_update_default_wep_only(struct ieee80211_local *local) +{ + int i = 0; + struct ieee80211_sub_if_data *sdata; + + read_lock(&local->sub_if_lock); + list_for_each_entry(sdata, &local->sub_if_list, list) { + + if (sdata->dev == local->mdev) + continue; + + /* If there is an AP interface then depend on userspace to + set default_wep_only correctly. */ + if (sdata->type == IEEE80211_IF_TYPE_AP) { + read_unlock(&local->sub_if_lock); + return; + } + + i++; + } + + read_unlock(&local->sub_if_lock); + + if (i <= 1) + ieee80211_ioctl_default_wep_only(local, 1); + else + ieee80211_ioctl_default_wep_only(local, 0); +} + + +static int ieee80211_ioctl_prism2_param(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, char *extra) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_sub_if_data *sdata; + int *i = (int *) extra; + int param = *i; + int value = *(i + 1); + int ret = 0; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + switch (param) { + case PRISM2_PARAM_HOST_ENCRYPT: + case PRISM2_PARAM_HOST_DECRYPT: + /* TODO: implement these; return success now to prevent + * hostapd from aborting */ + break; + + case PRISM2_PARAM_BEACON_INT: + local->hw.conf.beacon_int = value; + if (ieee80211_hw_config(local)) + ret = -EINVAL; + break; + + case PRISM2_PARAM_AP_BRIDGE_PACKETS: + local->bridge_packets = value; + break; + + case PRISM2_PARAM_AP_AUTH_ALGS: + if (sdata->type == IEEE80211_IF_TYPE_STA || + sdata->type == IEEE80211_IF_TYPE_IBSS) { + sdata->u.sta.auth_algs = value; + } else + ret = -EOPNOTSUPP; + break; + + case PRISM2_PARAM_DTIM_PERIOD: + if (value < 1) + ret = -EINVAL; + else if (sdata->type != IEEE80211_IF_TYPE_AP) + ret = -ENOENT; + else + sdata->u.ap.dtim_period = value; + break; + + case PRISM2_PARAM_IEEE_802_1X: + if (local->ops->set_ieee8021x) + ret = local->ops->set_ieee8021x(local_to_hw(local), + value); + if (ret) + printk(KERN_DEBUG "%s: failed to set IEEE 802.1X (%d) " + "for low-level driver\n", dev->name, value); + else + sdata->ieee802_1x = value; + break; + + case PRISM2_PARAM_ANTSEL_TX: + local->hw.conf.antenna_sel_tx = value; + if (ieee80211_hw_config(local)) + ret = -EINVAL; + break; + + case PRISM2_PARAM_ANTSEL_RX: + local->hw.conf.antenna_sel_rx = value; + if (ieee80211_hw_config(local)) + ret = -EINVAL; + break; + + case PRISM2_PARAM_CTS_PROTECT_ERP_FRAMES: + local->cts_protect_erp_frames = value; + break; + + case PRISM2_PARAM_DROP_UNENCRYPTED: + sdata->drop_unencrypted = value; + break; + + case PRISM2_PARAM_PREAMBLE: + local->short_preamble = value; + break; + + case PRISM2_PARAM_STAT_TIME: + if (!local->stat_time && value) { + local->stat_timer.expires = jiffies + HZ * value / 100; + add_timer(&local->stat_timer); + } else if (local->stat_time && !value) { + del_timer_sync(&local->stat_timer); + } + local->stat_time = value; + break; + case PRISM2_PARAM_SHORT_SLOT_TIME: + if (value) + local->hw.conf.flags |= IEEE80211_CONF_SHORT_SLOT_TIME; + else + local->hw.conf.flags &= ~IEEE80211_CONF_SHORT_SLOT_TIME; + if (ieee80211_hw_config(local)) + ret = -EINVAL; + break; + + case PRISM2_PARAM_PRIVACY_INVOKED: + if (local->ops->set_privacy_invoked) + ret = local->ops->set_privacy_invoked( + local_to_hw(local), value); + break; + + case PRISM2_PARAM_NEXT_MODE: + local->next_mode = value; + break; + + case PRISM2_PARAM_CLEAR_KEYS: + ret = ieee80211_ioctl_clear_keys(dev); + break; + + case PRISM2_PARAM_RADIO_ENABLED: + ret = ieee80211_ioctl_set_radio_enabled(dev, value); + break; + + case PRISM2_PARAM_ANTENNA_MODE: + local->hw.conf.antenna_mode = value; + if (ieee80211_hw_config(local)) + ret = -EINVAL; + break; + + case PRISM2_PARAM_BROADCAST_SSID: + if ((value < 0) || (value > 1)) + ret = -EINVAL; + else if (value) + local->hw.conf.flags |= IEEE80211_CONF_SSID_HIDDEN; + else + local->hw.conf.flags &= ~IEEE80211_CONF_SSID_HIDDEN; + break; + + case PRISM2_PARAM_STA_ANTENNA_SEL: + local->sta_antenna_sel = value; + break; + + case PRISM2_PARAM_FORCE_UNICAST_RATE: + ret = ieee80211_ioctl_force_unicast_rate(dev, sdata, value); + break; + + case PRISM2_PARAM_MAX_RATECTRL_RATE: + ret = ieee80211_ioctl_max_ratectrl_rate(dev, sdata, value); + break; + + case PRISM2_PARAM_RATE_CTRL_NUM_UP: + local->rate_ctrl_num_up = value; + break; + + case PRISM2_PARAM_RATE_CTRL_NUM_DOWN: + local->rate_ctrl_num_down = value; + break; + + case PRISM2_PARAM_TX_POWER_REDUCTION: + if (value < 0) + ret = -EINVAL; + else + local->hw.conf.tx_power_reduction = value; + break; + + case PRISM2_PARAM_EAPOL: + sdata->eapol = value; + break; + + case PRISM2_PARAM_KEY_TX_RX_THRESHOLD: + local->key_tx_rx_threshold = value; + break; + + case PRISM2_PARAM_KEY_INDEX: + if (value < 0 || value >= NUM_DEFAULT_KEYS) + ret = -EINVAL; + else if (!sdata->keys[value]) + ret = -ENOENT; + else + sdata->default_key = sdata->keys[value]; + break; + + case PRISM2_PARAM_DEFAULT_WEP_ONLY: + ret = ieee80211_ioctl_default_wep_only(local, value); + break; + + case PRISM2_PARAM_WIFI_WME_NOACK_TEST: + local->wifi_wme_noack_test = value; + break; + + case PRISM2_PARAM_ALLOW_BROADCAST_ALWAYS: + local->allow_broadcast_always = value; + break; + + case PRISM2_PARAM_SCAN_FLAGS: + local->scan_flags = value; + break; + + case PRISM2_PARAM_MIXED_CELL: + if (sdata->type != IEEE80211_IF_TYPE_STA && + sdata->type != IEEE80211_IF_TYPE_IBSS) + ret = -EINVAL; + else + sdata->u.sta.mixed_cell = !!value; + break; + + case PRISM2_PARAM_KEY_MGMT: + if (sdata->type != IEEE80211_IF_TYPE_STA) + ret = -EINVAL; + else + sdata->u.sta.key_mgmt = value; + break; + + case PRISM2_PARAM_HW_MODES: + local->enabled_modes = value; + break; + + case PRISM2_PARAM_CREATE_IBSS: + if (sdata->type != IEEE80211_IF_TYPE_IBSS) + ret = -EINVAL; + else + sdata->u.sta.create_ibss = !!value; + break; + case PRISM2_PARAM_WMM_ENABLED: + if (sdata->type != IEEE80211_IF_TYPE_STA && + sdata->type != IEEE80211_IF_TYPE_IBSS) + ret = -EINVAL; + else + sdata->u.sta.wmm_enabled = !!value; + break; + case PRISM2_PARAM_RADAR_DETECT: + local->hw.conf.radar_detect = value; + break; + case PRISM2_PARAM_SPECTRUM_MGMT: + local->hw.conf.spect_mgmt = value; + break; + case PRISM2_PARAM_MGMT_IF: + if (value == 1) { + if (!local->apdev) + ret = ieee80211_if_add_mgmt(local); + } else if (value == 0) { + if (local->apdev) + ieee80211_if_del_mgmt(local); + } else + ret = -EINVAL; + break; + case PRISM2_PARAM_USER_SPACE_MLME: + local->user_space_mlme = value; + break; + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} + + +static int ieee80211_ioctl_get_prism2_param(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, char *extra) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_sub_if_data *sdata; + int *param = (int *) extra; + int ret = 0; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + switch (*param) { + case PRISM2_PARAM_BEACON_INT: + *param = local->hw.conf.beacon_int; + break; + + case PRISM2_PARAM_AP_BRIDGE_PACKETS: + *param = local->bridge_packets; + break; + + case PRISM2_PARAM_AP_AUTH_ALGS: + if (sdata->type == IEEE80211_IF_TYPE_STA || + sdata->type == IEEE80211_IF_TYPE_IBSS) { + *param = sdata->u.sta.auth_algs; + } else + ret = -EOPNOTSUPP; + break; + + case PRISM2_PARAM_DTIM_PERIOD: + if (sdata->type != IEEE80211_IF_TYPE_AP) + ret = -ENOENT; + else + *param = sdata->u.ap.dtim_period; + break; + + case PRISM2_PARAM_IEEE_802_1X: + *param = sdata->ieee802_1x; + break; + + case PRISM2_PARAM_ANTSEL_TX: + *param = local->hw.conf.antenna_sel_tx; + break; + + case PRISM2_PARAM_ANTSEL_RX: + *param = local->hw.conf.antenna_sel_rx; + break; + + case PRISM2_PARAM_CTS_PROTECT_ERP_FRAMES: + *param = local->cts_protect_erp_frames; + break; + + case PRISM2_PARAM_DROP_UNENCRYPTED: + *param = sdata->drop_unencrypted; + break; + + case PRISM2_PARAM_PREAMBLE: + *param = local->short_preamble; + break; + + case PRISM2_PARAM_STAT_TIME: + *param = local->stat_time; + break; + case PRISM2_PARAM_SHORT_SLOT_TIME: + *param = !!(local->hw.conf.flags & IEEE80211_CONF_SHORT_SLOT_TIME); + break; + + case PRISM2_PARAM_NEXT_MODE: + *param = local->next_mode; + break; + + case PRISM2_PARAM_ANTENNA_MODE: + *param = local->hw.conf.antenna_mode; + break; + + case PRISM2_PARAM_BROADCAST_SSID: + *param = !!(local->hw.conf.flags & IEEE80211_CONF_SSID_HIDDEN); + break; + + case PRISM2_PARAM_STA_ANTENNA_SEL: + *param = local->sta_antenna_sel; + break; + + case PRISM2_PARAM_RATE_CTRL_NUM_UP: + *param = local->rate_ctrl_num_up; + break; + + case PRISM2_PARAM_RATE_CTRL_NUM_DOWN: + *param = local->rate_ctrl_num_down; + break; + + case PRISM2_PARAM_TX_POWER_REDUCTION: + *param = local->hw.conf.tx_power_reduction; + break; + + case PRISM2_PARAM_EAPOL: + *param = sdata->eapol; + break; + + case PRISM2_PARAM_KEY_TX_RX_THRESHOLD: + *param = local->key_tx_rx_threshold; + break; + + case PRISM2_PARAM_KEY_INDEX: + if (!sdata->default_key) + ret = -ENOENT; + else if (sdata->default_key == sdata->keys[0]) + *param = 0; + else if (sdata->default_key == sdata->keys[1]) + *param = 1; + else if (sdata->default_key == sdata->keys[2]) + *param = 2; + else if (sdata->default_key == sdata->keys[3]) + *param = 3; + else + ret = -ENOENT; + break; + + case PRISM2_PARAM_DEFAULT_WEP_ONLY: + *param = local->default_wep_only; + break; + + case PRISM2_PARAM_WIFI_WME_NOACK_TEST: + *param = local->wifi_wme_noack_test; + break; + + case PRISM2_PARAM_ALLOW_BROADCAST_ALWAYS: + *param = local->allow_broadcast_always; + break; + + case PRISM2_PARAM_SCAN_FLAGS: + *param = local->scan_flags; + break; + + case PRISM2_PARAM_HW_MODES: + *param = local->enabled_modes; + break; + + case PRISM2_PARAM_CREATE_IBSS: + if (sdata->type != IEEE80211_IF_TYPE_IBSS) + ret = -EINVAL; + else + *param = !!sdata->u.sta.create_ibss; + break; + + case PRISM2_PARAM_MIXED_CELL: + if (sdata->type != IEEE80211_IF_TYPE_STA && + sdata->type != IEEE80211_IF_TYPE_IBSS) + ret = -EINVAL; + else + *param = !!sdata->u.sta.mixed_cell; + break; + + case PRISM2_PARAM_KEY_MGMT: + if (sdata->type != IEEE80211_IF_TYPE_STA) + ret = -EINVAL; + else + *param = sdata->u.sta.key_mgmt; + break; + case PRISM2_PARAM_WMM_ENABLED: + if (sdata->type != IEEE80211_IF_TYPE_STA && + sdata->type != IEEE80211_IF_TYPE_IBSS) + ret = -EINVAL; + else + *param = !!sdata->u.sta.wmm_enabled; + break; + case PRISM2_PARAM_MGMT_IF: + if (local->apdev) + *param = local->apdev->ifindex; + else + ret = -ENOENT; + break; + case PRISM2_PARAM_USER_SPACE_MLME: + *param = local->user_space_mlme; + break; + + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} + +static int ieee80211_ioctl_siwmlme(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct ieee80211_sub_if_data *sdata; + struct iw_mlme *mlme = (struct iw_mlme *) extra; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + if (sdata->type != IEEE80211_IF_TYPE_STA && + sdata->type != IEEE80211_IF_TYPE_IBSS) + return -EINVAL; + + switch (mlme->cmd) { + case IW_MLME_DEAUTH: + /* TODO: mlme->addr.sa_data */ + return ieee80211_sta_deauthenticate(dev, mlme->reason_code); + case IW_MLME_DISASSOC: + /* TODO: mlme->addr.sa_data */ + return ieee80211_sta_disassociate(dev, mlme->reason_code); + default: + return -EOPNOTSUPP; + } +} + + +static int ieee80211_ioctl_siwencode(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *erq, char *keybuf) +{ + struct ieee80211_sub_if_data *sdata; + int idx, i, alg = ALG_WEP; + u8 bcaddr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + idx = erq->flags & IW_ENCODE_INDEX; + if (idx == 0) { + if (sdata->default_key) + for (i = 0; i < NUM_DEFAULT_KEYS; i++) { + if (sdata->default_key == sdata->keys[i]) { + idx = i; + break; + } + } + } else if (idx < 1 || idx > 4) + return -EINVAL; + else + idx--; + + if (erq->flags & IW_ENCODE_DISABLED) + alg = ALG_NONE; + else if (erq->length == 0) { + /* No key data - just set the default TX key index */ + if (sdata->default_key != sdata->keys[idx]) { + ieee80211_debugfs_key_remove_default(sdata); + sdata->default_key = sdata->keys[idx]; + if (sdata->default_key) + ieee80211_debugfs_key_add_default(sdata); + } + return 0; + } + + return ieee80211_set_encryption( + dev, bcaddr, + idx, alg, + !sdata->default_key, + NULL, keybuf, erq->length); +} + + +static int ieee80211_ioctl_giwencode(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *erq, char *key) +{ + struct ieee80211_sub_if_data *sdata; + int idx, i; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + idx = erq->flags & IW_ENCODE_INDEX; + if (idx < 1 || idx > 4) { + idx = -1; + if (!sdata->default_key) + idx = 0; + else for (i = 0; i < NUM_DEFAULT_KEYS; i++) { + if (sdata->default_key == sdata->keys[i]) { + idx = i; + break; + } + } + if (idx < 0) + return -EINVAL; + } else + idx--; + + erq->flags = idx + 1; + + if (!sdata->keys[idx]) { + erq->length = 0; + erq->flags |= IW_ENCODE_DISABLED; + return 0; + } + + memcpy(key, sdata->keys[idx]->key, + min((int)erq->length, sdata->keys[idx]->keylen)); + erq->length = sdata->keys[idx]->keylen; + erq->flags |= IW_ENCODE_ENABLED; + + return 0; +} + + +static int ieee80211_ioctl_siwgenie(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + return ieee80211_set_gen_ie(dev, extra, data->length); +} + + +static int ieee80211_ioctl_siwauth(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *data, char *extra) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + int ret = 0; + + switch (data->flags & IW_AUTH_INDEX) { + case IW_AUTH_WPA_VERSION: + case IW_AUTH_CIPHER_PAIRWISE: + case IW_AUTH_CIPHER_GROUP: + case IW_AUTH_WPA_ENABLED: + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + break; + case IW_AUTH_KEY_MGMT: + if (sdata->type != IEEE80211_IF_TYPE_STA) + ret = -EINVAL; + else { + /* + * TODO: sdata->u.sta.key_mgmt does not match with WE18 + * value completely; could consider modifying this to + * be closer to WE18. For now, this value is not really + * used for anything else than Privacy matching, so the + * current code here should be more or less OK. + */ + if (data->value & IW_AUTH_KEY_MGMT_802_1X) { + sdata->u.sta.key_mgmt = + IEEE80211_KEY_MGMT_WPA_EAP; + } else if (data->value & IW_AUTH_KEY_MGMT_PSK) { + sdata->u.sta.key_mgmt = + IEEE80211_KEY_MGMT_WPA_PSK; + } else { + sdata->u.sta.key_mgmt = + IEEE80211_KEY_MGMT_NONE; + } + } + break; + case IW_AUTH_80211_AUTH_ALG: + if (sdata->type == IEEE80211_IF_TYPE_STA || + sdata->type == IEEE80211_IF_TYPE_IBSS) + sdata->u.sta.auth_algs = data->value; + else + ret = -EOPNOTSUPP; + break; + case IW_AUTH_PRIVACY_INVOKED: + if (local->ops->set_privacy_invoked) + ret = local->ops->set_privacy_invoked( + local_to_hw(local), data->value); + break; + default: + ret = -EOPNOTSUPP; + break; + } + return ret; +} + +/* Get wireless statistics. Called by /proc/net/wireless and by SIOCGIWSTATS */ +static struct iw_statistics *ieee80211_get_wireless_stats(struct net_device *dev) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct iw_statistics *wstats = &local->wstats; + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct sta_info *sta = NULL; + + if (sdata->type == IEEE80211_IF_TYPE_STA || + sdata->type == IEEE80211_IF_TYPE_IBSS) + sta = sta_info_get(local, sdata->u.sta.bssid); + if (!sta) { + wstats->discard.fragment = 0; + wstats->discard.misc = 0; + wstats->qual.qual = 0; + wstats->qual.level = 0; + wstats->qual.noise = 0; + wstats->qual.updated = IW_QUAL_ALL_INVALID; + } else { + wstats->qual.level = sta->last_rssi; + wstats->qual.qual = sta->last_signal; + wstats->qual.noise = sta->last_noise; + wstats->qual.updated = local->wstats_flags; + sta_info_put(sta); + } + return wstats; +} + +static int ieee80211_ioctl_giwauth(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *data, char *extra) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + int ret = 0; + + switch (data->flags & IW_AUTH_INDEX) { + case IW_AUTH_80211_AUTH_ALG: + if (sdata->type == IEEE80211_IF_TYPE_STA || + sdata->type == IEEE80211_IF_TYPE_IBSS) + data->value = sdata->u.sta.auth_algs; + else + ret = -EOPNOTSUPP; + break; + default: + ret = -EOPNOTSUPP; + break; + } + return ret; +} + + +static int ieee80211_ioctl_siwencodeext(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *erq, char *extra) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct iw_encode_ext *ext = (struct iw_encode_ext *) extra; + int alg, idx, i; + + switch (ext->alg) { + case IW_ENCODE_ALG_NONE: + alg = ALG_NONE; + break; + case IW_ENCODE_ALG_WEP: + alg = ALG_WEP; + break; + case IW_ENCODE_ALG_TKIP: + alg = ALG_TKIP; + break; + case IW_ENCODE_ALG_CCMP: + alg = ALG_CCMP; + break; + default: + return -EOPNOTSUPP; + } + + if (erq->flags & IW_ENCODE_DISABLED) + alg = ALG_NONE; + + idx = erq->flags & IW_ENCODE_INDEX; + if (idx < 1 || idx > 4) { + idx = -1; + if (!sdata->default_key) + idx = 0; + else for (i = 0; i < NUM_DEFAULT_KEYS; i++) { + if (sdata->default_key == sdata->keys[i]) { + idx = i; + break; + } + } + if (idx < 0) + return -EINVAL; + } else + idx--; + + return ieee80211_set_encryption(dev, ext->addr.sa_data, idx, alg, + ext->ext_flags & + IW_ENCODE_EXT_SET_TX_KEY, + NULL, ext->key, ext->key_len); +} + + +static const struct iw_priv_args ieee80211_ioctl_priv[] = { + { PRISM2_IOCTL_PRISM2_PARAM, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "param" }, + { PRISM2_IOCTL_GET_PRISM2_PARAM, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_param" }, +}; + + +int ieee80211_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct iwreq *wrq = (struct iwreq *) rq; + int ret = 0; + + switch (cmd) { + /* Private ioctls (iwpriv) that have not yet been converted + * into new wireless extensions API */ + case PRISM2_IOCTL_HOSTAPD: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = ieee80211_ioctl_priv_hostapd(dev, &wrq->u.data); + break; + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} + + +/* Structures to export the Wireless Handlers */ + +static const iw_handler ieee80211_handler[] = +{ + (iw_handler) NULL, /* SIOCSIWCOMMIT */ + (iw_handler) ieee80211_ioctl_giwname, /* SIOCGIWNAME */ + (iw_handler) NULL, /* SIOCSIWNWID */ + (iw_handler) NULL, /* SIOCGIWNWID */ + (iw_handler) ieee80211_ioctl_siwfreq, /* SIOCSIWFREQ */ + (iw_handler) ieee80211_ioctl_giwfreq, /* SIOCGIWFREQ */ + (iw_handler) ieee80211_ioctl_siwmode, /* SIOCSIWMODE */ + (iw_handler) ieee80211_ioctl_giwmode, /* SIOCGIWMODE */ + (iw_handler) NULL, /* SIOCSIWSENS */ + (iw_handler) NULL, /* SIOCGIWSENS */ + (iw_handler) NULL /* not used */, /* SIOCSIWRANGE */ + (iw_handler) ieee80211_ioctl_giwrange, /* SIOCGIWRANGE */ + (iw_handler) NULL /* not used */, /* SIOCSIWPRIV */ + (iw_handler) NULL /* kernel code */, /* SIOCGIWPRIV */ + (iw_handler) NULL /* not used */, /* SIOCSIWSTATS */ + (iw_handler) NULL /* kernel code */, /* SIOCGIWSTATS */ + iw_handler_set_spy, /* SIOCSIWSPY */ + iw_handler_get_spy, /* SIOCGIWSPY */ + iw_handler_set_thrspy, /* SIOCSIWTHRSPY */ + iw_handler_get_thrspy, /* SIOCGIWTHRSPY */ + (iw_handler) ieee80211_ioctl_siwap, /* SIOCSIWAP */ + (iw_handler) ieee80211_ioctl_giwap, /* SIOCGIWAP */ + (iw_handler) ieee80211_ioctl_siwmlme, /* SIOCSIWMLME */ + (iw_handler) NULL, /* SIOCGIWAPLIST */ + (iw_handler) ieee80211_ioctl_siwscan, /* SIOCSIWSCAN */ + (iw_handler) ieee80211_ioctl_giwscan, /* SIOCGIWSCAN */ + (iw_handler) ieee80211_ioctl_siwessid, /* SIOCSIWESSID */ + (iw_handler) ieee80211_ioctl_giwessid, /* SIOCGIWESSID */ + (iw_handler) NULL, /* SIOCSIWNICKN */ + (iw_handler) NULL, /* SIOCGIWNICKN */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* SIOCSIWRATE */ + (iw_handler) NULL, /* SIOCGIWRATE */ + (iw_handler) ieee80211_ioctl_siwrts, /* SIOCSIWRTS */ + (iw_handler) ieee80211_ioctl_giwrts, /* SIOCGIWRTS */ + (iw_handler) ieee80211_ioctl_siwfrag, /* SIOCSIWFRAG */ + (iw_handler) ieee80211_ioctl_giwfrag, /* SIOCGIWFRAG */ + (iw_handler) NULL, /* SIOCSIWTXPOW */ + (iw_handler) NULL, /* SIOCGIWTXPOW */ + (iw_handler) ieee80211_ioctl_siwretry, /* SIOCSIWRETRY */ + (iw_handler) ieee80211_ioctl_giwretry, /* SIOCGIWRETRY */ + (iw_handler) ieee80211_ioctl_siwencode, /* SIOCSIWENCODE */ + (iw_handler) ieee80211_ioctl_giwencode, /* SIOCGIWENCODE */ + (iw_handler) NULL, /* SIOCSIWPOWER */ + (iw_handler) NULL, /* SIOCGIWPOWER */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) ieee80211_ioctl_siwgenie, /* SIOCSIWGENIE */ + (iw_handler) NULL, /* SIOCGIWGENIE */ + (iw_handler) ieee80211_ioctl_siwauth, /* SIOCSIWAUTH */ + (iw_handler) ieee80211_ioctl_giwauth, /* SIOCGIWAUTH */ + (iw_handler) ieee80211_ioctl_siwencodeext, /* SIOCSIWENCODEEXT */ + (iw_handler) NULL, /* SIOCGIWENCODEEXT */ + (iw_handler) NULL, /* SIOCSIWPMKSA */ + (iw_handler) NULL, /* -- hole -- */ +}; + +static const iw_handler ieee80211_private_handler[] = +{ /* SIOCIWFIRSTPRIV + */ + (iw_handler) ieee80211_ioctl_prism2_param, /* 0 */ + (iw_handler) ieee80211_ioctl_get_prism2_param, /* 1 */ +}; + +const struct iw_handler_def ieee80211_iw_handler_def = +{ + .num_standard = ARRAY_SIZE(ieee80211_handler), + .num_private = ARRAY_SIZE(ieee80211_private_handler), + .num_private_args = ARRAY_SIZE(ieee80211_ioctl_priv), + .standard = (iw_handler *) ieee80211_handler, + .private = (iw_handler *) ieee80211_private_handler, + .private_args = (struct iw_priv_args *) ieee80211_ioctl_priv, + .get_wireless_stats = ieee80211_get_wireless_stats, +}; diff --git a/net/mac80211/ieee80211_key.h b/net/mac80211/ieee80211_key.h new file mode 100644 index 0000000..c333849 --- /dev/null +++ b/net/mac80211/ieee80211_key.h @@ -0,0 +1,106 @@ +/* + * Copyright 2002-2004, Instant802 Networks, Inc. + * Copyright 2005, Devicescape Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef IEEE80211_KEY_H +#define IEEE80211_KEY_H + +#include +#include +#include +#include + +/* ALG_TKIP + * struct ieee80211_key::key is encoded as a 256-bit (32 byte) data block: + * Temporal Encryption Key (128 bits) + * Temporal Authenticator Tx MIC Key (64 bits) + * Temporal Authenticator Rx MIC Key (64 bits) + */ + +#define WEP_IV_LEN 4 +#define WEP_ICV_LEN 4 + +#define ALG_TKIP_KEY_LEN 32 +/* Starting offsets for each key */ +#define ALG_TKIP_TEMP_ENCR_KEY 0 +#define ALG_TKIP_TEMP_AUTH_TX_MIC_KEY 16 +#define ALG_TKIP_TEMP_AUTH_RX_MIC_KEY 24 +#define TKIP_IV_LEN 8 +#define TKIP_ICV_LEN 4 + +#define ALG_CCMP_KEY_LEN 16 +#define CCMP_HDR_LEN 8 +#define CCMP_MIC_LEN 8 +#define CCMP_TK_LEN 16 +#define CCMP_PN_LEN 6 + +#define NUM_RX_DATA_QUEUES 17 + +struct ieee80211_key { + struct kref kref; + + int hw_key_idx; /* filled and used by low-level driver */ + ieee80211_key_alg alg; + union { + struct { + /* last used TSC */ + u32 iv32; + u16 iv16; + u16 p1k[5]; + int tx_initialized; + + /* last received RSC */ + u32 iv32_rx[NUM_RX_DATA_QUEUES]; + u16 iv16_rx[NUM_RX_DATA_QUEUES]; + u16 p1k_rx[NUM_RX_DATA_QUEUES][5]; + int rx_initialized[NUM_RX_DATA_QUEUES]; + } tkip; + struct { + u8 tx_pn[6]; + u8 rx_pn[NUM_RX_DATA_QUEUES][6]; + struct crypto_cipher *tfm; + u32 replays; /* dot11RSNAStatsCCMPReplays */ + /* scratch buffers for virt_to_page() (crypto API) */ +#ifndef AES_BLOCK_LEN +#define AES_BLOCK_LEN 16 +#endif + u8 tx_crypto_buf[6 * AES_BLOCK_LEN]; + u8 rx_crypto_buf[6 * AES_BLOCK_LEN]; + } ccmp; + } u; + int tx_rx_count; /* number of times this key has been used */ + int keylen; + + /* if the low level driver can provide hardware acceleration it should + * clear this flag */ + unsigned int force_sw_encrypt:1; + unsigned int default_tx_key:1; /* This key is the new default TX key + * (used only for broadcast keys). */ + s8 keyidx; /* WEP key index */ + +#ifdef CONFIG_MAC80211_DEBUGFS + struct { + struct dentry *stalink; + struct dentry *dir; + struct dentry *keylen; + struct dentry *force_sw_encrypt; + struct dentry *keyidx; + struct dentry *hw_key_idx; + struct dentry *tx_rx_count; + struct dentry *algorithm; + struct dentry *tx_spec; + struct dentry *rx_spec; + struct dentry *replays; + struct dentry *key; + } debugfs; +#endif + + u8 key[0]; +}; + +#endif /* IEEE80211_KEY_H */ diff --git a/net/mac80211/ieee80211_led.c b/net/mac80211/ieee80211_led.c new file mode 100644 index 0000000..719d75b --- /dev/null +++ b/net/mac80211/ieee80211_led.c @@ -0,0 +1,91 @@ +/* + * Copyright 2006, Johannes Berg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* just for IFNAMSIZ */ +#include +#include "ieee80211_led.h" + +void ieee80211_led_rx(struct ieee80211_local *local) +{ + if (unlikely(!local->rx_led)) + return; + if (local->rx_led_counter++ % 2 == 0) + led_trigger_event(local->rx_led, LED_OFF); + else + led_trigger_event(local->rx_led, LED_FULL); +} + +/* q is 1 if a packet was enqueued, 0 if it has been transmitted */ +void ieee80211_led_tx(struct ieee80211_local *local, int q) +{ + if (unlikely(!local->tx_led)) + return; + /* not sure how this is supposed to work ... */ + local->tx_led_counter += 2*q-1; + if (local->tx_led_counter % 2 == 0) + led_trigger_event(local->tx_led, LED_OFF); + else + led_trigger_event(local->tx_led, LED_FULL); +} + +void ieee80211_led_init(struct ieee80211_local *local) +{ + local->rx_led = kzalloc(sizeof(struct led_trigger), GFP_KERNEL); + if (!local->rx_led) + return; + snprintf(local->rx_led_name, sizeof(local->rx_led_name), + "%srx", wiphy_name(local->hw.wiphy)); + local->rx_led->name = local->rx_led_name; + if (led_trigger_register(local->rx_led)) { + kfree(local->rx_led); + local->rx_led = NULL; + } + + local->tx_led = kzalloc(sizeof(struct led_trigger), GFP_KERNEL); + if (!local->tx_led) + return; + snprintf(local->tx_led_name, sizeof(local->tx_led_name), + "%stx", wiphy_name(local->hw.wiphy)); + local->tx_led->name = local->tx_led_name; + if (led_trigger_register(local->tx_led)) { + kfree(local->tx_led); + local->tx_led = NULL; + } +} + +void ieee80211_led_exit(struct ieee80211_local *local) +{ + if (local->tx_led) { + led_trigger_unregister(local->tx_led); + kfree(local->tx_led); + } + if (local->rx_led) { + led_trigger_unregister(local->rx_led); + kfree(local->rx_led); + } +} + +char *__ieee80211_get_tx_led_name(struct ieee80211_hw *hw) +{ + struct ieee80211_local *local = hw_to_local(hw); + + if (local->tx_led) + return local->tx_led_name; + return NULL; +} +EXPORT_SYMBOL(__ieee80211_get_tx_led_name); + +char *__ieee80211_get_rx_led_name(struct ieee80211_hw *hw) +{ + struct ieee80211_local *local = hw_to_local(hw); + + if (local->rx_led) + return local->rx_led_name; + return NULL; +} +EXPORT_SYMBOL(__ieee80211_get_rx_led_name); diff --git a/net/mac80211/ieee80211_led.h b/net/mac80211/ieee80211_led.h new file mode 100644 index 0000000..5c8ab82 --- /dev/null +++ b/net/mac80211/ieee80211_led.h @@ -0,0 +1,32 @@ +/* + * Copyright 2006, Johannes Berg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include "ieee80211_i.h" + +#ifdef CONFIG_MAC80211_LEDS +extern void ieee80211_led_rx(struct ieee80211_local *local); +extern void ieee80211_led_tx(struct ieee80211_local *local, int q); +extern void ieee80211_led_init(struct ieee80211_local *local); +extern void ieee80211_led_exit(struct ieee80211_local *local); +#else +static inline void ieee80211_led_rx(struct ieee80211_local *local) +{ +} +static inline void ieee80211_led_tx(struct ieee80211_local *local, int q) +{ +} +static inline void ieee80211_led_init(struct ieee80211_local *local) +{ +} +static inline void ieee80211_led_exit(struct ieee80211_local *local) +{ +} +#endif diff --git a/net/mac80211/ieee80211_rate.c b/net/mac80211/ieee80211_rate.c new file mode 100644 index 0000000..16e8508 --- /dev/null +++ b/net/mac80211/ieee80211_rate.c @@ -0,0 +1,140 @@ +/* + * Copyright 2002-2005, Instant802 Networks, Inc. + * Copyright 2005-2006, Devicescape Software, Inc. + * Copyright (c) 2006 Jiri Benc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include "ieee80211_rate.h" +#include "ieee80211_i.h" + +struct rate_control_alg { + struct list_head list; + struct rate_control_ops *ops; +}; + +static LIST_HEAD(rate_ctrl_algs); +static DEFINE_MUTEX(rate_ctrl_mutex); + +int ieee80211_rate_control_register(struct rate_control_ops *ops) +{ + struct rate_control_alg *alg; + + alg = kmalloc(sizeof(*alg), GFP_KERNEL); + if (alg == NULL) { + return -ENOMEM; + } + memset(alg, 0, sizeof(*alg)); + alg->ops = ops; + + mutex_lock(&rate_ctrl_mutex); + list_add_tail(&alg->list, &rate_ctrl_algs); + mutex_unlock(&rate_ctrl_mutex); + + return 0; +} +EXPORT_SYMBOL(ieee80211_rate_control_register); + +void ieee80211_rate_control_unregister(struct rate_control_ops *ops) +{ + struct rate_control_alg *alg; + + mutex_lock(&rate_ctrl_mutex); + list_for_each_entry(alg, &rate_ctrl_algs, list) { + if (alg->ops == ops) { + list_del(&alg->list); + break; + } + } + mutex_unlock(&rate_ctrl_mutex); + kfree(alg); +} +EXPORT_SYMBOL(ieee80211_rate_control_unregister); + +static struct rate_control_ops * +ieee80211_try_rate_control_ops_get(const char *name) +{ + struct rate_control_alg *alg; + struct rate_control_ops *ops = NULL; + + mutex_lock(&rate_ctrl_mutex); + list_for_each_entry(alg, &rate_ctrl_algs, list) { + if (!name || !strcmp(alg->ops->name, name)) + if (try_module_get(alg->ops->module)) { + ops = alg->ops; + break; + } + } + mutex_unlock(&rate_ctrl_mutex); + return ops; +} + +/* Get the rate control algorithm. If `name' is NULL, get the first + * available algorithm. */ +static struct rate_control_ops * +ieee80211_rate_control_ops_get(const char *name) +{ + struct rate_control_ops *ops; + + ops = ieee80211_try_rate_control_ops_get(name); + if (!ops) { + request_module("rc80211_%s", name ? name : "default"); + ops = ieee80211_try_rate_control_ops_get(name); + } + return ops; +} + +static void ieee80211_rate_control_ops_put(struct rate_control_ops *ops) +{ + module_put(ops->module); +} + +struct rate_control_ref *rate_control_alloc(const char *name, + struct ieee80211_local *local) +{ + struct rate_control_ref *ref; + + ref = kmalloc(sizeof(struct rate_control_ref), GFP_KERNEL); + if (!ref) + goto fail_ref; + kref_init(&ref->kref); + ref->ops = ieee80211_rate_control_ops_get(name); + if (!ref->ops) + goto fail_ops; + ref->priv = ref->ops->alloc(local); + if (!ref->priv) + goto fail_priv; + return ref; + +fail_priv: + ieee80211_rate_control_ops_put(ref->ops); +fail_ops: + kfree(ref); +fail_ref: + return NULL; +} + +static void rate_control_release(struct kref *kref) +{ + struct rate_control_ref *ctrl_ref; + + ctrl_ref = container_of(kref, struct rate_control_ref, kref); + ctrl_ref->ops->free(ctrl_ref->priv); + ieee80211_rate_control_ops_put(ctrl_ref->ops); + kfree(ctrl_ref); +} + +struct rate_control_ref *rate_control_get(struct rate_control_ref *ref) +{ + kref_get(&ref->kref); + return ref; +} + +void rate_control_put(struct rate_control_ref *ref) +{ + kref_put(&ref->kref, rate_control_release); +} diff --git a/net/mac80211/ieee80211_rate.h b/net/mac80211/ieee80211_rate.h new file mode 100644 index 0000000..f021a02 --- /dev/null +++ b/net/mac80211/ieee80211_rate.h @@ -0,0 +1,144 @@ +/* + * Copyright 2002-2005, Instant802 Networks, Inc. + * Copyright 2005, Devicescape Software, Inc. + * Copyright (c) 2006 Jiri Benc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef IEEE80211_RATE_H +#define IEEE80211_RATE_H + +#include +#include +#include +#include +#include "ieee80211_i.h" +#include "sta_info.h" + +#define RATE_CONTROL_NUM_DOWN 20 +#define RATE_CONTROL_NUM_UP 15 + + +struct rate_control_extra { + /* values from rate_control_get_rate() to the caller: */ + struct ieee80211_rate *probe; /* probe with this rate, or NULL for no + * probing */ + struct ieee80211_rate *nonerp; + + /* parameters from the caller to rate_control_get_rate(): */ + struct ieee80211_hw_mode *mode; + int mgmt_data; /* this is data frame that is used for management + * (e.g., IEEE 802.1X EAPOL) */ + u16 ethertype; +}; + + +struct rate_control_ops { + struct module *module; + const char *name; + void (*tx_status)(void *priv, struct net_device *dev, + struct sk_buff *skb, + struct ieee80211_tx_status *status); + struct ieee80211_rate *(*get_rate)(void *priv, struct net_device *dev, + struct sk_buff *skb, + struct rate_control_extra *extra); + void (*rate_init)(void *priv, void *priv_sta, + struct ieee80211_local *local, struct sta_info *sta); + void (*clear)(void *priv); + + void *(*alloc)(struct ieee80211_local *local); + void (*free)(void *priv); + void *(*alloc_sta)(void *priv, gfp_t gfp); + void (*free_sta)(void *priv, void *priv_sta); + + int (*add_attrs)(void *priv, struct kobject *kobj); + void (*remove_attrs)(void *priv, struct kobject *kobj); + void (*add_sta_debugfs)(void *priv, void *priv_sta, + struct dentry *dir); + void (*remove_sta_debugfs)(void *priv, void *priv_sta); +}; + +struct rate_control_ref { + struct rate_control_ops *ops; + void *priv; + struct kref kref; +}; + +int ieee80211_rate_control_register(struct rate_control_ops *ops); +void ieee80211_rate_control_unregister(struct rate_control_ops *ops); + +/* Get a reference to the rate control algorithm. If `name' is NULL, get the + * first available algorithm. */ +struct rate_control_ref *rate_control_alloc(const char *name, + struct ieee80211_local *local); +struct rate_control_ref *rate_control_get(struct rate_control_ref *ref); +void rate_control_put(struct rate_control_ref *ref); + +static inline void rate_control_tx_status(struct ieee80211_local *local, + struct net_device *dev, + struct sk_buff *skb, + struct ieee80211_tx_status *status) +{ + struct rate_control_ref *ref = local->rate_ctrl; + ref->ops->tx_status(ref->priv, dev, skb, status); +} + + +static inline struct ieee80211_rate * +rate_control_get_rate(struct ieee80211_local *local, struct net_device *dev, + struct sk_buff *skb, struct rate_control_extra *extra) +{ + struct rate_control_ref *ref = local->rate_ctrl; + return ref->ops->get_rate(ref->priv, dev, skb, extra); +} + + +static inline void rate_control_rate_init(struct sta_info *sta, + struct ieee80211_local *local) +{ + struct rate_control_ref *ref = sta->rate_ctrl; + ref->ops->rate_init(ref->priv, sta->rate_ctrl_priv, local, sta); +} + + +static inline void rate_control_clear(struct ieee80211_local *local) +{ + struct rate_control_ref *ref = local->rate_ctrl; + ref->ops->clear(ref->priv); +} + +static inline void *rate_control_alloc_sta(struct rate_control_ref *ref, + gfp_t gfp) +{ + return ref->ops->alloc_sta(ref->priv, gfp); +} + +static inline void rate_control_free_sta(struct rate_control_ref *ref, + void *priv) +{ + ref->ops->free_sta(ref->priv, priv); +} + +static inline void rate_control_add_sta_debugfs(struct sta_info *sta) +{ +#ifdef CONFIG_MAC80211_DEBUGFS + struct rate_control_ref *ref = sta->rate_ctrl; + if (sta->debugfs.dir && ref->ops->add_sta_debugfs) + ref->ops->add_sta_debugfs(ref->priv, sta->rate_ctrl_priv, + sta->debugfs.dir); +#endif +} + +static inline void rate_control_remove_sta_debugfs(struct sta_info *sta) +{ +#ifdef CONFIG_MAC80211_DEBUGFS + struct rate_control_ref *ref = sta->rate_ctrl; + if (ref->ops->remove_sta_debugfs) + ref->ops->remove_sta_debugfs(ref->priv, sta->rate_ctrl_priv); +#endif +} + +#endif /* IEEE80211_RATE_H */ diff --git a/net/mac80211/ieee80211_sta.c b/net/mac80211/ieee80211_sta.c new file mode 100644 index 0000000..b003912 --- /dev/null +++ b/net/mac80211/ieee80211_sta.c @@ -0,0 +1,3219 @@ +/* + * BSS client mode implementation + * Copyright 2003, Jouni Malinen + * Copyright 2004, Instant802 Networks, Inc. + * Copyright 2005, Devicescape Software, Inc. + * Copyright 2006-2007 Jiri Benc + * Copyright 2007, Michael Wu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* TODO: + * BSS table: use as the key to support multi-SSID APs + * order BSS list by RSSI(?) ("quality of AP") + * scan result table filtering (by capability (privacy, IBSS/BSS, WPA/RSN IE, + * SSID) + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "ieee80211_i.h" +#include "ieee80211_rate.h" +#include "hostapd_ioctl.h" + +#define IEEE80211_AUTH_TIMEOUT (HZ / 5) +#define IEEE80211_AUTH_MAX_TRIES 3 +#define IEEE80211_ASSOC_TIMEOUT (HZ / 5) +#define IEEE80211_ASSOC_MAX_TRIES 3 +#define IEEE80211_MONITORING_INTERVAL (2 * HZ) +#define IEEE80211_PROBE_INTERVAL (60 * HZ) +#define IEEE80211_RETRY_AUTH_INTERVAL (1 * HZ) +#define IEEE80211_SCAN_INTERVAL (2 * HZ) +#define IEEE80211_SCAN_INTERVAL_SLOW (15 * HZ) +#define IEEE80211_IBSS_JOIN_TIMEOUT (20 * HZ) + +#define IEEE80211_PROBE_DELAY (HZ / 33) +#define IEEE80211_CHANNEL_TIME (HZ / 33) +#define IEEE80211_PASSIVE_CHANNEL_TIME (HZ / 5) +#define IEEE80211_SCAN_RESULT_EXPIRE (10 * HZ) +#define IEEE80211_IBSS_MERGE_INTERVAL (30 * HZ) +#define IEEE80211_IBSS_INACTIVITY_LIMIT (60 * HZ) + +#define IEEE80211_IBSS_MAX_STA_ENTRIES 128 + + +#define IEEE80211_FC(type, stype) cpu_to_le16(type | stype) + +#define ERP_INFO_USE_PROTECTION BIT(1) + +/* mgmt header + 1 byte action code */ +#define IEEE80211_MIN_ACTION_SIZE (24 + 1) + +static void ieee80211_send_probe_req(struct net_device *dev, u8 *dst, + u8 *ssid, size_t ssid_len); +static struct ieee80211_sta_bss * +ieee80211_rx_bss_get(struct net_device *dev, u8 *bssid); +static void ieee80211_rx_bss_put(struct net_device *dev, + struct ieee80211_sta_bss *bss); +static int ieee80211_sta_find_ibss(struct net_device *dev, + struct ieee80211_if_sta *ifsta); +static int ieee80211_sta_wep_configured(struct net_device *dev); +static int ieee80211_sta_start_scan(struct net_device *dev, + u8 *ssid, size_t ssid_len); +static int ieee80211_sta_config_auth(struct net_device *dev, + struct ieee80211_if_sta *ifsta); + + +/* Parsed Information Elements */ +struct ieee802_11_elems { + u8 *ssid; + u8 ssid_len; + u8 *supp_rates; + u8 supp_rates_len; + u8 *fh_params; + u8 fh_params_len; + u8 *ds_params; + u8 ds_params_len; + u8 *cf_params; + u8 cf_params_len; + u8 *tim; + u8 tim_len; + u8 *ibss_params; + u8 ibss_params_len; + u8 *challenge; + u8 challenge_len; + u8 *wpa; + u8 wpa_len; + u8 *rsn; + u8 rsn_len; + u8 *erp_info; + u8 erp_info_len; + u8 *ht_cap_param; + u8 ht_cap_param_len; + u8 *ht_extra_param; + u8 ht_extra_param_len; + u8 *ext_supp_rates; + u8 ext_supp_rates_len; + u8 *wmm_info; + u8 wmm_info_len; + u8 *wmm_param; + u8 wmm_param_len; +}; + +typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes; + + +static ParseRes ieee802_11_parse_elems(u8 *start, size_t len, + struct ieee802_11_elems *elems) +{ + size_t left = len; + u8 *pos = start; + int unknown = 0; + + memset(elems, 0, sizeof(*elems)); + + while (left >= 2) { + u8 id, elen; + + id = *pos++; + elen = *pos++; + left -= 2; + + if (elen > left) { +#if 0 + if (net_ratelimit()) + printk(KERN_DEBUG "IEEE 802.11 element parse " + "failed (id=%d elen=%d left=%d)\n", + id, elen, left); +#endif + return ParseFailed; + } + + switch (id) { + case WLAN_EID_SSID: + elems->ssid = pos; + elems->ssid_len = elen; + break; + case WLAN_EID_SUPP_RATES: + elems->supp_rates = pos; + elems->supp_rates_len = elen; + break; + case WLAN_EID_FH_PARAMS: + elems->fh_params = pos; + elems->fh_params_len = elen; + break; + case WLAN_EID_DS_PARAMS: + elems->ds_params = pos; + elems->ds_params_len = elen; + break; + case WLAN_EID_CF_PARAMS: + elems->cf_params = pos; + elems->cf_params_len = elen; + break; + case WLAN_EID_TIM: + elems->tim = pos; + elems->tim_len = elen; + break; + case WLAN_EID_IBSS_PARAMS: + elems->ibss_params = pos; + elems->ibss_params_len = elen; + break; + case WLAN_EID_CHALLENGE: + elems->challenge = pos; + elems->challenge_len = elen; + break; + case WLAN_EID_WPA: + if (elen >= 4 && pos[0] == 0x00 && pos[1] == 0x50 && + pos[2] == 0xf2) { + /* Microsoft OUI (00:50:F2) */ + if (pos[3] == 1) { + /* OUI Type 1 - WPA IE */ + elems->wpa = pos; + elems->wpa_len = elen; + } else if (elen >= 5 && pos[3] == 2) { + if (pos[4] == 0) { + elems->wmm_info = pos; + elems->wmm_info_len = elen; + } else if (pos[4] == 1) { + elems->wmm_param = pos; + elems->wmm_param_len = elen; + } + } + } + break; + case WLAN_EID_RSN: + elems->rsn = pos; + elems->rsn_len = elen; + break; + case WLAN_EID_ERP_INFO: + elems->erp_info = pos; + elems->erp_info_len = elen; + break; + case WLAN_EID_EXT_SUPP_RATES: + elems->ext_supp_rates = pos; + elems->ext_supp_rates_len = elen; + break; + case WLAN_EID_HT_CAPABILITY: + elems->ht_cap_param = pos; + elems->ht_cap_param_len = elen; + break; + case WLAN_EID_HT_EXTRA_INFO: + elems->ht_extra_param = pos; + elems->ht_extra_param_len = elen; + break; + default: +#if 0 + printk(KERN_DEBUG "IEEE 802.11 element parse ignored " + "unknown element (id=%d elen=%d)\n", + id, elen); +#endif + unknown++; + break; + } + + left -= elen; + pos += elen; + } + + /* Do not trigger error if left == 1 as Apple Airport base stations + * send AssocResps that are one spurious byte too long. */ + + return unknown ? ParseUnknown : ParseOK; +} + + + + +static int ecw2cw(int ecw) +{ + int cw = 1; + while (ecw > 0) { + cw <<= 1; + ecw--; + } + return cw - 1; +} + +static void ieee80211_sta_wmm_params(struct net_device *dev, + struct ieee80211_if_sta *ifsta, + u8 *wmm_param, size_t wmm_param_len) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_tx_queue_params params; + size_t left; + int count; + u8 *pos; + + if (wmm_param_len < 8 || wmm_param[5] /* version */ != 1) + return; + count = wmm_param[6] & 0x0f; + if (count == ifsta->wmm_last_param_set) + return; + ifsta->wmm_last_param_set = count; + + pos = wmm_param + 8; + left = wmm_param_len - 8; + + memset(¶ms, 0, sizeof(params)); + + if (!local->ops->conf_tx) + return; + + local->wmm_acm = 0; + for (; left >= 4; left -= 4, pos += 4) { + int aci = (pos[0] >> 5) & 0x03; + int acm = (pos[0] >> 4) & 0x01; + int queue; + + switch (aci) { + case 1: + queue = IEEE80211_TX_QUEUE_DATA3; + if (acm) { + local->wmm_acm |= BIT(0) | BIT(3); + } + break; + case 2: + queue = IEEE80211_TX_QUEUE_DATA1; + if (acm) { + local->wmm_acm |= BIT(4) | BIT(5); + } + break; + case 3: + queue = IEEE80211_TX_QUEUE_DATA0; + if (acm) { + local->wmm_acm |= BIT(6) | BIT(7); + } + break; + case 0: + default: + queue = IEEE80211_TX_QUEUE_DATA2; + if (acm) { + local->wmm_acm |= BIT(1) | BIT(2); + } + break; + } + + params.aifs = pos[0] & 0x0f; + params.cw_max = ecw2cw((pos[1] & 0xf0) >> 4); + params.cw_min = ecw2cw(pos[1] & 0x0f); + /* TXOP is in units of 32 usec; burst_time in 0.1 ms */ + params.burst_time = (pos[2] | (pos[3] << 8)) * 32 / 100; + printk(KERN_DEBUG "%s: WMM queue=%d aci=%d acm=%d aifs=%d " + "cWmin=%d cWmax=%d burst=%d\n", + dev->name, queue, aci, acm, params.aifs, params.cw_min, + params.cw_max, params.burst_time); + /* TODO: handle ACM (block TX, fallback to next lowest allowed + * AC for now) */ + if (local->ops->conf_tx(local_to_hw(local), queue, ¶ms)) { + printk(KERN_DEBUG "%s: failed to set TX queue " + "parameters for queue %d\n", dev->name, queue); + } + } +} + + +static void ieee80211_sta_send_associnfo(struct net_device *dev, + struct ieee80211_if_sta *ifsta) +{ + char *buf; + size_t len; + int i; + union iwreq_data wrqu; + + if (!ifsta->assocreq_ies && !ifsta->assocresp_ies) + return; + + buf = kmalloc(50 + 2 * (ifsta->assocreq_ies_len + + ifsta->assocresp_ies_len), GFP_ATOMIC); + if (!buf) + return; + + len = sprintf(buf, "ASSOCINFO("); + if (ifsta->assocreq_ies) { + len += sprintf(buf + len, "ReqIEs="); + for (i = 0; i < ifsta->assocreq_ies_len; i++) { + len += sprintf(buf + len, "%02x", + ifsta->assocreq_ies[i]); + } + } + if (ifsta->assocresp_ies) { + if (ifsta->assocreq_ies) + len += sprintf(buf + len, " "); + len += sprintf(buf + len, "RespIEs="); + for (i = 0; i < ifsta->assocresp_ies_len; i++) { + len += sprintf(buf + len, "%02x", + ifsta->assocresp_ies[i]); + } + } + len += sprintf(buf + len, ")"); + + if (len > IW_CUSTOM_MAX) { + len = sprintf(buf, "ASSOCRESPIE="); + for (i = 0; i < ifsta->assocresp_ies_len; i++) { + len += sprintf(buf + len, "%02x", + ifsta->assocresp_ies[i]); + } + } + + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = len; + wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf); + + kfree(buf); +} + + +static void ieee80211_set_associated(struct net_device *dev, + struct ieee80211_if_sta *ifsta, int assoc) +{ + union iwreq_data wrqu; + + if (ifsta->associated == assoc) + return; + + ifsta->associated = assoc; + + if (assoc) { + struct ieee80211_sub_if_data *sdata; + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + if (sdata->type != IEEE80211_IF_TYPE_STA) + return; + netif_carrier_on(dev); + ifsta->prev_bssid_set = 1; + memcpy(ifsta->prev_bssid, sdata->u.sta.bssid, ETH_ALEN); + memcpy(wrqu.ap_addr.sa_data, sdata->u.sta.bssid, ETH_ALEN); + ieee80211_sta_send_associnfo(dev, ifsta); + } else { + netif_carrier_off(dev); + memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN); + } + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); + ifsta->last_probe = jiffies; +} + +static void ieee80211_set_disassoc(struct net_device *dev, + struct ieee80211_if_sta *ifsta, int deauth) +{ + if (deauth) + ifsta->auth_tries = 0; + ifsta->assoc_tries = 0; + ieee80211_set_associated(dev, ifsta, 0); +} + +static void ieee80211_sta_tx(struct net_device *dev, struct sk_buff *skb, + int encrypt) +{ + struct ieee80211_sub_if_data *sdata; + struct ieee80211_tx_packet_data *pkt_data; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + skb->dev = sdata->local->mdev; + skb_set_mac_header(skb, 0); + skb_set_network_header(skb, 0); + skb_set_transport_header(skb, 0); + + pkt_data = (struct ieee80211_tx_packet_data *) skb->cb; + memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data)); + pkt_data->ifindex = sdata->dev->ifindex; + pkt_data->mgmt_iface = (sdata->type == IEEE80211_IF_TYPE_MGMT); + pkt_data->do_not_encrypt = !encrypt; + + dev_queue_xmit(skb); +} + + +static void ieee80211_send_auth(struct net_device *dev, + struct ieee80211_if_sta *ifsta, + int transaction, u8 *extra, size_t extra_len, + int encrypt) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct sk_buff *skb; + struct ieee80211_mgmt *mgmt; + + skb = dev_alloc_skb(local->hw.extra_tx_headroom + + sizeof(*mgmt) + 6 + extra_len); + if (!skb) { + printk(KERN_DEBUG "%s: failed to allocate buffer for auth " + "frame\n", dev->name); + return; + } + skb_reserve(skb, local->hw.extra_tx_headroom); + + mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24 + 6); + memset(mgmt, 0, 24 + 6); + mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT, + IEEE80211_STYPE_AUTH); + if (encrypt) + mgmt->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); + memcpy(mgmt->da, ifsta->bssid, ETH_ALEN); + memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN); + memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN); + mgmt->u.auth.auth_alg = cpu_to_le16(ifsta->auth_alg); + mgmt->u.auth.auth_transaction = cpu_to_le16(transaction); + ifsta->auth_transaction = transaction + 1; + mgmt->u.auth.status_code = cpu_to_le16(0); + if (extra) + memcpy(skb_put(skb, extra_len), extra, extra_len); + + ieee80211_sta_tx(dev, skb, encrypt); +} + + +static void ieee80211_authenticate(struct net_device *dev, + struct ieee80211_if_sta *ifsta) +{ + ifsta->auth_tries++; + if (ifsta->auth_tries > IEEE80211_AUTH_MAX_TRIES) { + printk(KERN_DEBUG "%s: authentication with AP " MAC_FMT + " timed out\n", + dev->name, MAC_ARG(ifsta->bssid)); + ifsta->state = IEEE80211_DISABLED; + return; + } + + ifsta->state = IEEE80211_AUTHENTICATE; + printk(KERN_DEBUG "%s: authenticate with AP " MAC_FMT "\n", + dev->name, MAC_ARG(ifsta->bssid)); + + ieee80211_send_auth(dev, ifsta, 1, NULL, 0, 0); + + mod_timer(&ifsta->timer, jiffies + IEEE80211_AUTH_TIMEOUT); +} + + +static void ieee80211_send_assoc(struct net_device *dev, + struct ieee80211_if_sta *ifsta) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_hw_mode *mode; + struct sk_buff *skb; + struct ieee80211_mgmt *mgmt; + u8 *pos, *ies; + int i, len; + u16 capab; + struct ieee80211_sta_bss *bss; + int wmm = 0; + int ht_enabled = 0; + + skb = dev_alloc_skb(local->hw.extra_tx_headroom + + sizeof(*mgmt) + 200 + ifsta->extra_ie_len + + ifsta->ssid_len); + if (!skb) { + printk(KERN_DEBUG "%s: failed to allocate buffer for assoc " + "frame\n", dev->name); + return; + } + skb_reserve(skb, local->hw.extra_tx_headroom); + + mode = local->oper_hw_mode; + capab = ifsta->capab; + if (mode->mode == MODE_IEEE80211G) { + capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME | + WLAN_CAPABILITY_SHORT_PREAMBLE; + } + bss = ieee80211_rx_bss_get(dev, ifsta->bssid); + if (bss) { + if (bss->capability & WLAN_CAPABILITY_PRIVACY) + capab |= WLAN_CAPABILITY_PRIVACY; + if (bss->wmm_ie) { + wmm = 1; + + ht_enabled = 1; + } + ieee80211_rx_bss_put(dev, bss); + } + + mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); + memset(mgmt, 0, 24); + memcpy(mgmt->da, ifsta->bssid, ETH_ALEN); + memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN); + memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN); + + if (ifsta->prev_bssid_set) { + skb_put(skb, 10); + mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT, + IEEE80211_STYPE_REASSOC_REQ); + mgmt->u.reassoc_req.capab_info = cpu_to_le16(capab); + mgmt->u.reassoc_req.listen_interval = cpu_to_le16(1); + memcpy(mgmt->u.reassoc_req.current_ap, ifsta->prev_bssid, + ETH_ALEN); + } else { + skb_put(skb, 4); + mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT, + IEEE80211_STYPE_ASSOC_REQ); + mgmt->u.assoc_req.capab_info = cpu_to_le16(capab); + mgmt->u.assoc_req.listen_interval = cpu_to_le16(1); + } + + /* SSID */ + ies = pos = skb_put(skb, 2 + ifsta->ssid_len); + *pos++ = WLAN_EID_SSID; + *pos++ = ifsta->ssid_len; + memcpy(pos, ifsta->ssid, ifsta->ssid_len); + + len = mode->num_rates; + if (len > 8) + len = 8; + pos = skb_put(skb, len + 2); + *pos++ = WLAN_EID_SUPP_RATES; + *pos++ = len; + for (i = 0; i < len; i++) { + int rate = mode->rates[i].rate; + if (mode->mode == MODE_ATHEROS_TURBO) + rate /= 2; + *pos++ = (u8) (rate / 5); + } + + if (mode->num_rates > len) { + pos = skb_put(skb, mode->num_rates - len + 2); + *pos++ = WLAN_EID_EXT_SUPP_RATES; + *pos++ = mode->num_rates - len; + for (i = len; i < mode->num_rates; i++) { + int rate = mode->rates[i].rate; + if (mode->mode == MODE_ATHEROS_TURBO) + rate /= 2; + *pos++ = (u8) (rate / 5); + } + } + + if (ifsta->extra_ie) { + pos = skb_put(skb, ifsta->extra_ie_len); + memcpy(pos, ifsta->extra_ie, ifsta->extra_ie_len); + } + + if (wmm && ifsta->wmm_enabled) { + pos = skb_put(skb, 9); + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + *pos++ = 7; /* len */ + *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */ + *pos++ = 0x50; + *pos++ = 0xf2; + *pos++ = 2; /* WME */ + *pos++ = 0; /* WME info */ + *pos++ = 1; /* WME ver */ + *pos++ = 0; + } + + /* if low level driver supports 11n, fill in 11n IE */ + if (ht_enabled && ifsta->ht_enabled && local->ops->get_ht_capab) { + pos = skb_put(skb, sizeof(struct ieee80211_ht_capability)+2); + *pos++ = WLAN_EID_HT_CAPABILITY; + *pos++ = sizeof(struct ieee80211_ht_capability); + memset(pos, 0, sizeof(struct ieee80211_ht_capability)); + local->ops->get_ht_capab(local_to_hw(local), + (struct ieee80211_ht_capability *)pos); + } + + kfree(ifsta->assocreq_ies); + ifsta->assocreq_ies_len = (skb->data + skb->len) - ies; + ifsta->assocreq_ies = kmalloc(ifsta->assocreq_ies_len, GFP_ATOMIC); + if (ifsta->assocreq_ies) + memcpy(ifsta->assocreq_ies, ies, ifsta->assocreq_ies_len); + + ieee80211_sta_tx(dev, skb, 0); +} + + +static void ieee80211_send_deauth(struct net_device *dev, + struct ieee80211_if_sta *ifsta, u16 reason) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct sk_buff *skb; + struct ieee80211_mgmt *mgmt; + + skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt)); + if (!skb) { + printk(KERN_DEBUG "%s: failed to allocate buffer for deauth " + "frame\n", dev->name); + return; + } + skb_reserve(skb, local->hw.extra_tx_headroom); + + mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); + memset(mgmt, 0, 24); + memcpy(mgmt->da, ifsta->bssid, ETH_ALEN); + memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN); + memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN); + mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT, + IEEE80211_STYPE_DEAUTH); + skb_put(skb, 2); + mgmt->u.deauth.reason_code = cpu_to_le16(reason); + + ieee80211_sta_tx(dev, skb, 0); +} + + +static void ieee80211_send_disassoc(struct net_device *dev, + struct ieee80211_if_sta *ifsta, u16 reason) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct sk_buff *skb; + struct ieee80211_mgmt *mgmt; + + skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt)); + if (!skb) { + printk(KERN_DEBUG "%s: failed to allocate buffer for disassoc " + "frame\n", dev->name); + return; + } + skb_reserve(skb, local->hw.extra_tx_headroom); + + mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); + memset(mgmt, 0, 24); + memcpy(mgmt->da, ifsta->bssid, ETH_ALEN); + memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN); + memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN); + mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT, + IEEE80211_STYPE_DISASSOC); + skb_put(skb, 2); + mgmt->u.disassoc.reason_code = cpu_to_le16(reason); + + ieee80211_sta_tx(dev, skb, 0); +} + + +static int ieee80211_privacy_mismatch(struct net_device *dev, + struct ieee80211_if_sta *ifsta) +{ + struct ieee80211_sta_bss *bss; + int res = 0; + + if (!ifsta || ifsta->mixed_cell || + ifsta->key_mgmt != IEEE80211_KEY_MGMT_NONE) + return 0; + + bss = ieee80211_rx_bss_get(dev, ifsta->bssid); + if (!bss) + return 0; + + if (ieee80211_sta_wep_configured(dev) != + !!(bss->capability & WLAN_CAPABILITY_PRIVACY)) + res = 1; + + ieee80211_rx_bss_put(dev, bss); + + return res; +} + + +static void ieee80211_associate(struct net_device *dev, + struct ieee80211_if_sta *ifsta) +{ + ifsta->assoc_tries++; + if (ifsta->assoc_tries > IEEE80211_ASSOC_MAX_TRIES) { + printk(KERN_DEBUG "%s: association with AP " MAC_FMT + " timed out\n", + dev->name, MAC_ARG(ifsta->bssid)); + ifsta->state = IEEE80211_DISABLED; + return; + } + + ifsta->state = IEEE80211_ASSOCIATE; + printk(KERN_DEBUG "%s: associate with AP " MAC_FMT "\n", + dev->name, MAC_ARG(ifsta->bssid)); + if (ieee80211_privacy_mismatch(dev, ifsta)) { + printk(KERN_DEBUG "%s: mismatch in privacy configuration and " + "mixed-cell disabled - abort association\n", dev->name); + ifsta->state = IEEE80211_DISABLED; + return; + } + + ieee80211_send_assoc(dev, ifsta); + + mod_timer(&ifsta->timer, jiffies + IEEE80211_ASSOC_TIMEOUT); +} + + +static void ieee80211_associated(struct net_device *dev, + struct ieee80211_if_sta *ifsta) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct sta_info *sta; + int disassoc; + + /* TODO: start monitoring current AP signal quality and number of + * missed beacons. Scan other channels every now and then and search + * for better APs. */ + /* TODO: remove expired BSSes */ + + ifsta->state = IEEE80211_ASSOCIATED; + + sta = sta_info_get(local, ifsta->bssid); + if (!sta) { + printk(KERN_DEBUG "%s: No STA entry for own AP " MAC_FMT "\n", + dev->name, MAC_ARG(ifsta->bssid)); + disassoc = 1; + } else { + disassoc = 0; + if (time_after(jiffies, + sta->last_rx + IEEE80211_MONITORING_INTERVAL)) { + if (ifsta->probereq_poll) { + printk(KERN_DEBUG "%s: No ProbeResp from " + "current AP " MAC_FMT " - assume out of " + "range\n", + dev->name, MAC_ARG(ifsta->bssid)); + disassoc = 1; + sta_info_free(sta, 0); + ifsta->probereq_poll = 0; + } else { + ieee80211_send_probe_req(dev, ifsta->bssid, + local->scan_ssid, + local->scan_ssid_len); + ifsta->probereq_poll = 1; + } + } else { + ifsta->probereq_poll = 0; + if (time_after(jiffies, ifsta->last_probe + + IEEE80211_PROBE_INTERVAL)) { + ifsta->last_probe = jiffies; + ieee80211_send_probe_req(dev, ifsta->bssid, + ifsta->ssid, + ifsta->ssid_len); + } + } + sta_info_put(sta); + } + if (disassoc) { + union iwreq_data wrqu; + memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); + mod_timer(&ifsta->timer, jiffies + + IEEE80211_MONITORING_INTERVAL + 30 * HZ); + } else { + mod_timer(&ifsta->timer, jiffies + + IEEE80211_MONITORING_INTERVAL); + } +} + + +static void ieee80211_send_probe_req(struct net_device *dev, u8 *dst, + u8 *ssid, size_t ssid_len) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_hw_mode *mode; + struct sk_buff *skb; + struct ieee80211_mgmt *mgmt; + u8 *pos, *supp_rates, *esupp_rates = NULL; + int i; + + skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200); + if (!skb) { + printk(KERN_DEBUG "%s: failed to allocate buffer for probe " + "request\n", dev->name); + return; + } + skb_reserve(skb, local->hw.extra_tx_headroom); + + mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); + memset(mgmt, 0, 24); + mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT, + IEEE80211_STYPE_PROBE_REQ); + memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN); + if (dst) { + memcpy(mgmt->da, dst, ETH_ALEN); + memcpy(mgmt->bssid, dst, ETH_ALEN); + } else { + memset(mgmt->da, 0xff, ETH_ALEN); + memset(mgmt->bssid, 0xff, ETH_ALEN); + } + pos = skb_put(skb, 2 + ssid_len); + *pos++ = WLAN_EID_SSID; + *pos++ = ssid_len; + memcpy(pos, ssid, ssid_len); + + supp_rates = skb_put(skb, 2); + supp_rates[0] = WLAN_EID_SUPP_RATES; + supp_rates[1] = 0; + mode = local->oper_hw_mode; + for (i = 0; i < mode->num_rates; i++) { + struct ieee80211_rate *rate = &mode->rates[i]; + if (!(rate->flags & IEEE80211_RATE_SUPPORTED)) + continue; + if (esupp_rates) { + pos = skb_put(skb, 1); + esupp_rates[1]++; + } else if (supp_rates[1] == 8) { + esupp_rates = skb_put(skb, 3); + esupp_rates[0] = WLAN_EID_EXT_SUPP_RATES; + esupp_rates[1] = 1; + pos = &esupp_rates[2]; + } else { + pos = skb_put(skb, 1); + supp_rates[1]++; + } + if (mode->mode == MODE_ATHEROS_TURBO) + *pos = rate->rate / 10; + else + *pos = rate->rate / 5; + } + + ieee80211_sta_tx(dev, skb, 0); +} + + +static int ieee80211_sta_wep_configured(struct net_device *dev) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + if (!sdata || !sdata->default_key || + sdata->default_key->alg != ALG_WEP) + return 0; + return 1; +} + + +static void ieee80211_auth_completed(struct net_device *dev, + struct ieee80211_if_sta *ifsta) +{ + printk(KERN_DEBUG "%s: authenticated\n", dev->name); + ifsta->authenticated = 1; + ieee80211_associate(dev, ifsta); +} + + +static void ieee80211_auth_challenge(struct net_device *dev, + struct ieee80211_if_sta *ifsta, + struct ieee80211_mgmt *mgmt, + size_t len) +{ + u8 *pos; + struct ieee802_11_elems elems; + + printk(KERN_DEBUG "%s: replying to auth challenge\n", dev->name); + pos = mgmt->u.auth.variable; + if (ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems) + == ParseFailed) { + printk(KERN_DEBUG "%s: failed to parse Auth(challenge)\n", + dev->name); + return; + } + if (!elems.challenge) { + printk(KERN_DEBUG "%s: no challenge IE in shared key auth " + "frame\n", dev->name); + return; + } + ieee80211_send_auth(dev, ifsta, 3, elems.challenge - 2, + elems.challenge_len + 2, 1); +} + + +static void ieee80211_rx_mgmt_auth(struct net_device *dev, + struct ieee80211_if_sta *ifsta, + struct ieee80211_mgmt *mgmt, + size_t len) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + u16 auth_alg, auth_transaction, status_code; + + if (ifsta->state != IEEE80211_AUTHENTICATE && + sdata->type != IEEE80211_IF_TYPE_IBSS) { + printk(KERN_DEBUG "%s: authentication frame received from " + MAC_FMT ", but not in authenticate state - ignored\n", + dev->name, MAC_ARG(mgmt->sa)); + return; + } + + if (len < 24 + 6) { + printk(KERN_DEBUG "%s: too short (%zd) authentication frame " + "received from " MAC_FMT " - ignored\n", + dev->name, len, MAC_ARG(mgmt->sa)); + return; + } + + if (sdata->type != IEEE80211_IF_TYPE_IBSS && + memcmp(ifsta->bssid, mgmt->sa, ETH_ALEN) != 0) { + printk(KERN_DEBUG "%s: authentication frame received from " + "unknown AP (SA=" MAC_FMT " BSSID=" MAC_FMT ") - " + "ignored\n", dev->name, MAC_ARG(mgmt->sa), + MAC_ARG(mgmt->bssid)); + return; + } + + if (sdata->type != IEEE80211_IF_TYPE_IBSS && + memcmp(ifsta->bssid, mgmt->bssid, ETH_ALEN) != 0) { + printk(KERN_DEBUG "%s: authentication frame received from " + "unknown BSSID (SA=" MAC_FMT " BSSID=" MAC_FMT ") - " + "ignored\n", dev->name, MAC_ARG(mgmt->sa), + MAC_ARG(mgmt->bssid)); + return; + } + + auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg); + auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction); + status_code = le16_to_cpu(mgmt->u.auth.status_code); + + printk(KERN_DEBUG "%s: RX authentication from " MAC_FMT " (alg=%d " + "transaction=%d status=%d)\n", + dev->name, MAC_ARG(mgmt->sa), auth_alg, + auth_transaction, status_code); + + if (sdata->type == IEEE80211_IF_TYPE_IBSS) { + /* IEEE 802.11 standard does not require authentication in IBSS + * networks and most implementations do not seem to use it. + * However, try to reply to authentication attempts if someone + * has actually implemented this. + * TODO: Could implement shared key authentication. */ + if (auth_alg != WLAN_AUTH_OPEN || auth_transaction != 1) { + printk(KERN_DEBUG "%s: unexpected IBSS authentication " + "frame (alg=%d transaction=%d)\n", + dev->name, auth_alg, auth_transaction); + return; + } + ieee80211_send_auth(dev, ifsta, 2, NULL, 0, 0); + } + + if (auth_alg != ifsta->auth_alg || + auth_transaction != ifsta->auth_transaction) { + printk(KERN_DEBUG "%s: unexpected authentication frame " + "(alg=%d transaction=%d)\n", + dev->name, auth_alg, auth_transaction); + return; + } + + if (status_code != WLAN_STATUS_SUCCESS) { + printk(KERN_DEBUG "%s: AP denied authentication (auth_alg=%d " + "code=%d)\n", dev->name, ifsta->auth_alg, status_code); + if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG) { + u8 algs[3]; + const int num_algs = ARRAY_SIZE(algs); + int i, pos; + algs[0] = algs[1] = algs[2] = 0xff; + if (ifsta->auth_algs & IEEE80211_AUTH_ALG_OPEN) + algs[0] = WLAN_AUTH_OPEN; + if (ifsta->auth_algs & IEEE80211_AUTH_ALG_SHARED_KEY) + algs[1] = WLAN_AUTH_SHARED_KEY; + if (ifsta->auth_algs & IEEE80211_AUTH_ALG_LEAP) + algs[2] = WLAN_AUTH_LEAP; + if (ifsta->auth_alg == WLAN_AUTH_OPEN) + pos = 0; + else if (ifsta->auth_alg == WLAN_AUTH_SHARED_KEY) + pos = 1; + else + pos = 2; + for (i = 0; i < num_algs; i++) { + pos++; + if (pos >= num_algs) + pos = 0; + if (algs[pos] == ifsta->auth_alg || + algs[pos] == 0xff) + continue; + if (algs[pos] == WLAN_AUTH_SHARED_KEY && + !ieee80211_sta_wep_configured(dev)) + continue; + ifsta->auth_alg = algs[pos]; + printk(KERN_DEBUG "%s: set auth_alg=%d for " + "next try\n", + dev->name, ifsta->auth_alg); + break; + } + } + return; + } + + switch (ifsta->auth_alg) { + case WLAN_AUTH_OPEN: + case WLAN_AUTH_LEAP: + ieee80211_auth_completed(dev, ifsta); + break; + case WLAN_AUTH_SHARED_KEY: + if (ifsta->auth_transaction == 4) + ieee80211_auth_completed(dev, ifsta); + else + ieee80211_auth_challenge(dev, ifsta, mgmt, len); + break; + } +} + + +static void ieee80211_rx_mgmt_deauth(struct net_device *dev, + struct ieee80211_if_sta *ifsta, + struct ieee80211_mgmt *mgmt, + size_t len) +{ + u16 reason_code; + + if (len < 24 + 2) { + printk(KERN_DEBUG "%s: too short (%zd) deauthentication frame " + "received from " MAC_FMT " - ignored\n", + dev->name, len, MAC_ARG(mgmt->sa)); + return; + } + + if (memcmp(ifsta->bssid, mgmt->sa, ETH_ALEN) != 0) { + printk(KERN_DEBUG "%s: deauthentication frame received from " + "unknown AP (SA=" MAC_FMT " BSSID=" MAC_FMT ") - " + "ignored\n", dev->name, MAC_ARG(mgmt->sa), + MAC_ARG(mgmt->bssid)); + return; + } + + reason_code = le16_to_cpu(mgmt->u.deauth.reason_code); + + printk(KERN_DEBUG "%s: RX deauthentication from " MAC_FMT + " (reason=%d)\n", + dev->name, MAC_ARG(mgmt->sa), reason_code); + + if (ifsta->authenticated) { + printk(KERN_DEBUG "%s: deauthenticated\n", dev->name); + } + + if (ifsta->state == IEEE80211_AUTHENTICATE || + ifsta->state == IEEE80211_ASSOCIATE || + ifsta->state == IEEE80211_ASSOCIATED) { + ifsta->state = IEEE80211_AUTHENTICATE; + mod_timer(&ifsta->timer, jiffies + + IEEE80211_RETRY_AUTH_INTERVAL); + } + + ieee80211_set_disassoc(dev, ifsta, 1); + ifsta->authenticated = 0; +} + + +static void ieee80211_rx_mgmt_disassoc(struct net_device *dev, + struct ieee80211_if_sta *ifsta, + struct ieee80211_mgmt *mgmt, + size_t len) +{ + u16 reason_code; + + if (len < 24 + 2) { + printk(KERN_DEBUG "%s: too short (%zd) disassociation frame " + "received from " MAC_FMT " - ignored\n", + dev->name, len, MAC_ARG(mgmt->sa)); + return; + } + + if (memcmp(ifsta->bssid, mgmt->sa, ETH_ALEN) != 0) { + printk(KERN_DEBUG "%s: disassociation frame received from " + "unknown AP (SA=" MAC_FMT " BSSID=" MAC_FMT ") - " + "ignored\n", dev->name, MAC_ARG(mgmt->sa), + MAC_ARG(mgmt->bssid)); + return; + } + + reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code); + + printk(KERN_DEBUG "%s: RX disassociation from " MAC_FMT + " (reason=%d)\n", + dev->name, MAC_ARG(mgmt->sa), reason_code); + + if (ifsta->associated) + printk(KERN_DEBUG "%s: disassociated\n", dev->name); + + if (ifsta->state == IEEE80211_ASSOCIATED) { + ifsta->state = IEEE80211_ASSOCIATE; + mod_timer(&ifsta->timer, jiffies + + IEEE80211_RETRY_AUTH_INTERVAL); + } + + ieee80211_set_disassoc(dev, ifsta, 0); +} + + +static void ieee80211_rx_mgmt_assoc_resp(struct net_device *dev, + struct ieee80211_if_sta *ifsta, + struct ieee80211_mgmt *mgmt, + size_t len, + int reassoc) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_hw_mode *mode; + struct sta_info *sta; + u32 rates; + u16 capab_info, status_code, aid; + struct ieee802_11_elems elems; + u8 *pos; + int i, j; + + /* AssocResp and ReassocResp have identical structure, so process both + * of them in this function. */ + + if (ifsta->state != IEEE80211_ASSOCIATE) { + printk(KERN_DEBUG "%s: association frame received from " + MAC_FMT ", but not in associate state - ignored\n", + dev->name, MAC_ARG(mgmt->sa)); + return; + } + + if (len < 24 + 6) { + printk(KERN_DEBUG "%s: too short (%zd) association frame " + "received from " MAC_FMT " - ignored\n", + dev->name, len, MAC_ARG(mgmt->sa)); + return; + } + + if (memcmp(ifsta->bssid, mgmt->sa, ETH_ALEN) != 0) { + printk(KERN_DEBUG "%s: association frame received from " + "unknown AP (SA=" MAC_FMT " BSSID=" MAC_FMT ") - " + "ignored\n", dev->name, MAC_ARG(mgmt->sa), + MAC_ARG(mgmt->bssid)); + return; + } + + capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info); + status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); + aid = le16_to_cpu(mgmt->u.assoc_resp.aid); + if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14))) + printk(KERN_DEBUG "%s: invalid aid value %d; bits 15:14 not " + "set\n", dev->name, aid); + aid &= ~(BIT(15) | BIT(14)); + + printk(KERN_DEBUG "%s: RX %sssocResp from " MAC_FMT " (capab=0x%x " + "status=%d aid=%d)\n", + dev->name, reassoc ? "Rea" : "A", MAC_ARG(mgmt->sa), + capab_info, status_code, aid); + + if (status_code != WLAN_STATUS_SUCCESS) { + printk(KERN_DEBUG "%s: AP denied association (code=%d)\n", + dev->name, status_code); + return; + } + + pos = mgmt->u.assoc_resp.variable; + if (ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems) + == ParseFailed) { + printk(KERN_DEBUG "%s: failed to parse AssocResp\n", + dev->name); + return; + } + + if (!elems.supp_rates) { + printk(KERN_DEBUG "%s: no SuppRates element in AssocResp\n", + dev->name); + return; + } + + printk(KERN_DEBUG "%s: associated\n", dev->name); + ifsta->aid = aid; + ifsta->ap_capab = capab_info; + + kfree(ifsta->assocresp_ies); + ifsta->assocresp_ies_len = len - (pos - (u8 *) mgmt); + ifsta->assocresp_ies = kmalloc(ifsta->assocresp_ies_len, GFP_ATOMIC); + if (ifsta->assocresp_ies) + memcpy(ifsta->assocresp_ies, pos, ifsta->assocresp_ies_len); + + ieee80211_set_associated(dev, ifsta, 1); + + /* Add STA entry for the AP */ + sta = sta_info_get(local, ifsta->bssid); + if (!sta) { + struct ieee80211_sta_bss *bss; + sta = sta_info_add(local, dev, ifsta->bssid, GFP_ATOMIC); + if (!sta) { + printk(KERN_DEBUG "%s: failed to add STA entry for the" + " AP\n", dev->name); + return; + } + bss = ieee80211_rx_bss_get(dev, ifsta->bssid); + if (bss) { + sta->last_rssi = bss->rssi; + sta->last_signal = bss->signal; + sta->last_noise = bss->noise; + ieee80211_rx_bss_put(dev, bss); + } + } + + sta->dev = dev; + sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC; + sta->assoc_ap = 1; + + rates = 0; + mode = local->oper_hw_mode; + for (i = 0; i < elems.supp_rates_len; i++) { + int rate = (elems.supp_rates[i] & 0x7f) * 5; + if (mode->mode == MODE_ATHEROS_TURBO) + rate *= 2; + for (j = 0; j < mode->num_rates; j++) + if (mode->rates[j].rate == rate) + rates |= BIT(j); + } + for (i = 0; i < elems.ext_supp_rates_len; i++) { + int rate = (elems.ext_supp_rates[i] & 0x7f) * 5; + if (mode->mode == MODE_ATHEROS_TURBO) + rate *= 2; + for (j = 0; j < mode->num_rates; j++) + if (mode->rates[j].rate == rate) + rates |= BIT(j); + } + sta->supp_rates = rates; + + if (elems.ht_extra_param && elems.ht_cap_param && elems.wmm_param && + ifsta->ht_enabled && local->ops->conf_ht){ + int rc; + + rc = local->ops->conf_ht(local_to_hw(local), + (struct ieee80211_ht_capability *) + elems.ht_cap_param, + (struct ieee80211_ht_additional_info *) + elems.ht_extra_param); + if (!rc) + sta->flags |= WLAN_STA_HT; + } + + + rate_control_rate_init(sta, local); + + if (elems.wmm_param && ifsta->wmm_enabled) { + sta->flags |= WLAN_STA_WME; + ieee80211_sta_wmm_params(dev, ifsta, elems.wmm_param, + elems.wmm_param_len); + } + + + sta_info_put(sta); + + ieee80211_associated(dev, ifsta); +} + + +/* Caller must hold local->sta_bss_lock */ +static void __ieee80211_rx_bss_hash_add(struct net_device *dev, + struct ieee80211_sta_bss *bss) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + bss->hnext = local->sta_bss_hash[STA_HASH(bss->bssid)]; + local->sta_bss_hash[STA_HASH(bss->bssid)] = bss; +} + + +/* Caller must hold local->sta_bss_lock */ +static void __ieee80211_rx_bss_hash_del(struct net_device *dev, + struct ieee80211_sta_bss *bss) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_sta_bss *b, *prev = NULL; + b = local->sta_bss_hash[STA_HASH(bss->bssid)]; + while (b) { + if (b == bss) { + if (!prev) + local->sta_bss_hash[STA_HASH(bss->bssid)] = + bss->hnext; + else + prev->hnext = bss->hnext; + break; + } + prev = b; + b = b->hnext; + } +} + + +static struct ieee80211_sta_bss * +ieee80211_rx_bss_add(struct net_device *dev, u8 *bssid) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_sta_bss *bss; + + bss = kmalloc(sizeof(*bss), GFP_ATOMIC); + if (!bss) + return NULL; + memset(bss, 0, sizeof(*bss)); + atomic_inc(&bss->users); + atomic_inc(&bss->users); + memcpy(bss->bssid, bssid, ETH_ALEN); + + spin_lock_bh(&local->sta_bss_lock); + /* TODO: order by RSSI? */ + list_add_tail(&bss->list, &local->sta_bss_list); + __ieee80211_rx_bss_hash_add(dev, bss); + spin_unlock_bh(&local->sta_bss_lock); + return bss; +} + + +static struct ieee80211_sta_bss * +ieee80211_rx_bss_get(struct net_device *dev, u8 *bssid) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_sta_bss *bss; + + spin_lock_bh(&local->sta_bss_lock); + bss = local->sta_bss_hash[STA_HASH(bssid)]; + while (bss) { + if (memcmp(bss->bssid, bssid, ETH_ALEN) == 0) { + atomic_inc(&bss->users); + break; + } + bss = bss->hnext; + } + spin_unlock_bh(&local->sta_bss_lock); + return bss; +} + + +static void ieee80211_rx_bss_free(struct ieee80211_sta_bss *bss) +{ + kfree(bss->wpa_ie); + kfree(bss->rsn_ie); + kfree(bss->wmm_ie); + kfree(bss->ht_ie); + kfree(bss); +} + + +static void ieee80211_rx_bss_put(struct net_device *dev, + struct ieee80211_sta_bss *bss) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + if (!atomic_dec_and_test(&bss->users)) + return; + + spin_lock_bh(&local->sta_bss_lock); + __ieee80211_rx_bss_hash_del(dev, bss); + list_del(&bss->list); + spin_unlock_bh(&local->sta_bss_lock); + ieee80211_rx_bss_free(bss); +} + + +void ieee80211_rx_bss_list_init(struct net_device *dev) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + spin_lock_init(&local->sta_bss_lock); + INIT_LIST_HEAD(&local->sta_bss_list); +} + + +void ieee80211_rx_bss_list_deinit(struct net_device *dev) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_sta_bss *bss, *tmp; + + list_for_each_entry_safe(bss, tmp, &local->sta_bss_list, list) + ieee80211_rx_bss_put(dev, bss); +} + + +static void ieee80211_rx_bss_info(struct net_device *dev, + struct ieee80211_mgmt *mgmt, + size_t len, + struct ieee80211_rx_status *rx_status, + int beacon) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee802_11_elems elems; + size_t baselen; + int channel, invalid = 0, clen; + struct ieee80211_sta_bss *bss; + struct sta_info *sta; + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + u64 timestamp; + + if (!beacon && memcmp(mgmt->da, dev->dev_addr, ETH_ALEN)) + return; /* ignore ProbeResp to foreign address */ + +#if 0 + printk(KERN_DEBUG "%s: RX %s from " MAC_FMT " to " MAC_FMT "\n", + dev->name, beacon ? "Beacon" : "Probe Response", + MAC_ARG(mgmt->sa), MAC_ARG(mgmt->da)); +#endif + + baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt; + if (baselen > len) + return; + + timestamp = le64_to_cpu(mgmt->u.beacon.timestamp); + + if (sdata->type == IEEE80211_IF_TYPE_IBSS && beacon && + memcmp(mgmt->bssid, sdata->u.sta.bssid, ETH_ALEN) == 0) { +#ifdef CONFIG_MAC80211_IBSS_DEBUG + static unsigned long last_tsf_debug = 0; + u64 tsf; + if (local->ops->get_tsf) + tsf = local->ops->get_tsf(local_to_hw(local)); + else + tsf = -1LLU; + if (time_after(jiffies, last_tsf_debug + 5 * HZ)) { + printk(KERN_DEBUG "RX beacon SA=" MAC_FMT " BSSID=" + MAC_FMT " TSF=0x%llx BCN=0x%llx diff=%lld " + "@%lu\n", + MAC_ARG(mgmt->sa), MAC_ARG(mgmt->bssid), + (unsigned long long)tsf, + (unsigned long long)timestamp, + (unsigned long long)(tsf - timestamp), + jiffies); + last_tsf_debug = jiffies; + } +#endif /* CONFIG_MAC80211_IBSS_DEBUG */ + } + + if (ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen, + &elems) == ParseFailed) + invalid = 1; + + if (sdata->type == IEEE80211_IF_TYPE_IBSS && elems.supp_rates && + memcmp(mgmt->bssid, sdata->u.sta.bssid, ETH_ALEN) == 0 && + (sta = sta_info_get(local, mgmt->sa))) { + struct ieee80211_hw_mode *mode; + struct ieee80211_rate *rates; + size_t num_rates; + u32 supp_rates, prev_rates; + int i, j; + + mode = local->sta_scanning ? + local->scan_hw_mode : local->oper_hw_mode; + rates = mode->rates; + num_rates = mode->num_rates; + + supp_rates = 0; + for (i = 0; i < elems.supp_rates_len + + elems.ext_supp_rates_len; i++) { + u8 rate = 0; + int own_rate; + if (i < elems.supp_rates_len) + rate = elems.supp_rates[i]; + else if (elems.ext_supp_rates) + rate = elems.ext_supp_rates + [i - elems.supp_rates_len]; + own_rate = 5 * (rate & 0x7f); + if (mode->mode == MODE_ATHEROS_TURBO) + own_rate *= 2; + for (j = 0; j < num_rates; j++) + if (rates[j].rate == own_rate) + supp_rates |= BIT(j); + } + + prev_rates = sta->supp_rates; + sta->supp_rates &= supp_rates; + if (sta->supp_rates == 0) { + /* No matching rates - this should not really happen. + * Make sure that at least one rate is marked + * supported to avoid issues with TX rate ctrl. */ + sta->supp_rates = sdata->u.sta.supp_rates_bits; + } + if (sta->supp_rates != prev_rates) { + printk(KERN_DEBUG "%s: updated supp_rates set for " + MAC_FMT " based on beacon info (0x%x & 0x%x -> " + "0x%x)\n", + dev->name, MAC_ARG(sta->addr), prev_rates, + supp_rates, sta->supp_rates); + } + sta_info_put(sta); + } + + if (!elems.ssid) + return; + + if (elems.ds_params && elems.ds_params_len == 1) + channel = elems.ds_params[0]; + else + channel = rx_status->channel; + + bss = ieee80211_rx_bss_get(dev, mgmt->bssid); + if (!bss) { + bss = ieee80211_rx_bss_add(dev, mgmt->bssid); + if (!bss) + return; + } else { +#if 0 + /* TODO: order by RSSI? */ + spin_lock_bh(&local->sta_bss_lock); + list_move_tail(&bss->list, &local->sta_bss_list); + spin_unlock_bh(&local->sta_bss_lock); +#endif + } + + if (bss->probe_resp && beacon) { + /* Do not allow beacon to override data from Probe Response. */ + ieee80211_rx_bss_put(dev, bss); + return; + } + + bss->beacon_int = le16_to_cpu(mgmt->u.beacon.beacon_int); + bss->capability = le16_to_cpu(mgmt->u.beacon.capab_info); + if (elems.ssid && elems.ssid_len <= IEEE80211_MAX_SSID_LEN) { + memcpy(bss->ssid, elems.ssid, elems.ssid_len); + bss->ssid_len = elems.ssid_len; + } + + bss->supp_rates_len = 0; + if (elems.supp_rates) { + clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len; + if (clen > elems.supp_rates_len) + clen = elems.supp_rates_len; + memcpy(&bss->supp_rates[bss->supp_rates_len], elems.supp_rates, + clen); + bss->supp_rates_len += clen; + } + if (elems.ext_supp_rates) { + clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len; + if (clen > elems.ext_supp_rates_len) + clen = elems.ext_supp_rates_len; + memcpy(&bss->supp_rates[bss->supp_rates_len], + elems.ext_supp_rates, clen); + bss->supp_rates_len += clen; + } + + if (elems.wpa && + (!bss->wpa_ie || bss->wpa_ie_len != elems.wpa_len || + memcmp(bss->wpa_ie, elems.wpa, elems.wpa_len))) { + kfree(bss->wpa_ie); + bss->wpa_ie = kmalloc(elems.wpa_len + 2, GFP_ATOMIC); + if (bss->wpa_ie) { + memcpy(bss->wpa_ie, elems.wpa - 2, elems.wpa_len + 2); + bss->wpa_ie_len = elems.wpa_len + 2; + } else + bss->wpa_ie_len = 0; + } else if (!elems.wpa && bss->wpa_ie) { + kfree(bss->wpa_ie); + bss->wpa_ie = NULL; + bss->wpa_ie_len = 0; + } + + if (elems.rsn && + (!bss->rsn_ie || bss->rsn_ie_len != elems.rsn_len || + memcmp(bss->rsn_ie, elems.rsn, elems.rsn_len))) { + kfree(bss->rsn_ie); + bss->rsn_ie = kmalloc(elems.rsn_len + 2, GFP_ATOMIC); + if (bss->rsn_ie) { + memcpy(bss->rsn_ie, elems.rsn - 2, elems.rsn_len + 2); + bss->rsn_ie_len = elems.rsn_len + 2; + } else + bss->rsn_ie_len = 0; + } else if (!elems.rsn && bss->rsn_ie) { + kfree(bss->rsn_ie); + bss->rsn_ie = NULL; + bss->rsn_ie_len = 0; + } + + if (elems.wmm_param && + (!bss->wmm_ie || bss->wmm_ie_len != elems.wmm_param_len || + memcmp(bss->wmm_ie, elems.wmm_param, elems.wmm_param_len))) { + kfree(bss->wmm_ie); + bss->wmm_ie = kmalloc(elems.wmm_param_len + 2, GFP_ATOMIC); + if (bss->wmm_ie) { + memcpy(bss->wmm_ie, elems.wmm_param - 2, + elems.wmm_param_len + 2); + bss->wmm_ie_len = elems.wmm_param_len + 2; + } else + bss->wmm_ie_len = 0; + } else if (!elems.wmm_param && bss->wmm_ie) { + kfree(bss->wmm_ie); + bss->wmm_ie = NULL; + bss->wmm_ie_len = 0; + } + + if (elems.ht_cap_param && + (!bss->ht_ie || bss->ht_ie_len != elems.ht_cap_param_len || + memcmp(bss->ht_ie, elems.ht_cap_param, elems.ht_cap_param_len))) { + if (bss->ht_ie) + kfree(bss->ht_ie); + bss->ht_ie = kmalloc(elems.ht_cap_param_len + 2, GFP_ATOMIC); + if (bss->ht_ie) { + memcpy(bss->ht_ie, elems.ht_cap_param - 2, + elems.ht_cap_param_len + 2); + bss->ht_ie_len = elems.ht_cap_param_len + 2; + } else + bss->ht_ie_len = 0; + } else if (!elems.ht_cap_param && bss->ht_ie) { + kfree(bss->ht_ie); + bss->ht_ie = NULL; + bss->ht_ie_len = 0; + } + + bss->hw_mode = rx_status->phymode; + bss->channel = channel; + bss->freq = rx_status->freq; + if (channel != rx_status->channel && + (bss->hw_mode == MODE_IEEE80211G || + bss->hw_mode == MODE_IEEE80211B) && + channel >= 1 && channel <= 14) { + static const int freq_list[] = { + 2412, 2417, 2422, 2427, 2432, 2437, 2442, + 2447, 2452, 2457, 2462, 2467, 2472, 2484 + }; + /* IEEE 802.11g/b mode can receive packets from neighboring + * channels, so map the channel into frequency. */ + bss->freq = freq_list[channel - 1]; + } + bss->timestamp = timestamp; + bss->last_update = jiffies; + bss->rssi = rx_status->ssi; + bss->signal = rx_status->signal; + bss->noise = rx_status->noise; + if (!beacon) + bss->probe_resp++; + ieee80211_rx_bss_put(dev, bss); +} + + +static void ieee80211_rx_mgmt_probe_resp(struct net_device *dev, + struct ieee80211_mgmt *mgmt, + size_t len, + struct ieee80211_rx_status *rx_status) +{ + ieee80211_rx_bss_info(dev, mgmt, len, rx_status, 0); +} + + +static void ieee80211_rx_mgmt_beacon(struct net_device *dev, + struct ieee80211_mgmt *mgmt, + size_t len, + struct ieee80211_rx_status *rx_status) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_sub_if_data *sdata; + struct ieee80211_if_sta *ifsta; + int use_protection; + size_t baselen; + struct ieee802_11_elems elems; + + ieee80211_rx_bss_info(dev, mgmt, len, rx_status, 1); + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + if (sdata->type != IEEE80211_IF_TYPE_STA) + return; + ifsta = &sdata->u.sta; + + if (!ifsta->associated || + memcmp(ifsta->bssid, mgmt->bssid, ETH_ALEN) != 0) + return; + + /* Process beacon from the current BSS */ + baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt; + if (baselen > len) + return; + + if (ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen, + &elems) == ParseFailed) + return; + + use_protection = 0; + if (elems.erp_info && elems.erp_info_len >= 1) { + use_protection = + (elems.erp_info[0] & ERP_INFO_USE_PROTECTION) != 0; + } + + if (use_protection != !!ifsta->use_protection) { + if (net_ratelimit()) { + printk(KERN_DEBUG "%s: CTS protection %s (BSSID=" + MAC_FMT ")\n", + dev->name, + use_protection ? "enabled" : "disabled", + MAC_ARG(ifsta->bssid)); + } + ifsta->use_protection = use_protection ? 1 : 0; + local->cts_protect_erp_frames = use_protection; + } + + if (elems.wmm_param && ifsta->wmm_enabled) { + ieee80211_sta_wmm_params(dev, ifsta, elems.wmm_param, + elems.wmm_param_len); + } +} + + +static void ieee80211_rx_mgmt_probe_req(struct net_device *dev, + struct ieee80211_if_sta *ifsta, + struct ieee80211_mgmt *mgmt, + size_t len, + struct ieee80211_rx_status *rx_status) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + int tx_last_beacon; + struct sk_buff *skb; + struct ieee80211_mgmt *resp; + u8 *pos, *end; + + if (sdata->type != IEEE80211_IF_TYPE_IBSS || + ifsta->state != IEEE80211_IBSS_JOINED || + len < 24 + 2 || !ifsta->probe_resp) + return; + + if (local->ops->tx_last_beacon) + tx_last_beacon = local->ops->tx_last_beacon(local_to_hw(local)); + else + tx_last_beacon = 1; + +#ifdef CONFIG_MAC80211_IBSS_DEBUG + printk(KERN_DEBUG "%s: RX ProbeReq SA=" MAC_FMT " DA=" MAC_FMT " BSSID=" + MAC_FMT " (tx_last_beacon=%d)\n", + dev->name, MAC_ARG(mgmt->sa), MAC_ARG(mgmt->da), + MAC_ARG(mgmt->bssid), tx_last_beacon); +#endif /* CONFIG_MAC80211_IBSS_DEBUG */ + + if (!tx_last_beacon) + return; + + if (memcmp(mgmt->bssid, ifsta->bssid, ETH_ALEN) != 0 && + memcmp(mgmt->bssid, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) != 0) + return; + + end = ((u8 *) mgmt) + len; + pos = mgmt->u.probe_req.variable; + if (pos[0] != WLAN_EID_SSID || + pos + 2 + pos[1] > end) { + if (net_ratelimit()) { + printk(KERN_DEBUG "%s: Invalid SSID IE in ProbeReq " + "from " MAC_FMT "\n", + dev->name, MAC_ARG(mgmt->sa)); + } + return; + } + if (pos[1] != 0 && + (pos[1] != ifsta->ssid_len || + memcmp(pos + 2, ifsta->ssid, ifsta->ssid_len) != 0)) { + /* Ignore ProbeReq for foreign SSID */ + return; + } + + /* Reply with ProbeResp */ + skb = skb_copy(ifsta->probe_resp, GFP_ATOMIC); + if (!skb) + return; + + resp = (struct ieee80211_mgmt *) skb->data; + memcpy(resp->da, mgmt->sa, ETH_ALEN); +#ifdef CONFIG_MAC80211_IBSS_DEBUG + printk(KERN_DEBUG "%s: Sending ProbeResp to " MAC_FMT "\n", + dev->name, MAC_ARG(resp->da)); +#endif /* CONFIG_MAC80211_IBSS_DEBUG */ + ieee80211_sta_tx(dev, skb, 0); +} + +static void ieee80211_send_addba_resp(struct net_device *dev, + struct ieee80211_mgmt *mgmt_src, + size_t len, + u16 status) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_if_sta *ifsta = &sdata->u.sta; + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct sk_buff *skb; + struct ieee80211_mgmt *mgmt; + + skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom); + if (!skb) { + printk(KERN_DEBUG "%s: failed to allocate buffer " + "for addts frame\n", dev->name); + return; + } + + skb_reserve(skb, local->hw.extra_tx_headroom); + mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); + memset(mgmt, 0, 24); + memcpy(mgmt->da, ifsta->bssid, ETH_ALEN); + memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN); + memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN); + mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT, + IEEE80211_STYPE_ACTION); + + skb_put(skb, 1 + sizeof(mgmt->u.action.u.addba_resp)); + mgmt->u.action.category = WLAN_CATEGORY_BACK; + mgmt->u.action.u.addba_resp.action_code = WLAN_ACTION_ADDBA_RESP; + mgmt->u.action.u.addba_resp.dialog_token = + mgmt_src->u.action.u.addba_req.dialog_token; + mgmt->u.action.u.addba_resp.capab = + mgmt_src->u.action.u.addba_req.capab; + mgmt->u.action.u.addba_resp.timeout = + mgmt_src->u.action.u.addba_req.timeout; + mgmt->u.action.u.addba_resp.status = cpu_to_le16(status); + + ieee80211_sta_tx(dev, skb, 0); + + return; +} + +static void ieee80211_rx_mgmt_action(struct net_device *dev, + struct ieee80211_if_sta *ifsta, + struct ieee80211_mgmt *mgmt, + size_t len) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + + if (len < IEEE80211_MIN_ACTION_SIZE) + return; + + switch (mgmt->u.action.category) { + case WLAN_CATEGORY_BACK: + switch (mgmt->u.action.u.addba_req.action_code) { + case WLAN_ACTION_ADDBA_REQ: + if (len < (IEEE80211_MIN_ACTION_SIZE + + sizeof(mgmt->u.action.u.addba_req))) + break; + if (!local->ops->handle_ba_action || + (local->ops->handle_ba_action(local_to_hw(local), + mgmt))) + ieee80211_send_addba_resp(dev, mgmt, len, + WLAN_STATUS_REQUEST_DECLINED); + else + ieee80211_send_addba_resp(dev, mgmt, len, + WLAN_STATUS_SUCCESS); + break; + case WLAN_ACTION_ADDBA_RESP: + if (len < (IEEE80211_MIN_ACTION_SIZE + + sizeof(mgmt->u.action.u.addba_resp))) + break; + if (!local->ops->handle_ba_action) + break; + local->ops->handle_ba_action(local_to_hw(local), mgmt); + break; + case WLAN_ACTION_DELBA: + if (len < (IEEE80211_MIN_ACTION_SIZE + + sizeof(mgmt->u.action.u.delba))) + break; + + if (!local->ops->handle_ba_action) + break; + + local->ops->handle_ba_action(local_to_hw(local), mgmt); + break; + default: + break; + } + break; + + default: + break; + } +} + +void ieee80211_sta_rx_mgmt(struct net_device *dev, struct sk_buff *skb, + struct ieee80211_rx_status *rx_status) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_sub_if_data *sdata; + struct ieee80211_if_sta *ifsta; + struct ieee80211_mgmt *mgmt; + u16 fc; + + if (skb->len < 24) + goto fail; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + ifsta = &sdata->u.sta; + + mgmt = (struct ieee80211_mgmt *) skb->data; + fc = le16_to_cpu(mgmt->frame_control); + + switch (fc & IEEE80211_FCTL_STYPE) { + case IEEE80211_STYPE_PROBE_REQ: + case IEEE80211_STYPE_PROBE_RESP: + case IEEE80211_STYPE_BEACON: + memcpy(skb->cb, rx_status, sizeof(*rx_status)); + case IEEE80211_STYPE_AUTH: + case IEEE80211_STYPE_ASSOC_RESP: + case IEEE80211_STYPE_REASSOC_RESP: + case IEEE80211_STYPE_DEAUTH: + case IEEE80211_STYPE_DISASSOC: + case IEEE80211_STYPE_ACTION: + skb_queue_tail(&ifsta->skb_queue, skb); + queue_work(local->hw.workqueue, &ifsta->work); + return; + default: + printk(KERN_DEBUG "%s: received unknown management frame - " + "stype=%d\n", dev->name, + (fc & IEEE80211_FCTL_STYPE) >> 4); + break; + } + + fail: + kfree_skb(skb); +} + + +static void ieee80211_sta_rx_queued_mgmt(struct net_device *dev, + struct sk_buff *skb) +{ + struct ieee80211_rx_status *rx_status; + struct ieee80211_sub_if_data *sdata; + struct ieee80211_if_sta *ifsta; + struct ieee80211_mgmt *mgmt; + u16 fc; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + ifsta = &sdata->u.sta; + + rx_status = (struct ieee80211_rx_status *) skb->cb; + mgmt = (struct ieee80211_mgmt *) skb->data; + fc = le16_to_cpu(mgmt->frame_control); + + switch (fc & IEEE80211_FCTL_STYPE) { + case IEEE80211_STYPE_PROBE_REQ: + ieee80211_rx_mgmt_probe_req(dev, ifsta, mgmt, skb->len, + rx_status); + break; + case IEEE80211_STYPE_PROBE_RESP: + ieee80211_rx_mgmt_probe_resp(dev, mgmt, skb->len, rx_status); + break; + case IEEE80211_STYPE_BEACON: + ieee80211_rx_mgmt_beacon(dev, mgmt, skb->len, rx_status); + break; + case IEEE80211_STYPE_AUTH: + ieee80211_rx_mgmt_auth(dev, ifsta, mgmt, skb->len); + break; + case IEEE80211_STYPE_ASSOC_RESP: + ieee80211_rx_mgmt_assoc_resp(dev, ifsta, mgmt, skb->len, 0); + break; + case IEEE80211_STYPE_REASSOC_RESP: + ieee80211_rx_mgmt_assoc_resp(dev, ifsta, mgmt, skb->len, 1); + break; + case IEEE80211_STYPE_DEAUTH: + ieee80211_rx_mgmt_deauth(dev, ifsta, mgmt, skb->len); + break; + case IEEE80211_STYPE_DISASSOC: + ieee80211_rx_mgmt_disassoc(dev, ifsta, mgmt, skb->len); + break; + case IEEE80211_STYPE_ACTION: + ieee80211_rx_mgmt_action(dev, ifsta, mgmt, skb->len); + break; + } + + kfree_skb(skb); +} + + +void ieee80211_sta_rx_scan(struct net_device *dev, struct sk_buff *skb, + struct ieee80211_rx_status *rx_status) +{ + struct ieee80211_mgmt *mgmt; + u16 fc; + + if (skb->len < 24) { + dev_kfree_skb(skb); + return; + } + + mgmt = (struct ieee80211_mgmt *) skb->data; + fc = le16_to_cpu(mgmt->frame_control); + + if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) { + if ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PROBE_RESP) { + ieee80211_rx_mgmt_probe_resp(dev, mgmt, + skb->len, rx_status); + } else if ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_BEACON) { + ieee80211_rx_mgmt_beacon(dev, mgmt, skb->len, + rx_status); + } + } + + dev_kfree_skb(skb); +} + + +static int ieee80211_sta_active_ibss(struct net_device *dev) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + int active = 0; + struct sta_info *sta; + + spin_lock_bh(&local->sta_lock); + list_for_each_entry(sta, &local->sta_list, list) { + if (sta->dev == dev && + time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL, + jiffies)) { + active++; + break; + } + } + spin_unlock_bh(&local->sta_lock); + + return active; +} + + +static void ieee80211_sta_expire(struct net_device *dev) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct sta_info *sta, *tmp; + + spin_lock_bh(&local->sta_lock); + list_for_each_entry_safe(sta, tmp, &local->sta_list, list) + if (time_after(jiffies, sta->last_rx + + IEEE80211_IBSS_INACTIVITY_LIMIT)) { + printk(KERN_DEBUG "%s: expiring inactive STA " MAC_FMT + "\n", dev->name, MAC_ARG(sta->addr)); + sta_info_free(sta, 1); + } + spin_unlock_bh(&local->sta_lock); +} + + +static void ieee80211_sta_merge_ibss(struct net_device *dev, + struct ieee80211_if_sta *ifsta) +{ + mod_timer(&ifsta->timer, jiffies + IEEE80211_IBSS_MERGE_INTERVAL); + + ieee80211_sta_expire(dev); + if (ieee80211_sta_active_ibss(dev)) + return; + + printk(KERN_DEBUG "%s: No active IBSS STAs - trying to scan for other " + "IBSS networks with same SSID (merge)\n", dev->name); + ieee80211_sta_req_scan(dev, ifsta->ssid, ifsta->ssid_len); +} + + +void ieee80211_sta_timer(unsigned long data) +{ + struct ieee80211_sub_if_data *sdata = + (struct ieee80211_sub_if_data *) data; + struct ieee80211_if_sta *ifsta = &sdata->u.sta; + struct ieee80211_local *local = wdev_priv(&sdata->wdev); + + set_bit(IEEE80211_STA_REQ_RUN, &ifsta->request); + queue_work(local->hw.workqueue, &ifsta->work); +} + + +void ieee80211_sta_work(struct work_struct *work) +{ + struct ieee80211_sub_if_data *sdata = + container_of(work, struct ieee80211_sub_if_data, u.sta.work); + struct net_device *dev = sdata->dev; + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_if_sta *ifsta; + struct sk_buff *skb; + + if (!netif_running(dev)) + return; + + if (local->sta_scanning) + return; + + if (sdata->type != IEEE80211_IF_TYPE_STA && + sdata->type != IEEE80211_IF_TYPE_IBSS) { + printk(KERN_DEBUG "%s: ieee80211_sta_work: non-STA interface " + "(type=%d)\n", dev->name, sdata->type); + return; + } + ifsta = &sdata->u.sta; + + while ((skb = skb_dequeue(&ifsta->skb_queue))) + ieee80211_sta_rx_queued_mgmt(dev, skb); + + if (ifsta->state != IEEE80211_AUTHENTICATE && + ifsta->state != IEEE80211_ASSOCIATE && + test_and_clear_bit(IEEE80211_STA_REQ_SCAN, &ifsta->request)) { + ieee80211_sta_start_scan(dev, NULL, 0); + return; + } + + if (test_and_clear_bit(IEEE80211_STA_REQ_AUTH, &ifsta->request)) { + if (ieee80211_sta_config_auth(dev, ifsta)) + return; + clear_bit(IEEE80211_STA_REQ_RUN, &ifsta->request); + } else if (!test_and_clear_bit(IEEE80211_STA_REQ_RUN, &ifsta->request)) + return; + + switch (ifsta->state) { + case IEEE80211_DISABLED: + break; + case IEEE80211_AUTHENTICATE: + ieee80211_authenticate(dev, ifsta); + break; + case IEEE80211_ASSOCIATE: + ieee80211_associate(dev, ifsta); + break; + case IEEE80211_ASSOCIATED: + ieee80211_associated(dev, ifsta); + break; + case IEEE80211_IBSS_SEARCH: + ieee80211_sta_find_ibss(dev, ifsta); + break; + case IEEE80211_IBSS_JOINED: + ieee80211_sta_merge_ibss(dev, ifsta); + break; + default: + printk(KERN_DEBUG "ieee80211_sta_work: Unknown state %d\n", + ifsta->state); + break; + } + + if (ieee80211_privacy_mismatch(dev, ifsta)) { + printk(KERN_DEBUG "%s: privacy configuration mismatch and " + "mixed-cell disabled - disassociate\n", dev->name); + + ieee80211_send_disassoc(dev, ifsta, WLAN_REASON_UNSPECIFIED); + ieee80211_set_disassoc(dev, ifsta, 0); + } +} + + +static void ieee80211_sta_reset_auth(struct net_device *dev, + struct ieee80211_if_sta *ifsta) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + + if (local->ops->reset_tsf) { + /* Reset own TSF to allow time synchronization work. */ + local->ops->reset_tsf(local_to_hw(local)); + } + + ifsta->wmm_last_param_set = -1; /* allow any WMM update */ + + + if (ifsta->auth_algs & IEEE80211_AUTH_ALG_OPEN) + ifsta->auth_alg = WLAN_AUTH_OPEN; + else if (ifsta->auth_algs & IEEE80211_AUTH_ALG_SHARED_KEY) + ifsta->auth_alg = WLAN_AUTH_SHARED_KEY; + else if (ifsta->auth_algs & IEEE80211_AUTH_ALG_LEAP) + ifsta->auth_alg = WLAN_AUTH_LEAP; + else + ifsta->auth_alg = WLAN_AUTH_OPEN; + printk(KERN_DEBUG "%s: Initial auth_alg=%d\n", dev->name, + ifsta->auth_alg); + ifsta->auth_transaction = -1; + ifsta->associated = ifsta->auth_tries = ifsta->assoc_tries = 0; + netif_carrier_off(dev); +} + + +void ieee80211_sta_req_auth(struct net_device *dev, + struct ieee80211_if_sta *ifsta) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + if (sdata->type != IEEE80211_IF_TYPE_STA) + return; + + if ((ifsta->bssid_set || ifsta->auto_bssid_sel) && + (ifsta->ssid_set || ifsta->auto_ssid_sel)) { + set_bit(IEEE80211_STA_REQ_AUTH, &ifsta->request); + queue_work(local->hw.workqueue, &ifsta->work); + } +} + +static int ieee80211_sta_match_ssid(struct ieee80211_if_sta *ifsta, + const char *ssid, int ssid_len) +{ + int tmp, hidden_ssid; + + if (!memcmp(ifsta->ssid, ssid, ssid_len)) + return 1; + + if (ifsta->auto_bssid_sel) + return 0; + + hidden_ssid = 1; + tmp = ssid_len; + while (tmp--) { + if (ssid[tmp] != '\0') { + hidden_ssid = 0; + break; + } + } + + if (hidden_ssid && ifsta->ssid_len == ssid_len) + return 1; + + if (ssid_len == 1 && ssid[0] == ' ') + return 1; + + return 0; +} + +static int ieee80211_sta_config_auth(struct net_device *dev, + struct ieee80211_if_sta *ifsta) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_sta_bss *bss, *selected = NULL; + int top_rssi = 0, freq; + + rtnl_lock(); + + if (!ifsta->auto_channel_sel && !ifsta->auto_bssid_sel && + !ifsta->auto_ssid_sel) { + ifsta->state = IEEE80211_AUTHENTICATE; + rtnl_unlock(); + ieee80211_sta_reset_auth(dev, ifsta); + return 0; + } + + spin_lock_bh(&local->sta_bss_lock); + freq = local->oper_channel->freq; + list_for_each_entry(bss, &local->sta_bss_list, list) { + if (!(bss->capability & WLAN_CAPABILITY_ESS)) + continue; + + if (!!(bss->capability & WLAN_CAPABILITY_PRIVACY) ^ + !!sdata->default_key) + continue; + + if (!ifsta->auto_channel_sel && bss->freq != freq) + continue; + + if (!ifsta->auto_bssid_sel && + memcmp(bss->bssid, ifsta->bssid, ETH_ALEN)) + continue; + + if (!ifsta->auto_ssid_sel && + !ieee80211_sta_match_ssid(ifsta, bss->ssid, bss->ssid_len)) + continue; + + if (!selected || top_rssi < bss->rssi) { + selected = bss; + top_rssi = bss->rssi; + } + } + if (selected) + atomic_inc(&selected->users); + spin_unlock_bh(&local->sta_bss_lock); + + if (selected) { + ieee80211_set_channel(local, -1, selected->freq); + if (!ifsta->ssid_set) + ieee80211_sta_set_ssid(dev, selected->ssid, + selected->ssid_len); + ieee80211_sta_set_bssid(dev, selected->bssid); + ieee80211_rx_bss_put(dev, selected); + ifsta->state = IEEE80211_AUTHENTICATE; + rtnl_unlock(); + ieee80211_sta_reset_auth(dev, ifsta); + return 0; + } else { + if (ifsta->state != IEEE80211_AUTHENTICATE) { + ieee80211_sta_start_scan(dev, NULL, 0); + ifsta->state = IEEE80211_AUTHENTICATE; + set_bit(IEEE80211_STA_REQ_AUTH, &ifsta->request); + } else + ifsta->state = IEEE80211_DISABLED; + } + rtnl_unlock(); + return -1; +} + +static int ieee80211_sta_join_ibss(struct net_device *dev, + struct ieee80211_if_sta *ifsta, + struct ieee80211_sta_bss *bss) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + int res, rates, i, j; + struct sk_buff *skb; + struct ieee80211_mgmt *mgmt; + struct ieee80211_tx_control control; + struct ieee80211_rate *rate; + struct ieee80211_hw_mode *mode; + struct rate_control_extra extra; + u8 *pos; + struct ieee80211_sub_if_data *sdata; + + /* Remove possible STA entries from other IBSS networks. */ + sta_info_flush(local, NULL); + + if (local->ops->reset_tsf) { + /* Reset own TSF to allow time synchronization work. */ + local->ops->reset_tsf(local_to_hw(local)); + } + memcpy(ifsta->bssid, bss->bssid, ETH_ALEN); + res = ieee80211_if_config(dev); + if (res) + return res; + + local->hw.conf.beacon_int = bss->beacon_int >= 10 ? bss->beacon_int : 10; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + sdata->drop_unencrypted = bss->capability & + WLAN_CAPABILITY_PRIVACY ? 1 : 0; + + res = ieee80211_set_channel(local, -1, bss->freq); + + if (!(local->oper_channel->flag & IEEE80211_CHAN_W_IBSS)) { + printk(KERN_DEBUG "%s: IBSS not allowed on channel %d " + "(%d MHz)\n", dev->name, local->hw.conf.channel, + local->hw.conf.freq); + return -1; + } + + /* Set beacon template based on scan results */ + skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400); + do { + if (!skb) + break; + + skb_reserve(skb, local->hw.extra_tx_headroom); + + mgmt = (struct ieee80211_mgmt *) + skb_put(skb, 24 + sizeof(mgmt->u.beacon)); + memset(mgmt, 0, 24 + sizeof(mgmt->u.beacon)); + mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT, + IEEE80211_STYPE_BEACON); + memset(mgmt->da, 0xff, ETH_ALEN); + memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN); + memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN); + mgmt->u.beacon.beacon_int = + cpu_to_le16(local->hw.conf.beacon_int); + mgmt->u.beacon.capab_info = cpu_to_le16(bss->capability); + + pos = skb_put(skb, 2 + ifsta->ssid_len); + *pos++ = WLAN_EID_SSID; + *pos++ = ifsta->ssid_len; + memcpy(pos, ifsta->ssid, ifsta->ssid_len); + + rates = bss->supp_rates_len; + if (rates > 8) + rates = 8; + pos = skb_put(skb, 2 + rates); + *pos++ = WLAN_EID_SUPP_RATES; + *pos++ = rates; + memcpy(pos, bss->supp_rates, rates); + + pos = skb_put(skb, 2 + 1); + *pos++ = WLAN_EID_DS_PARAMS; + *pos++ = 1; + *pos++ = bss->channel; + + pos = skb_put(skb, 2 + 2); + *pos++ = WLAN_EID_IBSS_PARAMS; + *pos++ = 2; + /* FIX: set ATIM window based on scan results */ + *pos++ = 0; + *pos++ = 0; + + if (bss->supp_rates_len > 8) { + rates = bss->supp_rates_len - 8; + pos = skb_put(skb, 2 + rates); + *pos++ = WLAN_EID_EXT_SUPP_RATES; + *pos++ = rates; + memcpy(pos, &bss->supp_rates[8], rates); + } + + memset(&control, 0, sizeof(control)); + memset(&extra, 0, sizeof(extra)); + extra.mode = local->oper_hw_mode; + rate = rate_control_get_rate(local, dev, skb, &extra); + if (!rate) { + printk(KERN_DEBUG "%s: Failed to determine TX rate " + "for IBSS beacon\n", dev->name); + break; + } + control.tx_rate = (local->short_preamble && + (rate->flags & IEEE80211_RATE_PREAMBLE2)) ? + rate->val2 : rate->val; + control.antenna_sel_tx = local->hw.conf.antenna_sel_tx; + control.power_level = local->hw.conf.power_level; + control.flags |= IEEE80211_TXCTL_NO_ACK; + control.retry_limit = 1; + + ifsta->probe_resp = skb_copy(skb, GFP_ATOMIC); + if (ifsta->probe_resp) { + mgmt = (struct ieee80211_mgmt *) + ifsta->probe_resp->data; + mgmt->frame_control = + IEEE80211_FC(IEEE80211_FTYPE_MGMT, + IEEE80211_STYPE_PROBE_RESP); + } else { + printk(KERN_DEBUG "%s: Could not allocate ProbeResp " + "template for IBSS\n", dev->name); + } + + if (local->ops->beacon_update && + local->ops->beacon_update(local_to_hw(local), + skb, &control) == 0) { + printk(KERN_DEBUG "%s: Configured IBSS beacon " + "template based on scan results\n", dev->name); + skb = NULL; + } + + rates = 0; + mode = local->oper_hw_mode; + for (i = 0; i < bss->supp_rates_len; i++) { + int bitrate = (bss->supp_rates[i] & 0x7f) * 5; + if (mode->mode == MODE_ATHEROS_TURBO) + bitrate *= 2; + for (j = 0; j < mode->num_rates; j++) + if (mode->rates[j].rate == bitrate) + rates |= BIT(j); + } + ifsta->supp_rates_bits = rates; + } while (0); + + if (skb) { + printk(KERN_DEBUG "%s: Failed to configure IBSS beacon " + "template\n", dev->name); + dev_kfree_skb(skb); + } + + ifsta->state = IEEE80211_IBSS_JOINED; + mod_timer(&ifsta->timer, jiffies + IEEE80211_IBSS_MERGE_INTERVAL); + + ieee80211_rx_bss_put(dev, bss); + + return res; +} + + +static int ieee80211_sta_create_ibss(struct net_device *dev, + struct ieee80211_if_sta *ifsta) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_sta_bss *bss; + struct ieee80211_sub_if_data *sdata; + struct ieee80211_hw_mode *mode; + u8 bssid[ETH_ALEN], *pos; + int i; + +#if 0 + /* Easier testing, use fixed BSSID. */ + memset(bssid, 0xfe, ETH_ALEN); +#else + /* Generate random, not broadcast, locally administered BSSID. Mix in + * own MAC address to make sure that devices that do not have proper + * random number generator get different BSSID. */ + get_random_bytes(bssid, ETH_ALEN); + for (i = 0; i < ETH_ALEN; i++) + bssid[i] ^= dev->dev_addr[i]; + bssid[0] &= ~0x01; + bssid[0] |= 0x02; +#endif + + printk(KERN_DEBUG "%s: Creating new IBSS network, BSSID " MAC_FMT "\n", + dev->name, MAC_ARG(bssid)); + + bss = ieee80211_rx_bss_add(dev, bssid); + if (!bss) + return -ENOMEM; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + mode = local->oper_hw_mode; + + if (local->hw.conf.beacon_int == 0) + local->hw.conf.beacon_int = 100; + bss->beacon_int = local->hw.conf.beacon_int; + bss->hw_mode = local->hw.conf.phymode; + bss->channel = local->hw.conf.channel; + bss->freq = local->hw.conf.freq; + bss->last_update = jiffies; + bss->capability = WLAN_CAPABILITY_IBSS; + if (sdata->default_key) { + bss->capability |= WLAN_CAPABILITY_PRIVACY; + } else + sdata->drop_unencrypted = 0; + bss->supp_rates_len = mode->num_rates; + pos = bss->supp_rates; + for (i = 0; i < mode->num_rates; i++) { + int rate = mode->rates[i].rate; + if (mode->mode == MODE_ATHEROS_TURBO) + rate /= 2; + *pos++ = (u8) (rate / 5); + } + + return ieee80211_sta_join_ibss(dev, ifsta, bss); +} + + +static int ieee80211_sta_find_ibss(struct net_device *dev, + struct ieee80211_if_sta *ifsta) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_sta_bss *bss; + int found = 0; + u8 bssid[ETH_ALEN]; + int active_ibss; + + if (ifsta->ssid_len == 0) + return -EINVAL; + + active_ibss = ieee80211_sta_active_ibss(dev); +#ifdef CONFIG_MAC80211_IBSS_DEBUG + printk(KERN_DEBUG "%s: sta_find_ibss (active_ibss=%d)\n", + dev->name, active_ibss); +#endif /* CONFIG_MAC80211_IBSS_DEBUG */ + spin_lock_bh(&local->sta_bss_lock); + list_for_each_entry(bss, &local->sta_bss_list, list) { + if (ifsta->ssid_len != bss->ssid_len || + memcmp(ifsta->ssid, bss->ssid, bss->ssid_len) != 0 + || !(bss->capability & WLAN_CAPABILITY_IBSS)) + continue; +#ifdef CONFIG_MAC80211_IBSS_DEBUG + printk(KERN_DEBUG " bssid=" MAC_FMT " found\n", + MAC_ARG(bss->bssid)); +#endif /* CONFIG_MAC80211_IBSS_DEBUG */ + memcpy(bssid, bss->bssid, ETH_ALEN); + found = 1; + if (active_ibss || memcmp(bssid, ifsta->bssid, ETH_ALEN) != 0) + break; + } + spin_unlock_bh(&local->sta_bss_lock); + +#ifdef CONFIG_MAC80211_IBSS_DEBUG + printk(KERN_DEBUG " sta_find_ibss: selected " MAC_FMT " current " + MAC_FMT "\n", MAC_ARG(bssid), MAC_ARG(ifsta->bssid)); +#endif /* CONFIG_MAC80211_IBSS_DEBUG */ + if (found && memcmp(ifsta->bssid, bssid, ETH_ALEN) != 0 && + (bss = ieee80211_rx_bss_get(dev, bssid))) { + printk(KERN_DEBUG "%s: Selected IBSS BSSID " MAC_FMT + " based on configured SSID\n", + dev->name, MAC_ARG(bssid)); + return ieee80211_sta_join_ibss(dev, ifsta, bss); + } +#ifdef CONFIG_MAC80211_IBSS_DEBUG + printk(KERN_DEBUG " did not try to join ibss\n"); +#endif /* CONFIG_MAC80211_IBSS_DEBUG */ + + /* Selected IBSS not found in current scan results - try to scan */ + if (ifsta->state == IEEE80211_IBSS_JOINED && + !ieee80211_sta_active_ibss(dev)) { + mod_timer(&ifsta->timer, jiffies + + IEEE80211_IBSS_MERGE_INTERVAL); + } else if (time_after(jiffies, local->last_scan_completed + + IEEE80211_SCAN_INTERVAL)) { + printk(KERN_DEBUG "%s: Trigger new scan to find an IBSS to " + "join\n", dev->name); + return ieee80211_sta_req_scan(dev, ifsta->ssid, + ifsta->ssid_len); + } else if (ifsta->state != IEEE80211_IBSS_JOINED) { + int interval = IEEE80211_SCAN_INTERVAL; + + if (time_after(jiffies, ifsta->ibss_join_req + + IEEE80211_IBSS_JOIN_TIMEOUT)) { + if (ifsta->create_ibss && + local->oper_channel->flag & IEEE80211_CHAN_W_IBSS) + return ieee80211_sta_create_ibss(dev, ifsta); + if (ifsta->create_ibss) { + printk(KERN_DEBUG "%s: IBSS not allowed on the" + " configured channel %d (%d MHz)\n", + dev->name, local->hw.conf.channel, + local->hw.conf.freq); + } + + /* No IBSS found - decrease scan interval and continue + * scanning. */ + interval = IEEE80211_SCAN_INTERVAL_SLOW; + } + + ifsta->state = IEEE80211_IBSS_SEARCH; + mod_timer(&ifsta->timer, jiffies + interval); + return 0; + } + + return 0; +} + + +int ieee80211_sta_set_ssid(struct net_device *dev, char *ssid, size_t len) +{ + struct ieee80211_sub_if_data *sdata; + struct ieee80211_if_sta *ifsta; + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + + if (len > IEEE80211_MAX_SSID_LEN) + return -EINVAL; + + /* TODO: This should always be done for IBSS, even if IEEE80211_QOS is + * not defined. */ + if (local->ops->conf_tx) { + struct ieee80211_tx_queue_params qparam; + int i; + + memset(&qparam, 0, sizeof(qparam)); + /* TODO: are these ok defaults for all hw_modes? */ + qparam.aifs = 2; + qparam.cw_min = + local->hw.conf.phymode == MODE_IEEE80211B ? 31 : 15; + qparam.cw_max = 1023; + qparam.burst_time = 0; + for (i = IEEE80211_TX_QUEUE_DATA0; i < NUM_TX_DATA_QUEUES; i++) + { + local->ops->conf_tx(local_to_hw(local), + i + IEEE80211_TX_QUEUE_DATA0, + &qparam); + } + /* IBSS uses different parameters for Beacon sending */ + qparam.cw_min++; + qparam.cw_min *= 2; + qparam.cw_min--; + local->ops->conf_tx(local_to_hw(local), + IEEE80211_TX_QUEUE_BEACON, &qparam); + } + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + ifsta = &sdata->u.sta; + + if (ifsta->ssid_len != len || memcmp(ifsta->ssid, ssid, len) != 0) + ifsta->prev_bssid_set = 0; + memcpy(ifsta->ssid, ssid, len); + memset(ifsta->ssid + len, 0, IEEE80211_MAX_SSID_LEN - len); + ifsta->ssid_len = len; + + ifsta->ssid_set = len ? 1 : 0; + if (sdata->type == IEEE80211_IF_TYPE_IBSS && !ifsta->bssid_set) { + ifsta->ibss_join_req = jiffies; + ifsta->state = IEEE80211_IBSS_SEARCH; + return ieee80211_sta_find_ibss(dev, ifsta); + } + return 0; +} + + +int ieee80211_sta_get_ssid(struct net_device *dev, char *ssid, size_t *len) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_if_sta *ifsta = &sdata->u.sta; + memcpy(ssid, ifsta->ssid, ifsta->ssid_len); + *len = ifsta->ssid_len; + return 0; +} + + +int ieee80211_sta_set_bssid(struct net_device *dev, u8 *bssid) +{ + struct ieee80211_sub_if_data *sdata; + struct ieee80211_if_sta *ifsta; + int res; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + ifsta = &sdata->u.sta; + + if (memcmp(ifsta->bssid, bssid, ETH_ALEN) != 0) { + memcpy(ifsta->bssid, bssid, ETH_ALEN); + res = ieee80211_if_config(dev); + if (res) { + printk(KERN_DEBUG "%s: Failed to config new BSSID to " + "the low-level driver\n", dev->name); + return res; + } + } + + if (!is_valid_ether_addr(bssid)) + ifsta->bssid_set = 0; + else + ifsta->bssid_set = 1; + return 0; +} + + +static void ieee80211_send_nullfunc(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + int powersave) +{ + struct sk_buff *skb; + struct ieee80211_hdr *nullfunc; + u16 fc; + + skb = dev_alloc_skb(local->hw.extra_tx_headroom + 24); + if (!skb) { + printk(KERN_DEBUG "%s: failed to allocate buffer for nullfunc " + "frame\n", sdata->dev->name); + return; + } + skb_reserve(skb, local->hw.extra_tx_headroom); + + nullfunc = (struct ieee80211_hdr *) skb_put(skb, 24); + memset(nullfunc, 0, 24); + fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC | + IEEE80211_FCTL_TODS; + if (powersave) + fc |= IEEE80211_FCTL_PM; + nullfunc->frame_control = cpu_to_le16(fc); + memcpy(nullfunc->addr1, sdata->u.sta.bssid, ETH_ALEN); + memcpy(nullfunc->addr2, sdata->dev->dev_addr, ETH_ALEN); + memcpy(nullfunc->addr3, sdata->u.sta.bssid, ETH_ALEN); + + ieee80211_sta_tx(sdata->dev, skb, 0); +} + + +void ieee80211_scan_completed(struct ieee80211_hw *hw) +{ + struct ieee80211_local *local = hw_to_local(hw); + struct net_device *dev = local->scan_dev; + struct ieee80211_sub_if_data *sdata; + union iwreq_data wrqu; + + local->last_scan_completed = jiffies; + wmb(); + local->sta_scanning = 0; + + if (ieee80211_hw_config(local)) + printk(KERN_DEBUG "%s: failed to restore operational" + "channel after scan\n", dev->name); + + if (!(local->hw.flags & IEEE80211_HW_NO_PROBE_FILTERING) && + ieee80211_if_config(dev)) + printk(KERN_DEBUG "%s: failed to restore operational" + "BSSID after scan\n", dev->name); + + memset(&wrqu, 0, sizeof(wrqu)); + wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL); + + read_lock(&local->sub_if_lock); + list_for_each_entry(sdata, &local->sub_if_list, list) { + if (sdata->type == IEEE80211_IF_TYPE_STA) { + if (sdata->u.sta.associated) + ieee80211_send_nullfunc(local, sdata, 0); + ieee80211_sta_timer((unsigned long)sdata); + } + netif_wake_queue(sdata->dev); + } + read_unlock(&local->sub_if_lock); + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + if (sdata->type == IEEE80211_IF_TYPE_IBSS) { + struct ieee80211_if_sta *ifsta = &sdata->u.sta; + if (!ifsta->bssid_set || + (!ifsta->state == IEEE80211_IBSS_JOINED && + !ieee80211_sta_active_ibss(dev))) + ieee80211_sta_find_ibss(dev, ifsta); + } +} +EXPORT_SYMBOL(ieee80211_scan_completed); + +void ieee80211_sta_scan_work(struct work_struct *work) +{ + struct ieee80211_local *local = + container_of(work, struct ieee80211_local, scan_work.work); + struct net_device *dev = local->scan_dev; + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_hw_mode *mode; + struct ieee80211_channel *chan; + int skip; + unsigned long next_delay = 0; + + if (!local->sta_scanning) + return; + + switch (local->scan_state) { + case SCAN_SET_CHANNEL: + mode = local->scan_hw_mode; + if (local->scan_hw_mode->list.next == &local->modes_list && + local->scan_channel_idx >= mode->num_channels) { + ieee80211_scan_completed(local_to_hw(local)); + return; + } + skip = !(local->enabled_modes & (1 << mode->mode)); + chan = &mode->channels[local->scan_channel_idx]; + if (!(chan->flag & IEEE80211_CHAN_W_SCAN) || + (sdata->type == IEEE80211_IF_TYPE_IBSS && + !(chan->flag & IEEE80211_CHAN_W_IBSS)) || + (local->hw_modes & local->enabled_modes & + (1 << MODE_IEEE80211G) && mode->mode == MODE_IEEE80211B)) + skip = 1; + + if (!skip) { +#if 0 + printk(KERN_DEBUG "%s: scan channel %d (%d MHz)\n", + dev->name, chan->chan, chan->freq); +#endif + + local->scan_channel = chan; + if (ieee80211_hw_config(local)) { + printk(KERN_DEBUG "%s: failed to set channel " + "%d (%d MHz) for scan\n", dev->name, + chan->chan, chan->freq); + skip = 1; + } + } + + local->scan_channel_idx++; + if (local->scan_channel_idx >= local->scan_hw_mode->num_channels) { + if (local->scan_hw_mode->list.next != &local->modes_list) { + local->scan_hw_mode = list_entry(local->scan_hw_mode->list.next, + struct ieee80211_hw_mode, + list); + local->scan_channel_idx = 0; + } + } + + if (skip) + break; + + next_delay = IEEE80211_PROBE_DELAY + + usecs_to_jiffies(local->hw.channel_change_time); + local->scan_state = SCAN_SEND_PROBE; + break; + case SCAN_SEND_PROBE: + if (local->scan_channel->flag & IEEE80211_CHAN_W_ACTIVE_SCAN) { + ieee80211_send_probe_req(dev, NULL, local->scan_ssid, + local->scan_ssid_len); + next_delay = IEEE80211_CHANNEL_TIME; + } else + next_delay = IEEE80211_PASSIVE_CHANNEL_TIME; + local->scan_state = SCAN_SET_CHANNEL; + break; + } + + if (local->sta_scanning) + queue_delayed_work(local->hw.workqueue, &local->scan_work, + next_delay); +} + + +static int ieee80211_sta_start_scan(struct net_device *dev, + u8 *ssid, size_t ssid_len) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_sub_if_data *sdata; + + if (ssid_len > IEEE80211_MAX_SSID_LEN) + return -EINVAL; + + /* MLME-SCAN.request (page 118) page 144 (11.1.3.1) + * BSSType: INFRASTRUCTURE, INDEPENDENT, ANY_BSS + * BSSID: MACAddress + * SSID + * ScanType: ACTIVE, PASSIVE + * ProbeDelay: delay (in microseconds) to be used prior to transmitting + * a Probe frame during active scanning + * ChannelList + * MinChannelTime (>= ProbeDelay), in TU + * MaxChannelTime: (>= MinChannelTime), in TU + */ + + /* MLME-SCAN.confirm + * BSSDescriptionSet + * ResultCode: SUCCESS, INVALID_PARAMETERS + */ + + if (local->sta_scanning) { + if (local->scan_dev == dev) + return 0; + return -EBUSY; + } + + if (local->ops->hw_scan) { + int rc = local->ops->hw_scan(local_to_hw(local), + ssid, ssid_len); + if (!rc) { + local->sta_scanning = 1; + local->scan_dev = dev; + } + return rc; + } + + local->sta_scanning = 1; + + read_lock(&local->sub_if_lock); + list_for_each_entry(sdata, &local->sub_if_list, list) { + netif_stop_queue(sdata->dev); + if (sdata->type == IEEE80211_IF_TYPE_STA && + sdata->u.sta.associated) + ieee80211_send_nullfunc(local, sdata, 1); + } + read_unlock(&local->sub_if_lock); + + if (ssid) { + local->scan_ssid_len = ssid_len; + memcpy(local->scan_ssid, ssid, ssid_len); + } else + local->scan_ssid_len = 0; + local->scan_state = SCAN_SET_CHANNEL; + local->scan_hw_mode = list_entry(local->modes_list.next, + struct ieee80211_hw_mode, + list); + local->scan_channel_idx = 0; + local->scan_dev = dev; + + if (!(local->hw.flags & IEEE80211_HW_NO_PROBE_FILTERING) && + ieee80211_if_config(dev)) + printk(KERN_DEBUG "%s: failed to set BSSID for scan\n", + dev->name); + + /* TODO: start scan as soon as all nullfunc frames are ACKed */ + queue_delayed_work(local->hw.workqueue, &local->scan_work, + IEEE80211_CHANNEL_TIME); + + return 0; +} + + +int ieee80211_sta_req_scan(struct net_device *dev, u8 *ssid, size_t ssid_len) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_if_sta *ifsta = &sdata->u.sta; + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + + if (sdata->type != IEEE80211_IF_TYPE_STA) + return ieee80211_sta_start_scan(dev, ssid, ssid_len); + + if (local->sta_scanning) { + if (local->scan_dev == dev) + return 0; + return -EBUSY; + } + + set_bit(IEEE80211_STA_REQ_SCAN, &ifsta->request); + queue_work(local->hw.workqueue, &ifsta->work); + return 0; +} + +static char * +ieee80211_sta_scan_result(struct net_device *dev, + struct ieee80211_sta_bss *bss, + char *current_ev, char *end_buf) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct iw_event iwe; + + if (time_after(jiffies, + bss->last_update + IEEE80211_SCAN_RESULT_EXPIRE)) + return current_ev; + + if (!(local->enabled_modes & (1 << bss->hw_mode))) + return current_ev; + + if (local->scan_flags & IEEE80211_SCAN_WPA_ONLY && + !bss->wpa_ie && !bss->rsn_ie) + return current_ev; + + if (local->scan_flags & IEEE80211_SCAN_MATCH_SSID && + (local->scan_ssid_len != bss->ssid_len || + memcmp(local->scan_ssid, bss->ssid, bss->ssid_len) != 0)) + return current_ev; + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN); + current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + IW_EV_ADDR_LEN); + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWESSID; + iwe.u.data.length = bss->ssid_len; + iwe.u.data.flags = 1; + current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, + bss->ssid); + + if (bss->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) { + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWMODE; + if (bss->capability & WLAN_CAPABILITY_ESS) + iwe.u.mode = IW_MODE_MASTER; + else + iwe.u.mode = IW_MODE_ADHOC; + current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + IW_EV_UINT_LEN); + } + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = bss->channel; + iwe.u.freq.e = 0; + current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + IW_EV_FREQ_LEN); + iwe.u.freq.m = bss->freq * 100000; + iwe.u.freq.e = 1; + current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + IW_EV_FREQ_LEN); + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVQUAL; + iwe.u.qual.qual = bss->signal; + iwe.u.qual.level = bss->rssi; + iwe.u.qual.noise = bss->noise; + iwe.u.qual.updated = local->wstats_flags; + current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + IW_EV_QUAL_LEN); + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWENCODE; + if (bss->capability & 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; + current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, ""); + + if (bss && bss->wpa_ie) { + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVGENIE; + iwe.u.data.length = bss->wpa_ie_len; + current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, + bss->wpa_ie); + } + + if (bss && bss->rsn_ie) { + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVGENIE; + iwe.u.data.length = bss->rsn_ie_len; + current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, + bss->rsn_ie); + } + + if (bss && bss->supp_rates_len > 0) { + /* display all supported rates in readable format */ + char *p = current_ev + IW_EV_LCP_LEN; + int i; + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWRATE; + /* Those two flags are ignored... */ + iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; + + for (i = 0; i < bss->supp_rates_len; i++) { + iwe.u.bitrate.value = ((bss->supp_rates[i] & + 0x7f) * 500000); + p = iwe_stream_add_value(current_ev, p, + end_buf, &iwe, IW_EV_PARAM_LEN); + } + current_ev = p; + } + + if (bss) { + char *buf; + buf = kmalloc(30, GFP_ATOMIC); + if (buf) { + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + sprintf(buf, "tsf=%016llx", (unsigned long long)(bss->timestamp)); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(current_ev, end_buf, + &iwe, buf); + kfree(buf); + } + } + + do { + char *buf; + + if (!(local->scan_flags & IEEE80211_SCAN_EXTRA_INFO)) + break; + + buf = kmalloc(100, GFP_ATOMIC); + if (!buf) + break; + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + sprintf(buf, "bcn_int=%d", bss->beacon_int); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, + buf); + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + sprintf(buf, "capab=0x%04x", bss->capability); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, + buf); + + kfree(buf); + break; + } while (0); + + return current_ev; +} + + +int ieee80211_sta_scan_results(struct net_device *dev, char *buf, size_t len) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + char *current_ev = buf; + char *end_buf = buf + len; + struct ieee80211_sta_bss *bss; + + spin_lock_bh(&local->sta_bss_lock); + list_for_each_entry(bss, &local->sta_bss_list, list) { + if (buf + len - current_ev <= IW_EV_ADDR_LEN) { + spin_unlock_bh(&local->sta_bss_lock); + return -E2BIG; + } + current_ev = ieee80211_sta_scan_result(dev, bss, current_ev, + end_buf); + } + spin_unlock_bh(&local->sta_bss_lock); + return current_ev - buf; +} + + +int ieee80211_sta_set_extra_ie(struct net_device *dev, char *ie, size_t len) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_if_sta *ifsta = &sdata->u.sta; + kfree(ifsta->extra_ie); + if (len == 0) { + ifsta->extra_ie = NULL; + ifsta->extra_ie_len = 0; + return 0; + } + ifsta->extra_ie = kmalloc(len, GFP_KERNEL); + if (!ifsta->extra_ie) { + ifsta->extra_ie_len = 0; + return -ENOMEM; + } + memcpy(ifsta->extra_ie, ie, len); + ifsta->extra_ie_len = len; + return 0; +} + + +struct sta_info * ieee80211_ibss_add_sta(struct net_device *dev, + struct sk_buff *skb, u8 *bssid, + u8 *addr) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct sta_info *sta; + struct ieee80211_sub_if_data *sdata = NULL; + + /* TODO: Could consider removing the least recently used entry and + * allow new one to be added. */ + if (local->num_sta >= IEEE80211_IBSS_MAX_STA_ENTRIES) { + if (net_ratelimit()) { + printk(KERN_DEBUG "%s: No room for a new IBSS STA " + "entry " MAC_FMT "\n", dev->name, MAC_ARG(addr)); + } + return NULL; + } + + printk(KERN_DEBUG "%s: Adding new IBSS station " MAC_FMT " (dev=%s)\n", + local->mdev->name, MAC_ARG(addr), dev->name); + + sta = sta_info_add(local, dev, addr, GFP_ATOMIC); + if (!sta) + return NULL; + + sta->supp_rates = sdata->u.sta.supp_rates_bits; + + rate_control_rate_init(sta, local); + + return sta; /* caller will call sta_info_put() */ +} + + +int ieee80211_sta_deauthenticate(struct net_device *dev, u16 reason) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_if_sta *ifsta = &sdata->u.sta; + + printk(KERN_DEBUG "%s: deauthenticate(reason=%d)\n", + dev->name, reason); + + if (sdata->type != IEEE80211_IF_TYPE_STA && + sdata->type != IEEE80211_IF_TYPE_IBSS) + return -EINVAL; + + ieee80211_send_deauth(dev, ifsta, reason); + ieee80211_set_disassoc(dev, ifsta, 1); + return 0; +} + + +int ieee80211_sta_disassociate(struct net_device *dev, u16 reason) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_if_sta *ifsta = &sdata->u.sta; + + printk(KERN_DEBUG "%s: disassociate(reason=%d)\n", + dev->name, reason); + + if (sdata->type != IEEE80211_IF_TYPE_STA) + return -EINVAL; + + if (!ifsta->associated) + return -1; + + ieee80211_send_disassoc(dev, ifsta, reason); + ieee80211_set_disassoc(dev, ifsta, 0); + return 0; +} diff --git a/net/mac80211/michael.c b/net/mac80211/michael.c new file mode 100644 index 0000000..0f844f7 --- /dev/null +++ b/net/mac80211/michael.c @@ -0,0 +1,104 @@ +/* + * Michael MIC implementation - optimized for TKIP MIC operations + * Copyright 2002-2003, Instant802 Networks, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include + +#include "michael.h" + +static inline u32 rotr(u32 val, int bits) +{ + return (val >> bits) | (val << (32 - bits)); +} + + +static inline u32 rotl(u32 val, int bits) +{ + return (val << bits) | (val >> (32 - bits)); +} + + +static inline u32 xswap(u32 val) +{ + return ((val & 0xff00ff00) >> 8) | ((val & 0x00ff00ff) << 8); +} + + +#define michael_block(l, r) \ +do { \ + r ^= rotl(l, 17); \ + l += r; \ + r ^= xswap(l); \ + l += r; \ + r ^= rotl(l, 3); \ + l += r; \ + r ^= rotr(l, 2); \ + l += r; \ +} while (0) + + +static inline u32 michael_get32(u8 *data) +{ + return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); +} + + +static inline void michael_put32(u32 val, u8 *data) +{ + data[0] = val & 0xff; + data[1] = (val >> 8) & 0xff; + data[2] = (val >> 16) & 0xff; + data[3] = (val >> 24) & 0xff; +} + + +void michael_mic(u8 *key, u8 *da, u8 *sa, u8 priority, + u8 *data, size_t data_len, u8 *mic) +{ + u32 l, r, val; + size_t block, blocks, left; + + l = michael_get32(key); + r = michael_get32(key + 4); + + /* A pseudo header (DA, SA, Priority, 0, 0, 0) is used in Michael MIC + * calculation, but it is _not_ transmitted */ + l ^= michael_get32(da); + michael_block(l, r); + l ^= da[4] | (da[5] << 8) | (sa[0] << 16) | (sa[1] << 24); + michael_block(l, r); + l ^= michael_get32(&sa[2]); + michael_block(l, r); + l ^= priority; + michael_block(l, r); + + /* Real data */ + blocks = data_len / 4; + left = data_len % 4; + + for (block = 0; block < blocks; block++) { + l ^= michael_get32(&data[block * 4]); + michael_block(l, r); + } + + /* Partial block of 0..3 bytes and padding: 0x5a + 4..7 zeros to make + * total length a multiple of 4. */ + val = 0x5a; + while (left > 0) { + val <<= 8; + left--; + val |= data[blocks * 4 + left]; + } + l ^= val; + michael_block(l, r); + /* last block is zero, so l ^ 0 = l */ + michael_block(l, r); + + michael_put32(l, mic); + michael_put32(r, mic + 4); +} diff --git a/net/mac80211/michael.h b/net/mac80211/michael.h new file mode 100644 index 0000000..2e6aeba --- /dev/null +++ b/net/mac80211/michael.h @@ -0,0 +1,20 @@ +/* + * Michael MIC implementation - optimized for TKIP MIC operations + * Copyright 2002-2003, Instant802 Networks, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef MICHAEL_H +#define MICHAEL_H + +#include + +#define MICHAEL_MIC_LEN 8 + +void michael_mic(u8 *key, u8 *da, u8 *sa, u8 priority, + u8 *data, size_t data_len, u8 *mic); + +#endif /* MICHAEL_H */ diff --git a/net/mac80211/rc80211_simple.c b/net/mac80211/rc80211_simple.c new file mode 100644 index 0000000..2048cfd --- /dev/null +++ b/net/mac80211/rc80211_simple.c @@ -0,0 +1,432 @@ +/* + * Copyright 2002-2005, Instant802 Networks, Inc. + * Copyright 2005, Devicescape Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "ieee80211_i.h" +#include "ieee80211_rate.h" +#include "debugfs.h" + + +/* This is a minimal implementation of TX rate controlling that can be used + * as the default when no improved mechanisms are available. */ + + +#define RATE_CONTROL_EMERG_DEC 2 +#define RATE_CONTROL_INTERVAL (HZ / 20) +#define RATE_CONTROL_MIN_TX 10 + +MODULE_ALIAS("rc80211_default"); + +static void rate_control_rate_inc(struct ieee80211_local *local, + struct sta_info *sta) +{ + struct ieee80211_sub_if_data *sdata; + struct ieee80211_hw_mode *mode; + int i = sta->txrate; + int maxrate; + + sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); + if (sdata->bss && sdata->bss->force_unicast_rateidx > -1) { + /* forced unicast rate - do not change STA rate */ + return; + } + + mode = local->oper_hw_mode; + maxrate = sdata->bss ? sdata->bss->max_ratectrl_rateidx : -1; + + if (i > mode->num_rates) + i = mode->num_rates - 2; + + while (i + 1 < mode->num_rates) { + i++; + if (sta->supp_rates & BIT(i) && + mode->rates[i].flags & IEEE80211_RATE_SUPPORTED && + (maxrate < 0 || i <= maxrate)) { + sta->txrate = i; + break; + } + } +} + + +static void rate_control_rate_dec(struct ieee80211_local *local, + struct sta_info *sta) +{ + struct ieee80211_sub_if_data *sdata; + struct ieee80211_hw_mode *mode; + int i = sta->txrate; + + sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); + if (sdata->bss && sdata->bss->force_unicast_rateidx > -1) { + /* forced unicast rate - do not change STA rate */ + return; + } + + mode = local->oper_hw_mode; + if (i > mode->num_rates) + i = mode->num_rates; + + while (i > 0) { + i--; + if (sta->supp_rates & BIT(i) && + mode->rates[i].flags & IEEE80211_RATE_SUPPORTED) { + sta->txrate = i; + break; + } + } +} + + +static struct ieee80211_rate * +rate_control_lowest_rate(struct ieee80211_local *local, + struct ieee80211_hw_mode *mode) +{ + int i; + + for (i = 0; i < mode->num_rates; i++) { + struct ieee80211_rate *rate = &mode->rates[i]; + + if (rate->flags & IEEE80211_RATE_SUPPORTED) + return rate; + } + + printk(KERN_DEBUG "rate_control_lowest_rate - no supported rates " + "found\n"); + return &mode->rates[0]; +} + + +struct global_rate_control { + int dummy; +}; + +struct sta_rate_control { + unsigned long last_rate_change; + u32 tx_num_failures; + u32 tx_num_xmit; + + unsigned long avg_rate_update; + u32 tx_avg_rate_sum; + u32 tx_avg_rate_num; + +#ifdef CONFIG_MAC80211_DEBUGFS + struct dentry *tx_avg_rate_sum_dentry; + struct dentry *tx_avg_rate_num_dentry; +#endif +}; + + +static void rate_control_simple_tx_status(void *priv, struct net_device *dev, + struct sk_buff *skb, + struct ieee80211_tx_status *status) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + struct sta_info *sta; + struct sta_rate_control *srctrl; + + sta = sta_info_get(local, hdr->addr1); + + if (!sta) + return; + + srctrl = sta->rate_ctrl_priv; + srctrl->tx_num_xmit++; + if (status->excessive_retries) { + sta->antenna_sel_tx = sta->antenna_sel_tx == 1 ? 2 : 1; + sta->antenna_sel_rx = sta->antenna_sel_rx == 1 ? 2 : 1; + if (local->sta_antenna_sel == STA_ANTENNA_SEL_SW_CTRL_DEBUG) { + printk(KERN_DEBUG "%s: " MAC_FMT " TX antenna --> %d " + "RX antenna --> %d (@%lu)\n", + dev->name, MAC_ARG(hdr->addr1), + sta->antenna_sel_tx, sta->antenna_sel_rx, jiffies); + } + srctrl->tx_num_failures++; + sta->tx_retry_failed++; + sta->tx_num_consecutive_failures++; + sta->tx_num_mpdu_fail++; + } else { + sta->last_ack_rssi[0] = sta->last_ack_rssi[1]; + sta->last_ack_rssi[1] = sta->last_ack_rssi[2]; + sta->last_ack_rssi[2] = status->ack_signal; + sta->tx_num_consecutive_failures = 0; + sta->tx_num_mpdu_ok++; + } + sta->tx_retry_count += status->retry_count; + sta->tx_num_mpdu_fail += status->retry_count; + + if (time_after(jiffies, + srctrl->last_rate_change + RATE_CONTROL_INTERVAL) && + srctrl->tx_num_xmit > RATE_CONTROL_MIN_TX) { + u32 per_failed; + srctrl->last_rate_change = jiffies; + + per_failed = (100 * sta->tx_num_mpdu_fail) / + (sta->tx_num_mpdu_fail + sta->tx_num_mpdu_ok); + /* TODO: calculate average per_failed to make adjusting + * parameters easier */ +#if 0 + if (net_ratelimit()) { + printk(KERN_DEBUG "MPDU fail=%d ok=%d per_failed=%d\n", + sta->tx_num_mpdu_fail, sta->tx_num_mpdu_ok, + per_failed); + } +#endif + + if (per_failed > local->rate_ctrl_num_down) { + rate_control_rate_dec(local, sta); + } else if (per_failed < local->rate_ctrl_num_up) { + rate_control_rate_inc(local, sta); + } + srctrl->tx_avg_rate_sum += status->control.rate->rate; + srctrl->tx_avg_rate_num++; + srctrl->tx_num_failures = 0; + srctrl->tx_num_xmit = 0; + } else if (sta->tx_num_consecutive_failures >= + RATE_CONTROL_EMERG_DEC) { + rate_control_rate_dec(local, sta); + } + + if (srctrl->avg_rate_update + 60 * HZ < jiffies) { + srctrl->avg_rate_update = jiffies; + if (srctrl->tx_avg_rate_num > 0) { +#ifdef CONFIG_MAC80211_VERBOSE_DEBUG + printk(KERN_DEBUG "%s: STA " MAC_FMT " Average rate: " + "%d (%d/%d)\n", + dev->name, MAC_ARG(sta->addr), + srctrl->tx_avg_rate_sum / + srctrl->tx_avg_rate_num, + srctrl->tx_avg_rate_sum, + srctrl->tx_avg_rate_num); +#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ + srctrl->tx_avg_rate_sum = 0; + srctrl->tx_avg_rate_num = 0; + } + } + + sta_info_put(sta); +} + + +static struct ieee80211_rate * +rate_control_simple_get_rate(void *priv, struct net_device *dev, + struct sk_buff *skb, + struct rate_control_extra *extra) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_sub_if_data *sdata; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + struct ieee80211_hw_mode *mode = extra->mode; + struct sta_info *sta; + int rateidx, nonerp_idx; + u16 fc; + + memset(extra, 0, sizeof(*extra)); + + fc = le16_to_cpu(hdr->frame_control); + if ((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA || + (hdr->addr1[0] & 0x01)) { + /* Send management frames and broadcast/multicast data using + * lowest rate. */ + /* TODO: this could probably be improved.. */ + return rate_control_lowest_rate(local, mode); + } + + sta = sta_info_get(local, hdr->addr1); + + if (!sta) + return rate_control_lowest_rate(local, mode); + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + if (sdata->bss && sdata->bss->force_unicast_rateidx > -1) + sta->txrate = sdata->bss->force_unicast_rateidx; + + rateidx = sta->txrate; + + if (rateidx >= mode->num_rates) + rateidx = mode->num_rates - 1; + + sta->last_txrate = rateidx; + nonerp_idx = rateidx; + while (nonerp_idx > 0 && + ((mode->rates[nonerp_idx].flags & IEEE80211_RATE_ERP) || + !(mode->rates[nonerp_idx].flags & IEEE80211_RATE_SUPPORTED) || + !(sta->supp_rates & BIT(nonerp_idx)))) + nonerp_idx--; + extra->nonerp = &mode->rates[nonerp_idx]; + + sta_info_put(sta); + + return &mode->rates[rateidx]; +} + + +static void rate_control_simple_rate_init(void *priv, void *priv_sta, + struct ieee80211_local *local, + struct sta_info *sta) +{ + struct ieee80211_hw_mode *mode; + int i; + sta->txrate = 0; + mode = local->oper_hw_mode; + /* TODO: what is a good starting rate for STA? About middle? Maybe not + * the lowest or the highest rate.. Could consider using RSSI from + * previous packets? Need to have IEEE 802.1X auth succeed immediately + * after assoc.. */ + for (i = 0; i < mode->num_rates; i++) { + if ((sta->supp_rates & BIT(i)) && + (mode->rates[i].flags & IEEE80211_RATE_SUPPORTED)) + sta->txrate = i; + } +} + + +static void * rate_control_simple_alloc(struct ieee80211_local *local) +{ + struct global_rate_control *rctrl; + + rctrl = kzalloc(sizeof(*rctrl), GFP_ATOMIC); + + return rctrl; +} + + +static void rate_control_simple_free(void *priv) +{ + struct global_rate_control *rctrl = priv; + kfree(rctrl); +} + + +static void rate_control_simple_clear(void *priv) +{ +} + + +static void * rate_control_simple_alloc_sta(void *priv, gfp_t gfp) +{ + struct sta_rate_control *rctrl; + + rctrl = kzalloc(sizeof(*rctrl), gfp); + + return rctrl; +} + + +static void rate_control_simple_free_sta(void *priv, void *priv_sta) +{ + struct sta_rate_control *rctrl = priv_sta; + kfree(rctrl); +} + +#ifdef CONFIG_MAC80211_DEBUGFS + +static int open_file_generic(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t sta_tx_avg_rate_sum_read(struct file *file, + char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct sta_rate_control *srctrl = file->private_data; + char buf[20]; + + sprintf(buf, "%d\n", srctrl->tx_avg_rate_sum); + return simple_read_from_buffer(userbuf, count, ppos, buf, strlen(buf)); +} + +static const struct file_operations sta_tx_avg_rate_sum_ops = { + .read = sta_tx_avg_rate_sum_read, + .open = open_file_generic, +}; + +static ssize_t sta_tx_avg_rate_num_read(struct file *file, + char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct sta_rate_control *srctrl = file->private_data; + char buf[20]; + + sprintf(buf, "%d\n", srctrl->tx_avg_rate_num); + return simple_read_from_buffer(userbuf, count, ppos, buf, strlen(buf)); +} + +static const struct file_operations sta_tx_avg_rate_num_ops = { + .read = sta_tx_avg_rate_num_read, + .open = open_file_generic, +}; + +static void rate_control_simple_add_sta_debugfs(void *priv, void *priv_sta, + struct dentry *dir) +{ + struct sta_rate_control *srctrl = priv_sta; + + srctrl->tx_avg_rate_num_dentry = + debugfs_create_file("rc_simple_sta_tx_avg_rate_num", 0400, + dir, srctrl, &sta_tx_avg_rate_num_ops); + srctrl->tx_avg_rate_sum_dentry = + debugfs_create_file("rc_simple_sta_tx_avg_rate_sum", 0400, + dir, srctrl, &sta_tx_avg_rate_sum_ops); +} + +static void rate_control_simple_remove_sta_debugfs(void *priv, void *priv_sta) +{ + struct sta_rate_control *srctrl = priv_sta; + + debugfs_remove(srctrl->tx_avg_rate_sum_dentry); + debugfs_remove(srctrl->tx_avg_rate_num_dentry); +} +#endif + +static struct rate_control_ops rate_control_simple = { + .module = THIS_MODULE, + .name = "simple", + .tx_status = rate_control_simple_tx_status, + .get_rate = rate_control_simple_get_rate, + .rate_init = rate_control_simple_rate_init, + .clear = rate_control_simple_clear, + .alloc = rate_control_simple_alloc, + .free = rate_control_simple_free, + .alloc_sta = rate_control_simple_alloc_sta, + .free_sta = rate_control_simple_free_sta, +#ifdef CONFIG_MAC80211_DEBUGFS + .add_sta_debugfs = rate_control_simple_add_sta_debugfs, + .remove_sta_debugfs = rate_control_simple_remove_sta_debugfs, +#endif +}; + + +static int __init rate_control_simple_init(void) +{ + return ieee80211_rate_control_register(&rate_control_simple); +} + + +static void __exit rate_control_simple_exit(void) +{ + ieee80211_rate_control_unregister(&rate_control_simple); +} + + +module_init(rate_control_simple_init); +module_exit(rate_control_simple_exit); + +MODULE_DESCRIPTION("Simple rate control algorithm for ieee80211"); +MODULE_LICENSE("GPL"); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c new file mode 100644 index 0000000..ab7b1f0 --- /dev/null +++ b/net/mac80211/sta_info.c @@ -0,0 +1,470 @@ +/* + * Copyright 2002-2005, Instant802 Networks, Inc. + * Copyright 2006-2007 Jiri Benc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "ieee80211_i.h" +#include "ieee80211_rate.h" +#include "sta_info.h" +#include "debugfs_key.h" +#include "debugfs_sta.h" + +/* Caller must hold local->sta_lock */ +static void sta_info_hash_add(struct ieee80211_local *local, + struct sta_info *sta) +{ + sta->hnext = local->sta_hash[STA_HASH(sta->addr)]; + local->sta_hash[STA_HASH(sta->addr)] = sta; +} + + +/* Caller must hold local->sta_lock */ +static void sta_info_hash_del(struct ieee80211_local *local, + struct sta_info *sta) +{ + struct sta_info *s; + + s = local->sta_hash[STA_HASH(sta->addr)]; + if (!s) + return; + if (memcmp(s->addr, sta->addr, ETH_ALEN) == 0) { + local->sta_hash[STA_HASH(sta->addr)] = s->hnext; + return; + } + + while (s->hnext && memcmp(s->hnext->addr, sta->addr, ETH_ALEN) != 0) + s = s->hnext; + if (s->hnext) + s->hnext = s->hnext->hnext; + else + printk(KERN_ERR "%s: could not remove STA " MAC_FMT " from " + "hash table\n", local->mdev->name, MAC_ARG(sta->addr)); +} + +static inline void __sta_info_get(struct sta_info *sta) +{ + kref_get(&sta->kref); +} + +struct sta_info *sta_info_get(struct ieee80211_local *local, u8 *addr) +{ + struct sta_info *sta; + + spin_lock_bh(&local->sta_lock); + sta = local->sta_hash[STA_HASH(addr)]; + while (sta) { + if (memcmp(sta->addr, addr, ETH_ALEN) == 0) { + __sta_info_get(sta); + break; + } + sta = sta->hnext; + } + spin_unlock_bh(&local->sta_lock); + + return sta; +} +EXPORT_SYMBOL(sta_info_get); + +int sta_info_min_txrate_get(struct ieee80211_local *local) +{ + struct sta_info *sta; + struct ieee80211_hw_mode *mode; + int min_txrate = 9999999; + int i; + + spin_lock_bh(&local->sta_lock); + mode = local->oper_hw_mode; + for (i = 0; i < STA_HASH_SIZE; i++) { + sta = local->sta_hash[i]; + while (sta) { + if (sta->txrate < min_txrate) + min_txrate = sta->txrate; + sta = sta->hnext; + } + } + spin_unlock_bh(&local->sta_lock); + if (min_txrate == 9999999) + min_txrate = 0; + + return mode->rates[min_txrate].rate; +} + + +static void sta_info_release(struct kref *kref) +{ + struct sta_info *sta = container_of(kref, struct sta_info, kref); + struct ieee80211_local *local = sta->local; + struct sk_buff *skb; + + /* free sta structure; it has already been removed from + * hash table etc. external structures. Make sure that all + * buffered frames are release (one might have been added + * after sta_info_free() was called). */ + while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) { + local->total_ps_buffered--; + dev_kfree_skb_any(skb); + } + while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) { + dev_kfree_skb_any(skb); + } + rate_control_free_sta(sta->rate_ctrl, sta->rate_ctrl_priv); + rate_control_put(sta->rate_ctrl); + if (sta->key) + ieee80211_debugfs_key_sta_del(sta->key, sta); + kfree(sta); +} + + +void sta_info_put(struct sta_info *sta) +{ + kref_put(&sta->kref, sta_info_release); +} +EXPORT_SYMBOL(sta_info_put); + + +struct sta_info * sta_info_add(struct ieee80211_local *local, + struct net_device *dev, u8 *addr, gfp_t gfp) +{ + struct sta_info *sta; + + sta = kzalloc(sizeof(*sta), gfp); + if (!sta) + return NULL; + + kref_init(&sta->kref); + + sta->rate_ctrl = rate_control_get(local->rate_ctrl); + sta->rate_ctrl_priv = rate_control_alloc_sta(sta->rate_ctrl, gfp); + if (!sta->rate_ctrl_priv) { + rate_control_put(sta->rate_ctrl); + kref_put(&sta->kref, sta_info_release); + kfree(sta); + return NULL; + } + + memcpy(sta->addr, addr, ETH_ALEN); + sta->local = local; + sta->dev = dev; + skb_queue_head_init(&sta->ps_tx_buf); + skb_queue_head_init(&sta->tx_filtered); + __sta_info_get(sta); /* sta used by caller, decremented by + * sta_info_put() */ + spin_lock_bh(&local->sta_lock); + list_add(&sta->list, &local->sta_list); + local->num_sta++; + sta_info_hash_add(local, sta); + spin_unlock_bh(&local->sta_lock); + if (local->ops->sta_table_notification) + local->ops->sta_table_notification(local_to_hw(local), + local->num_sta); + sta->key_idx_compression = HW_KEY_IDX_INVALID; + +#ifdef CONFIG_MAC80211_VERBOSE_DEBUG + printk(KERN_DEBUG "%s: Added STA " MAC_FMT "\n", + local->mdev->name, MAC_ARG(addr)); +#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ + +#ifdef CONFIG_MAC80211_DEBUGFS + if (!in_interrupt()) { + sta->debugfs_registered = 1; + ieee80211_sta_debugfs_add(sta); + rate_control_add_sta_debugfs(sta); + } else { + /* debugfs entry adding might sleep, so schedule process + * context task for adding entry for STAs that do not yet + * have one. */ + queue_work(local->hw.workqueue, &local->sta_debugfs_add); + } +#endif + + return sta; +} + +static void finish_sta_info_free(struct ieee80211_local *local, + struct sta_info *sta) +{ +#ifdef CONFIG_MAC80211_VERBOSE_DEBUG + printk(KERN_DEBUG "%s: Removed STA " MAC_FMT "\n", + local->mdev->name, MAC_ARG(sta->addr)); +#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ + + if (sta->key) { + ieee80211_debugfs_key_remove(sta->key); + ieee80211_key_free(sta->key); + sta->key = NULL; + } + + rate_control_remove_sta_debugfs(sta); + ieee80211_sta_debugfs_remove(sta); + + sta_info_put(sta); +} + +static void sta_info_remove(struct sta_info *sta) +{ + struct ieee80211_local *local = sta->local; + struct ieee80211_sub_if_data *sdata; + + sta_info_hash_del(local, sta); + list_del(&sta->list); + sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); + if (sta->flags & WLAN_STA_PS) { + sta->flags &= ~WLAN_STA_PS; + if (sdata->bss) + atomic_dec(&sdata->bss->num_sta_ps); + } + local->num_sta--; + sta_info_remove_aid_ptr(sta); +} + +void sta_info_free(struct sta_info *sta, int locked) +{ + struct sk_buff *skb; + struct ieee80211_local *local = sta->local; + + if (!locked) { + spin_lock_bh(&local->sta_lock); + sta_info_remove(sta); + spin_unlock_bh(&local->sta_lock); + } else { + sta_info_remove(sta); + } + if (local->ops->sta_table_notification) + local->ops->sta_table_notification(local_to_hw(local), + local->num_sta); + + while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) { + local->total_ps_buffered--; + dev_kfree_skb_any(skb); + } + while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) { + dev_kfree_skb_any(skb); + } + + if (sta->key) { + if (local->ops->set_key) { + struct ieee80211_key_conf *key; + key = ieee80211_key_data2conf(local, sta->key); + if (key) { + local->ops->set_key(local_to_hw(local), + DISABLE_KEY, + sta->addr, key, sta->aid); + kfree(key); + } + } + } else if (sta->key_idx_compression != HW_KEY_IDX_INVALID) { + struct ieee80211_key_conf conf; + memset(&conf, 0, sizeof(conf)); + conf.hw_key_idx = sta->key_idx_compression; + conf.alg = ALG_NULL; + conf.flags |= IEEE80211_KEY_FORCE_SW_ENCRYPT; + local->ops->set_key(local_to_hw(local), DISABLE_KEY, + sta->addr, &conf, sta->aid); + sta->key_idx_compression = HW_KEY_IDX_INVALID; + } + +#ifdef CONFIG_MAC80211_DEBUGFS + if (in_atomic()) { + list_add(&sta->list, &local->deleted_sta_list); + queue_work(local->hw.workqueue, &local->sta_debugfs_add); + } else +#endif + finish_sta_info_free(local, sta); +} + + +static inline int sta_info_buffer_expired(struct ieee80211_local *local, + struct sta_info *sta, + struct sk_buff *skb) +{ + struct ieee80211_tx_packet_data *pkt_data; + int timeout; + + if (!skb) + return 0; + + pkt_data = (struct ieee80211_tx_packet_data *) skb->cb; + + /* Timeout: (2 * listen_interval * beacon_int * 1024 / 1000000) sec */ + timeout = (sta->listen_interval * local->hw.conf.beacon_int * 32 / + 15625) * HZ; + if (timeout < STA_TX_BUFFER_EXPIRE) + timeout = STA_TX_BUFFER_EXPIRE; + return time_after(jiffies, pkt_data->jiffies + timeout); +} + + +static void sta_info_cleanup_expire_buffered(struct ieee80211_local *local, + struct sta_info *sta) +{ + unsigned long flags; + struct sk_buff *skb; + + if (skb_queue_empty(&sta->ps_tx_buf)) + return; + + for (;;) { + spin_lock_irqsave(&sta->ps_tx_buf.lock, flags); + skb = skb_peek(&sta->ps_tx_buf); + if (sta_info_buffer_expired(local, sta, skb)) { + skb = __skb_dequeue(&sta->ps_tx_buf); + if (skb_queue_empty(&sta->ps_tx_buf)) + sta->flags &= ~WLAN_STA_TIM; + } else + skb = NULL; + spin_unlock_irqrestore(&sta->ps_tx_buf.lock, flags); + + if (skb) { + local->total_ps_buffered--; + printk(KERN_DEBUG "Buffered frame expired (STA " + MAC_FMT ")\n", MAC_ARG(sta->addr)); + dev_kfree_skb(skb); + } else + break; + } +} + + +static void sta_info_cleanup(unsigned long data) +{ + struct ieee80211_local *local = (struct ieee80211_local *) data; + struct sta_info *sta; + + spin_lock_bh(&local->sta_lock); + list_for_each_entry(sta, &local->sta_list, list) { + __sta_info_get(sta); + sta_info_cleanup_expire_buffered(local, sta); + sta_info_put(sta); + } + spin_unlock_bh(&local->sta_lock); + + local->sta_cleanup.expires = jiffies + STA_INFO_CLEANUP_INTERVAL; + add_timer(&local->sta_cleanup); +} + +#ifdef CONFIG_MAC80211_DEBUGFS +static void sta_info_debugfs_add_task(struct work_struct *work) +{ + struct ieee80211_local *local = + container_of(work, struct ieee80211_local, sta_debugfs_add); + struct sta_info *sta, *tmp; + + while (1) { + spin_lock_bh(&local->sta_lock); + if (!list_empty(&local->deleted_sta_list)) { + sta = list_entry(local->deleted_sta_list.next, + struct sta_info, list); + list_del(local->deleted_sta_list.next); + } else + sta = NULL; + spin_unlock_bh(&local->sta_lock); + if (!sta) + break; + finish_sta_info_free(local, sta); + } + + while (1) { + sta = NULL; + spin_lock_bh(&local->sta_lock); + list_for_each_entry(tmp, &local->sta_list, list) { + if (!tmp->debugfs_registered) { + sta = tmp; + __sta_info_get(sta); + break; + } + } + spin_unlock_bh(&local->sta_lock); + + if (!sta) + break; + + sta->debugfs_registered = 1; + ieee80211_sta_debugfs_add(sta); + rate_control_add_sta_debugfs(sta); + sta_info_put(sta); + } +} +#endif + +void sta_info_init(struct ieee80211_local *local) +{ + spin_lock_init(&local->sta_lock); + INIT_LIST_HEAD(&local->sta_list); + INIT_LIST_HEAD(&local->deleted_sta_list); + + init_timer(&local->sta_cleanup); + local->sta_cleanup.expires = jiffies + STA_INFO_CLEANUP_INTERVAL; + local->sta_cleanup.data = (unsigned long) local; + local->sta_cleanup.function = sta_info_cleanup; + +#ifdef CONFIG_MAC80211_DEBUGFS + INIT_WORK(&local->sta_debugfs_add, sta_info_debugfs_add_task); +#endif +} + +int sta_info_start(struct ieee80211_local *local) +{ + add_timer(&local->sta_cleanup); + return 0; +} + +void sta_info_stop(struct ieee80211_local *local) +{ + struct sta_info *sta, *tmp; + + del_timer(&local->sta_cleanup); + + list_for_each_entry_safe(sta, tmp, &local->sta_list, list) { + /* sta_info_free must be called with 0 as the last + * parameter to ensure all debugfs sta entries are + * unregistered. We don't need locking at this + * point. */ + sta_info_free(sta, 0); + } +} + +void sta_info_remove_aid_ptr(struct sta_info *sta) +{ + struct ieee80211_sub_if_data *sdata; + + if (sta->aid <= 0) + return; + + sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); + + if (sdata->local->ops->set_tim) + sdata->local->ops->set_tim(local_to_hw(sdata->local), + sta->aid, 0); + if (sdata->bss) + __bss_tim_clear(sdata->bss, sta->aid); +} + + +/** + * sta_info_flush - flush matching STA entries from the STA table + * @local: local interface data + * @dev: matching rule for the net device (sta->dev) or %NULL to match all STAs + */ +void sta_info_flush(struct ieee80211_local *local, struct net_device *dev) +{ + struct sta_info *sta, *tmp; + + spin_lock_bh(&local->sta_lock); + list_for_each_entry_safe(sta, tmp, &local->sta_list, list) + if (!dev || dev == sta->dev) + sta_info_free(sta, 1); + spin_unlock_bh(&local->sta_lock); +} diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h new file mode 100644 index 0000000..6a067a0 --- /dev/null +++ b/net/mac80211/sta_info.h @@ -0,0 +1,169 @@ +/* + * Copyright 2002-2005, Devicescape Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef STA_INFO_H +#define STA_INFO_H + +#include +#include +#include +#include +#include "ieee80211_key.h" + +/* Stations flags (struct sta_info::flags) */ +#define WLAN_STA_AUTH BIT(0) +#define WLAN_STA_ASSOC BIT(1) +#define WLAN_STA_PS BIT(2) +#define WLAN_STA_TIM BIT(3) /* TIM bit is on for PS stations */ +#define WLAN_STA_PERM BIT(4) /* permanent; do not remove entry on expiration */ +#define WLAN_STA_AUTHORIZED BIT(5) /* If 802.1X is used, this flag is + * controlling whether STA is authorized to + * send and receive non-IEEE 802.1X frames + */ +#define WLAN_STA_SHORT_PREAMBLE BIT(7) +#define WLAN_STA_WME BIT(9) +#define WLAN_STA_HT BIT(10) +#define WLAN_STA_WDS BIT(27) + + +struct sta_info { + struct kref kref; + struct list_head list; + struct sta_info *hnext; /* next entry in hash table list */ + + struct ieee80211_local *local; + + u8 addr[ETH_ALEN]; + u16 aid; /* STA's unique AID (1..2007), 0 = not yet assigned */ + u32 flags; /* WLAN_STA_ */ + + struct sk_buff_head ps_tx_buf; /* buffer of TX frames for station in + * power saving state */ + int pspoll; /* whether STA has send a PS Poll frame */ + struct sk_buff_head tx_filtered; /* buffer of TX frames that were + * already given to low-level driver, + * but were filtered */ + int clear_dst_mask; + + unsigned long rx_packets, tx_packets; /* number of RX/TX MSDUs */ + unsigned long rx_bytes, tx_bytes; + unsigned long tx_retry_failed, tx_retry_count; + unsigned long tx_filtered_count; + + unsigned int wep_weak_iv_count; /* number of RX frames with weak IV */ + + unsigned long last_rx; + u32 supp_rates; /* bitmap of supported rates in local->curr_rates */ + int txrate; /* index in local->curr_rates */ + int last_txrate; /* last rate used to send a frame to this STA */ + int last_nonerp_idx; + + struct net_device *dev; /* which net device is this station associated + * to */ + + struct ieee80211_key *key; + + u32 tx_num_consecutive_failures; + u32 tx_num_mpdu_ok; + u32 tx_num_mpdu_fail; + + struct rate_control_ref *rate_ctrl; + void *rate_ctrl_priv; + + /* last received seq/frag number from this STA (per RX queue) */ + __le16 last_seq_ctrl[NUM_RX_DATA_QUEUES]; + unsigned long num_duplicates; /* number of duplicate frames received + * from this STA */ + unsigned long tx_fragments; /* number of transmitted MPDUs */ + unsigned long rx_fragments; /* number of received MPDUs */ + unsigned long rx_dropped; /* number of dropped MPDUs from this STA */ + + int last_rssi; /* RSSI of last received frame from this STA */ + int last_signal; /* signal of last received frame from this STA */ + int last_noise; /* noise of last received frame from this STA */ + int last_ack_rssi[3]; /* RSSI of last received ACKs from this STA */ + unsigned long last_ack; + int channel_use; + int channel_use_raw; + + u8 antenna_sel_tx; + u8 antenna_sel_rx; + + + int key_idx_compression; /* key table index for compression and TX + * filtering; used only if sta->key is not + * set */ + +#ifdef CONFIG_MAC80211_DEBUGFS + int debugfs_registered; +#endif + int assoc_ap; /* whether this is an AP that we are + * associated with as a client */ + +#ifdef CONFIG_HOSTAPD_WPA_TESTING + u32 wpa_trigger; +#endif /* CONFIG_HOSTAPD_WPA_TESTING */ + +#ifdef CONFIG_MAC80211_DEBUG_COUNTERS + unsigned int wme_rx_queue[NUM_RX_DATA_QUEUES]; + unsigned int wme_tx_queue[NUM_RX_DATA_QUEUES]; +#endif /* CONFIG_MAC80211_DEBUG_COUNTERS */ + + int vlan_id; + + u16 listen_interval; + +#ifdef CONFIG_MAC80211_DEBUGFS + struct sta_info_debugfsdentries { + struct dentry *dir; + struct dentry *flags; + struct dentry *num_ps_buf_frames; + struct dentry *last_ack_rssi; + struct dentry *last_ack_ms; + struct dentry *inactive_ms; + struct dentry *last_seq_ctrl; +#ifdef CONFIG_MAC80211_DEBUG_COUNTERS + struct dentry *wme_rx_queue; + struct dentry *wme_tx_queue; +#endif + } debugfs; +#endif +}; + + +/* Maximum number of concurrently registered stations */ +#define MAX_STA_COUNT 2007 + +#define STA_HASH_SIZE 256 +#define STA_HASH(sta) (sta[5]) + + +/* Maximum number of frames to buffer per power saving station */ +#define STA_MAX_TX_BUFFER 128 + +/* Minimum buffered frame expiry time. If STA uses listen interval that is + * smaller than this value, the minimum value here is used instead. */ +#define STA_TX_BUFFER_EXPIRE (10 * HZ) + +/* How often station data is cleaned up (e.g., expiration of buffered frames) + */ +#define STA_INFO_CLEANUP_INTERVAL (10 * HZ) + +struct sta_info * sta_info_get(struct ieee80211_local *local, u8 *addr); +int sta_info_min_txrate_get(struct ieee80211_local *local); +void sta_info_put(struct sta_info *sta); +struct sta_info * sta_info_add(struct ieee80211_local *local, + struct net_device *dev, u8 *addr, gfp_t gfp); +void sta_info_free(struct sta_info *sta, int locked); +void sta_info_init(struct ieee80211_local *local); +int sta_info_start(struct ieee80211_local *local); +void sta_info_stop(struct ieee80211_local *local); +void sta_info_remove_aid_ptr(struct sta_info *sta); +void sta_info_flush(struct ieee80211_local *local, struct net_device *dev); + +#endif /* STA_INFO_H */ diff --git a/net/mac80211/tkip.c b/net/mac80211/tkip.c new file mode 100644 index 0000000..4162172 --- /dev/null +++ b/net/mac80211/tkip.c @@ -0,0 +1,341 @@ +/* + * Copyright 2002-2004, Instant802 Networks, Inc. + * Copyright 2005, Devicescape Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include +#include "ieee80211_key.h" +#include "tkip.h" +#include "wep.h" + + +/* TKIP key mixing functions */ + + +#define PHASE1_LOOP_COUNT 8 + + +/* 2-byte by 2-byte subset of the full AES S-box table; second part of this + * table is identical to first part but byte-swapped */ +static const u16 tkip_sbox[256] = +{ + 0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154, + 0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A, + 0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B, + 0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B, + 0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F, + 0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F, + 0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5, + 0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F, + 0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB, + 0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397, + 0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED, + 0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A, + 0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194, + 0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3, + 0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104, + 0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D, + 0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39, + 0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695, + 0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83, + 0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76, + 0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4, + 0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B, + 0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0, + 0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018, + 0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751, + 0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85, + 0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12, + 0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9, + 0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7, + 0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A, + 0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8, + 0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A, +}; + + +static inline u16 Mk16(u8 x, u8 y) +{ + return ((u16) x << 8) | (u16) y; +} + + +static inline u8 Hi8(u16 v) +{ + return v >> 8; +} + + +static inline u8 Lo8(u16 v) +{ + return v & 0xff; +} + + +static inline u16 Hi16(u32 v) +{ + return v >> 16; +} + + +static inline u16 Lo16(u32 v) +{ + return v & 0xffff; +} + + +static inline u16 RotR1(u16 v) +{ + return (v >> 1) | ((v & 0x0001) << 15); +} + + +static inline u16 tkip_S(u16 val) +{ + u16 a = tkip_sbox[Hi8(val)]; + + return tkip_sbox[Lo8(val)] ^ Hi8(a) ^ (Lo8(a) << 8); +} + + + +/* P1K := Phase1(TA, TK, TSC) + * TA = transmitter address (48 bits) + * TK = dot11DefaultKeyValue or dot11KeyMappingValue (128 bits) + * TSC = TKIP sequence counter (48 bits, only 32 msb bits used) + * P1K: 80 bits + */ +static void tkip_mixing_phase1(const u8 *ta, const u8 *tk, u32 tsc_IV32, + u16 *p1k) +{ + int i, j; + + p1k[0] = Lo16(tsc_IV32); + p1k[1] = Hi16(tsc_IV32); + p1k[2] = Mk16(ta[1], ta[0]); + p1k[3] = Mk16(ta[3], ta[2]); + p1k[4] = Mk16(ta[5], ta[4]); + + for (i = 0; i < PHASE1_LOOP_COUNT; i++) { + j = 2 * (i & 1); + p1k[0] += tkip_S(p1k[4] ^ Mk16(tk[ 1 + j], tk[ 0 + j])); + p1k[1] += tkip_S(p1k[0] ^ Mk16(tk[ 5 + j], tk[ 4 + j])); + p1k[2] += tkip_S(p1k[1] ^ Mk16(tk[ 9 + j], tk[ 8 + j])); + p1k[3] += tkip_S(p1k[2] ^ Mk16(tk[13 + j], tk[12 + j])); + p1k[4] += tkip_S(p1k[3] ^ Mk16(tk[ 1 + j], tk[ 0 + j])) + i; + } +} + + +static void tkip_mixing_phase2(const u16 *p1k, const u8 *tk, u16 tsc_IV16, + u8 *rc4key) +{ + u16 ppk[6]; + int i; + + ppk[0] = p1k[0]; + ppk[1] = p1k[1]; + ppk[2] = p1k[2]; + ppk[3] = p1k[3]; + ppk[4] = p1k[4]; + ppk[5] = p1k[4] + tsc_IV16; + + ppk[0] += tkip_S(ppk[5] ^ Mk16(tk[ 1], tk[ 0])); + ppk[1] += tkip_S(ppk[0] ^ Mk16(tk[ 3], tk[ 2])); + ppk[2] += tkip_S(ppk[1] ^ Mk16(tk[ 5], tk[ 4])); + ppk[3] += tkip_S(ppk[2] ^ Mk16(tk[ 7], tk[ 6])); + ppk[4] += tkip_S(ppk[3] ^ Mk16(tk[ 9], tk[ 8])); + ppk[5] += tkip_S(ppk[4] ^ Mk16(tk[11], tk[10])); + ppk[0] += RotR1(ppk[5] ^ Mk16(tk[13], tk[12])); + ppk[1] += RotR1(ppk[0] ^ Mk16(tk[15], tk[14])); + ppk[2] += RotR1(ppk[1]); + ppk[3] += RotR1(ppk[2]); + ppk[4] += RotR1(ppk[3]); + ppk[5] += RotR1(ppk[4]); + + rc4key[0] = Hi8(tsc_IV16); + rc4key[1] = (Hi8(tsc_IV16) | 0x20) & 0x7f; + rc4key[2] = Lo8(tsc_IV16); + rc4key[3] = Lo8((ppk[5] ^ Mk16(tk[1], tk[0])) >> 1); + + for (i = 0; i < 6; i++) { + rc4key[4 + 2 * i] = Lo8(ppk[i]); + rc4key[5 + 2 * i] = Hi8(ppk[i]); + } +} + + +/* Add TKIP IV and Ext. IV at @pos. @iv0, @iv1, and @iv2 are the first octets + * of the IV. Returns pointer to the octet following IVs (i.e., beginning of + * the packet payload). */ +u8 * ieee80211_tkip_add_iv(u8 *pos, struct ieee80211_key *key, + u8 iv0, u8 iv1, u8 iv2) +{ + *pos++ = iv0; + *pos++ = iv1; + *pos++ = iv2; + *pos++ = (key->keyidx << 6) | (1 << 5) /* Ext IV */; + *pos++ = key->u.tkip.iv32 & 0xff; + *pos++ = (key->u.tkip.iv32 >> 8) & 0xff; + *pos++ = (key->u.tkip.iv32 >> 16) & 0xff; + *pos++ = (key->u.tkip.iv32 >> 24) & 0xff; + return pos; +} + + +void ieee80211_tkip_gen_phase1key(struct ieee80211_key *key, u8 *ta, + u16 *phase1key) +{ + tkip_mixing_phase1(ta, &key->key[ALG_TKIP_TEMP_ENCR_KEY], + key->u.tkip.iv32, phase1key); +} + +void ieee80211_tkip_gen_rc4key(struct ieee80211_key *key, u8 *ta, + u8 *rc4key) +{ + /* Calculate per-packet key */ + if (key->u.tkip.iv16 == 0 || !key->u.tkip.tx_initialized) { + /* IV16 wrapped around - perform TKIP phase 1 */ + tkip_mixing_phase1(ta, &key->key[ALG_TKIP_TEMP_ENCR_KEY], + key->u.tkip.iv32, key->u.tkip.p1k); + key->u.tkip.tx_initialized = 1; + } + + tkip_mixing_phase2(key->u.tkip.p1k, &key->key[ALG_TKIP_TEMP_ENCR_KEY], + key->u.tkip.iv16, rc4key); +} + +/* Encrypt packet payload with TKIP using @key. @pos is a pointer to the + * beginning of the buffer containing payload. This payload must include + * headroom of eight octets for IV and Ext. IV and taildroom of four octets + * for ICV. @payload_len is the length of payload (_not_ including extra + * headroom and tailroom). @ta is the transmitter addresses. */ +void ieee80211_tkip_encrypt_data(struct crypto_blkcipher *tfm, + struct ieee80211_key *key, + u8 *pos, size_t payload_len, u8 *ta) +{ + u8 rc4key[16]; + + ieee80211_tkip_gen_rc4key(key, ta, rc4key); + pos = ieee80211_tkip_add_iv(pos, key, rc4key[0], rc4key[1], rc4key[2]); + ieee80211_wep_encrypt_data(tfm, rc4key, 16, pos, payload_len); +} + + +/* Decrypt packet payload with TKIP using @key. @pos is a pointer to the + * beginning of the buffer containing IEEE 802.11 header payload, i.e., + * including IV, Ext. IV, real data, Michael MIC, ICV. @payload_len is the + * length of payload, including IV, Ext. IV, MIC, ICV. */ +int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm, + struct ieee80211_key *key, + u8 *payload, size_t payload_len, u8 *ta, + int only_iv, int queue) +{ + u32 iv32; + u32 iv16; + u8 rc4key[16], keyid, *pos = payload; + int res; + + if (payload_len < 12) + return -1; + + iv16 = (pos[0] << 8) | pos[2]; + keyid = pos[3]; + iv32 = pos[4] | (pos[5] << 8) | (pos[6] << 16) | (pos[7] << 24); + pos += 8; +#ifdef CONFIG_TKIP_DEBUG + { + int i; + printk(KERN_DEBUG "TKIP decrypt: data(len=%zd)", payload_len); + for (i = 0; i < payload_len; i++) + printk(" %02x", payload[i]); + printk("\n"); + printk(KERN_DEBUG "TKIP decrypt: iv16=%04x iv32=%08x\n", + iv16, iv32); + } +#endif /* CONFIG_TKIP_DEBUG */ + + if (!(keyid & (1 << 5))) + return TKIP_DECRYPT_NO_EXT_IV; + + if ((keyid >> 6) != key->keyidx) + return TKIP_DECRYPT_INVALID_KEYIDX; + + if (key->u.tkip.rx_initialized[queue] && + (iv32 < key->u.tkip.iv32_rx[queue] || + (iv32 == key->u.tkip.iv32_rx[queue] && + iv16 <= key->u.tkip.iv16_rx[queue]))) { +#ifdef CONFIG_TKIP_DEBUG + printk(KERN_DEBUG "TKIP replay detected for RX frame from " + MAC_FMT " (RX IV (%04x,%02x) <= prev. IV (%04x,%02x)\n", + MAC_ARG(ta), + iv32, iv16, key->u.tkip.iv32_rx[queue], + key->u.tkip.iv16_rx[queue]); +#endif /* CONFIG_TKIP_DEBUG */ + return TKIP_DECRYPT_REPLAY; + } + + if (only_iv) { + res = TKIP_DECRYPT_OK; + key->u.tkip.rx_initialized[queue] = 1; + goto done; + } + + if (!key->u.tkip.rx_initialized[queue] || + key->u.tkip.iv32_rx[queue] != iv32) { + key->u.tkip.rx_initialized[queue] = 1; + /* IV16 wrapped around - perform TKIP phase 1 */ + tkip_mixing_phase1(ta, &key->key[ALG_TKIP_TEMP_ENCR_KEY], + iv32, key->u.tkip.p1k_rx[queue]); +#ifdef CONFIG_TKIP_DEBUG + { + int i; + printk(KERN_DEBUG "TKIP decrypt: Phase1 TA=" MAC_FMT + " TK=", MAC_ARG(ta)); + for (i = 0; i < 16; i++) + printk("%02x ", + key->key[ALG_TKIP_TEMP_ENCR_KEY + i]); + printk("\n"); + printk(KERN_DEBUG "TKIP decrypt: P1K="); + for (i = 0; i < 5; i++) + printk("%04x ", key->u.tkip.p1k_rx[queue][i]); + printk("\n"); + } +#endif /* CONFIG_TKIP_DEBUG */ + } + + tkip_mixing_phase2(key->u.tkip.p1k_rx[queue], + &key->key[ALG_TKIP_TEMP_ENCR_KEY], + iv16, rc4key); +#ifdef CONFIG_TKIP_DEBUG + { + int i; + printk(KERN_DEBUG "TKIP decrypt: Phase2 rc4key="); + for (i = 0; i < 16; i++) + printk("%02x ", rc4key[i]); + printk("\n"); + } +#endif /* CONFIG_TKIP_DEBUG */ + + res = ieee80211_wep_decrypt_data(tfm, rc4key, 16, pos, payload_len - 12); + done: + if (res == TKIP_DECRYPT_OK) { + /* FIX: these should be updated only after Michael MIC has been + * verified */ + /* Record previously received IV */ + key->u.tkip.iv32_rx[queue] = iv32; + key->u.tkip.iv16_rx[queue] = iv16; + } + + return res; +} + + diff --git a/net/mac80211/tkip.h b/net/mac80211/tkip.h new file mode 100644 index 0000000..a0d181a --- /dev/null +++ b/net/mac80211/tkip.h @@ -0,0 +1,36 @@ +/* + * Copyright 2002-2004, Instant802 Networks, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef TKIP_H +#define TKIP_H + +#include +#include +#include "ieee80211_key.h" + +u8 * ieee80211_tkip_add_iv(u8 *pos, struct ieee80211_key *key, + u8 iv0, u8 iv1, u8 iv2); +void ieee80211_tkip_gen_phase1key(struct ieee80211_key *key, u8 *ta, + u16 *phase1key); +void ieee80211_tkip_gen_rc4key(struct ieee80211_key *key, u8 *ta, + u8 *rc4key); +void ieee80211_tkip_encrypt_data(struct crypto_blkcipher *tfm, + struct ieee80211_key *key, + u8 *pos, size_t payload_len, u8 *ta); +enum { + TKIP_DECRYPT_OK = 0, + TKIP_DECRYPT_NO_EXT_IV = -1, + TKIP_DECRYPT_INVALID_KEYIDX = -2, + TKIP_DECRYPT_REPLAY = -3, +}; +int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm, + struct ieee80211_key *key, + u8 *payload, size_t payload_len, u8 *ta, + int only_iv, int queue); + +#endif /* TKIP_H */ diff --git a/net/mac80211/wep.c b/net/mac80211/wep.c new file mode 100644 index 0000000..1ad3d75 --- /dev/null +++ b/net/mac80211/wep.c @@ -0,0 +1,328 @@ +/* + * Software WEP encryption implementation + * Copyright 2002, Jouni Malinen + * Copyright 2003, Instant802 Networks, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "ieee80211_i.h" +#include "wep.h" + + +int ieee80211_wep_init(struct ieee80211_local *local) +{ + /* start WEP IV from a random value */ + get_random_bytes(&local->wep_iv, WEP_IV_LEN); + + local->wep_tx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, + CRYPTO_ALG_ASYNC); + if (IS_ERR(local->wep_tx_tfm)) + return -ENOMEM; + + local->wep_rx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, + CRYPTO_ALG_ASYNC); + if (IS_ERR(local->wep_rx_tfm)) { + crypto_free_blkcipher(local->wep_tx_tfm); + return -ENOMEM; + } + + return 0; +} + +void ieee80211_wep_free(struct ieee80211_local *local) +{ + crypto_free_blkcipher(local->wep_tx_tfm); + crypto_free_blkcipher(local->wep_rx_tfm); +} + +static inline int ieee80211_wep_weak_iv(u32 iv, int keylen) +{ + /* Fluhrer, Mantin, and Shamir have reported weaknesses in the + * key scheduling algorithm of RC4. At least IVs (KeyByte + 3, + * 0xff, N) can be used to speedup attacks, so avoid using them. */ + if ((iv & 0xff00) == 0xff00) { + u8 B = (iv >> 16) & 0xff; + if (B >= 3 && B < 3 + keylen) + return 1; + } + return 0; +} + + +void ieee80211_wep_get_iv(struct ieee80211_local *local, + struct ieee80211_key *key, u8 *iv) +{ + local->wep_iv++; + if (ieee80211_wep_weak_iv(local->wep_iv, key->keylen)) + local->wep_iv += 0x0100; + + if (!iv) + return; + + *iv++ = (local->wep_iv >> 16) & 0xff; + *iv++ = (local->wep_iv >> 8) & 0xff; + *iv++ = local->wep_iv & 0xff; + *iv++ = key->keyidx << 6; +} + + +u8 * ieee80211_wep_add_iv(struct ieee80211_local *local, + struct sk_buff *skb, + struct ieee80211_key *key) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + u16 fc; + int hdrlen; + u8 *newhdr; + + fc = le16_to_cpu(hdr->frame_control); + fc |= IEEE80211_FCTL_PROTECTED; + hdr->frame_control = cpu_to_le16(fc); + + if ((skb_headroom(skb) < WEP_IV_LEN || + skb_tailroom(skb) < WEP_ICV_LEN)) { + I802_DEBUG_INC(local->tx_expand_skb_head); + if (unlikely(pskb_expand_head(skb, WEP_IV_LEN, WEP_ICV_LEN, + GFP_ATOMIC))) + return NULL; + } + + hdrlen = ieee80211_get_hdrlen(fc); + newhdr = skb_push(skb, WEP_IV_LEN); + memmove(newhdr, newhdr + WEP_IV_LEN, hdrlen); + ieee80211_wep_get_iv(local, key, newhdr + hdrlen); + return newhdr + hdrlen; +} + + +void ieee80211_wep_remove_iv(struct ieee80211_local *local, + struct sk_buff *skb, + struct ieee80211_key *key) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + u16 fc; + int hdrlen; + + fc = le16_to_cpu(hdr->frame_control); + hdrlen = ieee80211_get_hdrlen(fc); + memmove(skb->data + WEP_IV_LEN, skb->data, hdrlen); + skb_pull(skb, WEP_IV_LEN); +} + + +/* Perform WEP encryption using given key. data buffer must have tailroom + * for 4-byte ICV. data_len must not include this ICV. Note: this function + * does _not_ add IV. data = RC4(data | CRC32(data)) */ +void ieee80211_wep_encrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key, + size_t klen, u8 *data, size_t data_len) +{ + struct blkcipher_desc desc = { .tfm = tfm }; + struct scatterlist sg; + __le32 *icv; + + icv = (__le32 *)(data + data_len); + *icv = cpu_to_le32(~crc32_le(~0, data, data_len)); + + crypto_blkcipher_setkey(tfm, rc4key, klen); + sg.page = virt_to_page(data); + sg.offset = offset_in_page(data); + sg.length = data_len + WEP_ICV_LEN; + crypto_blkcipher_encrypt(&desc, &sg, &sg, sg.length); +} + + +/* Perform WEP encryption on given skb. 4 bytes of extra space (IV) in the + * beginning of the buffer 4 bytes of extra space (ICV) in the end of the + * buffer will be added. Both IV and ICV will be transmitted, so the + * payload length increases with 8 bytes. + * + * WEP frame payload: IV + TX key idx, RC4(data), ICV = RC4(CRC32(data)) + */ +int ieee80211_wep_encrypt(struct ieee80211_local *local, struct sk_buff *skb, + struct ieee80211_key *key) +{ + u32 klen; + u8 *rc4key, *iv; + size_t len; + + if (!key || key->alg != ALG_WEP) + return -1; + + klen = 3 + key->keylen; + rc4key = kmalloc(klen, GFP_ATOMIC); + if (!rc4key) + return -1; + + iv = ieee80211_wep_add_iv(local, skb, key); + if (!iv) { + kfree(rc4key); + return -1; + } + + len = skb->len - (iv + WEP_IV_LEN - skb->data); + + /* Prepend 24-bit IV to RC4 key */ + memcpy(rc4key, iv, 3); + + /* Copy rest of the WEP key (the secret part) */ + memcpy(rc4key + 3, key->key, key->keylen); + + /* Add room for ICV */ + skb_put(skb, WEP_ICV_LEN); + + ieee80211_wep_encrypt_data(local->wep_tx_tfm, rc4key, klen, + iv + WEP_IV_LEN, len); + + kfree(rc4key); + + return 0; +} + + +/* Perform WEP decryption using given key. data buffer includes encrypted + * payload, including 4-byte ICV, but _not_ IV. data_len must not include ICV. + * Return 0 on success and -1 on ICV mismatch. */ +int ieee80211_wep_decrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key, + size_t klen, u8 *data, size_t data_len) +{ + struct blkcipher_desc desc = { .tfm = tfm }; + struct scatterlist sg; + __le32 crc; + + crypto_blkcipher_setkey(tfm, rc4key, klen); + sg.page = virt_to_page(data); + sg.offset = offset_in_page(data); + sg.length = data_len + WEP_ICV_LEN; + crypto_blkcipher_decrypt(&desc, &sg, &sg, sg.length); + + crc = cpu_to_le32(~crc32_le(~0, data, data_len)); + if (memcmp(&crc, data + data_len, WEP_ICV_LEN) != 0) + /* ICV mismatch */ + return -1; + + return 0; +} + + +/* Perform WEP decryption on given skb. Buffer includes whole WEP part of + * the frame: IV (4 bytes), encrypted payload (including SNAP header), + * ICV (4 bytes). skb->len includes both IV and ICV. + * + * Returns 0 if frame was decrypted successfully and ICV was correct and -1 on + * failure. If frame is OK, IV and ICV will be removed, i.e., decrypted payload + * is moved to the beginning of the skb and skb length will be reduced. + */ +int ieee80211_wep_decrypt(struct ieee80211_local *local, struct sk_buff *skb, + struct ieee80211_key *key) +{ + u32 klen; + u8 *rc4key; + u8 keyidx; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + u16 fc; + int hdrlen; + size_t len; + int ret = 0; + + fc = le16_to_cpu(hdr->frame_control); + if (!(fc & IEEE80211_FCTL_PROTECTED)) + return -1; + + hdrlen = ieee80211_get_hdrlen(fc); + + if (skb->len < 8 + hdrlen) + return -1; + + len = skb->len - hdrlen - 8; + + keyidx = skb->data[hdrlen + 3] >> 6; + + if (!key || keyidx != key->keyidx || key->alg != ALG_WEP) + return -1; + + klen = 3 + key->keylen; + + rc4key = kmalloc(klen, GFP_ATOMIC); + if (!rc4key) + return -1; + + /* Prepend 24-bit IV to RC4 key */ + memcpy(rc4key, skb->data + hdrlen, 3); + + /* Copy rest of the WEP key (the secret part) */ + memcpy(rc4key + 3, key->key, key->keylen); + + if (ieee80211_wep_decrypt_data(local->wep_rx_tfm, rc4key, klen, + skb->data + hdrlen + WEP_IV_LEN, + len)) { + printk(KERN_DEBUG "WEP decrypt failed (ICV)\n"); + ret = -1; + } + + kfree(rc4key); + + /* Trim ICV */ + skb_trim(skb, skb->len - WEP_ICV_LEN); + + /* Remove IV */ + memmove(skb->data + WEP_IV_LEN, skb->data, hdrlen); + skb_pull(skb, WEP_IV_LEN); + + return ret; +} + + +int ieee80211_wep_get_keyidx(struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + u16 fc; + int hdrlen; + + fc = le16_to_cpu(hdr->frame_control); + if (!(fc & IEEE80211_FCTL_PROTECTED)) + return -1; + + hdrlen = ieee80211_get_hdrlen(fc); + + if (skb->len < 8 + hdrlen) + return -1; + + return skb->data[hdrlen + 3] >> 6; +} + + +u8 * ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + u16 fc; + int hdrlen; + u8 *ivpos; + u32 iv; + + fc = le16_to_cpu(hdr->frame_control); + if (!(fc & IEEE80211_FCTL_PROTECTED)) + return NULL; + + hdrlen = ieee80211_get_hdrlen(fc); + ivpos = skb->data + hdrlen; + iv = (ivpos[0] << 16) | (ivpos[1] << 8) | ivpos[2]; + + if (ieee80211_wep_weak_iv(iv, key->keylen)) + return ivpos; + + return NULL; +} diff --git a/net/mac80211/wep.h b/net/mac80211/wep.h new file mode 100644 index 0000000..bfe29e8 --- /dev/null +++ b/net/mac80211/wep.h @@ -0,0 +1,40 @@ +/* + * Software WEP encryption implementation + * Copyright 2002, Jouni Malinen + * Copyright 2003, Instant802 Networks, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef WEP_H +#define WEP_H + +#include +#include +#include "ieee80211_i.h" +#include "ieee80211_key.h" + +int ieee80211_wep_init(struct ieee80211_local *local); +void ieee80211_wep_free(struct ieee80211_local *local); +void ieee80211_wep_get_iv(struct ieee80211_local *local, + struct ieee80211_key *key, u8 *iv); +u8 * ieee80211_wep_add_iv(struct ieee80211_local *local, + struct sk_buff *skb, + struct ieee80211_key *key); +void ieee80211_wep_remove_iv(struct ieee80211_local *local, + struct sk_buff *skb, + struct ieee80211_key *key); +void ieee80211_wep_encrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key, + size_t klen, u8 *data, size_t data_len); +int ieee80211_wep_decrypt_data(struct crypto_blkcipher *tfm, u8 *rc4key, + size_t klen, u8 *data, size_t data_len); +int ieee80211_wep_encrypt(struct ieee80211_local *local, struct sk_buff *skb, + struct ieee80211_key *key); +int ieee80211_wep_decrypt(struct ieee80211_local *local, struct sk_buff *skb, + struct ieee80211_key *key); +int ieee80211_wep_get_keyidx(struct sk_buff *skb); +u8 * ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key); + +#endif /* WEP_H */ diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c new file mode 100644 index 0000000..cf0a69a --- /dev/null +++ b/net/mac80211/wme.c @@ -0,0 +1,678 @@ +/* + * Copyright 2004, Instant802 Networks, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "ieee80211_i.h" +#include "wme.h" + +static inline int WLAN_FC_IS_QOS_DATA(u16 fc) +{ + return (fc & 0x8C) == 0x88; +} + + +ieee80211_txrx_result +ieee80211_rx_h_parse_qos(struct ieee80211_txrx_data *rx) +{ + u8 *data = rx->skb->data; + int tid; + unsigned int is_agg_frame = 0; + + /* does the frame have a qos control field? */ + if (WLAN_FC_IS_QOS_DATA(rx->fc)) { + u8 *qc = data + ieee80211_get_hdrlen(rx->fc) - QOS_CONTROL_LEN; + + /* frame has qos control */ + rx->u.rx.qos_control = le16_to_cpu(*((__le16*)qc)); + tid = rx->u.rx.qos_control & QOS_CONTROL_TID_MASK; + if (rx->u.rx.qos_control & + IEEE80211_QOS_CONTROL_A_MSDU_PRESENT) + is_agg_frame = 1; + } else { + if (unlikely((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT)) { + /* Separate TID for management frames */ + tid = NUM_RX_DATA_QUEUES - 1; + } else { + /* no qos control present */ + tid = 0; /* 802.1d - Best Effort */ + } + rx->u.rx.qos_control = 0; + } +#ifdef CONFIG_MAC80211_DEBUG_COUNTERS + I802_DEBUG_INC(rx->local->wme_rx_queue[tid]); + if (rx->sta) { + I802_DEBUG_INC(rx->sta->wme_rx_queue[tid]); + } +#endif /* CONFIG_MAC80211_DEBUG_COUNTERS */ + + rx->u.rx.queue = tid; + rx->u.rx.is_agg_frame = is_agg_frame; + /* Set skb->priority to 1d tag if highest order bit of TID is not set. + * For now, set skb->priority to 0 for other cases. */ + rx->skb->priority = (tid > 7) ? 0 : tid; + + return TXRX_CONTINUE; +} + + +ieee80211_txrx_result +ieee80211_rx_h_remove_qos_control(struct ieee80211_txrx_data *rx) +{ + u16 fc = rx->fc; + u8 *data = rx->skb->data; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) data; + + if (!WLAN_FC_IS_QOS_DATA(fc)) + return TXRX_CONTINUE; + + /* remove the qos control field, update frame type and meta-data */ + memmove(data + 2, data, ieee80211_get_hdrlen(fc) - 2); + hdr = (struct ieee80211_hdr *) skb_pull(rx->skb, 2); + /* change frame type to non QOS */ + rx->fc = fc &= ~IEEE80211_STYPE_QOS_DATA; + hdr->frame_control = cpu_to_le16(fc); + + return TXRX_CONTINUE; +} + + +#ifdef CONFIG_NET_SCHED +/* maximum number of hardware queues we support. */ +#define TC_80211_MAX_QUEUES 8 + +struct ieee80211_sched_data +{ + struct tcf_proto *filter_list; + struct Qdisc *queues[TC_80211_MAX_QUEUES]; + struct sk_buff_head requeued[TC_80211_MAX_QUEUES]; +}; + + +/* given a data frame determine the 802.1p/1d tag to use */ +static inline unsigned classify_1d(struct sk_buff *skb, struct Qdisc *qd) +{ + struct iphdr *ip; + int dscp; + int offset; + + struct ieee80211_sched_data *q = qdisc_priv(qd); + struct tcf_result res = { -1, 0 }; + + /* if there is a user set filter list, call out to that */ + if (q->filter_list) { + tc_classify(skb, q->filter_list, &res); + if (res.class != -1) + return res.class; + } + + /* skb->priority values from 256->263 are magic values to + * directly indicate a specific 802.1d priority. + * This is used to allow 802.1d priority to be passed directly in + * from VLAN tags, etc. */ + if (skb->priority >= 256 && skb->priority <= 263) + return skb->priority - 256; + + /* check there is a valid IP header present */ + offset = ieee80211_get_hdrlen_from_skb(skb) + 8 /* LLC + proto */; + if (skb->protocol != __constant_htons(ETH_P_IP) || + skb->len < offset + sizeof(*ip)) + return 0; + + ip = (struct iphdr *) (skb->data + offset); + + dscp = ip->tos & 0xfc; + if (dscp & 0x1c) + return 0; + return dscp >> 5; +} + + +static inline int wme_downgrade_ac(struct sk_buff *skb) +{ + switch (skb->priority) { + case 6: + case 7: + skb->priority = 5; /* VO -> VI */ + return 0; + case 4: + case 5: + skb->priority = 3; /* VI -> BE */ + return 0; + case 0: + case 3: + skb->priority = 2; /* BE -> BK */ + return 0; + default: + return -1; + } +} + + +/* positive return value indicates which queue to use + * negative return value indicates to drop the frame */ +static inline int classify80211(struct sk_buff *skb, struct Qdisc *qd) +{ + struct ieee80211_local *local = wdev_priv(qd->dev->ieee80211_ptr); + struct ieee80211_tx_packet_data *pkt_data = + (struct ieee80211_tx_packet_data *) skb->cb; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + unsigned short fc = le16_to_cpu(hdr->frame_control); + int qos; + const int ieee802_1d_to_ac[8] = { 2, 3, 3, 2, 1, 1, 0, 0 }; + + /* see if frame is data or non data frame */ + if (unlikely((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA)) { + /* management frames go on AC_VO queue, but are sent + * without QoS control fields */ + return IEEE80211_TX_QUEUE_DATA0; + } + + if (unlikely(pkt_data->mgmt_iface)) { + /* Data frames from hostapd (mainly, EAPOL) use AC_VO + * and they will include QoS control fields if + * the target STA is using WME. */ + skb->priority = 7; + return ieee802_1d_to_ac[skb->priority]; + } + + /* is this a QoS frame? */ + qos = fc & IEEE80211_STYPE_QOS_DATA; + + if (!qos) { + skb->priority = 0; /* required for correct WPA/11i MIC */ + return ieee802_1d_to_ac[skb->priority]; + } + + /* use the data classifier to determine what 802.1d tag the + * data frame has */ + skb->priority = classify_1d(skb, qd); + + /* incase we are a client verify acm is not set for this ac */ + while (unlikely(local->wmm_acm & BIT(skb->priority))) { + if (wme_downgrade_ac(skb)) { + /* No AC with lower priority has acm=0, + * drop packet. */ + return -1; + } + } + + /* look up which queue to use for frames with this 1d tag */ + return ieee802_1d_to_ac[skb->priority]; +} + + +static int wme_qdiscop_enqueue(struct sk_buff *skb, struct Qdisc* qd) +{ + struct ieee80211_local *local = wdev_priv(qd->dev->ieee80211_ptr); + struct ieee80211_sched_data *q = qdisc_priv(qd); + struct ieee80211_tx_packet_data *pkt_data = + (struct ieee80211_tx_packet_data *) skb->cb; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + unsigned short fc = le16_to_cpu(hdr->frame_control); + struct Qdisc *qdisc; + int err, queue; + + if (pkt_data->requeue) { + skb_queue_tail(&q->requeued[pkt_data->queue], skb); + qd->q.qlen++; + return 0; + } + + queue = classify80211(skb, qd); + + /* now we know the 1d priority, fill in the QoS header if there is one + */ + if (WLAN_FC_IS_QOS_DATA(fc)) { + u8 *p = skb->data + ieee80211_get_hdrlen(fc) - 2; + u8 qos_hdr = skb->priority & QOS_CONTROL_TAG1D_MASK; + if (local->wifi_wme_noack_test) + qos_hdr |= QOS_CONTROL_ACK_POLICY_NOACK << + QOS_CONTROL_ACK_POLICY_SHIFT; + /* qos header is 2 bytes, second reserved */ + *p = qos_hdr; + p++; + *p = 0; + } + + if (unlikely(queue >= local->hw.queues)) { +#if 0 + if (net_ratelimit()) { + printk(KERN_DEBUG "%s - queue=%d (hw does not " + "support) -> %d\n", + __func__, queue, local->hw.queues - 1); + } +#endif + queue = local->hw.queues - 1; + } + + if (unlikely(queue < 0)) { + kfree_skb(skb); + err = NET_XMIT_DROP; + } else { + pkt_data->queue = (unsigned int) queue; + qdisc = q->queues[queue]; + err = qdisc->enqueue(skb, qdisc); + if (err == NET_XMIT_SUCCESS) { + qd->q.qlen++; + qd->bstats.bytes += skb->len; + qd->bstats.packets++; + return NET_XMIT_SUCCESS; + } + } + qd->qstats.drops++; + return err; +} + + +/* TODO: clean up the cases where master_hard_start_xmit + * returns non 0 - it shouldn't ever do that. Once done we + * can remove this function */ +static int wme_qdiscop_requeue(struct sk_buff *skb, struct Qdisc* qd) +{ + struct ieee80211_sched_data *q = qdisc_priv(qd); + struct ieee80211_tx_packet_data *pkt_data = + (struct ieee80211_tx_packet_data *) skb->cb; + struct Qdisc *qdisc; + int err; + + /* we recorded which queue to use earlier! */ + qdisc = q->queues[pkt_data->queue]; + + if ((err = qdisc->ops->requeue(skb, qdisc)) == 0) { + qd->q.qlen++; + return 0; + } + qd->qstats.drops++; + return err; +} + + +static struct sk_buff *wme_qdiscop_dequeue(struct Qdisc* qd) +{ + struct ieee80211_sched_data *q = qdisc_priv(qd); + struct net_device *dev = qd->dev; + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_hw *hw = &local->hw; + struct sk_buff *skb; + struct Qdisc *qdisc; + int queue; + + /* check all the h/w queues in numeric/priority order */ + for (queue = 0; queue < hw->queues; queue++) { + /* see if there is room in this hardware queue */ + if (test_bit(IEEE80211_LINK_STATE_XOFF, + &local->state[queue]) || + test_bit(IEEE80211_LINK_STATE_PENDING, + &local->state[queue])) + continue; + + /* there is space - try and get a frame */ + skb = skb_dequeue(&q->requeued[queue]); + if (skb) { + qd->q.qlen--; + return skb; + } + + qdisc = q->queues[queue]; + skb = qdisc->dequeue(qdisc); + if (skb) { + qd->q.qlen--; + return skb; + } + } + /* returning a NULL here when all the h/w queues are full means we + * never need to call netif_stop_queue in the driver */ + return NULL; +} + + +static void wme_qdiscop_reset(struct Qdisc* qd) +{ + struct ieee80211_sched_data *q = qdisc_priv(qd); + struct ieee80211_local *local = wdev_priv(qd->dev->ieee80211_ptr); + struct ieee80211_hw *hw = &local->hw; + int queue; + + /* QUESTION: should we have some hardware flush functionality here? */ + + for (queue = 0; queue < hw->queues; queue++) { + skb_queue_purge(&q->requeued[queue]); + qdisc_reset(q->queues[queue]); + } + qd->q.qlen = 0; +} + + +static void wme_qdiscop_destroy(struct Qdisc* qd) +{ + struct ieee80211_sched_data *q = qdisc_priv(qd); + struct ieee80211_local *local = wdev_priv(qd->dev->ieee80211_ptr); + struct ieee80211_hw *hw = &local->hw; + int queue; + + tcf_destroy_chain(q->filter_list); + q->filter_list = NULL; + + for (queue=0; queue < hw->queues; queue++) { + skb_queue_purge(&q->requeued[queue]); + qdisc_destroy(q->queues[queue]); + q->queues[queue] = &noop_qdisc; + } +} + + +/* called whenever parameters are updated on existing qdisc */ +static int wme_qdiscop_tune(struct Qdisc *qd, struct rtattr *opt) +{ +/* struct ieee80211_sched_data *q = qdisc_priv(qd); +*/ + /* check our options block is the right size */ + /* copy any options to our local structure */ +/* Ignore options block for now - always use static mapping + struct tc_ieee80211_qopt *qopt = RTA_DATA(opt); + + if (opt->rta_len < RTA_LENGTH(sizeof(*qopt))) + return -EINVAL; + memcpy(q->tag2queue, qopt->tag2queue, sizeof(qopt->tag2queue)); +*/ + return 0; +} + + +/* called during initial creation of qdisc on device */ +static int wme_qdiscop_init(struct Qdisc *qd, struct rtattr *opt) +{ + struct ieee80211_sched_data *q = qdisc_priv(qd); + struct net_device *dev = qd->dev; + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + int queues = local->hw.queues; + int err = 0, i; + + /* check this device is an ieee80211 master type device */ + if (dev->type != ARPHRD_IEEE80211) + return -EINVAL; + + /* check that there is no qdisc currently attached to device + * this ensures that we will be the root qdisc. (I can't find a better + * way to test this explicitly) */ + if (dev->qdisc_sleeping != &noop_qdisc) + return -EINVAL; + + if (qd->flags & TCQ_F_INGRESS) + return -EINVAL; + + /* if options were passed in, set them */ + if (opt) { + err = wme_qdiscop_tune(qd, opt); + } + + /* create child queues */ + for (i = 0; i < queues; i++) { + skb_queue_head_init(&q->requeued[i]); + q->queues[i] = qdisc_create_dflt(qd->dev, &pfifo_qdisc_ops, + qd->handle); + if (q->queues[i] == 0) { + q->queues[i] = &noop_qdisc; + printk(KERN_ERR "%s child qdisc %i creation failed", dev->name, i); + } + } + + return err; +} + +static int wme_qdiscop_dump(struct Qdisc *qd, struct sk_buff *skb) +{ +/* struct ieee80211_sched_data *q = qdisc_priv(qd); + unsigned char *p = skb->tail; + struct tc_ieee80211_qopt opt; + + memcpy(&opt.tag2queue, q->tag2queue, TC_80211_MAX_TAG + 1); + RTA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt); +*/ return skb->len; +/* +rtattr_failure: + skb_trim(skb, p - skb->data);*/ + return -1; +} + + +static int wme_classop_graft(struct Qdisc *qd, unsigned long arg, + struct Qdisc *new, struct Qdisc **old) +{ + struct ieee80211_sched_data *q = qdisc_priv(qd); + struct ieee80211_local *local = wdev_priv(qd->dev->ieee80211_ptr); + struct ieee80211_hw *hw = &local->hw; + unsigned long queue = arg - 1; + + if (queue >= hw->queues) + return -EINVAL; + + if (!new) + new = &noop_qdisc; + + sch_tree_lock(qd); + *old = q->queues[queue]; + q->queues[queue] = new; + qdisc_reset(*old); + sch_tree_unlock(qd); + + return 0; +} + + +static struct Qdisc * +wme_classop_leaf(struct Qdisc *qd, unsigned long arg) +{ + struct ieee80211_sched_data *q = qdisc_priv(qd); + struct ieee80211_local *local = wdev_priv(qd->dev->ieee80211_ptr); + struct ieee80211_hw *hw = &local->hw; + unsigned long queue = arg - 1; + + if (queue >= hw->queues) + return NULL; + + return q->queues[queue]; +} + + +static unsigned long wme_classop_get(struct Qdisc *qd, u32 classid) +{ + struct ieee80211_local *local = wdev_priv(qd->dev->ieee80211_ptr); + struct ieee80211_hw *hw = &local->hw; + unsigned long queue = TC_H_MIN(classid); + + if (queue - 1 >= hw->queues) + return 0; + + return queue; +} + + +static unsigned long wme_classop_bind(struct Qdisc *qd, unsigned long parent, + u32 classid) +{ + return wme_classop_get(qd, classid); +} + + +static void wme_classop_put(struct Qdisc *q, unsigned long cl) +{ +} + + +static int wme_classop_change(struct Qdisc *qd, u32 handle, u32 parent, + struct rtattr **tca, unsigned long *arg) +{ + unsigned long cl = *arg; + struct ieee80211_local *local = wdev_priv(qd->dev->ieee80211_ptr); + struct ieee80211_hw *hw = &local->hw; + + if (cl - 1 > hw->queues) + return -ENOENT; + + /* TODO: put code to program hardware queue parameters here, + * to allow programming from tc command line */ + + return 0; +} + + +/* we don't support deleting hardware queues + * when we add WMM-SA support - TSPECs may be deleted here */ +static int wme_classop_delete(struct Qdisc *qd, unsigned long cl) +{ + struct ieee80211_local *local = wdev_priv(qd->dev->ieee80211_ptr); + struct ieee80211_hw *hw = &local->hw; + + if (cl - 1 > hw->queues) + return -ENOENT; + return 0; +} + + +static int wme_classop_dump_class(struct Qdisc *qd, unsigned long cl, + struct sk_buff *skb, struct tcmsg *tcm) +{ + struct ieee80211_sched_data *q = qdisc_priv(qd); + struct ieee80211_local *local = wdev_priv(qd->dev->ieee80211_ptr); + struct ieee80211_hw *hw = &local->hw; + + if (cl - 1 > hw->queues) + return -ENOENT; + tcm->tcm_handle = TC_H_MIN(cl); + tcm->tcm_parent = qd->handle; + tcm->tcm_info = q->queues[cl-1]->handle; /* do we need this? */ + return 0; +} + + +static void wme_classop_walk(struct Qdisc *qd, struct qdisc_walker *arg) +{ + struct ieee80211_local *local = wdev_priv(qd->dev->ieee80211_ptr); + struct ieee80211_hw *hw = &local->hw; + int queue; + + if (arg->stop) + return; + + for (queue = 0; queue < hw->queues; queue++) { + if (arg->count < arg->skip) { + arg->count++; + continue; + } + /* we should return classids for our internal queues here + * as well as the external ones */ + if (arg->fn(qd, queue+1, arg) < 0) { + arg->stop = 1; + break; + } + arg->count++; + } +} + + +static struct tcf_proto ** wme_classop_find_tcf(struct Qdisc *qd, + unsigned long cl) +{ + struct ieee80211_sched_data *q = qdisc_priv(qd); + + if (cl) + return NULL; + + return &q->filter_list; +} + + +/* this qdisc is classful (i.e. has classes, some of which may have leaf qdiscs attached) + * - these are the operations on the classes */ +static struct Qdisc_class_ops class_ops = +{ + .graft = wme_classop_graft, + .leaf = wme_classop_leaf, + + .get = wme_classop_get, + .put = wme_classop_put, + .change = wme_classop_change, + .delete = wme_classop_delete, + .walk = wme_classop_walk, + + .tcf_chain = wme_classop_find_tcf, + .bind_tcf = wme_classop_bind, + .unbind_tcf = wme_classop_put, + + .dump = wme_classop_dump_class, +}; + + +/* queueing discipline operations */ +static struct Qdisc_ops wme_qdisc_ops = +{ + .next = NULL, + .cl_ops = &class_ops, + .id = "ieee80211", + .priv_size = sizeof(struct ieee80211_sched_data), + + .enqueue = wme_qdiscop_enqueue, + .dequeue = wme_qdiscop_dequeue, + .requeue = wme_qdiscop_requeue, + .drop = NULL, /* drop not needed since we are always the root qdisc */ + + .init = wme_qdiscop_init, + .reset = wme_qdiscop_reset, + .destroy = wme_qdiscop_destroy, + .change = wme_qdiscop_tune, + + .dump = wme_qdiscop_dump, +}; + + +void ieee80211_install_qdisc(struct net_device *dev) +{ + struct Qdisc *qdisc; + + qdisc = qdisc_create_dflt(dev, &wme_qdisc_ops, TC_H_ROOT); + if (!qdisc) { + printk(KERN_ERR "%s: qdisc installation failed\n", dev->name); + return; + } + + /* same handle as would be allocated by qdisc_alloc_handle() */ + qdisc->handle = 0x80010000; + + qdisc_lock_tree(dev); + list_add_tail(&qdisc->list, &dev->qdisc_list); + dev->qdisc_sleeping = qdisc; + qdisc_unlock_tree(dev); +} + + +int ieee80211_qdisc_installed(struct net_device *dev) +{ + return dev->qdisc_sleeping->ops == &wme_qdisc_ops; +} + + +int ieee80211_wme_register(void) +{ + return register_qdisc(&wme_qdisc_ops); +} + + +void ieee80211_wme_unregister(void) +{ + unregister_qdisc(&wme_qdisc_ops); +} +#endif /* CONFIG_NET_SCHED */ diff --git a/net/mac80211/wme.h b/net/mac80211/wme.h new file mode 100644 index 0000000..d65945b --- /dev/null +++ b/net/mac80211/wme.h @@ -0,0 +1,57 @@ +/* + * IEEE 802.11 driver (80211.o) - QoS datatypes + * Copyright 2004, Instant802 Networks, Inc. + * Copyright 2005, Devicescape Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _WME_H +#define _WME_H + +#include +#include "ieee80211_i.h" + +#define QOS_CONTROL_LEN 2 + +#define QOS_CONTROL_ACK_POLICY_NORMAL 0 +#define QOS_CONTROL_ACK_POLICY_NOACK 1 + +#define QOS_CONTROL_TID_MASK 0x0f +#define QOS_CONTROL_ACK_POLICY_SHIFT 5 + +#define QOS_CONTROL_TAG1D_MASK 0x07 + +ieee80211_txrx_result +ieee80211_rx_h_parse_qos(struct ieee80211_txrx_data *rx); + +ieee80211_txrx_result +ieee80211_rx_h_remove_qos_control(struct ieee80211_txrx_data *rx); + +#ifdef CONFIG_NET_SCHED +void ieee80211_install_qdisc(struct net_device *dev); +int ieee80211_qdisc_installed(struct net_device *dev); + +int ieee80211_wme_register(void); +void ieee80211_wme_unregister(void); +#else +static inline void ieee80211_install_qdisc(struct net_device *dev) +{ +} +static inline int ieee80211_qdisc_installed(struct net_device *dev) +{ + return 1; +} + +static inline int ieee80211_wme_register(void) +{ + return 0; +} +static inline void ieee80211_wme_unregister(void) +{ +} +#endif /* CONFIG_NET_SCHED */ + +#endif /* _WME_H */ diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c new file mode 100644 index 0000000..2806886 --- /dev/null +++ b/net/mac80211/wpa.c @@ -0,0 +1,846 @@ +/* + * Copyright 2002-2004, Instant802 Networks, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include "ieee80211_common.h" +#include "ieee80211_i.h" +#include "michael.h" +#include "tkip.h" +#include "aes_ccm.h" +#include "wpa.h" +#ifdef CONFIG_HOSTAPD_WPA_TESTING +#include "hostapd_ioctl.h" +#endif /* CONFIG_HOSTAPD_WPA_TESTING */ + + +static int ieee80211_get_hdr_info(const struct sk_buff *skb, u8 **sa, u8 **da, + u8 *qos_tid, u8 **data, size_t *data_len) +{ + struct ieee80211_hdr *hdr; + size_t hdrlen; + u16 fc; + int a4_included; + u8 *pos; + + hdr = (struct ieee80211_hdr *) skb->data; + fc = le16_to_cpu(hdr->frame_control); + + hdrlen = 24; + if ((fc & (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) == + (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) { + hdrlen += ETH_ALEN; + *sa = hdr->addr4; + *da = hdr->addr3; + } else if (fc & IEEE80211_FCTL_FROMDS) { + *sa = hdr->addr3; + *da = hdr->addr1; + } else if (fc & IEEE80211_FCTL_TODS) { + *sa = hdr->addr2; + *da = hdr->addr3; + } else { + *sa = hdr->addr2; + *da = hdr->addr1; + } + + if (fc & 0x80) + hdrlen += 2; + + *data = skb->data + hdrlen; + *data_len = skb->len - hdrlen; + + a4_included = (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == + (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS); + if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA && + fc & IEEE80211_STYPE_QOS_DATA) { + pos = (u8 *) &hdr->addr4; + if (a4_included) + pos += 6; + *qos_tid = pos[0] & 0x0f; + *qos_tid |= 0x80; /* qos_included flag */ + } else + *qos_tid = 0; + + return skb->len < hdrlen ? -1 : 0; +} + + +ieee80211_txrx_result +ieee80211_tx_h_michael_mic_add(struct ieee80211_txrx_data *tx) +{ + u8 *data, *sa, *da, *key, *mic, qos_tid; + size_t data_len; + u16 fc; + struct sk_buff *skb = tx->skb; + int authenticator; + int wpa_test = 0; + + fc = tx->fc; + + if (!tx->key || tx->key->alg != ALG_TKIP || skb->len < 24 || + !WLAN_FC_DATA_PRESENT(fc)) + return TXRX_CONTINUE; + + if (ieee80211_get_hdr_info(skb, &sa, &da, &qos_tid, &data, &data_len)) + return TXRX_DROP; + +#ifdef CONFIG_HOSTAPD_WPA_TESTING + if ((tx->sta && tx->sta->wpa_trigger & WPA_TRIGGER_FAIL_TX_MIC) || + (!tx->u.tx.unicast && + tx->local->wpa_trigger & WPA_TRIGGER_FAIL_TX_MIC)) { + wpa_test = 1; + } +#endif /* CONFIG_HOSTAPD_WPA_TESTING */ + + if (!tx->key->force_sw_encrypt && + !tx->fragmented && + !(tx->local->hw.flags & IEEE80211_HW_TKIP_INCLUDE_MMIC) && + !wpa_test) { + /* hwaccel - with no need for preallocated room for Michael MIC + */ + return TXRX_CONTINUE; + } + + if (skb_tailroom(skb) < MICHAEL_MIC_LEN) { + I802_DEBUG_INC(tx->local->tx_expand_skb_head); + if (unlikely(pskb_expand_head(skb, TKIP_IV_LEN, + MICHAEL_MIC_LEN + TKIP_ICV_LEN, + GFP_ATOMIC))) { + printk(KERN_DEBUG "%s: failed to allocate more memory " + "for Michael MIC\n", tx->dev->name); + return TXRX_DROP; + } + } + +#if 0 + authenticator = fc & IEEE80211_FCTL_FROMDS; /* FIX */ +#else + authenticator = 1; +#endif + key = &tx->key->key[authenticator ? ALG_TKIP_TEMP_AUTH_TX_MIC_KEY : + ALG_TKIP_TEMP_AUTH_RX_MIC_KEY]; + mic = skb_put(skb, MICHAEL_MIC_LEN); + michael_mic(key, da, sa, qos_tid & 0x0f, data, data_len, mic); + +#ifdef CONFIG_HOSTAPD_WPA_TESTING + if (tx->sta && tx->sta->wpa_trigger & WPA_TRIGGER_FAIL_TX_MIC) { + printk(KERN_INFO "%s: WPA testing - corrupting TX Michael MIC " + "for STA " MAC_FMT "\n", + tx->dev->name, MAC_ARG(tx->sta->addr)); + tx->u.tx.control->key_idx = HW_KEY_IDX_INVALID; + tx->sta->wpa_trigger &= ~WPA_TRIGGER_FAIL_TX_MIC; + tx->wpa_test = 1; + mic[0]++; + } else if (!tx->u.tx.unicast && + tx->local->wpa_trigger & WPA_TRIGGER_FAIL_TX_MIC) { + printk(KERN_INFO "%s: WPA testing - corrupting TX Michael MIC " + "for Group Key\n", tx->dev->name); + tx->u.tx.control->key_idx = HW_KEY_IDX_INVALID; + tx->local->wpa_trigger &= ~WPA_TRIGGER_FAIL_TX_MIC; + tx->wpa_test = 1; + mic[0]++; + } +#endif /* CONFIG_HOSTAPD_WPA_TESTING */ + + return TXRX_CONTINUE; +} + + +ieee80211_txrx_result +ieee80211_rx_h_michael_mic_verify(struct ieee80211_txrx_data *rx) +{ + u8 *data, *sa, *da, *key = NULL, qos_tid; + size_t data_len; + u16 fc; + u8 mic[MICHAEL_MIC_LEN]; + struct sk_buff *skb = rx->skb; + int authenticator = 1, wpa_test = 0; + + fc = rx->fc; + + /* If device handles decryption totally, skip this check */ + if ((rx->local->hw.flags & IEEE80211_HW_DEVICE_HIDES_WEP) || + (rx->local->hw.flags & IEEE80211_HW_DEVICE_STRIPS_MIC)) + return TXRX_CONTINUE; + + if (!rx->key || rx->key->alg != ALG_TKIP || + !(rx->fc & IEEE80211_FCTL_PROTECTED) || !WLAN_FC_DATA_PRESENT(fc)) + return TXRX_CONTINUE; + +#ifdef CONFIG_HOSTAPD_WPA_TESTING + if (rx->sta && rx->sta->wpa_trigger & WPA_TRIGGER_FAIL_RX_MIC) { + wpa_test = 1; + } +#endif /* CONFIG_HOSTAPD_WPA_TESTING */ + + if ((rx->u.rx.status->flag & RX_FLAG_DECRYPTED) && + !rx->key->force_sw_encrypt) { + if (rx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV) { + if (skb->len < MICHAEL_MIC_LEN) + return TXRX_DROP; + } + /* Need to verify Michael MIC sometimes in software even when + * hwaccel is used. Atheros ar5212: fragmented frames and QoS + * frames. */ + if (!rx->fragmented && !wpa_test) + goto remove_mic; + } + + if (ieee80211_get_hdr_info(skb, &sa, &da, &qos_tid, &data, &data_len) + || data_len < MICHAEL_MIC_LEN) + return TXRX_DROP; + + data_len -= MICHAEL_MIC_LEN; + +#if 0 + authenticator = fc & IEEE80211_FCTL_TODS; /* FIX */ +#else + authenticator = 1; +#endif + key = &rx->key->key[authenticator ? ALG_TKIP_TEMP_AUTH_RX_MIC_KEY : + ALG_TKIP_TEMP_AUTH_TX_MIC_KEY]; + michael_mic(key, da, sa, qos_tid & 0x0f, data, data_len, mic); +#ifdef CONFIG_HOSTAPD_WPA_TESTING + if (rx->sta && rx->sta->wpa_trigger & WPA_TRIGGER_FAIL_RX_MIC) { + printk(KERN_INFO "%s: WPA testing - corrupting RX Michael MIC " + "for STA " MAC_FMT "\n", + rx->dev->name, MAC_ARG(rx->sta->addr)); + rx->sta->wpa_trigger &= ~WPA_TRIGGER_FAIL_RX_MIC; + mic[0]++; + } +#endif /* CONFIG_HOSTAPD_WPA_TESTING */ + if (memcmp(mic, data + data_len, MICHAEL_MIC_LEN) != 0 || wpa_test) { +#ifdef CONFIG_HOSTAPD_WPA_TESTING + int i; +#endif /* CONFIG_HOSTAPD_WPA_TESTING */ + + if (!rx->u.rx.ra_match) + return TXRX_DROP; + + printk(KERN_DEBUG "%s: invalid Michael MIC in data frame from " + MAC_FMT "\n", rx->dev->name, MAC_ARG(sa)); +#ifdef CONFIG_HOSTAPD_WPA_TESTING + printk(KERN_DEBUG " received"); + for (i = 0; i < MICHAEL_MIC_LEN; i++) + printk(" %02x", data[data_len + i]); + printk(" expected"); + for (i = 0; i < MICHAEL_MIC_LEN; i++) + printk(" %02x", mic[i]); + printk("\n"); + printk(KERN_DEBUG " SA=" MAC_FMT " DA=" MAC_FMT " key", + MAC_ARG(sa), MAC_ARG(da)); + for (i = 0; i < 8; i++) + printk(" %02x", key[i]); + printk(" (%d)\n", authenticator); +#endif /* CONFIG_HOSTAPD_WPA_TESTING */ + + do { + struct ieee80211_hdr *hdr; + union iwreq_data wrqu; + char *buf = kmalloc(128, GFP_ATOMIC); + if (!buf) + break; + + /* TODO: needed parameters: count, key type, TSC */ + hdr = (struct ieee80211_hdr *) skb->data; + sprintf(buf, "MLME-MICHAELMICFAILURE.indication(" + "keyid=%d %scast addr=" MAC_FMT ")", + rx->key->keyidx, + hdr->addr1[0] & 0x01 ? "broad" : "uni", + MAC_ARG(hdr->addr2)); + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = strlen(buf); + wireless_send_event(rx->dev, IWEVCUSTOM, &wrqu, buf); + kfree(buf); + } while (0); + + if (!rx->local->apdev) + return TXRX_DROP; + + ieee80211_rx_mgmt(rx->local, rx->skb, rx->u.rx.status, + ieee80211_msg_michael_mic_failure); + + return TXRX_QUEUED; + } + + remove_mic: + /* remove Michael MIC from payload */ + skb_trim(skb, skb->len - MICHAEL_MIC_LEN); + + return TXRX_CONTINUE; +} + + +static int tkip_encrypt_skb(struct ieee80211_txrx_data *tx, + struct sk_buff *skb, int test) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + struct ieee80211_key *key = tx->key; + int hdrlen, len, tailneed; + u16 fc; + u8 *pos; + + fc = le16_to_cpu(hdr->frame_control); + hdrlen = ieee80211_get_hdrlen(fc); + len = skb->len - hdrlen; + + tailneed = !tx->key->force_sw_encrypt ? 0 : TKIP_ICV_LEN; + if ((skb_headroom(skb) < TKIP_IV_LEN || + skb_tailroom(skb) < tailneed)) { + I802_DEBUG_INC(tx->local->tx_expand_skb_head); + if (unlikely(pskb_expand_head(skb, TKIP_IV_LEN, tailneed, + GFP_ATOMIC))) + return -1; + } + + pos = skb_push(skb, TKIP_IV_LEN); + memmove(pos, pos + TKIP_IV_LEN, hdrlen); + pos += hdrlen; + +#ifdef CONFIG_HOSTAPD_WPA_TESTING + if (test & WPA_TRIGGER_TX_REPLAY) + goto skip_iv_inc; +iv_inc: +#endif /* CONFIG_HOSTAPD_WPA_TESTING */ + + /* Increase IV for the frame */ + key->u.tkip.iv16++; + if (key->u.tkip.iv16 == 0) + key->u.tkip.iv32++; + +#ifdef CONFIG_HOSTAPD_WPA_TESTING + if (test & WPA_TRIGGER_TX_SKIP_SEQ) { + test = 0; + goto iv_inc; + } +skip_iv_inc: +#endif /* CONFIG_HOSTAPD_WPA_TESTING */ + + if (!tx->key->force_sw_encrypt +#ifdef CONFIG_HOSTAPD_WPA_TESTING + && !tx->wpa_test +#endif /* CONFIG_HOSTAPD_WPA_TESTING */ + ) { + u32 flags = tx->local->hw.flags; + hdr = (struct ieee80211_hdr *)skb->data; + + /* hwaccel - with preallocated room for IV */ + ieee80211_tkip_add_iv(pos, key, + (u8) (key->u.tkip.iv16 >> 8), + (u8) (((key->u.tkip.iv16 >> 8) | 0x20) & + 0x7f), + (u8) key->u.tkip.iv16); + + if (flags & IEEE80211_HW_TKIP_REQ_PHASE2_KEY) + ieee80211_tkip_gen_rc4key(key, hdr->addr2, + tx->u.tx.control->tkip_key); + else if (flags & IEEE80211_HW_TKIP_REQ_PHASE1_KEY) { + if (key->u.tkip.iv16 == 0 || + !key->u.tkip.tx_initialized) { + ieee80211_tkip_gen_phase1key(key, hdr->addr2, + (u16 *)tx->u.tx.control->tkip_key); + key->u.tkip.tx_initialized = 1; + tx->u.tx.control->flags |= + IEEE80211_TXCTL_TKIP_NEW_PHASE1_KEY; + } else + tx->u.tx.control->flags &= + ~IEEE80211_TXCTL_TKIP_NEW_PHASE1_KEY; + } + + tx->u.tx.control->key_idx = tx->key->hw_key_idx; + return 0; + } + + /* Add room for ICV */ + skb_put(skb, TKIP_ICV_LEN); + + hdr = (struct ieee80211_hdr *) skb->data; + ieee80211_tkip_encrypt_data(tx->local->wep_tx_tfm, + key, pos, len, hdr->addr2); + return 0; +} + + +ieee80211_txrx_result +ieee80211_tx_h_tkip_encrypt(struct ieee80211_txrx_data *tx) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data; + u16 fc; + struct ieee80211_key *key = tx->key; + struct sk_buff *skb = tx->skb; + int wpa_test = 0, test = 0; + + fc = le16_to_cpu(hdr->frame_control); + + if (!key || key->alg != ALG_TKIP || !WLAN_FC_DATA_PRESENT(fc)) + return TXRX_CONTINUE; + + tx->u.tx.control->icv_len = TKIP_ICV_LEN; + tx->u.tx.control->iv_len = TKIP_IV_LEN; + ieee80211_tx_set_iswep(tx); + +#ifdef CONFIG_HOSTAPD_WPA_TESTING + if ((tx->sta && tx->sta->wpa_trigger & WPA_TRIGGER_FAIL_TX_ICV) || + (!tx->u.tx.unicast && + tx->local->wpa_trigger & WPA_TRIGGER_FAIL_TX_ICV)) { + wpa_test = 1; + } + + if (tx->sta) { + test = tx->sta->wpa_trigger; + tx->sta->wpa_trigger &= + ~(WPA_TRIGGER_TX_REPLAY | WPA_TRIGGER_TX_REPLAY_FRAG | + WPA_TRIGGER_TX_SKIP_SEQ); + } else { + test = tx->local->wpa_trigger; + tx->local->wpa_trigger &= + ~(WPA_TRIGGER_TX_REPLAY | WPA_TRIGGER_TX_REPLAY_FRAG | + WPA_TRIGGER_TX_SKIP_SEQ); + } + if (test & + (WPA_TRIGGER_TX_REPLAY | WPA_TRIGGER_TX_REPLAY_FRAG | + WPA_TRIGGER_TX_SKIP_SEQ)) { + printk(KERN_INFO "%s: WPA testing - TKIP TX packet number " + "%s%s%s%s\n", tx->dev->name, + tx->sta ? "[UNICAST]" : "[MULTICAST]", + test & WPA_TRIGGER_TX_REPLAY ? "[REPLAY]" : "", + test & WPA_TRIGGER_TX_REPLAY_FRAG ? + "[REPLAY FRAG]" : "", + test & WPA_TRIGGER_TX_SKIP_SEQ ? "[SKIP SEQ]" : ""); + } +#endif /* CONFIG_HOSTAPD_WPA_TESTING */ + + if (!tx->key->force_sw_encrypt && + !(tx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV) && + !wpa_test) { + /* hwaccel - with no need for preallocated room for IV/ICV */ + tx->u.tx.control->key_idx = tx->key->hw_key_idx; + return TXRX_CONTINUE; + } + + if (tkip_encrypt_skb(tx, skb, test) < 0) + return TXRX_DROP; + + if (tx->u.tx.extra_frag) { + int i; +#ifdef CONFIG_HOSTAPD_WPA_TESTING + if (test & WPA_TRIGGER_TX_REPLAY_FRAG) + test |= WPA_TRIGGER_TX_REPLAY; +#endif /* CONFIG_HOSTAPD_WPA_TESTING */ + for (i = 0; i < tx->u.tx.num_extra_frag; i++) { + if (tkip_encrypt_skb(tx, tx->u.tx.extra_frag[i], test) + < 0) + return TXRX_DROP; + } + } + +#ifdef CONFIG_HOSTAPD_WPA_TESTING + if (tx->sta && tx->sta->wpa_trigger & WPA_TRIGGER_FAIL_TX_ICV) { + printk(KERN_INFO "%s: WPA testing - corrupting TX TKIP ICV " + "for STA " MAC_FMT "\n", + tx->dev->name, MAC_ARG(tx->sta->addr)); + tx->u.tx.control->key_idx = HW_KEY_IDX_INVALID; + tx->sta->wpa_trigger &= ~WPA_TRIGGER_FAIL_TX_ICV; + skb->data[skb->len - 1]++; + } else if (!tx->u.tx.unicast && + tx->local->wpa_trigger & WPA_TRIGGER_FAIL_TX_ICV) { + printk(KERN_INFO "%s: WPA testing - corrupting TX TKIP ICV " + "for Group Key\n", + tx->dev->name); + tx->u.tx.control->key_idx = HW_KEY_IDX_INVALID; + tx->local->wpa_trigger &= ~WPA_TRIGGER_FAIL_TX_ICV; + skb->data[skb->len - 1]++; + } +#endif /* CONFIG_HOSTAPD_WPA_TESTING */ + + return TXRX_CONTINUE; +} + + +ieee80211_txrx_result +ieee80211_rx_h_tkip_decrypt(struct ieee80211_txrx_data *rx) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data; + u16 fc; + int hdrlen, res, hwaccel = 0, wpa_test = 0; + struct ieee80211_key *key = rx->key; + struct sk_buff *skb = rx->skb; + + fc = le16_to_cpu(hdr->frame_control); + hdrlen = ieee80211_get_hdrlen(fc); + + if (!rx->key || rx->key->alg != ALG_TKIP || + !(rx->fc & IEEE80211_FCTL_PROTECTED) || + (rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA) + return TXRX_CONTINUE; + + if (!rx->sta || skb->len - hdrlen < 12) + return TXRX_DROP; + +#ifdef CONFIG_HOSTAPD_WPA_TESTING + if (rx->sta && rx->sta->wpa_trigger & WPA_TRIGGER_FAIL_RX_ICV) { + printk(KERN_INFO "%s: WPA testing - corrupting RX TKIP ICV " + "for STA " MAC_FMT "\n", + rx->dev->name, MAC_ARG(rx->sta->addr)); + rx->sta->wpa_trigger &= ~WPA_TRIGGER_FAIL_RX_ICV; + skb->data[skb->len - 1]++; + wpa_test = 1; + } +#endif /* CONFIG_HOSTAPD_WPA_TESTING */ + + if ((rx->u.rx.status->flag & RX_FLAG_DECRYPTED) && + !rx->key->force_sw_encrypt) { + if (!(rx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV)) { + /* Hardware takes care of all processing, including + * replay protection, so no need to continue here. */ + return TXRX_CONTINUE; + } + + /* let TKIP code verify IV, but skip decryption */ + hwaccel = 1; + } + + res = ieee80211_tkip_decrypt_data(rx->local->wep_rx_tfm, + key, skb->data + hdrlen, + skb->len - hdrlen, rx->sta->addr, + hwaccel, rx->u.rx.queue); + if (res != TKIP_DECRYPT_OK || wpa_test) { + printk(KERN_DEBUG "%s: TKIP decrypt failed for RX frame from " + MAC_FMT " (res=%d)\n", + rx->dev->name, MAC_ARG(rx->sta->addr), res); + return TXRX_DROP; + } + + /* Trim ICV */ + skb_trim(skb, skb->len - TKIP_ICV_LEN); + + /* Remove IV */ + memmove(skb->data + TKIP_IV_LEN, skb->data, hdrlen); + skb_pull(skb, TKIP_IV_LEN); + + return TXRX_CONTINUE; +} + + +static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *b_0, u8 *aad, + int encrypted) +{ + u16 fc; + int a4_included, qos_included; + u8 qos_tid, *fc_pos, *data, *sa, *da; + int len_a; + size_t data_len; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + + fc_pos = (u8 *) &hdr->frame_control; + fc = fc_pos[0] ^ (fc_pos[1] << 8); + a4_included = (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == + (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS); + + ieee80211_get_hdr_info(skb, &sa, &da, &qos_tid, &data, &data_len); + data_len -= CCMP_HDR_LEN + (encrypted ? CCMP_MIC_LEN : 0); + if (qos_tid & 0x80) { + qos_included = 1; + qos_tid &= 0x0f; + } else + qos_included = 0; + /* First block, b_0 */ + + b_0[0] = 0x59; /* flags: Adata: 1, M: 011, L: 001 */ + /* Nonce: QoS Priority | A2 | PN */ + b_0[1] = qos_tid; + memcpy(&b_0[2], hdr->addr2, 6); + memcpy(&b_0[8], pn, CCMP_PN_LEN); + /* l(m) */ + b_0[14] = (data_len >> 8) & 0xff; + b_0[15] = data_len & 0xff; + + + /* AAD (extra authenticate-only data) / masked 802.11 header + * FC | A1 | A2 | A3 | SC | [A4] | [QC] */ + + len_a = a4_included ? 28 : 22; + if (qos_included) + len_a += 2; + + aad[0] = 0; /* (len_a >> 8) & 0xff; */ + aad[1] = len_a & 0xff; + /* Mask FC: zero subtype b4 b5 b6 */ + aad[2] = fc_pos[0] & ~(BIT(4) | BIT(5) | BIT(6)); + /* Retry, PwrMgt, MoreData; set Protected */ + aad[3] = (fc_pos[1] & ~(BIT(3) | BIT(4) | BIT(5))) | BIT(6); + memcpy(&aad[4], &hdr->addr1, 18); + + /* Mask Seq#, leave Frag# */ + aad[22] = *((u8 *) &hdr->seq_ctrl) & 0x0f; + aad[23] = 0; + if (a4_included) { + memcpy(&aad[24], hdr->addr4, 6); + aad[30] = 0; + aad[31] = 0; + } else + memset(&aad[24], 0, 8); + if (qos_included) { + u8 *dpos = &aad[a4_included ? 30 : 24]; + + /* Mask QoS Control field */ + dpos[0] = qos_tid; + dpos[1] = 0; + } +} + + +static inline void ccmp_pn2hdr(u8 *hdr, u8 *pn, int key_id) +{ + hdr[0] = pn[5]; + hdr[1] = pn[4]; + hdr[2] = 0; + hdr[3] = 0x20 | (key_id << 6); + hdr[4] = pn[3]; + hdr[5] = pn[2]; + hdr[6] = pn[1]; + hdr[7] = pn[0]; +} + + +static inline int ccmp_hdr2pn(u8 *pn, u8 *hdr) +{ + pn[0] = hdr[7]; + pn[1] = hdr[6]; + pn[2] = hdr[5]; + pn[3] = hdr[4]; + pn[4] = hdr[1]; + pn[5] = hdr[0]; + return (hdr[3] >> 6) & 0x03; +} + + +static int ccmp_encrypt_skb(struct ieee80211_txrx_data *tx, + struct sk_buff *skb, int test) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + struct ieee80211_key *key = tx->key; + int hdrlen, len, tailneed; + u16 fc; + u8 *pos, *pn, *b_0, *aad, *scratch; + int i; + + scratch = key->u.ccmp.tx_crypto_buf; + b_0 = scratch + 3 * AES_BLOCK_LEN; + aad = scratch + 4 * AES_BLOCK_LEN; + + fc = le16_to_cpu(hdr->frame_control); + hdrlen = ieee80211_get_hdrlen(fc); + len = skb->len - hdrlen; + + tailneed = !key->force_sw_encrypt ? 0 : CCMP_MIC_LEN; + + if ((skb_headroom(skb) < CCMP_HDR_LEN || + skb_tailroom(skb) < tailneed)) { + I802_DEBUG_INC(tx->local->tx_expand_skb_head); + if (unlikely(pskb_expand_head(skb, CCMP_HDR_LEN, tailneed, + GFP_ATOMIC))) + return -1; + } + + pos = skb_push(skb, CCMP_HDR_LEN); + memmove(pos, pos + CCMP_HDR_LEN, hdrlen); + hdr = (struct ieee80211_hdr *) pos; + pos += hdrlen; + + /* PN = PN + 1 */ + pn = key->u.ccmp.tx_pn; + +#ifdef CONFIG_HOSTAPD_WPA_TESTING + if (test & WPA_TRIGGER_TX_REPLAY) + goto skip_pn_inc; +pn_inc: +#endif /* CONFIG_HOSTAPD_WPA_TESTING */ + + for (i = CCMP_PN_LEN - 1; i >= 0; i--) { + pn[i]++; + if (pn[i]) + break; + } + +#ifdef CONFIG_HOSTAPD_WPA_TESTING + if (test & WPA_TRIGGER_TX_SKIP_SEQ) { + test = 0; + goto pn_inc; + } +skip_pn_inc: +#endif /* CONFIG_HOSTAPD_WPA_TESTING */ + + ccmp_pn2hdr(pos, pn, key->keyidx); + + if (!key->force_sw_encrypt) { + /* hwaccel - with preallocated room for CCMP header */ + tx->u.tx.control->key_idx = key->hw_key_idx; + return 0; + } + + pos += CCMP_HDR_LEN; + ccmp_special_blocks(skb, pn, b_0, aad, 0); + ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, scratch, b_0, aad, pos, len, + pos, skb_put(skb, CCMP_MIC_LEN)); + + return 0; +} + + +ieee80211_txrx_result +ieee80211_tx_h_ccmp_encrypt(struct ieee80211_txrx_data *tx) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data; + struct ieee80211_key *key = tx->key; + u16 fc; + struct sk_buff *skb = tx->skb; + int test = 0; + + fc = le16_to_cpu(hdr->frame_control); + + if (!key || key->alg != ALG_CCMP || !WLAN_FC_DATA_PRESENT(fc)) + return TXRX_CONTINUE; + +#ifdef CONFIG_HOSTAPD_WPA_TESTING + if (tx->sta) { + test = tx->sta->wpa_trigger; + tx->sta->wpa_trigger = 0; + } else { + test = tx->local->wpa_trigger; + tx->local->wpa_trigger = 0; + } + if (test & + (WPA_TRIGGER_TX_REPLAY | WPA_TRIGGER_TX_REPLAY_FRAG | + WPA_TRIGGER_TX_SKIP_SEQ)) { + printk(KERN_INFO "%s: WPA testing - CCMP TX packet number " + "%s%s%s%s\n", tx->dev->name, + tx->sta ? "[UNICAST]" : "[MULTICAST]", + test & WPA_TRIGGER_TX_REPLAY ? "[REPLAY]" : "", + test & WPA_TRIGGER_TX_REPLAY_FRAG ? + "[REPLAY FRAG]" : "", + test & WPA_TRIGGER_TX_SKIP_SEQ ? "[SKIP SEQ]" : ""); + } +#endif /* CONFIG_HOSTAPD_WPA_TESTING */ + + tx->u.tx.control->icv_len = CCMP_MIC_LEN; + tx->u.tx.control->iv_len = CCMP_HDR_LEN; + ieee80211_tx_set_iswep(tx); + + if (!tx->key->force_sw_encrypt && + !(tx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV)) { + /* hwaccel - with no need for preallocated room for CCMP " + * header or MIC fields */ + tx->u.tx.control->key_idx = tx->key->hw_key_idx; + return TXRX_CONTINUE; + } + + if (ccmp_encrypt_skb(tx, skb, test) < 0) + return TXRX_DROP; + + if (tx->u.tx.extra_frag) { + int i; +#ifdef CONFIG_HOSTAPD_WPA_TESTING + if (test & WPA_TRIGGER_TX_REPLAY_FRAG) + test |= WPA_TRIGGER_TX_REPLAY; +#endif /* CONFIG_HOSTAPD_WPA_TESTING */ + for (i = 0; i < tx->u.tx.num_extra_frag; i++) { + if (ccmp_encrypt_skb(tx, tx->u.tx.extra_frag[i], test) + < 0) + return TXRX_DROP; + } + } + + return TXRX_CONTINUE; +} + + +ieee80211_txrx_result +ieee80211_rx_h_ccmp_decrypt(struct ieee80211_txrx_data *rx) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) rx->skb->data; + u16 fc; + int hdrlen; + struct ieee80211_key *key = rx->key; + struct sk_buff *skb = rx->skb; + u8 pn[CCMP_PN_LEN]; + int data_len; + + fc = le16_to_cpu(hdr->frame_control); + hdrlen = ieee80211_get_hdrlen(fc); + + if (!key || key->alg != ALG_CCMP || + !(rx->fc & IEEE80211_FCTL_PROTECTED) || + (rx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA) + return TXRX_CONTINUE; + + data_len = skb->len - hdrlen - CCMP_HDR_LEN - CCMP_MIC_LEN; + if (!rx->sta || data_len < 0) + return TXRX_DROP; + + if ((rx->u.rx.status->flag & RX_FLAG_DECRYPTED) && + !key->force_sw_encrypt && + !(rx->local->hw.flags & IEEE80211_HW_WEP_INCLUDE_IV)) + return TXRX_CONTINUE; + + (void) ccmp_hdr2pn(pn, skb->data + hdrlen); + + if (memcmp(pn, key->u.ccmp.rx_pn[rx->u.rx.queue], CCMP_PN_LEN) <= 0) { +#ifdef CONFIG_MAC80211_DEBUG + u8 *ppn = key->u.ccmp.rx_pn[rx->u.rx.queue]; + printk(KERN_DEBUG "%s: CCMP replay detected for RX frame from " + MAC_FMT " (RX PN %02x%02x%02x%02x%02x%02x <= prev. PN " + "%02x%02x%02x%02x%02x%02x)\n", rx->dev->name, + MAC_ARG(rx->sta->addr), + pn[0], pn[1], pn[2], pn[3], pn[4], pn[5], + ppn[0], ppn[1], ppn[2], ppn[3], ppn[4], ppn[5]); +#endif /* CONFIG_MAC80211_DEBUG */ + key->u.ccmp.replays++; + return TXRX_DROP; + } + + if ((rx->u.rx.status->flag & RX_FLAG_DECRYPTED) && + !key->force_sw_encrypt) { + /* hwaccel has already decrypted frame and verified MIC */ + } else { + u8 *scratch, *b_0, *aad; + + scratch = key->u.ccmp.rx_crypto_buf; + b_0 = scratch + 3 * AES_BLOCK_LEN; + aad = scratch + 4 * AES_BLOCK_LEN; + + ccmp_special_blocks(skb, pn, b_0, aad, 1); + + if (ieee80211_aes_ccm_decrypt( + key->u.ccmp.tfm, scratch, b_0, aad, + skb->data + hdrlen + CCMP_HDR_LEN, data_len, + skb->data + skb->len - CCMP_MIC_LEN, + skb->data + hdrlen + CCMP_HDR_LEN)) { + printk(KERN_DEBUG "%s: CCMP decrypt failed for RX " + "frame from " MAC_FMT "\n", rx->dev->name, + MAC_ARG(rx->sta->addr)); + return TXRX_DROP; + } + } + + memcpy(key->u.ccmp.rx_pn[rx->u.rx.queue], pn, CCMP_PN_LEN); + + /* Remove CCMP header and MIC */ + skb_trim(skb, skb->len - CCMP_MIC_LEN); + memmove(skb->data + CCMP_HDR_LEN, skb->data, hdrlen); + skb_pull(skb, CCMP_HDR_LEN); + + return TXRX_CONTINUE; +} + diff --git a/net/mac80211/wpa.h b/net/mac80211/wpa.h new file mode 100644 index 0000000..da3b959 --- /dev/null +++ b/net/mac80211/wpa.h @@ -0,0 +1,31 @@ +/* + * Copyright 2002-2004, Instant802 Networks, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef WPA_H +#define WPA_H + +#include +#include +#include "ieee80211_i.h" + +ieee80211_txrx_result +ieee80211_tx_h_michael_mic_add(struct ieee80211_txrx_data *tx); +ieee80211_txrx_result +ieee80211_rx_h_michael_mic_verify(struct ieee80211_txrx_data *rx); + +ieee80211_txrx_result +ieee80211_tx_h_tkip_encrypt(struct ieee80211_txrx_data *tx); +ieee80211_txrx_result +ieee80211_rx_h_tkip_decrypt(struct ieee80211_txrx_data *rx); + +ieee80211_txrx_result +ieee80211_tx_h_ccmp_encrypt(struct ieee80211_txrx_data *tx); +ieee80211_txrx_result +ieee80211_rx_h_ccmp_decrypt(struct ieee80211_txrx_data *rx); + +#endif /* WPA_H */ diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index a228d56..6291f13 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig @@ -1,6 +1,19 @@ config CFG80211 tristate "Improved wireless configuration API" +config NL80211 + bool "nl80211 new netlink interface support" + depends CFG80211 + default y + ---help--- + This option turns on the new netlink interface + (nl80211) support in cfg80211. + + If =n, drivers using mac80211 will be configured via + wireless extension support provided by that subsystem. + + If unsure, say Y. + config WIRELESS_EXT bool "Wireless extensions" default n @@ -10,7 +23,9 @@ config WIRELESS_EXT Wireless extensions will be replaced by cfg80211 and will be required only by legacy drivers that implement - wireless extension handlers. + wireless extension handlers. This option does not + affect the wireless-extension backward compatibility + code in cfg80211. Say N (if you can) unless you know you need wireless extensions for external modules. diff --git a/net/wireless/Makefile b/net/wireless/Makefile index 3a96ae6..e746b3a 100644 --- a/net/wireless/Makefile +++ b/net/wireless/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_WIRELESS_EXT) += wext.o obj-$(CONFIG_CFG80211) += cfg80211.o cfg80211-y += core.o sysfs.o +cfg80211-$(CONFIG_NL80211) += nl80211.o diff --git a/net/wireless/core.c b/net/wireless/core.c index 7eabd55..46e5ae0 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -16,6 +16,7 @@ #include #include #include #include +#include "nl80211.h" #include "core.h" #include "sysfs.h" @@ -36,6 +37,141 @@ static int wiphy_counter; /* for debugfs */ static struct dentry *ieee80211_debugfs_dir; +/* requires cfg80211_drv_mutex to be held! */ +static struct cfg80211_registered_device *cfg80211_drv_by_wiphy(int wiphy) +{ + struct cfg80211_registered_device *result = NULL, *drv; + + list_for_each_entry(drv, &cfg80211_drv_list, list) { + if (drv->idx == wiphy) { + result = drv; + break; + } + } + + return result; +} + +/* requires cfg80211_drv_mutex to be held! */ +static struct cfg80211_registered_device * +__cfg80211_drv_from_info(struct genl_info *info) +{ + int ifindex; + struct cfg80211_registered_device *bywiphy = NULL, *byifidx = NULL; + struct net_device *dev; + int err = -EINVAL; + + if (info->attrs[NL80211_ATTR_WIPHY]) { + bywiphy = cfg80211_drv_by_wiphy( + nla_get_u32(info->attrs[NL80211_ATTR_WIPHY])); + err = -ENODEV; + } + + if (info->attrs[NL80211_ATTR_IFINDEX]) { + ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]); + dev = dev_get_by_index(ifindex); + if (dev) { + if (dev->ieee80211_ptr) + byifidx = + wiphy_to_dev(dev->ieee80211_ptr->wiphy); + dev_put(dev); + } + err = -ENODEV; + } + + if (bywiphy && byifidx) { + if (bywiphy != byifidx) + return ERR_PTR(-EINVAL); + else + return bywiphy; /* == byifidx */ + } + if (bywiphy) + return bywiphy; + + if (byifidx) + return byifidx; + + return ERR_PTR(err); +} + +struct cfg80211_registered_device * +cfg80211_get_dev_from_info(struct genl_info *info) +{ + struct cfg80211_registered_device *drv; + + mutex_lock(&cfg80211_drv_mutex); + drv = __cfg80211_drv_from_info(info); + + /* if it is not an error we grab the lock on + * it to assure it won't be going away while + * we operate on it */ + if (!IS_ERR(drv)) + mutex_lock(&drv->mtx); + + mutex_unlock(&cfg80211_drv_mutex); + + return drv; +} + +struct cfg80211_registered_device * +cfg80211_get_dev_from_ifindex(int ifindex) +{ + struct cfg80211_registered_device *drv = ERR_PTR(-ENODEV); + struct net_device *dev; + + mutex_lock(&cfg80211_drv_mutex); + dev = dev_get_by_index(ifindex); + if (!dev) + goto out; + if (dev->ieee80211_ptr) { + drv = wiphy_to_dev(dev->ieee80211_ptr->wiphy); + mutex_lock(&drv->mtx); + } else + drv = ERR_PTR(-ENODEV); + dev_put(dev); + out: + mutex_unlock(&cfg80211_drv_mutex); + return drv; +} + +void cfg80211_put_dev(struct cfg80211_registered_device *drv) +{ + BUG_ON(IS_ERR(drv)); + mutex_unlock(&drv->mtx); +} + +int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, + char *newname) +{ + int idx, taken = -1, result, digits; + + /* prohibit calling the thing phy%d when %d is not its number */ + sscanf(newname, PHY_NAME "%d%n", &idx, &taken); + if (taken == strlen(newname) && idx != rdev->idx) { + /* count number of places needed to print idx */ + digits = 1; + while (idx /= 10) + digits++; + /* + * deny the name if it is phy where is printed + * without leading zeroes. taken == strlen(newname) here + */ + if (taken == strlen(PHY_NAME) + digits) + return -EINVAL; + } + + /* this will check for collisions */ + result = device_rename(&rdev->wiphy.dev, newname); + if (!result) + return result; + + /* TODO: do debugfs rename! */ + + nl80211_notify_dev_rename(rdev); + + return 0; +} + /* exported functions */ struct wiphy *wiphy_new(struct cfg80211_ops *ops, int sizeof_priv) @@ -204,10 +340,16 @@ static int cfg80211_init(void) if (err) goto out_fail_notifier; + err = nl80211_init(); + if (err) + goto out_fail_nl80211; + ieee80211_debugfs_dir = debugfs_create_dir("ieee80211", NULL); return 0; +out_fail_nl80211: + unregister_netdevice_notifier(&cfg80211_netdev_notifier); out_fail_notifier: wiphy_sysfs_exit(); out_fail_sysfs: @@ -218,6 +360,7 @@ module_init(cfg80211_init); static void cfg80211_exit(void) { debugfs_remove(ieee80211_debugfs_dir); + nl80211_exit(); unregister_netdevice_notifier(&cfg80211_netdev_notifier); wiphy_sysfs_exit(); } diff --git a/net/wireless/core.h b/net/wireless/core.h index 158db1e..eb0f846 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -43,7 +43,39 @@ struct cfg80211_registered_device *wiphy extern struct mutex cfg80211_drv_mutex; extern struct list_head cfg80211_drv_list; +/* + * This function returns a pointer to the driver + * that the genl_info item that is passed refers to. + * If successful, it returns non-NULL and also locks + * the driver's mutex! + * + * This means that you need to call cfg80211_put_dev() + * before being allowed to acquire &cfg80211_drv_mutex! + * + * This is necessary because we need to lock the global + * mutex to get an item off the list safely, and then + * we lock the drv mutex so it doesn't go away under us. + * + * We don't want to keep cfg80211_drv_mutex locked + * for all the time in order to allow requests on + * other interfaces to go through at the same time. + * + * The result of this can be a PTR_ERR and hence must + * be checked with IS_ERR() for errors. + */ +extern struct cfg80211_registered_device * +cfg80211_get_dev_from_info(struct genl_info *info); + +/* identical to cfg80211_get_dev_from_info but only operate on ifindex */ +extern struct cfg80211_registered_device * +cfg80211_get_dev_from_ifindex(int ifindex); + +extern void cfg80211_put_dev(struct cfg80211_registered_device *drv); + /* free object */ extern void cfg80211_dev_free(struct cfg80211_registered_device *drv); +extern int cfg80211_dev_rename(struct cfg80211_registered_device *drv, + char *newname); + #endif /* __NET_WIRELESS_CORE_H */ diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c new file mode 100644 index 0000000..d6a44a3 --- /dev/null +++ b/net/wireless/nl80211.c @@ -0,0 +1,994 @@ +/* + * This is the new netlink-based wireless configuration interface. + * + * Copyright 2006 Johannes Berg + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "core.h" +#include "nl80211.h" + +/* the netlink family */ +static struct genl_family nl80211_fam = { + .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */ + .name = "nl80211", /* have users key off the name instead */ + .hdrsize = 0, /* no private header */ + .version = 1, /* no particular meaning now */ + .maxattr = NL80211_ATTR_MAX, +}; + +/* internal helper: validate an information element attribute */ +static int check_information_element(struct nlattr *nla) +{ + int len = nla_len(nla); + u8 *data = nla_data(nla); + int elementlen; + + while (len >= 2) { + /* 1 byte ID, 1 byte len, `len' bytes data */ + elementlen = *(data+1) + 2; + data += elementlen; + len -= elementlen; + } + return len ? -EINVAL : 0; +} + +/* internal helper: get drv and dev */ +static int get_drv_dev_by_info_ifindex(struct genl_info *info, + struct cfg80211_registered_device **drv, + struct net_device **dev) +{ + int ifindex; + + if (!info->attrs[NL80211_ATTR_IFINDEX]) + return -EINVAL; + + ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]); + *dev = dev_get_by_index(ifindex); + if (!dev) + return -ENODEV; + + *drv = cfg80211_get_dev_from_ifindex(ifindex); + if (IS_ERR(*drv)) { + dev_put(*dev); + return PTR_ERR(*drv); + } + + return 0; +} + +/* policy for the attributes */ +static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { + [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 }, + [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 }, + [NL80211_ATTR_WIPHY] = { .type = NLA_U32 }, + [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING, + .len = BUS_ID_SIZE-1 }, + [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 }, + [NL80211_ATTR_BSSID] = { .len = ETH_ALEN }, + [NL80211_ATTR_SSID] = { .type = NLA_BINARY, + .len = IEEE80211_MAX_SSID_LEN }, + [NL80211_ATTR_CHANNEL] = { .type = NLA_U32 }, + [NL80211_ATTR_PHYMODE] = { .type = NLA_U32 }, + [NL80211_ATTR_CHANNEL_LIST] = { .type = NLA_NESTED }, + [NL80211_ATTR_BSS_LIST] = { .type = NLA_NESTED }, + [NL80211_ATTR_BSSTYPE] = { .type = NLA_U32 }, + [NL80211_ATTR_BEACON_PERIOD] = { .type = NLA_U32 }, + [NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 }, + [NL80211_ATTR_TIMESTAMP] = { .type = NLA_U64 }, + [NL80211_ATTR_IE] = { .type = NLA_BINARY, .len = NL80211_MAX_IE_LEN }, + [NL80211_ATTR_AUTH_ALGORITHM] = { .type = NLA_U32 }, + [NL80211_ATTR_TIMEOUT_TU] = { .type = NLA_U32 }, + [NL80211_ATTR_REASON_CODE] = { .type = NLA_U32 }, + [NL80211_ATTR_ASSOCIATION_ID] = { .type = NLA_U16 }, + [NL80211_ATTR_DEAUTHENTICATED] = { .type = NLA_FLAG }, + [NL80211_ATTR_RX_SENSITIVITY] = { .type = NLA_U32 }, + [NL80211_ATTR_TRANSMIT_POWER] = { .type = NLA_U32 }, + [NL80211_ATTR_FRAG_THRESHOLD] = { .type = NLA_U32 }, + [NL80211_ATTR_FLAG_SCAN_ACTIVE] = { .type = NLA_FLAG }, + [NL80211_ATTR_BEACON_HEAD] = { .type = NLA_BINARY }, + [NL80211_ATTR_BEACON_TAIL] = { .type = NLA_BINARY }, + [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY, + .len = WLAN_MAX_KEY_LEN }, + [NL80211_ATTR_KEY_ID] = { .type = NLA_U32 }, + [NL80211_ATTR_KEY_TYPE] = { .type = NLA_U32 }, + [NL80211_ATTR_MAC] = { .len = ETH_ALEN }, + [NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 }, +}; + +/* netlink command implementations */ + +#define CHECK_CMD(ptr, cmd) \ + if (drv->ops->ptr) \ + NLA_PUT_FLAG(msg, NL80211_CMD_##cmd); + +static int nl80211_get_cmdlist(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *drv; + struct sk_buff *msg; + void *hdr; + int err; + struct nlattr *start; + + drv = cfg80211_get_dev_from_info(info); + if (IS_ERR(drv)) + return PTR_ERR(drv); + + hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0, + NL80211_CMD_NEW_CMDLIST); + if (IS_ERR(hdr)) { + err = PTR_ERR(hdr); + goto put_drv; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, drv->idx); + + start = nla_nest_start(msg, NL80211_ATTR_CMDS); + if (!start) + goto nla_put_failure; + + /* unconditionally allow some common commands we handle centrally + * or where we require the implementation */ + NLA_PUT_FLAG(msg, NL80211_CMD_GET_CMDLIST); + NLA_PUT_FLAG(msg, NL80211_CMD_GET_WIPHYS); + NLA_PUT_FLAG(msg, NL80211_CMD_GET_INTERFACES); + NLA_PUT_FLAG(msg, NL80211_CMD_RENAME_WIPHY); + + CHECK_CMD(add_virtual_intf, ADD_VIRTUAL_INTERFACE); + CHECK_CMD(del_virtual_intf, DEL_VIRTUAL_INTERFACE); + CHECK_CMD(associate, ASSOCIATE); + CHECK_CMD(disassociate, DISASSOCIATE); + CHECK_CMD(deauth, DEAUTH); + CHECK_CMD(initiate_scan, INITIATE_SCAN); + CHECK_CMD(get_association, GET_ASSOCIATION); + CHECK_CMD(get_auth_list, GET_AUTH_LIST); + CHECK_CMD(add_key, ADD_KEY); + CHECK_CMD(del_key, DEL_KEY); + + nla_nest_end(msg, start); + + genlmsg_end(msg, hdr); + + err = genlmsg_unicast(msg, info->snd_pid); + goto put_drv; + + nla_put_failure: + err = -ENOBUFS; + nlmsg_free(msg); + put_drv: + cfg80211_put_dev(drv); + return err; +} +#undef CHECK_CMD + +static int nl80211_get_wiphys(struct sk_buff *skb, struct genl_info *info) +{ + struct sk_buff *msg; + void *hdr; + struct nlattr *start, *indexstart; + struct cfg80211_registered_device *drv; + int idx = 1; + + hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0, + NL80211_CMD_NEW_WIPHYS); + if (IS_ERR(hdr)) + return PTR_ERR(hdr); + + start = nla_nest_start(msg, NL80211_ATTR_WIPHY_LIST); + if (!start) + goto nla_outer_nest_failure; + + mutex_lock(&cfg80211_drv_mutex); + list_for_each_entry(drv, &cfg80211_drv_list, list) { + indexstart = nla_nest_start(msg, idx++); + if (!indexstart) + goto nla_put_failure; + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, drv->idx); + nla_nest_end(msg, indexstart); + } + mutex_unlock(&cfg80211_drv_mutex); + + nla_nest_end(msg, start); + + genlmsg_end(msg, hdr); + + return genlmsg_unicast(msg, info->snd_pid); + + nla_put_failure: + mutex_unlock(&cfg80211_drv_mutex); + nla_outer_nest_failure: + nlmsg_free(msg); + return -ENOBUFS; +} + +static int addifidx(struct net_device *dev, struct sk_buff *skb, int *idx) +{ + int err = -ENOBUFS; + struct nlattr *start; + + dev_hold(dev); + + start = nla_nest_start(skb, *idx++); + if (!start) + goto nla_put_failure; + + NLA_PUT_U32(skb, NL80211_ATTR_IFINDEX, dev->ifindex); + NLA_PUT_STRING(skb, NL80211_ATTR_IFNAME, dev->name); + + nla_nest_end(skb, start); + err = 0; + + nla_put_failure: + dev_put(dev); + return err; +} + +static int nl80211_get_intfs(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *drv; + struct sk_buff *msg; + void *hdr; + int err, array_idx; + struct nlattr *start; + struct wireless_dev *wdev; + + drv = cfg80211_get_dev_from_info(info); + if (IS_ERR(drv)) + return PTR_ERR(drv); + + hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0, + NL80211_CMD_NEW_INTERFACES); + if (IS_ERR(hdr)) { + err = PTR_ERR(hdr); + goto put_drv; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, drv->idx); + + start = nla_nest_start(msg, NL80211_ATTR_INTERFACE_LIST); + if (!start) { + err = -ENOBUFS; + goto msg_free; + } + + array_idx = 1; + err = 0; + mutex_lock(&drv->devlist_mtx); + list_for_each_entry(wdev, &drv->netdev_list, list) { + err = addifidx(wdev->netdev, msg, &array_idx); + if (err) + break; + } + mutex_unlock(&drv->devlist_mtx); + if (err) + goto msg_free; + + nla_nest_end(msg, start); + + genlmsg_end(msg, hdr); + + err = genlmsg_unicast(msg, info->snd_pid); + goto put_drv; + + nla_put_failure: + err = -ENOBUFS; + msg_free: + nlmsg_free(msg); + put_drv: + cfg80211_put_dev(drv); + return err; +} + +static int nl80211_add_virt_intf(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *drv; + int err; + unsigned int type = NL80211_IFTYPE_UNSPECIFIED; + + if (!info->attrs[NL80211_ATTR_IFNAME]) + return -EINVAL; + + if (info->attrs[NL80211_ATTR_IFTYPE]) { + type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]); + if (type > NL80211_IFTYPE_MAX) + return -EINVAL; + } + + drv = cfg80211_get_dev_from_info(info); + if (IS_ERR(drv)) + return PTR_ERR(drv); + + if (!drv->ops->add_virtual_intf) { + err = -EOPNOTSUPP; + goto unlock; + } + + rtnl_lock(); + err = drv->ops->add_virtual_intf(&drv->wiphy, + nla_data(info->attrs[NL80211_ATTR_IFNAME]), type); + rtnl_unlock(); + + unlock: + cfg80211_put_dev(drv); + return err; +} + +static int nl80211_del_virt_intf(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *drv; + int ifindex, err; + struct net_device *dev; + + err = get_drv_dev_by_info_ifindex(info, &drv, &dev); + if (err) + return err; + ifindex = dev->ifindex; + dev_put(dev); + + if (!drv->ops->del_virtual_intf) { + err = -EOPNOTSUPP; + goto out; + } + + rtnl_lock(); + err = drv->ops->del_virtual_intf(&drv->wiphy, ifindex); + rtnl_unlock(); + + out: + cfg80211_put_dev(drv); + return err; +} + +static int nl80211_change_virt_intf(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *drv; + int err, ifindex; + unsigned int type; + struct net_device *dev; + + if (info->attrs[NL80211_ATTR_IFTYPE]) { + type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]); + if (type > NL80211_IFTYPE_MAX) + return -EINVAL; + } else + return -EINVAL; + + err = get_drv_dev_by_info_ifindex(info, &drv, &dev); + if (err) + return err; + ifindex = dev->ifindex; + dev_put(dev); + + if (!drv->ops->change_virtual_intf) { + err = -EOPNOTSUPP; + goto unlock; + } + + rtnl_lock(); + err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex, type); + rtnl_unlock(); + + unlock: + cfg80211_put_dev(drv); + return err; +} + +static int nl80211_get_association(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *drv; + int err; + struct net_device *dev; + struct sk_buff *msg; + void *hdr; + u8 bssid[ETH_ALEN]; + + err = get_drv_dev_by_info_ifindex(info, &drv, &dev); + if (err) + return err; + + if (!drv->ops->get_association) { + err = -EOPNOTSUPP; + goto out_put_drv; + } + + rtnl_lock(); + err = drv->ops->get_association(&drv->wiphy, dev, bssid); + rtnl_unlock(); + if (err < 0) + goto out_put_drv; + + hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0, + NL80211_CMD_ASSOCIATION_CHANGED); + + if (IS_ERR(hdr)) { + err = PTR_ERR(hdr); + goto out_put_drv; + } + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); + if (err == 1) + NLA_PUT(msg, NL80211_ATTR_BSSID, ETH_ALEN, bssid); + + genlmsg_end(msg, hdr); + err = genlmsg_unicast(msg, info->snd_pid); + goto out_put_drv; + + nla_put_failure: + err = -ENOBUFS; + nlmsg_free(msg); + out_put_drv: + cfg80211_put_dev(drv); + dev_put(dev); + return err; +} + +static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *drv; + int err; + struct net_device *dev; + struct association_params assoc_params; + + memset(&assoc_params, 0, sizeof(assoc_params)); + + err = get_drv_dev_by_info_ifindex(info, &drv, &dev); + if (err) + return err; + + if (!drv->ops->associate) { + err = -EOPNOTSUPP; + goto out; + } + + if (!info->attrs[NL80211_ATTR_SSID]) + return -EINVAL; + + assoc_params.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); + assoc_params.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); + + if (info->attrs[NL80211_ATTR_BSSID]) + assoc_params.bssid = nla_data(info->attrs[NL80211_ATTR_BSSID]); + + if (info->attrs[NL80211_ATTR_IE]) { + err = check_information_element(info->attrs[NL80211_ATTR_IE]); + if (err) + goto out; + assoc_params.ie = nla_data(info->attrs[NL80211_ATTR_IE]); + assoc_params.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); + } + + if (info->attrs[NL80211_ATTR_TIMEOUT_TU]) { + assoc_params.timeout = + nla_get_u32(info->attrs[NL80211_ATTR_TIMEOUT_TU]); + assoc_params.valid |= ASSOC_PARAMS_TIMEOUT; + } + + rtnl_lock(); + err = drv->ops->associate(&drv->wiphy, dev, &assoc_params); + rtnl_unlock(); + + out: + cfg80211_put_dev(drv); + dev_put(dev); + return err; +} + +static int nl80211_disassoc_deauth(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *drv; + int err; + struct net_device *dev; + int (*act)(struct wiphy *wiphy, struct net_device *dev); + + err = get_drv_dev_by_info_ifindex(info, &drv, &dev); + if (err) + return err; + + switch (info->genlhdr->cmd) { + case NL80211_CMD_DISASSOCIATE: + act = drv->ops->disassociate; + break; + case NL80211_CMD_DEAUTH: + act = drv->ops->deauth; + break; + default: + act = NULL; + } + + if (!act) { + err = -EOPNOTSUPP; + goto out; + } + + rtnl_lock(); + err = act(&drv->wiphy, dev); + rtnl_unlock(); + out: + cfg80211_put_dev(drv); + dev_put(dev); + return err; +} + +struct add_cb_data { + int idx; + struct sk_buff *skb; +}; + +static int add_bssid(void *data, u8 *bssid) +{ + struct add_cb_data *cb = data; + int err = -ENOBUFS; + struct nlattr *start; + + start = nla_nest_start(cb->skb, cb->idx++); + if (!start) + goto nla_put_failure; + + NLA_PUT(cb->skb, NL80211_ATTR_BSSID, ETH_ALEN, bssid); + + nla_nest_end(cb->skb, start); + err = 0; + + nla_put_failure: + return err; +} + +static int nl80211_get_auth_list(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *drv; + struct net_device *dev; + struct sk_buff *msg; + void *hdr; + int err; + struct nlattr *start; + struct add_cb_data cb; + + err = get_drv_dev_by_info_ifindex(info, &drv, &dev); + if (err) + return err; + + if (!drv->ops->get_auth_list) { + err = -EOPNOTSUPP; + goto put_drv; + } + + hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0, + NL80211_CMD_NEW_AUTH_LIST); + if (IS_ERR(hdr)) { + err = PTR_ERR(hdr); + goto put_drv; + } + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); + + start = nla_nest_start(msg, NL80211_ATTR_BSS_LIST); + if (!start) { + err = -ENOBUFS; + goto msg_free; + } + + cb.skb = msg; + cb.idx = 1; + rtnl_lock(); + err = drv->ops->get_auth_list(&drv->wiphy, dev, &cb, add_bssid); + rtnl_unlock(); + if (err) + goto msg_free; + + nla_nest_end(msg, start); + + genlmsg_end(msg, hdr); + + err = genlmsg_unicast(msg, info->snd_pid); + goto put_drv; + + nla_put_failure: + err = -ENOBUFS; + msg_free: + nlmsg_free(msg); + put_drv: + cfg80211_put_dev(drv); + dev_put(dev); + return err; +} + +static int nl80211_initiate_scan(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *drv; + int err; + struct net_device *dev; + struct scan_params params; + struct scan_channel *channels = NULL; + int count = -1; + + if (info->attrs[NL80211_ATTR_PHYMODE]) + params.phymode = nla_get_u32(info->attrs[NL80211_ATTR_PHYMODE]); + + if (params.phymode > NL80211_PHYMODE_MAX) + return -EINVAL; + + err = get_drv_dev_by_info_ifindex(info, &drv, &dev); + if (err) + return err; + + if (!drv->ops->initiate_scan) { + err = -EOPNOTSUPP; + goto out; + } + + params.active = nla_get_flag(info->attrs[NL80211_ATTR_FLAG_SCAN_ACTIVE]); + + if (info->attrs[NL80211_ATTR_CHANNEL_LIST]) { + struct nlattr *attr = info->attrs[NL80211_ATTR_CHANNEL_LIST]; + struct nlattr *nla; + int rem; + struct nlattr **tb; + + /* let's count first */ + count = 0; + nla_for_each_attr(nla, nla_data(attr), nla_len(attr), rem) + count++; + + if (count == 0) { + /* assume we should actually scan all channels, + * scanning no channels make no sense */ + count = -1; + goto done_channels; + } + + if (count > NL80211_MAX_CHANNEL_LIST_ITEM) { + err = -EINVAL; + goto out; + } + + channels = kmalloc(count * sizeof(struct scan_channel), + GFP_KERNEL); + tb = kmalloc((NL80211_ATTR_MAX+1) * sizeof(struct nlattr), + GFP_KERNEL); + + count = 0; + nla_for_each_attr(nla, nla_data(attr), nla_len(attr), rem) { + err = nla_parse(tb, NL80211_ATTR_MAX, nla_data(nla), + nla_len(nla), nl80211_policy); + + if (err || !tb[NL80211_ATTR_CHANNEL]) { + err = -EINVAL; + kfree(tb); + kfree(channels); + goto out; + } + + channels[count].phymode = params.phymode; + + if (tb[NL80211_ATTR_PHYMODE]) + channels[count].phymode = + nla_get_u32(tb[NL80211_ATTR_PHYMODE]); + + if (channels[count].phymode > NL80211_PHYMODE_MAX) { + err = -EINVAL; + kfree(tb); + kfree(channels); + goto out; + } + + channels[count].channel = + nla_get_u32(tb[NL80211_ATTR_CHANNEL]); + + channels[count].active = + nla_get_flag(tb[NL80211_ATTR_FLAG_SCAN_ACTIVE]); + count++; + } + kfree(tb); + } + + done_channels: + params.channels = channels; + params.n_channels = count; + + rtnl_lock(); + err = drv->ops->initiate_scan(&drv->wiphy, dev, ¶ms); + rtnl_unlock(); + + kfree(channels); + out: + cfg80211_put_dev(drv); + dev_put(dev); + return err; +} + +static int nl80211_rename_wiphy(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev; + int result; + + if (!info->attrs[NL80211_ATTR_WIPHY_NAME]) + return -EINVAL; + + rdev = cfg80211_get_dev_from_info(info); + if (IS_ERR(rdev)) + return PTR_ERR(rdev); + + result = cfg80211_dev_rename(rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME])); + + cfg80211_put_dev(rdev); + return result; +} + +static int nl80211_key_cmd(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *drv; + int err, del; + struct net_device *dev; + struct key_params params; + int (*act)(struct wiphy *wiphy, struct net_device *dev, + struct key_params *params); + + memset(¶ms, 0, sizeof(params)); + + if (!info->attrs[NL80211_ATTR_KEY_TYPE]) + return -EINVAL; + + if (!info->attrs[NL80211_ATTR_KEY_CIPHER]) + return -EINVAL; + + params.key_type = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]); + if (params.key_type > NL80211_KEYTYPE_MAX) + return -EINVAL; + + err = get_drv_dev_by_info_ifindex(info, &drv, &dev); + if (err) + return err; + + switch (info->genlhdr->cmd) { + case NL80211_CMD_ADD_KEY: + act = drv->ops->add_key; + del = 0; + break; + case NL80211_CMD_DEL_KEY: + act = drv->ops->del_key; + del = 1; + break; + default: + act = NULL; + } + + if (!act) { + err = -EOPNOTSUPP; + goto out; + } + + if (info->attrs[NL80211_ATTR_KEY_DATA]) { + params.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]); + params.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]); + } + + if (info->attrs[NL80211_ATTR_KEY_ID]) { + params.key_id = nla_get_u32(info->attrs[NL80211_ATTR_KEY_ID]); + } else { + params.key_id = -1; + } + + params.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]); + + if (info->attrs[NL80211_ATTR_MAC]) { + params.macaddress = nla_data(info->attrs[NL80211_ATTR_MAC]); + } else { + params.macaddress = NULL; + } + + rtnl_lock(); + err = act(&drv->wiphy, dev, ¶ms); + rtnl_unlock(); + + out: + cfg80211_put_dev(drv); + dev_put(dev); + return err; +} + +static struct genl_ops nl80211_ops[] = { + { + .cmd = NL80211_CMD_RENAME_WIPHY, + .doit = nl80211_rename_wiphy, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = NL80211_CMD_GET_CMDLIST, + .doit = nl80211_get_cmdlist, + .policy = nl80211_policy, + /* can be retrieved by unprivileged users */ + }, + { + .cmd = NL80211_CMD_ADD_VIRTUAL_INTERFACE, + .doit = nl80211_add_virt_intf, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = NL80211_CMD_DEL_VIRTUAL_INTERFACE, + .doit = nl80211_del_virt_intf, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = NL80211_CMD_CHANGE_VIRTUAL_INTERFACE, + .doit = nl80211_change_virt_intf, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = NL80211_CMD_GET_WIPHYS, + .doit = nl80211_get_wiphys, + .policy = nl80211_policy, + /* can be retrieved by unprivileged users */ + }, + { + .cmd = NL80211_CMD_GET_INTERFACES, + .doit = nl80211_get_intfs, + .policy = nl80211_policy, + /* can be retrieved by unprivileged users */ + }, + { + .cmd = NL80211_CMD_INITIATE_SCAN, + .doit = nl80211_initiate_scan, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = NL80211_CMD_GET_ASSOCIATION, + .doit = nl80211_get_association, + .policy = nl80211_policy, + /* can be retrieved by unprivileged users */ + }, + { + .cmd = NL80211_CMD_ASSOCIATE, + .doit = nl80211_associate, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = NL80211_CMD_DISASSOCIATE, + .doit = nl80211_disassoc_deauth, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = NL80211_CMD_DEAUTH, + .doit = nl80211_disassoc_deauth, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = NL80211_CMD_GET_AUTH_LIST, + .doit = nl80211_get_auth_list, + .policy = nl80211_policy, + /* can be retrieved by unprivileged users */ + }, +/* + { + .cmd = NL80211_CMD_AP_SET_BEACON, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = NL80211_CMD_AP_ADD_STA, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = NL80211_CMD_AP_UPDATE_STA, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = NL80211_CMD_AP_GET_STA_INFO, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = NL80211_CMD_AP_SET_RATESETS, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, +*/ + { + .cmd = NL80211_CMD_ADD_KEY, + .doit = nl80211_key_cmd, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = NL80211_CMD_DEL_KEY, + .doit = nl80211_key_cmd, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, +}; + + +/* exported functions */ + +void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq, int flags, u8 cmd) +{ + /* since there is no private header just add the generic one */ + return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd); +} +EXPORT_SYMBOL_GPL(nl80211hdr_put); + +void *nl80211msg_new(struct sk_buff **skb, u32 pid, u32 seq, int flags, u8 cmd) +{ + void *hdr; + + *skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (!*skb) + return ERR_PTR(-ENOBUFS); + + hdr = nl80211hdr_put(*skb, pid, seq, flags, cmd); + if (!hdr) { + nlmsg_free(*skb); + return ERR_PTR(-ENOBUFS); + } + + return hdr; +} +EXPORT_SYMBOL_GPL(nl80211msg_new); + +/* notification functions */ + +void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev) +{ + struct sk_buff *msg; + void *hdr; + + hdr = nl80211msg_new(&msg, 0, 0, 0, NL80211_CMD_WIPHY_NEWNAME); + if (IS_ERR(hdr)) + return; + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->idx); + NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&rdev->wiphy)); + + genlmsg_end(msg, hdr); + genlmsg_multicast(msg, 0, NL80211_GROUP_CONFIG, GFP_KERNEL); + + return; + + nla_put_failure: + nlmsg_free(msg); +} + +/* initialisation/exit functions */ + +int nl80211_init(void) +{ + int err, i; + + err = genl_register_family(&nl80211_fam); + if (err) + return err; + + for (i = 0; i < ARRAY_SIZE(nl80211_ops); i++) { + err = genl_register_ops(&nl80211_fam, &nl80211_ops[i]); + if (err) + goto err_out; + } + return 0; + err_out: + genl_unregister_family(&nl80211_fam); + return err; +} + +void nl80211_exit(void) +{ + genl_unregister_family(&nl80211_fam); +} diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h new file mode 100644 index 0000000..f3ea5c0 --- /dev/null +++ b/net/wireless/nl80211.h @@ -0,0 +1,24 @@ +#ifndef __NET_WIRELESS_NL80211_H +#define __NET_WIRELESS_NL80211_H + +#include "core.h" + +#ifdef CONFIG_NL80211 +extern int nl80211_init(void); +extern void nl80211_exit(void); +extern void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev); +#else +static inline int nl80211_init(void) +{ + return 0; +} +static inline void nl80211_exit(void) +{ +} +static inline void nl80211_notify_dev_rename( + struct cfg80211_registered_device *rdev) +{ +} +#endif /* CONFIG_NL80211 */ + +#endif /* __NET_WIRELESS_NL80211_H */ diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c index 3ebae14..7844be4 100644 --- a/net/wireless/sysfs.c +++ b/net/wireless/sysfs.c @@ -39,9 +39,59 @@ static ssize_t _show_permaddr(struct dev addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); } +static ssize_t _store_add_iface(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct cfg80211_registered_device *rdev = dev_to_rdev(dev); + int res; + + if (len > IFNAMSIZ) + return -EINVAL; + + if (!rdev->ops->add_virtual_intf) + return -ENOSYS; + + rtnl_lock(); + res = rdev->ops->add_virtual_intf(&rdev->wiphy, (char*)buf, + NL80211_IFTYPE_UNSPECIFIED); + rtnl_unlock(); + + return res ? res : len; +} + +static ssize_t _store_remove_iface(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct cfg80211_registered_device *rdev = dev_to_rdev(dev); + int res, ifidx; + struct net_device *netdev; + + if (len > IFNAMSIZ) + return -EINVAL; + + if (!rdev->ops->del_virtual_intf) + return -ENOSYS; + + netdev = dev_get_by_name(buf); + if (!netdev) + return -ENODEV; + ifidx = netdev->ifindex; + dev_put(netdev); + + rtnl_lock(); + res = rdev->ops->del_virtual_intf(&rdev->wiphy, ifidx); + rtnl_unlock(); + + return res ? res : len; +} + static struct device_attribute ieee80211_dev_attrs[] = { __ATTR(index, S_IRUGO, _show_index, NULL), __ATTR(macaddress, S_IRUGO, _show_permaddr, NULL), + __ATTR(add_iface, S_IWUGO, NULL, _store_add_iface), + __ATTR(remove_iface, S_IWUGO, NULL, _store_remove_iface), {} };