GIT 3fa2b256fd02538dd130c5e027ec1dda7ebad99c git+ssh://master.kernel.org/pub/scm/linux/kernel/git/linville/wireless-dev.git#mm-master commit 3fa2b256fd02538dd130c5e027ec1dda7ebad99c 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 0d800b75a5a2a7a1b8d84969f404d19c29fd0df2 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 45f6524f636b7773f4aa3c1c7f501bc7fbcefe3c 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 d1e46ae603c385e43af5d0e510ca17dfd831fec6 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 7be45265a3802158da6cc4f2cdc4326e245fb9ee 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 7c99c1690311dd401956aa98c89e27f9377a4389 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 690c6e66d59f3576c013eaf2928624292ab005fd 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 6d5d485dbc49ad7d12317f67726a7b4e81fd32af 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 c5cb7599d8a96c27c37fe6cec3f78d08de283b70 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 Signed-off-by: John W. Linville commit 99002f69b066cbea052a10447615b6bc69490192 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 Signed-off-by: John W. Linville commit 03246148d562659e18f9f86f5313b6ab1d72ae01 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 Signed-off-by: John W. Linville commit 3d8e1e28642d4340d6ba851995f4677af8ca3db5 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 Signed-off-by: John W. Linville commit 6fae4d825a42bf916c74fd2b417fd95f24013ebd 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 Signed-off-by: John W. Linville commit 0c34722332ac3f5823ce96062ef4ea3ed97b1cf3 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 Signed-off-by: John W. Linville commit 706983a56aab2391e2088e17a02af604887524db 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 Signed-off-by: John W. Linville commit a6d8a0f1514d9fc3f0d97cd66760fbb272bb9d4e 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 Signed-off-by: John W. Linville commit 381ce95f0149dc0ddc3b3745223283a33c45f717 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 Signed-off-by: John W. Linville commit ee0ff65641422625cae94bac61535060400733af 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 Signed-off-by: John W. Linville commit d32a6569390ec9fdbc9d38489c29bd328a3ae88f 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 Signed-off-by: John W. Linville commit 6eec14cc3ecc89f5dedd5c67aa995ad78244c26f 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 Signed-off-by: John W. Linville commit f16a5f49a6cb9ac911945ec821a767388cd9dd2a 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 Signed-off-by: John W. Linville commit ba9bfdc3e56c04fc937a7b512ea71996cfc8e41a 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 Signed-off-by: John W. Linville commit 4f108b7a15166aa14c1dfe08d009e96e23b66f63 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 Signed-off-by: John W. Linville commit 0cc4f5cdea76bc2c4b6226a09e7776fc8610af52 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 Signed-off-by: John W. Linville commit 6fb6f87552b1f40affdd477fbfd4a4665cdc20d5 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 Signed-off-by: John W. Linville commit 9f6709185f339cc69faf3b77ae563b26e1399429 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 Signed-off-by: John W. Linville commit 236bc086ec1af84c5e8540e1c2dde7f47bd4b5a5 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 Signed-off-by: John W. Linville commit 5312a9068032525ac2d7db898d5f93f32c4edac9 Author: Michael Wu Date: Thu Mar 22 21:12:35 2007 -0400 [PATCH] rtl8187: Set MSR This sets the MSR accordingly when a link has be established/lost. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit 495ad9a351ddea839fd30f2dbc055c762a7f2495 Author: Michael Wu Date: Thu Mar 22 21:12:13 2007 -0400 [PATCH] rtl8187: Enable contention window Random backoff shouldn't be disabled. This reenables it. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit 22a3e330316af8a15c13a62d9c1d4e1bba0244be Author: Michael Wu Date: Thu Mar 22 19:59:16 2007 -0400 [PATCH] p54: Add RTS/CTS support Thanks to Christian Lamparter for discovering these flags. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit 94ccee91653892543dd3a2f306e4db8c22366579 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 Signed-off-by: John W. Linville commit f18e062711d0056daf9adcaf31e2252c611155cf Author: Michael Wu Date: Thu Mar 22 19:47:42 2007 -0400 [PATCH] 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 Signed-off-by: John W. Linville commit 4b3ff85b73f3c989ee438eb71721404b1bf406c0 Author: Michael Wu Date: Thu Mar 22 19:47:11 2007 -0400 [PATCH] 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 Signed-off-by: John W. Linville commit 18a5966b62b5ef500ddaca3b9e8f82a37d384949 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 c73da3e2eca814c9ec2a108e836c1c8cf9357458 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 0396304a4cf29afe4e876d6d0dca78cd609db22a 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 7a8324194ae0cf33b4c2aa74b28018a8a2531567 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 61ff6d1bc56561fc9031455498d9f47288216342 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 507b1b38cf67a37a89f5c0516ce252aadadaf402 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 1d235c9f76c4011e5ae70000866917a263cb8852 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 5714256abaf4176c9891b49e24d4b8ce416248d1 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 ba81a3257c8f81d9c30eac6f0f6ef226d3f0172e 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 75d0d152685ef3604f1f639c2b0eb439bda4f39e 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 9072ca4d3460d7782400d1d538bb7c2e15c67e4d 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 86a50cdce961403f1f45b40355da48267a191223 Author: Michael Buesch Date: Thu Mar 15 16:05:42 2007 +0100 [PATCH] bcm43xx-mac80211: Select SSB_DEBUG if bcm43xx debugging is enabled. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 117cd48e4ed814cf6540af0b45562f1e032e0976 Author: Michael Buesch Date: Thu Mar 8 01:11:29 2007 +0100 [PATCH] bcm43xx-mac80211: More LO fixes. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 42663e6ed34cc15c24ac9bc50a0a6d338da42fa4 Author: Michael Buesch Date: Thu Mar 8 00:33:26 2007 +0100 [PATCH] bcm43xx-mac80211: Rewrite of the LO feedthrough measuring statemachine. There are still some issues left. All marked with FIXME. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit e9ea3f8561663dd3c8dc690a3008955c10b2b787 Author: Michael Buesch Date: Wed Mar 7 23:01:08 2007 +0100 [PATCH] ssb: Don't freeze unregistered devices. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit edc49bbb9a41f9e5460f0e4179493078edf35d60 Author: Michael Buesch Date: Wed Mar 7 22:45:07 2007 +0100 [PATCH] ssb: Move and rename some files. Directory structure should be _much_ more sane and maintainable now. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit f164dade97b4ab1b3b4ab97aa70d7c4615a5f607 Author: Michael Buesch Date: Wed Mar 7 22:32:45 2007 +0100 [PATCH] ssb-ohci: Completely disable the driver on non-embedded systems Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 616e9ec78530245a344f129c8602db99472aaded Author: Michael Wu Date: Wed Mar 7 13:05:16 2007 -0500 [PATCH] p54: Suppress warning in prism54common.c This eliminates a warning in p54_assign_address in prism54common.c. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit 89ddb236c2e1322e3311f57e4a1a4d69a3448a6d Author: Michael Wu Date: Wed Mar 7 13:01:36 2007 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit d0a147651314f2b41c3350df6c6a0047f45c5de6 Author: Michael Wu Date: Wed Mar 7 12:57:04 2007 -0500 [PATCH] rtl8187: Select EEPROM_93CX6 rtl8187 uses the eeprom_93cx6 library, so select it. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit dea2caf92c37862c02399afa1a130d00c21b174f Author: Andrea Merello Date: Wed Mar 7 12:56:09 2007 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit adc239ccbf6c71db4d178569394107786c424d53 Author: Andrea Merello Date: Wed Mar 7 12:55:37 2007 -0500 [PATCH] rtl8187: Minimize power consumption when down This turns off various parts of the card when it is down. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit 34f67de4c4660a5dd8eb1d915565f875265ab836 Author: Michael Wu Date: Wed Mar 7 12:50:55 2007 -0500 [PATCH] adm8211, p54: select CRC32 adm8211 and p54 use crc32, so select it. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit 95b83e04c20224c66ab21eb339fb0d0c5be32702 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 Signed-off-by: John W. Linville commit 1201ff06520f9843b23b385a9e6b9be6bbe09419 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 dcad5eadcbdb9a2769c4de44385dfc89f4db596b 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 71a0b66be098435cc581ddc7a86a37d27ba4283c 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 ae601f4b7842ae1d700634e4835eca32a27685bd 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 ef0ece8568c37be97ffabd91e4229dd1f372a7f5 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 6a7825f9f6d54b61169589de5e9d50164d259122 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 9bea50b8d8e23de00c3409ffec668407df60243e 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 7a993d91c25ffad9cab777fb5e8d41b4334d6d32 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 927740122c5c200d1a186eb2675ce3a9fea1e26a 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 b7ec33513b88f36118e88758a5321b437cc5be4e 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 cefd1eecf1b1c951e51c933e70dc5b213a98d571 Author: Michael Buesch Date: Tue Mar 6 17:27:52 2007 +0100 [PATCH] bcm43xx-mac80211: b5init: fix misinterpretation from the rev-eng group. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit a7adee6132da3d8779c4cff1325b864633fb0242 Author: Michael Buesch Date: Tue Mar 6 15:42:18 2007 +0100 [PATCH] 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 Signed-off-by: John W. Linville commit 22e0ff22d31f28fe5099de6a3a1b9bcb5d6807a5 Author: Michael Buesch Date: Mon Mar 5 22:27:28 2007 +0100 [PATCH] ssb-ohci: Proper start/reset and suspend/resume routines. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit bcc4d2cb22b0f16edd1d51646d6d851b8fced1ba Author: Michael Buesch Date: Mon Mar 5 21:54:44 2007 +0100 [PATCH] ssb: Don't poke with dev->drvdata in SSB to avoid a conflict with USB OHCI. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 5d0ec674d37022da4de604e2fa1f85ff3082b086 Author: Michael Buesch Date: Mon Mar 5 20:56:26 2007 +0100 [PATCH] ssb: Fix compile of OHCI core driver. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 81dc786fd9a536b42c42745c659b557d0618314c Author: Michael Buesch Date: Mon Mar 5 14:44:04 2007 +0100 [PATCH] 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 Signed-off-by: John W. Linville commit 7a6f59520cf815db330542438cc8b8dfad89854e Author: Michael Buesch Date: Mon Mar 5 14:42:14 2007 +0100 [PATCH] bcm43xx-mac80211: bcm43xx-pcmcia must depend on PCMCIA Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 0a0345b46cf600f76d275971b035aed68512cc38 Author: Michael Buesch Date: Mon Mar 5 14:36:10 2007 +0100 [PATCH] bcm43xx-mac80211: Don't free beacon_control, as it's not allocated dynamically. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 0bb27424fe4ed0a23c6c7be590f7aa6a6c5e25ae Author: Michael Buesch Date: Mon Mar 5 14:34:50 2007 +0100 [PATCH] bcm43xx-mac80211: Flip radio_ver conditional in b5 init. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit cd7bad8a7682347be9d62d2db2e0990c31c91f84 Author: Michael Buesch Date: Mon Mar 5 14:33:17 2007 +0100 [PATCH] bcm43xx-mac80211: Ignore any IRQ that's not for us. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 37d01e7c8d5831e0434f1ab7ac4a9278967a4304 Author: Michael Buesch Date: Mon Mar 5 14:28:40 2007 +0100 [PATCH] 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 Signed-off-by: John W. Linville commit 0fde2b473417cecb935cdf6d54ceaf501aef022b Author: Michael Buesch Date: Mon Mar 5 14:25:24 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: Michael Buesch Signed-off-by: John W. Linville commit 1e6d06395aa165b98fb4ef4902ae026163d94416 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 586c7d51e3a98595d53198e7fe16a63f07a319e7 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 2e07325fbdf54064f715b72744a6b0341299453c 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 8de1023f79dfafb0573d7e070263064c160a1ad8 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 b71199ffbad2b976dd0efa48fb2711e2a6573cc1 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 198a69234dd5f524908dcaab682b2c7e4c9380eb 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 c7d00e825ec00d2bf83ecc78f38ce716fbfa2a32 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 a4cd671c97437f3cb31d8c715330f71444656379 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 13e9a3527ec88a678178444efd616a542237dceb 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 2a7855c093fed00f0d5a319c8edd999a71e0cde1 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 e6223906f2cd12dd8148f102e295d66acb7bc9df 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 690bb432525f18da5ac944cda40017abaadb00f7 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 b7f0b213d3fb7d3708835abcd68ca7cc382c84ca 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 0e6c1f89d20ced57c978d821cc240768958fd36a 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 80e43cf7c82a57c086dd0524f49d40c97de23f42 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 e83c81d5afaa39cc16155cbd9b3003c51ac46356 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 a1e09c4e74806c163019bb4622151b2e089cbfc7 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 a05541d8521ee06cefbc43e7c58a2bc10a1903cb 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 24cf64682b328fc24b4b5c13421446e60c96ba5e 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 5c0b57d589eb442739e883c6eeca3d1f8224b40d 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 e74bacb774ca536f66b1771c6de3f8ce63a96ba6 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 336abf7f914691727d35ce8d9fbbd856bfd6f808 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 1085355653cb94906401fa51c5dede1a4316db11 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 19ac0b226005bf04dd0351e34292b243127a402b 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 9822b8c1498fc387692179913ec424102bcd38da 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 39cc0f4df3d1ec3128c8e6649fe12172452c473a 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 4e5d4394fb34da9b39819681fcf3e2c0b98a83b3 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 d870b3a480857efd24931981b0e74b6b90ab3c7d 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 327267944d419cd4b2bf1c6373cef32951e91f0c 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 9fa98ab21baf35a101bb877a7e1ebe720542c0a3 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 6a9a44274b2fa1ae53a4651433eb8e46fef6477b 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 ec8bf2870ba63724becc8c50bff3de1eca2c33cf 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 b15669c8319e0371ce0b5a0fb66d84b3c909b9f3 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 1189de798cba24a38e1d2842b51ab4cb488caf4d 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 c35b2ddfa0017718f745873c7a31e5a75bf22b3d 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 1e089a1e66f296d905e487ef16fe2f9f6da576b4 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 d7a9451f8ccae4970ca781c4aecd005685523400 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 0ba8fc468eb2f6172f3d5ebb3a350509f9f09e58 Author: John W. Linville Date: Mon Feb 26 17:33:15 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 bedebbba4cc74f9eb04e1d060145104247781d4d Author: Michael Buesch Date: Sun Feb 25 19:29:01 2007 +0100 [PATCH] bcm43xx-d80211: Fix incorrect has_loopback_gain condition. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit a526d7166fc1cad2aa091237318bc535e329152c Author: Michael Buesch Date: Sun Feb 25 19:20:10 2007 +0100 [PATCH] bcm43xx-d80211: Fix 2050 radio init. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 954e03595753fdb642c4580cd5f744f863012f4c Author: Michael Buesch Date: Fri Feb 23 23:37:31 2007 +0100 [PATCH] ssb: Memory usage and speed optimizations. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 83c9ee90fb5492c1b4f0400b7e08ba79005f8ae0 Author: Michael Buesch Date: Fri Feb 23 23:10:24 2007 +0100 [PATCH] bcm43xx-d80211: Use generic dev_get_drvdata. Saves memory (one pointer). Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit b39ed65a6c3d4ecf88a70e9ece29899fa9bf5760 Author: Michael Buesch Date: Thu Feb 22 17:45:46 2007 +0100 [PATCH] bcm43xx-d80211: Remove UCODEFLAGS stuff. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 3967726755f8b04e515875fe3ec002ea520ca363 Author: Michael Buesch Date: Thu Feb 22 17:17:17 2007 +0100 [PATCH] bcm43xx-d80211: Fix a few PHY register writes to match recent specs. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 8f23d8268b8cf4fcb0fccaf990e78152c56e88da Author: Michael Wu Date: Sun Feb 25 23:33:36 2007 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit f0d42d67f5032051fb0212c75c23237dbd98ae52 Author: Michael Wu Date: Sun Feb 25 22:47:56 2007 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit 30f53d1922212b68d9641f6ee3663b2640bcdf9d Author: Michael Wu Date: Sun Feb 25 22:41:06 2007 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit 530c602a221b9af43aa92c7a34e5e28df2e0d099 Author: Christian Lamparter Date: Sun Feb 25 22:32:26 2007 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit b5a48ce70a65ff96de20e36693d7852646438db6 Author: Michael Wu Date: Sun Feb 25 21:24:15 2007 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit 4cd8e2027e14d94c78506f042e9b5d73821ff69e 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 c6051fbc99d9038086dd7d45b624cc147f975929 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 ef1311003cdd66c856dc5b5979a6d208b8d5d376 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 0e293aba1807e28d2044aeef5838684c93ccaec3 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 d5797b33c42b8de6808c217e577cfe6b4e76ead7 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 337e6264ca15e6df7d7af70304e0d7b3ffca1871 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 dfb0251d9e1a41200af7b80714905589f7c6d7c1 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 Signed-off-by: John W. Linville commit 899686727308b31d412b76fff58415e455265cb8 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 Signed-off-by: John W. Linville commit 08872a95ef972902ab310bda39917aeffa10cff4 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 Signed-off-by: John W. Linville commit bb38a5825d5512ae0462f2d9c0049c33a4f1fac6 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 Signed-off-by: John W. Linville commit b81ebcabf76615af5a0bb7eaad0f353651858123 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 f185b9cf4386667d7c7238c384265c317042b343 Author: Michael Buesch Date: Tue Feb 20 16:01:10 2007 +0100 [PATCH] bcm43xx-d80211: Fix register write for !GPHY. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 1ea49875f358f5445fe376499931ade70f025ec1 Author: Michael Buesch Date: Tue Feb 20 15:50:38 2007 +0100 [PATCH] bcm43xx-d80211: Set GMODE-MACCTL, if we are operating in GMODE. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit a1b6faf51af871f63a2eab73e6776ccd83e25666 Author: Michael Buesch Date: Tue Feb 20 15:46:15 2007 +0100 [PATCH] bcm43xx-d80211: Turn the Radio and Analog off on core-exit and attach-finish. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit eb00736faa61ffc50c0d5cbb7d6413990fffb139 Author: Michael Buesch Date: Tue Feb 20 12:33:26 2007 +0100 [PATCH] ssb: Take care of changed REJECT bit in new backplane revisions. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit fef047bae2607113ce6a447e83463d2300b82909 Author: Michael Buesch Date: Mon Feb 19 21:27:33 2007 +0100 [PATCH] bcm43xx-d80211: Add modparam to keep bad frames in monitor mode. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 41961a1147f28c14e5be0a280a7b811bc88ebc6e Author: Michael Buesch Date: Sat Feb 17 19:48:48 2007 +0100 [PATCH] ssb: Also check for PCI vendor ID, not just PCI device ID. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit f1ba8c58172426a2632d7c0555f4cbabb4ae7691 Author: Michael Buesch Date: Sat Feb 17 19:35:24 2007 +0100 [PATCH] bcm43xx-d80211: Move firmware pointers to bcm43xx_wldev, where they belong. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 9cb963c1d104aa0afa074f1cc73ca4fa989e9e66 Author: Michael Buesch Date: Sat Feb 17 19:10:45 2007 +0100 [PATCH] 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 Signed-off-by: John W. Linville commit d5fec0eb0dbcc6fa2a4d2977f8c75220286dfba6 Author: Michael Buesch Date: Sat Feb 17 18:53:40 2007 +0100 [PATCH] bcm43xx-d80211: Remove unneeded SHUTTINGDOWN and RESTARTING flags. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 7c0c0469f517302a5f873b7654af40cbbefee2ab Author: Michael Buesch Date: Sat Feb 17 18:45:47 2007 +0100 [PATCH] bcm43xx-d80211: Add "is-running" checks on core_stop and core_exit. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit eb3d2aa0e6917b79908bbb825779c1b51bc9d584 Author: Michael Buesch Date: Sat Feb 17 18:32:08 2007 +0100 [PATCH] bcm43xx-d80211: Ignore any A-PHY, as we don't support it, yet. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 298f84fed30726da30df66baa03fcd592960e5ed Author: Michael Buesch Date: Sat Feb 17 18:19:09 2007 +0100 [PATCH] ssb: Ignore additional 802.11 cores on devices with dangling pins on those cores. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit c472d9adc7a7dc086c9d1a9bad821ed7d33e9cb7 Author: Michael Buesch Date: Thu Feb 15 23:42:13 2007 +0100 [PATCH] 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 Signed-off-by: John W. Linville commit 4e90bc377ba55b34a0d9ff58a6e9678949d25045 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 e158514db4f8ae62c8ee05cd9fc1404b6da5d7ee 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 1b7a8e5f88fef9cab06e878c0861bea5b196d866 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 7e9d2f7460c793f1fde423f4908f1f8103656459 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 7f73bc80c44b85e061528957b5e429242791a846 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 f17373d80536026b585d9258bb70c6d33d00a8ce 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 88bbdf7f2c1f5b9c78d740b33d420ae349b545dd 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 8ddc1ccdf676fc497f987397ebcb101fe5059301 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 180a1df470828237e6430e88e484ad0d052eb56f 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 9273c4cea4f1669b05fa1a7b9407c7bab5690726 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 d026778e761c704335eb5c1adc77dce6316962e3 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 c5c30388d2ce96d138c934ddd87520a484721934 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 b444c755466fcec32fc1f6b4f3e507e3d1f47076 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 a7816603579e7725119f4d91671d38e4ac5df654 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 ffef24114dac700bf072c3c7de8d0fe6aa3a25d8 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 2ebd94500e93a16ba626af5d69dec6ac87034812 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 Signed-off-by: John W. Linville commit 12adc903cb5f5d3d850efa83ecdb5a9a649fa49f 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 Signed-off-by: John W. Linville commit 4689807d0dc68a1263a26cfd241a906ae68f6a61 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 Signed-off-by: John W. Linville commit c1f114e1ab3c711ae0f0ce138f2055c5d5bbfe42 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 Signed-off-by: John W. Linville commit ae89cba4592f1e4f8455a922e1264f6d40f788c8 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 Signed-off-by: John W. Linville commit de66772b0008de0344a988c5d866b0c49fea7456 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 Signed-off-by: John W. Linville commit 4ee84985852940a006952a904bc45442fc8b2e35 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 Signed-off-by: John W. Linville commit 74e249f1599f9d2de170af3212648a9a667b199e 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 Signed-off-by: John W. Linville commit 066306fe0a5faba87db5d8895ac7e225267da376 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 Signed-off-by: John W. Linville commit dcfa6e6997e6db1b358c72519761560d6a2cf619 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 Signed-off-by: John W. Linville commit 30c5c0407e415cb3cec83ad10db309d678175658 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 Signed-off-by: John W. Linville commit e3e69e0c8f86027f84fc0ace0adcb468eb4ea320 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 Signed-off-by: John W. Linville commit 407b4474ae23d992a34b86222c97850c9dbd17df 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 54e5b2f3f0af0269cd4570af1c607b9f5643852c 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 dda4e9d8eccdacc3c10800f0334831ff30751226 Author: John W. Linville Date: Thu Feb 15 23:28:31 2007 -0500 [PATCH] p54: update for wiphy api Signed-off-by: John W. Linville commit c2096d18dff4d965c64c27ac18a9ea59f2f74542 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 ca3f85cda3374e7bb2e870fbe8ac5faf7e6bf8ee 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 190e9bc7a40e835580a2e127340f61e779c68695 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 c6c0ec4503262aefcecb7fde5a7be60c635c69b9 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 87371285cdbfce200152e6864afc2c86a887bd63 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 e8606bce5a89ec4e869e39ce86664d21fa2d6890 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 af34022b826be1180927928dac6bc337015b0aeb 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 7ae5ed562670ba54508fbf9c9c602092d564b028 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 06a957ef23e6feeb60db47f3c6ccd48be879fdea 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 1ce02d61017ba270c071a4276732ac5ad0584101 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 37dfc40a153980808c60688d8476dd84534fe5a0 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 4c345a2b4d1f56bd1fb788e5a0d9a5c807d3b3f0 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 0eefb9fbd83698804672fdb19ed0ff7b7bf9ba0f 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 d773b99366dcc711799c903e4be3a0ea8b0c296d 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 60fc5800e005d58917c24950df1e1876837147a2 Author: Michael Buesch Date: Wed Feb 14 01:00:33 2007 +0100 [PATCH] bcm43xx-d80211: Fix locking info text. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 9208fcda26f62658d122f67e922db5839d0580e3 Author: Michael Buesch Date: Wed Feb 14 00:45:58 2007 +0100 [PATCH] bcm43xx-d80211: Fix stupid locking bug on error in chip_reset. Arghhh... Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 49338f75b940585b5dfbd2b848c12027bbd00946 Author: Michael Buesch Date: Wed Feb 14 00:42:46 2007 +0100 [PATCH] bcm43xx-d80211: Implement card reset routine to properly reset on fatal error conditions. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 0fe567285a66e27e821cdef7374cc77e6f51c1b8 Author: Michael Buesch Date: Tue Feb 13 12:05:00 2007 +0100 [PATCH] bcm43xx-d80211: Powerdown bus after attach step to save power. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 83e9cd09f4159ce50258c4ccaa6870da3dda3b8d Author: Michael Buesch Date: Tue Feb 13 02:02:00 2007 +0100 [PATCH] ssb: Add API to properly handle bus powercontrol. Unexport lowlevel chipcommon clockcontrol. This must not be used by drivers. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 7166a47330b9ed90401fcb976b54a78f9e0e4ada Author: Michael Buesch Date: Tue Feb 13 01:13:35 2007 +0100 [PATCH] 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 Signed-off-by: John W. Linville commit 743f3185c266f8925a46895de4b0accbdba857d7 Author: Michael Buesch Date: Tue Feb 13 00:54:04 2007 +0100 [PATCH] 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 Signed-off-by: John W. Linville commit 9903f0f1f50802ef9415905ec8b4a1e80aafa132 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 beb6dbee19ebc9fd5af5e8201235dda44d0be095 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 c7362ba07acd6bedfe02a15518e0bfbb5f0a8a44 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 25a38497f00d05948ce203d4274e19f822bcbd25 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 0a59fedd225a1ec45894d0b206578b33481af9ce 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 230ccaebe9ce4ffe9ad0b2a2bc466144f9fbdb06 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 d0bfb66f7e170a4014051433854db7fb136efd86 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 833a8c6ec9c9a47181bec073d9a0d397893dad9b Author: Michael Buesch Date: Sun Feb 11 23:37:20 2007 +0100 [PATCH] bcm43xx-d80211: Implement PHYmode switching support. This implements support for switching PHYmode via gmode-bit and coreswitch. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit d507252eb08883989b82a6c0cdbd80de322e8073 Author: Michael Buesch Date: Sun Feb 11 22:03:35 2007 +0100 [PATCH] bcm43xx-d80211: Some BTcoext stuff. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit ace9f1e5def489b3d90de2a0b11d621de943954e Author: Michael Buesch Date: Sun Feb 11 21:35:19 2007 +0100 [PATCH] bcm43xx-d80211: Add an assert()ion for dev->started to the IRQ handler. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 53973dce05d3d3fc4cf5df441f48499391f15243 Author: Michael Buesch Date: Sun Feb 11 20:57:34 2007 +0100 [PATCH] bcm43xx-d80211: Assert SHM-sh offsets to be 16bit aligned. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 2fc834e9a9b1999daca92e315b4865209210643d Author: Michael Buesch Date: Sun Feb 11 20:50:18 2007 +0100 [PATCH] bcm43xx-d80211: A few fixes and cleanups in LO setup. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit a6dfa4b4fbdb9b03330674fda720de6908cd981b Author: Michael Buesch Date: Sun Feb 11 17:31:45 2007 +0100 [PATCH] bcm43xx-d80211: Add missing udelay() in loopback gain calc. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 1b5f7a9e105c5e170550a46a2a194e1141123c64 Author: Michael Buesch Date: Sun Feb 11 16:25:42 2007 +0100 [PATCH] bcm43xx-d80211: Assign PCMCIA suspend and resume function pointers. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit ddb57e01d46fbeb580c76988ab2f1904358ba08b Author: Michael Buesch Date: Sun Feb 11 16:11:11 2007 +0100 [PATCH] b44: Use the generic PCIhost wrapper from SSB. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 3f7e8bbbe15a11f2aa57dc0f879d78da2b86121f Author: Michael Buesch Date: Sun Feb 11 16:03:22 2007 +0100 [PATCH] ssb, bcm43xx-d80211: Put the PCIhost wrapper into SSB. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit dc79646eb9d7bb9fce7193a9ff5cffbcda390f12 Author: Michael Buesch Date: Sun Feb 11 14:50:32 2007 +0100 [PATCH] bcm43xx-d80211: Fix 2050 radio init for devices with analog>=2 Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit ede7e9e925ce98c8fefe9d4808e681d4e51a47ff Author: Michael Buesch Date: Sun Feb 11 14:43:19 2007 +0100 [PATCH] bcm43xx-d80211, ssb: Fix suspend/resume. This fixes some bugs to get suspend/resume working again. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 358a55f236612ab914e4edf2e02840d90f7f1285 Author: Michael Buesch Date: Sat Feb 10 23:32:47 2007 +0100 [PATCH] bcm43xx-d80211: Set device parent for the ssb bus. Original patch by Matthew Garrett. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 8b834b07d2d4d89da3d8356242d32bef8c908772 Author: Michael Buesch Date: Fri Feb 9 19:51:30 2007 +0100 [PATCH] bcm43xx-d80211: Various PHY fixes. specs changed. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 80b16bc20bef2f0aa03cdaa64c9460a023c9f1ac Author: Michael Buesch Date: Fri Feb 9 18:31:25 2007 +0100 [PATCH] bcm43xx-d80211: Convert phy_version to analog_type No functional change. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit d7e39438ae2c217ceb094c2854c633514835db62 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 Signed-off-by: John W. Linville commit 765fb3a8a18e9c8d67a7b2cb12f095e20f243b7e 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 Signed-off-by: John W. Linville commit 7ef05a0cc84889892dec5d3fbd2ca461742bb793 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 Signed-off-by: John W. Linville commit 9929e1b6614330c553410e9abfaa18ce8f79bd2a 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 Signed-off-by: John W. Linville commit 0f2fce40af103f775560cbd474138763e934f687 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 Signed-off-by: John W. Linville commit c7a155cc230462fc54e230f764267b3711619e70 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 Signed-off-by: John W. Linville commit 24140e03e918a846db2973924db2cffb9f277933 Author: Michael Buesch Date: Mon Feb 5 14:01:08 2007 +0100 [PATCH] bcm43xx-d80211: Ignore ampdu status reports. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit ad14d146e990bc09c33d73f1df3aae36e6ec732b Author: Ulrich Kunitz Date: Sat Feb 3 02:25:30 2007 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit 7d5176aa8e9ee9543d42647b04635c52c033cc8b Author: Maxime Austruy Date: Sat Feb 3 02:17:37 2007 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit 8fb03f0e45f05528981e479c14ba0e199aad7032 Author: Ulrich Kunitz Date: Sat Feb 3 02:12:59 2007 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit 65a0d2df49d3b24b5707b1d16de13aa8595358df Author: Michael Wu Date: Sat Feb 3 02:04:22 2007 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit 046c73baf53368a19b11b229d52b9564f11a2463 Author: Daniel Drake Date: Sat Feb 3 01:58:05 2007 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit 500f499c8fa140220ae6cdfba97f1b79b0d5abbb Author: Daniel Drake Date: Sat Feb 3 01:58:04 2007 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit c6031f062db7c2c0b30865fa3363376b4bc20179 Author: Daniel Drake Date: Sat Feb 3 01:58:04 2007 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit fd5f8c9c155bc6eb945269e98e3ceeb4eca77ece Author: Michael Wu Date: Sat Feb 3 01:52:23 2007 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit bfa33015249d68343601cc491c0b7b6e28bd2257 Author: Michael Wu Date: Sat Feb 3 01:44:17 2007 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit ca4e68cabe71c174b352a80450ead05a1b740fac Author: Michael Wu Date: Sat Feb 3 01:32:19 2007 -0500 [PATCH] p54: set MAC address properly This allows the MAC address to be changed from the EEPROM default. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit 3045cfe162d9d317733b99d47a216dbf3e80c07d Author: Michael Wu Date: Sat Feb 3 01:26:57 2007 -0500 [PATCH] adm8211: set MAC address properly This allows the MAC address to be changed from the EEPROM default. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit a085274abe83305afaf72228f1f95b8270e313b9 Author: Michael Wu Date: Sat Feb 3 00:46:28 2007 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit 9a7b12360c680f164030d9e1790f8db71797c1a1 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 Signed-off-by: John W. Linville commit 32b5d4c854756041ef70285b51ea4c59e56710b4 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 Signed-off-by: John W. Linville commit 0936348277ed64fa0a14b16de00874eb3e10e3c5 Author: Michael Buesch Date: Tue Jan 30 20:18:53 2007 +0100 [PATCH] 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 Signed-off-by: John W. Linville commit 4b9e9765db8076f00eb87f0975e5e72f406ac630 Author: Michael Buesch Date: Tue Jan 30 18:10:08 2007 +0100 [PATCH] bcm43xx-d80211: Add missing PHY version register masking. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 357b0a0496ee0b510448c441670b8cb1e458ccce Author: Michael Buesch Date: Mon Jan 29 17:45:26 2007 +0100 [PATCH] 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 Signed-off-by: John W. Linville commit 6eb5b2a393ac5e83eb157fcc160897baccb88aa1 Author: Michael Buesch Date: Sun Jan 28 01:30:06 2007 +0100 [PATCH] bcm43xx-d80211: const-ify ieee80211_ops. Now that d80211 allows const ieee80211_ops, convert them to be const. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 4c3eb23bb217bbc99e315fd12d8c8db047b1a133 Author: Michael Buesch Date: Sun Jan 28 01:12:13 2007 +0100 [PATCH] ssb: pcicore: Remove FIXME. The busnumber is OK. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 3987814da07464d46da5ca3883b1e17a820a5462 Author: Michael Buesch Date: Sun Jan 28 01:02:27 2007 +0100 [PATCH] 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 Signed-off-by: John W. Linville commit b46b277ee119c79c7bf8c8612cf3e60335370281 Author: Michael Buesch Date: Sat Jan 27 20:55:17 2007 +0100 [PATCH] 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 Signed-off-by: John W. Linville commit a174cd8a39ae8028ed716f64d312c181a5db1904 Author: Michael Buesch Date: Fri Jan 26 23:48:26 2007 +0100 [PATCH] ssb: Fix TODO in chipcommon driver. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit ef49f1a435a5c3a7d0972315e23367466487e996 Author: Michael Buesch Date: Fri Jan 26 22:12:45 2007 +0100 [PATCH] bcm43xx-d80211: Fix a bunch of FIXMEs in the loopback_gain calculation. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 3444fe45dd9b522e73128912f04b2601f1a5f0c2 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 30a07392ca9fe512cd6f0ccd1fb2693535fa0383 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 c7ddee3a6c501f4acecc5dc03b606704ed6c8d17 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 20b95b202eba99150e5546d2bc011e5a3575477a 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 c817906fd39a1a9c8657a6654407800f5d61571c 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 7b5868c353b319f3f4b27a0ca82c890d12671137 Author: Michael Buesch Date: Wed Jan 24 12:08:39 2007 +0100 [PATCH] 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 Signed-off-by: John W. Linville commit a5dcf2a8c08c7268e2540dc48114d78c6fc497b8 Author: Michael Buesch Date: Tue Jan 23 21:51:20 2007 +0100 [PATCH] 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 Signed-off-by: John W. Linville commit 9db08a0ee3c1eac8a920fe5da616566fc1f6c465 Author: Michael Buesch Date: Tue Jan 23 17:14:37 2007 +0100 [PATCH] ssb: b44 related fixes. Some fallback fixes. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 20fe2597f27260d322ed22e6fe85263a25634746 Author: Michael Buesch Date: Tue Jan 23 15:40:30 2007 +0100 [PATCH] ssb: add PM config register definitions Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 97fe60a8698a75ff7ab2a064c85e579864d1ff93 Author: Michael Buesch Date: Tue Jan 23 14:44:43 2007 +0100 [PATCH] ssb: export ssb_clockspeed() Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit ccdeefe8bca8c15bc86d6682bc1d1d13f823c30b Author: Michael Buesch Date: Tue Jan 23 12:59:07 2007 +0100 [PATCH] ssb: PCIcore hostmode fixes. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit e899c70d6830a61cad8c762622e777dfa9efe3fd Author: Pavel Roskin Date: Sun Jan 21 00:27:40 2007 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit 56d6808d666d6804cb8acd06345e8c4c0d6c28bf Author: Michael Buesch Date: Mon Jan 22 11:25:01 2007 +0100 [PATCH] ssb, usb: Implement SSB based Broadcom USB OHCI driver. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 4e058f18b2dae5eb1d1e1b62fddc8415e7661056 Author: Michael Buesch Date: Wed Jan 17 15:29:12 2007 +0100 [PATCH] ssb: Add missing include to delay.h in ssb/pcmcia.c Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 1b9791930ed80a44e483725250bc547912370ef1 Author: Michael Buesch Date: Tue Jan 16 21:37:59 2007 +0100 [PATCH] bcm43xx-d80211: gphy init: Some cleanups and some bugfixes. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 7d3b5fc27950bd04265d97757a551ced14821222 Author: Michael Buesch Date: Tue Jan 16 17:42:00 2007 +0100 [PATCH] bcm43xx-d80211: Fix semantical errors in LO measure setup. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit d4ede14f5a7aad699aaa6904c4277d642fc14652 Author: Michael Buesch Date: Mon Jan 15 21:05:07 2007 +0100 [PATCH] bcm43xx-d80211: Various cleanups all over the code. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 4d1313e3c2a369ce5242a8a8fed796275f3a4ca3 Author: Michael Buesch Date: Mon Jan 15 18:59:50 2007 +0100 [PATCH] bcm43xx-d80211: Fix error return codes. This fixes operating two or more interfaces on one card. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 6203504c5a402d5f48b5028bac2ed10de8f798a8 Author: Michael Buesch Date: Mon Jan 15 17:57:35 2007 +0100 [PATCH] bcm43xx-d80211: Fix wrong register write in lo_measure_feedthrough(). Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 0bf811c624dd8ef07670b6939a36f857ebd6abee Author: Michael Buesch Date: Mon Jan 15 14:54:29 2007 +0100 [PATCH] bcm43xx-d80211: Get rid of "PHY-connected" semantics. "PHY-connected" are really "G-mode" semantics. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit b89d14143aa71d293c63c4b0abdd60452f16c27b Author: Michael Buesch Date: Mon Jan 15 13:31:05 2007 +0100 [PATCH] bcm43xx-d80211: Remove leds_exit() call in detach stage. It's wrong and crashes. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 6a4b652959ed84c7c93fdad6ef7a64688154c4f3 Author: Michael Buesch Date: Mon Jan 15 12:22:31 2007 +0100 [PATCH] bcm43xx-d80211: re-add chipid printk Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 4e0287eff51ee4f7a91c7e0cf9f69d744da071f8 Author: Michael Buesch Date: Mon Jan 15 09:21:10 2007 +0100 [PATCH] bcm43xx-d80211: Support for PCMCIA devices. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 34145ed97bb130f397f96854371955aebe4150a1 Author: Michael Buesch Date: Mon Jan 15 09:18:21 2007 +0100 [PATCH] ssb: PCMCIA-hostbus support. Support for a Sonics Silicon Backplane on a PCMCIA host device. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 2f98c3073148aff48934375d418c12ac0d8ac8fa Author: Michael Buesch Date: Sun Jan 14 18:15:20 2007 +0100 [PATCH] 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 Signed-off-by: John W. Linville commit 2327dfb8dc7458ed829528783e7f2a1aada58581 Author: Michael Buesch Date: Sat Jan 13 22:58:20 2007 +0100 [PATCH] ssb: Fix busnumber assignment. Must assign it before scanning the bus. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit cc69a6a9d9d3fc5dd1bbb3e896a3188c97d9ec65 Author: Michael Buesch Date: Sat Jan 13 18:46:58 2007 +0100 [PATCH] ssb, bcm43xx-d80211: Add function to set DMA mask on SSB. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 9988f25e69bb7204c374b2498732b7ebcdcffd9a Author: Michael Buesch Date: Sat Jan 13 18:12:05 2007 +0100 [PATCH] bcm43xx-d80211: Remove bogus call to refresh_templates in add_interface. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 39ceabb1e30cb36751341469db9369ac9281aa8e Author: Michael Buesch Date: Sat Jan 13 17:57:54 2007 +0100 [PATCH] ssb, bcm43xx-d80211: Move DMA translation logic to ssb. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 8d735290453e00695a0f972308337f9012b72b81 Author: Michael Buesch Date: Sat Jan 13 17:04:08 2007 +0100 [PATCH] ssb: Fix typo. SSB_PCICORE_SBTOPCI1_CFG1 does not exist. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit b56a8b410e44451ea2f8ebda72be5dc179f3f506 Author: Michael Buesch Date: Sat Jan 13 17:02:03 2007 +0100 [PATCH] ssb: Fix dependencies. MIPS core must depend on MIPS platform. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 5dbc339c8bce97464849e6e19764d36313c3dad3 Author: Michael Buesch Date: Fri Jan 12 18:34:56 2007 +0100 [PATCH] bcm43xx-d80211: Fix LO feedthrough measurement. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit aee854b90ea471d449c4c470461c80cf8edd3392 Author: Michael Buesch Date: Thu Jan 11 20:04:19 2007 +0100 [PATCH] bcm43xx-d80211: Port driver to the new SSB subsystem. This ports bcm43xx to use the new SSB subsystem. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 3b844b8a33b8bec1db485286572576960d63366f Author: Michael Buesch Date: Thu Jan 11 20:02:17 2007 +0100 [PATCH] Implement new SSB subsystem. This implements a new Sonics Silicon Backplane subsystem. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit e09febc5b78243399cedbccf63163e9f93750618 Author: Michael Buesch Date: Thu Jan 11 19:59:49 2007 +0100 [PATCH] Remove obsolete SSB driver library. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 1df9ea2d56e8f5cd49d54b8c9c6c6ed88277d0a5 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 Signed-off-by: John W. Linville commit 0b44ee7f33dd71d8d544d9aeddf7ba1e83e55489 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 Signed-off-by: John W. Linville commit 7948e14a339637cf771622271e57deba74329d65 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 Signed-off-by: John W. Linville commit 4eb5c30f2aacf734c163fb6b6ab00b144d686010 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 Signed-off-by: John W. Linville commit 700104bfab7a20ee3ed9aca9ba584417fba7ae91 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 2c22b65f2b2f6378d4fb36b4cb25d10e790a96cd 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 d7bc6a6c3622d4bffa1f03943eb92535c47ec280 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 c82b473d878f7b90acaf9d6409c2f2f97c83aa8a 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 13bc411ffb9f11bf026976b467694ffa4c5e366a 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 5b5e5a18124be5b9a51acce3cf43b4386c7210aa 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 af553314578c2c00d4e97431c2761ca7231b6f69 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 d3cd450ac085cd6f5b05f1f11f98175ed7b89354 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 d4f5ac502ccd0f623aea439cb9bd24373beb4931 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 694ab207ab2b0d97cdb2b2fe285a9787284954c4 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 372f60a4e9ba9ebd4350872b0bbe92e2934e652c 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 763f193aab6200aeade47b7f45be0ea30642f99d 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 Signed-off-by: John W. Linville commit 5eae6f78693b6201aa3dc5588d3601c9ccd373b2 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 Signed-off-by: John W. Linville commit b22410e3ea5a2e8ba7ebb8c3bdb8b8f98f6df3b8 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 Signed-off-by: John W. Linville commit 53a903fc67a2cf594bebcad129eaa0eb4fdb0294 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 Signed-off-by: John W. Linville commit a6c4d3e9251b4c200aa70e0b37f76565a14b3a44 Author: Michael Wu Date: Sun Dec 31 03:38:07 2006 -0500 [PATCH] adm8211: set phymode in RX This makes adm8211 set the phymode in ieee80211_rx_status. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit 7544a06592f0c2fb6f99cc25a2639c61ad88d24c Author: Michael Wu Date: Sun Dec 31 03:32:55 2006 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit 02de28111e2942c4be91f2473033e6accaff04e9 Author: Michael Wu Date: Sun Dec 31 03:29:52 2006 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit a37b36fcb6894873d75fbd41bd39e2bf07fecd89 Author: Michael Wu Date: Sun Dec 31 03:17:16 2006 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit a7f62761f1ee9c189ab4d74162aa1e3bedbe7155 Author: Michael Wu Date: Sun Dec 31 03:16:10 2006 -0500 [PATCH] p54: fix issues found by sparse This fixes a number of issues in p54 found by sparse bitwise annotations. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit baf50411d1532ee5595e846ff3d445c71930222b Author: Michael Wu Date: Sun Dec 24 22:18:59 2006 -0500 [PATCH] p54: use link LED This turns the link LED on when a valid BSSID is set. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit dbcfc75f710473700c288fab037fb7de46d61d8f Author: Michael Wu Date: Sun Dec 24 19:05:15 2006 -0500 [PATCH] p54: use hardware RX frequency reporting This makes RX report the frequency/channel provided by the hardware. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit e764dedefdd369072daf32efe49bb09713124717 Author: Michael Wu Date: Sun Dec 24 13:07:56 2006 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit e304a5d61a38f4c4b7f022e53e2bf4001486bf38 Author: Michael Wu Date: Sun Dec 24 12:48:51 2006 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit b6dc4d17686166d8ef3c7895b96d1bebc84d029b Author: Michael Wu Date: Sun Dec 24 12:45:02 2006 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit d3c183e830f251dd34be1afdc4a442db597f7939 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 Signed-off-by: John W. Linville commit 2013f238947f84565469c12f137a056ad8c7fbe2 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 Signed-off-by: John W. Linville commit d428d3d31da5a8dab065c92c53a46d2487082e7a 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 Signed-off-by: John W. Linville commit 6552cada5876cd12616f0e9da07e2c4f9bb4e98f 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 Signed-off-by: John W. Linville commit 99ee9488ac3ee4ca9d59f8dca0e56ead0d41d195 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 Signed-off-by: John W. Linville commit 0562cf3e97e6ac35a032aafca22cf6dbb7a32f24 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 Signed-off-by: John W. Linville commit c3f18d6accff5be005f1d9fb222d1bbe40551325 Author: Michael Wu Date: Sun Dec 10 22:12:10 2006 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit 57551d2b134b5c93164d94d6691101cafe4b2e75 Author: Michael Wu Date: Tue Dec 5 18:47:30 2006 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit 223b9eb1efda76facfa18bc08c7ab4b279426f7b Author: Michael Wu Date: Mon Dec 4 01:36:56 2006 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit 78b571dd20c852b807aa686343d33fa85d3f436c Author: Michael Wu Date: Mon Dec 4 01:36:51 2006 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit 0ccb3122dd776ad5e5de255ecb7049d7c132364c Author: Michael Wu Date: Mon Dec 4 01:31:32 2006 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit 749599d2828599522a3a1a3849a588f7bcce49e8 Author: Michael Wu Date: Mon Dec 4 01:31:32 2006 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit a8603b5393b823f83d2b3e005a120e0705b8df35 Author: Michael Wu Date: Mon Dec 4 01:31:32 2006 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit 209c427d1502518ff494937cc3dccea8291baf2a Author: Michael Wu Date: Mon Dec 4 01:31:32 2006 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit 6eee53f98b467b80e9560283fb93c87477dedf42 Author: Michael Wu Date: Mon Dec 4 01:31:10 2006 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit 6f4cd9328a233f8879c2ead061c9b59c39e3df7b Author: Michael Wu Date: Sun Dec 3 23:47:35 2006 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit 4b3541af4a6ee8ef37274edaf5b3a83811e441e8 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 Signed-off-by: John W. Linville commit 65387fe55b947a3bf15288155c52f315048776a8 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 Signed-off-by: John W. Linville commit 9a13149dc71645be57755979a03815d8f5e8ddc8 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 Signed-off-by: John W. Linville commit f2313a7d95004e3f2cec08097f139fb672f988f2 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 Signed-off-by: John W. Linville commit 43ee20f29626e699d3854f745868683d06f03fd1 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 Signed-off-by: John W. Linville commit 44375f2de874bdb19398d65fcbcd0b6de9576f70 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 Signed-off-by: John W. Linville commit ccb53bf3497095f7d0d775d9c0fac325e8cf13dd 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 88148addea280f53b4457203c0238323f42f1861 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 fb0f309035dd7d80ae3a114c6074df8a780a12f0 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 b8a35057a928914ffead07fea944529083f1ea8f 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 14d69f725d236fa8232e0175419545d45cc19a1a 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 75332986a81b075ab40cc2cdaaae63b9e7a76698 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 0cc6b9e04db56a8c488baa68e13d2940c016d087 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 65e8396ae6b6e004d80db37734cb09d438a2019d 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 8be3ccaf1492ac06982e28bc56d924f5634bcf46 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 cbe4ebe29819ccd81219d1211fb95213f0bcc4c4 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 470edeca5475c0bc9875a796075b86916fa34de0 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 8abcd51165ec95806b932bd1fd6373bb53154a49 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 ee304d4d637db77bebc3bdfef6dd362955b73f4d 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 e79ed5bf1124f4eb05d7778b39598cd3afb52322 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 d396ffbd97ee2bc73d2c62fee6b031492a885366 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 13b6983aa8ec7b9d7165f48b0c0e95ff156a6661 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 1c0bbf32e98200667b6235968de3b441bb16a0c1 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 e53378e702f5a47b5305acb6f664014418840d7e 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 f496d2fcf051cbfb1080e36468e7b71a63929867 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 922357707a6293cd7f4a543ffc954a5a21ca2247 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 5db293ebe46f06c44a0ea467f18802d275c8d2bf 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 8a0dee8f201a41c0387f21fb954711f130dc77e1 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 f67462b7bfeb7a3f95688db79cd9c00832cd5308 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 43ecfa612c61a186aab083c608afb95d7297659d 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 22990548c9dd59cf6be04a56c40434759f5bce7b 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 0df39c4d2523df14130fe2bd9409dd7bbfed7459 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 f641ece8bd6d1cee800821c6ad5d58c49993736d 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 058e362cce5659a24d5693bb4e79879462005f17 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 06f9c3b96ac74ff2142dee0d5770218a823467b3 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 Signed-off-by: John W. Linville commit abb4ff82b464b01f64a3892d42a25c1827abd155 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 Signed-off-by: John W. Linville commit b88a73f72e48573bec70bed1587ebd5567dc74f4 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 Signed-off-by: John W. Linville commit 681c53b95e38210053629c31afd12966dca9f691 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 Signed-off-by: John W. Linville commit c12243cb89a83a3dee8d4f0a4186ec730cdb1edb 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 Signed-off-by: John W. Linville commit 1391d4e3089fba5d4009c3f0dda634e839aba658 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 Signed-off-by: John W. Linville commit 589815ef7a093fe73a20c203a1e6ffc1fb8a5369 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 Signed-off-by: John W. Linville commit fb34638dbe73decaae14d74222f75f1e236b1afc 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 Signed-off-by: John W. Linville commit bf9a8477f94a864faf936b37379812f3b957f5e0 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 Signed-off-by: John W. Linville commit 9235ed6c6b78ee59ac78d64ab482d241dff3055f 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 Signed-off-by: John W. Linville commit 388ae7a0f3961363090d6659560fa772580a9073 Author: Michael Wu Date: Sat Dec 2 02:25:26 2006 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit 11ff53e552c99788e3f14e935834ffd58ec41b08 Author: Michael Wu Date: Sat Dec 2 02:16:27 2006 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit c2fcb917d7cf32c0ef4e3ba6346e478f4de05ec3 Author: Michael Wu Date: Sat Dec 2 02:00:12 2006 -0500 [PATCH] zd1211-d80211: Hook up Kconfig and Makefiles This integrates the zd1211 directory into the d80211 Kconfig and Makefiles. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit 385359505edb10c476c4d899128f460ac9f9e938 Author: Michael Wu Date: Sat Dec 2 01:55:00 2006 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit 314ff35f394c433f9ede5327556d195b830ea8e2 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 33d337a37fc8629cd5c8b3b4d3363042e9d897a3 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 7612b5d30a6e8f960ab71a0bfd81f20f21d682f1 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 e152c213ca8e9b073d2a5572a7972c3be4771351 Author: Michael Wu Date: Mon Nov 27 12:50:15 2006 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit a07cf97cfee48274d981e7f5c94ff13604ae7923 Author: Michael Wu Date: Sun Nov 26 22:42:34 2006 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit d36f3388c614cc9875d5a2ae6829b2e13a82f1f2 Author: Michael Wu Date: Thu Nov 23 23:04:08 2006 -0500 [PATCH] p54: Convert to new API This makes p54 compile again after the recent d80211 API changes. Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit 6f384f0f482f86f465a26d5e9c0a8c67490f43c1 Author: Michael Wu Date: Thu Nov 23 23:02:26 2006 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit d338bd59230f46da35b8d0630d43cc5d92de4d66 Author: Michael Wu Date: Thu Nov 23 22:56:49 2006 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit 26049310ff2c5059d3b4b38fdd365a70e1e9005f Author: Michael Wu Date: Thu Nov 23 22:50:21 2006 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit 4737d08ac152239f3939fddb1352b508f22ba9c8 Author: Michael Wu Date: Thu Nov 23 22:32:28 2006 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit 447e8d38575d9c2904cf5e1f9406bac5cd737c94 Author: Michael Wu Date: Thu Nov 23 22:26:30 2006 -0500 [PATCH] p54: Add license boilerplate This adds some copyright/license boilerplates as requested by Jean-Baptiste Note . Signed-off-by: Michael Wu Signed-off-by: John W. Linville commit 7a29e782084b7ece0015ac2e86b7e5e074fcb907 Author: Michael Wu Date: Thu Nov 23 22:19:48 2006 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit 34abb10ef2561e381b7bf16f05973c24e017cb52 Author: Michael Wu Date: Thu Nov 23 22:06:45 2006 -0500 [PATCH] 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 Signed-off-by: John W. Linville commit 9f082d65a81020c9f0fc9d5d1299afddce2a1f40 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 Signed-off-by: John W. Linville commit 7fb330f9cbf1c8ffddcb5738895e4bca0f5de8c1 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 Signed-off-by: John W. Linville commit 26eba1da489fa5817e609cf145d7ac8ec7765ed3 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 Signed-off-by: John W. Linville commit a76f6fd03124767f94aee212ce6d1b9d0e90b159 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 Signed-off-by: John W. Linville commit 42414ca0f38a5782f4272397fceb694cea576cef 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 Signed-off-by: John W. Linville commit 4c4649a0272cf67ebcdab49c081eec831f1651d7 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 Signed-off-by: John W. Linville commit 13f124047acac4c673523aa4fdfa04b52fc823c3 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 Signed-off-by: John W. Linville commit cd1a33ca161d6d32a1e5d67f9f190e284209a7a3 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 Signed-off-by: John W. Linville commit 97144cd38554d1164421c83566dda5808e37924b 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 Signed-off-by: John W. Linville commit f3d8cf6cbee4a049855a48045489f97b41c2c589 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 Signed-off-by: John W. Linville commit e5749161abc86c8352b1759672c6ffb91f9aa225 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 Signed-off-by: John W. Linville commit d4cf2abdde9f26152214b696519c361399120c4c 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 Signed-off-by: John W. Linville commit bd0c304cd406ac529c41f1e42c0b7b2168a51a76 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 38248329e5c7a5f2e9dfd1879b66f9ddd7e3fd8e 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 5cebcfff7fd64550c9f8cd9fe9b927da84cc3fe3 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 f6d401051750bcba5192b37ffb5448e0cff8edf5 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 8f7c063a472e3e4a9de2a44f6a3bd2245379eb35 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 99edae51071609364ea7ccc8e096c04aa1883ae5 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 4e256899edd78d6dd83738011543ab0b7c7d9ff4 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 100e39155b5c8e067a95f1a1b3cbeeefac3b025f 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 cb55316c6f47028a6fb9ccf765e7bb1f8908a32c Author: Michael Buesch Date: Tue Nov 7 20:36:53 2006 +0100 [PATCH] bcm43xx-d80211: Remove netpoll and ethtool stuff. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 6f27221f3d53141e1ff4a53921b85e452e43d3a0 Author: Michael Buesch Date: Tue Nov 7 20:03:23 2006 +0100 [PATCH] bcm43xx-d80211: Fix bogus LO validation failure. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 69d558a9fc1733dc8b3606d9651f0cd13f6d5f84 Author: Michael Buesch Date: Tue Nov 7 19:16:16 2006 +0100 [PATCH] 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 Signed-off-by: John W. Linville commit 2d43c9b21ae673657389229537993903702c4b02 Author: Michael Buesch Date: Sat Nov 4 20:55:04 2006 +0100 [PATCH] 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 Signed-off-by: John W. Linville commit 505ce7bb8ab1d4746e5c37a583f000160147bea9 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 f5a56c740be9ceeb4b2f48cd984bd7aba2cdb91f 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 e65180317eb5e5bb3502a877aaf2fbad35cbc07a 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 46ba05c26568547d83f10944bc047633efb14405 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 c898fb0f4c7ecd170578d4ca9473f0307c212cb3 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 7e8f7eed9277067819796b7ca9ce4ada8681afad 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 4bdf77506eaf6bcd624950878e40a08525062f4d 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 df33e27da422e1f1cd807a0154095cde8fb5f83e 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 cbcc158b0e3bb8d45a7079735988c65f80cc6939 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 88987baeb682745335fdb681cd5272e7bfb7d1e0 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 3b19b5464bb663442b1d6f75cbcf35ddcd772e83 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 dc175d26308f49fc71eb5edd55fdcd65ce5869be Author: Michael Buesch Date: Wed Nov 1 18:18:22 2006 +0100 [PATCH] bcm43xx-d80211: Merge all "radio" stuff into phy.c Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 80815a8a160a428ab62f00fb1c63e5a3240ca6ca Author: Michael Buesch Date: Wed Nov 1 17:43:19 2006 +0100 [PATCH] 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 Signed-off-by: John W. Linville commit f5a52d95d8ca73fe2cf43dc8728c2c9f189b431d Author: Michael Buesch Date: Wed Nov 1 16:32:17 2006 +0100 [PATCH] bcm43xx-d80211: Rename struct bcm43xx_phyinfo to struct bcm43xx_phy Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 24813bd1eefe2bf7a0a504f1151746ab5f4d3421 Author: Michael Buesch Date: Mon Oct 30 23:44:28 2006 +0100 [PATCH] bcm43xx-d80211: Fix compilation: Missing files for LO and VSTACK. Forgot to git-add them :-/ Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 8d5031fb4084570c6aaa1599fe7a90d142510ab6 Author: Michael Buesch Date: Mon Oct 30 01:22:22 2006 +0100 [PATCH] 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 Signed-off-by: John W. Linville commit fa149ec2cd2cc24842a476696fec352e25668d29 Author: Michael Buesch Date: Sun Oct 29 21:05:09 2006 +0100 [PATCH] 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 Signed-off-by: John W. Linville commit 1064e4014a37552538b2f03e5ddd1a5f44b24a1c Author: Michael Buesch Date: Sun Oct 29 18:17:30 2006 +0100 [PATCH] bcm43xx-d80211: Move ILT stuff to OFDM table stuff Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 4856b400394bab92356006d78a15c39a85e3d4e9 Author: Michael Buesch Date: Sun Oct 29 16:09:40 2006 +0100 [PATCH] bcm43xx-d80211: Add some PHY register definitions. This adds some new known PHY register defines Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 03181015cddbfb70f665ae9076317bbbb7d06982 Author: Michael Buesch Date: Sat Oct 28 17:16:27 2006 +0200 [PATCH] bcm43xx-d80211: Don't ignore return value of pci_enable_device() Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 2f2ae462614fcc3e4bafcb41410b47215aa5901c Author: Michael Buesch Date: Sat Oct 28 17:09:12 2006 +0200 [PATCH] 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 Signed-off-by: John W. Linville commit b109b9bd6e23f8ef6db54d1cd9f8cb1445eaa833 Author: Michael Buesch Date: Thu Oct 26 16:07:26 2006 +0200 [PATCH] bcm43xx-d80211: Only set USEDEFKEYS hostflag for WEP. Default keys are used implicitely for group keys in WPA. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 894001ccb11466b72bac6e33decd592c8aab0032 Author: Michael Buesch Date: Thu Oct 26 16:00:23 2006 +0200 [PATCH] bcm43xx-d80211: No support for hw encryption with v3 firmware. Various hwenc fixes. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 6807dcefabc257a158b2567ce83ed2238e71d2eb Author: Michael Buesch Date: Wed Oct 25 21:04:08 2006 +0200 [PATCH] bcm43xx-d80211: Use software encryption for TKIP for now. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit dcb0bbe7577bf69dc9b5030812c7f2efc91222c5 Author: Michael Buesch Date: Wed Oct 25 20:41:21 2006 +0200 [PATCH] bcm43xx-d80211: Fix hardware based encryption for v4 firmware. This enables hardware-based encrption. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit b238480e8b8a6fc213b78d33fd6f4d1fe219e1f7 Author: Michael Buesch Date: Wed Oct 25 19:34:37 2006 +0200 [PATCH] bcm43xx-d80211: Rename IRQs Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 6365a5453dc43fa9826ee497af98089c177a7768 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 cbd8b3e1be634a8ed748e1171e784774ecd26c90 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 bfca65aff3c0ecaab9a75d2f81d603d802d97c70 Author: John W. Linville Date: Mon Oct 23 22:16:19 2006 -0400 [PATCH] adm8211: remove #include Signed-off-by: John W. Linville commit 1406290027cb5a423846cfa054b7ffbf44969310 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 aa540bd39f4a0e811540748b302bcd0a89e6f8c9 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 358a7c3a19ea505e9ed289f2c9345ab8ff524431 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 866b0ee8bc14921676aa1d6de2b941f53bacd4fe 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 40c63d1b9930190a5f8d6b761723f9a7d3db5874 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 2e196e22cfa01815b4c57704a80b1b2f2eed1eec 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 Signed-off-by: John W. Linville commit cfa72ea673e6e787b33a1a4c9c9f27bbd6ed0957 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 Signed-off-by: John W. Linville commit efc41bb9b5276d0c7f040ec26335d5c1b85e0fc2 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 Signed-off-by: John W. Linville commit 80f65e736b871383f63e0540701fb98cd82b16c9 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 Signed-off-by: John W. Linville commit 65429439edc69fabcdc92fea96aa8657569da866 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 Signed-off-by: John W. Linville commit a46cc6b9bc9bd4645cbe93e64b50d2f6a4546452 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 Signed-off-by: John W. Linville commit 213e87840cf04179c11be00bed3cc5b31acb437c 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 Signed-off-by: John W. Linville commit 0273cc6e8fe7df979d3be31fdd53345986d5b162 Author: Michael Buesch Date: Tue Oct 10 00:11:17 2006 +0200 [PATCH] 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 Signed-off-by: John W. Linville commit 97047bff81854cd7ed51266332782ffb1ab2ea50 Author: Michael Buesch Date: Thu Oct 5 16:43:47 2006 +0200 [PATCH] bcm43xx-d80211: Remove unused "err" variables. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit fdf0038eb5850d1f56158fc8b7c0e148235fe81f Author: Michael Buesch Date: Thu Oct 5 16:39:36 2006 +0200 [PATCH] 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 Signed-off-by: John W. Linville commit bc257a5ffb3d83aad6048f3efdc364248d84af7b 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 Signed-off-by: John W. Linville commit 2790deeebe58830b9e899266e88a4b001fa2a5a9 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 Signed-off-by: John W. Linville commit c0c08f101e91707d25eaee962276b641cbc88bbf 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 Signed-off-by: John W. Linville commit 0f3b8670874ca06bf274e77f79f632c164a8db2d 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 Signed-off-by: John W. Linville commit 8536c76108d38b0aed84d2a3a099afb9db7c2fdd 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 Signed-off-by: John W. Linville commit a004c0d88bdad464f647b7f80f9d54de3d67523c 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 Signed-off-by: John W. Linville commit 51aa8905ebff1f86e0f097b1675e15e09f834e62 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 Signed-off-by: John W. Linville commit 26f3df19a76ee4fb7496a274e4454d569d538a05 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 Signed-off-by: John W. Linville commit f0a8793f5943925b89f6781443fd5f34cf0f5f1e 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 Signed-off-by: John W. Linville commit 5eaef093b06f39704e4469587e9cc96f8638076b 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 Signed-off-by: John W. Linville commit f7f61313d4a584fcf1f71a877ee88f98dbd0c75d 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 Signed-off-by: John W. Linville commit e966624ee03347c1a37c81df0f0e6e076cf7bb3d 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 Signed-off-by: John W. Linville commit e1b48216abcb6a8b2ddc60e5e7937c4709962e23 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 Signed-off-by: John W. Linville commit 99892d0aafc3b42bd86e11b97acf3a843f0e379b 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 Signed-off-by: John W. Linville commit 6470718771983725e7949d07a9e9d5abf9d20c55 Author: Michael Buesch Date: Mon Oct 2 18:06:33 2006 +0200 [PATCH] 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 Signed-off-by: John W. Linville commit 6ecc018c4930bf40da2e9b84c81199f70084ebdd Author: Michael Buesch Date: Mon Oct 2 17:38:06 2006 +0200 [PATCH] 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 Signed-off-by: John W. Linville commit 8a1917a611f18754e37e8366fdd723165c1f63c8 Author: Michael Buesch Date: Mon Oct 2 17:25:34 2006 +0200 [PATCH] 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 Signed-off-by: John W. Linville commit a15c1c3b309cd861cc62003d2cc453ff97f7ecc3 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 Signed-off-by: John W. Linville commit fd57009d64677ac82cbb744fed51ded183728093 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 Signed-off-by: John W. Linville commit 7ec39838f6996e8c712df6d6bf8f5ca1cad32146 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 Signed-off-by: John W. Linville commit 0256f4eb896352acce7ab32e1ee94c0fce4e8371 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 Signed-off-by: John W. Linville commit 003140d2f9b7e6f23f592c181b8f12c15332842a 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 Signed-off-by: John W. Linville commit 65b53439f7838e6448169b1489b1dc63ebbee8b1 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 Signed-off-by: John W. Linville commit 603a56c95c0234f6d6f86916b076cdb069b0ac8c 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 Signed-off-by: John W. Linville commit 6dfb67075129e566dea2a56a830c8d52d311279c 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 Signed-off-by: John W. Linville commit f203b6eddf419412ece43ea6f2271888b4cf3dd7 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 05525d8a75cc18d32a81d491e591fb070fb073e1 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 0bf027daaa34279904eb24e7718663398cab23e9 Author: Michael Buesch Date: Tue Sep 26 18:56:10 2006 +0200 [PATCH] 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 Signed-off-by: John W. Linville commit 655557421bfbea41683284c9785da5d8f3e663bf Author: Michael Buesch Date: Tue Sep 26 17:32:54 2006 +0200 [PATCH] bcm43xx-d80211: DMA-mask fixes. This is based on a patch by Larry Finger. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit c9a1c7e6b108d20efe02c94c2384afea783120c1 Author: Michael Buesch Date: Mon Sep 25 21:41:46 2006 +0200 [PATCH] bcm43xx-d80211: Don't use low level netif and ieee80211_netif_oper functions. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 30a0f45e90b4fad812c7e9a8a7262a99f8a601bc 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 Signed-off-by: John W. Linville commit 3affca911a05ec32cfff94d064898d94eb6b0150 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 Signed-off-by: John W. Linville commit 6c305a0f8e12ced5b218df4746dd89cd4978b6ba 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 Signed-off-by: John W. Linville commit aec45517502b8e72958d267c6999818192c50fb4 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 Signed-off-by: John W. Linville commit 2daa72c94a5a9fe3a793ebed3a8d3e2df32ad7b7 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 Signed-off-by: John W. Linville commit 2e7331e54ed6b4624b71ceb0a43c290dbb47388a 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 Signed-off-by: John W. Linville commit 308c6941994b01fe14db1997b547e4b628d4f43c Author: Michael Buesch Date: Sat Sep 9 18:13:36 2006 +0200 [PATCH] bcm43xx-d80211: add SHM constants Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 8bf38c1f3f1b2606c6751fb4d3452aee41b995d0 Author: Michael Buesch Date: Sat Sep 9 16:02:32 2006 +0200 [PATCH] bcm43xx-d80211: Add support for v4 firmware. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 0040347b30c1b3e2e3eaa19ff9eb525b2e9f95f1 Author: Michael Buesch Date: Thu Sep 7 01:10:22 2006 +0200 [PATCH] bcm43xx-d80211: Firmware revision/patchlevel detection. This is based on a patch by Martin Langer. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 870986c531991ba7dadd9a941e9bfe3fb040f229 Author: Michael Buesch Date: Thu Sep 7 00:33:50 2006 +0200 [PATCH] bcm43xx-d80211: Always make fwpostfix option available. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit f509102b83b1a56e37f0a97c1e1ae302518ea91c Author: Michael Buesch Date: Thu Sep 7 00:30:37 2006 +0200 [PATCH] bcm43xx-d80211: Don't crash if we use v4 firmware. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 8a6cf32bddc55bb7bdbe7e6c7c2225533a0e3193 Author: Michael Buesch Date: Wed Sep 6 02:31:49 2006 +0200 [PATCH] bcm43xx-d80211: convert to ssb abstract bus access API Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 163b4149e3dd260d5c28dd7377d4ac2b0d7be76e Author: Michael Buesch Date: Wed Sep 6 02:26:48 2006 +0200 [PATCH] 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 Signed-off-by: John W. Linville commit 9daef6564af32c2ac5f4c8d5dc6ef271acc25395 Author: Michael Buesch Date: Wed Sep 6 02:22:26 2006 +0200 [PATCH] ssb: fix init sprom read/write race Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit cc12e2734f19970f8b85b487f405371fb4e3c7fa 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 a6097d5f58286139899ecb578a072802537caee2 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 2c31a5fe3ef1ed1ca210466240668a4e50ced7b4 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 fef537ead20e49c24c09d986775cbbba2a51cabe 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 35a4df388341cb35a1231e430ba9dd5a57af2f74 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 90cc1762fb63d792a5d189dd0d5fc3794500218b 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 850f778dcfe46c6303c880cff2c7e4246903ecba 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 8990bb3700ee83e3b40d62140aa35e4dcb10bcb8 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 92afbc6693c0a7002c61f3d4eb68072238434015 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 bcd7e587b476c316c56c2f84bbef7ada3cf14a0d 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 fc3966183d2617bf488eb2e5cb8e946e16ad0605 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 58dd7b26045a92a98d9c6097ba2a6231d05c31a6 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 dd6ea8f0e5385d617171c7894d8544c1fb60d639 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 ff176a57cf8cd9a279b2b53c688dc84283fcd8b2 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 c1fcdf0b8e840fa392328de3330013999aae2997 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 e816a2036a4923d219e0b64c05bd1c4c6fc9da6a 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 837fa1cca69ed364d60c57b5e7b2784df1ec0c17 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 7057f845deeb31afdf7d3b962d83e65af817e139 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 80e4515777ed76e9787ec7ed8e5bc4a57dac17cf 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 910c90e9cca67f3ac48d3c6a06c1137a5dccec19 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 f38f5b9bd8ab5a89e81ad5fd8d1094ee5092da9c 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 5db01c1aa27cc782e58316cd4992b0b545386c32 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 Signed-off-by: John W. Linville commit ece65502b0383216b2a4fd21233dd136336bfeee 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 Signed-off-by: John W. Linville commit ec2018b4eb1a32df15618e380f5492d0bd813755 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 Signed-off-by: John W. Linville commit 955085dfe12c20103ac9ce69e96459d67e44ca98 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 Signed-off-by: John W. Linville commit d06a16624014e4c09793533240224de4b14a3123 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 Signed-off-by: John W. Linville commit 78b8269f14d958ac3f3766af8b6dae0740734cab 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 Signed-off-by: John W. Linville commit 7788b00ad3d8665265162743253ab0d4a7383edf 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 Signed-off-by: John W. Linville commit fdd2d900dce1ce95fa656967b93337fba5a1ad14 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 Signed-off-by: John W. Linville commit 89035b87258fa3ba4f67a19098672d7a48e87731 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 Signed-off-by: John W. Linville commit 3b94513cb60e95da74495cbfc2126f461ce65bb2 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 Signed-off-by: John W. Linville commit 88e37c42ee0a4003ef2168a61e4d87dd78d7ac7d 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 Signed-off-by: John W. Linville commit 73b79fe028fdac51ee1f13264657a2b287c1a772 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 Signed-off-by: John W. Linville commit fb21605dcc5d216d7de3f3425e2328c52a3153fc 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 Signed-off-by: John W. Linville commit 5862471315916106f7c8086264acfe1405f68348 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 Signed-off-by: John W. Linville commit 30d4d3419e11b4d32a4716c996862bc5638c8ef1 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 Signed-off-by: John W. Linville commit c1846e6107c3908b4cf233999afadd4abcf0a534 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 Signed-off-by: John W. Linville commit d2c1cb5c1ca858a4076a9cb765a82c4430f35a1b 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 Signed-off-by: John W. Linville commit a2b25b5724011ced424f10f2ef81a3594ccf2d26 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 Signed-off-by: John W. Linville commit fabab3b7ddc8f9df5efaf6d92de1185450a3d21c 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 Signed-off-by: John W. Linville commit be3a148cdf93ef5e98acfad671300c14cbfdb0b4 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 Signed-off-by: John W. Linville commit 250ef20b8b9befa8b61725c40bafe9baecf8cd2d 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 Signed-off-by: John W. Linville commit ab1e1d870370a2572ca64c883eff83c31a90f755 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 03950ecc6a3300f2b13aaf1f04a71492cdb79fce 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 267ff34bf96a4c02e53314d6ec8b7df3f171f597 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 434f413e91d44fc12e7318ae9651de05e385581d 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 9c642ab32ad932a04e76d73f3c774ccb89792aa7 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 d546ac47e1f837a46ce22a838ea73938ed157bc5 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 df4a88d4ebc9a3edf19dd142f4a8098a810b47b7 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 d07da8068b040f1411534dbf6e67e7b833875346 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 19a4cc66e6d1d75f4eb34ad7aeb8e251950db46e 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 813ea704ee45d879500b4f3691772716cf9864a6 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 1ce76c61bdc21a33a78cfc6c9eef8c3e666f7e89 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 65c22e71b3a95ecc6a00131834f53281dfa969c0 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 abff34a9356f4e3ec3f2830f8e7a98924b20e018 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 be4438b246ce990d3ac402695dcff2628f8bdfde 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 fe0609648a5297530321baa3698aa279222f9a23 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 6e5758d10cdf894168a85cd1f75fb6740210095e 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 b650f9f664d81b4b7994cb09cc52ede08ac76625 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 0bdfb8213ad3e967d91d280866d6922acf40446e 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 Signed-off-by: John W. Linville commit e96ecbbaf8b7cda1c9484ae653cd5311d13655a7 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 Signed-off-by: John W. Linville commit d767c4e3e8dbf5f9dc4dd6e5fa4081f625fbefbd 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 Signed-off-by: John W. Linville commit 04ecdca33970b6a4f37db68d41075bf5bae242c8 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 Signed-off-by: John W. Linville commit 35e9338e59fe230a87f4d06d6c95f2f0ba235ead 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 Signed-off-by: John W. Linville commit eb3ba7e9e2f6cacf6fd12f051646c3eb42d41207 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 17a32b22b92015b1c8f50dc6e3de06db194bea1e 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 55e63ea9b0f959b60481d336377aa7f624de4b4f 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 74eb446b56b2f6d5d0a8ba1a131bbb0800b44410 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 9a3f46b63a2e5d8023d77f74cdbb5c7d9323b8c0 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 d3d6682604bbc072a5945378c77a4c1f02a48f44 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 e1b74b09ea17b7553679320ceb43288c262c1c88 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 dd0111f1a6fc46bde8f7b99989b821f5208d50ef 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 aca363102f3ebaac9f11755478427e5ee6d82bc1 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 13735b556643bfb7639a8b108fbec3e38653f815 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 24b80b9d8d78ef17f1910b75ee5a9b72d7cfa3af 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 c7e1750f543f9ebc80b9e5bdd99e5bc8bb1ec5ae 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 991e018498fd0dc02655ae9174f06fbe900e42b7 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 4d5e65edd3e7ff144d019a3f13dd7bdc9df712fe 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 ec31ec550e7a41095ddea77db3c7ab472d9ed581 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 7dd55b22603884ba90fd4ad4af82a470db2f2e4f 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 b129caadade3cbc5cf3a3b04a2f0f2b580852feb 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 8a23d1d0ec0d3d8fed5b5cac11b397fe50af8f36 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 c8e275dc615600d279c45be610b302ef503c387c 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 9ed3466791387387c682f85c05a18b6cba1903e6 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 41d6c76ec7ea4fbff62015a9ee2f2f5c1672a3de 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 b49617fae1e75ac66ba3e1499d891a8d62fedf71 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 86ab384961ed98ab0f8c3275a2a7c7c5d8eb8524 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 ac10767c07d797534913ab3c6d54f146ddd01fd2 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 991c1cc6814ac99c5a6515c3586aeecf46a93960 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 34335f2188852ab3c1b45d4d68bf64aa4175f1f8 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 92cc77c6e41b3050f9394acabac584bcd3d1dbbc 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 a77bc44b6269777dbc38fce4bcfcdf03efff38ee 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 4ef9ebf53bd1bd6c5a6c20f55c93aa767a527872 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 133bbb50c22dabc2188a4a5ae64ddb90e180c07a 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 b87007277b2203de2c24fd24bda4803312026be1 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 e40ccc6c0801cac28561f42805aeabe9a50d7a1e 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 cd9ea05a6efaf6a0452c631a22eee0a88a4a45e6 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 ea689b1fbf288808ddd2e88e08853436dafbb091 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 6db028d185ff05d29cf91ef4d2c638b22bc7f3bb 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 3d6c4f419eff8601ab0f6c1c31669dcb2bf31689 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 Signed-off-by: John W. Linville commit d55829f25704f038a4bb9916e14f1b59291dde22 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 Signed-off-by: John W. Linville commit e1b7995897e5e773d56f7db0d71f0e97fda706fe 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 Signed-off-by: John W. Linville commit 7e6d39d7d8e3f1c62a3ee2c6bdfe1be5df2a4953 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 Signed-off-by: John W. Linville commit b6882752fad43929288ef30ed16a6ad83e71d723 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 Signed-off-by: John W. Linville commit adb6cda6a63b0ebb7f33e6477fc3fe97e573a8d0 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 Signed-off-by: John W. Linville commit 93c60699a57a1811975a277049725b64a06e9576 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 Signed-off-by: John W. Linville commit 3b2a9f00497ad2f5cfc3a1276f220240d9c0d139 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 Signed-off-by: John W. Linville commit 2271744a0db7eab3c93efedede30468efe3c2893 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 Signed-off-by: John W. Linville commit a9760e3455120dcf719f4fcfba31a460d30fc09e 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 Signed-off-by: John W. Linville commit 9cd816cc92273ece00151baa911ec3af3584953d 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 Signed-off-by: John W. Linville commit 2c9233651537ff07857c6846dccb4cd59f51ad08 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 adf0da1eaa5b4dd69d0d636515c6f8fb097e126a 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 645b2217e826a7af7fde6787211848b5a0729d60 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 cc53f80c7a4e3c998da7a6e1b538b31a6e920e3a 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 761ea2fb3ba84a4f87c679f3f30183b8dd0160d5 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 de63888fafab601ad0b96666e4a7f4a20e01cd55 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 054be480ba29e4acc38394eb05d585c06c00b37e 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 cc3937696230d69c4742f7d43cdca755381ff82f 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 6857709f59e21554f69a64e4f299fe02250ca30d 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 ca980de472ccabf7aa91f640dfb17473adcdad66 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 Signed-off-by: John W. Linville commit 3132456398ab57a2308c74ee2656306332121b93 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 Signed-off-by: John W. Linville commit 93ed829e0a2d4b630508d008d96c4be40ecedd47 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 Signed-off-by: John W. Linville commit cb4cee78521b0b767792bbdcb9e892c7e9aee2d9 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 Signed-off-by: John W. Linville commit 30ccf37c0055bf28706d629416cb2bb7948bba18 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 Signed-off-by: John W. Linville commit d0c43c17f15f867b6ecc3978f54ec5c860512a47 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 Signed-off-by: John W. Linville commit 2b21a58cce563bc2d5eff10c612322db0721303e 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 37443782180c852f6172dfd0989f8c19b558486e 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 b6a7f502bc8ec99359bd3141333cb98a0c09b05a 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 56ba9b00fc829fdf0889fc6247c45bcba9ae562a 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 89247317b10c227850fbd11c7e65c6e54bf3f470 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 6ed9f17044d1d6f1866c67537afc49c4b58dbeb5 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 6001638e0c97e5078aa8d5758dcf0f40160e60e1 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 4548c70ed4063a69dceb1f7bf7f4f5969a8c326a 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 870b688c3a55c6c87f302a8c0ec02b7851a768eb 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 e0e1a63301b865048cff2bc9f566cbdb35f2b9c8 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 e2fdfaf4eb046789cff008b73654751816e204b4 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 b3a24dbe5c2ec996d5285c9f02f1b183dae0a630 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 e5d5adc0f9c67f4dddb2a146e79da6cc5764fea8 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 de97aafb4631467c2af49a2964084f21eebde118 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 60354514da9730c54ac6e7f4d7ab6ff601999a68 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 8fddb4d33a576c611044e152e1796bb63da6a682 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 a1065b5b05f2a325d99ea0f29f167ea3edcc0377 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 feb106b3ea201441ff0f12e52f794759fcc3bee7 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 fc26608523fa63edb58cea9d787a4b520faedcee 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 c20883e188d28bca541db3f006b596fe7ba51bca 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 d50b26d00eb4cb4d343d5f64fe3387d0b719e1a9 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 077cfd6575a9f4f4b2f2ebfc154c494108c2511c 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 dc98eb8f9323fc425a16f7d903f21a37fdf4f49b 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 d47a38130368833ba3f5f567cf17c81db7da7fca 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 7990925d9f8ca905e186d8601e6e9a63f5bb5777 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 Signed-off-by: John W. Linville commit 9506d0182ca025ba8caf026944fe6490d35ecee0 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 Signed-off-by: John W. Linville commit 583cf9cb286a9db3729563a443879474e7129077 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 Signed-off-by: John W. Linville commit 257620fcb22c9d4ffd2c8624993c7f2f860383f3 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 Signed-off-by: John W. Linville commit 22531993b79f69ff32b673399d66f9cf2db0ace6 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 Signed-off-by: John W. Linville commit b570f505c3f37e9da8337acf613b7bc8d2505750 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 Signed-off-by: John W. Linville commit 5f8f966acce2b4cbe4059d45ad3c50c89246f379 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 Signed-off-by: John W. Linville commit 891d60efbca0408bf4b5ae95e362345e32c6c380 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 d48cb3581248347f512a00c2737b5057e3b257be 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 0f8ee79b8b2fc11e69d51082092a7ad23df1abf3 Author: Ivo van Doorn Date: Sat Apr 29 11:47:24 2006 +0200 [PATCH] rt2x00 drivers: Makefile & CRC 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 7a848895b6bb4df982743832e07866c19b2bfef5 Author: Ivo van Doorn Date: Sat Apr 29 11:36:15 2006 +0200 [PATCH] rt2x00: Compile fix and kernel version cleanup 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 abab1384d8034c2787899e5bc26a2ff8c334648c Author: Ivo van Doorn Date: Fri Apr 28 16:52:29 2006 +0200 [PATCH] rt2x00 update: Apply correct error handling to dscape stack 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 e36c391c22e854bd7fadc80b8f8d9f3719e61bca 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. 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 3a187c98378c29a7ec053dc5bbf1384856703b68 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 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 cd26024687fa55c86232a62beabb4aa7d5441942 Author: Ivo van Doorn Date: Fri Apr 28 16:52:27 2006 +0200 [PATCH] rt2x00 update: Use static const for the vals arrays Make the vals arrays static const. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 9b7c79ecdb88af0b5bcc4dc096f0ecee1872aefb Author: Ivo van Doorn Date: Fri Apr 28 16:52:26 2006 +0200 [PATCH] rt2x00 update: Deactivate monitor_during_oper for now 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 a683f2dd305d852664500869ae9bef39ea2f7c02 Author: Ivo van Doorn Date: Fri Apr 28 16:52:25 2006 +0200 [PATCH] rt2x00 update: Remove casts from void* pointers Remove unneeded casts when working with a void* pointer. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit b1c11287208e180865af077947c98340b4f4f01d Author: Ivo van Doorn Date: Fri Apr 28 00:03:21 2006 +0200 [PATCH] rt2x00: misc fixes Misc. small fixes. Add small comments, remove unwanted whitespaces etc. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville commit 4aa73046463b54a8c9efdb9e8090822ae1b4420b Author: Ivo van Doorn Date: Fri Apr 28 00:03:20 2006 +0200 [PATCH] rt2x00: Correctly initialization and uninitialization of device 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 2a92fba4eb1cca4ee6df03791fbc9f1f33dd7360 Author: Ivo van Doorn Date: Fri Apr 28 00:03:20 2006 +0200 [PATCH] rt2x00: Correctly initialize TX power in registers 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 04b34637405bb5e6c080db5028a2e0f9b0c74e68 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 82f8c5e147b061bd446011e5dfbadc9192b53ed6 Author: Ivo van Doorn Date: Fri Apr 28 00:03:18 2006 +0200 [PATCH] rt2x00: Support TXRX led handling 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 00f7a68707d423136b90cf19d3d9b309c370e787 Author: Ivo van Doorn Date: Fri Apr 28 00:03:17 2006 +0200 [PATCH] rt2x00: Put Hardware button in generic header 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 a31a4122e641947150cf0111975d8195a4c90b90 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 2b3ab5f6e132bd1ff6c1d382839d0c48679a9fba Author: Ivo van Doorn Date: Fri Apr 28 00:03:16 2006 +0200 [PATCH] rt2x00: Add flag handlers 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 0577188c0f94ff43609cf5278759e81ae06cf764 Author: Ivo van Doorn Date: Fri Apr 28 00:03:15 2006 +0200 [PATCH] rt2x00: Use correct desc_addr and data_addr 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 d63b5cd75ba01edcb97ad1c70fd2aa597eb68434 Author: Ivo van Doorn Date: Fri Apr 28 00:03:14 2006 +0200 [PATCH] rt2x00: Make correct cast in USB interrupt 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 644e9c28ae1b117266294dab09687d65b5b2b6c1 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 2440e37e4799545164f536c944ba397785ec5734 Author: Ivo van Doorn Date: Fri Apr 28 00:03:13 2006 +0200 [PATCH] rt2x00: PRIO ring should be treated as regular TX ring 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 9b29c72648d64c869160b1957f8b8552e874f7e3 Author: Ivo van Doorn Date: Fri Apr 28 00:03:12 2006 +0200 [PATCH] rt2x00: byte ordering correctness 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 b11ed3edd02d0de8e769a8a6486b2f0e64a87976 Author: Ivo van Doorn Date: Fri Apr 28 00:03:11 2006 +0200 [PATCH] rt2x00: Fix panics in interrupt handlers 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 e2dd370cfabdb3d954ad6d732b894b7b7ea6c02a 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 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 fc2cab5e7192b40eddd8be8ce15c2b617f68fbec Author: Ivo van Doorn Date: Fri Apr 28 00:03:09 2006 +0200 [PATCH] rt2x00: Put net_device structure in data_ring 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 4d3c8375f7d2553ee0c5e0bcbe7d7d8f0eefe597 Author: Ivo van Doorn Date: Fri Apr 28 00:03:08 2006 +0200 [PATCH] rt2x00: Make sure TX rings are empty when scanning 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 c684f4a31360103796f678d014c4712b9109ce8a Author: Ivo van Doorn Date: Fri Apr 28 00:03:07 2006 +0200 [PATCH] rt2x00: Move rx_params to correct location 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 c98fee4893d8116a6302440d24c1e27f2158806b Author: Ivo van Doorn Date: Fri Apr 28 00:03:05 2006 +0200 [PATCH] rt2x00: Allocate eeprom memory 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 bb37d73a7ebd7c11450f05e0435900b940d4921b Author: Ivo van Doorn Date: Fri Apr 28 00:03:04 2006 +0200 [PATCH] rt2x00: Tune link depending on link quality 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 e9b1cd91f6a347a845a5aac93d675d4cb25231ec Author: Ivo van Doorn Date: Fri Apr 28 00:03:03 2006 +0200 [PATCH] rt2x00: Add USB ID's 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 94965788b3cd57d49c78967af2d7be68c27fbbc2 Author: Ivo van Doorn Date: Fri Apr 28 00:03:02 2006 +0200 [PATCH] rt2x00: Add more register defines 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 58437b305a448602a46a01daf5670475f42d3764 Author: Ivo van Doorn Date: Fri Apr 28 00:03:01 2006 +0200 [PATCH] rt2x00: Move TSF counting activation to correct funtion 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 b6d6813a94c3ef57b6be7d293b4f49c94dc32780 Author: Ivo van Doorn Date: Fri Apr 28 00:03:00 2006 +0200 [PATCH] rt2x00: Fix antenna configuration 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 0fb313fc33dba23230ee2fa9c83f6a6d9120244a Author: Ivo van Doorn Date: Fri Apr 28 00:02:58 2006 +0200 [PATCH] rt2x00: Invalid memory allocation check 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 1a9d8f0c79aac098944182c4bfa795c19c7b2e25 Author: Ivo van Doorn Date: Fri Apr 28 00:02:57 2006 +0200 [PATCH] rt2x00: make vals static 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 ab8e00b56de9ca1588a93d958260045bf12f9e6f Author: Ivo van Doorn Date: Fri Apr 28 00:02:56 2006 +0200 [PATCH] rt2x00: Use arraylike accessors for entries in DMA ring 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 9d4df1585ed2b01b48eda7c8d5536517eeebf338 Author: Ivo van Doorn Date: Fri Apr 28 00:02:55 2006 +0200 [PATCH] rt2x00: Optimize RATE flag handling 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 e51dda798ee59cb8458a4ed6564a88ee252e968c Author: Ivo van Doorn Date: Fri Apr 28 00:02:53 2006 +0200 [PATCH] rt2x00: Add eeprom_multiread function 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 f4a4cd435288463f20ee8187162a14aa36c574ec 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 24a88a6f1ffae966f2ea25c938dbd5dcbcc7d94c Author: Ivo van Doorn Date: Fri Apr 28 00:02:50 2006 +0200 [PATCH] rt2x00: use enumerations 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 1a36339020f314412f85e59bb0c740a14eae5c1b 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 40546759f03c3e7f9b667aed0aefcd6c6f5a6f09 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 3e5ce58df4216a3027a79fdcbd4b5c530a67ebc0 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 520d8a133bdaaa13c768afe9bc800eaaa60499ef 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 6e16fa9db64ca7567e202755bec4d3bb494627dd 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 62aae343e6116f15b5f30728b8ac7e5304b93ca3 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 0c328d18d9f27259aa592a2bbb4a07875f4ecf29 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 513b213db9da7b210a8b8084aea295a2b6be5627 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 8551e7a1d57254b9ab39254f11e3db0355787c0a 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 114e1c65cf64bbe640d79ad6290cd02f7a0f2cb8 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 3df07aa28ed538949e3c4a72b7b814e46398e2d6 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 75db4d3e2c47cd1866f056d049f0326b5e40c94b 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 53ea18ba462b52ccbd67da0aeac8697e6d623690 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 67d76a97d94817fff2fbbe262c4eaa5ac9d76a41 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 e65df27c1f71ae76e21f688ffb44ffed89f25a92 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 8aadcbaa9dec1f4a7c7715454c45ee727587715c 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 82fc237c817e4184edc4b22d368859fe3f4d582e 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 280068abda803f29f7ee7b7519d17d7d715d3baf 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 95a59e0553751857a6eef2c591c4d441afbdad0a 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 b6f29db5f76d4ea4795d351159f68138b6931bf2 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 1ace37d022f76f235465b600ce92837edaace1a8 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 7fdd0496918db040ccc72ecd2b9f40acbe74cad4 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 1e6274adf6e3e64f22e420be2914064e5afffa3b 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 92bdc86eb802b2bd2b981fdd729663b5606d54bc 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 007480a8af3cd69ad6aa174425aeb3bb0c41dfb1 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 bbcaf59a31b3418e3540e4d7dcda747a9f789f94 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 8310adf89e5a6a3e0ca37dccd2793aa7f3916b9e 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 9f3c2cc3cbe0de416ec64f8438824639ad7d29f8 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 145ba6b288718aaa67888cf2a4ecd1f72c0f908c 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 b59a8e3e8909cc5e785dbf229722384cfe253d89 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 3958e7408e3c121a5f09babbe09498bcc03dca27 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 deaa308b17f125b9a6876ec9fe94f4c15ee06e34 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 afd9bb9294a2e906ecec5ee8b21309bbae593885 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 f04bfaef63562a6860506b8532eda1204321ddc2 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 197ef974fd6faa95541ab199bc300bd826f49df3 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 54b210ef191f199dd1e71dbdb14daa0d634ccdfe 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 5b9f93c631ec7f3d4fa5d9b426288390a504f69b 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 62cffe9ed57d86ff3fe5b828c9084f562ca772b2 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 f0d0006509d15f96e79dd853f74c4aaef65e0733 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 2de769106abf1aed3ac9d881ec50e35477ce0f02 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 3cead6cc8c39709c5ee47101fe4161631acb62b8 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 2b0b3fcb3ec57fedb04fb832f5ffa8806424007d 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 f909d2d0345489c2b026ea2bc73a90ea141269cc 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 877f3a4ab58ff71fbbf1c9594d356bfa82b27d97 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 a824256544d081070ad07147408dac544b1248ec 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 9268c54451f0473174c3e7f81939136fa8ffb358 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 203314e8ff0514edc3701e89ac95524d28ea5337 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 aaba5b695732b65f5327ed0302fd7ef279a51d8d 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 e784b786e68f864396de82d2a3d383a61967b603 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 8e08faddb78fa0bde7f6144e883151db4417567e 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 96cc47a5e59fe8ed9a54801d21b32d78c5d50218 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 790ab9dc4ad68fb2500f678ce61351e2db0a450b 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 49f1ed76a99adee22d1dfa1f0da0b93ff9ce15f3 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 8d2946cfb54c078e23ae7eaaa80dde3ff3056817 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 9f7fede5b606c5669f00abfe2b57d3c50d916427 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 a0eeda48aca3c91c8ef0229ccd5776784a02ebd2 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 56770c5cd8c90e75127db36438d4d2c654c0099f 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 5fb74412d33d602d80f0221d4cd3dd55047fb86b 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 3b12701890e8dc9487e1288801f0228b137ac203 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 27847dd17ce0f8b72a11074a80cb02a9e732f485 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 ca68d46f23bb992abede26760dd0f1a88ab8b7e4 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 b09f887ef462d5319e3e9bc45fdadaadd8733d20 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 Signed-off-by: John W. Linville commit 4fe8f9207bae35fb655599b45349ef1d7c57ca0c 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 Signed-off-by: John W. Linville commit db4ac5c320ec5e87094fa2e1a419042b20e05568 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 c49afb123ee1cb1d9fd60f0435d522d547063d42 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 f424a75a43dd226ce6e6a67234eea96b20c814f1 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 d9af6bfc08a200ddac4d2e1d3475b33fb39c1cda 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 6c5c07b1b8873e92ff304361ba4528b14cf0c299 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 530add0ebb8168165c3aacfe4efd46fe5d2d497a Author: Michael Buesch Date: Sat Feb 25 13:46:30 2006 +0100 [PATCH] bcm43xx-d80211: fix typo in comment. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 53305c5319df09fd3e184a26b18f69d11cbe90e0 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 Signed-off-by: John W. Linville commit 10b08b0c04aa04b6fbac5735ba56b82d78e38c17 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 Signed-off-by: John W. Linville commit a7b9e3c34516fe034d108480ab08a7cccb09e6c2 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 Signed-off-by: John W. Linville commit 7b78010dddac65d82841f1212992a19073a5b8dd 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 Signed-off-by: John W. Linville commit 9a0bfd85f9f589b250815244ed5e7dac4fe5b8b6 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 Signed-off-by: John W. Linville commit 89da8afbfe69cd9bfc9b9e72781a3449166dd96d 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 Signed-off-by: John W. Linville commit 30c47e42519444b405bb6ae3cbf7c994280ad595 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 Signed-off-by: John W. Linville commit 80f015ea8903cb91446c4d98b88081075de541ce 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 Signed-off-by: John W. Linville commit dba23cee3d281db44696a7151e6b5491a005a3e3 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 Signed-off-by: John W. Linville commit 522522d60964d40917aa7d7c02c878c405d84823 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 Signed-off-by: John W. Linville commit f606063c1d45266b9081e6f2f8abfeb04930cea1 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 Signed-off-by: John W. Linville commit e60e369da7cd1682726bbe28433fb4e9a39a3eb0 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 Signed-off-by: John W. Linville commit ee685523b0b42b90d93fba06c22198e16c42d9cd 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 Signed-off-by: John W. Linville commit 12656261e6ee1a6ad10ed114fac13afe458353d2 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 Signed-off-by: John W. Linville commit 1368606f153436e6bbbc3c744e7fdfb57f211567 Author: Michael Buesch Date: Sun Feb 19 22:22:42 2006 +0100 [PATCH] bcm43xx-d80211: remove old unused struct. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit e9c3f0c6908d45e6abc5b13ac2bf2f854cb79592 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 Signed-off-by: John W. Linville commit bcfaf66da65bf0df459f8f028ae9371b27682a72 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 Signed-off-by: John W. Linville commit c1c9ce99032ebf636e680e1701e5baa9b5207c02 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 Signed-off-by: John W. Linville commit 6287b25925fddde37463f5c36cc58dec4e85586a 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 Signed-off-by: John W. Linville commit cca5d95d7b6ff90b880c256a06f8dcfc2be73b26 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 Signed-off-by: John W. Linville commit 1ee511eda69656aeb6b34e2420b216d8dccfdfb1 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 Signed-off-by: John W. Linville commit 65fecf074f33b07b896acf0fac45599209306bfc 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 Signed-off-by: John W. Linville commit aba789adeca1fba4beb892d9869c89e1db5ca4bc Author: Michael Buesch Date: Sat Feb 18 11:54:45 2006 +0100 [PATCH] bcm43xx-d80211: fix typo. Missing D80211. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 986147591c685c81155252f6f482b37fe9e2834e 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 Signed-off-by: John W. Linville commit c12c2803c937c2f973373a0ae86fd019c2a7cd9b 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 Signed-off-by: John W. Linville commit 38a98b2371afad388626e1b148168b63a1f55faa Author: Michael Buesch Date: Sun Feb 12 20:25:55 2006 +0100 [PATCH] bcm43xx-d80211: fix LED code. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 0ce6d1abbe86f1e61ac3775ea85cca915272bb1a 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 Signed-off-by: John W. Linville commit f6b2bbcc87d2638d4f47898b95921f212237aff9 Author: Michael Buesch Date: Wed Feb 8 18:06:22 2006 +0100 [PATCH] bcm43xx-d80211: remove WX debugging. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 90f239238753edb94795c6008ba2f5c23c77cfa0 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 Signed-off-by: John W. Linville commit 82fabd7bafab76c6720d0ab38485b7dda6d01298 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 Signed-off-by: John W. Linville commit 401d81f1582e1f1ad50626202e180e0cd2d2a2e8 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 Signed-off-by: John W. Linville commit d26476688473937ac4a2d446e361289f2fbe5cd3 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 f23807cc75ac996d5154f53b7aac96216a2322b4 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 801802ab40b7c6c51c11fd1f37faaa96f278e726 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 Signed-off-by: John W. Linville commit 2c5350c5d6853b4ce2f36fb2ff37e1f7c1295059 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 Signed-off-by: John W. Linville commit 50dde6c86948b5fe6f579916ef03b5f8252665eb 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 Signed-off-by: John W. Linville commit ea75646f1be977a4ca9ddc88fe403db94df10139 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 Signed-off-by: John W. Linville commit fcd1e493bda4b643e46f7ad73330df9ebbbd64a9 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 Signed-off-by: John W. Linville commit 11169c79321177e0382d08c8f531d1e6bf6492a7 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 bb03fcad2125dfb24192cfd07122565ecba38d55 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 79114141fdc8992a602a606b08b70b46e93945dd 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 93b227d763cb64132b01ccabc46d16fa05a37065 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 ff4867f29781789916a0cc89e419823eb6647678 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 fa531403b542e93b418d33a18a5e795d38c7cdf2 Author: Michael Buesch Date: Tue Jan 31 16:06:54 2006 +0100 [PATCH] bcm43xx: fix Kconfig "depends on" typo. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 5ca9179753efaabcc3bb4ed1f44cb51234074fca 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 Signed-off-by: John W. Linville commit 6ba9758420ad2567a8a559c1c3b5803cd86d4742 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 Signed-off-by: John W. Linville commit 56d95a6ea6d116f1f08e2ec7fa5f83c5701127ee Author: Michael Buesch Date: Fri Jan 27 17:45:14 2006 +0100 [PATCH] bcm43xx: Remove redundant COPYING file. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit 4fb5512702c9e1da0f7988350f29a581ca1c2b9d 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 Signed-off-by: John W. Linville commit 9d51e90a7cda8171f64da21a3802c0c5495ce7af Author: Michael Buesch Date: Fri Jan 27 17:30:48 2006 +0100 [PATCH] bcm43xx: sync with svn.berlios.de Signed-off-by: Michael Buesch Signed-off-by: John W. Linville commit e608417b6a07da3e0c11a0581bdeeb56196587d9 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 656ea0d249aa32cb3eef0a3ca8bd2c88cba29b14 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 e4063478b882b719435d6b15d6be586c1d13cc51 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 c27fefdcaac40f6c497c12c17c5558525b7345e5 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 Signed-off-by: John W. Linville commit 6e418df55c4cfadf1e7fccc627487907b8255e21 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 Signed-off-by: John W. Linville commit 93157b46f3c066baa3effe2f9e1dca368e557ec1 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 Signed-off-by: John W. Linville commit 8e0498c4b707d186755ec9f6ee0fafa05897c3bb 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 Signed-off-by: John W. Linville commit 6cbd98b0757425b1caa167665bbe77b1924dfbee 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 Signed-off-by: John W. Linville commit 507d7e96fa24047a4248ca3b4b8869391e495b91 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 Signed-off-by: John W. Linville commit 4651cf927ac5faa6f9b7c19e962318c1781c46f9 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 Signed-off-by: John W. Linville commit 42061d223962591d4829fd39c3fcbc4329dba12c 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 Signed-off-by: John W. Linville commit a841d52ab44bd4b69a96b56eb8b53958079504c6 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 Signed-off-by: John W. Linville commit e563179629dde79ffb231eabfe6e39ec8bde0919 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 Signed-off-by: John W. Linville commit 58caaa8eda31a4688a142e3491a1ac8cf05c0f3e 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 Signed-off-by: John W. Linville commit caa705fcf5144cc81e8702c74e402ba3986987c6 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 Signed-off-by: John W. Linville commit 711c2e73a5483e1ac50da125cebf4e20cadcf33b 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 Signed-off-by: John W. Linville commit 135021e5a5da9d42d73a0cc779cbef0b59683e3f 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 Signed-off-by: John W. Linville commit 42e6c69fa16f8b88ea3387c3139a22b1548e8341 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 Signed-off-by: John W. Linville commit 85aee1824db933fece8b862f25cd4b6518b6608b 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 Signed-off-by: John W. Linville commit 64750f4ae59befa774e4a806896952d2d00c46a2 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 Signed-off-by: John W. Linville commit 7d123ec1cc4af1da9ed4609d9f0aecf46f186747 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 Signed-off-by: John W. Linville commit a19288fdc82fa002d409b221808ccc9fcc2b3d56 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 Signed-off-by: John W. Linville commit 7fa22d48190331d887cf0d517a2b8853829535ef Author: Johannes Berg Date: Tue Jan 30 19:55:16 2007 -0500 [PATCH] wireless: add nl80211 This patch adds nl80211, the netlink-based userspace interface for cfg80211. 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 546f8b3943136a25246811146632a1fe31f83674 Author: Johannes Berg Date: Tue Jan 30 20:15:49 2007 -0500 [PATCH] cfg80211: add wext-compatible client Signed-off-by: Johannes Berg Signed-off-by: Jiri Benc Signed-off-by: John W. Linville commit ac091756bdb4743231f85720d98e04569c31de92 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 9b427b1e17b0f17354901211c946409ab0d3a656 Author: Johannes Berg Date: Tue Jan 30 19:55:16 2007 -0500 [PATCH] wireless: add cfg80211 This patch adds cfg80211, a new configuration system for wireless hardware. 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 Signed-off-by: Andrew Morton --- .mailmap | 1 CREDITS | 37 Documentation/feature-removal-schedule.txt | 12 MAINTAINERS | 60 drivers/Kconfig | 2 drivers/Makefile | 1 drivers/net/Kconfig | 11 drivers/net/Makefile | 2 drivers/net/b44.c | 742 - drivers/net/b44.h | 81 drivers/net/wireless/Kconfig | 120 drivers/net/wireless/bcm43xx/Kconfig | 3 drivers/net/wireless/hostap/Kconfig | 3 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 | 2188 ++++ drivers/net/wireless/mac80211/adm8211/adm8211.h | 622 + drivers/net/wireless/mac80211/bcm43xx/Kconfig | 100 drivers/net/wireless/mac80211/bcm43xx/Makefile | 16 drivers/net/wireless/mac80211/bcm43xx/bcm43xx.h | 870 + drivers/net/wireless/mac80211/bcm43xx/bcm43xx_debugfs.c | 433 drivers/net/wireless/mac80211/bcm43xx/bcm43xx_debugfs.h | 110 drivers/net/wireless/mac80211/bcm43xx/bcm43xx_dma.c | 1297 ++ drivers/net/wireless/mac80211/bcm43xx/bcm43xx_dma.h | 361 drivers/net/wireless/mac80211/bcm43xx/bcm43xx_leds.c | 300 drivers/net/wireless/mac80211/bcm43xx/bcm43xx_leds.h | 56 drivers/net/wireless/mac80211/bcm43xx/bcm43xx_lo.c | 1081 ++ drivers/net/wireless/mac80211/bcm43xx/bcm43xx_lo.h | 92 drivers/net/wireless/mac80211/bcm43xx/bcm43xx_main.c | 3988 ++++++++ drivers/net/wireless/mac80211/bcm43xx/bcm43xx_main.h | 156 drivers/net/wireless/mac80211/bcm43xx/bcm43xx_pcmcia.c | 163 drivers/net/wireless/mac80211/bcm43xx/bcm43xx_pcmcia.h | 22 drivers/net/wireless/mac80211/bcm43xx/bcm43xx_phy.c | 4235 ++++++++ drivers/net/wireless/mac80211/bcm43xx/bcm43xx_phy.h | 309 drivers/net/wireless/mac80211/bcm43xx/bcm43xx_pio.c | 671 + drivers/net/wireless/mac80211/bcm43xx/bcm43xx_pio.h | 170 drivers/net/wireless/mac80211/bcm43xx/bcm43xx_power.c | 82 drivers/net/wireless/mac80211/bcm43xx/bcm43xx_power.h | 41 drivers/net/wireless/mac80211/bcm43xx/bcm43xx_sysfs.c | 232 drivers/net/wireless/mac80211/bcm43xx/bcm43xx_sysfs.h | 9 drivers/net/wireless/mac80211/bcm43xx/bcm43xx_tables.c | 376 drivers/net/wireless/mac80211/bcm43xx/bcm43xx_tables.h | 28 drivers/net/wireless/mac80211/bcm43xx/bcm43xx_vstack.c | 202 drivers/net/wireless/mac80211/bcm43xx/bcm43xx_vstack.h | 83 drivers/net/wireless/mac80211/bcm43xx/bcm43xx_xmit.c | 615 + drivers/net/wireless/mac80211/bcm43xx/bcm43xx_xmit.h | 215 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 | 813 + drivers/net/wireless/mac80211/p54/prism54common.h | 308 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 | 86 drivers/net/wireless/mac80211/rt2x00/Makefile | 9 drivers/net/wireless/mac80211/rt2x00/rt2400pci.c | 2586 +++++ drivers/net/wireless/mac80211/rt2x00/rt2400pci.h | 931 + drivers/net/wireless/mac80211/rt2x00/rt2500pci.c | 2883 +++++ drivers/net/wireless/mac80211/rt2x00/rt2500pci.h | 1198 ++ drivers/net/wireless/mac80211/rt2x00/rt2500usb.c | 2718 +++++ drivers/net/wireless/mac80211/rt2x00/rt2500usb.h | 746 + drivers/net/wireless/mac80211/rt2x00/rt2x00.h | 1069 ++ drivers/net/wireless/mac80211/rt2x00/rt2x00debug.c | 380 drivers/net/wireless/mac80211/rt2x00/rt2x00debug.h | 70 drivers/net/wireless/mac80211/rt2x00/rt2x00dev.c | 231 drivers/net/wireless/mac80211/rt2x00/rt2x00lib.h | 74 drivers/net/wireless/mac80211/rt2x00/rt2x00mac.c | 540 + drivers/net/wireless/mac80211/rt2x00/rt2x00pci.h | 39 drivers/net/wireless/mac80211/rt2x00/rt2x00usb.h | 82 drivers/net/wireless/mac80211/rt2x00/rt61pci.c | 3364 ++++++ drivers/net/wireless/mac80211/rt2x00/rt61pci.h | 1361 ++ drivers/net/wireless/mac80211/rt2x00/rt73usb.c | 3024 ++++++ drivers/net/wireless/mac80211/rt2x00/rt73usb.h | 945 + drivers/net/wireless/mac80211/rtl818x/Kconfig | 9 drivers/net/wireless/mac80211/rtl818x/Makefile | 2 drivers/net/wireless/mac80211/rtl818x/rtl8187.h | 126 drivers/net/wireless/mac80211/rtl818x/rtl8187_dev.c | 726 + drivers/net/wireless/mac80211/rtl818x/rtl8187_rtl8225.c | 747 + drivers/net/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 | 1676 +++ drivers/net/wireless/mac80211/zd1211rw/zd_chip.h | 909 + drivers/net/wireless/mac80211/zd1211rw/zd_def.h | 57 drivers/net/wireless/mac80211/zd1211rw/zd_ieee80211.h | 67 drivers/net/wireless/mac80211/zd1211rw/zd_mac.c | 697 + drivers/net/wireless/mac80211/zd1211rw/zd_mac.h | 250 drivers/net/wireless/mac80211/zd1211rw/zd_rf.c | 156 drivers/net/wireless/mac80211/zd1211rw/zd_rf.h | 81 drivers/net/wireless/mac80211/zd1211rw/zd_rf_al2230.c | 436 drivers/net/wireless/mac80211/zd1211rw/zd_rf_al7230b.c | 274 drivers/net/wireless/mac80211/zd1211rw/zd_rf_rf2959.c | 279 drivers/net/wireless/mac80211/zd1211rw/zd_usb.c | 1336 ++ drivers/net/wireless/mac80211/zd1211rw/zd_usb.h | 241 drivers/net/wireless/mac80211/zd1211rw/zd_util.c | 82 drivers/net/wireless/mac80211/zd1211rw/zd_util.h | 29 drivers/net/wireless/zd1211rw/Kconfig | 3 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 | 1042 ++ drivers/ssb/pci.c | 667 + 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/Kbuild | 6 include/linux/crc-itu-t.h | 27 include/linux/eeprom_93cx6.h | 77 include/linux/ieee80211.h | 342 include/linux/netdevice.h | 7 include/linux/nl80211.h | 305 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 | 153 include/net/iw_handler.h | 13 include/net/mac80211.h | 1077 ++ include/net/wireless.h | 159 lib/Kconfig | 16 lib/Makefile | 3 lib/crc-itu-t.c | 64 lib/eeprom_93cx6.c | 344 net/Kconfig | 12 net/Makefile | 2 net/core/Makefile | 1 net/core/dev.c | 30 net/core/rtnetlink.c | 15 net/core/wireless.c | 3 net/ieee80211/Kconfig | 3 net/mac80211/Kconfig | 80 net/mac80211/Makefile | 25 net/mac80211/aes_ccm.c | 155 net/mac80211/aes_ccm.h | 26 net/mac80211/debugfs.c | 436 net/mac80211/debugfs.h | 16 net/mac80211/debugfs_key.c | 253 net/mac80211/debugfs_key.h | 34 net/mac80211/debugfs_netdev.c | 440 net/mac80211/debugfs_netdev.h | 30 net/mac80211/debugfs_sta.c | 242 net/mac80211/debugfs_sta.h | 12 net/mac80211/fifo_qdisc.c | 102 net/mac80211/hostapd_ioctl.h | 347 net/mac80211/ieee80211.c | 4938 ++++++++++ net/mac80211/ieee80211_cfg.c | 90 net/mac80211/ieee80211_cfg.h | 9 net/mac80211/ieee80211_common.h | 98 net/mac80211/ieee80211_i.h | 826 + net/mac80211/ieee80211_iface.c | 365 net/mac80211/ieee80211_ioctl.c | 3285 ++++++ 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_scan.c | 344 net/mac80211/ieee80211_sta.c | 3071 ++++++ net/mac80211/michael.c | 104 net/mac80211/michael.h | 20 net/mac80211/rc80211_simple.c | 432 net/mac80211/sta_info.c | 463 net/mac80211/sta_info.h | 164 net/mac80211/tkip.c | 341 net/mac80211/tkip.h | 36 net/mac80211/wep.c | 328 net/mac80211/wep.h | 40 net/mac80211/wme.c | 679 + net/mac80211/wme.h | 38 net/mac80211/wpa.c | 846 + net/mac80211/wpa.h | 31 net/wireless/Kconfig | 46 net/wireless/Makefile | 17 net/wireless/core.c | 359 net/wireless/core.h | 80 net/wireless/nl80211.c | 982 + net/wireless/nl80211.h | 24 net/wireless/sysfs.c | 125 net/wireless/sysfs.h | 9 net/wireless/wext-common.c | 663 + net/wireless/wext-compat.c | 789 + net/wireless/wext-export.c | 29 net/wireless/wext-mod.c | 20 net/wireless/wext-old.c | 853 + net/wireless/wext.h | 51 201 files changed, 85611 insertions(+), 614 deletions(-) diff -puN .mailmap~git-wireless .mailmap --- a/.mailmap~git-wireless +++ a/.mailmap @@ -57,6 +57,7 @@ Jean Tourrilhes Jeff Garzik Jens Axboe Jens Osterkamp +Johannes Berg John Stultz Juha Yrjola Juha Yrjola diff -puN CREDITS~git-wireless CREDITS --- a/CREDITS~git-wireless +++ a/CREDITS @@ -317,6 +317,12 @@ S: 2322 37th Ave SW S: Seattle, Washington 98126-2010 S: USA +N: Johannes Berg +E: johannes@sipsolutions.net +W: http://johannes.sipsolutions.net/ +P: 1024D/9AB78CA5 AD02 0176 4E29 C137 1DF6 08D2 FC44 CF86 9AB7 8CA5 +D: powerpc & 802.11 hacker + N: Stephen R. van den Berg (AKA BuGless) E: berg@pool.informatik.rwth-aachen.de D: General kernel, gcc, and libc hacker @@ -659,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/ @@ -673,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) @@ -827,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 @@ -3510,6 +3532,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 @@ -3644,6 +3672,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 -puN Documentation/feature-removal-schedule.txt~git-wireless Documentation/feature-removal-schedule.txt --- a/Documentation/feature-removal-schedule.txt~git-wireless +++ a/Documentation/feature-removal-schedule.txt @@ -283,18 +283,6 @@ Who: Richard Purdie --------------------------- -What: Wireless extensions over netlink (CONFIG_NET_WIRELESS_RTNETLINK) -When: with the merge of wireless-dev, 2.6.22 or later -Why: The option/code is - * not enabled on most kernels - * not required by any userspace tools (except an experimental one, - and even there only for some parts, others use ioctl) - * pointless since wext is no longer evolving and the ioctl - interface needs to be kept -Who: Johannes Berg - ---------------------------- - What: i8xx_tco watchdog driver When: in 2.6.22 Why: the i8xx_tco watchdog driver has been replaced by the iTCO_wdt diff -puN MAINTAINERS~git-wireless MAINTAINERS --- a/MAINTAINERS~git-wireless +++ a/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 @@ -662,6 +670,7 @@ S: Supported ATMEL WIRELESS DRIVER P: Simon Kelley M: simon@thekelleys.org.uk +L: linux-wireless@vger.kernel.org W: http://www.thekelleys.org.uk/atmel W: http://atmelwlandriver.sourceforge.net/ S: Maintained @@ -712,11 +721,21 @@ 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 P: Stefano Brivio M: st3@riseup.net +L: linux-wireless@vger.kernel.org W: http://bcm43xx.berlios.de/ S: Maintained @@ -911,6 +930,12 @@ M: maxextreme@gmail.com L: linux-kernel@vger.kernel.org S: Maintained +CFG80211 and NL80211 +P: Johannes Berg +M: johannes@sipsolutions.net +L: linux-wireless@vger.kernel.org +S: Maintained + COMMON INTERNET FILE SYSTEM (CIFS) P: Steve French M: sfrench@samba.org @@ -1589,6 +1614,7 @@ S: Supported HOST AP DRIVER P: Jouni Malinen M: jkmaline@cc.hut.fi +L: linux-wireless@vger.kernel.org L: hostap@shmoo.com W: http://hostap.epitest.fi/ S: Maintained @@ -1875,6 +1901,7 @@ P: Yi Zhu M: yi.zhu@intel.com P: James Ketrenos M: jketreno@linux.intel.com +L: linux-wireless@vger.kernel.org L: ipw2100-devel@lists.sourceforge.net L: http://lists.sourceforge.net/mailman/listinfo/ipw2100-devel W: http://ipw2100.sourceforge.net @@ -1885,6 +1912,7 @@ P: Yi Zhu M: yi.zhu@intel.com P: James Ketrenos M: jketreno@linux.intel.com +L: linux-wireless@vger.kernel.org L: ipw2100-devel@lists.sourceforge.net L: http://lists.sourceforge.net/mailman/listinfo/ipw2100-devel W: http://ipw2200.sourceforge.net @@ -2597,6 +2625,7 @@ P: Pavel Roskin M: proski@gnu.org P: David Gibson M: hermes@gibson.dropbear.id.au +L: linux-wireless@vger.kernel.org L: orinoco-users@lists.sourceforge.net L: orinoco-devel@lists.sourceforge.net W: http://www.nongnu.org/orinoco/ @@ -2773,10 +2802,19 @@ 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 -L: netdev@vger.kernel.org +L: linux-wireless@vger.kernel.org W: http://prism54.org S: Maintained @@ -2847,7 +2885,14 @@ S: Maintained RAYLINK/WEBGEAR 802.11 WIRELESS LAN DRIVER P: Corey Thomas M: corey@world.std.com -L: linux-kernel@vger.kernel.org +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 @@ -3112,7 +3157,7 @@ M: josejx@gentoo.org P: Daniel Drake M: dsd@gentoo.org W: http://softmac.sipsolutions.net/ -L: netdev@vger.kernel.org +L: linux-wireless@vger.kernel.org S: Maintained SOFTWARE RAID (Multiple Disks) SUPPORT @@ -3135,6 +3180,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 @@ -3846,6 +3897,7 @@ S: Maintained WAVELAN NETWORK DRIVER & WIRELESS EXTENSIONS P: Jean Tourrilhes M: jt@hpl.hp.com +L: linux-wireless@vger.kernel.org W: http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/ S: Maintained @@ -3863,6 +3915,7 @@ S: Maintained WL3501 WIRELESS PCMCIA CARD DRIVER P: Arnaldo Carvalho de Melo M: acme@conectiva.com.br +L: linux-wireless@vger.kernel.org W: http://advogato.org/person/acme S: Maintained @@ -3926,6 +3979,7 @@ M: dsd@gentoo.org P: Ulrich Kunitz M: kune@deine-taler.de W: http://zd1211.ath.cx/wiki/DriverRewrite +L: linux-wireless@vger.kernel.org L: zd1211-devs@lists.sourceforge.net (subscribers-only) S: Maintained diff -puN drivers/Kconfig~git-wireless drivers/Kconfig --- a/drivers/Kconfig~git-wireless +++ a/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 -puN drivers/Makefile~git-wireless drivers/Makefile --- a/drivers/Makefile~git-wireless +++ a/drivers/Makefile @@ -85,3 +85,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 -puN drivers/net/Kconfig~git-wireless drivers/net/Kconfig --- a/drivers/net/Kconfig~git-wireless +++ a/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 -puN drivers/net/Makefile~git-wireless drivers/net/Makefile --- a/drivers/net/Makefile~git-wireless +++ a/drivers/net/Makefile @@ -207,7 +207,7 @@ obj-$(CONFIG_TR) += tokenring/ obj-$(CONFIG_WAN) += wan/ obj-$(CONFIG_ARCNET) += arcnet/ obj-$(CONFIG_NET_PCMCIA) += pcmcia/ -obj-$(CONFIG_NET_RADIO) += wireless/ +obj-y += wireless/ obj-$(CONFIG_NET_TULIP) += tulip/ obj-$(CONFIG_HAMRADIO) += hamradio/ obj-$(CONFIG_IRDA) += irda/ diff -puN drivers/net/b44.c~git-wireless drivers/net/b44.c --- a/drivers/net/b44.c~git-wireless +++ a/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 "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_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) -{ - dma_sync_single_range_for_device(&pdev->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) -{ - dma_sync_single_range_for_cpu(&pdev->dev, dma_base, - offset & dma_desc_align_mask, - dma_desc_sync_size, 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(sdev->dev, dma_base, + offset & dma_desc_align_mask, + dma_desc_sync_size, 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(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); - - pci_dma_sync_single_for_device(bp->pdev, le32_to_cpu(src_desc->addr), - RX_PKT_BUF_SZ, - PCI_DMA_FROMDEVICE); + b44_sync_dma_desc_for_device(bp->sdev, bp->rx_ring_dma, + dest_idx * sizeof(dest_desc), + DMA_BIDIRECTIONAL); + + 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; - - err = b44_read_eeprom(bp, &eeprom[0]); - if (err) - goto out; - - 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]; + struct ssb_device *sdev = bp->sdev; + int err = 0; + u8 *addr; + + bp->dma_offset = ssb_dma_translation(sdev); + + 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 @@ static int __devinit b44_init_one(struct 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 @@ static int __devinit b44_init_one(struct 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 -puN drivers/net/b44.h~git-wireless drivers/net/b44.h --- a/drivers/net/b44.h~git-wireless +++ a/drivers/net/b44.h @@ -129,6 +129,7 @@ #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_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 @@ struct rx_header { 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 @@ struct b44 { 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 @@ struct b44 { u32 rx_pending; u32 tx_pending; u8 phy_addr; - u8 core_unit; struct mii_if_info mii_if; }; diff -puN drivers/net/wireless/Kconfig~git-wireless drivers/net/wireless/Kconfig --- a/drivers/net/wireless/Kconfig~git-wireless +++ a/drivers/net/wireless/Kconfig @@ -2,47 +2,21 @@ # Wireless LAN device configuration # -menu "Wireless LAN (non-hamradio)" - depends on NETDEVICES +menu "Wireless LAN" -config NET_RADIO - bool "Wireless LAN drivers (non-hamradio) & Wireless Extensions" - select WIRELESS_EXT +config WLAN_PRE80211 + bool "Wireless LAN (pre-802.11)" + depends on NETDEVICES ---help--- - Support for wireless LANs and everything having to do with radio, - but not with amateur radio or FM broadcasting. + Say Y if you have any pre-802.11 wireless LAN hardware. - Saying Y here also enables the Wireless Extensions (creates - /proc/net/wireless and enables iwconfig access). The Wireless - Extension is a generic API allowing a driver to expose to the user - space configuration and statistics specific to common Wireless LANs. - The beauty of it is that a single set of tool can support all the - variations of Wireless LANs, regardless of their type (as long as - the driver supports Wireless Extension). Another advantage is that - these parameters may be changed on the fly without restarting the - driver (or Linux). If you wish to use Wireless Extensions with - wireless PCMCIA (PC-) cards, you need to say Y here; you can fetch - the tools from - . - -config NET_WIRELESS_RTNETLINK - bool "Wireless Extension API over RtNetlink" - depends on NET_RADIO - ---help--- - Support the Wireless Extension API over the RtNetlink socket - in addition to the traditional ioctl interface (selected above). - - For now, few tools use this facility, but it might grow in the - future. The only downside is that it adds 4.5 kB to your kernel. - -# Note : the cards are obsolete (can't buy them anymore), but the drivers -# are not, as people are still using them... -comment "Obsolete Wireless cards support (pre-802.11)" - depends on NET_RADIO && (INET || ISA || PCMCIA) + This option does not affect the kernel build, it only + let's you choose drivers. config STRIP tristate "STRIP (Metricom starmode radio IP)" - depends on NET_RADIO && INET + depends on INET && WLAN_PRE80211 + select WIRELESS_EXT ---help--- Say Y if you have a Metricom radio and intend to use Starmode Radio IP. STRIP is a radio protocol developed for the MosquitoNet project @@ -65,7 +39,8 @@ config STRIP config ARLAN tristate "Aironet Arlan 655 & IC2200 DS support" - depends on NET_RADIO && ISA && !64BIT + depends on ISA && !64BIT && WLAN_PRE80211 + select WIRELESS_EXT ---help--- Aironet makes Arlan, a class of wireless LAN adapters. These use the www.Telxon.com chip, which is also used on several similar cards. @@ -80,7 +55,8 @@ config ARLAN config WAVELAN tristate "AT&T/Lucent old WaveLAN & DEC RoamAbout DS ISA support" - depends on NET_RADIO && ISA + depends on ISA && WLAN_PRE80211 + select WIRELESS_EXT ---help--- The Lucent WaveLAN (formerly NCR and AT&T; or DEC RoamAbout DS) is a Radio LAN (wireless Ethernet-like Local Area Network) using the @@ -107,7 +83,8 @@ config WAVELAN config PCMCIA_WAVELAN tristate "AT&T/Lucent old WaveLAN Pcmcia wireless support" - depends on NET_RADIO && PCMCIA + depends on PCMCIA && WLAN_PRE80211 + select WIRELESS_EXT help Say Y here if you intend to attach an AT&T/Lucent Wavelan PCMCIA (PC-card) wireless Ethernet networking card to your computer. This @@ -118,7 +95,8 @@ config PCMCIA_WAVELAN config PCMCIA_NETWAVE tristate "Xircom Netwave AirSurfer Pcmcia wireless support" - depends on NET_RADIO && PCMCIA + depends on PCMCIA && WLAN_PRE80211 + select WIRELESS_EXT help Say Y here if you intend to attach this type of PCMCIA (PC-card) wireless Ethernet networking card to your computer. @@ -126,12 +104,20 @@ config PCMCIA_NETWAVE To compile this driver as a module, choose M here: the module will be called netwave_cs. If unsure, say N. -comment "Wireless 802.11 Frequency Hopping cards support" - depends on NET_RADIO && PCMCIA + +config WLAN_80211 + bool "Wireless LAN (IEEE 802.11)" + depends on NETDEVICES + ---help--- + Say Y if you have any 802.11 wireless LAN hardware. + + This option does not affect the kernel build, it only + let's you choose drivers. config PCMCIA_RAYCS tristate "Aviator/Raytheon 2.4MHz wireless support" - depends on NET_RADIO && PCMCIA + depends on PCMCIA && WLAN_80211 + select WIRELESS_EXT ---help--- Say Y here if you intend to attach an Aviator/Raytheon PCMCIA (PC-card) wireless Ethernet networking card to your computer. @@ -141,12 +127,10 @@ config PCMCIA_RAYCS To compile this driver as a module, choose M here: the module will be called ray_cs. If unsure, say N. -comment "Wireless 802.11b ISA/PCI cards support" - depends on NET_RADIO && (ISA || PCI || PPC_PMAC || PCMCIA) - config IPW2100 tristate "Intel PRO/Wireless 2100 Network Connection" - depends on NET_RADIO && PCI + depends on PCI && WLAN_80211 + select WIRELESS_EXT select FW_LOADER select IEEE80211 ---help--- @@ -200,7 +184,8 @@ config IPW2100_DEBUG config IPW2200 tristate "Intel PRO/Wireless 2200BG and 2915ABG Network Connection" - depends on NET_RADIO && PCI + depends on PCI && WLAN_80211 + select WIRELESS_EXT select FW_LOADER select IEEE80211 ---help--- @@ -295,7 +280,8 @@ config LIBERTAS_USB_DEBUG config AIRO tristate "Cisco/Aironet 34X/35X/4500/4800 ISA and PCI cards" - depends on NET_RADIO && ISA_DMA_API && (PCI || BROKEN) + depends on ISA_DMA_API && WLAN_80211 && (PCI || BROKEN) + select WIRELESS_EXT select CRYPTO ---help--- This is the standard Linux driver to support Cisco/Aironet ISA and @@ -312,7 +298,8 @@ config AIRO config HERMES tristate "Hermes chipset 802.11b support (Orinoco/Prism2/Symbol)" - depends on NET_RADIO && (PPC_PMAC || PCI || PCMCIA) + depends on (PPC_PMAC || PCI || PCMCIA) && WLAN_80211 + select WIRELESS_EXT ---help--- A driver for 802.11b wireless cards based on the "Hermes" or Intersil HFA384x (Prism 2) MAC controller. This includes the vast @@ -386,7 +373,8 @@ config PCI_HERMES config ATMEL tristate "Atmel at76c50x chipset 802.11b support" - depends on NET_RADIO && (PCI || PCMCIA) + depends on (PCI || PCMCIA) && WLAN_80211 + select WIRELESS_EXT select FW_LOADER select CRC32 ---help--- @@ -407,13 +395,9 @@ config PCI_ATMEL Enable support for PCI and mini-PCI cards containing the Atmel at76c506 chip. -# If Pcmcia is compiled in, offer Pcmcia cards... -comment "Wireless 802.11b Pcmcia/Cardbus cards support" - depends on NET_RADIO && PCMCIA - config PCMCIA_HERMES tristate "Hermes PCMCIA card support" - depends on NET_RADIO && PCMCIA && HERMES + depends on PCMCIA && HERMES ---help--- A driver for "Hermes" chipset based PCMCIA wireless adaptors, such as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/ @@ -433,7 +417,7 @@ config PCMCIA_HERMES config PCMCIA_SPECTRUM tristate "Symbol Spectrum24 Trilogy PCMCIA card support" - depends on NET_RADIO && PCMCIA && HERMES + depends on PCMCIA && HERMES select FW_LOADER ---help--- @@ -447,7 +431,8 @@ config PCMCIA_SPECTRUM config AIRO_CS tristate "Cisco/Aironet 34X/35X/4500/4800 PCMCIA cards" - depends on NET_RADIO && PCMCIA && (BROKEN || !M32R) + depends on PCMCIA && (BROKEN || !M32R) && WLAN_80211 + select WIRELESS_EXT select CRYPTO select CRYPTO_AES ---help--- @@ -471,7 +456,8 @@ config AIRO_CS config PCMCIA_ATMEL tristate "Atmel at76c502/at76c504 PCMCIA cards" - depends on NET_RADIO && ATMEL && PCMCIA + depends on ATMEL && PCMCIA + select WIRELESS_EXT select FW_LOADER select CRC32 ---help--- @@ -480,17 +466,17 @@ config PCMCIA_ATMEL config PCMCIA_WL3501 tristate "Planet WL3501 PCMCIA cards" - depends on NET_RADIO && EXPERIMENTAL && PCMCIA + depends on EXPERIMENTAL && PCMCIA && WLAN_80211 + select WIRELESS_EXT ---help--- A driver for WL3501 PCMCIA 802.11 wireless cards made by Planet. It has basic support for Linux wireless extensions and initial micro support for ethtool. -comment "Prism GT/Duette 802.11(a/b/g) PCI/Cardbus support" - depends on NET_RADIO && PCI config PRISM54 tristate 'Intersil Prism GT/Duette/Indigo PCI/Cardbus' - depends on PCI && NET_RADIO && EXPERIMENTAL + depends on PCI && EXPERIMENTAL && WLAN_80211 + select WIRELESS_EXT select FW_LOADER ---help--- Enable PCI and Cardbus support for the following chipset based cards: @@ -536,7 +522,8 @@ config PRISM54 config USB_ZD1201 tristate "USB ZD1201 based Wireless device support" - depends on USB && NET_RADIO + depends on USB && WLAN_80211 + select WIRELESS_EXT select FW_LOADER ---help--- Say Y if you want to use wireless LAN adapters based on the ZyDAS @@ -555,11 +542,6 @@ source "drivers/net/wireless/hostap/Kcon source "drivers/net/wireless/bcm43xx/Kconfig" source "drivers/net/wireless/zd1211rw/Kconfig" -# yes, this works even when no drivers are selected -config NET_WIRELESS - bool - depends on NET_RADIO && (ISA || PCI || PPC_PMAC || PCMCIA) - default y +source "drivers/net/wireless/mac80211/Kconfig" endmenu - diff -puN drivers/net/wireless/Makefile~git-wireless drivers/net/wireless/Makefile diff -puN drivers/net/wireless/bcm43xx/Kconfig~git-wireless drivers/net/wireless/bcm43xx/Kconfig --- a/drivers/net/wireless/bcm43xx/Kconfig~git-wireless +++ a/drivers/net/wireless/bcm43xx/Kconfig @@ -1,6 +1,7 @@ config BCM43XX tristate "Broadcom BCM43xx wireless support" - depends on PCI && IEEE80211 && IEEE80211_SOFTMAC && NET_RADIO && EXPERIMENTAL + depends on PCI && IEEE80211 && IEEE80211_SOFTMAC && WLAN_80211 && EXPERIMENTAL + select WIRELESS_EXT select FW_LOADER select HW_RANDOM ---help--- diff -puN drivers/net/wireless/hostap/Kconfig~git-wireless drivers/net/wireless/hostap/Kconfig --- a/drivers/net/wireless/hostap/Kconfig~git-wireless +++ a/drivers/net/wireless/hostap/Kconfig @@ -1,6 +1,7 @@ config HOSTAP tristate "IEEE 802.11 for Host AP (Prism2/2.5/3 and WEP/TKIP/CCMP)" - depends on NET_RADIO + depends on WLAN_80211 + select WIRELESS_EXT select IEEE80211 select IEEE80211_CRYPT_WEP ---help--- diff -puN /dev/null drivers/net/wireless/mac80211/Kconfig --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/Makefile --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/README --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/adm8211/Kconfig --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/adm8211/Makefile --- /dev/null +++ a/drivers/net/wireless/mac80211/adm8211/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_ADM8211) += adm8211.o diff -puN /dev/null drivers/net/wireless/mac80211/adm8211/adm8211.c --- /dev/null +++ a/drivers/net/wireless/mac80211/adm8211/adm8211.c @@ -0,0 +1,2188 @@ + +/* + * 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_scan(struct ieee80211_hw *dev, int state, + struct ieee80211_scan_conf *conf) +{ + struct adm8211_priv *priv = dev->priv; + if (state == IEEE80211_SCAN_START) { + priv->channel = conf->scan_channel; + adm8211_rf_set_channel(dev, conf->scan_channel); + } else if (state == IEEE80211_SCAN_END) { + priv->channel = conf->running_channel; + adm8211_rf_set_channel(dev, conf->running_channel); + } + + 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 -1; + } + + 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, + .passive_scan = adm8211_scan, + .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 -puN /dev/null drivers/net/wireless/mac80211/adm8211/adm8211.h --- /dev/null +++ a/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_PREAMBLE2 | IEEE80211_RATE_CCK }, + { .rate = 20, + .val = 20, + .val2 = -20, + .flags = IEEE80211_RATE_PREAMBLE2 | IEEE80211_RATE_CCK_2 }, + { .rate = 55, + .val = 55, + .val2 = -55, + .flags = IEEE80211_RATE_PREAMBLE2 | IEEE80211_RATE_CCK_2 }, + { .rate = 110, + .val = 110, + .val2 = -110, + .flags = IEEE80211_RATE_PREAMBLE2 | 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 -puN /dev/null drivers/net/wireless/mac80211/bcm43xx/Kconfig --- /dev/null +++ a/drivers/net/wireless/mac80211/bcm43xx/Kconfig @@ -0,0 +1,100 @@ +config BCM43XX_MAC80211 + tristate "Broadcom BCM43xx wireless support (mac80211 stack)" + depends on MAC80211 && WLAN_80211 && EXPERIMENTAL + select FW_LOADER + select SSB + ---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 -puN /dev/null drivers/net/wireless/mac80211/bcm43xx/Makefile --- /dev/null +++ a/drivers/net/wireless/mac80211/bcm43xx/Makefile @@ -0,0 +1,16 @@ +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_vstack.o \ + $(bcm43xx-mac80211-obj-y) $(bcm43xx-mac80211-obj-m) diff -puN /dev/null drivers/net/wireless/mac80211/bcm43xx/bcm43xx.h --- /dev/null +++ a/drivers/net/wireless/mac80211/bcm43xx/bcm43xx.h @@ -0,0 +1,870 @@ +#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" +#include "bcm43xx_vstack.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_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) + +/* Rate values. */ +#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 + +#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_vstack; + +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 generic vstack + + /* 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; + + /* List of all wireless devices on this chip */ + struct list_head devlist; + u8 nr_devs; +}; + +/* 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; + + /* Generic value stack used to tempoarly store + * register values. + */ + struct bcm43xx_vstack genstack; +#define BCM43xx_GENSTACK_SIZE 23 + + /* The firmware for this wireless core. */ + const struct firmware *ucode; + const struct firmware *pcm; + const struct firmware *initvals0; + const struct firmware *initvals1; + + /* 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 -puN /dev/null drivers/net/wireless/mac80211/bcm43xx/bcm43xx_debugfs.c --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/bcm43xx/bcm43xx_debugfs.h --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/bcm43xx/bcm43xx_dma.c --- /dev/null +++ a/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_dma.c @@ -0,0 +1,1297 @@ +/* + + 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); + 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) + 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; + + 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; + } + + 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_meta; + err = dmacontroller_setup(ring); + if (err) + goto err_free_ringmemory; + +out: + return ring; + +err_free_ringmemory: + free_ringmemory(ring); +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 void 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; + struct bcm43xx_dmadesc_generic *desc; + struct bcm43xx_dmadesc_meta *meta; + +#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); + memset(meta, 0, sizeof(*meta)); + + 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->dmaaddr = map_descbuffer(ring, (unsigned char *)header, + sizeof(struct bcm43xx_txhdr_fw4), 1); + ops->fill_descriptor(ring, desc, meta->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->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1); + meta->is_last_fragment = 1; + + 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)); +} + +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; + + 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; + } + + dma_tx_fragment(ring, skb, ctl); + 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 -puN /dev/null drivers/net/wireless/mac80211/bcm43xx/bcm43xx_dma.h --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/bcm43xx/bcm43xx_leds.c --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/bcm43xx/bcm43xx_leds.h --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/bcm43xx/bcm43xx_lo.c --- /dev/null +++ a/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_lo.c @@ -0,0 +1,1081 @@ +/* + + 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_vstack.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); +} + +static void lo_measure_setup(struct bcm43xx_wldev *dev, + struct bcm43xx_vstack *stack) +{ + 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)) { + bcm43xx_phy_stacksave(stack, BCM43xx_PHY_LO_MASK); + bcm43xx_phy_stacksave(stack, BCM43xx_PHY_EXTG(0x01)); + bcm43xx_phy_stacksave(stack, BCM43xx_PHY_DACCTL); + bcm43xx_phy_stacksave(stack, BCM43xx_PHY_BASE(0x14)); + bcm43xx_phy_stacksave(stack, BCM43xx_PHY_HPWR_TSSICTL); + + bcm43xx_phy_write(dev, BCM43xx_PHY_HPWR_TSSICTL, 0x100); + bcm43xx_phy_write(dev, BCM43xx_PHY_EXTG(0x01), 0x40); + bcm43xx_phy_write(dev, BCM43xx_PHY_DACCTL, 0x40); + bcm43xx_phy_write(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) { + bcm43xx_phy_stacksave(stack, BCM43xx_PHY_ANALOGOVER); + bcm43xx_phy_stacksave(stack, BCM43xx_PHY_ANALOGOVERVAL); + bcm43xx_phy_stacksave(stack, BCM43xx_PHY_RFOVER); + bcm43xx_phy_stacksave(stack, BCM43xx_PHY_RFOVERVAL); + bcm43xx_phy_stacksave(stack, BCM43xx_PHY_CLASSCTL); + bcm43xx_phy_stacksave(stack, BCM43xx_PHY_BASE(0x3E)); + bcm43xx_phy_stacksave(stack, 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); + } + bcm43xx_mmio_stacksave(stack, 0x3F4); + bcm43xx_mmio_stacksave(stack, 0x3E2); + bcm43xx_radio_stacksave(stack, 0x43); + bcm43xx_radio_stacksave(stack, 0x7A); + bcm43xx_phy_stacksave(stack, BCM43xx_PHY_PGACTL); + bcm43xx_phy_stacksave(stack, BCM43xx_PHY_BASE(0x2A)); + bcm43xx_phy_stacksave(stack, BCM43xx_PHY_SYNCCTL); + bcm43xx_phy_stacksave(stack, BCM43xx_PHY_DACCTL); + + if (!has_tx_magnification(phy)) { + tmp = bcm43xx_radio_read16(dev, 0x52); + tmp &= 0x00F0; + bcm43xx_vstack_save(stack, BCM43xx_VSTACK_RADIO, + 0x52, tmp); + } + if (phy->type == BCM43xx_PHYTYPE_B) { + bcm43xx_phy_stacksave(stack, BCM43xx_PHY_BASE(0x30)); + bcm43xx_phy_stacksave(stack, 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 = bcm43xx_vstack_restore(stack, BCM43xx_VSTACK_PHY, + BCM43xx_PHY_SYNCCTL); + bcm43xx_phy_write(dev, BCM43xx_PHY_SYNCCTL, tmp & 0xFF7F); + tmp = bcm43xx_vstack_restore(stack, BCM43xx_VSTACK_RADIO, 0x7A); + 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 bcm43xx_vstack *stack, + u8 old_channel) +{ + 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_mmio_stackrestore(stack, 0x3F4); + bcm43xx_phy_stackrestore(stack, BCM43xx_PHY_PGACTL); + bcm43xx_phy_stackrestore(stack, BCM43xx_PHY_BASE(0x2A)); + bcm43xx_phy_stackrestore(stack, BCM43xx_PHY_SYNCCTL); + bcm43xx_phy_stackrestore(stack, BCM43xx_PHY_DACCTL); + bcm43xx_radio_stackrestore(stack, 0x43); + bcm43xx_radio_stackrestore(stack, 0x7A); + if (!has_tx_magnification(phy)) { + tmp = bcm43xx_vstack_restore(stack, BCM43xx_VSTACK_RADIO, 0x52); + bcm43xx_radio_write16(dev, 0x52, + (bcm43xx_radio_read16(dev, 0x52) + & 0xFF0F) | tmp); + } + bcm43xx_mmio_stackrestore(stack, 0x3E2); + if (phy->type == BCM43xx_PHYTYPE_B && + phy->radio_ver == 0x2050 && + phy->radio_rev <= 5) { + bcm43xx_phy_stackrestore(stack, BCM43xx_PHY_BASE(0x30)); + bcm43xx_phy_stackrestore(stack, BCM43xx_PHY_BASE(0x06)); + } + if (phy->rev >= 2) { + bcm43xx_phy_stackrestore(stack, BCM43xx_PHY_ANALOGOVER); + bcm43xx_phy_stackrestore(stack, BCM43xx_PHY_ANALOGOVERVAL); + bcm43xx_phy_stackrestore(stack, BCM43xx_PHY_CLASSCTL); + bcm43xx_phy_stackrestore(stack, BCM43xx_PHY_RFOVER); + bcm43xx_phy_stackrestore(stack, BCM43xx_PHY_RFOVERVAL); + bcm43xx_phy_stackrestore(stack, BCM43xx_PHY_BASE(0x3E)); + bcm43xx_phy_stackrestore(stack, BCM43xx_PHY_CRS0); + } + if (has_hardware_pctl(phy)) { + tmp = bcm43xx_vstack_restore(stack, BCM43xx_VSTACK_PHY, + BCM43xx_PHY_LO_MASK); + tmp &= 0xBFFF; + bcm43xx_phy_write(dev, BCM43xx_PHY_LO_MASK, tmp); + bcm43xx_phy_stackrestore(stack, BCM43xx_PHY_EXTG(0x01)); + bcm43xx_phy_stackrestore(stack, BCM43xx_PHY_DACCTL); + bcm43xx_phy_stackrestore(stack, BCM43xx_PHY_BASE(0x14)); + bcm43xx_phy_stackrestore(stack, BCM43xx_PHY_OFDM(0x78)); + } + bcm43xx_radio_selectchannel(dev, 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; + u8 old_channel; + struct bcm43xx_vstack *stack; + + assert(phy->type == BCM43xx_PHYTYPE_B || + phy->type == BCM43xx_PHYTYPE_G); + + /* We use the generic value stack. */ + stack = &dev->genstack; + + old_channel = phy->channel; + lo_measure_setup(dev, stack); + lo_measure(dev); + lo_measure_restore(dev, stack, old_channel); + + bcm43xx_vstack_cleanup(stack); + 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 -puN /dev/null drivers/net/wireless/mac80211/bcm43xx/bcm43xx_lo.h --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/bcm43xx/bcm43xx_main.c --- /dev/null +++ a/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_main.c @@ -0,0 +1,3988 @@ +/* + + 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 + +#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 = (((_rateid) * 10) / 2), \ + .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 = index; + if (kidx >= 4) + kidx -= 4; + 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; + + 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. + */ + for (i = 8; 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 = 8; 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) { + /* 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_IHR_ENABLED; + if (flags & BCM43xx_TMSLOW_GMODE) + macctl |= BCM43xx_MACCTL_GMODE; + 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->ucode); + dev->ucode = NULL; + release_firmware(dev->pcm); + dev->pcm = NULL; + release_firmware(dev->initvals0); + dev->initvals0 = NULL; + release_firmware(dev->initvals1); + dev->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->ucode) { + snprintf(buf, ARRAY_SIZE(buf), "bcm43xx_microcode%d%s.fw", + (rev >= 5 ? 5 : rev), + modparam_fwpostfix); + err = request_firmware(&dev->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->pcm) { + snprintf(buf, ARRAY_SIZE(buf), + "bcm43xx_pcm%d%s.fw", + (rev < 5 ? 4 : 5), + modparam_fwpostfix); + err = request_firmware(&dev->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->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->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->initvals0->size % sizeof(struct bcm43xx_initval)) { + printk(KERN_ERR PFX "InitVals fileformat error.\n"); + goto error; + } + } + + if (!dev->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->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->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->ucode->data); + len = dev->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->pcm->data); + len = dev->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); + +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->initvals0->data, + dev->initvals0->size / sizeof(struct bcm43xx_initval)); + if (err) + goto out; + if (dev->initvals1) { + err = bcm43xx_write_initvals(dev, (struct bcm43xx_initval *)dev->initvals1->data, + dev->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_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_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); + bcm43xx_vstack_free(&dev->genstack); + 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_vstack_alloc(dev, &dev->genstack, + BCM43xx_GENSTACK_SIZE, + GFP_KERNEL); + if (err) + goto err_kfree_lo_control; + + err = bcm43xx_phy_init_tssi2dbm_table(dev); + if (err) + goto err_vstack_free; + + /* 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_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_vstack_free: + bcm43xx_vstack_free(&dev->genstack); +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_rssi = -110; + hw->max_signal = 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, 0x4301) }, + { 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 -puN /dev/null drivers/net/wireless/mac80211/bcm43xx/bcm43xx_main.h --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/bcm43xx/bcm43xx_pcmcia.c --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/bcm43xx/bcm43xx_pcmcia.h --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/bcm43xx/bcm43xx_phy.c --- /dev/null +++ a/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_phy.c @@ -0,0 +1,4235 @@ +/* + + 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_vstack.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) { + bcm43xx_wireless_core_reset(dev, 0); + bcm43xx_phy_initg(dev); + 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, 0x04A2, + (bcm43xx_phy_read(dev, 0x04A2) + & 0xF0FF) | 0x0700); + bcm43xx_phy_write(dev, 0x04A0, + (bcm43xx_phy_read(dev, 0x04A0) + & 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 (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->type == BCM43xx_PHYTYPE_G) { + 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]; + 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); + 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); + 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); + 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: + + 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 (has_loopback_gain(phy)) + 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) { + 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) + 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) { + 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->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); + if (0 /*FIXME: OFDM may not be used in the current locale*/) { + 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); + 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); + 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); + } + 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]); + } + 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; + } + phy_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(0x04A8); + 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) { + extlna |= (i << 8); + if (extlna) + extlna |= 0x8000; + 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; +} + +u16 bcm43xx_radio_init2050(struct bcm43xx_wldev *dev) +{ + struct bcm43xx_phy *phy = &dev->phy; + struct bcm43xx_vstack *stack = &dev->genstack; + u16 rcc; + u16 radio78; + u16 ret; + u16 i, j; + u32 tmp1 = 0, tmp2 = 0; + + bcm43xx_radio_stacksave(stack, 0x43); + bcm43xx_radio_stacksave(stack, 0x51); + bcm43xx_radio_stacksave(stack, 0x52); + bcm43xx_phy_stacksave(stack, BCM43xx_PHY_PGACTL); + bcm43xx_phy_stacksave(stack, BCM43xx_PHY_BASE(0x5A)); + bcm43xx_phy_stacksave(stack, BCM43xx_PHY_BASE(0x59)); + bcm43xx_phy_stacksave(stack, BCM43xx_PHY_BASE(0x58)); + + if (phy->type == BCM43xx_PHYTYPE_B) { + bcm43xx_phy_stacksave(stack, BCM43xx_PHY_BASE(0x30)); + bcm43xx_mmio_stacksave(stack, 0x3EC); + + bcm43xx_phy_write(dev, BCM43xx_PHY_BASE(0x30), 0xFF); + bcm43xx_write16(dev, 0x3EC, 0x3F3F); + } else if (phy->gmode) { + bcm43xx_phy_stacksave(stack, BCM43xx_PHY_RFOVER); + bcm43xx_phy_stacksave(stack, BCM43xx_PHY_RFOVERVAL); + bcm43xx_phy_stacksave(stack, BCM43xx_PHY_ANALOGOVER); + bcm43xx_phy_stacksave(stack, BCM43xx_PHY_ANALOGOVERVAL); + bcm43xx_phy_stacksave(stack, BCM43xx_PHY_CRS0); + bcm43xx_phy_stacksave(stack, 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)) { + bcm43xx_phy_stacksave(stack, BCM43xx_PHY_LO_MASK); + bcm43xx_phy_stacksave(stack, 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); + + bcm43xx_phy_stacksave(stack, BCM43xx_PHY_SYNCCTL); + bcm43xx_phy_write(dev, BCM43xx_PHY_SYNCCTL, + bcm43xx_phy_read(dev, BCM43xx_PHY_SYNCCTL) + & 0xFF7F); + bcm43xx_mmio_stacksave(stack, 0x3E6); + bcm43xx_mmio_stacksave(stack, 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) { + 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) { + 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) { + 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) { + 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) { + 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) { + 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) { + 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) { + 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) { + 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) { + 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_stackrestore(stack, BCM43xx_PHY_PGACTL); + bcm43xx_radio_stackrestore(stack, 0x51); + bcm43xx_radio_stackrestore(stack, 0x52); + bcm43xx_radio_stackrestore(stack, 0x43); + bcm43xx_phy_stackrestore(stack, BCM43xx_PHY_BASE(0x5A)); + bcm43xx_phy_stackrestore(stack, BCM43xx_PHY_BASE(0x59)); + bcm43xx_phy_stackrestore(stack, BCM43xx_PHY_BASE(0x58)); + bcm43xx_mmio_stackrestore(stack, 0x3E6); + if (phy->analog != 0) + bcm43xx_mmio_stackrestore(stack, 0x3F4); + bcm43xx_phy_stackrestore(stack, BCM43xx_PHY_SYNCCTL); + bcm43xx_synth_pu_workaround(dev, phy->channel); + if (phy->type == BCM43xx_PHYTYPE_B) { + bcm43xx_phy_stackrestore(stack, BCM43xx_PHY_BASE(0x30)); + bcm43xx_mmio_stackrestore(stack, 0x3EC); + } else if (phy->gmode) { + bcm43xx_write16(dev, BCM43xx_MMIO_PHY_RADIO, + bcm43xx_read16(dev, BCM43xx_MMIO_PHY_RADIO) + & 0x7FFF); + bcm43xx_phy_stackrestore(stack, BCM43xx_PHY_RFOVER); + bcm43xx_phy_stackrestore(stack, BCM43xx_PHY_RFOVERVAL); + bcm43xx_phy_stackrestore(stack, BCM43xx_PHY_ANALOGOVER); + bcm43xx_phy_stackrestore(stack, BCM43xx_PHY_ANALOGOVERVAL); + bcm43xx_phy_stackrestore(stack, BCM43xx_PHY_CRS0); + bcm43xx_phy_stackrestore(stack, BCM43xx_PHY_CLASSCTL); + bcm43xx_write16(dev, BCM43xx_MMIO_PHY_RADIO, + bcm43xx_read16(dev, BCM43xx_MMIO_PHY_RADIO) + & 0x7FFF); + } + if (i > 15) + ret = radio78; + else + ret = rcc; + bcm43xx_vstack_cleanup(stack); + + 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 -puN /dev/null drivers/net/wireless/mac80211/bcm43xx/bcm43xx_phy.h --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/bcm43xx/bcm43xx_pio.c --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/bcm43xx/bcm43xx_pio.h --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/bcm43xx/bcm43xx_power.c --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/bcm43xx/bcm43xx_power.h --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/bcm43xx/bcm43xx_sysfs.c --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/bcm43xx/bcm43xx_sysfs.h --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/bcm43xx/bcm43xx_tables.c --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/bcm43xx/bcm43xx_tables.h --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/bcm43xx/bcm43xx_vstack.c --- /dev/null +++ a/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_vstack.c @@ -0,0 +1,202 @@ +/* + + Broadcom BCM43xx wireless driver + + Copyright (c) 2005 Martin Langer , + Stefano Brivio + Danny van Dyk + Andreas Jaggi + Copyright (c) 2005, 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.h" +#include "bcm43xx_vstack.h" +#include "bcm43xx_phy.h" +#include "bcm43xx_tables.h" + + +void bcm43xx_vstack_free(struct bcm43xx_vstack *stack) +{ + if (!stack) + return; + kfree(stack->items); + stack->items = NULL; + stack->nr_items = 0; + stack->top = 0; +} + +void bcm43xx_vstack_cleanup(struct bcm43xx_vstack *stack) +{ + stack->top = 0; +} + +int bcm43xx_vstack_alloc(struct bcm43xx_wldev *dev, + struct bcm43xx_vstack *stack, + u8 size, + gfp_t gfp_flags) +{ + stack->items = kcalloc(size, + sizeof(struct bcm43xx_vstack_item), + gfp_flags); + if (!stack->items) + return -ENOMEM; + assert(size <= 254); + stack->nr_items = size; + stack->dev = dev; + bcm43xx_vstack_cleanup(stack); + + return 0; +} + +void bcm43xx_vstack_save(struct bcm43xx_vstack *stack, + enum bcm43xx_vstack_type type, + u16 offset, u16 value) +{ + struct bcm43xx_vstack_item *top; + + if (BCM43xx_DEBUG) { + if (unlikely((offset & 0xC000) != 0)) { + printk(KERN_ERR PFX "bcm43xx_vstack_save() upper two bits " + "in \"offset\" set. They are used for " + "type encoding and must be free.\n"); + dump_stack(); + return; + } + assert((type & ~0x3UL) == 0); + assert(stack->nr_items <= 254); + if (unlikely(stack->top >= stack->nr_items)) { + printk(KERN_ERR PFX "bcm43xx_vstack_save() overflow\n"); + dump_stack(); + return; + } + } + + top = &(stack->items[stack->top]); + top->value = value; + top->offset_type = offset; + top->offset_type |= (type << 14); + stack->top++; +} + +u16 bcm43xx_vstack_restore(struct bcm43xx_vstack *stack, + enum bcm43xx_vstack_type type, + u16 offset) +{ + struct bcm43xx_vstack_item *item; + int i; + + assert(stack->top <= stack->nr_items); + item = &(stack->items[0]); + for (i = 0; i < stack->top + 1; i++, item++) { + if ((item->offset_type & 0x3FFF) != offset) + continue; + if (((item->offset_type & 0xC000) >> 14) != type) + continue; + return item->value; + } + + if (BCM43xx_DEBUG) { + printk(KERN_ERR PFX "bcm43xx_vstack_restore() value " + "(%d, 0x%04X) not found\n", + type, offset); + dump_stack(); + } + + return 0; +} + +void bcm43xx_phy_stacksave(struct bcm43xx_vstack *stack, + u16 offset) +{ + u16 v; + + v = bcm43xx_phy_read(stack->dev, offset); + bcm43xx_vstack_save(stack, BCM43xx_VSTACK_PHY, + offset, v); +} + +void bcm43xx_phy_stackrestore(struct bcm43xx_vstack *stack, + u16 offset) +{ + u16 v; + + v = bcm43xx_vstack_restore(stack, BCM43xx_VSTACK_PHY, + offset); + bcm43xx_phy_write(stack->dev, offset, v); +} + +void bcm43xx_radio_stacksave(struct bcm43xx_vstack *stack, + u16 offset) +{ + u16 v; + + v = bcm43xx_radio_read16(stack->dev, offset); + bcm43xx_vstack_save(stack, BCM43xx_VSTACK_RADIO, + offset, v); +} + +void bcm43xx_radio_stackrestore(struct bcm43xx_vstack *stack, + u16 offset) +{ + u16 v; + + v = bcm43xx_vstack_restore(stack, BCM43xx_VSTACK_RADIO, + offset); + bcm43xx_radio_write16(stack->dev, offset, v); +} + +void bcm43xx_ilt_stacksave(struct bcm43xx_vstack *stack, + u16 offset) +{ + u16 v; + + v = bcm43xx_ofdmtab_read16(stack->dev, offset, 0); + bcm43xx_vstack_save(stack, BCM43xx_VSTACK_ILT, + offset, v); +} + +void bcm43xx_ilt_stackrestore(struct bcm43xx_vstack *stack, + u16 offset) +{ + u16 v; + + v = bcm43xx_vstack_restore(stack, BCM43xx_VSTACK_ILT, + offset); + bcm43xx_ofdmtab_write16(stack->dev, offset, 0, v); +} + +void bcm43xx_mmio_stacksave(struct bcm43xx_vstack *stack, + u16 offset) +{ + u16 v; + + v = bcm43xx_read16(stack->dev, offset); + bcm43xx_vstack_save(stack, BCM43xx_VSTACK_MMIO, + offset, v); +} + +void bcm43xx_mmio_stackrestore(struct bcm43xx_vstack *stack, + u16 offset) +{ + u16 v; + + v = bcm43xx_vstack_restore(stack, BCM43xx_VSTACK_MMIO, + offset); + bcm43xx_write16(stack->dev, offset, v); +} diff -puN /dev/null drivers/net/wireless/mac80211/bcm43xx/bcm43xx_vstack.h --- /dev/null +++ a/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_vstack.h @@ -0,0 +1,83 @@ +#ifndef BCM43xx_VSTACK_H_ +#define BCM43xx_VSTACK_H_ + +#include + + +/* A generic implementation of a value-stack to + * backup random values and restore them in random order. + */ + +struct bcm43xx_wldev; + +enum bcm43xx_vstack_type { + BCM43xx_VSTACK_PHY = 0x0, + BCM43xx_VSTACK_RADIO = 0x1, + BCM43xx_VSTACK_ILT = 0x2, + BCM43xx_VSTACK_MMIO = 0x3, +}; + +struct bcm43xx_vstack_item { + u16 offset_type; + u16 value; +}; + +struct bcm43xx_vstack { + struct bcm43xx_vstack_item *items; + struct bcm43xx_wldev *dev; + u8 nr_items; + u8 top; +}; + +/* Static allocation. + * Note that stacksize must be < 255. + */ +#define BCM43xx_VSTACK_STATIC(bcm, stackname, stacksize) \ + struct bcm43xx_vstack_item stackname##_items[(stacksize)]; \ + struct bcm43xx_vstack stackname = { \ + .items = stackname##_items, \ + .nr_items = (stacksize), \ + .top = 0, \ + .bcm = (bcm), \ + }; + +/* Dynamic allocation. + * Note that size must be < 255. + */ +int bcm43xx_vstack_alloc(struct bcm43xx_wldev *dev, + struct bcm43xx_vstack *stack, + u8 size, + gfp_t gfp_flags); +void bcm43xx_vstack_free(struct bcm43xx_vstack *stack); +void bcm43xx_vstack_cleanup(struct bcm43xx_vstack *stack); + +/* Stack save/restore */ +void bcm43xx_phy_stacksave(struct bcm43xx_vstack *stack, + u16 offset); +void bcm43xx_phy_stackrestore(struct bcm43xx_vstack *stack, + u16 offset); + +void bcm43xx_radio_stacksave(struct bcm43xx_vstack *stack, + u16 offset); +void bcm43xx_radio_stackrestore(struct bcm43xx_vstack *stack, + u16 offset); + +void bcm43xx_ilt_stacksave(struct bcm43xx_vstack *stack, + u16 offset); +void bcm43xx_ilt_stackrestore(struct bcm43xx_vstack *stack, + u16 offset); + +void bcm43xx_mmio_stacksave(struct bcm43xx_vstack *stack, + u16 offset); +void bcm43xx_mmio_stackrestore(struct bcm43xx_vstack *stack, + u16 offset); + +/* Lowlevel save/restore. */ +void bcm43xx_vstack_save(struct bcm43xx_vstack *stack, + enum bcm43xx_vstack_type type, + u16 offset, u16 value); +u16 bcm43xx_vstack_restore(struct bcm43xx_vstack *stack, + enum bcm43xx_vstack_type type, + u16 offset); + +#endif /* BCM43xx_VSTACK_H_ */ diff -puN /dev/null drivers/net/wireless/mac80211/bcm43xx/bcm43xx_xmit.c --- /dev/null +++ a/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_xmit.c @@ -0,0 +1,615 @@ +/* + + 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 +__le16 bcm43xx_calc_duration(const struct ieee80211_hdr *wireless_header, + u8 bitrate) +{ + const u16 frame_ctl = le16_to_cpu(wireless_header->frame_control); + __le16 duration_id = wireless_header->duration_id; + + switch (frame_ctl & IEEE80211_FCTL_FTYPE) { + case IEEE80211_FTYPE_DATA: + case IEEE80211_FTYPE_MGMT: + //TODO: Steal the code from ieee80211, once it is completed there. + break; + case IEEE80211_FTYPE_CTL: + /* Use the original duration/id. */ + break; + default: + assert(0); + } + + return duration_id; +} + +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 = bcm43xx_calc_fallback_rate(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); + txhdr->dur_fb = bcm43xx_calc_duration(wlhdr, rate_fb); + + 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; + + /* We must adjust the key index here. We got the "physical" + * key index, but the ucode uses it slightly different. + */ + if (key_idx >= 4) + key_idx -= 4; + 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 += 4; + assert((keyidx >= 4) && (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 -puN /dev/null drivers/net/wireless/mac80211/bcm43xx/bcm43xx_xmit.h --- /dev/null +++ a/drivers/net/wireless/mac80211/bcm43xx/bcm43xx_xmit.h @@ -0,0 +1,215 @@ +#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); + +#endif /* BCM43xx_XMIT_H_ */ diff -puN /dev/null drivers/net/wireless/mac80211/p54/Kconfig --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/p54/Makefile --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/p54/net2280.h --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/p54/prism54.h --- /dev/null +++ a/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_INIT, /* ?? */ + 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 -puN /dev/null drivers/net/wireless/mac80211/p54/prism54common.c --- /dev/null +++ a/drivers/net/wireless/mac80211/p54/prism54common.c @@ -0,0 +1,813 @@ + +/* + * 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 = min(hdr->rate + 1, 12); /* TODO: check this */ + 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 ((le16_to_cpu(payload->status) >> 8) & 1) + status.flags |= IEEE80211_TX_STATUS_ACK; + 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); + + /* 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 = (control->flags & IEEE80211_TXCTL_NO_ACK) ? + 0 : cpu_to_le32(0x7f020000); + 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 -1; + } + + 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 -1; + } + + 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 -puN /dev/null drivers/net/wireless/mac80211/p54/prism54common.h --- /dev/null +++ a/drivers/net/wireless/mac80211/p54/prism54common.h @@ -0,0 +1,308 @@ +#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 unknown1; + u8 rate; + u8 rssi; + u8 padding; + u16 unknown2; + __le64 timestamp; + u8 data[0]; +} __attribute__ ((packed)); + +struct p54_frame_sent_hdr { + __le16 status; + __le16 ack_rssi; + __le16 seq; + u16 unknown; +} __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; + __le32 magic4; + __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)); + +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 -puN /dev/null drivers/net/wireless/mac80211/p54/prism54magic.h --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/p54/prism54pci.c --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/p54/prism54pci.h --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/p54/prism54usb.c --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/p54/prism54usb.h --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/rt2x00/Kconfig --- /dev/null +++ a/drivers/net/wireless/mac80211/rt2x00/Kconfig @@ -0,0 +1,86 @@ +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_FIRMWARE + boolean + depends on RT2X00 + +config RT2400PCI + tristate "Ralink rt2400 pci/pcmcia support" + depends on RT2X00 && PCI + select RT2X00_LIB + 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 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_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 + ---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_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 + tristate "Ralink debugfs support" + depends on RT2X00 && RT2X00_LIB && DEBUG_FS + ---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 -puN /dev/null drivers/net/wireless/mac80211/rt2x00/Makefile --- /dev/null +++ a/drivers/net/wireless/mac80211/rt2x00/Makefile @@ -0,0 +1,9 @@ +rt2x00lib-objs := rt2x00dev.o rt2x00mac.o + +obj-$(CONFIG_RT2X00_LIB) += rt2x00lib.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 +obj-$(CONFIG_RT2X00_DEBUGFS) += rt2x00debug.o diff -puN /dev/null drivers/net/wireless/mac80211/rt2x00/rt2400pci.c --- /dev/null +++ a/drivers/net/wireless/mac80211/rt2x00/rt2400pci.c @@ -0,0 +1,2586 @@ +/* + 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 "rt2x00lib.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); +} + +#if defined(CONFIG_RT2X00_DEBUGFS) || defined(CONFIG_RT2X00_DEBUGFS_MODULE) +#include "rt2x00debug.h" + +#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 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), + .length = CSR_REG_SIZE, + }, + .reg_eeprom = { + .read = rt2400pci_read_eeprom, + .write = rt2400pci_write_eeprom, + .word_size = sizeof(u16), + .length = EEPROM_SIZE, + }, + .reg_bbp = { + .read = rt2400pci_read_bbp, + .write = rt2400pci_write_bbp, + .word_size = sizeof(u8), + .length = BBP_SIZE, + }, +}; + +static void rt2400pci_open_debugfs(struct rt2x00_dev *rt2x00dev) +{ + if (!rt2x00debug_register(&rt2400pci_rt2x00debug, + rt2x00dev->hw->wiphy, rt2x00dev)) + ERROR("Failed to register debug handler.\n"); +} + +static void rt2400pci_close_debugfs(struct rt2x00_dev *rt2x00dev) +{ + rt2x00debug_deregister(&rt2x00dev->debugfs_data); +} +#else /* CONFIG_RT2X00_DEBUGFS */ +static inline void rt2400pci_open_debugfs(struct rt2x00_dev *rt2x00dev){} +static inline void rt2400pci_close_debugfs(struct rt2x00_dev *rt2x00dev){} +#endif /* CONFIG_RT2X00_DEBUGFS */ + +/* + * Configuration handlers. + */ +static void rt2400pci_config_bssid(struct rt2x00_dev *rt2x00dev, u8 *bssid) +{ + /* + * 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, bssid, ETH_ALEN); +} + +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 rf2, int channel, int freq, int txpower) +{ + u32 rf1 = rt2x00dev->rf1; + 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) +{ + /* + * 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, addr, ETH_ALEN); +} + +/* + * 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 int rt2400pci_alloc_dma_ring(struct rt2x00_dev *rt2x00dev, + enum ring_index ring_type, work_func_t handler, + const u16 max_entries, const u16 data_size, const u16 desc_size) +{ + struct data_ring *ring = &rt2x00dev->ring[ring_type]; + unsigned int i; + + /* + * Initialize work structure for deferred work. + */ + INIT_WORK(&ring->irq_work, handler); + + 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 = ring->data_addr + + (i * ring->desc_size); + ring->entry[i].data_addr = ring->data_addr + + (ring->stats.limit * ring->desc_size) + + (i * ring->data_size); + ring->entry[i].data_dma = ring->data_dma + + (ring->stats.limit * ring->desc_size) + + (i * ring->data_size); + } + + return 0; +} + +static void rt2400pci_free_ring(struct rt2x00_dev *rt2x00dev, + enum ring_index ring_type) +{ + struct data_ring *ring = &rt2x00dev->ring[ring_type]; + + 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 rt2400pci_allocate_dma_rings(struct rt2x00_dev *rt2x00dev) +{ + if (rt2400pci_alloc_dma_ring(rt2x00dev, RING_RX, + rt2400pci_rxdone, RX_ENTRIES, DATA_FRAME_SIZE, + RXD_DESC_SIZE) || + rt2400pci_alloc_dma_ring(rt2x00dev, RING_TX, + rt2400pci_txdone, TX_ENTRIES, DATA_FRAME_SIZE, + TXD_DESC_SIZE) || + rt2400pci_alloc_dma_ring(rt2x00dev, RING_ATIM, + rt2400pci_txdone, ATIM_ENTRIES, DATA_FRAME_SIZE, + TXD_DESC_SIZE) || + rt2400pci_alloc_dma_ring(rt2x00dev, RING_PRIO, + rt2400pci_txdone, TX_ENTRIES, DATA_FRAME_SIZE, + TXD_DESC_SIZE) || + rt2400pci_alloc_dma_ring(rt2x00dev, RING_BEACON, + NULL, BEACON_ENTRIES, MGMT_FRAME_SIZE, TXD_DESC_SIZE)) { + return -ENOMEM; + } + + return 0; +} + +static void rt2400pci_free_rings(struct rt2x00_dev *rt2x00dev) +{ + rt2400pci_free_ring(rt2x00dev, RING_RX); + rt2400pci_free_ring(rt2x00dev, RING_TX); + rt2400pci_free_ring(rt2x00dev, RING_ATIM); + rt2400pci_free_ring(rt2x00dev, RING_PRIO); + rt2400pci_free_ring(rt2x00dev, RING_BEACON); +} + +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; +} + +/* + * Device initialization functions. + */ +static int rt2400pci_initialize(struct rt2x00_dev *rt2x00dev) +{ + if (GET_FLAG(rt2x00dev, DEVICE_INITIALIZED)) + return 0; + + /* + * Allocate all data rings. + */ + if (rt2400pci_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; + + /* + * Register interrupt handler. + */ + if (request_irq(rt2x00dev_pci(rt2x00dev)->irq, rt2400pci_interrupt, + IRQF_SHARED, DRV_NAME, rt2x00dev)) { + ERROR("IRQ %d allocation failed.\n", + rt2x00dev_pci(rt2x00dev)->irq); + goto exit_fail; + } + + SET_FLAG(rt2x00dev, DEVICE_INITIALIZED); + + return 0; + +exit_fail: + rt2400pci_free_rings(rt2x00dev); + + return -EIO; +} + +static void rt2400pci_uninitialize(struct rt2x00_dev *rt2x00dev) +{ + if (!GET_FLAG(rt2x00dev, DEVICE_INITIALIZED)) + return; + + /* + * Cancel scanning. + */ + if (rt2x00dev->scan) + rt2x00_signal_scan(rt2x00dev->scan, SCANNING_CANCELLED); + + /* + * Flush out all pending work. + */ + flush_workqueue(rt2x00dev->workqueue); + + /* + * Free DMA rings. + */ + rt2400pci_free_rings(rt2x00dev); + + /* + * Free irq line. + */ + free_irq(rt2x00dev_pci(rt2x00dev)->irq, rt2x00dev); + + CLEAR_FLAG(rt2x00dev, DEVICE_INITIALIZED); +} + +/* + * 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; + + /* + * Don't enable the radio twice. + * or if the hardware button has been disabled. + */ + if (GET_FLAG(rt2x00dev, DEVICE_ENABLED_RADIO) || + !GET_FLAG(rt2x00dev, DEVICE_ENABLED_RADIO_HW)) + return 0; + + /* + * Initialize all registers. + */ + if (rt2400pci_init_rings(rt2x00dev) || + rt2400pci_init_registers(rt2x00dev) || + rt2400pci_init_bbp(rt2x00dev)) { + ERROR("Register initialization failed.\n"); + goto exit_fail; + } + + /* + * Determine channel change time. + */ + if (rt2x00lib_detect_channel_time(rt2x00dev)) + goto exit_fail; + + /* + * Clear interrupts. + */ + rt2x00_register_read(rt2x00dev, CSR7, ®); + rt2x00_register_write(rt2x00dev, CSR7, reg); + + SET_FLAG(rt2x00dev, DEVICE_ENABLED_RADIO); + + /* + * 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 RX. + */ + rt2400pci_toggle_rx(rt2x00dev, 1); + + /* + * Enable LED + */ + rt2400pci_enable_led(rt2x00dev); + + ieee80211_start_queues(rt2x00dev->hw); + + return 0; + +exit_fail: + rt2400pci_uninitialize(rt2x00dev); + return -EIO; +} + +static void rt2400pci_disable_radio(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + if (!GET_FLAG(rt2x00dev, DEVICE_ENABLED_RADIO)) + return; + + ieee80211_stop_queues(rt2x00dev->hw); + + /* + * Disable LED + */ + rt2400pci_disable_led(rt2x00dev); + + CLEAR_FLAG(rt2x00dev, DEVICE_ENABLED_RADIO); + + 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); + + rt2400pci_toggle_rx(rt2x00dev, 0); + + /* + * 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_desc *txd, struct ieee80211_hdr *ieee80211hdr, + unsigned int length, struct ieee80211_tx_control *control) +{ + int tx_rate; + u32 word; + u32 duration; + u32 residual; + u16 length_high; + u16 length_low; + u16 signal; + u16 service; + u16 frame_control; + u16 seq_ctrl; + char rts_frame; + char req_timestamp; + char more_frag; + char ifs; + u8 bitrate; + + /* + * Read required fields from ieee80211 header. + */ + frame_control = le16_to_cpu(ieee80211hdr->frame_control); + seq_ctrl = le16_to_cpu(ieee80211hdr->seq_ctrl); + + /* + * Check if this frame is a RTS frame. + */ + rts_frame = is_rts_frame(frame_control); + + /* + * Check which rate should be used for this frame. + */ + if (rts_frame && control->rts_cts_rate) + tx_rate = control->rts_cts_rate; + else + tx_rate = control->tx_rate; + + /* + * Check if more fragments will follow this frame. + */ + more_frag = !!(ieee80211_get_morefrag(ieee80211hdr)); + + /* + * Beacons and probe responses require the tsf timestamp + * to be inserted into the frame. + */ + req_timestamp = !!(control->queue == IEEE80211_TX_QUEUE_BEACON || + is_probe_resp(frame_control)); + + /* + * 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) || rts_frame) + ifs = IFS_SIFS; + else + ifs = IFS_BACKOFF; + + /* + * Convert length to microseconds. + */ + bitrate = DEVICE_GET_RATE_FIELD(tx_rate, RATE); + residual = get_duration_res(length + FCS_LEN, bitrate); + duration = get_duration(length + FCS_LEN, bitrate); + + if (residual) + duration++; + + /* + * Create the signal and service values, the values are stored + * as if it was a BBP register including the busy bit + * and register number. + */ + length_high = 0x8000 | 0x0700 | (duration >> 8); + length_low = 0x8000 | 0x0800 | (duration & 0xff); + + signal = 0x8000 | 0x0500 | DEVICE_GET_RATE_FIELD(tx_rate, PLCP); + if (DEVICE_GET_RATE_FIELD(tx_rate, PREAMBLE)) + signal |= 0x0008; + + service = 0x8000 | 0x0600 | 0x0004; + if (residual <= (8 % 11)) + service |= 0x0080; + + /* + * 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, signal); + rt2x00_set_field32(&word, TXD_W3_PLCP_SERVICE, service); + rt2x00_desc_write(txd, 3, word); + + rt2x00_desc_read(txd, 4, &word); + rt2x00_set_field32(&word, TXD_W4_PLCP_LENGTH_LOW, length_low); + rt2x00_set_field32(&word, TXD_W4_PLCP_LENGTH_HIGH, 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, more_frag); + rt2x00_set_field32(&word, TXD_W0_ACK, + !(control->flags & IEEE80211_TXCTL_NO_ACK)); + rt2x00_set_field32(&word, TXD_W0_TIMESTAMP, req_timestamp); + rt2x00_set_field32(&word, TXD_W0_RTS, rts_frame); + rt2x00_set_field32(&word, TXD_W0_IFS, ifs); + rt2x00_set_field32(&word, TXD_W0_RETRY_MODE, 0); + rt2x00_desc_write(txd, 0, word); +} + +/* + * TX data initialization + */ +static int rt2400pci_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; + u16 fc; + + 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_W0_OWNER_NIC) || + rt2x00_get_field32(word, TXD_W0_VALID)) { + 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); + rt2400pci_write_tx_desc(rt2x00dev, txd, ieee80211hdr, + skb->len, control); + memcpy(&entry->tx_status.control, control, sizeof(*control)); + entry->skb = skb; + + fc = le16_to_cpu(ieee80211hdr->frame_control); + if (is_cts_frame(fc) || is_rts_frame(fc)) + SET_FLAG(entry, ENTRY_RTS_CTS_FRAME); + + rt2x00_ring_index_inc(ring); + + if (rt2x00_ring_full(ring)) + ieee80211_stop_queue(rt2x00dev->hw, control->queue); + + return 0; +} + +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); +} + +/* + * Interrupt functions. + */ +static void rt2400pci_beacondone(struct rt2x00_dev *rt2x00dev) +{ + struct data_ring *ring = &rt2x00dev->ring[RING_BEACON]; + 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->hw_ops->beacon_update(rt2x00dev->hw, skb, + &entry->tx_status.control); + + dev_kfree_skb_any(skb); +} + +static void rt2400pci_rxdone(struct work_struct *work) +{ + struct data_ring *ring = + container_of(work, struct data_ring, irq_work); + struct rt2x00_dev *rt2x00dev = ring->rt2x00dev; + struct data_entry *entry; + struct sk_buff *skb; + 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; + + size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT); + + /* + * 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; + + skb = dev_alloc_skb(size + NET_IP_ALIGN); + if (!skb) + break; + + skb_reserve(skb, NET_IP_ALIGN); + + memcpy(skb_put(skb, size), entry->data_addr, size); + + signal = rt2x00_get_field32(word2, RXD_W2_SIGNAL); + rssi = rt2x00_get_field32(word2, RXD_W2_RSSI); + rt2x00lib_update_rx_stats(rt2x00dev, signal, rssi, 0); + + __ieee80211_rx(rt2x00dev->hw, skb, &rt2x00dev->rx_status); + +skip_entry: + 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 work_struct *work) +{ + struct data_ring *ring = + container_of(work, struct data_ring, irq_work); + struct rt2x00_dev *rt2x00dev = ring->rt2x00dev; + struct data_entry *entry; + struct data_desc *txd; + u32 word; + int tx_status; + int retry; + int ack; + + 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; + + ack = rt2x00_get_field32(word, TXD_W0_ACK); + tx_status = rt2x00_get_field32(word, TXD_W0_RESULT); + retry = rt2x00_get_field32(word, TXD_W0_RETRY_COUNT); + rt2x00lib_update_tx_stats(entry, tx_status, ack, retry); + + /* + * If this is not an RTS frame send the tx_status to mac80211, + * that method also cleans up the skb structure. When this + * is a RTS frame, that it is our job to clean this structure up. + */ + if (!GET_FLAG(entry, ENTRY_RTS_CTS_FRAME)) + ieee80211_tx_status(rt2x00dev->hw, + entry->skb, &entry->tx_status); + else + dev_kfree_skb(entry->skb); + + rt2x00_set_field32(&word, TXD_W0_VALID, 0); + rt2x00_desc_write(txd, 0, word); + entry->skb = NULL; + + CLEAR_FLAG(entry, ENTRY_RTS_CTS_FRAME); + + rt2x00_ring_index_done_inc(ring); + } + + /* + * Check if we are waiting on an empty queue + * to start scanning. + */ + if (rt2x00dev->scan && + rt2x00_ring_empty(&rt2x00dev->ring[RING_TX]) && + rt2x00_ring_empty(&rt2x00dev->ring[RING_ATIM]) && + rt2x00_ring_empty(&rt2x00dev->ring[RING_PRIO])) + rt2x00_signal_scan(rt2x00dev->scan, SCANNING_READY); + + /* + * 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)) + rt2400pci_beacondone(rt2x00dev); + + /* + * 2 - Rx ring done interrupt. + * Enable the TXRX activity led. + */ + if (rt2x00_get_field32(reg, CSR7_RXDONE)) { + queue_work(rt2x00dev->workqueue, + &rt2x00dev->ring[RING_RX].irq_work); + rt2400pci_activity_led(rt2x00dev, 1); + } + + /* + * 3 - Atim ring transmit done interrupt. + */ + if (rt2x00_get_field32(reg, CSR7_TXDONE_ATIMRING)) + queue_work(rt2x00dev->workqueue, + &rt2x00dev->ring[RING_ATIM].irq_work); + + /* + * 4 - Priority ring transmit done interrupt. + */ + if (rt2x00_get_field32(reg, CSR7_TXDONE_PRIORING)) + queue_work(rt2x00dev->workqueue, + &rt2x00dev->ring[RING_PRIO].irq_work); + + /* + * 5 - Tx ring transmit done interrupt. + */ + if (rt2x00_get_field32(reg, CSR7_TXDONE_TXRING)) + queue_work(rt2x00dev->workqueue, + &rt2x00dev->ring[RING_TX].irq_work); + + return IRQ_HANDLED; +} + +/* + * 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_beacon_update(struct ieee80211_hw *hw, + struct sk_buff *skb, struct ieee80211_tx_control *control) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct data_entry *entry; + u32 reg; + + entry = rt2x00_get_data_entry(&rt2x00dev->ring[RING_BEACON]); + + /* + * 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); + rt2400pci_write_tx_desc(rt2x00dev, entry->priv, + (struct ieee80211_hdr *)skb->data, skb->len, control); + + /* + * Enable beacon generation. + */ + 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); + } + + return 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, + .passive_scan = rt2x00lib_passive_scan, + .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 = rt2400pci_beacon_update, + .tx_last_beacon = rt2400pci_tx_last_beacon, +}; + +static const struct rt2x00lib_ops rt2400pci_rt2x00_ops = { + .initialize = rt2400pci_initialize, + .uninitialize = rt2400pci_uninitialize, + .enable_radio = rt2400pci_enable_radio, + .disable_radio = rt2400pci_disable_radio, + .toggle_rx = rt2400pci_toggle_rx, + .write_tx_data = rt2400pci_write_tx_data, + .kick_tx_queue = rt2400pci_kick_tx_queue, + .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, +}; + +/* + * Device initialization functions. + */ +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); +} + +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_alloc_rings(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + + rt2x00dev->ring = kzalloc( + sizeof(struct data_ring) * RING_NUM, GFP_KERNEL); + if (!rt2x00dev->ring) { + ERROR("Ring allocation failed.\n"); + return -ENOMEM; + } + + SET_FLAG(rt2x00dev, DEVICE_SUPPORT_ATIM); + + 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.cw_min = 5; + rt2x00dev->ring[i].tx_params.cw_max = 10; + } + + 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."); + 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; +} + +static int rt2400pci_init_hw_mac(struct rt2x00_dev *rt2x00dev) +{ + u8 *addr; + + /* + * Get the pointer to the MAC address in the EEPROM. + */ + addr = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); + + /* + * Check if a valid MAC address is present. + */ + if (!is_valid_ether_addr(addr)) { + ERROR("Invalid MAC address: " MAC_FMT ".\n", MAC_ARG(addr)); + return -EINVAL; + } + + /* + * Write MAC address to register. + */ + rt2400pci_config_mac_addr(rt2x00dev, addr); + + /* + * Copy MAC address to the hw structure. + */ + SET_IEEE80211_PERM_ADDR(rt2x00dev->hw, addr); + + return 0; +} + +static void rt2400pci_init_hw_channels(struct rt2x00_dev *rt2x00dev, + struct ieee80211_channel *channels) +{ + unsigned int i; + u16 eeprom; + static const u32 vals[] = { + 0x000c1fda, 0x000c1fee, 0x000c2002, 0x000c2016, + 0x000c202a, 0x000c203e, 0x000c2052, 0x000c2066, + 0x000c207a, 0x000c208e, 0x000c20a2, 0x000c20b6, + 0x000c20ca, 0x000c20fa + }; + + /* + * Channel initialization. + * First we set the basic variables. + */ + for (i = 0; i < 13; i++) { + channels[i].chan = i + 1; + channels[i].freq = 2407 + ((i + 1) * 5); + channels[i].flag = IEEE80211_CHAN_W_IBSS | + IEEE80211_CHAN_W_ACTIVE_SCAN | IEEE80211_CHAN_W_SCAN; + channels[i].val = vals[i]; + channels[i].antenna_max = 0xff; + } + + channels[13].chan = 14; + channels[13].freq = 2484; + channels[13].flag = IEEE80211_CHAN_W_IBSS | + IEEE80211_CHAN_W_ACTIVE_SCAN | IEEE80211_CHAN_W_SCAN; + channels[13].val = vals[13]; + channels[13].antenna_max = 0xff; + + /* + * Set TX power, each EEPROM TXpower entry + * contains the TXpower value for 2 channels. + */ + for (i = 0; i < EEPROM_TXPOWER_SIZE; i++) { + rt2x00_eeprom_read(rt2x00dev, + EEPROM_TXPOWER_START + i, &eeprom); + + channels[(i * 2)].power_level = TXPOWER_FROM_DEV( + rt2x00_get_field16(eeprom, EEPROM_TXPOWER_1)); + + channels[(i * 2) + 1].power_level = TXPOWER_FROM_DEV( + rt2x00_get_field16(eeprom, EEPROM_TXPOWER_2)); + } + + /* + * Set device specific, but channel independent RF values. + */ + rt2x00dev->rf1 = 0x00022058; + if (rt2x00_rf(&rt2x00dev->chip, RF2420)) + rt2x00dev->rf3 = 0x00000111; + else + rt2x00dev->rf3 = 0x00000101; +} + +static void rt2400pci_init_hw_rates(struct rt2x00_dev *rt2x00dev, + struct ieee80211_rate *rates) +{ + /* + * Rates initialization. + */ + device_rate_entry(&rates[0], 10, 0x001, 0x00, IEEE80211_RATE_CCK); + device_rate_entry(&rates[1], 20, 0x003, 0x01, IEEE80211_RATE_CCK_2); + device_rate_entry(&rates[2], 55, 0x007, 0x02, IEEE80211_RATE_CCK_2); + device_rate_entry(&rates[3], 110, 0x00f, 0x03, IEEE80211_RATE_CCK_2); +} + +static int rt2400pci_init_hw_modes(struct rt2x00_dev *rt2x00dev) +{ + struct ieee80211_channel *channels; + struct ieee80211_rate *rates; + int status = -ENOMEM; + + /* + * RT2400 only supports 802.11b. + * Allocate memory for 14 OFDM channels and 4 CCK rates. + */ + rt2x00dev->hwmodes = + kzalloc(sizeof(struct ieee80211_hw_mode), GFP_KERNEL); + if (!rt2x00dev->hwmodes) + goto exit; + + channels = kzalloc(sizeof(struct ieee80211_channel) * 14, + GFP_KERNEL); + if (!channels) + goto exit_free_modes; + + rates = kzalloc(sizeof(struct ieee80211_rate) * 4, GFP_KERNEL); + if (!rates) + goto exit_free_channels; + + /* + * Initialize channels and rate arrays. + */ + rt2400pci_init_hw_channels(rt2x00dev, channels); + rt2400pci_init_hw_rates(rt2x00dev, rates); + + /* + * Intitialize 802.11b + * Rates: CCK. + * Channels: OFDM. + */ + rt2x00dev->hwmodes[HWMODE_B].mode = MODE_IEEE80211B; + rt2x00dev->hwmodes[HWMODE_B].num_channels = 14; + rt2x00dev->hwmodes[HWMODE_B].num_rates = 4; + rt2x00dev->hwmodes[HWMODE_B].channels = channels; + rt2x00dev->hwmodes[HWMODE_B].rates = rates; + + /* + * Register the working modes. + */ + status = ieee80211_register_hwmode(rt2x00dev->hw, + &rt2x00dev->hwmodes[HWMODE_B]); + if (status) + goto exit_free_rates; + + return 0; + +exit_free_rates: + kfree(rates); + +exit_free_channels: + kfree(channels); + +exit_free_modes: + kfree(rt2x00dev->hwmodes); + rt2x00dev->hwmodes = NULL; + +exit: + ERROR("Allocation ieee80211 modes failed.\n"); + return status; +} + +static int rt2400pci_init_hw(struct rt2x00_dev *rt2x00dev) +{ + int status; + + if (GET_FLAG(rt2x00dev, DEVICE_INITIALIZED_HW)) + return 0; + + SET_IEEE80211_DEV(rt2x00dev->hw, &rt2x00dev_pci(rt2x00dev)->dev); + + /* + * Read MAC address from EEPROM. + */ + status = rt2400pci_init_hw_mac(rt2x00dev); + if (status) + return status; + + /* + * 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; + + if (ieee80211_register_hw(rt2x00dev->hw)) + return -EIO; + + status = rt2400pci_init_hw_modes(rt2x00dev); + if (status) { + ieee80211_unregister_hw(rt2x00dev->hw); + return status; + } + + SET_FLAG(rt2x00dev, DEVICE_INITIALIZED_HW); + + return 0; +} + +static void rt2400pci_free_dev(struct rt2x00_dev *rt2x00dev) +{ + /* + * Close debugfs entry. + */ + rt2400pci_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); + + /* + * Release CSR memory. + */ + if (likely(rt2x00dev->csr_addr)) { + iounmap(rt2x00dev->csr_addr); + rt2x00dev->csr_addr = NULL; + } + + /* + * Free ieee80211_hw memory. + */ + if (likely(rt2x00dev->hwmodes)) { + kfree(rt2x00dev->hwmodes->channels); + kfree(rt2x00dev->hwmodes->rates); + kfree(rt2x00dev->hwmodes); + rt2x00dev->hwmodes = NULL; + } +} + +static int rt2400pci_alloc_dev(struct pci_dev *pci_dev, + struct ieee80211_hw *hw) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + + rt2x00dev->dev = pci_dev; + rt2x00dev->lib_ops = &rt2400pci_rt2x00_ops; + rt2x00dev->hw_ops = &rt2400pci_mac80211_ops; + rt2x00dev->hw = hw; + + /* + * 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; + } + + /* + * Allocate eeprom data. + */ + if (rt2400pci_alloc_eeprom(rt2x00dev)) + goto exit; + + /* + * Create workqueue. + */ + rt2x00dev->workqueue = create_singlethread_workqueue(DRV_NAME); + if (!rt2x00dev->workqueue) + goto exit; + + /* + * Initialize configuration work. + */ + INIT_DELAYED_WORK(&rt2x00dev->link.work, rt2400pci_link_tuner); + + /* + * Reset current working type. + */ + rt2x00dev->interface.type = -EINVAL; + + /* + * Intialize scanning attributes. + */ + rt2x00dev->scan = NULL; + + /* + * Allocate ring array. + */ + if (rt2400pci_alloc_rings(rt2x00dev)) + goto exit; + + /* + * Initialize hardware. + */ + if (rt2400pci_init_eeprom(rt2x00dev) || + rt2400pci_init_hw(rt2x00dev)) { + ERROR("Failed to initialize device.\n"); + goto exit; + } + + /* + * Open the debugfs entry. + */ + rt2400pci_open_debugfs(rt2x00dev); + + return 0; + +exit: + rt2400pci_free_dev(rt2x00dev); + + return -ENODEV; +} + +/* + * PCI driver handlers. + */ +static int rt2400pci_probe(struct pci_dev *pci_dev, + const struct pci_device_id *id) +{ + struct ieee80211_hw *hw; + int status; + + status = pci_request_regions(pci_dev, pci_name(pci_dev)); + if (status) { + ERROR("PCI request regions failed.\n"); + return status; + } + + status = pci_enable_device(pci_dev); + if (status) { + 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"); + status = -EIO; + goto exit_disable_device; + } + + hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), + &rt2400pci_mac80211_ops); + if (!hw) { + ERROR("Failed to allocate hardware.\n"); + status = -ENOMEM; + goto exit_disable_device; + } + + pci_set_drvdata(pci_dev, hw); + + status = rt2400pci_alloc_dev(pci_dev, hw); + if (status) { + ERROR("Failed to allocate device.\n"); + goto exit_free_device; + } + + return 0; + +exit_free_device: + ieee80211_free_hw(hw); + +exit_disable_device: + if (status != -EBUSY) + pci_disable_device(pci_dev); + +exit_release_regions: + pci_release_regions(pci_dev); + + pci_set_drvdata(pci_dev, NULL); + + return status; +} + +static void rt2400pci_remove(struct pci_dev *pci_dev) +{ + struct ieee80211_hw *hw = pci_get_drvdata(pci_dev); + struct rt2x00_dev *rt2x00dev = hw->priv; + + /* + * Uninitialize the 80211 stack data. + */ + ieee80211_unregister_hw(hw); + + /* + * Uninitialize and free the rt2400pci driver data. + */ + rt2400pci_disable_radio(rt2x00dev); + rt2400pci_uninitialize(rt2x00dev); + rt2400pci_free_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); +} + +#ifdef CONFIG_PM +static int rt2400pci_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 status; + + NOTICE("Going to sleep.\n"); + + /* + * Disable the radio. + */ + rt2400pci_disable_radio(rt2x00dev); + + /* + * Set device mode to sleep for power management. + */ + status = rt2400pci_set_state(rt2x00dev, STATE_SLEEP); + if (status) + return status; + + /* + * Uninitialize device and hardware. + */ + rt2400pci_uninitialize(rt2x00dev); + rt2400pci_free_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)); +} + +static int rt2400pci_resume(struct pci_dev *pci_dev) +{ + struct ieee80211_hw *hw = pci_get_drvdata(pci_dev); + struct rt2x00_dev *rt2x00dev = hw->priv; + int status; + + 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. + */ + status = rt2400pci_alloc_dev(pci_dev, hw); + if (status) { + ERROR("Failed to allocate device.\n"); + return status; + } + + /* + * Set device mode to awake for power management. + */ + status = rt2400pci_set_state(rt2x00dev, STATE_AWAKE); + if (status) + return status; + + return 0; +} +#endif /* CONFIG_PM */ + +/* + * 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) }, + { 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 = rt2400pci_probe, + .remove = __devexit_p(rt2400pci_remove), +#ifdef CONFIG_PM + .suspend = rt2400pci_suspend, + .resume = rt2400pci_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 -puN /dev/null drivers/net/wireless/mac80211/rt2x00/rt2400pci.h --- /dev/null +++ a/drivers/net/wireless/mac80211/rt2x00/rt2400pci.h @@ -0,0 +1,931 @@ +/* + 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. + */ + +/* + * PCISR: 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); + +/* + * Radio control functions. + */ +static int rt2400pci_enable_radio(struct rt2x00_dev *rt2x00dev); +static void rt2400pci_disable_radio(struct rt2x00_dev *rt2x00dev); + +/* + * Interrupt functions. + */ +static void rt2400pci_rxdone(struct work_struct *work); +static void rt2400pci_txdone(struct work_struct *work); +static irqreturn_t rt2400pci_interrupt(int irq, void *dev_instance); + +#endif /* RT2400PCI_H */ diff -puN /dev/null drivers/net/wireless/mac80211/rt2x00/rt2500pci.c --- /dev/null +++ a/drivers/net/wireless/mac80211/rt2x00/rt2500pci.c @@ -0,0 +1,2883 @@ +/* + 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 "rt2x00lib.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); +} + +#if defined(CONFIG_RT2X00_DEBUGFS) || defined(CONFIG_RT2X00_DEBUGFS_MODULE) +#include "rt2x00debug.h" + +#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 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), + .length = CSR_REG_SIZE, + }, + .reg_eeprom = { + .read = rt2500pci_read_eeprom, + .write = rt2500pci_write_eeprom, + .word_size = sizeof(u16), + .length = EEPROM_SIZE, + }, + .reg_bbp = { + .read = rt2500pci_read_bbp, + .write = rt2500pci_write_bbp, + .word_size = sizeof(u8), + .length = BBP_SIZE, + }, +}; + +static void rt2500pci_open_debugfs(struct rt2x00_dev *rt2x00dev) +{ + if (!rt2x00debug_register(&rt2500pci_rt2x00debug, + rt2x00dev->hw->wiphy, rt2x00dev)) + ERROR("Failed to register debug handler.\n"); +} + +static void rt2500pci_close_debugfs(struct rt2x00_dev *rt2x00dev) +{ + rt2x00debug_deregister(&rt2x00dev->debugfs_data); +} +#else /* CONFIG_RT2X00_DEBUGFS */ +static inline void rt2500pci_open_debugfs(struct rt2x00_dev *rt2x00dev){} +static inline void rt2500pci_close_debugfs(struct rt2x00_dev *rt2x00dev){} +#endif /* CONFIG_RT2X00_DEBUGFS */ + +/* + * Configuration handlers. + */ +static void rt2500pci_config_bssid(struct rt2x00_dev *rt2x00dev, u8 *bssid) +{ + /* + * 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, bssid, ETH_ALEN); +} + +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 rf2, int channel, int freq, int txpower) +{ + u32 rf1 = rt2x00dev->rf1; + 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, 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) +{ + /* + * 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, addr, ETH_ALEN); +} + +/* + * 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 int rt2500pci_alloc_dma_ring(struct rt2x00_dev *rt2x00dev, + enum ring_index ring_type, work_func_t handler, + const u16 max_entries, const u16 data_size, const u16 desc_size) +{ + struct data_ring *ring = &rt2x00dev->ring[ring_type]; + unsigned int i; + + /* + * Initialize work structure for deferred work. + */ + INIT_WORK(&ring->irq_work, handler); + + 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 = ring->data_addr + + (i * ring->desc_size); + ring->entry[i].data_addr = ring->data_addr + + (ring->stats.limit * ring->desc_size) + + (i * ring->data_size); + ring->entry[i].data_dma = ring->data_dma + + (ring->stats.limit * ring->desc_size) + + (i * ring->data_size); + } + + return 0; +} + +static void rt2500pci_free_ring(struct rt2x00_dev *rt2x00dev, + enum ring_index ring_type) +{ + struct data_ring *ring = &rt2x00dev->ring[ring_type]; + + 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 rt2500pci_allocate_dma_rings(struct rt2x00_dev *rt2x00dev) +{ + if (rt2500pci_alloc_dma_ring(rt2x00dev, RING_RX, + rt2500pci_rxdone, RX_ENTRIES, DATA_FRAME_SIZE, + RXD_DESC_SIZE) || + rt2500pci_alloc_dma_ring(rt2x00dev, RING_TX, + rt2500pci_txdone, TX_ENTRIES, DATA_FRAME_SIZE, + TXD_DESC_SIZE) || + rt2500pci_alloc_dma_ring(rt2x00dev, RING_ATIM, + rt2500pci_txdone, ATIM_ENTRIES, DATA_FRAME_SIZE, + TXD_DESC_SIZE) || + rt2500pci_alloc_dma_ring(rt2x00dev, RING_PRIO, + rt2500pci_txdone, TX_ENTRIES, DATA_FRAME_SIZE, + TXD_DESC_SIZE) || + rt2500pci_alloc_dma_ring(rt2x00dev, RING_BEACON, + NULL, BEACON_ENTRIES, MGMT_FRAME_SIZE, TXD_DESC_SIZE)) { + return -ENOMEM; + } + + return 0; +} + +static void rt2500pci_free_rings(struct rt2x00_dev *rt2x00dev) +{ + rt2500pci_free_ring(rt2x00dev, RING_RX); + rt2500pci_free_ring(rt2x00dev, RING_TX); + rt2500pci_free_ring(rt2x00dev, RING_ATIM); + rt2500pci_free_ring(rt2x00dev, RING_PRIO); + rt2500pci_free_ring(rt2x00dev, RING_BEACON); +} + +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; +} + +/* + * Device initialization functions. + */ +static int rt2500pci_initialize(struct rt2x00_dev *rt2x00dev) +{ + if (GET_FLAG(rt2x00dev, DEVICE_INITIALIZED)) + return 0; + + /* + * Allocate all data rings. + */ + if (rt2500pci_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; + + /* + * Register interrupt handler. + */ + if (request_irq(rt2x00dev_pci(rt2x00dev)->irq, rt2500pci_interrupt, + IRQF_SHARED, DRV_NAME, rt2x00dev)) { + ERROR("IRQ %d allocation failed.\n", + rt2x00dev_pci(rt2x00dev)->irq); + goto exit_fail; + } + + SET_FLAG(rt2x00dev, DEVICE_INITIALIZED); + + return 0; + +exit_fail: + rt2500pci_free_rings(rt2x00dev); + + return -EIO; +} + +static void rt2500pci_uninitialize(struct rt2x00_dev *rt2x00dev) +{ + if (!GET_FLAG(rt2x00dev, DEVICE_INITIALIZED)) + return; + + /* + * Cancel scanning. + */ + if (rt2x00dev->scan) + rt2x00_signal_scan(rt2x00dev->scan, SCANNING_CANCELLED); + + /* + * Flush out all pending work. + */ + flush_workqueue(rt2x00dev->workqueue); + + /* + * Free DMA rings. + */ + rt2500pci_free_rings(rt2x00dev); + + /* + * Free irq line. + */ + free_irq(rt2x00dev_pci(rt2x00dev)->irq, rt2x00dev); + + CLEAR_FLAG(rt2x00dev, DEVICE_INITIALIZED); +} + +/* + * 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; + + /* + * Don't enable the radio twice, + * or if the hardware button has been disabled. + */ + if (GET_FLAG(rt2x00dev, DEVICE_ENABLED_RADIO) || + !GET_FLAG(rt2x00dev, DEVICE_ENABLED_RADIO_HW)) + return 0; + + /* + * Initialize all registers. + */ + if (rt2500pci_init_rings(rt2x00dev) || + rt2500pci_init_registers(rt2x00dev) || + rt2500pci_init_bbp(rt2x00dev)) { + ERROR("Register initialization failed.\n"); + goto exit_fail; + } + + /* + * Determine channel change time. + */ + if (rt2x00lib_detect_channel_time(rt2x00dev)) + goto exit_fail; + + /* + * Clear interrupts. + */ + rt2x00_register_read(rt2x00dev, CSR7, ®); + rt2x00_register_write(rt2x00dev, CSR7, reg); + + SET_FLAG(rt2x00dev, DEVICE_ENABLED_RADIO); + + /* + * 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 RX. + */ + rt2500pci_toggle_rx(rt2x00dev, 1); + + /* + * Enable LED + */ + rt2500pci_enable_led(rt2x00dev); + + ieee80211_start_queues(rt2x00dev->hw); + + return 0; + +exit_fail: + rt2500pci_uninitialize(rt2x00dev); + return -EIO; +} + +static void rt2500pci_disable_radio(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + if (!GET_FLAG(rt2x00dev, DEVICE_ENABLED_RADIO)) + return; + + ieee80211_stop_queues(rt2x00dev->hw); + + /* + * Disable LED + */ + rt2500pci_disable_led(rt2x00dev); + + CLEAR_FLAG(rt2x00dev, DEVICE_ENABLED_RADIO); + + 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); + + rt2500pci_toggle_rx(rt2x00dev, 0); + + /* + * 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_desc *txd, struct ieee80211_hdr *ieee80211hdr, + unsigned int length, struct ieee80211_tx_control *control) +{ + struct data_ring *ring; + int tx_rate; + u32 word; + u32 duration; + u32 residual; + u16 length_high; + u16 length_low; + u16 frame_control; + u16 seq_ctrl; + char rts_frame; + char ofdm_rate; + char req_timestamp; + char more_frag; + char ifs; + u8 signal; + u8 service; + u8 bitrate; + + /* + * We require the ring structure this packet is being send to. + */ + ring = rt2x00_get_ring(rt2x00dev, control->queue); + if (unlikely(!ring)) + return; + + /* + * Read required fields from ieee80211 header. + */ + frame_control = le16_to_cpu(ieee80211hdr->frame_control); + seq_ctrl = le16_to_cpu(ieee80211hdr->seq_ctrl); + + /* + * Check if this frame is a RTS frame. + */ + rts_frame = is_rts_frame(frame_control); + + /* + * Check which rate should be used for this frame. + */ + if (rts_frame && control->rts_cts_rate) + tx_rate = control->rts_cts_rate; + else + tx_rate = control->tx_rate; + + /* + * Are we working with OFDM rates. + */ + ofdm_rate = !!(DEVICE_GET_RATE_FIELD(tx_rate, RATEMASK) & + DEV_OFDM_RATE); + + /* + * Check if more fragments will follow this frame. + */ + more_frag = !!(ieee80211_get_morefrag(ieee80211hdr)); + + /* + * Beacons and probe responses require the tsf timestamp + * to be inserted into the frame. + */ + req_timestamp = !!(control->queue == IEEE80211_TX_QUEUE_BEACON || + is_probe_resp(frame_control)); + + /* + * 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) || rts_frame) + ifs = IFS_SIFS; + else + ifs = IFS_BACKOFF; + + /* + * How the length should be processed depends + * on if we are working with OFDM rates or not. + */ + if (ofdm_rate) { + residual = 0; + length_high = ((length + FCS_LEN) >> 6) & 0x3f; + 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++; + + length_high = duration >> 8; + length_low = duration & 0xff; + } + + /* + * Create the signal and service values. + */ + signal = DEVICE_GET_RATE_FIELD(tx_rate, PLCP); + if (DEVICE_GET_RATE_FIELD(tx_rate, PREAMBLE)) + signal |= 0x08; + + service = 0x04; + if (residual <= (8 % 11)) + service |= 0x80; + + /* + * 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, ring->tx_params.aifs); + rt2x00_set_field32(&word, TXD_W2_CWMIN, ring->tx_params.cw_min); + rt2x00_set_field32(&word, TXD_W2_CWMAX, 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, signal); + rt2x00_set_field32(&word, TXD_W3_PLCP_SERVICE, service); + rt2x00_set_field32(&word, TXD_W3_PLCP_LENGTH_LOW, length_low); + rt2x00_set_field32(&word, TXD_W3_PLCP_LENGTH_HIGH, length_high); + rt2x00_desc_write(txd, 3, word); + + rt2x00_desc_read(txd, 10, &word); + rt2x00_set_field32(&word, TXD_W10_RTS, 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, more_frag); + rt2x00_set_field32(&word, TXD_W0_ACK, + !(control->flags & IEEE80211_TXCTL_NO_ACK)); + rt2x00_set_field32(&word, TXD_W0_TIMESTAMP, req_timestamp); + rt2x00_set_field32(&word, TXD_W0_OFDM, ofdm_rate); + rt2x00_set_field32(&word, TXD_W0_CIPHER_OWNER, 1); + rt2x00_set_field32(&word, TXD_W0_IFS, 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 int rt2500pci_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; + u16 fc; + + 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_W0_OWNER_NIC) || + rt2x00_get_field32(word, TXD_W0_VALID)) { + 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); + rt2500pci_write_tx_desc(rt2x00dev, txd, ieee80211hdr, + skb->len, control); + memcpy(&entry->tx_status.control, control, sizeof(*control)); + entry->skb = skb; + + fc = le16_to_cpu(ieee80211hdr->frame_control); + if (is_cts_frame(fc) || is_rts_frame(fc)) + SET_FLAG(entry, ENTRY_RTS_CTS_FRAME); + + rt2x00_ring_index_inc(ring); + + if (rt2x00_ring_full(ring)) + ieee80211_stop_queue(rt2x00dev->hw, control->queue); + + return 0; +} + +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); +} + +/* + * Interrupt functions. + */ +static void rt2500pci_beacondone(struct rt2x00_dev *rt2x00dev) +{ + struct data_ring *ring = &rt2x00dev->ring[RING_BEACON]; + 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->hw_ops->beacon_update(rt2x00dev->hw, skb, + &entry->tx_status.control); + + dev_kfree_skb_any(skb); +} + +static void rt2500pci_rxdone(struct work_struct *work) +{ + struct data_ring *ring = + container_of(work, struct data_ring, irq_work); + struct rt2x00_dev *rt2x00dev = ring->rt2x00dev; + struct data_entry *entry; + struct sk_buff *skb; + 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; + + size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT); + + /* + * 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; + + skb = dev_alloc_skb(size + NET_IP_ALIGN); + if (!skb) + break; + + skb_reserve(skb, NET_IP_ALIGN); + + memcpy(skb_put(skb, size), entry->data_addr, size); + + signal = rt2x00_get_field32(word2, RXD_W2_SIGNAL); + rssi = rt2x00_get_field32(word2, RXD_W2_RSSI); + ofdm = rt2x00_get_field32(word0, RXD_W0_OFDM); + rt2x00lib_update_rx_stats(rt2x00dev, signal, rssi, ofdm); + + __ieee80211_rx(rt2x00dev->hw, skb, &rt2x00dev->rx_status); + + /* + * Update link statistics + */ + rt2x00_update_link_rssi(&rt2x00dev->link, + rt2x00dev->rx_status.ssi); + +skip_entry: + 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 work_struct *work) +{ + struct data_ring *ring = + container_of(work, struct data_ring, irq_work); + struct rt2x00_dev *rt2x00dev = ring->rt2x00dev; + struct data_entry *entry; + struct data_desc *txd; + u32 word; + int tx_status; + int retry; + int ack; + + 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; + + ack = rt2x00_get_field32(word, TXD_W0_ACK); + tx_status = rt2x00_get_field32(word, TXD_W0_RESULT); + retry = rt2x00_get_field32(word, TXD_W0_RETRY_COUNT); + rt2x00lib_update_tx_stats(entry, tx_status, ack, retry); + + /* + * If this is not an RTS frame send the tx_status to mac80211, + * that method also cleans up the skb structure. When this + * is a RTS frame, that it is our job to clean this structure up. + */ + if (!GET_FLAG(entry, ENTRY_RTS_CTS_FRAME)) + ieee80211_tx_status(rt2x00dev->hw, + entry->skb, &entry->tx_status); + else + dev_kfree_skb(entry->skb); + + rt2x00_set_field32(&word, TXD_W0_VALID, 0); + rt2x00_desc_write(txd, 0, word); + entry->skb = NULL; + + CLEAR_FLAG(entry, ENTRY_RTS_CTS_FRAME); + + rt2x00_ring_index_done_inc(ring); + } + + /* + * Check if we are waiting on an empty queue + * to start scanning. + */ + if (rt2x00dev->scan && + rt2x00_ring_empty(&rt2x00dev->ring[RING_TX]) && + rt2x00_ring_empty(&rt2x00dev->ring[RING_ATIM]) && + rt2x00_ring_empty(&rt2x00dev->ring[RING_PRIO])) + rt2x00_signal_scan(rt2x00dev->scan, SCANNING_READY); + + /* + * 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)) + rt2500pci_beacondone(rt2x00dev); + + /* + * 2 - Rx ring done interrupt. + * Enable the TXRX activity led. + */ + if (rt2x00_get_field32(reg, CSR7_RXDONE)) { + queue_work(rt2x00dev->workqueue, + &rt2x00dev->ring[RING_RX].irq_work); + rt2500pci_activity_led(rt2x00dev, 1); + } + + /* + * 3 - Atim ring transmit done interrupt. + */ + if (rt2x00_get_field32(reg, CSR7_TXDONE_ATIMRING)) + queue_work(rt2x00dev->workqueue, + &rt2x00dev->ring[RING_ATIM].irq_work); + + /* + * 4 - Priority ring transmit done interrupt. + */ + if (rt2x00_get_field32(reg, CSR7_TXDONE_PRIORING)) + queue_work(rt2x00dev->workqueue, + &rt2x00dev->ring[RING_PRIO].irq_work); + + /* + * 5 - Tx ring transmit done interrupt. + */ + if (rt2x00_get_field32(reg, CSR7_TXDONE_TXRING)) + queue_work(rt2x00dev->workqueue, + &rt2x00dev->ring[RING_TX].irq_work); + + return IRQ_HANDLED; +} + +/* + * 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_beacon_update(struct ieee80211_hw *hw, + struct sk_buff *skb, struct ieee80211_tx_control *control) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct data_entry *entry; + u32 reg; + + entry = rt2x00_get_data_entry(&rt2x00dev->ring[RING_BEACON]); + + /* + * 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); + rt2500pci_write_tx_desc(rt2x00dev, entry->priv, + (struct ieee80211_hdr *)skb->data, skb->len, control); + + /* + * Enable beacon generation. + */ + 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); + } + + return 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, + .passive_scan = rt2x00lib_passive_scan, + .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 = rt2500pci_beacon_update, + .tx_last_beacon = rt2500pci_tx_last_beacon, +}; + +static const struct rt2x00lib_ops rt2500pci_rt2x00_ops = { + .initialize = rt2500pci_initialize, + .uninitialize = rt2500pci_uninitialize, + .enable_radio = rt2500pci_enable_radio, + .disable_radio = rt2500pci_disable_radio, + .toggle_rx = rt2500pci_toggle_rx, + .write_tx_data = rt2500pci_write_tx_data, + .kick_tx_queue = rt2500pci_kick_tx_queue, + .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, +}; + +/* + * Device initialization functions. + */ +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); +} + +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_alloc_rings(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + + rt2x00dev->ring = kzalloc( + sizeof(struct data_ring) * RING_NUM, GFP_KERNEL); + if (!rt2x00dev->ring) { + ERROR("Ring allocation failed.\n"); + return -ENOMEM; + } + + SET_FLAG(rt2x00dev, DEVICE_SUPPORT_ATIM); + + 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; +} + +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."); + 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 int rt2500pci_init_hw_mac(struct rt2x00_dev *rt2x00dev) +{ + u8 *addr; + + /* + * Get the pointer to the MAC address in the EEPROM. + */ + addr = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); + + /* + * Check if a valid MAC address is present. + */ + if (!is_valid_ether_addr(addr)) { + ERROR("Invalid MAC address: " MAC_FMT ".\n", MAC_ARG(addr)); + return -EINVAL; + } + + /* + * Write MAC address to register. + */ + rt2500pci_config_mac_addr(rt2x00dev, addr); + + /* + * Copy MAC address to the hw structure. + */ + SET_IEEE80211_PERM_ADDR(rt2x00dev->hw, addr); + + return 0; +} + +static void rt2500pci_init_hw_channels(struct rt2x00_dev *rt2x00dev, + struct ieee80211_channel *channels) +{ + unsigned int i; + u32 rf2_base; + u16 eeprom; + static const struct { + unsigned int chip; + u32 val[3]; + } rf[] = { + { RF2522, { 0x00002050, 0x00000101, 0x00000000 } }, + { RF2523, { 0x00022010, 0x000e0111, 0x00000a1b } }, + { RF2524, { 0x00032020, 0x00000101, 0x00000a1b } }, + { RF2525, { 0x00022020, 0x00060111, 0x00000a1b } }, + { RF2525E, { 0x00022020, 0x00060111, 0x00000a0b } }, + { RF5222, { 0x00000000, 0x00000101, 0x00000000 } } + }; + + /* + * Channel initialization. + * First we set the basic variables. + */ + for (i = 0; i < 13; i++) { + channels[i].chan = i + 1; + channels[i].freq = 2407 + ((i + 1) * 5); + channels[i].flag = IEEE80211_CHAN_W_IBSS | + IEEE80211_CHAN_W_ACTIVE_SCAN | IEEE80211_CHAN_W_SCAN; + channels[i].antenna_max = 0xff; + } + + channels[13].chan = 14; + channels[13].freq = 2484; + channels[13].flag = IEEE80211_CHAN_W_IBSS | + IEEE80211_CHAN_W_ACTIVE_SCAN | IEEE80211_CHAN_W_SCAN; + channels[13].antenna_max = 0xff; + + if (rt2x00_rf(&rt2x00dev->chip, RF5222)) { + for (i = 14; i < 37; 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); + channels[i].freq = ((i - 13) + 1000) * 5; + channels[i].flag = IEEE80211_CHAN_W_IBSS | + IEEE80211_CHAN_W_ACTIVE_SCAN | + IEEE80211_CHAN_W_SCAN; + channels[i].power_level = DEFAULT_TXPOWER; + channels[i].antenna_max = 0xff; + } + } + + /* + * Set device specific value. + */ + rf2_base = 0; + if (rt2x00_rf(&rt2x00dev->chip, RF2525) || + rt2x00_rf(&rt2x00dev->chip, RF2525E)) + rf2_base = 0x00080000; + + if (rt2x00_rf(&rt2x00dev->chip, RF2522)) { + static const u32 vals[] = { + 0x000c1fda, 0x000c1fee, 0x000c2002, 0x000c2016, + 0x000c202a, 0x000c203e, 0x000c2052, 0x000c2066, + 0x000c207a, 0x000c208e, 0x000c20a2, 0x000c20b6, + 0x000c20ca, 0x000c20fa + }; + + for (i = 0; i < ARRAY_SIZE(vals); i++) + channels[i].val = vals[i]; + } else if (rt2x00_rf(&rt2x00dev->chip, RF2523) || + rt2x00_rf(&rt2x00dev->chip, RF2524) || + rt2x00_rf(&rt2x00dev->chip, RF2525)) { + static const u32 vals[] = { + 0x00000c9e, 0x00000ca2, 0x00000ca6, 0x00000caa, + 0x00000cae, 0x00000cb2, 0x00000cb6, 0x00000cba, + 0x00000cbe, 0x00000d02, 0x00000d06, 0x00000d0a, + 0x00000d0e, 0x00000d1a + }; + + for (i = 0; i < ARRAY_SIZE(vals); i++) + channels[i].val = vals[i] | rf2_base; + } else if (rt2x00_rf(&rt2x00dev->chip, RF2525E) || + rt2x00_rf(&rt2x00dev->chip, RF5222)) { + static const u32 vals[] = { + 0x00001136, 0x0000113a, 0x0000113e, 0x00001182, + 0x00001186, 0x0000118a, 0x0000118e, 0x00001192, + 0x00001196, 0x0000119a, 0x0000119e, 0x000011a2, + 0x000011a6, 0x000011ae + }; + + for (i = 0; i < ARRAY_SIZE(vals); i++) + channels[i].val = vals[i] | rf2_base; + } + if (rt2x00_rf(&rt2x00dev->chip, RF5222)) { + static const u32 vals[] = { + 0x00018896, 0x0001889a, 0x0001889e, 0x000188a2, + 0x000188a6, 0x000188aa, 0x000188ae, 0x000188b2, + 0x00008802, 0x00008806, 0x0000880a, 0x0000880e, + 0x00008812, 0x00008816, 0x0000881a, 0x0000881e, + 0x00008822, 0x00008826, 0x0000882a, 0x000090a6, + 0x000090ae, 0x000090b6, 0x000090be + }; + + struct ieee80211_channel *chan = channels + 14; + + for (i = 0; i < ARRAY_SIZE(vals); i++) + (chan++)->val = vals[i]; + } + + /* + * Set TX power, each EEPROM TXpower entry + * contains the TXpower value for 2 channels. + */ + for (i = 0; i < EEPROM_TXPOWER_SIZE; i++) { + rt2x00_eeprom_read(rt2x00dev, + EEPROM_TXPOWER_START + i, &eeprom); + + channels[(i * 2)].power_level = TXPOWER_FROM_DEV( + rt2x00_get_field16(eeprom, EEPROM_TXPOWER_1)); + + channels[(i * 2) + 1].power_level = TXPOWER_FROM_DEV( + rt2x00_get_field16(eeprom, EEPROM_TXPOWER_2)); + } + + /* + * Set device specific, but channel independent RF values. + */ + for (i = 0; i < ARRAY_SIZE(rf); i++) { + if (rt2x00_rf(&rt2x00dev->chip, rf[i].chip)) { + rt2x00dev->rf1 = rf[i].val[0]; + rt2x00dev->rf3 = rf[i].val[1]; + rt2x00dev->rf4 = rf[i].val[2]; + } + } +} + +static void rt2500pci_init_hw_rates(struct rt2x00_dev *rt2x00dev, + struct ieee80211_rate *rates) +{ + /* + * Rates initialization. + */ + device_rate_entry(&rates[0], 10, 0x001, 0x00, IEEE80211_RATE_CCK); + device_rate_entry(&rates[1], 20, 0x003, 0x01, IEEE80211_RATE_CCK_2); + device_rate_entry(&rates[2], 55, 0x007, 0x02, IEEE80211_RATE_CCK_2); + device_rate_entry(&rates[3], 110, 0x00f, 0x03, IEEE80211_RATE_CCK_2); + device_rate_entry(&rates[4], 60, 0x01f, 0x0b, IEEE80211_RATE_OFDM); + device_rate_entry(&rates[5], 90, 0x03f, 0x0f, IEEE80211_RATE_OFDM); + device_rate_entry(&rates[6], 120, 0x07f, 0x0a, IEEE80211_RATE_OFDM); + device_rate_entry(&rates[7], 180, 0x0ff, 0x0e, IEEE80211_RATE_OFDM); + device_rate_entry(&rates[8], 240, 0x1ff, 0x09, IEEE80211_RATE_OFDM); + device_rate_entry(&rates[9], 360, 0x3ff, 0x0d, IEEE80211_RATE_OFDM); + device_rate_entry(&rates[10], 480, 0x7ff, 0x08, IEEE80211_RATE_OFDM); + device_rate_entry(&rates[11], 540, 0xfff, 0x0c, IEEE80211_RATE_OFDM); +} + +static int rt2500pci_init_hw_modes(struct rt2x00_dev *rt2x00dev) +{ + struct ieee80211_channel *channels; + struct ieee80211_rate *rates; + int status = -ENOMEM; + int num_modes; + int num_channels; + + /* + * RT2500 only supports 802.11b & 802.11g, + * so we should allocate 14 OFDM channels, 4 CCK rates + * and 8 OFDM rates. + * RF5222 also supports 802.11a, so allocate an + * additional 23 5.2GHz channels. + */ + num_modes = 2; + num_channels = 14; + if (rt2x00_rf(&rt2x00dev->chip, RF5222)) { + num_modes = 3; + num_channels = 37; + } + + rt2x00dev->hwmodes = + kzalloc(sizeof(struct ieee80211_hw_mode) * num_modes, + GFP_KERNEL); + if (!rt2x00dev->hwmodes) + goto exit; + + channels = kzalloc(sizeof(struct ieee80211_channel) * num_channels, + GFP_KERNEL); + if (!channels) + goto exit_free_modes; + + rates = kzalloc(sizeof(struct ieee80211_rate) * 12, GFP_KERNEL); + if (!rates) + goto exit_free_channels; + + /* + * Initialize channels and rate arrays. + */ + rt2500pci_init_hw_channels(rt2x00dev, channels); + rt2500pci_init_hw_rates(rt2x00dev, rates); + + /* + * Intitialize 802.11b + * Rates: CCK. + * Channels: OFDM. + */ + rt2x00dev->hwmodes[HWMODE_B].mode = MODE_IEEE80211B; + rt2x00dev->hwmodes[HWMODE_B].num_channels = 14; + rt2x00dev->hwmodes[HWMODE_B].num_rates = 4; + rt2x00dev->hwmodes[HWMODE_B].channels = channels; + rt2x00dev->hwmodes[HWMODE_B].rates = rates; + + /* + * Intitialize 802.11g + * Rates: CCK, OFDM. + * Channels: OFDM. + */ + rt2x00dev->hwmodes[HWMODE_G].mode = MODE_IEEE80211G; + rt2x00dev->hwmodes[HWMODE_G].num_channels = 14; + rt2x00dev->hwmodes[HWMODE_G].num_rates = 12; + rt2x00dev->hwmodes[HWMODE_G].channels = channels; + rt2x00dev->hwmodes[HWMODE_G].rates = rates; + + /* + * Intitialize 802.11a + * Rates: OFDM. + * Channels: OFDM, UNII, HiperLAN2. + */ + if (num_modes == 3) { + rt2x00dev->hwmodes[HWMODE_A].mode = MODE_IEEE80211A; + rt2x00dev->hwmodes[HWMODE_A].num_channels = 23; + rt2x00dev->hwmodes[HWMODE_A].num_rates = 8; + rt2x00dev->hwmodes[HWMODE_A].channels = &channels[14]; + rt2x00dev->hwmodes[HWMODE_A].rates = &rates[4]; + } + + /* + * Register the working modes. + */ + status = ieee80211_register_hwmode(rt2x00dev->hw, + &rt2x00dev->hwmodes[HWMODE_G]); + if (status) + goto exit_free_rates; + + status = ieee80211_register_hwmode(rt2x00dev->hw, + &rt2x00dev->hwmodes[HWMODE_B]); + if (status) + goto exit_free_rates; + + if (num_modes == 3) { + status = ieee80211_register_hwmode(rt2x00dev->hw, + &rt2x00dev->hwmodes[HWMODE_A]); + if (status) + goto exit_free_rates; + } + + return 0; + +exit_free_rates: + kfree(rates); + +exit_free_channels: + kfree(channels); + +exit_free_modes: + kfree(rt2x00dev->hwmodes); + rt2x00dev->hwmodes = NULL; + +exit: + ERROR("Allocation ieee80211 modes failed.\n"); + return status; +} + +static int rt2500pci_init_hw(struct rt2x00_dev *rt2x00dev) +{ + int status; + + if (GET_FLAG(rt2x00dev, DEVICE_INITIALIZED_HW)) + return 0; + + SET_IEEE80211_DEV(rt2x00dev->hw, &rt2x00dev_pci(rt2x00dev)->dev); + + /* + * Read MAC address from EEPROM. + */ + status = rt2500pci_init_hw_mac(rt2x00dev); + if (status) + return status; + + /* + * 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; + + if (ieee80211_register_hw(rt2x00dev->hw)) + return -EIO; + + status = rt2500pci_init_hw_modes(rt2x00dev); + if (status) { + ieee80211_unregister_hw(rt2x00dev->hw); + return status; + } + + SET_FLAG(rt2x00dev, DEVICE_INITIALIZED_HW); + + return 0; +} + +static void rt2500pci_free_dev(struct rt2x00_dev *rt2x00dev) +{ + /* + * Close debugfs entry. + */ + rt2500pci_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); + + /* + * Release CSR memory. + */ + if (likely(rt2x00dev->csr_addr)) { + iounmap(rt2x00dev->csr_addr); + rt2x00dev->csr_addr = NULL; + } + + /* + * Free ieee80211_hw memory. + */ + if (likely(rt2x00dev->hwmodes)) { + kfree(rt2x00dev->hwmodes->channels); + kfree(rt2x00dev->hwmodes->rates); + kfree(rt2x00dev->hwmodes); + rt2x00dev->hwmodes = NULL; + } +} + +static int rt2500pci_alloc_dev(struct pci_dev *pci_dev, + struct ieee80211_hw *hw) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + + rt2x00dev->dev = pci_dev; + rt2x00dev->lib_ops = &rt2500pci_rt2x00_ops; + rt2x00dev->hw_ops = &rt2500pci_mac80211_ops; + rt2x00dev->hw = hw; + + /* + * 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; + } + + /* + * Allocate eeprom data. + */ + if (rt2500pci_alloc_eeprom(rt2x00dev)) + goto exit; + + /* + * Create workqueue. + */ + rt2x00dev->workqueue = create_singlethread_workqueue(DRV_NAME); + if (!rt2x00dev->workqueue) + goto exit; + + /* + * Initialize configuration work. + */ + INIT_DELAYED_WORK(&rt2x00dev->link.work, rt2500pci_link_tuner); + + /* + * Reset current working type. + */ + rt2x00dev->interface.type = -EINVAL; + + /* + * Intialize scanning attributes. + */ + rt2x00dev->scan = NULL; + + /* + * Allocate ring array. + */ + if (rt2500pci_alloc_rings(rt2x00dev)) + goto exit; + + /* + * Initialize hardware. + */ + if (rt2500pci_init_eeprom(rt2x00dev) || + rt2500pci_init_hw(rt2x00dev)) { + ERROR("Failed to initialize device.\n"); + goto exit; + } + + /* + * Open the debugfs entry. + */ + rt2500pci_open_debugfs(rt2x00dev); + + return 0; + +exit: + rt2500pci_free_dev(rt2x00dev); + + return -ENODEV; +} + +/* + * PCI driver handlers. + */ +static int rt2500pci_probe(struct pci_dev *pci_dev, + const struct pci_device_id *id) +{ + struct ieee80211_hw *hw; + int status; + + status = pci_request_regions(pci_dev, pci_name(pci_dev)); + if (status) { + ERROR("PCI request regions failed.\n"); + return status; + } + + status = pci_enable_device(pci_dev); + if (status) { + 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"); + status = -EIO; + goto exit_disable_device; + } + + hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), + &rt2500pci_mac80211_ops); + if (!hw) { + ERROR("Failed to allocate hardware.\n"); + status = -ENOMEM; + goto exit_disable_device; + } + + pci_set_drvdata(pci_dev, hw); + + status = rt2500pci_alloc_dev(pci_dev, hw); + if (status) { + ERROR("Failed to allocate device.\n"); + goto exit_free_device; + } + + return 0; + +exit_free_device: + ieee80211_free_hw(hw); + +exit_disable_device: + if (status != -EBUSY) + pci_disable_device(pci_dev); + +exit_release_regions: + pci_release_regions(pci_dev); + + pci_set_drvdata(pci_dev, NULL); + + return status; +} + +static void rt2500pci_remove(struct pci_dev *pci_dev) +{ + struct ieee80211_hw *hw = pci_get_drvdata(pci_dev); + struct rt2x00_dev *rt2x00dev = hw->priv; + + /* + * Uninitialize the 80211 stack data. + */ + ieee80211_unregister_hw(hw); + + /* + * Uninitialize and free the rt2500pci driver data. + */ + rt2500pci_disable_radio(rt2x00dev); + rt2500pci_uninitialize(rt2x00dev); + rt2500pci_free_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); +} + +#ifdef CONFIG_PM +static int rt2500pci_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 status; + + NOTICE("Going to sleep.\n"); + + /* + * Disable the radio. + */ + rt2500pci_disable_radio(rt2x00dev); + + /* + * Set device mode to sleep for power management. + */ + status = rt2500pci_set_state(rt2x00dev, STATE_SLEEP); + if (status) + return status; + + /* + * Uninitialize device and hardware. + */ + rt2500pci_uninitialize(rt2x00dev); + rt2500pci_free_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)); +} + +static int rt2500pci_resume(struct pci_dev *pci_dev) +{ + struct ieee80211_hw *hw = pci_get_drvdata(pci_dev); + struct rt2x00_dev *rt2x00dev = hw->priv; + int status; + + 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. + */ + status = rt2500pci_alloc_dev(pci_dev, hw); + if (status) { + ERROR("Failed to allocate device.\n"); + return status; + } + + /* + * Set device mode to awake for power management. + */ + status = rt2500pci_set_state(rt2x00dev, STATE_AWAKE); + if (status) + return status; + + return 0; +} +#endif /* CONFIG_PM */ + +/* + * 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) }, + { 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 = rt2500pci_probe, + .remove = __devexit_p(rt2500pci_remove), +#ifdef CONFIG_PM + .suspend = rt2500pci_suspend, + .resume = rt2500pci_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 -puN /dev/null drivers/net/wireless/mac80211/rt2x00/rt2500pci.h --- /dev/null +++ a/drivers/net/wireless/mac80211/rt2x00/rt2500pci.h @@ -0,0 +1,1198 @@ +/* + 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); + +/* + * Radio control functions. + */ +static int rt2500pci_enable_radio(struct rt2x00_dev *rt2x00dev); +static void rt2500pci_disable_radio(struct rt2x00_dev *rt2x00dev); + +/* + * Interrupt functions. + */ +static void rt2500pci_rxdone(struct work_struct *work); +static void rt2500pci_txdone(struct work_struct *work); +static irqreturn_t rt2500pci_interrupt(int irq, void *dev_instance); + +#endif /* RT2500PCI_H */ diff -puN /dev/null drivers/net/wireless/mac80211/rt2x00/rt2500usb.c --- /dev/null +++ a/drivers/net/wireless/mac80211/rt2x00/rt2500usb.c @@ -0,0 +1,2718 @@ +/* + 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 "rt2x00lib.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, ®, 2, 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, ®, 2, 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); +} + +#if defined(CONFIG_RT2X00_DEBUGFS) || defined(CONFIG_RT2X00_DEBUGFS_MODULE) +#include "rt2x00debug.h" + +#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 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), + .length = CSR_REG_SIZE, + }, + .reg_eeprom = { + .read = rt2500usb_read_eeprom, + .write = rt2500usb_write_eeprom, + .word_size = sizeof(u16), + .length = EEPROM_SIZE, + }, + .reg_bbp = { + .read = rt2500usb_read_bbp, + .write = rt2500usb_write_bbp, + .word_size = sizeof(u8), + .length = BBP_SIZE, + }, +}; + +static void rt2500usb_open_debugfs(struct rt2x00_dev *rt2x00dev) +{ + if (!rt2x00debug_register(&rt2500usb_rt2x00debug, + rt2x00dev->hw->wiphy, rt2x00dev)) + ERROR("Failed to register debug handler.\n"); +} + +static void rt2500usb_close_debugfs(struct rt2x00_dev *rt2x00dev) +{ + rt2x00debug_deregister(&rt2x00dev->debugfs_data); +} +#else /* CONFIG_RT2X00_DEBUGFS */ +static inline void rt2500usb_open_debugfs(struct rt2x00_dev *rt2x00dev){} +static inline void rt2500usb_close_debugfs(struct rt2x00_dev *rt2x00dev){} +#endif /* CONFIG_RT2X00_DEBUGFS */ + +/* + * Configuration handlers. + */ +static void rt2500usb_config_bssid(struct rt2x00_dev *rt2x00dev, u8 *bssid) +{ + /* + * 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, bssid, ETH_ALEN); +} + +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 rf2, int channel, int freq, int txpower) +{ + u32 rf1 = rt2x00dev->rf1; + 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, 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) +{ + /* + * 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, addr, ETH_ALEN); +} + +/* + * 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 int rt2500usb_alloc_dma_ring(struct rt2x00_dev *rt2x00dev, + enum ring_index ring_type, const u16 max_entries, + const u16 data_size, const u16 desc_size) +{ + struct data_ring *ring = &rt2x00dev->ring[ring_type]; + 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. + */ + 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 (ring_type == RING_RX) { + ring->entry[i].skb = dev_alloc_skb(NET_IP_ALIGN + + ring->data_size + ring->desc_size); + if (!ring->entry[i].skb) + return -ENOMEM; + + skb_reserve(ring->entry[i].skb, NET_IP_ALIGN); + skb_put(ring->entry[i].skb, + ring->data_size + ring->desc_size); + } + } + + return 0; +} + +static void rt2500usb_free_ring(struct rt2x00_dev *rt2x00dev, + enum ring_index ring_type) +{ + struct data_ring *ring = &rt2x00dev->ring[ring_type]; + 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_type == RING_RX) + kfree_skb(ring->entry[i].skb); + } + + kfree(ring->entry); + ring->entry = NULL; +} + +static int rt2500usb_allocate_dma_rings(struct rt2x00_dev *rt2x00dev) +{ + if (rt2500usb_alloc_dma_ring(rt2x00dev, RING_RX, + RX_ENTRIES, DATA_FRAME_SIZE, RXD_DESC_SIZE) || + rt2500usb_alloc_dma_ring(rt2x00dev, RING_TX, + TX_ENTRIES, DATA_FRAME_SIZE, TXD_DESC_SIZE) || + rt2500usb_alloc_dma_ring(rt2x00dev, RING_ATIM, + ATIM_ENTRIES, DATA_FRAME_SIZE, TXD_DESC_SIZE) || + rt2500usb_alloc_dma_ring(rt2x00dev, RING_PRIO, + TX_ENTRIES, DATA_FRAME_SIZE, TXD_DESC_SIZE) || + rt2500usb_alloc_dma_ring(rt2x00dev, RING_BEACON, + BEACON_ENTRIES, MGMT_FRAME_SIZE, TXD_DESC_SIZE)) { + return -ENOMEM; + } + + return 0; +} + +static void rt2500usb_free_rings(struct rt2x00_dev *rt2x00dev) +{ + rt2500usb_free_ring(rt2x00dev, RING_RX); + rt2500usb_free_ring(rt2x00dev, RING_TX); + rt2500usb_free_ring(rt2x00dev, RING_ATIM); + rt2500usb_free_ring(rt2x00dev, RING_PRIO); + rt2500usb_free_ring(rt2x00dev, RING_BEACON); +} + +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; +} + +/* + * Device initialization functions. + */ +static int rt2500usb_initialize(struct rt2x00_dev *rt2x00dev) +{ + if (GET_FLAG(rt2x00dev, DEVICE_INITIALIZED)) + return 0; + + /* + * Allocate all data rings. + */ + if (rt2500usb_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: + rt2500usb_free_rings(rt2x00dev); + + return -EIO; +} + +static void rt2500usb_uninitialize(struct rt2x00_dev *rt2x00dev) +{ + if (!GET_FLAG(rt2x00dev, DEVICE_INITIALIZED)) + return; + + /* + * Cancel scanning. + */ + if (rt2x00dev->scan) + rt2x00_signal_scan(rt2x00dev->scan, SCANNING_CANCELLED); + + /* + * Flush out all pending work. + */ + flush_workqueue(rt2x00dev->workqueue); + + /* + * Free DMA rings. + */ + rt2500usb_free_rings(rt2x00dev); + + CLEAR_FLAG(rt2x00dev, DEVICE_INITIALIZED); +} + +/* + * 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; + + /* + * Don't enable the radio twice. + */ + if (GET_FLAG(rt2x00dev, DEVICE_ENABLED_RADIO)) + return 0; + + /* + * Initialize all registers. + */ + if (rt2500usb_init_rings(rt2x00dev) || + rt2500usb_init_registers(rt2x00dev) || + rt2500usb_init_bbp(rt2x00dev)) { + ERROR("Register initialization failed.\n"); + goto exit_fail; + } + + /* + * Determine channel change time. + */ + if (rt2x00lib_detect_channel_time(rt2x00dev)) + goto exit_fail; + + SET_FLAG(rt2x00dev, DEVICE_ENABLED_RADIO); + + /* + * Enable RX. + */ + rt2500usb_toggle_rx(rt2x00dev, 1); + + 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); + + ieee80211_start_queues(rt2x00dev->hw); + + return 0; + +exit_fail: + rt2500usb_uninitialize(rt2x00dev); + return -EIO; +} + +static void rt2500usb_disable_radio(struct rt2x00_dev *rt2x00dev) +{ + struct data_ring *ring; + unsigned int i; + + if (!GET_FLAG(rt2x00dev, DEVICE_ENABLED_RADIO)) + return; + + ieee80211_stop_queues(rt2x00dev->hw); + + /* + * Disable LED + */ + rt2500usb_disable_led(rt2x00dev); + + CLEAR_FLAG(rt2x00dev, DEVICE_ENABLED_RADIO); + + 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. + */ + rt2500usb_toggle_rx(rt2x00dev, 0); + + 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_desc *txd, struct ieee80211_hdr *ieee80211hdr, + unsigned int length, struct ieee80211_tx_control *control) +{ + struct data_ring *ring; + int tx_rate; + u32 word; + u32 duration; + u32 residual; + u16 length_high; + u16 length_low; + u16 frame_control; + u16 seq_ctrl; + char rts_frame; + char ofdm_rate; + char req_timestamp; + char more_frag; + char new_seq; + char ifs; + u8 signal; + u8 service; + u8 bitrate; + + /* + * We require the ring structure this packet is being send to. + */ + ring = rt2x00_get_ring(rt2x00dev, control->queue); + if (unlikely(!ring)) + return; + + /* + * Read required fields from ieee80211 header. + */ + frame_control = le16_to_cpu(ieee80211hdr->frame_control); + seq_ctrl = le16_to_cpu(ieee80211hdr->seq_ctrl); + + /* + * Check if this frame is a RTS frame. + */ + rts_frame = is_rts_frame(frame_control); + + /* + * Check which rate should be used for this frame. + */ + if (rts_frame && control->rts_cts_rate) + tx_rate = control->rts_cts_rate; + else + tx_rate = control->tx_rate; + + /* + * Are we working with OFDM rates. + */ + ofdm_rate = !!(DEVICE_GET_RATE_FIELD(tx_rate, RATEMASK) & + DEV_OFDM_RATE); + + /* + * Check if more fragments will follow this frame. + */ + more_frag = !!(ieee80211_get_morefrag(ieee80211hdr)); + + /* + * Check if this is the first frame in a new sequence. + */ + new_seq = !!((seq_ctrl & IEEE80211_SCTL_FRAG) == 0); + + /* + * Beacons and probe responses require the tsf timestamp + * to be inserted into the frame. + */ + req_timestamp = !!(control->queue == IEEE80211_TX_QUEUE_BEACON || + is_probe_resp(frame_control)); + + /* + * 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) || rts_frame) + ifs = IFS_SIFS; + else + ifs = IFS_BACKOFF; + + /* + * How the length should be processed depends + * on if we are working with OFDM rates or not. + */ + if (ofdm_rate) { + residual = 0; + length_high = ((length + FCS_LEN) >> 6) & 0x3f; + 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++; + + length_high = duration >> 8; + length_low = duration & 0xff; + } + + /* + * Create the signal and service values. + */ + signal = DEVICE_GET_RATE_FIELD(tx_rate, PLCP); + if (DEVICE_GET_RATE_FIELD(tx_rate, PREAMBLE)) + signal |= 0x08; + + service = 0x04; + if (residual <= (8 % 11)) + service |= 0x80; + + /* + * 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, ring->tx_params.aifs); + rt2x00_set_field32(&word, TXD_W1_CWMIN, ring->tx_params.cw_min); + rt2x00_set_field32(&word, TXD_W1_CWMAX, 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, signal); + rt2x00_set_field32(&word, TXD_W2_PLCP_SERVICE, service); + rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_LOW, length_low); + rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_HIGH, 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, more_frag); + rt2x00_set_field32(&word, TXD_W0_ACK, + !(control->flags & IEEE80211_TXCTL_NO_ACK)); + rt2x00_set_field32(&word, TXD_W0_TIMESTAMP, req_timestamp); + rt2x00_set_field32(&word, TXD_W0_OFDM, ofdm_rate); + rt2x00_set_field32(&word, TXD_W0_NEW_SEQ, new_seq); + rt2x00_set_field32(&word, TXD_W0_IFS, 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 int rt2500usb_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; + u16 fc; + + 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; + rt2500usb_write_tx_desc(rt2x00dev, txd, ieee80211hdr, + skb->len, control); + memcpy(&entry->tx_status.control, control, sizeof(*control)); + entry->skb = skb; + + fc = le16_to_cpu(ieee80211hdr->frame_control); + if (is_cts_frame(fc) || is_rts_frame(fc)) + SET_FLAG(entry, ENTRY_RTS_CTS_FRAME); + + /* + * 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, + rt2500usb_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; +} + +/* + * Interrupt functions. + */ +static void rt2500usb_interrupt_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); + } +} + +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); + struct sk_buff *skb; + 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; + + /* + * Allocate a new sk_buffer to replace the sk_buffer + * that has been filled in by the device. + */ + skb = dev_alloc_skb(NET_IP_ALIGN + ring->data_size + ring->desc_size); + if (!skb) + return; + + skb_reserve(skb, NET_IP_ALIGN); + skb_put(skb, ring->data_size + ring->desc_size); + + urb->transfer_buffer = skb->data; + urb->transfer_buffer_length = skb->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); + rt2x00lib_update_rx_stats(rt2x00dev, signal, rssi, ofdm); + + /* + * Received USB packets have 4 bytes of extra data, + * Trim the skb_buffer to only contain the valid + * frame data (so ignore the device's descriptor). + */ + size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT); + size -= FCS_LEN; + skb_trim(entry->skb, size); + + /* + * Send frame to stack, and set the new sk_buffer + * in its place to be able to receive new frames + * in the new buffer. + */ + ieee80211_rx_irqsafe(rt2x00dev->hw, entry->skb, &rt2x00dev->rx_status); + entry->skb = skb; + + /* + * Update link statistics + */ + rt2x00_update_link_rssi(&rt2x00dev->link, rt2x00dev->rx_status.ssi); + +skip_entry: + if (!GET_FLAG(ring->rt2x00dev, DEVICE_ENABLED_RADIO)) + return; + + SET_FLAG(entry, ENTRY_OWNER_NIC); + usb_submit_urb(urb, GFP_ATOMIC); + rt2x00_ring_index_inc(ring); +} + +static void rt2500usb_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; + int ack; + + 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); + + ack = rt2x00_get_field32(word, TXD_W0_ACK); + tx_status = !urb->status ? TX_SUCCESS : TX_FAIL_RETRY; + rt2x00lib_update_tx_stats(entry, tx_status, ack, 0); + + skb_pull(entry->skb, ring->desc_size); + + /* + * If this is not an RTS frame send the tx_status to mac80211, + * that method also cleans up the skb structure. When this + * is a RTS frame, that it is our job to clean this structure up. + */ + if (!GET_FLAG(entry, ENTRY_RTS_CTS_FRAME)) + ieee80211_tx_status_irqsafe(rt2x00dev->hw, + entry->skb, &entry->tx_status); + else + dev_kfree_skb(entry->skb); + + entry->skb = NULL; + + CLEAR_FLAG(entry, ENTRY_RTS_CTS_FRAME); + + rt2x00_ring_index_done_inc(entry->ring); + + /* + * Check if we are waiting on an empty queue + * to start scanning. + */ + if (rt2x00dev->scan && + rt2x00_ring_empty(&rt2x00dev->ring[RING_TX]) && + rt2x00_ring_empty(&rt2x00dev->ring[RING_ATIM]) && + rt2x00_ring_empty(&rt2x00dev->ring[RING_PRIO])) + rt2x00_signal_scan(rt2x00dev->scan, SCANNING_READY); + + /* + * 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); +} + +/* + * 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 int rt2500usb_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 = &rt2x00dev->ring[RING_BEACON]; + struct data_entry *beacon; + struct data_entry *guardian; + int length; + u16 reg; + + /* + * 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); + rt2500usb_write_tx_desc(rt2x00dev, + (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, + rt2500usb_interrupt_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, + rt2500usb_interrupt_beacondone, + guardian); + + /* + * Send out the guardian byte. + */ + usb_submit_urb(guardian->priv, GFP_ATOMIC); + + /* + * Enable beacon generation. + */ + 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); + } + + 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, + .passive_scan = rt2x00lib_passive_scan, + .get_stats = rt2500usb_get_stats, + .conf_tx = rt2x00lib_conf_tx, + .get_tx_stats = rt2x00lib_get_tx_stats, + .beacon_update = rt2500usb_beacon_update, +}; + +static const struct rt2x00lib_ops rt2500usb_rt2x00_ops = { + .initialize = rt2500usb_initialize, + .uninitialize = rt2500usb_uninitialize, + .enable_radio = rt2500usb_enable_radio, + .disable_radio = rt2500usb_disable_radio, + .toggle_rx = rt2500usb_toggle_rx, + .write_tx_data = rt2500usb_write_tx_data, + .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, +}; + +/* + * 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_alloc_rings(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + + rt2x00dev->ring = kzalloc( + sizeof(struct data_ring) * RING_NUM, GFP_KERNEL); + if (!rt2x00dev->ring) { + ERROR("Ring allocation failed.\n"); + return -ENOMEM; + } + + SET_FLAG(rt2x00dev, DEVICE_SUPPORT_ATIM); + + 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; +} + +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."); + 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 int rt2500usb_init_hw_mac(struct rt2x00_dev *rt2x00dev) +{ + u8 *addr; + + /* + * Get the pointer to the MAC address in the EEPROM. + */ + addr = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); + + /* + * Check if a valid MAC address is present. + */ + if (!is_valid_ether_addr(addr)) { + ERROR("Invalid MAC address: " MAC_FMT ".\n", MAC_ARG(addr)); + return -EINVAL; + } + + /* + * Write MAC address to register. + */ + rt2500usb_config_mac_addr(rt2x00dev, addr); + + /* + * Copy MAC address to the hw structure. + */ + SET_IEEE80211_PERM_ADDR(rt2x00dev->hw, addr); + + return 0; +} + +static void rt2500usb_init_hw_channels(struct rt2x00_dev *rt2x00dev, + struct ieee80211_channel *channels) +{ + unsigned int i; + u32 rf2_base; + u16 eeprom; + static const struct { + unsigned int chip; + u32 val[3]; + } rf[] = { + { RF2522, { 0x00002050, 0x00000101, 0x00000000 } }, + { RF2523, { 0x00022010, 0x000e0111, 0x00000a1b } }, + { RF2524, { 0x00032020, 0x00000101, 0x00000a1b } }, + { RF2525, { 0x00022020, 0x00060111, 0x00000a1b } }, + { RF2525E, { 0x00022010, 0x00060111, 0x00000000 } }, + { RF5222, { 0x00000000, 0x00000101, 0x00000000 } } + }; + + /* + * Channel initialization. + * First we set the basic variables. + */ + for (i = 0; i < 13; i++) { + channels[i].chan = i + 1; + channels[i].freq = 2407 + ((i + 1) * 5); + channels[i].flag = IEEE80211_CHAN_W_IBSS | + IEEE80211_CHAN_W_ACTIVE_SCAN | IEEE80211_CHAN_W_SCAN; + channels[i].antenna_max = 0xff; + } + + channels[13].chan = 14; + channels[13].freq = 2484; + channels[13].flag = IEEE80211_CHAN_W_IBSS | + IEEE80211_CHAN_W_ACTIVE_SCAN | IEEE80211_CHAN_W_SCAN; + channels[13].antenna_max = 0xff; + + if (rt2x00_rf(&rt2x00dev->chip, RF5222)) { + for (i = 14; i < 37; 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); + channels[i].freq = ((i - 13) + 1000) * 5; + channels[i].flag = IEEE80211_CHAN_W_IBSS | + IEEE80211_CHAN_W_ACTIVE_SCAN | + IEEE80211_CHAN_W_SCAN; + channels[i].power_level = DEFAULT_TXPOWER; + channels[i].antenna_max = 0xff; + } + } + + /* + * Set device specific value. + */ + rf2_base = 0; + if (rt2x00_rf(&rt2x00dev->chip, RF2525)) + rf2_base = 0x00080000; + + if (rt2x00_rf(&rt2x00dev->chip, RF2522)) { + static const u32 vals[] = { + 0x000c1fda, 0x000c1fee, 0x000c2002, 0x000c2016, + 0x000c202a, 0x000c203e, 0x000c2052, 0x000c2066, + 0x000c207a, 0x000c208e, 0x000c20a2, 0x000c20b6, + 0x000c20ca, 0x000c20fa + }; + + for (i = 0; i < ARRAY_SIZE(vals); i++) + channels[i].val = vals[i]; + } else if (rt2x00_rf(&rt2x00dev->chip, RF2523) || + rt2x00_rf(&rt2x00dev->chip, RF2524) || + rt2x00_rf(&rt2x00dev->chip, RF2525)) { + static const u32 vals[] = { + 0x00000c9e, 0x00000ca2, 0x00000ca6, 0x00000caa, + 0x00000cae, 0x00000cb2, 0x00000cb6, 0x00000cba, + 0x00000cbe, 0x00000d02, 0x00000d06, 0x00000d0a, + 0x00000d0e, 0x00000d1a + }; + + for (i = 0; i < ARRAY_SIZE(vals); i++) + channels[i].val = vals[i] | rf2_base; + } else if (rt2x00_rf(&rt2x00dev->chip, RF2525E)) { + static const u32 vals[] = { + 0x0000089a, 0x0000089e, 0x0000089e, 0x000008a2, + 0x000008a2, 0x000008a6, 0x000008a6, 0x000008aa, + 0x000008aa, 0x000008ae, 0x000008ae, 0x000008b2, + 0x000008b2, 0x000008b6 + }; + + for (i = 0; i < ARRAY_SIZE(vals); i++) + channels[i].val = vals[i]; + } else if (rt2x00_rf(&rt2x00dev->chip, RF5222)) { + static const u32 vals[] = { + 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 + }; + + for (i = 0; i < ARRAY_SIZE(vals); i++) + channels[i].val = vals[i]; + } + + /* + * Set TX power, each EEPROM TXpower entry + * contains the TXpower value for 2 channels. + */ + for (i = 0; i < EEPROM_TXPOWER_SIZE; i++) { + rt2x00_eeprom_read(rt2x00dev, + EEPROM_TXPOWER_START + i, &eeprom); + + channels[(i * 2)].power_level = TXPOWER_FROM_DEV( + rt2x00_get_field16(eeprom, EEPROM_TXPOWER_1)); + + channels[(i * 2) + 1].power_level = TXPOWER_FROM_DEV( + rt2x00_get_field16(eeprom, EEPROM_TXPOWER_2)); + } + + /* + * Set device specific, but channel independent RF values. + */ + for (i = 0; i < ARRAY_SIZE(rf); i++) { + if (rt2x00_rf(&rt2x00dev->chip, rf[i].chip)) { + rt2x00dev->rf1 = rf[i].val[0]; + rt2x00dev->rf3 = rf[i].val[1]; + rt2x00dev->rf4 = rf[i].val[2]; + } + } +} + +static void rt2500usb_init_hw_rates(struct rt2x00_dev *rt2x00dev, + struct ieee80211_rate *rates) +{ + /* + * Rates initialization. + */ + device_rate_entry(&rates[0], 10, 0x001, 0x00, IEEE80211_RATE_CCK); + device_rate_entry(&rates[1], 20, 0x003, 0x01, IEEE80211_RATE_CCK_2); + device_rate_entry(&rates[2], 55, 0x007, 0x02, IEEE80211_RATE_CCK_2); + device_rate_entry(&rates[3], 110, 0x00f, 0x03, IEEE80211_RATE_CCK_2); + device_rate_entry(&rates[4], 60, 0x01f, 0x0b, IEEE80211_RATE_OFDM); + device_rate_entry(&rates[5], 90, 0x03f, 0x0f, IEEE80211_RATE_OFDM); + device_rate_entry(&rates[6], 120, 0x07f, 0x0a, IEEE80211_RATE_OFDM); + device_rate_entry(&rates[7], 180, 0x0ff, 0x0e, IEEE80211_RATE_OFDM); + device_rate_entry(&rates[8], 240, 0x1ff, 0x09, IEEE80211_RATE_OFDM); + device_rate_entry(&rates[9], 360, 0x3ff, 0x0d, IEEE80211_RATE_OFDM); + device_rate_entry(&rates[10], 480, 0x7ff, 0x08, IEEE80211_RATE_OFDM); + device_rate_entry(&rates[11], 540, 0xfff, 0x0c, IEEE80211_RATE_OFDM); +} + +static int rt2500usb_init_hw_modes(struct rt2x00_dev *rt2x00dev) +{ + struct ieee80211_channel *channels; + struct ieee80211_rate *rates; + int status = -ENOMEM; + int num_modes; + int num_channels; + + /* + * RT2500 only supports 802.11b & 802.11g, + * so we should allocate 14 OFDM channels, 4 CCK rates + * and 8 OFDM rates. + * RF5222 also supports 802.11a, so allocate an + * additional 23 5.2GHz channels. + */ + num_modes = 2; + num_channels = 14; + if (rt2x00_rf(&rt2x00dev->chip, RF5222)) { + num_modes = 3; + num_channels = 37; + } + + rt2x00dev->hwmodes = + kzalloc(sizeof(struct ieee80211_hw_mode) * num_modes, + GFP_KERNEL); + if (!rt2x00dev->hwmodes) + goto exit; + + channels = kzalloc(sizeof(struct ieee80211_channel) * num_channels, + GFP_KERNEL); + if (!channels) + goto exit_free_modes; + + rates = kzalloc(sizeof(struct ieee80211_rate) * 12, GFP_KERNEL); + if (!rates) + goto exit_free_channels; + + /* + * Initialize channels and rate arrays. + */ + rt2500usb_init_hw_channels(rt2x00dev, channels); + rt2500usb_init_hw_rates(rt2x00dev, rates); + + /* + * Intitialize 802.11b + * Rates: CCK. + * Channels: OFDM. + */ + rt2x00dev->hwmodes[HWMODE_B].mode = MODE_IEEE80211B; + rt2x00dev->hwmodes[HWMODE_B].num_channels = 14; + rt2x00dev->hwmodes[HWMODE_B].num_rates = 4; + rt2x00dev->hwmodes[HWMODE_B].channels = channels; + rt2x00dev->hwmodes[HWMODE_B].rates = rates; + + /* + * Intitialize 802.11g + * Rates: CCK, OFDM. + * Channels: OFDM. + */ + rt2x00dev->hwmodes[HWMODE_G].mode = MODE_IEEE80211G; + rt2x00dev->hwmodes[HWMODE_G].num_channels = 14; + rt2x00dev->hwmodes[HWMODE_G].num_rates = 12; + rt2x00dev->hwmodes[HWMODE_G].channels = channels; + rt2x00dev->hwmodes[HWMODE_G].rates = rates; + + /* + * Intitialize 802.11a + * Rates: OFDM. + * Channels: OFDM, UNII, HiperLAN2. + */ + if (num_channels == 3) { + rt2x00dev->hwmodes[HWMODE_A].mode = MODE_IEEE80211A; + rt2x00dev->hwmodes[HWMODE_A].num_channels = 23; + rt2x00dev->hwmodes[HWMODE_A].num_rates = 8; + rt2x00dev->hwmodes[HWMODE_A].channels = &channels[14]; + rt2x00dev->hwmodes[HWMODE_A].rates = &rates[4]; + } + + /* + * Register the working modes. + */ + status = ieee80211_register_hwmode(rt2x00dev->hw, + &rt2x00dev->hwmodes[HWMODE_G]); + if (status) + goto exit_free_rates; + + status = ieee80211_register_hwmode(rt2x00dev->hw, + &rt2x00dev->hwmodes[HWMODE_B]); + if (status) + goto exit_free_rates; + + if (num_modes == 3) { + status = ieee80211_register_hwmode(rt2x00dev->hw, + &rt2x00dev->hwmodes[HWMODE_A]); + if (status) + goto exit_free_rates; + } + + return 0; + +exit_free_rates: + kfree(rates); + +exit_free_channels: + kfree(channels); + +exit_free_modes: + kfree(rt2x00dev->hwmodes); + rt2x00dev->hwmodes = NULL; + +exit: + ERROR("Allocation ieee80211 modes failed.\n"); + return status; +} + +static int rt2500usb_init_hw(struct rt2x00_dev *rt2x00dev) +{ + int status; + + if (GET_FLAG(rt2x00dev, DEVICE_INITIALIZED_HW)) + return 0; + + SET_IEEE80211_DEV(rt2x00dev->hw, &rt2x00dev_usb(rt2x00dev)->dev); + + /* + * Read MAC address from EEPROM. + */ + status = rt2500usb_init_hw_mac(rt2x00dev); + if (status) + return status; + + /* + * 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; + + if (ieee80211_register_hw(rt2x00dev->hw)) + return -EIO; + + status = rt2500usb_init_hw_modes(rt2x00dev); + if (status) { + ieee80211_unregister_hw(rt2x00dev->hw); + return status; + } + + SET_FLAG(rt2x00dev, DEVICE_INITIALIZED_HW); + + return 0; +} + +static void rt2500usb_free_dev(struct rt2x00_dev *rt2x00dev) +{ + /* + * Close debugfs entry. + */ + rt2500usb_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); + + /* + * Free ieee80211_hw memory. + */ + if (likely(rt2x00dev->hwmodes)) { + kfree(rt2x00dev->hwmodes->channels); + kfree(rt2x00dev->hwmodes->rates); + kfree(rt2x00dev->hwmodes); + rt2x00dev->hwmodes = NULL; + } +} + +static int rt2500usb_alloc_dev(struct usb_interface *usb_intf, + struct ieee80211_hw *hw) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + + rt2x00dev->dev = usb_intf; + rt2x00dev->lib_ops = &rt2500usb_rt2x00_ops; + rt2x00dev->hw_ops = &rt2500usb_mac80211_ops; + rt2x00dev->hw = hw; + + /* + * Allocate eeprom data. + */ + if (rt2500usb_alloc_eeprom(rt2x00dev)) + goto exit; + + /* + * Create workqueue. + */ + rt2x00dev->workqueue = create_singlethread_workqueue(DRV_NAME); + if (!rt2x00dev->workqueue) + return -ENODEV; + + /* + * Initialize configuration work. + */ + INIT_DELAYED_WORK(&rt2x00dev->link.work, rt2500usb_link_tuner); + + /* + * Reset current working type. + */ + rt2x00dev->interface.type = -EINVAL; + + /* + * Intialize scanning attributes. + */ + rt2x00dev->scan = NULL; + + /* + * Allocate ring array. + */ + if (rt2500usb_alloc_rings(rt2x00dev)) + goto exit; + + /* + * Initialize hardware. + */ + if (rt2500usb_init_eeprom(rt2x00dev) || + rt2500usb_init_hw(rt2x00dev)) { + ERROR("Failed to initialize device.\n"); + goto exit; + } + + /* + * Open the debugfs entry. + */ + rt2500usb_open_debugfs(rt2x00dev); + + return 0; + +exit: + rt2500usb_free_dev(rt2x00dev); + + return -ENODEV; +} + +/* + * USB driver handlers. + */ +static int rt2500usb_probe(struct usb_interface *usb_intf, + const struct usb_device_id *id) +{ + struct usb_device *usb_dev = interface_to_usbdev(usb_intf); + struct ieee80211_hw *hw; + int status; + + usb_dev = usb_get_dev(usb_dev); + + hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), + &rt2500usb_mac80211_ops); + if (!hw) { + ERROR("Failed to allocate hardware.\n"); + status = -ENOMEM; + goto exit_put_device; + } + + usb_set_intfdata(usb_intf, hw); + + status = rt2500usb_alloc_dev(usb_intf, hw); + if (status) { + ERROR("Failed to allocate device.\n"); + goto exit_free_device; + } + + return 0; + +exit_free_device: + ieee80211_free_hw(hw); + +exit_put_device: + usb_put_dev(usb_dev); + + return status; +} + +static void rt2500usb_disconnect(struct usb_interface *usb_intf) +{ + struct ieee80211_hw *hw = usb_get_intfdata(usb_intf); + struct rt2x00_dev *rt2x00dev = hw->priv; + + /* + * Uninitialize the 80211 stack data. + */ + ieee80211_unregister_hw(hw); + + /* + * Uninitialize and free the rt2500usb driver data. + */ + rt2500usb_disable_radio(rt2x00dev); + rt2500usb_uninitialize(rt2x00dev); + rt2500usb_free_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)); +} + +#ifdef CONFIG_PM +static int rt2500usb_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 status; + + NOTICE("Going to sleep.\n"); + + /* + * Disable the radio. + */ + rt2500usb_disable_radio(rt2x00dev); + + /* + * Set device mode to sleep for power management. + */ + status = rt2500usb_set_state(rt2x00dev, STATE_SLEEP); + if (status) + return status; + + /* + * Uninitialize device and hardware. + */ + rt2500usb_uninitialize(rt2x00dev); + rt2500usb_free_dev(rt2x00dev); + + /* + * Decrease usbdev refcount. + */ + usb_put_dev(interface_to_usbdev(usb_intf)); + + return 0; +} + +static int rt2500usb_resume(struct usb_interface *usb_intf) +{ + struct ieee80211_hw *hw = usb_get_intfdata(usb_intf); + struct rt2x00_dev *rt2x00dev = hw->priv; + int status; + + NOTICE("Waking up.\n"); + + /* + * Increase usbdev refcount. + */ + usb_get_dev(interface_to_usbdev(usb_intf)); + + /* + * Initialize hardware. + */ + status = rt2500usb_alloc_dev(usb_intf, hw); + if (status) { + ERROR("Failed to allocate device.\n"); + return status; + } + + /* + * Set device mode to awake for power management. + */ + status = rt2500usb_set_state(rt2x00dev, STATE_AWAKE); + if (status) + return status; + + return 0; +} +#endif /* CONFIG_PM */ + +/* + * 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(0x0b05, 0x1707) }, + /* Belkin */ + { USB_DEVICE(0x050d, 0x7050) }, + { USB_DEVICE(0x050d, 0x7051) }, + { USB_DEVICE(0x050d, 0x705a) }, + /* Cisco Systems */ + { USB_DEVICE(0x13b1, 0x000d) }, + { USB_DEVICE(0x13b1, 0x0011) }, + { USB_DEVICE(0x13b1, 0x001a) }, + /* Conceptronic */ + { USB_DEVICE(0x14b2, 0x3c02) }, + /* D-LINK */ + { USB_DEVICE(0x2001, 0x3c00) }, + /* Gigabyte */ + { USB_DEVICE(0x1044, 0x8001) }, + { USB_DEVICE(0x1044, 0x8007) }, + /* Hercules */ + { USB_DEVICE(0x06f8, 0xe000) }, + /* Melco */ + { USB_DEVICE(0x0411, 0x0066) }, + { USB_DEVICE(0x0411, 0x0067) }, + { USB_DEVICE(0x0411, 0x008b) }, + /* MSI */ + { USB_DEVICE(0x0db0, 0x6861) }, + { USB_DEVICE(0x0db0, 0x6865) }, + { USB_DEVICE(0x0db0, 0x6869) }, + /* Ralink */ + { USB_DEVICE(0x148f, 0x1706) }, + { USB_DEVICE(0x148f, 0x2570) }, + { USB_DEVICE(0x148f, 0x2573) }, + { USB_DEVICE(0x148f, 0x9020) }, + /* Siemens */ + { USB_DEVICE(0x0681, 0x3c06) }, + /* SMC */ + { USB_DEVICE(0x0707, 0xee13) }, + /* Spairon */ + { USB_DEVICE(0x114b, 0x0110) }, + /* Trust */ + { USB_DEVICE(0x0eb0, 0x9020) }, + /* Zinwell */ + { USB_DEVICE(0x5a57, 0x0260) }, + { 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 = rt2500usb_probe, + .disconnect = rt2500usb_disconnect, +#ifdef CONFIG_PM + .suspend = rt2500usb_suspend, + .resume = rt2500usb_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 -puN /dev/null drivers/net/wireless/mac80211/rt2x00/rt2500usb.h --- /dev/null +++ a/drivers/net/wireless/mac80211/rt2x00/rt2500usb.h @@ -0,0 +1,746 @@ +/* + 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); + +/* + * Radio control functions. + */ +static int rt2500usb_enable_radio(struct rt2x00_dev *rt2x00dev); +static void rt2500usb_disable_radio(struct rt2x00_dev *rt2x00dev); + +/* + * Interrupt functions. + */ +static void rt2500usb_interrupt_beacondone(struct urb *urb); +static void rt2500usb_interrupt_rxdone(struct urb *urb); +static void rt2500usb_interrupt_txdone(struct urb *urb); + +#endif /* RT2500USB_H */ diff -puN /dev/null drivers/net/wireless/mac80211/rt2x00/rt2x00.h --- /dev/null +++ a/drivers/net/wireless/mac80211/rt2x00/rt2x00.h @@ -0,0 +1,1069 @@ +/* + 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 & rt2573. + */ + +#ifndef RT2X00_H +#define RT2X00_H + +#include +#include +#include + +/* + * 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) || \ + (__ffs((__mask) >= 16))); \ + (struct rt2x00_field16) { \ + __ffs(__mask), (__mask) \ + }; \ +}) + +#define FIELD32(__mask) \ +({ \ + BUILD_BUG_ON(!(__mask) || \ + !is_valid_mask(__mask) || \ + (__ffs((__mask) >= 32))); \ + (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 RT73 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 + * 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 +#define ENTRY_RTS_CTS_FRAME 0x00000004 + + /* + * extra register field + */ + unsigned int reg; + + /* + * 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; + + /* + * Work structure for bottom half interrupt handling. + */ + struct work_struct irq_work; + + /* + * 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; +}; + +/* + * Scanning structure. + * Swithing channel during scanning will be put + * in a workqueue so we will be able to sleep + * during the switch. + * We also make use of the completion structure + * in case a frame must be send before a + * channel switch. + */ +struct scanning { + /* + * Pointer to main rt2x00dev structure where this + * scanning structure belongs to. + */ + struct rt2x00_dev *rt2x00dev; + + /* + * Completion structure if an packet needs to be send. + */ + struct completion completion; + + /* + * Scanning parameters. + */ + struct ieee80211_scan_conf conf; + + /* + * Scanning state: IEEE80211_SCAN_START or IEEE80211_SCAN_END. + */ + short state; + + /* + * Flag to see if this scan has been cancelled. + */ + short status; +#define SCANNING_READY 0x0000 +#define SCANNING_WAITING 0x0001 +#define SCANNING_CANCELLED 0x0002 + + /* + * Work structure for scheduling the scanning work. + */ + struct work_struct work; +}; + +static inline void rt2x00_signal_scan(struct scanning *scan, short status) +{ + scan->status = status; + + complete_all(&scan->completion); +} + +/* + * rt2x00lib callback functions. + */ +struct rt2x00lib_ops { + /* + * 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); + void (*toggle_rx)(struct rt2x00_dev *rt2x00dev, int enable); + + /* + * TX control handlers + */ + 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); + + /* + * 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 rf2, + 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 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; +#define rt2x00dev_pci(__dev) ( (struct pci_dev*)(__dev)->dev ) +#define rt2x00dev_usb(__dev) ( (struct usb_interface*)(__dev)->dev ) + + /* + * Callback functions. + */ + const struct rt2x00lib_ops *lib_ops; + const struct ieee80211_ops *hw_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_ENABLED_RADIO_HW 0x00000002 +#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; + + /* + * Base address of device registers (PCI devices only). + */ + void __iomem *csr_addr; + + /* + * If enabled, the structure for the debugfs files. + */ + void *debugfs_data; + + /* + * 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; + + /* + * Scanning structure. + */ + struct scanning *scan; + + /* + * 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; +}; + +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 ) + +/* + * Initialize a ieee80211_entry by filling in all fields and correctly + * construct the device specific val and val2 fields. + */ +static inline void device_rate_entry(struct ieee80211_rate *entry, + int rate, int mask, int plcp, int 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 | + DEVICE_SET_RATE_FIELD(entry->flags & IEEE80211_RATE_PREAMBLE2, + PREAMBLE); + entry->min_rssi_ack = 0; + entry->min_rssi_ack_delta = 0; +} + +/* + * 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 -puN /dev/null drivers/net/wireless/mac80211/rt2x00/rt2x00debug.c --- /dev/null +++ a/drivers/net/wireless/mac80211/rt2x00/rt2x00debug.c @@ -0,0 +1,380 @@ +/* + 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: rt2x00debug specific routines. + Supported chipsets: RT2460, RT2560, RT2570, + rt2561, rt2561s, rt2661 & rt2573. + */ + +#include +#include +#include +#include +#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. + */ + 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, 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, + 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; \ + struct rt2x00debug *debug = intf->debug; \ + struct rt2x00debug_reg *reg = &debug->reg_##__name; \ + \ + if (intf->offset_##__name > reg->length) \ + 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; \ + struct rt2x00debug *debug = intf->debug; \ + struct rt2x00debug_reg *reg = &debug->reg_##__name; \ + \ + if (intf->offset_##__name > reg->length) \ + 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) +{ + 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) +{ + 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.length); + data += sprintf(data, "eeprom length: %d\n", debug->reg_eeprom.length); + data += sprintf(data, "bbp length: %d\n", debug->reg_bbp.length); + blob->size = strlen(blob->data); + + return debugfs_create_blob(name, S_IRUGO, intf->driver_folder, blob); +} + +void *rt2x00debug_register(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(debug); + + return NULL; +} +EXPORT_SYMBOL_GPL(rt2x00debug_register); + +void rt2x00debug_deregister(void *debugfs_data) +{ + struct rt2x00debug_intf *intf = debugfs_data; + + if (!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); +} +EXPORT_SYMBOL_GPL(rt2x00debug_deregister); + +/* + * rt2x00debug module information. + */ +MODULE_AUTHOR("http://rt2x00.serialmonkey.com"); +MODULE_DESCRIPTION("Debugfs support for rt2x00 drivers."); +MODULE_LICENSE("GPL"); + +static int __init rt2x00debug_init(void) +{ + return 0; +} + +static void __exit rt2x00debug_exit(void) +{ + +} + +module_init(rt2x00debug_init); +module_exit(rt2x00debug_exit); diff -puN /dev/null drivers/net/wireless/mac80211/rt2x00/rt2x00debug.h --- /dev/null +++ a/drivers/net/wireless/mac80211/rt2x00/rt2x00debug.h @@ -0,0 +1,70 @@ +/* + 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 & rt2573. + */ + +#ifndef RT2X00DEBUG_H +#define RT2X00DEBUG_H + +#include + +struct rt2x00_dev; + +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 length; +}; + +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; +}; + +void *rt2x00debug_register(struct rt2x00debug *debug, struct wiphy *wiphy, + struct rt2x00_dev *rt2x00dev); +void rt2x00debug_deregister(void *debugfs_data); + +#endif /* RT2X00DEBUG_H */ diff -puN /dev/null drivers/net/wireless/mac80211/rt2x00/rt2x00dev.c --- /dev/null +++ a/drivers/net/wireless/mac80211/rt2x00/rt2x00dev.c @@ -0,0 +1,231 @@ +/* + 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 & rt2573. + */ + +/* + * Set enviroment defines for rt2x00.h + */ +#define DRV_NAME "rt2x00lib" + +#include +#include +#include +#include +#include +#include + +#include "rt2x00.h" +#include "rt2x00lib.h" + +#ifdef CONFIG_RT2X00_LIB_FIRMWARE +struct fw_entry { + u32 chip; + char *name; +}; + +int rt2x00lib_load_firmware(struct rt2x00_dev *rt2x00dev, struct device *dev, + void (*cont)(const struct firmware *fw, void *context)) +{ + unsigned int i; + int status = -EINVAL; + static const struct fw_entry fw_list[] = { + { RT2561, "rt2561.bin" }, + { RT2561s, "rt2561s.bin" }, + { RT2661, "rt2661.bin" }, + { RT73, "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, dev, + rt2x00dev, cont); + } + + if (status) + ERROR("Failed to request Firmware.\n"); + + return status; + +} +EXPORT_SYMBOL_GPL(rt2x00lib_load_firmware); + +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); +#endif /* CONFIG_RT2X00_LIB_FIRMWARE */ + +void rt2x00lib_update_tx_stats(struct data_entry *entry, + const int status, const int is_ack, const int retry) +{ + struct ieee80211_low_level_stats *stats = + &entry->ring->rt2x00dev->low_level_stats; + + entry->tx_status.flags = 0; + entry->tx_status.ack_signal = 0; + entry->tx_status.excessive_retries = (status == TX_FAIL_RETRY); + entry->tx_status.retry_count = retry; + if (is_ack) + entry->tx_status.flags |= IEEE80211_TX_STATUS_ACK; + + entry->tx_status.queue_length = entry->ring->stats.limit; + entry->tx_status.queue_number = entry->tx_status.control.queue; + + if (!is_ack && status == TX_FAIL_RETRY) + stats->dot11ACKFailureCount++; + + if (entry->tx_status.control.flags & IEEE80211_TXCTL_USE_RTS_CTS) { + if (status == TX_SUCCESS || status == TX_SUCCESS_RETRY) + stats->dot11RTSSuccessCount++; + else + stats->dot11RTSFailureCount++; + } +} +EXPORT_SYMBOL_GPL(rt2x00lib_update_tx_stats); + +void rt2x00lib_update_rx_stats(struct rt2x00_dev *rt2x00dev, + const int signal, const int rssi, const int ofdm) +{ + struct ieee80211_hw_mode *mode; + struct ieee80211_rate *rate; + unsigned int i; + int val = 0; + + 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; + } + } + + rt2x00dev->rx_status.rate = val; + rt2x00dev->rx_status.ssi = rssi; + rt2x00dev->rx_status.noise = rt2x00_get_link_noise(&rt2x00dev->link); +} +EXPORT_SYMBOL_GPL(rt2x00lib_update_rx_stats); + +int rt2x00lib_detect_channel_time(struct rt2x00_dev *rt2x00dev) +{ + struct ieee80211_hw_mode *mode; + unsigned long jiffies_start; + unsigned long jiffies_end; + + /* + * Only initialize the channel_change_time + * if it has not been set previously. + */ + if (rt2x00dev->hw->channel_change_time) + return 0; + + /* + * Invalidate the rx_status.channel value to make sure + * the config channel will be correctly executed. + */ + rt2x00dev->rx_status.channel = 0; + + /* + * Determine channel_change_time + * by measuring the time it takes + * to switch the channel. + */ + jiffies_start = jiffies; + mode = &rt2x00dev->hwmodes[rt2x00dev->curr_hwmode]; + rt2x00dev->lib_ops->config_channel(rt2x00dev, + mode->channels[0].val, + mode->channels[0].chan, + mode->channels[0].freq, + mode->channels[0].power_level); + jiffies_end = jiffies; + + rt2x00dev->hw->channel_change_time = + jiffies_to_usecs((long)jiffies_end - (long)jiffies_start); + + NOTICE("Channel change time has been set to %d.\n", + rt2x00dev->hw->channel_change_time); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2x00lib_detect_channel_time); + +/* + * 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 -puN /dev/null drivers/net/wireless/mac80211/rt2x00/rt2x00lib.h --- /dev/null +++ a/drivers/net/wireless/mac80211/rt2x00/rt2x00lib.h @@ -0,0 +1,74 @@ +/* + 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 & rt2573. + */ + +#ifndef RT2X00LIB_H +#define RT2X00LIB_H + +#include + +/* + * Firmware handlers. + */ +#ifdef CONFIG_RT2X00_LIB_FIRMWARE +int rt2x00lib_load_firmware(struct rt2x00_dev *rt2x00dev, struct device *dev, + void (*cont)(const struct firmware *fw, void *context)); +int rt2x00lib_load_firmware_wait(struct rt2x00_dev *rt2x00dev); +#endif /* CONFIG_RT2X00_LIB_FIRMWARE */ + +/* + * Interrupt context handlers. + */ +void rt2x00lib_update_tx_stats(struct data_entry *entry, + const int status, const int is_ack, const int retry); +void rt2x00lib_update_rx_stats(struct rt2x00_dev *rt2x00dev, + const int signal, const int rssi, const int ofdm); + +int rt2x00lib_detect_channel_time(struct rt2x00_dev *rt2x00dev); + +/* + * 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_passive_scan(struct ieee80211_hw *hw, + int state, struct ieee80211_scan_conf *conf); +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 -puN /dev/null drivers/net/wireless/mac80211/rt2x00/rt2x00mac.c --- /dev/null +++ a/drivers/net/wireless/mac80211/rt2x00/rt2x00mac.c @@ -0,0 +1,540 @@ +/* + 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 & rt2573. + */ + +#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->lib_ops->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->lib_ops->write_tx_data(rt2x00dev, ring, skb, control)) + return NETDEV_TX_BUSY; + + if (rt2x00dev->lib_ops->kick_tx_queue) + rt2x00dev->lib_ops->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; + + rt2x00dev->lib_ops->disable_radio(rt2x00dev); + return rt2x00dev->lib_ops->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->lib_ops->config_mac_addr(rt2x00dev, conf->mac_addr); + + /* + * Initialize the device. + */ + status = rt2x00dev->lib_ops->initialize(rt2x00dev); + if (status) + return status; + + /* + * Enable radio. + */ + status = rt2x00dev->lib_ops->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)) + rt2x00dev->lib_ops->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->lib_ops->config_type(rt2x00dev, + rt2x00dev->interface.type); + else + rt2x00dev->lib_ops->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) + rt2x00dev->lib_ops->disable_radio(rt2x00dev); + else { + rt2x00dev->lib_ops->toggle_rx(rt2x00dev, 0); + } + } + + rt2x00dev->lib_ops->config_phymode(rt2x00dev, conf->phymode); + rt2x00dev->lib_ops->config_channel(rt2x00dev, + conf->channel_val, conf->channel, conf->freq, + conf->power_level); + rt2x00dev->lib_ops->config_txpower(rt2x00dev, conf->power_level); + rt2x00dev->lib_ops->config_antenna(rt2x00dev, + conf->antenna_sel_tx, conf->antenna_sel_rx); + rt2x00dev->lib_ops->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->lib_ops->toggle_rx(rt2x00dev, 1); + } else if (conf->radio_enabled) + return rt2x00dev->lib_ops->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->lib_ops->config_type(rt2x00dev, conf->type); + rt2x00dev->lib_ops->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->lib_ops->config_promisc) + rt2x00dev->lib_ops->config_promisc(rt2x00dev, + rt2x00dev->interface.promisc); + else + NOTICE("For the moment promisc mode is ignored"); + } +} +EXPORT_SYMBOL_GPL(rt2x00lib_set_multicast_list); + +static void rt2x00lib_scan(struct work_struct *work) +{ + struct scanning *scan = + container_of(work, struct scanning, work); + + if (unlikely(!scan->rt2x00dev)) + return; + + /* + * Before we can start switch the channel for scanning + * we need to wait until all TX rings are empty to guarantee + * that all frames are send on the correct channel. + * Check if the status is set SCANNING_WAITING in order to + * start waiting, or if it is set to SCANNING_CANCELLED which + * means that we shouldn't proceed with the scanning. + */ + if (scan->status == SCANNING_WAITING) + wait_for_completion(&scan->completion); + if (scan->status == SCANNING_CANCELLED) + goto exit; + + /* + * Switch channel and update active info for RX. + */ + if (scan->state == IEEE80211_SCAN_START) { + scan->rt2x00dev->lib_ops->config_phymode( + scan->rt2x00dev, + scan->conf.scan_phymode); + + scan->rt2x00dev->lib_ops->config_channel( + scan->rt2x00dev, + scan->conf.scan_channel_val, + scan->conf.scan_channel, + scan->conf.scan_freq, + scan->conf.scan_power_level); + } else { + scan->rt2x00dev->lib_ops->config_phymode( + scan->rt2x00dev, + scan->conf.running_phymode); + + scan->rt2x00dev->lib_ops->config_channel( + scan->rt2x00dev, + scan->conf.running_channel_val, + scan->conf.running_channel, + scan->conf.running_freq, + scan->conf.scan_power_level); + } + +exit: + scan->rt2x00dev->scan = NULL; + kfree(scan); +} + +int rt2x00lib_passive_scan(struct ieee80211_hw *hw, + int state, struct ieee80211_scan_conf *conf) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + + /* + * Check if we are not busy with the previous + * passive scan request. + */ + if (rt2x00dev->scan) + return -EBUSY; + + /* + * Check if the radio is enabled. + */ + if (!GET_FLAG(rt2x00dev, DEVICE_ENABLED_RADIO)) + return -EIO; + + /* + * Allocate scanning structure to store scanning info. + */ + rt2x00dev->scan = kzalloc(sizeof(struct scanning), GFP_ATOMIC); + if (!rt2x00dev->scan) + return -ENOMEM; + + /* + * Initialize Scanning structure. + */ + rt2x00dev->scan->rt2x00dev = rt2x00dev; + rt2x00dev->scan->state = state; + memcpy(&rt2x00dev->scan->conf, conf, sizeof(*conf)); + + /* + * Initialize completion handler. + * Set initial status to SCANNING_WAITING to prevent scanning + * to begin while there are still TX packets queued. + */ + init_completion(&rt2x00dev->scan->completion); + rt2x00dev->scan->status = SCANNING_WAITING; + + /* + * Check if we have to send a packet before the + * channel switch. + */ + if (conf->skb) { + if (rt2x00dev->hw_ops->tx(hw, conf->skb, conf->tx_control)) + goto exit; + } + + /* + * Queue work. + */ + INIT_WORK(&rt2x00dev->scan->work, rt2x00lib_scan); + if (!queue_work(rt2x00dev->workqueue, &rt2x00dev->scan->work)) + goto exit; + + return 0; + +exit: + kfree(rt2x00dev->scan); + rt2x00dev->scan = NULL; + + return -EIO; +} +EXPORT_SYMBOL_GPL(rt2x00lib_passive_scan); + +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). + * RT2500 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 -puN /dev/null drivers/net/wireless/mac80211/rt2x00/rt2x00pci.h --- /dev/null +++ a/drivers/net/wireless/mac80211/rt2x00/rt2x00pci.h @@ -0,0 +1,39 @@ +/* + 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: rt2400, rt2500, rt2561, rt2561s & rt2661. + */ + +#ifndef RT2X00PCI_H +#define RT2X00PCI_H + +/* + * 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 + +#endif /* RT2X00PCI_H */ diff -puN /dev/null drivers/net/wireless/mac80211/rt2x00/rt2x00usb.h --- /dev/null +++ a/drivers/net/wireless/mac80211/rt2x00/rt2x00usb.h @@ -0,0 +1,82 @@ +/* + 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: rt2570 & rt2573. + */ + +#ifndef RT2X00USB_H +#define RT2X00USB_H + +/* + * 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 + +#endif /* RT2X00USB_H */ diff -puN /dev/null drivers/net/wireless/mac80211/rt2x00/rt61pci.c --- /dev/null +++ a/drivers/net/wireless/mac80211/rt2x00/rt61pci.c @@ -0,0 +1,3364 @@ +/* + 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 + +#include "rt2x00.h" +#include "rt2x00lib.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); +} + +#if defined(CONFIG_RT2X00_DEBUGFS) || defined(CONFIG_RT2X00_DEBUGFS_MODULE) +#include "rt2x00debug.h" + +#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 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), + .length = CSR_REG_SIZE, + }, + .reg_eeprom = { + .read = rt61pci_read_eeprom, + .write = rt61pci_write_eeprom, + .word_size = sizeof(u16), + .length = EEPROM_SIZE, + }, + .reg_bbp = { + .read = rt61pci_read_bbp, + .write = rt61pci_write_bbp, + .word_size = sizeof(u8), + .length = BBP_SIZE, + }, +}; + +static void rt61pci_open_debugfs(struct rt2x00_dev *rt2x00dev) +{ + if (!rt2x00debug_register(&rt61pci_rt2x00debug, + rt2x00dev->hw->wiphy, rt2x00dev)) + ERROR("Failed to register debug handler.\n"); +} + +static void rt61pci_close_debugfs(struct rt2x00_dev *rt2x00dev) +{ + rt2x00debug_deregister(&rt2x00dev->debugfs_data); +} +#else /* CONFIG_RT2X00_DEBUGFS */ +static inline void rt61pci_open_debugfs(struct rt2x00_dev *rt2x00dev){} +static inline void rt61pci_close_debugfs(struct rt2x00_dev *rt2x00dev){} +#endif /* CONFIG_RT2X00_DEBUGFS */ + +/* + * Configuration handlers. + */ +static void rt61pci_config_bssid(struct rt2x00_dev *rt2x00dev, u8 *bssid) +{ + u32 reg; + + /* + * The BSSID is passed to us as an array of bytes, + * that array is little endian, so no need for byte ordering. + * We only need to set the BSS ID MASK at the correct offset. + */ + rt2x00_register_multiwrite(rt2x00dev, MAC_CSR4, bssid, ETH_ALEN); + + rt2x00_register_read(rt2x00dev, MAC_CSR5, ®); + rt2x00_set_field32(®, MAC_CSR5_BSS_ID_MASK, 3); + rt2x00_register_write(rt2x00dev, MAC_CSR5, 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 rf2, int channel, int freq, int txpower) +{ + u8 reg = 0; + u32 rf1 = 0; + 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; + + /* + * The MAC address is passed to us as an array of bytes, + * that array is little endian, so no need for byte ordering. + * We only need to set the MAC_CSR3_UNICAST_TO_ME_MASK + * at the correct offset. + */ + rt2x00_register_multiwrite(rt2x00dev, MAC_CSR2, addr, ETH_ALEN); + + rt2x00_register_read(rt2x00dev, MAC_CSR3, ®); + rt2x00_set_field32(®, MAC_CSR3_UNICAST_TO_ME_MASK, 0xff); + rt2x00_register_write(rt2x00dev, MAC_CSR3, 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_alloc_dma_ring(struct rt2x00_dev *rt2x00dev, + enum ring_index ring_type, work_func_t handler, + const u16 max_entries, const u16 data_size, const u16 desc_size) +{ + struct data_ring *ring = &rt2x00dev->ring[ring_type]; + unsigned int i; + + /* + * Initialize work structure for deferred work. + */ + INIT_WORK(&ring->irq_work, handler); + + 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 = ring->data_addr + + (i * ring->desc_size); + ring->entry[i].data_addr = ring->data_addr + + (ring->stats.limit * ring->desc_size) + + (i * ring->data_size); + ring->entry[i].data_dma = ring->data_dma + + (ring->stats.limit * ring->desc_size) + + (i * ring->data_size); + } + + return 0; +} + +static void rt61pci_free_ring(struct rt2x00_dev *rt2x00dev, + enum ring_index ring_type) +{ + struct data_ring *ring = &rt2x00dev->ring[ring_type]; + + 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 rt61pci_allocate_dma_rings(struct rt2x00_dev *rt2x00dev) +{ + if (rt61pci_alloc_dma_ring(rt2x00dev, RING_RX, + rt61pci_rxdone, RX_ENTRIES, DATA_FRAME_SIZE, + RXD_DESC_SIZE) || + rt61pci_alloc_dma_ring(rt2x00dev, RING_AC_VO, + rt61pci_txdone, TX_ENTRIES, DATA_FRAME_SIZE, + TXD_DESC_SIZE) || + rt61pci_alloc_dma_ring(rt2x00dev, RING_AC_VI, + rt61pci_txdone, TX_ENTRIES, DATA_FRAME_SIZE, + TXD_DESC_SIZE) || + rt61pci_alloc_dma_ring(rt2x00dev, RING_AC_BE, + rt61pci_txdone, TX_ENTRIES, DATA_FRAME_SIZE, + TXD_DESC_SIZE) || + rt61pci_alloc_dma_ring(rt2x00dev, RING_AC_BK, + rt61pci_txdone, TX_ENTRIES, DATA_FRAME_SIZE, + TXD_DESC_SIZE) || + rt61pci_alloc_dma_ring(rt2x00dev, RING_PRIO, + rt61pci_txdone, TX_ENTRIES, DATA_FRAME_SIZE, + TXD_DESC_SIZE) || + rt61pci_alloc_dma_ring(rt2x00dev, RING_BEACON, + NULL, BEACON_ENTRIES, MGMT_FRAME_SIZE, TXD_DESC_SIZE) ) { + return -ENOMEM; + } + + return 0; +} + +static void rt61pci_free_rings(struct rt2x00_dev *rt2x00dev) +{ + rt61pci_free_ring(rt2x00dev, RING_RX); + rt61pci_free_ring(rt2x00dev, RING_AC_VO); + rt61pci_free_ring(rt2x00dev, RING_AC_VI); + rt61pci_free_ring(rt2x00dev, RING_AC_BE); + rt61pci_free_ring(rt2x00dev, RING_AC_BK); + rt61pci_free_ring(rt2x00dev, RING_PRIO); + rt61pci_free_ring(rt2x00dev, RING_BEACON); +} + +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; +} + +/* + * Device initialization functions. + */ +static int rt61pci_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 (rt61pci_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; + + /* + * Register interrupt handler. + */ + if (request_irq(rt2x00dev_pci(rt2x00dev)->irq, rt61pci_interrupt, + IRQF_SHARED, DRV_NAME, rt2x00dev)) { + ERROR("IRQ %d allocation failed.\n", + rt2x00dev_pci(rt2x00dev)->irq); + goto exit_fail; + } + + SET_FLAG(rt2x00dev, DEVICE_INITIALIZED); + + return 0; + +exit_fail: + rt61pci_free_rings(rt2x00dev); + + return -EIO; +} + +static void rt61pci_uninitialize(struct rt2x00_dev *rt2x00dev) +{ + if (!GET_FLAG(rt2x00dev, DEVICE_INITIALIZED)) + return; + + /* + * Cancel scanning. + */ + if (rt2x00dev->scan) + rt2x00_signal_scan(rt2x00dev->scan, SCANNING_CANCELLED); + + /* + * Flush out all pending work. + */ + flush_workqueue(rt2x00dev->workqueue); + + /* + * Free DMA rings. + */ + rt61pci_free_rings(rt2x00dev); + + /* + * Free irq line. + */ + free_irq(rt2x00dev_pci(rt2x00dev)->irq, rt2x00dev); + + CLEAR_FLAG(rt2x00dev, DEVICE_INITIALIZED); +} + +/* + * 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; + + /* + * Don't enable the radio twice. + * or if the hardware button has been disabled. + */ + if (GET_FLAG(rt2x00dev, DEVICE_ENABLED_RADIO) || + !GET_FLAG(rt2x00dev, DEVICE_ENABLED_RADIO_HW)) + return 0; + + /* + * Initialize all registers. + */ + if (rt61pci_init_rings(rt2x00dev) || + rt61pci_init_registers(rt2x00dev) || + rt61pci_init_bbp(rt2x00dev)) { + ERROR("Register initialization failed.\n"); + goto exit_fail; + } + + /* + * Determine channel change time. + */ + if (rt2x00lib_detect_channel_time(rt2x00dev)) + goto exit_fail; + + /* + * 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); + + SET_FLAG(rt2x00dev, DEVICE_ENABLED_RADIO); + + /* + * 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); + rt61pci_toggle_rx(rt2x00dev, 1); + + /* + * Enable LED + */ + rt61pci_enable_led(rt2x00dev); + + ieee80211_start_queues(rt2x00dev->hw); + + return 0; + +exit_fail: + rt61pci_uninitialize(rt2x00dev); + return -EIO; +} + +static void rt61pci_disable_radio(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + if (!GET_FLAG(rt2x00dev, DEVICE_ENABLED_RADIO)) + return; + + ieee80211_stop_queues(rt2x00dev->hw); + + /* + * Disable LED + */ + rt61pci_disable_led(rt2x00dev); + + CLEAR_FLAG(rt2x00dev, DEVICE_ENABLED_RADIO); + + 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); + + rt61pci_toggle_rx(rt2x00dev, 0); + + /* + * 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_desc *txd, struct ieee80211_hdr *ieee80211hdr, + unsigned int length, struct ieee80211_tx_control *control) +{ + struct data_ring *ring; + int tx_rate; + u32 word; + u32 duration; + u32 residual; + u16 length_high; + u16 length_low; + u16 frame_control; + u16 seq_ctrl; + char rts_frame; + char ofdm_rate; + char req_timestamp; + char more_frag; + char ifs; + char queue; + u8 signal; + u8 service; + u8 bitrate; + + /* + * We require the ring structure this packet is being send to. + */ + ring = rt2x00_get_ring(rt2x00dev, control->queue); + if (unlikely(!ring)) + return; + + /* + * Read required fields from ieee80211 header. + */ + frame_control = le16_to_cpu(ieee80211hdr->frame_control); + seq_ctrl = le16_to_cpu(ieee80211hdr->seq_ctrl); + + /* + * Check if this frame is a RTS frame. + */ + rts_frame = is_rts_frame(frame_control); + + /* + * Check which rate should be used for this frame. + */ + if (rts_frame && control->rts_cts_rate) + tx_rate = control->rts_cts_rate; + else + tx_rate = control->tx_rate; + + /* + * Are we working with OFDM rates. + */ + ofdm_rate = !!(DEVICE_GET_RATE_FIELD(tx_rate, RATEMASK) & + DEV_OFDM_RATE); + + /* + * Check if more fragments will follow this frame. + */ + more_frag = !!(ieee80211_get_morefrag(ieee80211hdr)); + + /* + * Beacons and probe responses require the tsf timestamp + * to be inserted into the frame. + */ + req_timestamp = !!(control->queue == IEEE80211_TX_QUEUE_BEACON || + is_probe_resp(frame_control)); + + /* + * 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) || rts_frame) + ifs = IFS_SIFS; + else + ifs = IFS_BACKOFF; + + /* + * Determine queue identification number. + */ + if (control->queue < rt2x00dev->hw->queues) + queue = control->queue; + else + queue = 15; + + /* + * How the length should be processed depends + * on if we are working with OFDM rates or not. + */ + if (ofdm_rate) { + residual = 0; + length_high = ((length + FCS_LEN) >> 6) & 0x3f; + 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++; + + length_high = duration >> 8; + length_low = duration & 0xff; + } + + /* + * Create the signal and service values. + */ + signal = DEVICE_GET_RATE_FIELD(tx_rate, PLCP); + if (DEVICE_GET_RATE_FIELD(tx_rate, PREAMBLE)) + signal |= 0x08; + + service = 0x04; + if (residual <= (8 % 11)) + service |= 0x80; + + /* + * Start writing the descriptor words. + */ + rt2x00_desc_read(txd, 1, &word); + rt2x00_set_field32(&word, TXD_W1_HOST_Q_ID, queue); + rt2x00_set_field32(&word, TXD_W1_AIFSN, ring->tx_params.aifs); + rt2x00_set_field32(&word, TXD_W1_CWMIN, ring->tx_params.cw_min); + rt2x00_set_field32(&word, TXD_W1_CWMAX, 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, signal); + rt2x00_set_field32(&word, TXD_W2_PLCP_SERVICE, service); + rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_LOW, length_low); + rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_HIGH, 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, more_frag); + rt2x00_set_field32(&word, TXD_W0_ACK, + !(control->flags & IEEE80211_TXCTL_NO_ACK)); + rt2x00_set_field32(&word, TXD_W0_TIMESTAMP, req_timestamp); + rt2x00_set_field32(&word, TXD_W0_OFDM, ofdm_rate); + rt2x00_set_field32(&word, TXD_W0_IFS, 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 int rt61pci_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; + u16 fc; + + 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_W0_OWNER_NIC) || + rt2x00_get_field32(word, TXD_W0_VALID)) { + 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); + rt61pci_write_tx_desc(rt2x00dev, txd, ieee80211hdr, skb->len, control); + memcpy(&entry->tx_status.control, control, sizeof(*control)); + entry->skb = skb; + + fc = le16_to_cpu(ieee80211hdr->frame_control); + if (is_cts_frame(fc) || is_rts_frame(fc)) + SET_FLAG(entry, ENTRY_RTS_CTS_FRAME); + + rt2x00_ring_index_inc(ring); + + if (rt2x00_ring_full(ring)) + ieee80211_stop_queue(rt2x00dev->hw, control->queue); + + return 0; +} + +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); +} + +/* + * Interrupt functions. + */ +static void rt61pci_beacondone(struct rt2x00_dev *rt2x00dev) +{ + struct data_ring *ring = &rt2x00dev->ring[RING_BEACON]; + 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->hw_ops->beacon_update(rt2x00dev->hw, skb, + &entry->tx_status.control); + + dev_kfree_skb_any(skb); +} + +static void rt61pci_rxdone(struct work_struct *work) +{ + struct data_ring *ring = + container_of(work, struct data_ring, irq_work); + struct rt2x00_dev *rt2x00dev = ring->rt2x00dev; + struct data_entry *entry; + struct sk_buff *skb; + 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; + + size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT); + + /* + * 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; + + skb = dev_alloc_skb(size + NET_IP_ALIGN); + if (!skb) + return; + + skb_reserve(skb, NET_IP_ALIGN); + + memcpy(skb_put(skb, size), entry->data_addr, size); + + signal = rt2x00_get_field32(word1, RXD_W1_SIGNAL); + rssi = rt2x00_get_field32(word1, RXD_W1_RSSI); + ofdm = rt2x00_get_field32(word0, RXD_W0_OFDM); + rt2x00lib_update_rx_stats(rt2x00dev, signal, rssi, ofdm); + + __ieee80211_rx(rt2x00dev->hw, skb, &rt2x00dev->rx_status); + + /* + * Update link statistics + */ + rt2x00_update_link_rssi(&rt2x00dev->link, + rt2x00dev->rx_status.ssi); + +skip_entry: + 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 work_struct *work) +{ + struct data_ring *ring = + container_of(work, struct data_ring, irq_work); + struct rt2x00_dev *rt2x00dev = ring->rt2x00dev; + struct data_entry *entry; + struct data_desc *txd; + u32 word; + int tx_status; + int retry; + int ack; + + while (!rt2x00_ring_empty(ring)) { + entry = rt2x00_get_data_entry_done(ring); + txd = entry->priv; + rt2x00_desc_read(txd, 0, &word); + + if (!GET_FLAG(entry, ENTRY_TXDONE) || + rt2x00_get_field32(word, TXD_W0_OWNER_NIC) || + !rt2x00_get_field32(word, TXD_W0_VALID)) + return; + + ack = rt2x00_get_field32(word, TXD_W0_ACK); + tx_status = rt2x00_get_field32(entry->reg, STA_CSR4_TX_RESULT); + retry = rt2x00_get_field32(entry->reg, STA_CSR4_RETRY_COUNT); + rt2x00lib_update_tx_stats(entry, tx_status, ack, retry); + + /* + * If this is not an RTS frame send the tx_status to mac80211, + * that method also cleans up the skb structure. When this + * is a RTS frame, that it is our job to clean this structure up. + */ + if (!GET_FLAG(entry, ENTRY_RTS_CTS_FRAME)) + ieee80211_tx_status(rt2x00dev->hw, + entry->skb, &entry->tx_status); + else + dev_kfree_skb(entry->skb); + + rt2x00_set_field32(&word, TXD_W0_VALID, 0); + rt2x00_desc_write(txd, 0, word); + entry->skb = NULL; + + CLEAR_FLAG(entry, ENTRY_TXDONE); + CLEAR_FLAG(entry, ENTRY_RTS_CTS_FRAME); + + rt2x00_ring_index_done_inc(entry->ring); + } + + /* + * Check if we are waiting on an empty queue + * to start scanning. + */ + if (rt2x00dev->scan && + rt2x00_ring_empty(&rt2x00dev->ring[RING_AC_VO]) && + rt2x00_ring_empty(&rt2x00dev->ring[RING_AC_VI]) && + rt2x00_ring_empty(&rt2x00dev->ring[RING_AC_BE]) && + rt2x00_ring_empty(&rt2x00dev->ring[RING_AC_BK]) && + rt2x00_ring_empty(&rt2x00dev->ring[RING_PRIO])) + rt2x00_signal_scan(rt2x00dev->scan, SCANNING_READY); + + /* + * 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 rt61pci_interrupt(int irq, void *dev_instance) +{ + struct rt2x00_dev *rt2x00dev = dev_instance; + struct data_ring *ring; + int index; + 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)) + rt61pci_beacondone(rt2x00dev); + + /* + * 2 - Rx ring done interrupt. + */ + if (rt2x00_get_field32(reg, INT_SOURCE_CSR_RXDONE)) + queue_work(rt2x00dev->workqueue, + &rt2x00dev->ring[RING_RX].irq_work); + + /* + * 3 - Tx ring done interrupt. + * Keep looping while STA_CSR4 contains value data. + * at each read the value will be reset to a new value, + * which we should check since it contains the ring + * and index number of the entry which has been + * completely transmitted. + */ + if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TXDONE)) { + 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; + + ring->entry[index].reg = reg; + SET_FLAG(&ring->entry[index], ENTRY_TXDONE); + + queue_work(rt2x00dev->workqueue, &ring->irq_work); + } + } + + return IRQ_HANDLED; +} + +/* + * 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 int rt61pci_beacon_update(struct ieee80211_hw *hw, + struct sk_buff *skb, struct ieee80211_tx_control *control) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + struct data_entry *entry; + u32 reg; + + entry = rt2x00_get_data_entry(&rt2x00dev->ring[RING_BEACON]); + + /* + * 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); + rt61pci_write_tx_desc(rt2x00dev, entry->priv, + (struct ieee80211_hdr *)skb->data, skb->len, control); + + /* + * Enable beacon generation. + */ + 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); + } + + return 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, + .passive_scan = rt2x00lib_passive_scan, + .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 = rt61pci_beacon_update, +}; + +static const struct rt2x00lib_ops rt61pci_rt2x00_ops = { + .initialize = rt61pci_initialize, + .uninitialize = rt61pci_uninitialize, + .enable_radio = rt61pci_enable_radio, + .disable_radio = rt61pci_disable_radio, + .toggle_rx = rt61pci_toggle_rx, + .write_tx_data = rt61pci_write_tx_data, + .kick_tx_queue = rt61pci_kick_tx_queue, + .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, +}; + +/* + * Device initialization functions. + */ +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); +} + +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_alloc_rings(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + + 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; +} + +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."); + 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; +} + +static int rt61pci_init_hw_mac(struct rt2x00_dev *rt2x00dev) +{ + u8 *addr; + + /* + * Get the pointer to the MAC address in the EEPROM. + */ + addr = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); + + /* + * Check if a valid MAC address is present. + */ + if (!is_valid_ether_addr(addr)) { + ERROR("Invalid MAC address: " MAC_FMT ".\n", MAC_ARG(addr)); + return -EINVAL; + } + + /* + * Write MAC address to register. + */ + rt61pci_config_mac_addr(rt2x00dev, addr); + + /* + * Copy MAC address to the hw structure. + */ + SET_IEEE80211_PERM_ADDR(rt2x00dev->hw, addr); + + return 0; +} + +static void rt61pci_init_hw_channels(struct rt2x00_dev *rt2x00dev, + struct ieee80211_channel *channels) +{ + unsigned int i; + u16 eeprom; + + /* + * Channel initialization. + * First we set the basic variables. + */ + for (i = 0; i < 13; i++) { + channels[i].chan = i + 1; + channels[i].freq = 2407 + ((i + 1) * 5); + channels[i].flag = IEEE80211_CHAN_W_IBSS | + IEEE80211_CHAN_W_ACTIVE_SCAN | IEEE80211_CHAN_W_SCAN; + channels[i].antenna_max = 0xff; + } + + channels[13].chan = 14; + channels[13].freq = 2484; + channels[13].flag = IEEE80211_CHAN_W_IBSS | + IEEE80211_CHAN_W_ACTIVE_SCAN | IEEE80211_CHAN_W_SCAN; + channels[13].antenna_max = 0xff; + + if (rt2x00_rf(&rt2x00dev->chip, RF5225) || + rt2x00_rf(&rt2x00dev->chip, RF5325)) { + for (i = 14; i < 38; 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); + channels[i].freq = ((i - 13) + 1000) * 5; + channels[i].flag = IEEE80211_CHAN_W_IBSS | + IEEE80211_CHAN_W_ACTIVE_SCAN | + IEEE80211_CHAN_W_SCAN; + channels[i].antenna_max = 0xff; + } + } + + /* + * Set device specific value. + */ + if (!GET_FLAG(rt2x00dev, CONFIG_RF_SEQUENCE)) { + static const u32 vals[] = { + 0x00004786, 0x00004786, 0x0000478a, 0x0000478a, + 0x0000478e, 0x0000478e, 0x00004792, 0x00004792, + 0x00004796, 0x00004796, 0x0000479a, 0x0000479a, + 0x0000479e, 0x000047a2 + }; + + for (i = 0; i < ARRAY_SIZE(vals); i++) + channels[i].val = vals[i]; + + if (rt2x00_rf(&rt2x00dev->chip, RF5225) || + rt2x00_rf(&rt2x00dev->chip, RF5325)) { + static const u32 vals_a[] = { + 0x0000499a, 0x000049a2, 0x000049a6, 0x000049aa, + 0x000049ae, 0x000049b2, 0x000049ba, 0x000049be, + 0x00004a2a, 0x00004a2e, 0x00004a32, 0x00004a36, + 0x00004a3a, 0x00004a82, 0x00004a86, 0x00004a8a, + 0x00004a8e, 0x00004a92, 0x00004a9a, 0x00004aa2, + 0x00004aa6, 0x00004aae, 0x00004ab2, 0x00004ab6 + }; + + struct ieee80211_channel *chan = channels + 14; + + for (i = 0; i < ARRAY_SIZE(vals_a); i++) + (chan++)->val = vals_a[i]; + } + } else { + static const u32 vals[] = { + 0x00004786, 0x00004786, 0x0000478a, 0x0000478a, + 0x0000478e, 0x0000478e, 0x00004792, 0x00004792, + 0x00004796, 0x00004796, 0x0000479a, 0x0000479a, + 0x0000479e, 0x000047a2 + }; + + for (i = 0; i < ARRAY_SIZE(vals); i++) + channels[i].val = vals[i]; + + if (rt2x00_rf(&rt2x00dev->chip, RF5225) || + rt2x00_rf(&rt2x00dev->chip, RF5325)) { + static const u32 vals_a[] = { + 0x0004481a, 0x00044682, 0x00044686, 0x0004468e, + 0x00044692, 0x0004469a, 0x000446a2, 0x000446a6, + 0x0004489a, 0x000448a2, 0x000448aa, 0x000448b2, + 0x000448ba, 0x00044702, 0x00044706, 0x0004470e, + 0x00044712, 0x0004471a, 0x00044722, 0x0004472e, + 0x00044736, 0x0004490a, 0x00044912, 0x0004491a + }; + + struct ieee80211_channel *chan = channels + 14; + + for (i = 0; i < ARRAY_SIZE(vals_a); i++) + (chan++)->val = vals_a[i]; + } + } + + /* + * Set TX power, each EEPROM TXpower entry + * contains the TXpower value for 2 channels. + */ + for (i = 0; i < EEPROM_TXPOWER_G_SIZE; i++) { + rt2x00_eeprom_read(rt2x00dev, + EEPROM_TXPOWER_G_START + i, &eeprom); + + channels[(i * 2)].power_level = TXPOWER_FROM_DEV( + rt2x00_get_field16(eeprom, EEPROM_TXPOWER_G_1)); + + channels[(i * 2) + 1].power_level = TXPOWER_FROM_DEV( + rt2x00_get_field16(eeprom, EEPROM_TXPOWER_G_2)); + } + + if (rt2x00_rf(&rt2x00dev->chip, RF5225) || + rt2x00_rf(&rt2x00dev->chip, RF5325)) { + for (i = 0; i < EEPROM_TXPOWER_A_SIZE; i++) { + rt2x00_eeprom_read(rt2x00dev, + EEPROM_TXPOWER_A_START + i, &eeprom); + + channels[(i * 2)].power_level = TXPOWER_FROM_DEV( + rt2x00_get_field16(eeprom, + EEPROM_TXPOWER_A_1)); + + channels[(i * 2) + 1].power_level = TXPOWER_FROM_DEV( + rt2x00_get_field16(eeprom, + EEPROM_TXPOWER_A_2)); + } + } +} + +static void rt61pci_init_hw_rates(struct rt2x00_dev *rt2x00dev, + struct ieee80211_rate *rates) +{ + /* + * Rates initialization. + */ + device_rate_entry(&rates[0], 10, 0x001, 0x00, IEEE80211_RATE_CCK); + device_rate_entry(&rates[1], 20, 0x003, 0x01, IEEE80211_RATE_CCK_2); + device_rate_entry(&rates[2], 55, 0x007, 0x02, IEEE80211_RATE_CCK_2); + device_rate_entry(&rates[3], 110, 0x00f, 0x03, IEEE80211_RATE_CCK_2); + device_rate_entry(&rates[4], 60, 0x01f, 0x0b, IEEE80211_RATE_OFDM); + device_rate_entry(&rates[5], 90, 0x03f, 0x0f, IEEE80211_RATE_OFDM); + device_rate_entry(&rates[6], 120, 0x07f, 0x0a, IEEE80211_RATE_OFDM); + device_rate_entry(&rates[7], 180, 0x0ff, 0x0e, IEEE80211_RATE_OFDM); + device_rate_entry(&rates[8], 240, 0x1ff, 0x09, IEEE80211_RATE_OFDM); + device_rate_entry(&rates[9], 360, 0x3ff, 0x0d, IEEE80211_RATE_OFDM); + device_rate_entry(&rates[10], 480, 0x7ff, 0x08, IEEE80211_RATE_OFDM); + device_rate_entry(&rates[11], 540, 0xfff, 0x0c, IEEE80211_RATE_OFDM); +} + +static int rt61pci_init_hw_modes(struct rt2x00_dev *rt2x00dev) +{ + struct ieee80211_channel *channels; + struct ieee80211_rate *rates; + int status = -ENOMEM; + int num_modes; + int num_channels; + + /* + * RF2527 and RF2529 only supports 802.11b & 802.11g, + * so we should allocate 14 OFDM channels, 4 CCK rates + * and 8 OFDM rates. + * RF5225 and RF5325 also supports 802.11a, so allocate an + * additional 24 5.2GHz channels. + */ + num_modes = 2; + num_channels = 14; + if (rt2x00_rf(&rt2x00dev->chip, RF5225) || + rt2x00_rf(&rt2x00dev->chip, RF5325)) { + num_modes = 3; + num_channels = 38; + } + + rt2x00dev->hwmodes = + kzalloc(sizeof(struct ieee80211_hw_mode) * num_modes, + GFP_KERNEL); + if (!rt2x00dev->hwmodes) + goto exit; + + channels = kzalloc(sizeof(struct ieee80211_channel) * num_channels, + GFP_KERNEL); + if (!channels) + goto exit_free_modes; + + rates = kzalloc(sizeof(struct ieee80211_rate) * 12, GFP_KERNEL); + if (!rates) + goto exit_free_channels; + + /* + * Initialize channels and rate arrays. + */ + rt61pci_init_hw_channels(rt2x00dev, channels); + rt61pci_init_hw_rates(rt2x00dev, rates); + + /* + * Initialize 802.11b + * Rates: CCK. + * Channels: OFDM. + */ + rt2x00dev->hwmodes[HWMODE_B].mode = MODE_IEEE80211B; + rt2x00dev->hwmodes[HWMODE_B].num_channels = 14; + rt2x00dev->hwmodes[HWMODE_B].num_rates = 4; + rt2x00dev->hwmodes[HWMODE_B].channels = channels; + rt2x00dev->hwmodes[HWMODE_B].rates = rates; + + /* + * Initialize 802.11g + * Rates: CCK, OFDM. + * Channels: OFDM. + */ + rt2x00dev->hwmodes[HWMODE_G].mode = MODE_IEEE80211G; + rt2x00dev->hwmodes[HWMODE_G].num_channels = 14; + rt2x00dev->hwmodes[HWMODE_G].num_rates = 12; + rt2x00dev->hwmodes[HWMODE_G].channels = channels; + rt2x00dev->hwmodes[HWMODE_G].rates = rates; + + /* + * Initialize 802.11a + * Rates: OFDM. + * Channels: OFDM, UNII, HiperLAN2. + */ + if (num_channels == 3) { + rt2x00dev->hwmodes[HWMODE_A].mode = MODE_IEEE80211A; + rt2x00dev->hwmodes[HWMODE_A].num_channels = 24; + rt2x00dev->hwmodes[HWMODE_A].num_rates = 8; + rt2x00dev->hwmodes[HWMODE_A].channels = &channels[14]; + rt2x00dev->hwmodes[HWMODE_A].rates = &rates[4]; + } + + /* + * Register the working modes. + */ + status = ieee80211_register_hwmode(rt2x00dev->hw, + &rt2x00dev->hwmodes[HWMODE_G]); + if (status) + goto exit_free_rates; + + status = ieee80211_register_hwmode(rt2x00dev->hw, + &rt2x00dev->hwmodes[HWMODE_B]); + if (status) + goto exit_free_rates; + + if (num_modes == 3) { + status = ieee80211_register_hwmode(rt2x00dev->hw, + &rt2x00dev->hwmodes[HWMODE_A]); + if (status) + goto exit_free_rates; + } + + return 0; + +exit_free_rates: + kfree(rates); + +exit_free_channels: + kfree(channels); + +exit_free_modes: + kfree(rt2x00dev->hwmodes); + rt2x00dev->hwmodes = NULL; + +exit: + ERROR("ieee80211 modes allocation failed.\n"); + return status; +} + +static int rt61pci_init_hw(struct rt2x00_dev *rt2x00dev) +{ + int status; + + if (GET_FLAG(rt2x00dev, DEVICE_INITIALIZED_HW)) + return 0; + + SET_IEEE80211_DEV(rt2x00dev->hw, &rt2x00dev_pci(rt2x00dev)->dev); + + /* + * Read MAC address from EEPROM. + */ + status = rt61pci_init_hw_mac(rt2x00dev); + if (status) + return status; + + /* + * 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; + + if (ieee80211_register_hw(rt2x00dev->hw)) + return -EIO; + + status = rt61pci_init_hw_modes(rt2x00dev); + if (status) { + ieee80211_unregister_hw(rt2x00dev->hw); + return status; + } + + SET_FLAG(rt2x00dev, DEVICE_INITIALIZED_HW); + + return 0; +} + +static void rt61pci_load_firmware(const struct firmware *fw, void *context) +{ + struct rt2x00_dev *rt2x00dev = context; + int i; + u32 reg; + u16 crc; + + if (!fw || !fw->size || !fw->data) { + ERROR("Failed to load Firmware.\n"); + goto exit; + } + + /* + * 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"); + goto exit; + } + + /* + * 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"); + goto exit; + } + + 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"); + goto exit; + } + + /* + * 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); + + SET_FLAG(rt2x00dev, FIRMWARE_LOADED); + + /* + * Initialize the remaining bits of the device + * that had to wait until the firmware was loaded. + */ + if (rt61pci_init_hw(rt2x00dev)) { + ERROR("Failed to initialize device.\n"); + return; + } + + rt61pci_open_debugfs(rt2x00dev); + + return; + +exit: + SET_FLAG(rt2x00dev, FIRMWARE_FAILED); +} + +static void rt61pci_free_dev(struct rt2x00_dev *rt2x00dev) +{ + /* + * Close debugfs entry. + */ + rt61pci_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); + + /* + * Release CSR memory. + */ + if (likely(rt2x00dev->csr_addr)) { + iounmap(rt2x00dev->csr_addr); + rt2x00dev->csr_addr = NULL; + } + + /* + * Free ieee80211_hw memory. + */ + if (likely(rt2x00dev->hwmodes)) { + kfree(rt2x00dev->hwmodes->channels); + kfree(rt2x00dev->hwmodes->rates); + kfree(rt2x00dev->hwmodes); + rt2x00dev->hwmodes = NULL; + } +} + +static int rt61pci_alloc_dev(struct pci_dev *pci_dev, + struct ieee80211_hw *hw) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + + rt2x00dev->dev = pci_dev; + rt2x00dev->lib_ops = &rt61pci_rt2x00_ops; + rt2x00dev->hw_ops = &rt61pci_mac80211_ops; + rt2x00dev->hw = hw; + + /* + * 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; + } + + /* + * Allocate eeprom data. + */ + if (rt61pci_alloc_eeprom(rt2x00dev)) + goto exit; + + /* + * Create workqueue. + */ + rt2x00dev->workqueue = create_singlethread_workqueue(DRV_NAME); + if (!rt2x00dev->workqueue) + goto exit; + + /* + * Initialize configuration work. + */ + INIT_DELAYED_WORK(&rt2x00dev->link.work, rt61pci_link_tuner); + + /* + * Reset current working type. + */ + rt2x00dev->interface.type = -EINVAL; + + /* + * Initialize scanning attributes. + */ + rt2x00dev->scan = NULL; + + /* + * Allocate ring array. + */ + if (rt61pci_alloc_rings(rt2x00dev)) + goto exit; + + /* + * Initialize hardware. + */ + if (rt61pci_init_eeprom(rt2x00dev)) { + ERROR("Failed to initialize device.\n"); + goto exit; + } + + /* + * Request firmware and wait with further + * initializing of the card until the firmware + * has been loaded. + */ + if (rt2x00lib_load_firmware(rt2x00dev, &rt2x00dev_pci(rt2x00dev)->dev, + rt61pci_load_firmware)) + goto exit; + + return 0; + +exit: + rt61pci_free_dev(rt2x00dev); + + return -ENODEV; +} + +/* + * PCI driver handlers. + */ +static int rt61pci_probe(struct pci_dev *pci_dev, + const struct pci_device_id *id) +{ + struct ieee80211_hw *hw; + int status; + + status = pci_request_regions(pci_dev, pci_name(pci_dev)); + if (status) { + ERROR("PCI request regions failed.\n"); + return status; + } + + status = pci_enable_device(pci_dev); + if (status) { + 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"); + status = -EIO; + goto exit_disable_device; + } + + hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), + &rt61pci_mac80211_ops); + if (!hw) { + ERROR("Failed to allocate hardware.\n"); + status = -ENOMEM; + goto exit_disable_device; + } + + pci_set_drvdata(pci_dev, hw); + + status = rt61pci_alloc_dev(pci_dev, hw); + if (status) { + ERROR("Failed to allocate device.\n"); + goto exit_free_device; + } + + return 0; + +exit_free_device: + ieee80211_free_hw(hw); + +exit_disable_device: + if (status != -EBUSY) + pci_disable_device(pci_dev); + +exit_release_regions: + pci_release_regions(pci_dev); + + pci_set_drvdata(pci_dev, NULL); + + return status; +} + +static void rt61pci_remove(struct pci_dev *pci_dev) +{ + struct ieee80211_hw *hw = pci_get_drvdata(pci_dev); + struct rt2x00_dev *rt2x00dev = hw->priv; + + /* + * Uninitialize the 80211 stack data. + */ + ieee80211_unregister_hw(hw); + + /* + * Uninitialize and free the rt61pci driver data. + */ + rt61pci_disable_radio(rt2x00dev); + rt61pci_uninitialize(rt2x00dev); + rt61pci_free_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); +} + +#ifdef CONFIG_PM +static int rt61pci_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 status; + + NOTICE("Going to sleep.\n"); + + /* + * Disable the radio. + */ + rt61pci_disable_radio(rt2x00dev); + + /* + * Set device mode to sleep for power management. + */ + status = rt61pci_set_state(rt2x00dev, STATE_SLEEP); + if (status) + return status; + + /* + * Uninitialize device and hardware. + */ + rt61pci_uninitialize(rt2x00dev); + rt61pci_free_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)); +} + +static int rt61pci_resume(struct pci_dev *pci_dev) +{ + struct ieee80211_hw *hw = pci_get_drvdata(pci_dev); + struct rt2x00_dev *rt2x00dev = hw->priv; + int status; + + 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. + */ + status = rt61pci_alloc_dev(pci_dev, hw); + if (status) { + ERROR("Failed to allocate device.\n"); + return status; + } + + /* + * Set device mode to awake for power management. + */ + status = rt61pci_set_state(rt2x00dev, STATE_AWAKE); + if (status) + return status; + + return 0; +} +#endif /* CONFIG_PM */ + +/* + * 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) }, + /* RT2561 v2 */ + { PCI_DEVICE(0x1814, 0x0302) }, + /* RT2661 */ + { PCI_DEVICE(0x1814, 0x0401) }, + { 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 = rt61pci_probe, + .remove = __devexit_p(rt61pci_remove), +#ifdef CONFIG_PM + .suspend = rt61pci_suspend, + .resume = rt61pci_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 -puN /dev/null drivers/net/wireless/mac80211/rt2x00/rt61pci.h --- /dev/null +++ a/drivers/net/wireless/mac80211/rt2x00/rt61pci.h @@ -0,0 +1,1361 @@ +/* + 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); + +/* + * Radio control functions. + */ +static int rt61pci_enable_radio(struct rt2x00_dev *rt2x00dev); +static void rt61pci_disable_radio(struct rt2x00_dev *rt2x00dev); + +/* + * Interrupt functions. + */ +static void rt61pci_rxdone(struct work_struct *work); +static void rt61pci_txdone(struct work_struct *work); +static irqreturn_t rt61pci_interrupt(int irq, void *dev_instance); + +#endif /* RT61PCI_H */ diff -puN /dev/null drivers/net/wireless/mac80211/rt2x00/rt73usb.c --- /dev/null +++ a/drivers/net/wireless/mac80211/rt2x00/rt73usb.c @@ -0,0 +1,3024 @@ +/* + 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: RT2573. + */ + +/* + * Set enviroment defines for rt2x00.h + */ +#define DRV_NAME "rt73usb" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt2x00.h" +#include "rt2x00lib.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, ®, 4, 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, ®, 4, 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); +} + +#if defined(CONFIG_RT2X00_DEBUGFS) || defined(CONFIG_RT2X00_DEBUGFS_MODULE) +#include "rt2x00debug.h" + +#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 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), + .length = CSR_REG_SIZE, + }, + .reg_eeprom = { + .read = rt73usb_read_eeprom, + .write = rt73usb_write_eeprom, + .word_size = sizeof(u16), + .length = EEPROM_SIZE, + }, + .reg_bbp = { + .read = rt73usb_read_bbp, + .write = rt73usb_write_bbp, + .word_size = sizeof(u8), + .length = BBP_SIZE, + }, +}; + +static void rt73usb_open_debugfs(struct rt2x00_dev *rt2x00dev) +{ + if (!rt2x00debug_register(&rt73usb_rt2x00debug, + rt2x00dev->hw->wiphy, rt2x00dev)) + ERROR("Failed to register debug handler.\n"); +} + +static void rt73usb_close_debugfs(struct rt2x00_dev *rt2x00dev) +{ + rt2x00debug_deregister(&rt2x00dev->debugfs_data); +} +#else /* CONFIG_RT2X00_DEBUGFS */ +static inline void rt73usb_open_debugfs(struct rt2x00_dev *rt2x00dev){} +static inline void rt73usb_close_debugfs(struct rt2x00_dev *rt2x00dev){} +#endif /* CONFIG_RT2X00_DEBUGFS */ + +/* + * Configuration handlers. + */ +static void rt73usb_config_bssid(struct rt2x00_dev *rt2x00dev, u8 *bssid) +{ + u32 reg; + + /* + * The BSSID is passed to us as an array of bytes, + * that array is little endian, so no need for byte ordering. + * We only need to set the BSS ID MASK at the correct offset. + */ + rt2x00_register_multiwrite(rt2x00dev, MAC_CSR4, bssid, ETH_ALEN); + + rt2x00_register_read(rt2x00dev, MAC_CSR5, ®); + rt2x00_set_field32(®, MAC_CSR5_BSS_ID_MASK, 3); + rt2x00_register_write(rt2x00dev, MAC_CSR5, 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 rf2, int channel, int freq, int txpower) +{ + u8 reg = 0; + u32 rf1 = rt2x00dev->rf1; + 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)) { + 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; + + /* + * The MAC address is passed to us as an array of bytes, + * that array is little endian, so no need for byte ordering. + * We only need to set the MAC_CSR3_UNICAST_TO_ME_MASK + * at the correct offset. + */ + rt2x00_register_multiwrite(rt2x00dev, MAC_CSR2, addr, ETH_ALEN); + + rt2x00_register_read(rt2x00dev, MAC_CSR3, ®); + rt2x00_set_field32(®, MAC_CSR3_UNICAST_TO_ME_MASK, 0xff); + rt2x00_register_write(rt2x00dev, MAC_CSR3, 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_alloc_dma_ring(struct rt2x00_dev *rt2x00dev, + enum ring_index ring_type, const u16 max_entries, + const u16 data_size, const u16 desc_size) +{ + struct data_ring *ring = &rt2x00dev->ring[ring_type]; + 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. + */ + 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 (ring_type == RING_RX) { + ring->entry[i].skb = dev_alloc_skb(NET_IP_ALIGN + + ring->data_size + ring->desc_size); + if (!ring->entry[i].skb) + return -ENOMEM; + + skb_reserve(ring->entry[i].skb, NET_IP_ALIGN); + skb_put(ring->entry[i].skb, + ring->data_size + ring->desc_size); + } + } + + return 0; +} + +static void rt73usb_free_ring(struct rt2x00_dev *rt2x00dev, + enum ring_index ring_type) +{ + struct data_ring *ring = &rt2x00dev->ring[ring_type]; + 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_type == RING_RX) + kfree_skb(ring->entry[i].skb); + } + + kfree(ring->entry); + ring->entry = NULL; +} + +static int rt73usb_allocate_dma_rings(struct rt2x00_dev *rt2x00dev) +{ + if (rt73usb_alloc_dma_ring(rt2x00dev, RING_RX, + RX_ENTRIES, DATA_FRAME_SIZE, RXD_DESC_SIZE) || + rt73usb_alloc_dma_ring(rt2x00dev, RING_AC_VO, + TX_ENTRIES, DATA_FRAME_SIZE, TXD_DESC_SIZE) || + rt73usb_alloc_dma_ring(rt2x00dev, RING_AC_VI, + TX_ENTRIES, DATA_FRAME_SIZE, TXD_DESC_SIZE) || + rt73usb_alloc_dma_ring(rt2x00dev, RING_AC_BE, + TX_ENTRIES, DATA_FRAME_SIZE, TXD_DESC_SIZE) || + rt73usb_alloc_dma_ring(rt2x00dev, RING_AC_BK, + TX_ENTRIES, DATA_FRAME_SIZE, TXD_DESC_SIZE) || + rt73usb_alloc_dma_ring(rt2x00dev, RING_PRIO, + TX_ENTRIES, DATA_FRAME_SIZE, TXD_DESC_SIZE) || + rt73usb_alloc_dma_ring(rt2x00dev, RING_BEACON, + BEACON_ENTRIES, MGMT_FRAME_SIZE, TXD_DESC_SIZE)) { + return -ENOMEM; + } + + return 0; +} + +static void rt73usb_free_rings(struct rt2x00_dev *rt2x00dev) +{ + rt73usb_free_ring(rt2x00dev, RING_RX); + rt73usb_free_ring(rt2x00dev, RING_AC_VO); + rt73usb_free_ring(rt2x00dev, RING_AC_VI); + rt73usb_free_ring(rt2x00dev, RING_AC_BE); + rt73usb_free_ring(rt2x00dev, RING_AC_BK); + rt73usb_free_ring(rt2x00dev, RING_PRIO); + rt73usb_free_ring(rt2x00dev, RING_BEACON); +} + +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; +} + +/* + * Device initialization functions. + */ +static int rt73usb_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 (rt73usb_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: + rt73usb_free_rings(rt2x00dev); + + return -EIO; +} + +static void rt73usb_uninitialize(struct rt2x00_dev *rt2x00dev) +{ + if (!GET_FLAG(rt2x00dev, DEVICE_INITIALIZED)) + return; + + /* + * Cancel scanning. + */ + if (rt2x00dev->scan) + rt2x00_signal_scan(rt2x00dev->scan, SCANNING_CANCELLED); + + /* + * Flush out all pending work. + */ + flush_workqueue(rt2x00dev->workqueue); + + /* + * Free DMA rings. + */ + rt73usb_free_rings(rt2x00dev); + + CLEAR_FLAG(rt2x00dev, DEVICE_INITIALIZED); +} + +/* + * 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; + + /* + * Don't enable the radio twice. + */ + if (GET_FLAG(rt2x00dev, DEVICE_ENABLED_RADIO)) + return 0; + + /* + * Initialize all registers. + */ + if (rt73usb_init_rings(rt2x00dev) || + rt73usb_init_registers(rt2x00dev) || + rt73usb_init_bbp(rt2x00dev)) { + ERROR("Register initialization failed.\n"); + goto exit_fail; + } + + /* + * Determine channel change time. + */ + if (rt2x00lib_detect_channel_time(rt2x00dev)) + goto exit_fail; + + SET_FLAG(rt2x00dev, DEVICE_ENABLED_RADIO); + + /* + * Enable RX. + */ + rt73usb_toggle_rx(rt2x00dev, 1); + + 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); + + ieee80211_start_queues(rt2x00dev->hw); + + return 0; + +exit_fail: + rt73usb_uninitialize(rt2x00dev); + return -EIO; +} + +static void rt73usb_disable_radio(struct rt2x00_dev *rt2x00dev) +{ + struct data_ring *ring; + unsigned int i; + + if (!GET_FLAG(rt2x00dev, DEVICE_ENABLED_RADIO)) + return; + + ieee80211_stop_queues(rt2x00dev->hw); + + /* + * Disable LED + */ + rt73usb_disable_led(rt2x00dev); + + CLEAR_FLAG(rt2x00dev, DEVICE_ENABLED_RADIO); + + rt2x00_register_write(rt2x00dev, MAC_CSR10, 0x00001818); + + /* + * Disable synchronisation. + */ + rt2x00_register_write(rt2x00dev, TXRX_CSR9, 0); + + /* + * Cancel RX and TX. + */ + rt73usb_toggle_rx(rt2x00dev, 0); + + 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_desc *txd, struct ieee80211_hdr *ieee80211hdr, + unsigned int length, struct ieee80211_tx_control *control) +{ + struct data_ring *ring; + int tx_rate; + u32 word; + u32 duration; + u32 residual; + u16 length_high; + u16 length_low; + u16 frame_control; + u16 seq_ctrl; + char rts_frame; + char ofdm_rate; + char req_timestamp; + char more_frag; + char ifs; + char queue; + u8 signal; + u8 service; + u8 bitrate; + + /* + * We require the ring structure this packet is being send to. + */ + ring = rt2x00_get_ring(rt2x00dev, control->queue); + if (unlikely(!ring)) + return; + + /* + * Read required fields from ieee80211 header. + */ + frame_control = le16_to_cpu(ieee80211hdr->frame_control); + seq_ctrl = le16_to_cpu(ieee80211hdr->seq_ctrl); + + /* + * Check if this frame is a RTS frame. + */ + rts_frame = is_rts_frame(frame_control); + + /* + * Check which rate should be used for this frame. + */ + if (rts_frame && control->rts_cts_rate) + tx_rate = control->rts_cts_rate; + else + tx_rate = control->tx_rate; + + /* + * Are we working with OFDM rates. + */ + ofdm_rate = !!(DEVICE_GET_RATE_FIELD(tx_rate, RATEMASK) & + DEV_OFDM_RATE); + + /* + * Check if more fragments will follow this frame. + */ + more_frag = !!(ieee80211_get_morefrag(ieee80211hdr)); + + /* + * Beacons and probe responses require the tsf timestamp + * to be inserted into the frame. + */ + req_timestamp = !!(control->queue == IEEE80211_TX_QUEUE_BEACON || + is_probe_resp(frame_control)); + + /* + * 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) || rts_frame) + ifs = IFS_SIFS; + else + ifs = IFS_BACKOFF; + + /* + * Determine queue identification number. + */ + if (control->queue < rt2x00dev->hw->queues) + queue = control->queue; + else + queue = 15; + + /* + * How the length should be processed depends + * on if we are working with OFDM rates or not. + */ + if (ofdm_rate) { + residual = 0; + length_high = ((length + FCS_LEN) >> 6) & 0x3f; + 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++; + + length_high = duration >> 8; + length_low = duration & 0xff; + } + + /* + * Create the signal and service values. + */ + signal = DEVICE_GET_RATE_FIELD(tx_rate, PLCP); + if (DEVICE_GET_RATE_FIELD(tx_rate, PREAMBLE)) + signal |= 0x08; + + service = 0x04; + if (residual <= (8 % 11)) + service |= 0x80; + + /* + * Start writing the descriptor words. + */ + rt2x00_desc_read(txd, 1, &word); + rt2x00_set_field32(&word, TXD_W1_HOST_Q_ID, queue); + rt2x00_set_field32(&word, TXD_W1_AIFSN, ring->tx_params.aifs); + rt2x00_set_field32(&word, TXD_W1_CWMIN, ring->tx_params.cw_min); + rt2x00_set_field32(&word, TXD_W1_CWMAX, 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, signal); + rt2x00_set_field32(&word, TXD_W2_PLCP_SERVICE, service); + rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_LOW, length_low); + rt2x00_set_field32(&word, TXD_W2_PLCP_LENGTH_HIGH, 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, more_frag); + rt2x00_set_field32(&word, TXD_W0_ACK, + !(control->flags & IEEE80211_TXCTL_NO_ACK)); + rt2x00_set_field32(&word, TXD_W0_TIMESTAMP, req_timestamp); + rt2x00_set_field32(&word, TXD_W0_OFDM, ofdm_rate); + rt2x00_set_field32(&word, TXD_W0_IFS, 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 int rt73usb_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; + u16 fc; + + 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; + rt73usb_write_tx_desc(rt2x00dev, txd, ieee80211hdr, skb->len, control); + memcpy(&entry->tx_status.control, control, sizeof(*control)); + entry->skb = skb; + + fc = le16_to_cpu(ieee80211hdr->frame_control); + if (is_cts_frame(fc) || is_rts_frame(fc)) + SET_FLAG(entry, ENTRY_RTS_CTS_FRAME); + + /* + * 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, + rt73usb_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; +} + +/* + * Interrupt functions. + */ +static void rt73usb_interrupt_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); + } +} + +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; + struct sk_buff *skb; + 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; + + /* + * Allocate a new sk_buffer to replace the sk_buffer + * that has been filled in by the device. + */ + skb = dev_alloc_skb(NET_IP_ALIGN + ring->data_size + ring->desc_size); + if (!skb) + return; + + skb_reserve(skb, NET_IP_ALIGN); + skb_put(skb, ring->data_size + ring->desc_size); + + urb->transfer_buffer = skb->data; + urb->transfer_buffer_length = skb->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); + rt2x00lib_update_rx_stats(rt2x00dev, signal, rssi, ofdm); + + /* + * Trim the skb_buffer to only contain the valid + * frame data (so ignore the device's descriptor). + */ + size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT); + skb_pull(entry->skb, ring->desc_size); + skb_trim(entry->skb, size); + + /* + * Send frame to stack, and set the new sk_buffer + * in its place to be able to receive new frames + * in the new buffer. + */ + ieee80211_rx_irqsafe(rt2x00dev->hw, entry->skb, &rt2x00dev->rx_status); + entry->skb = skb; + + /* + * Update link statistics + */ + rt2x00_update_link_rssi(&rt2x00dev->link, rt2x00dev->rx_status.ssi); + +skip_entry: + if (!GET_FLAG(ring->rt2x00dev, DEVICE_ENABLED_RADIO)) + return; + + SET_FLAG(entry, ENTRY_OWNER_NIC); + usb_submit_urb(urb, GFP_ATOMIC); + rt2x00_ring_index_inc(ring); +} + +static void rt73usb_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; + int ack; + + 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); + + ack = rt2x00_get_field32(word, TXD_W0_ACK); + tx_status = !urb->status ? TX_SUCCESS : TX_FAIL_RETRY; + rt2x00lib_update_tx_stats(entry, tx_status, ack, 0); + + skb_pull(entry->skb, ring->desc_size); + + /* + * If this is not an RTS frame send the tx_status to mac80211, + * that method also cleans up the skb structure. When this + * is a RTS frame, that it is our job to clean this structure up. + */ + if (!GET_FLAG(entry, ENTRY_RTS_CTS_FRAME)) + ieee80211_tx_status_irqsafe(rt2x00dev->hw, + entry->skb, &entry->tx_status); + else + dev_kfree_skb(entry->skb); + + entry->skb = NULL; + + CLEAR_FLAG(entry, ENTRY_RTS_CTS_FRAME); + + rt2x00_ring_index_done_inc(entry->ring); + + /* + * Check if we are waiting on an empty queue + * to start scanning. + */ + if (rt2x00dev->scan && + rt2x00_ring_empty(&rt2x00dev->ring[RING_AC_VO]) && + rt2x00_ring_empty(&rt2x00dev->ring[RING_AC_VI]) && + rt2x00_ring_empty(&rt2x00dev->ring[RING_AC_BE]) && + rt2x00_ring_empty(&rt2x00dev->ring[RING_AC_BK]) && + rt2x00_ring_empty(&rt2x00dev->ring[RING_PRIO])) + rt2x00_signal_scan(rt2x00dev->scan, SCANNING_READY); + + /* + * 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); +} + +/* + * 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 int rt73usb_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 = &rt2x00dev->ring[RING_BEACON]; + struct data_entry *beacon; + struct data_entry *guardian; + int length; + u32 reg; + + /* + * 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); + rt73usb_write_tx_desc(rt2x00dev, + (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, + rt73usb_interrupt_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, + rt73usb_interrupt_beacondone, + guardian); + + /* + * Send out the guardian byte. + */ + usb_submit_urb(guardian->priv, GFP_ATOMIC); + + /* + * Enable beacon generation. + */ + 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); + } + + return 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, + .passive_scan = rt2x00lib_passive_scan, + .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 = rt73usb_beacon_update, +}; + +static const struct rt2x00lib_ops rt73usb_rt2x00_ops = { + .initialize = rt73usb_initialize, + .uninitialize = rt73usb_uninitialize, + .enable_radio = rt73usb_enable_radio, + .disable_radio = rt73usb_disable_radio, + .toggle_rx = rt73usb_toggle_rx, + .write_tx_data = rt73usb_write_tx_data, + .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, +}; + +/* + * 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_alloc_rings(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + + 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; +} + +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, RT73, 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."); + 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 int rt73usb_init_hw_mac(struct rt2x00_dev *rt2x00dev) +{ + u8 *addr; + + /* + * Get the pointer to the MAC address in the EEPROM. + */ + addr = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); + + /* + * Check if a valid MAC address is present. + */ + if (!is_valid_ether_addr(addr)) { + ERROR("Invalid MAC address: " MAC_FMT ".\n", MAC_ARG(addr)); + return -EINVAL; + } + + /* + * Write MAC address to register. + */ + rt73usb_config_mac_addr(rt2x00dev, addr); + + /* + * Copy MAC address to the hw structure. + */ + SET_IEEE80211_PERM_ADDR(rt2x00dev->hw, addr); + + return 0; +} + +static void rt73usb_init_hw_channels(struct rt2x00_dev *rt2x00dev, + struct ieee80211_channel *channels) +{ + unsigned int i; + u32 rf2_base; + u16 eeprom; + static const struct { + unsigned int chip; + u32 val[3]; + } rf[] = { + { RF5226, { 0x00002c0c, 0x00068255 } }, + { RF2528, { 0x00002c0c, 0x00068255 } }, + { RF5225, { 0x00002ccc, 0x00000000 } }, + { RF2527, { 0x00002ccc, 0x00068455 } }, + }; + static const u32 vals[] = { + 0x00000786, 0x00000786, 0x0000078a, 0x0000078a, + 0x0000078e, 0x0000078e, 0x00000792, 0x00000792, + 0x00000796, 0x00000796, 0x0000079a, 0x0000079a, + 0x0000079e, 0x000007a2 + }; + + /* + * Channel initialization. + * First we set the basic variables. + */ + for (i = 0; i < 13; i++) { + channels[i].chan = i + 1; + channels[i].freq = 2407 + ((i + 1) * 5); + channels[i].flag = IEEE80211_CHAN_W_IBSS | + IEEE80211_CHAN_W_ACTIVE_SCAN | IEEE80211_CHAN_W_SCAN; + channels[i].antenna_max = 0xff; + } + + channels[13].chan = 14; + channels[13].freq = 2484; + channels[13].flag = IEEE80211_CHAN_W_IBSS | + IEEE80211_CHAN_W_ACTIVE_SCAN | IEEE80211_CHAN_W_SCAN; + channels[13].antenna_max = 0xff; + + if (rt2x00_rf(&rt2x00dev->chip, RF5225) || + rt2x00_rf(&rt2x00dev->chip, RF5226)) { + for (i = 14; i < 38; 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); + channels[i].freq = ((i - 13) + 1000) * 5; + channels[i].flag = IEEE80211_CHAN_W_IBSS | + IEEE80211_CHAN_W_ACTIVE_SCAN | + IEEE80211_CHAN_W_SCAN; + channels[i].antenna_max = 0xff; + } + } + + /* + * Set device specific value. + */ + rf2_base = 0; + if (rt2x00_rf(&rt2x00dev->chip, RF5225) || + rt2x00_rf(&rt2x00dev->chip, RF2527)) + rf2_base = 0x00004000; + + for (i = 0; i < ARRAY_SIZE(vals); i++) + channels[i].val = vals[i] | rf2_base; + + if (rt2x00_rf(&rt2x00dev->chip, RF5225) || + rt2x00_rf(&rt2x00dev->chip, RF5226)) { + static const u32 vals_a[] = { + 0x0000099a, 0x000009a2, 0x000009a6, 0x000009aa, + 0x000009ae, 0x000009b2, 0x000009ba, 0x000009be, + 0x00000a2a, 0x00000a2e, 0x00000a32, 0x00000a36, + 0x00000a3a, 0x00000a82, 0x00000a86, 0x00000a8a, + 0x00000a8e, 0x00000a92, 0x00000a9a, 0x00000aa2, + 0x00000aa6, 0x00000aae, 0x00000ab2, 0x00000ab6 + }; + + struct ieee80211_channel *chan = channels + 14; + + for (i = 0; i < ARRAY_SIZE(vals_a); i++) + (chan++)->val = vals_a[i]; + } + + /* + * Set TX power, each EEPROM TXpower entry + * contains the TXpower value for 2 channels. + */ + for (i = 0; i < EEPROM_TXPOWER_G_SIZE; i++) { + rt2x00_eeprom_read(rt2x00dev, + EEPROM_TXPOWER_G_START + i, &eeprom); + + channels[(i * 2)].power_level = TXPOWER_FROM_DEV( + rt2x00_get_field16(eeprom, EEPROM_TXPOWER_G_1)); + + channels[(i * 2) + 1].power_level = TXPOWER_FROM_DEV( + rt2x00_get_field16(eeprom, EEPROM_TXPOWER_G_2)); + } + + if (rt2x00_rf(&rt2x00dev->chip, RF5225) || + rt2x00_rf(&rt2x00dev->chip, RF5226)) { + for (i = 0; i < EEPROM_TXPOWER_A_SIZE; i++) { + rt2x00_eeprom_read(rt2x00dev, + EEPROM_TXPOWER_A_START + i, &eeprom); + + channels[(i * 2)].power_level = TXPOWER_FROM_DEV( + rt2x00_get_field16(eeprom, + EEPROM_TXPOWER_A_1)); + + channels[(i * 2) + 1].power_level = TXPOWER_FROM_DEV( + rt2x00_get_field16(eeprom, + EEPROM_TXPOWER_A_2)); + } + } + + /* + * Set device specific, but channel independent RF values. + */ + for (i = 0; i < ARRAY_SIZE(rf); i++) { + if (rt2x00_rf(&rt2x00dev->chip, rf[i].chip)) { + rt2x00dev->rf1 = rf[i].val[0]; + rt2x00dev->rf3 = rf[i].val[1]; + } + } +} + +static void rt73usb_init_hw_rates(struct rt2x00_dev *rt2x00dev, + struct ieee80211_rate *rates) +{ + /* + * Rates initialization. + */ + device_rate_entry(&rates[0], 10, 0x001, 0x00, IEEE80211_RATE_CCK); + device_rate_entry(&rates[1], 20, 0x003, 0x01, IEEE80211_RATE_CCK_2); + device_rate_entry(&rates[2], 55, 0x007, 0x02, IEEE80211_RATE_CCK_2); + device_rate_entry(&rates[3], 110, 0x00f, 0x03, IEEE80211_RATE_CCK_2); + device_rate_entry(&rates[4], 60, 0x01f, 0x0b, IEEE80211_RATE_OFDM); + device_rate_entry(&rates[5], 90, 0x03f, 0x0f, IEEE80211_RATE_OFDM); + device_rate_entry(&rates[6], 120, 0x07f, 0x0a, IEEE80211_RATE_OFDM); + device_rate_entry(&rates[7], 180, 0x0ff, 0x0e, IEEE80211_RATE_OFDM); + device_rate_entry(&rates[8], 240, 0x1ff, 0x09, IEEE80211_RATE_OFDM); + device_rate_entry(&rates[9], 360, 0x3ff, 0x0d, IEEE80211_RATE_OFDM); + device_rate_entry(&rates[10], 480, 0x7ff, 0x08, IEEE80211_RATE_OFDM); + device_rate_entry(&rates[11], 540, 0xfff, 0x0c, IEEE80211_RATE_OFDM); +} + +static int rt73usb_init_hw_modes(struct rt2x00_dev *rt2x00dev) +{ + struct ieee80211_channel *channels; + struct ieee80211_rate *rates; + int status = -ENOMEM; + int num_modes; + int num_channels; + + /* + * RF2527 and RF2528 only supports 802.11b & 802.11g, + * so we should allocate 14 OFDM channels, 4 CCK rates + * and 8 OFDM rates. + * RF5225 and RF5226 also supports 802.11a, so allocate an + * additional 24 5.2GHz channels. + */ + num_modes = 2; + num_channels = 14; + if (rt2x00_rf(&rt2x00dev->chip, RF5225) || + rt2x00_rf(&rt2x00dev->chip, RF5226)) { + num_modes = 3; + num_channels = 38; + } + + rt2x00dev->hwmodes = + kzalloc(sizeof(struct ieee80211_hw_mode) * num_modes, + GFP_KERNEL); + if (!rt2x00dev->hwmodes) + goto exit; + + channels = kzalloc(sizeof(struct ieee80211_channel) * num_channels, + GFP_KERNEL); + if (!channels) + goto exit_free_modes; + + rates = kzalloc(sizeof(struct ieee80211_rate) * 12, GFP_KERNEL); + if (!rates) + goto exit_free_channels; + + /* + * Initialize channels and rate arrays. + */ + rt73usb_init_hw_channels(rt2x00dev, channels); + rt73usb_init_hw_rates(rt2x00dev, rates); + + /* + * Intitialize 802.11b + * Rates: CCK. + * Channels: OFDM. + */ + rt2x00dev->hwmodes[HWMODE_B].mode = MODE_IEEE80211B; + rt2x00dev->hwmodes[HWMODE_B].num_channels = 14; + rt2x00dev->hwmodes[HWMODE_B].num_rates = 4; + rt2x00dev->hwmodes[HWMODE_B].channels = channels; + rt2x00dev->hwmodes[HWMODE_B].rates = rates; + + /* + * Intitialize 802.11g + * Rates: CCK, OFDM. + * Channels: OFDM. + */ + rt2x00dev->hwmodes[HWMODE_G].mode = MODE_IEEE80211G; + rt2x00dev->hwmodes[HWMODE_G].num_channels = 14; + rt2x00dev->hwmodes[HWMODE_G].num_rates = 12; + rt2x00dev->hwmodes[HWMODE_G].channels = channels; + rt2x00dev->hwmodes[HWMODE_G].rates = rates; + + /* + * Intitialize 802.11a + * Rates: OFDM. + * Channels: OFDM, UNII, HiperLAN2. + */ + if (num_channels == 3) { + rt2x00dev->hwmodes[HWMODE_A].mode = MODE_IEEE80211A; + rt2x00dev->hwmodes[HWMODE_A].num_channels = 24; + rt2x00dev->hwmodes[HWMODE_A].num_rates = 8; + rt2x00dev->hwmodes[HWMODE_A].channels = &channels[14]; + rt2x00dev->hwmodes[HWMODE_A].rates = &rates[4]; + } + + /* + * Register the working modes. + */ + status = ieee80211_register_hwmode(rt2x00dev->hw, + &rt2x00dev->hwmodes[HWMODE_G]); + if (status) + goto exit_free_rates; + + status = ieee80211_register_hwmode(rt2x00dev->hw, + &rt2x00dev->hwmodes[HWMODE_B]); + if (status) + goto exit_free_rates; + + if (num_modes == 3) { + status = ieee80211_register_hwmode(rt2x00dev->hw, + &rt2x00dev->hwmodes[HWMODE_A]); + if (status) + goto exit_free_rates; + } + + return 0; + +exit_free_rates: + kfree(rates); + +exit_free_channels: + kfree(channels); + +exit_free_modes: + kfree(rt2x00dev->hwmodes); + rt2x00dev->hwmodes = NULL; + +exit: + ERROR("Allocation ieee80211 modes failed.\n"); + return status; +} + +static int rt73usb_init_hw(struct rt2x00_dev *rt2x00dev) +{ + int status; + + if (GET_FLAG(rt2x00dev, DEVICE_INITIALIZED_HW)) + return 0; + + SET_IEEE80211_DEV(rt2x00dev->hw, &rt2x00dev_usb(rt2x00dev)->dev); + + /* + * Read MAC address from EEPROM. + */ + status = rt73usb_init_hw_mac(rt2x00dev); + if (status) + return status; + + /* + * 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; + + if (ieee80211_register_hw(rt2x00dev->hw)) + return -EIO; + + status = rt73usb_init_hw_modes(rt2x00dev); + if (status) { + ieee80211_unregister_hw(rt2x00dev->hw); + return status; + } + + SET_FLAG(rt2x00dev, DEVICE_INITIALIZED_HW); + + return 0; +} + +static void rt73usb_load_firmware(const struct firmware *fw, void *context) +{ + struct rt2x00_dev *rt2x00dev = context; + unsigned int i; + int status; + u32 reg; + u16 crc; + + if (!fw || !fw->size || !fw->data) { + ERROR("Failed to load Firmware.\n"); + goto exit; + } + + /* + * 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"); + goto exit; + } + + /* + * 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"); + goto exit; + } + + 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"); + goto exit; + } + + SET_FLAG(rt2x00dev, FIRMWARE_LOADED); + + /* + * Initialize the remaining bits of the device + * that had to wait until the firmware was loaded. + */ + if (rt73usb_init_hw(rt2x00dev)) { + ERROR("Failed to initialize device.\n"); + return; + } + + rt73usb_disable_led(rt2x00dev); + rt73usb_open_debugfs(rt2x00dev); + + return; + +exit: + SET_FLAG(rt2x00dev, FIRMWARE_FAILED); +} + +static void rt73usb_free_dev(struct rt2x00_dev *rt2x00dev) +{ + /* + * Close debugfs entry. + */ + rt73usb_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); + + /* + * Free ieee80211_hw memory. + */ + if (likely(rt2x00dev->hwmodes)) { + kfree(rt2x00dev->hwmodes->channels); + kfree(rt2x00dev->hwmodes->rates); + kfree(rt2x00dev->hwmodes); + rt2x00dev->hwmodes = NULL; + } +} + +static int rt73usb_alloc_dev(struct usb_interface *usb_intf, + struct ieee80211_hw *hw) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + + rt2x00dev->dev = usb_intf; + rt2x00dev->lib_ops = &rt73usb_rt2x00_ops; + rt2x00dev->hw_ops = &rt73usb_mac80211_ops; + rt2x00dev->hw = hw; + + /* + * Allocate eeprom data. + */ + if (rt73usb_alloc_eeprom(rt2x00dev)) + goto exit; + + /* + * Create workqueue. + */ + rt2x00dev->workqueue = create_singlethread_workqueue(DRV_NAME); + if (!rt2x00dev->workqueue) + return -ENODEV; + + /* + * Initialize configuration work. + */ + INIT_DELAYED_WORK(&rt2x00dev->link.work, rt73usb_link_tuner); + + /* + * Reset current working type. + */ + rt2x00dev->interface.type = -EINVAL; + + /* + * Intialize scanning attributes. + */ + rt2x00dev->scan = NULL; + + /* + * Allocate ring array. + */ + if (rt73usb_alloc_rings(rt2x00dev)) + goto exit; + + /* + * Initialize hardware. + */ + if (rt73usb_init_eeprom(rt2x00dev)) { + ERROR("Failed to initialize device.\n"); + goto exit; + } + + /* + * Request firmware and wait with further + * initializing of the card until the firmware + * has been loaded. + */ + if (rt2x00lib_load_firmware(rt2x00dev, &rt2x00dev_usb(rt2x00dev)->dev, + rt73usb_load_firmware)) + goto exit; + + return 0; + +exit: + rt73usb_free_dev(rt2x00dev); + + return -ENODEV; +} + +/* + * USB driver handlers. + */ +static int rt73usb_probe(struct usb_interface *usb_intf, + const struct usb_device_id *id) +{ + struct usb_device *usb_dev = interface_to_usbdev(usb_intf); + struct ieee80211_hw *hw; + int status; + + usb_dev = usb_get_dev(usb_dev); + + hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), + &rt73usb_mac80211_ops); + if (!hw) { + ERROR("Failed to allocate hardware.\n"); + status = -ENOMEM; + goto exit_put_device; + } + + usb_set_intfdata(usb_intf, hw); + + status = rt73usb_alloc_dev(usb_intf, hw); + if (status) { + ERROR("Failed to allocate device.\n"); + goto exit_free_device; + } + + return 0; + +exit_free_device: + ieee80211_free_hw(hw); + +exit_put_device: + usb_put_dev(usb_dev); + + return status; +} + +static void rt73usb_disconnect(struct usb_interface *usb_intf) +{ + struct ieee80211_hw *hw = usb_get_intfdata(usb_intf); + struct rt2x00_dev *rt2x00dev = hw->priv; + + /* + * Uninitialize the 80211 stack data. + */ + ieee80211_unregister_hw(hw); + + /* + * Uninitialize and free the rt73usb driver data. + */ + rt73usb_disable_radio(rt2x00dev); + rt73usb_uninitialize(rt2x00dev); + rt73usb_free_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)); +} + +#ifdef CONFIG_PM +static int rt73usb_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 status; + + NOTICE("Going to sleep.\n"); + + /* + * Disable the radio. + */ + rt73usb_disable_radio(rt2x00dev); + + /* + * Set device mode to sleep for power management. + */ + status = rt73usb_set_state(rt2x00dev, STATE_SLEEP); + if (status) + return status; + + /* + * Uninitialize device and hardware. + */ + rt73usb_uninitialize(rt2x00dev); + rt73usb_free_dev(rt2x00dev); + + /* + * Decrease usbdev refcount. + */ + usb_put_dev(interface_to_usbdev(usb_intf)); + + return 0; +} + +static int rt73usb_resume(struct usb_interface *usb_intf) +{ + struct ieee80211_hw *hw = usb_get_intfdata(usb_intf); + struct rt2x00_dev *rt2x00dev = hw->priv; + int status; + + NOTICE("Waking up.\n"); + + /* + * Increase usbdev refcount. + */ + usb_get_dev(interface_to_usbdev(usb_intf)); + + /* + * Initialize hardware. + */ + status = rt73usb_alloc_dev(usb_intf, hw); + if (status) { + ERROR("Failed to allocate device.\n"); + return status; + } + + /* + * Set device mode to awake for power management. + */ + status = rt73usb_set_state(rt2x00dev, STATE_AWAKE); + if (status) + return status; + + return 0; +} +#endif /* CONFIG_PM */ + +/* + * 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) }, + /* Askey */ + { USB_DEVICE(0x1690, 0x0722) }, + /* ASUS */ + { USB_DEVICE(0x0b05, 0x1723) }, + /* Belkin */ + { USB_DEVICE(0x050d, 0x7050) }, + { USB_DEVICE(0x050d, 0x705a) }, + { USB_DEVICE(0x050d, 0x905b) }, + /* Billionton */ + { USB_DEVICE(0x1631, 0xc019) }, + /* CNet */ + { USB_DEVICE(0x1371, 0x9022) }, + { USB_DEVICE(0x1371, 0x9032) }, + /* Conceptronic */ + { USB_DEVICE(0x14b2, 0x3c22) }, + /* D-Link */ + { USB_DEVICE(0x07d1, 0x3c03) }, + { USB_DEVICE(0x07d1, 0x3c04) }, + /* Gemtek */ + { USB_DEVICE(0x15a9, 0x0004) }, + /* Gigabyte */ + { USB_DEVICE(0x1044, 0x8008) }, + { USB_DEVICE(0x1044, 0x800a) }, + /* Huawei-3Com */ + { USB_DEVICE(0x1472, 0x0009) }, + /* Hercules */ + { USB_DEVICE(0x06f8, 0xe010) }, + { USB_DEVICE(0x06f8, 0xe020) }, + /* Linksys */ + { USB_DEVICE(0x13b1, 0x0020) }, + { USB_DEVICE(0x13b1, 0x0023) }, + /* MSI */ + { USB_DEVICE(0x0db0, 0x6877) }, + { USB_DEVICE(0x0db0, 0xa861) }, + { USB_DEVICE(0x0db0, 0xa874) }, + /* Ralink */ + { USB_DEVICE(0x148f, 0x2573) }, + { USB_DEVICE(0x148f, 0x2671) }, + /* Qcom */ + { USB_DEVICE(0x18e8, 0x6196) }, + { USB_DEVICE(0x18e8, 0x6229) }, + /* Sitecom */ + { USB_DEVICE(0x0df6, 0x9712) }, + { USB_DEVICE(0x0df6, 0x90ac) }, + /* Surecom */ + { USB_DEVICE(0x0769, 0x31f3) }, + /* Planex */ + { USB_DEVICE(0x2019, 0xab01) }, + { 0, } +}; + +MODULE_AUTHOR(DRV_PROJECT); +MODULE_VERSION(DRV_VERSION); +MODULE_DESCRIPTION("Ralink RT73 USB Wireless LAN driver."); +MODULE_SUPPORTED_DEVICE("Ralink RT2573 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 = rt73usb_probe, + .disconnect = rt73usb_disconnect, +#ifdef CONFIG_PM + .suspend = rt73usb_suspend, + .resume = rt73usb_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 -puN /dev/null drivers/net/wireless/mac80211/rt2x00/rt73usb.h --- /dev/null +++ a/drivers/net/wireless/mac80211/rt2x00/rt73usb.h @@ -0,0 +1,945 @@ +/* + 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: RT2573. + */ + +#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); + +/* + * Radio control functions. + */ +static int rt73usb_enable_radio(struct rt2x00_dev *rt2x00dev); +static void rt73usb_disable_radio(struct rt2x00_dev *rt2x00dev); + +/* + * Interrupt functions. + */ +static void rt73usb_interrupt_beacondone(struct urb *urb); +static void rt73usb_interrupt_rxdone(struct urb *urb); +static void rt73usb_interrupt_txdone(struct urb *urb); + +#endif /* RT73USB_H */ diff -puN /dev/null drivers/net/wireless/mac80211/rtl818x/Kconfig --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/rtl818x/Makefile --- /dev/null +++ a/drivers/net/wireless/mac80211/rtl818x/Makefile @@ -0,0 +1,2 @@ +rtl8187-objs := rtl8187_dev.o rtl8187_rtl8225.o +obj-$(CONFIG_RTL8187) += rtl8187.o diff -puN /dev/null drivers/net/wireless/mac80211/rtl818x/rtl8187.h --- /dev/null +++ a/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 u8 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 u8 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 -puN /dev/null drivers/net/wireless/mac80211/rtl818x/rtl8187_dev.c --- /dev/null +++ a/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 -1; + } + + 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 -puN /dev/null drivers/net/wireless/mac80211/rtl818x/rtl8187_rtl8225.c --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/rtl818x/rtl8187_rtl8225.h --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/rtl818x/rtl818x.h --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/zd1211rw/Kconfig --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/zd1211rw/Makefile --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/zd1211rw/zd_chip.c --- /dev/null +++ a/drivers/net/wireless/mac80211/zd1211rw/zd_chip.c @@ -0,0 +1,1676 @@ +/* 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 */ +static int patch_cr157(struct zd_chip *chip) +{ + int r; + u32 value; + + if (!chip->patch_cr157) + return 0; + + 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 >> 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, int channel) +{ + struct zd_ioreq16 ioreqs[] = { + { CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 }, + { CR47, 0x1e }, + }; + + if (!chip->patch_6m_band_edge || !chip->rf.patch_6m_band_edge) + return 0; + + /* 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 }, + { CR123, 0x27 }, { 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)); + 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 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_rx(struct zd_chip *chip) +{ + int r; + + mutex_lock(&chip->mutex); + r = zd_usb_enable_rx(&chip->usb); + mutex_unlock(&chip->mutex); + return r; +} + +void zd_chip_disable_rx(struct zd_chip *chip) +{ + mutex_lock(&chip->mutex); + zd_usb_disable_rx(&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 -puN /dev/null drivers/net/wireless/mac80211/zd1211rw/zd_chip.h --- /dev/null +++ a/drivers/net/wireless/mac80211/zd1211rw/zd_chip.h @@ -0,0 +1,909 @@ +/* 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_PHY_REG E2P_DATA(0x1a) +#define E2P_DEVICE_VER E2P_DATA(0x20) +#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_rx(struct zd_chip *chip); +void zd_chip_disable_rx(struct zd_chip *chip); +int zd_chip_enable_hwint(struct zd_chip *chip); +int zd_chip_disable_hwint(struct zd_chip *chip); + +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 -puN /dev/null drivers/net/wireless/mac80211/zd1211rw/zd_def.h --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/zd1211rw/zd_ieee80211.h --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/zd1211rw/zd_mac.c --- /dev/null +++ a/drivers/net/wireless/mac80211/zd1211rw/zd_mac.c @@ -0,0 +1,697 @@ +/* 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" + +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_rx(chip); + if (r < 0) + goto disable_radio; + r = zd_chip_enable_hwint(chip); + if (r < 0) + goto disable_rx; + + housekeeping_enable(mac); + return 0; +disable_rx: + zd_chip_disable_rx(chip); +disable_radio: + zd_chip_switch_radio_off(chip); +disable_int: + zd_chip_disable_int(chip); +out: + return r; +} + +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; + + /* + * 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_rx(chip); + housekeeping_disable(mac); + + zd_chip_disable_hwint(chip); + zd_chip_switch_radio_off(chip); + zd_chip_disable_int(chip); + + while ((skb = skb_dequeue(&mac->tx_queue))) { + struct ieee80211_tx_control *control = + *(struct ieee80211_tx_control **)skb->cb; + kfree(control); + kfree_skb(skb); + } + + return 0; +} + +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; +} + +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); + struct ieee80211_tx_control *control_copy; + int r; + + r = fill_ctrlset(mac, skb, control); + if (r) + return r; + r = zd_usb_tx(&mac->chip.usb, skb->data, skb->len); + if (r) + return r; + + if (control->flags & IEEE80211_TXCTL_NO_ACK) { + kfree_skb(skb); + return 0; + } + + control_copy = kmalloc(sizeof(*control_copy), GFP_ATOMIC); + if (control_copy) + memcpy(control_copy, control, sizeof(*control_copy)); + + *(struct ieee80211_tx_control **)skb->cb = control_copy; + skb_pull(skb, sizeof(struct zd_ctrlset)); + skb_queue_tail(&mac->tx_queue, skb); + return 0; +} + +void zd_mac_tx_failed(struct ieee80211_hw *dev) +{ + struct zd_mac *mac = zd_dev_mac(dev); + struct ieee80211_tx_control *control; + struct sk_buff *skb; + + skb = skb_dequeue(&mac->tx_queue); + if (!skb) + return; + + control = *(struct ieee80211_tx_control **)skb->cb; + if (control) { + struct ieee80211_tx_status status = {{0}}; + memcpy(&status.control, control, sizeof(status.control)); + ieee80211_tx_status_irqsafe(dev, skb, &status); + kfree(control); + } else + kfree_skb(skb); + + return; +} + +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; +} + +static int filter_ack(struct ieee80211_hw *dev, struct ieee80211_hdr *rx_hdr, + struct ieee80211_rx_status *stats) +{ + struct zd_mac *mac = zd_dev_mac(dev); + u16 fc = le16_to_cpu(rx_hdr->frame_control); + struct sk_buff *skb; + struct ieee80211_hdr *tx_hdr; + struct ieee80211_tx_control *control; + struct ieee80211_tx_status status = {{0}}; + + if ((fc & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) != + (IEEE80211_FTYPE_CTL | IEEE80211_STYPE_ACK)) + return 0; + + spin_lock(&mac->tx_queue.lock); + + skb = skb_peek(&mac->tx_queue); + if (!skb) { + spin_unlock(&mac->tx_queue.lock); + return 1; + } + + tx_hdr = (struct ieee80211_hdr *) skb->data; + + if (!compare_ether_addr(tx_hdr->addr2, rx_hdr->addr1)) + skb = __skb_dequeue(&mac->tx_queue); + else { + spin_unlock(&mac->tx_queue.lock); + return 1; + } + + spin_unlock(&mac->tx_queue.lock); + + control = *(struct ieee80211_tx_control **)skb->cb; + if (control) { + memcpy(&status.control, control, sizeof(status.control)); + status.flags = IEEE80211_TX_STATUS_ACK; + status.ack_signal = stats->ssi; + ieee80211_tx_status_irqsafe(dev, skb, &status); + kfree(control); + } else + kfree_skb(skb); + + 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)) + return 0; + + skb = dev_alloc_skb(sizeof(struct zd_rt_hdr) + length); + if (!skb) + return -ENOMEM; + 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_STA: + mac->mode = conf->type; + break; + default: + return -1; + } + + 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); + + 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; + } + } + + skb_queue_head_init(&mac->tx_queue); + 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 -puN /dev/null drivers/net/wireless/mac80211/zd1211rw/zd_mac.h --- /dev/null +++ a/drivers/net/wireless/mac80211/zd1211rw/zd_mac.h @@ -0,0 +1,250 @@ +/* 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; +}; + +#define ZD_MAC_STATS_BUFFER_SIZE 16 + +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; + 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); + +#ifdef DEBUG +void zd_dump_rx_status(const struct rx_status *status); +#else +#define zd_dump_rx_status(status) +#endif /* DEBUG */ + +/* 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} +}; + +#endif /* _ZD_MAC_H */ diff -puN /dev/null drivers/net/wireless/mac80211/zd1211rw/zd_rf.c --- /dev/null +++ a/drivers/net/wireless/mac80211/zd1211rw/zd_rf.c @@ -0,0 +1,156 @@ +/* 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; +} diff -puN /dev/null drivers/net/wireless/mac80211/zd1211rw/zd_rf.h --- /dev/null +++ a/drivers/net/wireless/mac80211/zd1211rw/zd_rf.h @@ -0,0 +1,81 @@ +/* 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; + /* + * Whether this RF should patch the 6M band edge + * (assuming E2P_POD agrees) + */ + u8 patch_6m_band_edge:1; + + /* 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); +}; + +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); + +/* 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 -puN /dev/null drivers/net/wireless/mac80211/zd1211rw/zd_rf_al2230.c --- /dev/null +++ a/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 = 1; + return 0; +} diff -puN /dev/null drivers/net/wireless/mac80211/zd1211rw/zd_rf_al7230b.c --- /dev/null +++ a/drivers/net/wireless/mac80211/zd1211rw/zd_rf_al7230b.c @@ -0,0 +1,274 @@ +/* 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 int al7230b_init_hw(struct zd_rf *rf) +{ + int i, 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[] = { + /* PLL_ON */ + { CR251, 0x3f }, + { 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_rfwrite_cr_locked(chip, 0x09ec04); + if (r) + return r; + r = zd_rfwrite_cr_locked(chip, 0x8cccc8); + if (r) + return r; + + for (i = 0; i < ARRAY_SIZE(std_rv); i++) { + r = zd_rfwrite_cr_locked(chip, std_rv[i]); + if (r) + return r; + } + + r = zd_rfwrite_cr_locked(chip, 0x3c9000); + if (r) + return r; + r = zd_rfwrite_cr_locked(chip, 0xbfffff); + if (r) + return r; + r = zd_rfwrite_cr_locked(chip, 0x700000); + if (r) + return r; + r = zd_rfwrite_cr_locked(chip, 0xf15d58); + if (r) + return r; + + r = zd_iowrite16a_locked(chip, ioreqs_2, ARRAY_SIZE(ioreqs_2)); + if (r) + return r; + + r = zd_rfwrite_cr_locked(chip, 0xf15d59); + if (r) + return r; + r = zd_rfwrite_cr_locked(chip, 0xf15d5c); + if (r) + return r; + r = zd_rfwrite_cr_locked(chip, 0xf15d58); + 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 al7230b_set_channel(struct zd_rf *rf, u8 channel) +{ + int i, r; + const u32 *rv = chan_rv[channel-1]; + struct zd_chip *chip = zd_rf_to_chip(rf); + + static const struct zd_ioreq16 ioreqs_1[] = { + { CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 }, + { CR38, 0x38 }, { CR136, 0xdf }, + }; + + static const struct zd_ioreq16 ioreqs_2[] = { + /* 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; + + for (i = 0; i < ARRAY_SIZE(std_rv); i++) { + r = zd_rfwrite_cr_locked(chip, std_rv[i]); + 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_1, ARRAY_SIZE(ioreqs_1)); + if (r) + return r; + + for (i = 0; i < 2; i++) { + r = zd_rfwrite_cr_locked(chip, rv[i]); + if (r) + return r; + } + + r = zd_rfwrite_cr_locked(chip, 0x3c9000); + if (r) + return r; + + return zd_iowrite16a_locked(chip, ioreqs_2, ARRAY_SIZE(ioreqs_2)); +} + +static int 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 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)); +} + +int zd_rf_init_al7230b(struct zd_rf *rf) +{ + struct zd_chip *chip = zd_rf_to_chip(rf); + + if (chip->is_zd1211b) { + dev_err(zd_chip_dev(chip), "AL7230B is currently not " + "supported for ZD1211B devices\n"); + return -ENODEV; + } + + rf->init_hw = al7230b_init_hw; + rf->set_channel = al7230b_set_channel; + rf->switch_radio_on = al7230b_switch_radio_on; + rf->switch_radio_off = al7230b_switch_radio_off; + rf->patch_6m_band_edge = 1; + return 0; +} diff -puN /dev/null drivers/net/wireless/mac80211/zd1211rw/zd_rf_rf2959.c --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/zd1211rw/zd_usb.c --- /dev/null +++ a/drivers/net/wireless/mac80211/zd1211rw/zd_usb.c @@ -0,0 +1,1336 @@ +/* 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(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 }, + /* "Driverless" devices that need ejecting */ + { USB_DEVICE(0x0ace, 0x2011), .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_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_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(URBS_COUNT, sizeof(struct urb *), GFP_KERNEL); + if (!urbs) + goto error; + for (i = 0; i < URBS_COUNT; i++) { + urbs[i] = alloc_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 = URBS_COUNT; + spin_unlock_irq(&rx->lock); + + for (i = 0; i < URBS_COUNT; i++) { + r = usb_submit_urb(urbs[i], GFP_KERNEL); + if (r) + goto error_submit; + } + + return 0; +error_submit: + for (i = 0; i < 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 < URBS_COUNT; i++) + free_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_urb(urbs[i]); + } + kfree(urbs); + + spin_lock_irqsave(&rx->lock, flags); + rx->urbs = NULL; + rx->urbs_count = 0; + spin_unlock_irqrestore(&rx->lock, flags); +} + +static void tx_urb_complete(struct urb *urb) +{ + int r; + + 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: + usb_buffer_free(urb->dev, urb->transfer_buffer_length, + urb->transfer_buffer, urb->transfer_dma); + usb_free_urb(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; + } +} + +/* Puts the frame on the USB endpoint. It doesn't wait for + * completion. The frame must contain the control set. + */ +int zd_usb_tx(struct zd_usb *usb, const u8 *frame, unsigned int length) +{ + int r; + struct usb_device *udev = zd_usb_to_usbdev(usb); + struct urb *urb; + void *buffer; + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) { + r = -ENOMEM; + goto out; + } + + buffer = usb_buffer_alloc(zd_usb_to_usbdev(usb), length, GFP_ATOMIC, + &urb->transfer_dma); + if (!buffer) { + r = -ENOMEM; + goto error_free_urb; + } + memcpy(buffer, frame, length); + + usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT), + buffer, length, tx_urb_complete, NULL); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + r = usb_submit_urb(urb, GFP_ATOMIC); + if (r) + goto error; + return 0; +error: + usb_buffer_free(zd_usb_to_usbdev(usb), length, buffer, + urb->transfer_dma); +error_free_urb: + usb_free_urb(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) +{ + /* FIXME: at this point we will allocate a fixed number of urb's for + * use in a cyclic scheme */ +} + +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 -puN /dev/null drivers/net/wireless/mac80211/zd1211rw/zd_usb.h --- /dev/null +++ a/drivers/net/wireless/mac80211/zd1211rw/zd_usb.h @@ -0,0 +1,241 @@ +/* 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 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 { + spinlock_t lock; +}; + +/* 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); + +int zd_usb_tx(struct zd_usb *usb, const u8 *frame, unsigned int length); + +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 -puN /dev/null drivers/net/wireless/mac80211/zd1211rw/zd_util.c --- /dev/null +++ a/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 -puN /dev/null drivers/net/wireless/mac80211/zd1211rw/zd_util.h --- /dev/null +++ a/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 -puN drivers/net/wireless/zd1211rw/Kconfig~git-wireless drivers/net/wireless/zd1211rw/Kconfig --- a/drivers/net/wireless/zd1211rw/Kconfig~git-wireless +++ a/drivers/net/wireless/zd1211rw/Kconfig @@ -1,6 +1,7 @@ config ZD1211RW tristate "ZyDAS ZD1211/ZD1211B USB-wireless support" - depends on USB && IEEE80211 && IEEE80211_SOFTMAC && NET_RADIO && EXPERIMENTAL + depends on USB && IEEE80211_SOFTMAC && WLAN_80211 && EXPERIMENTAL + select WIRELESS_EXT select FW_LOADER ---help--- This is an experimental driver for the ZyDAS ZD1211/ZD1211B wireless diff -puN /dev/null drivers/ssb/Kconfig --- /dev/null +++ a/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 -puN /dev/null drivers/ssb/Makefile --- /dev/null +++ a/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 -puN /dev/null drivers/ssb/driver_chipcommon.c --- /dev/null +++ a/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 -puN /dev/null drivers/ssb/driver_mipscore.c --- /dev/null +++ a/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 -puN /dev/null drivers/ssb/driver_pcicore.c --- /dev/null +++ a/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 -puN /dev/null drivers/ssb/main.c --- /dev/null +++ a/drivers/ssb/main.c @@ -0,0 +1,1042 @@ +/* + * 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_pci_exit(bus); + ssb_iounmap(bus); +} +EXPORT_SYMBOL(ssb_bus_unregister); + +static void ssb_release_dev(struct device *dev) +{ + kfree(dev); +} + +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(dev); + 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; + } + 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; + + ssb_printk(KERN_INFO PFX "Sonics Silicon Backplane found on "); + switch (bus->bustype) { + case SSB_BUSTYPE_SSB: + ssb_printk("address 0x%08lX\n", baseaddr); + break; + case SSB_BUSTYPE_PCI: +#ifdef CONFIG_SSB_PCIHOST + ssb_printk("PCI device %s\n", bus->host_pci->dev.bus_id); +#endif + break; + case SSB_BUSTYPE_PCMCIA: +#ifdef CONFIG_SSB_PCMCIAHOST + ssb_printk("PCMCIA device %s\n", bus->host_pcmcia->devname); +#endif + break; + } + + 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_unmap; + + /* Initialize basic system devices (if available) */ + ssb_chipcommon_init(&bus->chipco); + ssb_mipscore_init(&bus->mipscore); + ssb_pcicore_init(&bus->pcicore); + + /* 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_unmap: + ssb_iounmap(bus); +err_disable_xtal: + ssb_buses_unlock(); + ssb_pci_xtal(bus, SSB_GPIO_XTAL | SSB_GPIO_PLL, 0); + goto out; +} + +#ifdef CONFIG_SSB_PCIHOST +int ssb_bus_pcibus_register(struct ssb_bus *bus, + struct pci_dev *host_pci) +{ + int err; + + 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; + + 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; + + 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 -puN /dev/null drivers/ssb/pci.c --- /dev/null +++ a/drivers/ssb/pci.c @@ -0,0 +1,667 @@ +/* + * 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 out; + +out: + return err; +} diff -puN /dev/null drivers/ssb/pcihost_wrapper.c --- /dev/null +++ a/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 -puN /dev/null drivers/ssb/pcmcia.c --- /dev/null +++ a/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 -puN /dev/null drivers/ssb/scan.c --- /dev/null +++ a/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 -puN /dev/null drivers/ssb/ssb_private.h --- /dev/null +++ a/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 -puN drivers/usb/host/Kconfig~git-wireless drivers/usb/host/Kconfig --- a/drivers/usb/host/Kconfig~git-wireless +++ a/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 -puN drivers/usb/host/ohci-hcd.c~git-wireless drivers/usb/host/ohci-hcd.c --- a/drivers/usb/host/ohci-hcd.c~git-wireless +++ a/drivers/usb/host/ohci-hcd.c @@ -927,11 +927,17 @@ MODULE_LICENSE ("GPL"); #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 @@ -979,10 +985,20 @@ static int __init ohci_hcd_mod_init(void goto error_pci; #endif +#ifdef SSB_OHCI_DRIVER + retval = ssb_driver_register(&SSB_OHCI_DRIVER); + if (retval) + goto error_ssb; +#endif + return retval; /* Error path */ +#ifdef SSB_OHCI_DRIVER + error_ssb: +#endif #ifdef PCI_DRIVER + pci_unregister_driver(&PCI_DRIVER); error_pci: #endif #ifdef SA1111_DRIVER @@ -1008,6 +1024,9 @@ module_init(ohci_hcd_mod_init); static void __exit ohci_hcd_mod_exit(void) { +#ifdef SSB_OHCI_DRIVER + ssb_driver_unregister(&SSB_OHCI_DRIVER); +#endif #ifdef PCI_DRIVER pci_unregister_driver(&PCI_DRIVER); #endif diff -puN /dev/null drivers/usb/host/ohci-ssb.c --- /dev/null +++ a/drivers/usb/host/ohci-ssb.c @@ -0,0 +1,254 @@ +/* + * Sonics Silicon Backplane + * Broadcom USB-core OHCI driver + * + * Copyright 2007 Michael Buesch + * + * Derived from the OHCI-PCI driver + * Copyright 1999 Roman Weissgaerber + * Copyright 2000-2002 David Brownell + * Copyright 1999 Linus Torvalds + * Copyright 1999 Gregory P. Smith + * + * Derived from the USBcore related parts of Broadcom-SB + * Copyright 2005 Broadcom Corporation + * + * Licensed under the GNU/GPL. See COPYING for details. + */ + +#include + + +#define SSB_OHCI_TMSLOW_HOSTMODE (1 << 29) + +struct ssb_ohci_device { + struct ohci_hcd ohci; /* _must_ be at the beginning. */ + + u32 enable_flags; +}; + + +static inline +struct ssb_ohci_device * hcd_to_ssb_ohci(struct usb_hcd *hcd) +{ + return (struct ssb_ohci_device *)(hcd->hcd_priv); +} + + +static const struct ssb_device_id ssb_ohci_table[] = { + SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOSTDEV, SSB_ANY_REV), + SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOST, SSB_ANY_REV), + SSB_DEVTABLE_END +}; +MODULE_DEVICE_TABLE(ssb, ssb_ohci_table); + + +static int ssb_ohci_reset(struct usb_hcd *hcd) +{ + struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd); + struct ohci_hcd *ohci = &ohcidev->ohci; + int err; + + ohci_hcd_init(ohci); + err = ohci_init(ohci); + + return err; +} + +static int ssb_ohci_start(struct usb_hcd *hcd) +{ + struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd); + struct ohci_hcd *ohci = &ohcidev->ohci; + int err; + + err = ohci_run(ohci); + if (err < 0) { + ohci_err(ohci, "can't start\n"); + ohci_stop(hcd); + } + + return err; +} + +#ifdef CONFIG_PM +static int ssb_ohci_hcd_suspend(struct usb_hcd *hcd, pm_message_t message) +{ + struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd); + struct ohci_hcd *ohci = &ohcidev->ohci; + unsigned long flags; + + spin_lock_irqsave(&ohci->lock, flags); + + ohci_writel(ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable); + ohci_readl(ohci, &ohci->regs->intrdisable); /* commit write */ + + /* make sure snapshot being resumed re-enumerates everything */ + if (message.event == PM_EVENT_PRETHAW) + ohci_usb_reset(ohci); + + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + + spin_unlock_irqrestore(&ohci->lock, flags); + + return 0; +} + +static int ssb_ohci_hcd_resume(struct usb_hcd *hcd) +{ + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + usb_hcd_resume_root_hub(hcd); + return 0; +} +#endif /* CONFIG_PM */ + +static const struct hc_driver ssb_ohci_hc_driver = { + .description = "ssb-usb-ohci", + .product_desc = "SSB OHCI Controller", + .hcd_priv_size = sizeof(struct ssb_ohci_device), + + .irq = ohci_irq, + .flags = HCD_MEMORY | HCD_USB11, + + .reset = ssb_ohci_reset, + .start = ssb_ohci_start, + .stop = ohci_stop, + .shutdown = ohci_shutdown, + +#ifdef CONFIG_PM + .suspend = ssb_ohci_hcd_suspend, + .resume = ssb_ohci_hcd_resume, +#endif + + .urb_enqueue = ohci_urb_enqueue, + .urb_dequeue = ohci_urb_dequeue, + .endpoint_disable = ohci_endpoint_disable, + + .get_frame_number = ohci_get_frame, + + .hub_status_data = ohci_hub_status_data, + .hub_control = ohci_hub_control, + .hub_irq_enable = ohci_rhsc_enable, + +#ifdef CONFIG_PM + .bus_suspend = ohci_bus_suspend, + .bus_resume = ohci_bus_resume, +#endif + + .start_port_reset = ohci_start_port_reset, +}; + + +static void ssb_ohci_detach(struct ssb_device *dev) +{ + struct usb_hcd *hcd = ssb_get_drvdata(dev); + + usb_remove_hcd(hcd); + iounmap(hcd->regs); + usb_put_hcd(hcd); + ssb_device_disable(dev, 0); +} + +static int ssb_ohci_attach(struct ssb_device *dev) +{ + struct ssb_ohci_device *ohcidev; + struct usb_hcd *hcd; + int err = -ENOMEM; + u32 tmp, flags = 0; + + if (dev->id.coreid == SSB_DEV_USB11_HOSTDEV) + flags |= SSB_OHCI_TMSLOW_HOSTMODE; + + ssb_device_enable(dev, flags); + + hcd = usb_create_hcd(&ssb_ohci_hc_driver, dev->dev, + dev->dev->bus_id); + if (!hcd) + goto err_dev_disable; + ohcidev = hcd_to_ssb_ohci(hcd); + ohcidev->enable_flags = flags; + + tmp = ssb_read32(dev, SSB_ADMATCH0); + hcd->rsrc_start = ssb_admatch_base(tmp); + hcd->rsrc_len = ssb_admatch_size(tmp); + hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len); + if (!hcd->regs) + goto err_put_hcd; + err = usb_add_hcd(hcd, dev->irq, IRQF_SHARED); + if (err) + goto err_iounmap; + + ssb_set_drvdata(dev, hcd); + + return err; + +err_iounmap: + iounmap(hcd->regs); +err_put_hcd: + usb_put_hcd(hcd); +err_dev_disable: + ssb_device_disable(dev, flags); + return err; +} + +static int ssb_ohci_probe(struct ssb_device *dev, + const struct ssb_device_id *id) +{ + int err; + u16 chipid_top; + + chipid_top = (dev->bus->chip_id & 0xFF00); + if (chipid_top != 0x4700 && + chipid_top != 0x5300) { + /* USBcores are only connected on embedded devices. */ + return -ENODEV; + } + /* TODO: Probably need more checks here whether the core is connected. */ + + if (usb_disabled()) + return -ENODEV; + + /* We currently always attach SSB_DEV_USB11_HOSTDEV + * as HOST OHCI. If we want to attach it as Client device, + * we must branch here and call into the (yet to + * be written) Client mode driver. Same for remove(). */ + + err = ssb_ohci_attach(dev); + + return err; +} + +static void ssb_ohci_remove(struct ssb_device *dev) +{ + ssb_ohci_detach(dev); +} + +#ifdef CONFIG_PM +static int ssb_ohci_suspend(struct ssb_device *dev, pm_message_t state) +{ + ssb_device_disable(dev, 0); + + return 0; +} + +static int ssb_ohci_resume(struct ssb_device *dev) +{ + struct usb_hcd *hcd = ssb_get_drvdata(dev); + struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd); + + ssb_device_enable(dev, ohcidev->enable_flags); + + return 0; +} +#else /* CONFIG_PM */ +# define ssb_ohci_suspend NULL +# define ssb_ohci_resume NULL +#endif /* CONFIG_PM */ + +static struct ssb_driver ssb_ohci_driver = { + .name = KBUILD_MODNAME, + .id_table = ssb_ohci_table, + .probe = ssb_ohci_probe, + .remove = ssb_ohci_remove, + .suspend = ssb_ohci_suspend, + .resume = ssb_ohci_resume, +}; diff -puN include/linux/Kbuild~git-wireless include/linux/Kbuild --- a/include/linux/Kbuild~git-wireless +++ a/include/linux/Kbuild @@ -69,6 +69,11 @@ header-y += hdsmart.h header-y += hysdn_if.h header-y += i2c-dev.h header-y += i8k.h +<<<<<<< HEAD/include/linux/Kbuild +======= +header-y += icmp.h +header-y += ieee80211.h +>>>>>>> /include/linux/Kbuild header-y += if_arcnet.h header-y += if_bonding.h header-y += if_cablemodem.h @@ -114,6 +119,7 @@ header-y += netrom.h header-y += nfs2.h header-y += nfs4_mount.h header-y += nfs_mount.h +header-y += nl80211.h header-y += oom.h header-y += param.h header-y += pci_regs.h diff -puN /dev/null include/linux/crc-itu-t.h --- /dev/null +++ a/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 -puN /dev/null include/linux/eeprom_93cx6.h --- /dev/null +++ a/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 -puN /dev/null include/linux/ieee80211.h --- /dev/null +++ a/include/linux/ieee80211.h @@ -0,0 +1,342 @@ +/* + * 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_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; + } 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.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, +}; + + +/* 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.11i */ + WLAN_EID_RSN = 48, + WLAN_EID_WPA = 221, + WLAN_EID_GENERIC = 221, + WLAN_EID_VENDOR_SPECIFIC = 221, + WLAN_EID_QOS_PARAMETER = 222 +}; + +/* 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 -puN include/linux/netdevice.h~git-wireless include/linux/netdevice.h --- a/include/linux/netdevice.h~git-wireless +++ a/include/linux/netdevice.h @@ -42,6 +42,8 @@ struct vlan_group; struct ethtool_ops; struct netpoll_info; +/* 802.11 specific */ +struct wireless_dev; /* source back-compat hooks */ #define SET_ETHTOOL_OPS(netdev,ops) \ ( (netdev)->ethtool_ops = (ops) ) @@ -350,12 +352,13 @@ struct net_device struct net_device_stats* (*get_stats)(struct net_device *dev); struct net_device_stats stats; +#ifdef CONFIG_WIRELESS_EXT /* List of functions to handle Wireless Extensions (instead of ioctl). * See for details. Jean II */ const struct iw_handler_def * wireless_handlers; /* Instance data managed by the core of Wireless Extensions. */ struct iw_public_data * wireless_data; - +#endif const struct ethtool_ops *ethtool_ops; /* @@ -400,6 +403,8 @@ struct net_device void *ip6_ptr; /* IPv6 specific data */ void *ec_ptr; /* Econet specific data */ void *ax25_ptr; /* AX.25 specific data */ + struct wireless_dev *ieee80211_ptr; /* IEEE 802.11 specific data, + assign before registering */ /* * Cache line mostly used on receive path (including eth_type_trans()) diff -puN /dev/null include/linux/nl80211.h --- /dev/null +++ a/include/linux/nl80211.h @@ -0,0 +1,305 @@ +#ifndef __LINUX_NL80211_H +#define __LINUX_NL80211_H +/* + * 802.11 netlink interface public header + * + * Copyright 2006 Johannes Berg + */ + +/** + * 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_REASSOCIATE, + 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 + * @NL80211_IFTYPE_STATION: managed BSS member + * @NL80211_IFTYPE_AP: access point + * @NL80211_IFTYPE_WDS: wireless distribution interface + * @NL80211_IFTYPE_SECONDARY: ??? + * @NL80211_IFTYPE_MONITOR: monitor interface receiving all frames + * @__NL80211_IFTYPE_AFTER_LAST: internal use + * + * These values are used with the NL80211_ATTR_IFTYPE + * to set the type of an interface. + * Note that these are intentionally compatible with + * the IW_MODE_* constants except for the removal of + * IW_MODE_AUTO. + * + */ +enum nl80211_iftype { + NL80211_IFTYPE_UNSPECIFIED, + NL80211_IFTYPE_ADHOC, + NL80211_IFTYPE_STATION, + NL80211_IFTYPE_AP, + NL80211_IFTYPE_WDS, + NL80211_IFTYPE_SECONDARY, + NL80211_IFTYPE_MONITOR, + + /* keep last */ + __NL80211_IFTYPE_AFTER_LAST +}; +#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 -puN /dev/null include/linux/ssb/ssb.h --- /dev/null +++ a/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; /* _must_ be at the beginning. */ + 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 = (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 -puN /dev/null include/linux/ssb/ssb_driver_chipcommon.h --- /dev/null +++ a/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 -puN /dev/null include/linux/ssb/ssb_driver_extif.h --- /dev/null +++ a/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 -puN /dev/null include/linux/ssb/ssb_driver_mips.h --- /dev/null +++ a/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 -puN /dev/null include/linux/ssb/ssb_driver_pci.h --- /dev/null +++ a/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 -puN /dev/null include/linux/ssb/ssb_regs.h --- /dev/null +++ a/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 -puN /dev/null include/net/cfg80211.h --- /dev/null +++ a/include/net/cfg80211.h @@ -0,0 +1,153 @@ +#ifndef __NET_CFG80211_H +#define __NET_CFG80211_H + +#include +#include +#include + +/* + * 802.11 configuration in-kernel interface + * + * 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; + +/** + * struct cfg80211_ops - backend description for wireless configuration + * + * This struct is registered by fullmac card drivers and/or wireless stacks + * in order to handle configuration requests on their interfaces. + * + * All callbacks except where otherwise noted should return 0 + * on success or a negative error code. + * + * @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 + * + * @reassociate: reassociate with settings + * + * @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 (*reassociate)(struct wiphy *wiphy, struct net_device *dev); + 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 -puN include/net/iw_handler.h~git-wireless include/net/iw_handler.h --- a/include/net/iw_handler.h~git-wireless +++ a/include/net/iw_handler.h @@ -437,19 +437,6 @@ struct iw_public_data { extern int dev_get_wireless_info(char * buffer, char **start, off_t offset, int length); -/* Handle IOCTLs, called in net/core/dev.c */ -extern int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd); - -/* Handle RtNetlink requests, called in net/core/rtnetlink.c */ -extern int wireless_rtnetlink_set(struct net_device * dev, - char * data, - int len); -extern int wireless_rtnetlink_get(struct net_device * dev, - char * data, - int len, - char ** p_buf, - int * p_len); - /* Second : functions that may be called by driver modules */ /* Send a single event to user space */ diff -puN /dev/null include/net/mac80211.h --- /dev/null +++ a/include/net/mac80211.h @@ -0,0 +1,1077 @@ +/* + * Low-level hardware driver -- IEEE 802.11 driver (80211.o) interface + * 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 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. */ +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 */ +}; + +#define RX_FLAG_MMIC_ERROR 0x1 +#define RX_FLAG_DECRYPTED 0x2 + +/* 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; + 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 */ + 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_SCAN_START 1 +#define IEEE80211_SCAN_END 2 + +struct ieee80211_scan_conf { + int scan_channel; /* IEEE 802.11 channel number to do passive scan + * on */ + int scan_freq; /* new freq in MHz to switch to for passive scan + */ + int scan_channel_val; /* hw specific value for the channel */ + int scan_phymode; /* MODE_IEEE80211A, .. */ + unsigned char scan_power_level; + unsigned char scan_antenna_max; + + + int running_channel; /* IEEE 802.11 channel number we operate on + * normally */ + int running_freq; /* freq in MHz we're operating on normally */ + int running_channel_val; /* hw specific value for the channel */ + int running_phymode; + unsigned char running_power_level; + unsigned char running_antenna_max; + + int scan_time; /* time a scan will take in us */ + int tries; + + struct sk_buff *skb; /* skb to transmit before changing channels, maybe + * NULL for none */ + struct ieee80211_tx_control *tx_control; + +}; + +#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; + + /* 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) + + /* please fill this gap when adding new flags */ + + /* 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) + + 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. */ + 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. */ + 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. */ + 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. */ + 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 do a passive scan on a new channel. The hardware + * will do what ever is required to nicely leave the current channel + * including transmit any CTS packets, etc. */ + int (*passive_scan)(struct ieee80211_hw *hw, int state, + struct ieee80211_scan_conf *conf); + + /* 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); + + /* Enable/disable test modes; mode = IEEE80211_TEST_* */ + int (*test_mode)(struct ieee80211_hw *hw, int mode); + + /* Configuration of test parameters */ + int (*test_param)(struct ieee80211_hw *hw, int param, int value); + + /* 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) */ + 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_*. */ + 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. */ + u64 (*get_tsf)(struct ieee80211_hw *hw); + + /* 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); + + /* 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_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); + +/* Function for net interface operation. IEEE 802.11 may use multiple kernel + * netdevices for each hardware device. The low-level driver does not "see" + * these interfaces, so it should use this function to perform netif + * operations on all interface. */ +/* This function is deprecated. */ +typedef enum { + NETIF_ATTACH, NETIF_DETACH, NETIF_START, NETIF_STOP, NETIF_WAKE, + NETIF_IS_STOPPED, NETIF_UPDATE_TX_START +} Netif_Oper; +int ieee80211_netif_oper(struct ieee80211_hw *hw, Netif_Oper op); + +/** + * 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_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); + +/* Test modes */ +enum { + IEEE80211_TEST_DISABLE = 0 /* terminate testing */, + IEEE80211_TEST_UNMASK_CHANNELS = 1 /* allow all channels to be used */, + IEEE80211_TEST_CONTINUOUS_TX = 2, +}; + +/* Test parameters */ +enum { + /* TX power in hardware specific raw value */ + IEEE80211_TEST_PARAM_TX_POWER_RAW = 0, + /* TX rate in hardware specific raw value */ + IEEE80211_TEST_PARAM_TX_RATE_RAW = 1, + /* Continuous TX pattern (32-bit) */ + IEEE80211_TEST_PARAM_TX_PATTERN = 2, + /* TX power in 0.1 dBm, 100 = 10 dBm */ + IEEE80211_TEST_PARAM_TX_POWER = 3, + /* TX rate in 100 kbps, 540 = 54 Mbps */ + IEEE80211_TEST_PARAM_TX_RATE = 4, + IEEE80211_TEST_PARAM_TX_ANT_SEL_RAW = 5, +}; + +/* 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 -puN /dev/null include/net/wireless.h --- /dev/null +++ a/include/net/wireless.h @@ -0,0 +1,159 @@ +#ifndef __NET_WIRELESS_H +#define __NET_WIRELESS_H + +/* + * 802.11 device management + * + * Copyright 2007 Johannes Berg + */ + +#include +#include +#include +#include + +/** + * struct wiphy - wireless hardware description + * @idx: the wiphy index assigned to this item + * @class_dev: the class device representing /sys/class/ieee80211/ + */ +struct wiphy { + /* assign these fields before you register the wiphy */ + + /* permanent MAC address */ + u8 perm_addr[ETH_ALEN]; + + /* If multiple wiphys are registered and you're handed e.g. + * a regular netdev with assigned ieee80211_ptr, you won't + * know whether it points to a wiphy your driver has registered + * or not. Assign this to something global to your driver to + * help determine whether you own this wiphy or not. */ + void *privid; + + /* fields below are read-only, assigned by cfg80211 */ + + /* the item in /sys/class/ieee80211/ points to this, + * you need use set_wiphy_dev() (see below) */ + struct device dev; + + /* dir in debugfs: ieee80211/ */ + struct dentry *debugfsdir; + + char priv[0] __attribute__((__aligned__(NETDEV_ALIGN))); +}; + +/** struct wireless_dev - wireless per-netdev state + * + * This structure must be allocated by the driver/stack + * that uses the ieee80211_ptr field in struct net_device + * (this is intentional so it can be allocated along with + * the netdev.) + * + * @wiphy: pointer to hardware description + */ +struct wireless_dev { + struct wiphy *wiphy; + + /* private to the generic wireless code */ + struct list_head list; + struct net_device *netdev; +}; + +/** + * wiphy_priv - return priv from wiphy + */ +static inline void *wiphy_priv(struct wiphy *wiphy) +{ + BUG_ON(!wiphy); + return &wiphy->priv; +} + +/** + * set_wiphy_dev - set device pointer for wiphy + */ +static inline void set_wiphy_dev(struct wiphy *wiphy, struct device *dev) +{ + wiphy->dev.parent = dev; +} + +/** + * wiphy_dev - get wiphy dev pointer + */ +static inline struct device *wiphy_dev(struct wiphy *wiphy) +{ + return wiphy->dev.parent; +} + +/** + * wiphy_name - get wiphy name + */ +static inline char *wiphy_name(struct wiphy *wiphy) +{ + return wiphy->dev.bus_id; +} + +/** + * wdev_priv - return wiphy priv from wireless_dev + */ +static inline void *wdev_priv(struct wireless_dev *wdev) +{ + BUG_ON(!wdev); + return wiphy_priv(wdev->wiphy); +} + +/** + * wiphy_new - create a new wiphy for use with cfg80211 + * + * create a new wiphy and associate the given operations with it. + * @sizeof_priv bytes are allocated for private use. + * + * the returned pointer must be assigned to each netdev's + * ieee80211_ptr for proper operation. + */ +struct wiphy *wiphy_new(struct cfg80211_ops *ops, int sizeof_priv); + +/** + * wiphy_register - register a wiphy with cfg80211 + * + * register the given wiphy + * + * Returns a non-negative wiphy index or a negative error code. + */ +extern int wiphy_register(struct wiphy *wiphy); + +/** + * wiphy_unregister - deregister a wiphy from cfg80211 + * + * unregister a device with the given priv pointer. + * After this call, no more requests can be made with this priv + * pointer, but the call may sleep to wait for an outstanding + * request that is being handled. + */ +extern void wiphy_unregister(struct wiphy *wiphy); + +/** + * wiphy_free - free wiphy + */ +extern void wiphy_free(struct wiphy *wiphy); + + +/* + * internal definitions for wireless + */ + +#if defined(CONFIG_CFG80211_WEXT_COMPAT) || defined(CONFIG_WIRELESS_EXT) +int wext_ioctl(unsigned int cmd, struct ifreq *ifreq, void __user *arg); +int wireless_proc_init(void); +#else +static inline +int wext_ioctl(unsigned int cmd, struct ifreq *ifreq, void __user *arg) +{ + return -EINVAL; +} +static inline int wireless_proc_init(void) +{ + return 0; +} +#endif + +#endif /* __NET_WIRELESS_H */ diff -puN lib/Kconfig~git-wireless lib/Kconfig --- a/lib/Kconfig~git-wireless +++ a/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 @@ -42,6 +50,14 @@ config LIBCRC32C require M here. See Castagnoli93. Module will be libcrc32c. +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. + + When compiled as a module, this driver will be called "eeprom_93c6.ko". + config AUDIT_GENERIC bool depends on AUDIT && !AUDIT_ARCH diff -puN lib/Makefile~git-wireless lib/Makefile --- a/lib/Makefile~git-wireless +++ a/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 @@ -56,6 +57,8 @@ obj-$(CONFIG_TEXTSEARCH_FSM) += ts_fsm.o obj-$(CONFIG_SMP) += percpu_counter.o obj-$(CONFIG_AUDIT_GENERIC) += audit.o +obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o + obj-$(CONFIG_SWIOTLB) += swiotlb.o obj-$(CONFIG_FAULT_INJECTION) += fault-inject.o diff -puN /dev/null lib/crc-itu-t.c --- /dev/null +++ a/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 -puN /dev/null lib/eeprom_93cx6.c --- /dev/null +++ a/lib/eeprom_93cx6.c @@ -0,0 +1,344 @@ +/* + 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; + + 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) + *data |= (1 << (i - 1)); + + eeprom_93cx6_pulse_low(eeprom); + } +} + +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 -puN net/Kconfig~git-wireless net/Kconfig --- a/net/Kconfig~git-wireless +++ a/net/Kconfig @@ -212,14 +212,18 @@ endmenu source "net/ax25/Kconfig" source "net/irda/Kconfig" source "net/bluetooth/Kconfig" -source "net/ieee80211/Kconfig" - -config WIRELESS_EXT - bool config FIB_RULES bool +menu "Wireless" + +source "net/wireless/Kconfig" +source "net/mac80211/Kconfig" +source "net/ieee80211/Kconfig" + +endmenu + endif # if NET endmenu # Networking diff -puN net/Makefile~git-wireless net/Makefile --- a/net/Makefile~git-wireless +++ a/net/Makefile @@ -44,6 +44,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/ diff -puN net/core/Makefile~git-wireless net/core/Makefile --- a/net/core/Makefile~git-wireless +++ a/net/core/Makefile @@ -13,7 +13,6 @@ obj-y += dev.o ethtool.o dev_mcast obj-$(CONFIG_XFRM) += flow.o obj-$(CONFIG_SYSFS) += net-sysfs.o obj-$(CONFIG_NET_PKTGEN) += pktgen.o -obj-$(CONFIG_WIRELESS_EXT) += wireless.o obj-$(CONFIG_NETPOLL) += netpoll.o obj-$(CONFIG_NET_DMA) += user_dma.o obj-$(CONFIG_FIB_RULES) += fib_rules.o diff -puN net/core/dev.c~git-wireless net/core/dev.c --- a/net/core/dev.c~git-wireless +++ a/net/core/dev.c @@ -116,6 +116,7 @@ #include #include #include +#include /* * The list of packet types we will receive (as opposed to discard) @@ -2225,6 +2226,7 @@ static const struct file_operations soft .release = seq_release, }; +<<<<<<< HEAD/net/core/dev.c static void *ptype_get_idx(loff_t pos) { struct packet_type *pt = NULL; @@ -2360,6 +2362,8 @@ extern int wireless_proc_init(void); #define wireless_proc_init() 0 #endif +======= +>>>>>>> /net/core/dev.c static int __init dev_proc_init(void) { int rc = -ENOMEM; @@ -2929,29 +2933,9 @@ int dev_ioctl(unsigned int cmd, void __u ret = -EFAULT; return ret; } -#ifdef CONFIG_WIRELESS_EXT - /* Take care of Wireless Extensions */ - if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) { - /* If command is `set a parameter', or - * `get the encoding parameters', check if - * the user has the right to do it */ - if (IW_IS_SET(cmd) || cmd == SIOCGIWENCODE - || cmd == SIOCGIWENCODEEXT) { - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - } - dev_load(ifr.ifr_name); - rtnl_lock(); - /* Follow me in net/core/wireless.c */ - ret = wireless_process_ioctl(&ifr, cmd); - rtnl_unlock(); - if (IW_IS_GET(cmd) && - copy_to_user(arg, &ifr, - sizeof(struct ifreq))) - ret = -EFAULT; - return ret; - } -#endif /* CONFIG_WIRELESS_EXT */ + /* Take care of wireless extensions */ + if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) + return wext_ioctl(cmd, &ifr, arg); return -EINVAL; } } diff -puN net/core/rtnetlink.c~git-wireless net/core/rtnetlink.c --- a/net/core/rtnetlink.c~git-wireless +++ a/net/core/rtnetlink.c @@ -50,11 +50,15 @@ #include #include #include +<<<<<<< HEAD/net/core/rtnetlink.c #include #ifdef CONFIG_NET_WIRELESS_RTNETLINK #include #include #endif /* CONFIG_NET_WIRELESS_RTNETLINK */ +======= +#include +>>>>>>> /net/core/rtnetlink.c struct rtnl_link { @@ -686,17 +690,6 @@ static int rtnl_setlink(struct sk_buff * modified = 1; } -#ifdef CONFIG_NET_WIRELESS_RTNETLINK - if (tb[IFLA_WIRELESS]) { - /* Call Wireless Extensions. - * Various stuff checked in there... */ - err = wireless_rtnetlink_set(dev, nla_data(tb[IFLA_WIRELESS]), - nla_len(tb[IFLA_WIRELESS])); - if (err < 0) - goto errout_dev; - } -#endif /* CONFIG_NET_WIRELESS_RTNETLINK */ - if (tb[IFLA_BROADCAST]) { nla_memcpy(dev->broadcast, tb[IFLA_BROADCAST], dev->addr_len); send_addr_notify = 1; diff -puN net/core/wireless.c~git-wireless net/core/wireless.c --- a/net/core/wireless.c~git-wireless +++ a/net/core/wireless.c @@ -1,3 +1,4 @@ +<<<<<<< HEAD/net/core/wireless.c /* * This file implement the Wireless Extensions APIs. * @@ -2366,3 +2367,5 @@ EXPORT_SYMBOL(iw_handler_set_spy); EXPORT_SYMBOL(iw_handler_set_thrspy); EXPORT_SYMBOL(wireless_send_event); EXPORT_SYMBOL(wireless_spy_update); +======= +>>>>>>> /net/core/wireless.c diff -puN net/ieee80211/Kconfig~git-wireless net/ieee80211/Kconfig --- a/net/ieee80211/Kconfig~git-wireless +++ a/net/ieee80211/Kconfig @@ -56,7 +56,8 @@ config IEEE80211_CRYPT_CCMP config IEEE80211_CRYPT_TKIP tristate "IEEE 802.11i TKIP encryption" - depends on IEEE80211 && NET_RADIO + depends on IEEE80211 + select WIRELESS_EXT select CRYPTO select CRYPTO_MICHAEL_MIC select CRYPTO_ECB diff -puN /dev/null net/mac80211/Kconfig --- /dev/null +++ a/net/mac80211/Kconfig @@ -0,0 +1,80 @@ +config MAC80211 + tristate "Generic IEEE 802.11 Networking Stack (dscape)" + select CRYPTO + select CRYPTO_ECB + select CRYPTO_ARC4 + select CRYPTO_AES + select CRC32 + select WIRELESS_EXT + select CFG80211 + ---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 -puN /dev/null net/mac80211/Makefile --- /dev/null +++ a/net/mac80211/Makefile @@ -0,0 +1,25 @@ +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_scan.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) + +ifeq ($(CONFIG_NET_SCHED),) + mac80211-objs += fifo_qdisc.o +endif diff -puN /dev/null net/mac80211/aes_ccm.c --- /dev/null +++ a/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 -puN /dev/null net/mac80211/aes_ccm.h --- /dev/null +++ a/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 -puN /dev/null net/mac80211/debugfs.c --- /dev/null +++ a/net/mac80211/debugfs.c @@ -0,0 +1,436 @@ +/* + * 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); +DEBUGFS_STATS_FILE(num_scans, 20, "%u", + local->scan.num_scans); +#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); +#endif + +DEBUGFS_DEVSTATS_FILE(dot11ACKFailureCount); +DEBUGFS_DEVSTATS_FILE(dot11RTSFailureCount); +DEBUGFS_DEVSTATS_FILE(dot11FCSErrorCount); +DEBUGFS_DEVSTATS_FILE(dot11RTSSuccessCount); + +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, +}; + + +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); + DEBUGFS_STATS_ADD(num_scans); +#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); +#endif + DEBUGFS_STATS_ADD(dot11ACKFailureCount); + DEBUGFS_STATS_ADD(dot11RTSFailureCount); + DEBUGFS_STATS_ADD(dot11FCSErrorCount); + DEBUGFS_STATS_ADD(dot11RTSSuccessCount); + DEBUGFS_STATS_ADD(wme_tx_queue); + DEBUGFS_STATS_ADD(wme_rx_queue); +} + +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); +#endif + DEBUGFS_STATS_DEL(dot11ACKFailureCount); + DEBUGFS_STATS_DEL(dot11RTSFailureCount); + DEBUGFS_STATS_DEL(dot11FCSErrorCount); + DEBUGFS_STATS_DEL(dot11RTSSuccessCount); + DEBUGFS_STATS_DEL(wme_tx_queue); + DEBUGFS_STATS_DEL(wme_rx_queue); + + 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 -puN /dev/null net/mac80211/debugfs.h --- /dev/null +++ a/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 -puN /dev/null net/mac80211/debugfs_key.c --- /dev/null +++ a/net/mac80211/debugfs_key.c @@ -0,0 +1,253 @@ +/* + * 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 "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 -puN /dev/null net/mac80211/debugfs_key.h --- /dev/null +++ a/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 -puN /dev/null net/mac80211/debugfs_netdev.c --- /dev/null +++ a/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 -puN /dev/null net/mac80211/debugfs_netdev.h --- /dev/null +++ a/net/mac80211/debugfs_netdev.h @@ -0,0 +1,30 @@ +/* routines exported for sysfs handling */ + +#ifndef __IEEE80211_SYSFS_H +#define __IEEE80211_SYSFS_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_SYSFS_H */ diff -puN /dev/null net/mac80211/debugfs_sta.c --- /dev/null +++ a/net/mac80211/debugfs_sta.c @@ -0,0 +1,242 @@ +/* + * 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", + 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_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); + DEBUGFS_ADD(wme_rx_queue); + DEBUGFS_ADD(wme_tx_queue); +} + +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); + DEBUGFS_DEL(wme_rx_queue); + DEBUGFS_DEL(wme_tx_queue); + + debugfs_remove(sta->debugfs.dir); + sta->debugfs.dir = NULL; +} diff -puN /dev/null net/mac80211/debugfs_sta.h --- /dev/null +++ a/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 -puN /dev/null net/mac80211/fifo_qdisc.c --- /dev/null +++ a/net/mac80211/fifo_qdisc.c @@ -0,0 +1,102 @@ +/* + * 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. + * + * If building without CONFIG_NET_SCHED we need a simple + * fifo qdisc to install by default as the sub-qdisc. + * This is a simple replacement for sch_fifo. + */ + +#include +#include +#include +#include "ieee80211_i.h" +#include "wme.h" + +static int pfifo_enqueue(struct sk_buff *skb, struct Qdisc* qd) +{ + struct sk_buff_head *q = qdisc_priv(qd); + + if (skb_queue_len(q) > qd->dev->tx_queue_len) { + qd->qstats.drops++; + kfree_skb(skb); + return NET_XMIT_DROP; + } + + skb_queue_tail(q, skb); + qd->q.qlen++; + qd->bstats.bytes += skb->len; + qd->bstats.packets++; + + return NET_XMIT_SUCCESS; +} + + +static int pfifo_requeue(struct sk_buff *skb, struct Qdisc* qd) +{ + struct sk_buff_head *q = qdisc_priv(qd); + + skb_queue_head(q, skb); + qd->q.qlen++; + qd->bstats.bytes += skb->len; + qd->bstats.packets++; + + return NET_XMIT_SUCCESS; +} + + +static struct sk_buff *pfifo_dequeue(struct Qdisc* qd) +{ + struct sk_buff_head *q = qdisc_priv(qd); + + return skb_dequeue(q); +} + + +static int pfifo_init(struct Qdisc* qd, struct rtattr *opt) +{ + struct sk_buff_head *q = qdisc_priv(qd); + + skb_queue_head_init(q); + return 0; +} + + +static void pfifo_reset(struct Qdisc* qd) +{ + struct sk_buff_head *q = qdisc_priv(qd); + + skb_queue_purge(q); + qd->q.qlen = 0; +} + + +static int pfifo_dump(struct Qdisc *qd, struct sk_buff *skb) +{ + return skb->len; +} + + +struct Qdisc_ops pfifo_qdisc_ops = +{ + .next = NULL, + .cl_ops = NULL, + .id = "ieee80211_pfifo", + .priv_size = sizeof(struct sk_buff_head), + + .enqueue = pfifo_enqueue, + .dequeue = pfifo_dequeue, + .requeue = pfifo_requeue, + .drop = NULL, + + .init = pfifo_init, + .reset = pfifo_reset, + .destroy = NULL, + .change = NULL, + + .dump = pfifo_dump, +}; + diff -puN /dev/null net/mac80211/hostapd_ioctl.h --- /dev/null +++ a/net/mac80211/hostapd_ioctl.h @@ -0,0 +1,347 @@ +/* + * 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) +#define PRISM2_IOCTL_TEST_PARAM (SIOCIWFIRSTPRIV + 4) + +/* 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_TEST_MODE = 1007, + 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_SCAN = 1003, + 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 -puN /dev/null net/mac80211/ieee80211.c --- /dev/null +++ a/net/mac80211/ieee80211.c @@ -0,0 +1,4938 @@ +/* + * 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 +#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 u8 * ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len); + +static int ieee80211_mgmt_start_xmit(struct sk_buff *skb, + struct net_device *dev); + +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; + if (rate->rate != 10 && rate->rate != 20 && + rate->rate != 55 && rate->rate != 110) + rate->flags |= IEEE80211_RATE_ERP; + break; + } + } +} + + +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); +} + + +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 */ + if (fc & IEEE80211_STYPE_QOS_DATA) + hdrlen += 2; /* QoS Control Field */ + break; + case IEEE80211_FTYPE_CTL: + switch (fc & IEEE80211_FCTL_STYPE) { + case IEEE80211_STYPE_CTS: + case IEEE80211_STYPE_ACK: + hdrlen = 10; + break; + default: + hdrlen = 16; + break; + } + 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); + +#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 inline int ceiling_div(int dividend, int divisor) +{ + return ((dividend + divisor - 1) / divisor); +} + + +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 + * ceiling_div() 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 * ceiling_div((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 += ceiling_div(8 * (len + 4) * 10, rate); + } + + return dur; +} + + +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; + } + } + } + tx->local->scan.txrx_count++; + + 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_DEBUG + struct ieee80211_hdr *hdr = + (struct ieee80211_hdr *) tx->skb->data; + 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; + + spin_lock_bh(&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); + } + spin_unlock_bh(&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->nh.raw - skb->data; + h_pos = skb->h.raw - 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->mac.raw = skb->data; + skb->nh.raw = skb->data + nh_pos; + skb->h.raw = skb->data + 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; + + 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) { + 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; +} + + +static int ieee80211_set_mac_address(struct net_device *dev, void *addr) +{ + struct sockaddr *a = addr; + + if (netif_running(dev)) + return -EBUSY; + + memcpy(dev->dev_addr, a->sa_data, ETH_ALEN); + return 0; +} + +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; + + 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; + local->ops->set_multicast_list(local_to_hw(local), flags, + local->mc_count); + } +} + +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); +} + +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); + flush_scheduled_work(); + 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_scheduled_work(); + /* see comment in ieee80211_unregister_hw to + * understand why this works */ + local->scan_dev = NULL; + } else + flush_scheduled_work(); + 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; + + list_for_each_entry(sdata, &local->sub_if_list, list) { + if (sdata->dev != dev && netif_running(sdata->dev)) { + res = 0; + tasklet_enable(&local->tx_pending_tasklet); + break; + } + } + 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; + + tasklet_disable(&local->tx_pending_tasklet); + list_for_each_entry(sdata, &local->sub_if_list, list) { + if (sdata->dev != dev && netif_running(sdata->dev)) + return -EOPNOTSUPP; + } + 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); + 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)) + return -ENOTUNIQ; + } + + 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++; + 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; + 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; + } + ieee80211_init_scan(local); + } + local->open_count++; + + if (sdata->type == IEEE80211_IF_TYPE_MNTR) + local->monitors++; + 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--; + return 0; + } + + netif_stop_queue(dev); + + if (sdata->type == IEEE80211_IF_TYPE_MNTR) + local->monitors--; + + local->open_count--; + if (local->open_count == 0) { + ieee80211_stop_scan(local); + dev_close(local->mdev); + if (local->apdev) + dev_close(local->apdev); + if (local->ops->stop) + local->ops->stop(local_to_hw(local)); + } + 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_if_shutdown(dev); + + ieee80211_start_hard_monitor(local); + + return 0; +} + + +static int header_parse_80211(struct sk_buff *skb, unsigned char *haddr) +{ + memcpy(haddr, skb->mac.raw + 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); +} + + +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); + skb2->mac.raw = skb2->nh.raw = skb2->data; + 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 = 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 = 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->mac.raw = skb->data; + 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_frame_info *fi; + struct ieee80211_sub_if_data *sdata; + const size_t hlen = sizeof(struct ieee80211_frame_info) + - sizeof(fi->msg_type); + + skb->dev = dev; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + 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); + sdata->stats.rx_packets++; + sdata->stats.rx_bytes += skb->len; + + skb->mac.raw = skb->data; + 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; + } + + 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; +} + + +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 ((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) + local->scan.txrx_count++; + if (unlikely(local->scan.in_scan != 0 && + rx->u.rx.status->freq == local->scan.freq)) { + struct ieee80211_hdr *hdr; + u16 fc; + + local->scan.rx_packets++; + + hdr = (struct ieee80211_hdr *) skb->data; + fc = le16_to_cpu(hdr->frame_control); + + if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT && + (fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_BEACON && + rx->dev == local->mdev) { + local->scan.rx_beacon++; + /* Need to trim FCS here because it is normally + * removed only after this passive scan handler. */ + 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 (!rx->local->apdev) + return TXRX_DROP; + ieee80211_rx_mgmt(rx->local, rx->skb, + rx->u.rx.status, + ieee80211_msg_passive_scan); + return TXRX_QUEUED; + } else { + I802_DEBUG_INC(local->rx_handlers_drop_passive_scan); + return TXRX_DROP; + } + } + + 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 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; +} + +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; + + 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 || local->scan.in_scan)) + 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; + + 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); + + 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(local->mdev, + skb, bssid, + hdr->addr2); + /* FIXME: call with sdata->dev */ + 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); + } + + 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 */ + 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; + } + + /* 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, + 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; + + /* 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_mac_address = ieee80211_set_mac_address; + 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->scan.in_scan = 0; + local->enabled_modes = (unsigned int) -1; + + init_timer(&local->scan.timer); /* clear it out */ + + INIT_LIST_HEAD(&local->modes_list); + + spin_lock_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->get_stats = ieee80211_get_stats; + 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); + 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); + struct net_device *sta_dev; + int result; + + result = wiphy_register(local->hw.wiphy); + if (result < 0) + return result; + + 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) { + rtnl_unlock(); + 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; + } + + /* TODO: add rtnl locking around device creation and qdisc install */ + ieee80211_install_qdisc(local->mdev); + + /* add one default STA interface */ + result = ieee80211_if_add(local->mdev, "wlan%d", 1, &sta_dev); + if (result == 0) + ieee80211_if_set_type(sta_dev, IEEE80211_IF_TYPE_STA); + + 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); + 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; + } + + 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; + 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); + + list_for_each_entry_safe(sdata, tmp, &local->sub_if_list, list) + __ieee80211_if_del(local, sdata); + + rtnl_unlock(); + + if (local->stat_time) + del_timer_sync(&local->stat_timer); + if (!local->ops->hw_scan && local->scan_dev) { + local->sta_scanning = 0; + cancel_delayed_work(&local->scan_work); + flush_scheduled_work(); + /* The scan_work is guaranteed not to be called at this + * point. It is not scheduled and not running now. It can be + * scheduled again only by sta_work (stopped by now) or under + * rtnl lock. */ + } + + 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); + + 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); + +/* Perform netif operations on all configured interfaces */ +int ieee80211_netif_oper(struct ieee80211_hw *hw, Netif_Oper op) +{ + struct ieee80211_local *local = hw_to_local(hw); + struct net_device *dev = local->mdev; + + switch (op) { + case NETIF_ATTACH: + netif_device_attach(dev); + break; + case NETIF_DETACH: + netif_device_detach(dev); + break; + case NETIF_START: + netif_start_queue(dev); + break; + case NETIF_STOP: + break; + case NETIF_WAKE: + if (local->scan.in_scan == 0) { + netif_wake_queue(dev); +#if 1 + if (/* FIX: 802.11 qdisc in use */ 1) + __netif_schedule(dev); +#endif + } + break; + case NETIF_IS_STOPPED: + if (netif_queue_stopped(dev)) + return 1; + break; + case NETIF_UPDATE_TX_START: + dev->trans_start = jiffies; + break; + } + + return 0; +} +EXPORT_SYMBOL(ieee80211_netif_oper); + +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) +{ + struct ieee80211_local *local = hw_to_local(hw); + int i; + + for (i = 0; i < local->hw.queues; i++) + ieee80211_stop_queue(hw, i); +} +EXPORT_SYMBOL(ieee80211_stop_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 -puN /dev/null net/mac80211/ieee80211_cfg.c --- /dev/null +++ a/net/mac80211/ieee80211_cfg.c @@ -0,0 +1,90 @@ +/* + * 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" + +/* copied from ieee80211_sysfs.c for now ... */ +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; +} + + +static int ieee80211_add_iface(struct wiphy *wiphy, char *name, + unsigned int type) +{ + struct ieee80211_local *local = wiphy_priv(wiphy); + struct net_device *new_dev; + int res, itype; + + 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; + } + + res = rtnl_lock_local(local); + if (res) + return res; + + res = ieee80211_if_add(local->mdev, name, 0, &new_dev); + if (res == 0) + ieee80211_if_set_type(new_dev, itype); + rtnl_unlock(); + return res; +} + +static int ieee80211_del_iface(struct wiphy *wiphy, int ifindex) +{ + struct ieee80211_local *local = wiphy_priv(wiphy); + int res; + struct net_device *dev; + char *name; + + res = rtnl_lock_local(local); + if (res) + return res; + dev = dev_get_by_index(ifindex); + name = dev->name; + dev_put(dev); + + res = ieee80211_if_remove(local->mdev, name, -1); + rtnl_unlock(); + return res; +} + +struct cfg80211_ops mac80211_config_ops = { + .add_virtual_intf = ieee80211_add_iface, + .del_virtual_intf = ieee80211_del_iface, +}; diff -puN /dev/null net/mac80211/ieee80211_cfg.h --- /dev/null +++ a/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 -puN /dev/null net/mac80211/ieee80211_common.h --- /dev/null +++ a/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 -puN /dev/null net/mac80211/ieee80211_i.h --- /dev/null +++ a/net/mac80211/ieee80211_i.h @@ -0,0 +1,826 @@ +/* + * 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. + */ + +#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; +#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; + unsigned int in_scan:1; + /* frame is destined to interface currently processed + * (including multicast frames) */ + unsigned int ra_match: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; +}; + +struct ieee80211_passive_scan { + unsigned int in_scan:1; /* this must be cleared before calling + * netif_oper(WAKEUP) */ + unsigned int our_mode_only:1; /* only scan our physical mode a/b/g/etc + */ + int interval; /* time in seconds between scans */ + int time; /* time in microseconds to scan for */ + int channel; /* channel to be scanned */ + int tries; + + struct ieee80211_hw_mode *mode; + int chan_idx; + + int freq; + int rx_packets; + int rx_beacon; + int txrx_count; + + struct timer_list timer; + + struct sk_buff *skb; /* skb to transmit before changing channels, + * maybe null for none */ + struct ieee80211_tx_control tx_control; + + unsigned int num_scans; +}; + +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 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 */ + + struct ieee80211_passive_scan scan; + + + ieee80211_rx_handler *rx_pre_handlers; + ieee80211_rx_handler *rx_handlers; + ieee80211_tx_handler *tx_handlers; + + spinlock_t sub_if_lock; /* mutex for STA data structures */ + 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; + + struct work_struct sta_proc_add; + + 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; +#endif + struct dentry *dot11ACKFailureCount; + struct dentry *dot11RTSFailureCount; + struct dentry *dot11FCSErrorCount; + struct dentry *dot11RTSSuccessCount; + struct dentry *wme_tx_queue; + struct dentry *wme_rx_queue; + } 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.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); +void ieee80211_if_shutdown(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); + +/* ieee80211_scan.c */ +void ieee80211_init_scan(struct ieee80211_local *local); +void ieee80211_stop_scan(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, + int format, struct net_device **new_dev); +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); +void ieee80211_if_del(struct net_device *dev); +int ieee80211_if_remove(struct net_device *dev, const char *name, int id); +void ieee80211_if_free(struct net_device *dev); +void ieee80211_if_flush(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 -puN /dev/null net/mac80211/ieee80211_iface.c --- /dev/null +++ a/net/mac80211/ieee80211_iface.c @@ -0,0 +1,365 @@ +/* + * 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, + int format, struct net_device **new_dev) +{ + 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 = *new_dev = alloc_netdev(sizeof(struct ieee80211_sub_if_data), + "", ieee80211_if_setup); + if (!ndev) + return -ENOMEM; + + if (*name == '\0') { + snprintf(ndev->name, IFNAMSIZ, "%s.%%d", dev->name); + format = 1; + } + + if (format) { + ret = dev_alloc_name(ndev, name); + if (ret < 0) + goto fail; + } else + snprintf(ndev->name, IFNAMSIZ, "%s", name); + + 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); + list_add(&sdata->list, &local->sub_if_list); + ieee80211_update_default_wep_only(local); + + return 0; + +fail: + free_netdev(ndev); + *new_dev = NULL; + 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), "", + ieee80211_if_mgmt_setup); + if (!ndev) + return -ENOMEM; + ret = dev_alloc_name(ndev, "wmgmt%d"); + 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) ifsta); + 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->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_PRISM; + 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_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); + __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; + + list_del(&sdata->list); + 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(); + + 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) { + __ieee80211_if_del(local, sdata); + ieee80211_update_default_wep_only(local); + return 0; + } + } + 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); +} + +/* Must be called with rtnl lock held. */ +void ieee80211_if_flush(struct net_device *dev) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_sub_if_data *sdata, *n; + + ASSERT_RTNL(); + list_for_each_entry_safe(sdata, n, &local->sub_if_list, list) { + __ieee80211_if_del(local, sdata); + } +} + +void ieee80211_if_del(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); + + rtnl_lock(); + if (sdata->type == IEEE80211_IF_TYPE_MGMT) + ieee80211_if_del_mgmt(local); + else + __ieee80211_if_del(local, sdata); + rtnl_unlock(); +} diff -puN /dev/null net/mac80211/ieee80211_ioctl.c --- /dev/null +++ a/net/mac80211/ieee80211_ioctl.c @@ -0,0 +1,3285 @@ +/* + * 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_scan(struct net_device *dev, + struct prism2_hostapd_param *param) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + + if (!local->ops->passive_scan) + return -EOPNOTSUPP; + + if ((param->u.scan.now == 1) && (local->scan.in_scan == 1)) + return -EBUSY; + + if (param->u.scan.our_mode_only >= 0) + local->scan.our_mode_only = param->u.scan.our_mode_only; + if (param->u.scan.interval >= 0) + local->scan.interval = param->u.scan.interval; + if (param->u.scan.listen >= 0) + local->scan.time = param->u.scan.listen; + if (param->u.scan.channel > 0) + local->scan.channel = param->u.scan.channel; + if (param->u.scan.now == 1) { + local->scan.in_scan = 0; + mod_timer(&local->scan.timer, jiffies); + } + + param->u.scan.our_mode_only = local->scan.our_mode_only; + param->u.scan.interval = local->scan.interval; + param->u.scan.listen = local->scan.time; + if (local->scan.in_scan == 1) + param->u.scan.last_rx = -1; + else { + param->u.scan.last_rx = local->scan.rx_packets; + local->scan.rx_packets = -1; + } + param->u.scan.channel = + local->scan.mode->channels[local->scan.chan_idx].chan; + + 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 (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff && + param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff && + param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) { + 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 (sta_addr[0] == 0xff && sta_addr[1] == 0xff && + sta_addr[2] == 0xff && sta_addr[3] == 0xff && + sta_addr[4] == 0xff && sta_addr[5] == 0xff) { + 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 (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff && + param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff && + param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) { + 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 (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff && + param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff && + param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) { + 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, 0, &new_dev); + if (res) + return res; + ieee80211_if_set_type(new_dev, IEEE80211_IF_TYPE_WDS); + res = ieee80211_if_update_wds(new_dev, wds->remote_addr); + if (res) + __ieee80211_if_del(wdev_priv(dev->ieee80211_ptr), + IEEE80211_DEV_TO_SUB_IF(new_dev)); + 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, 0, &new_dev); + if (res) + return res; + ieee80211_if_set_type(new_dev, IEEE80211_IF_TYPE_VLAN); +#if 0 + res = ieee80211_if_update_vlan(new_dev, vlan->id); + if (res) + __ieee80211_if_del(wdev_priv(dev->ieee80211_ptr), + IEEE80211_DEV_TO_SUB_IF(new_dev)); +#endif + 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, 0, &new_dev); + if (res) + return res; + ieee80211_if_set_type(new_dev, IEEE80211_IF_TYPE_AP); + 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, 0, &new_dev); + if (res) + return res; + ieee80211_if_set_type(new_dev, IEEE80211_IF_TYPE_STA); + return 0; + 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; + + 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; + } + } + + 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_flush_ifs(struct net_device *dev, + struct prism2_hostapd_param *param) +{ + ieee80211_if_flush(dev); + return 0; +} + + +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; + case PRISM2_HOSTAPD_SCAN: + ret = ieee80211_ioctl_scan(dev, param); + 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_FLUSH_IFS: + ret = ieee80211_ioctl_flush_ifs(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 = 14; + 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->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; + + 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; + if (netif_running(dev)) + return -EBUSY; + + 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) { + 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 & IW_RETRY_TYPE) != IW_RETRY_LIMIT) + return -EINVAL; + if (retry->flags & IW_RETRY_MAX) { + retry->flags = IW_RETRY_LIMIT | IW_RETRY_MAX; + retry->value = local->long_retry_limit; + } else { + 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; +} + + +static void ieee80211_ioctl_unmask_channels(struct ieee80211_local *local) +{ + struct ieee80211_hw_mode *mode; + int c; + + list_for_each_entry(mode, &local->modes_list, list) { + for (c = 0; c < mode->num_channels; c++) { + struct ieee80211_channel *chan = &mode->channels[c]; + chan->flag |= IEEE80211_CHAN_W_SCAN; + } + } +} + + +static int ieee80211_ioctl_test_mode(struct net_device *dev, int mode) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + int ret = -EOPNOTSUPP; + + if (mode == IEEE80211_TEST_UNMASK_CHANNELS) { + ieee80211_ioctl_unmask_channels(local); + ret = 0; + } + + if (local->ops->test_mode) + ret = local->ops->test_mode(local_to_hw(local), mode); + + return ret; +} + + +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); + 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; + } + + 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; + 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]); + + return 0; +} + + +void ieee80211_update_default_wep_only(struct ieee80211_local *local) +{ + int i = 0; + struct ieee80211_sub_if_data *sdata; + + spin_lock_bh(&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) { + spin_unlock_bh(&local->sub_if_lock); + return; + } + + i++; + } + + if (i <= 1) + ieee80211_ioctl_default_wep_only(local, 1); + else + ieee80211_ioctl_default_wep_only(local, 0); + + spin_unlock_bh(&local->sub_if_lock); +} + + +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: + sdata->ieee802_1x = value; + if (local->ops->set_ieee8021x && + local->ops->set_ieee8021x(local_to_hw(local), value)) + printk(KERN_DEBUG "%s: failed to set IEEE 802.1X (%d) " + "for low-level driver\n", dev->name, 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_TEST_MODE: + ret = ieee80211_ioctl_test_mode(dev, 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_test_param(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, char *extra) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + int *i = (int *) extra; + int param = *i; + int value = *(i + 1); + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (local->ops->test_param) + return local->ops->test_param(local_to_hw(local), + param, value); + + return -EOPNOTSUPP; +} + + +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" }, + { PRISM2_IOCTL_TEST_PARAM, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "test_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_TEST_PARAM: + ret = ieee80211_ioctl_test_param(dev, NULL, &wrq->u, + (char *) &wrq->u); + break; + 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 = sizeof(ieee80211_handler) / sizeof(iw_handler), + .num_private = sizeof(ieee80211_private_handler) / + sizeof(iw_handler), + .num_private_args = sizeof(ieee80211_ioctl_priv) / + sizeof(struct iw_priv_args), + .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 -puN /dev/null net/mac80211/ieee80211_key.h --- /dev/null +++ a/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 -puN /dev/null net/mac80211/ieee80211_led.c --- /dev/null +++ a/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 -puN /dev/null net/mac80211/ieee80211_led.h --- /dev/null +++ a/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 -puN /dev/null net/mac80211/ieee80211_rate.c --- /dev/null +++ a/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 -puN /dev/null net/mac80211/ieee80211_rate.h --- /dev/null +++ a/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 -puN /dev/null net/mac80211/ieee80211_scan.c --- /dev/null +++ a/net/mac80211/ieee80211_scan.c @@ -0,0 +1,344 @@ +/* + * 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 "ieee80211_i.h" +#include "ieee80211_rate.h" + + +/* Maximum number of seconds to wait for the traffic load to get below + * threshold before forcing a passive scan. */ +#define MAX_SCAN_WAIT 60 +/* Threshold (pkts/sec TX or RX) for delaying passive scan */ +#define SCAN_TXRX_THRESHOLD 75 + +static void get_channel_params(struct ieee80211_local *local, int channel, + struct ieee80211_hw_mode **mode, + struct ieee80211_channel **chan) +{ + struct ieee80211_hw_mode *m; + + list_for_each_entry(m, &local->modes_list, list) { + *mode = m; + if (m->mode == local->hw.conf.phymode) + break; + } + local->scan.mode = m; + local->scan.chan_idx = 0; + do { + *chan = &m->channels[local->scan.chan_idx]; + if ((*chan)->chan == channel) + return; + local->scan.chan_idx++; + } while (local->scan.chan_idx < m->num_channels); + *chan = NULL; +} + + +static void next_chan_same_mode(struct ieee80211_local *local, + struct ieee80211_hw_mode **mode, + struct ieee80211_channel **chan) +{ + struct ieee80211_hw_mode *m; + int prev; + + list_for_each_entry(m, &local->modes_list, list) { + *mode = m; + if (m->mode == local->hw.conf.phymode) + break; + } + local->scan.mode = m; + + /* Select next channel - scan only channels marked with W_SCAN flag */ + prev = local->scan.chan_idx; + do { + local->scan.chan_idx++; + if (local->scan.chan_idx >= m->num_channels) + local->scan.chan_idx = 0; + *chan = &m->channels[local->scan.chan_idx]; + if ((*chan)->flag & IEEE80211_CHAN_W_SCAN) + break; + } while (local->scan.chan_idx != prev); +} + + +static void next_chan_all_modes(struct ieee80211_local *local, + struct ieee80211_hw_mode **mode, + struct ieee80211_channel **chan) +{ + struct ieee80211_hw_mode *prev_m; + int prev; + + /* Select next channel - scan only channels marked with W_SCAN flag */ + prev = local->scan.chan_idx; + prev_m = local->scan.mode; + do { + *mode = local->scan.mode; + local->scan.chan_idx++; + if (local->scan.chan_idx >= (*mode)->num_channels) { + struct list_head *next; + + local->scan.chan_idx = 0; + next = (*mode)->list.next; + if (next == &local->modes_list) + next = next->next; + *mode = list_entry(next, + struct ieee80211_hw_mode, + list); + local->scan.mode = *mode; + } + *chan = &(*mode)->channels[local->scan.chan_idx]; + if ((*chan)->flag & IEEE80211_CHAN_W_SCAN) + break; + } while (local->scan.chan_idx != prev || + local->scan.mode != prev_m); +} + + +static void ieee80211_scan_start(struct ieee80211_local *local, + struct ieee80211_scan_conf *conf) +{ + struct ieee80211_hw_mode *old_mode = local->scan.mode; + int old_chan_idx = local->scan.chan_idx; + struct ieee80211_hw_mode *mode = NULL; + struct ieee80211_channel *chan = NULL; + int ret; + + if (!local->ops->passive_scan) { + printk(KERN_DEBUG "%s: Scan handler called, yet the hardware " + "does not support passive scanning. Disabled.\n", + local->mdev->name); + return; + } + + if ((local->scan.tries < MAX_SCAN_WAIT && + local->scan.txrx_count > SCAN_TXRX_THRESHOLD)) { + local->scan.tries++; + /* Count TX/RX packets during one second interval and allow + * scan to start only if the number of packets is below the + * threshold. */ + local->scan.txrx_count = 0; + local->scan.timer.expires = jiffies + HZ; + add_timer(&local->scan.timer); + return; + } + + if (!local->scan.skb) { + printk(KERN_DEBUG "%s: Scan start called even though scan.skb " + "is not set\n", local->mdev->name); + } + + if (local->scan.our_mode_only) { + if (local->scan.channel > 0) { + get_channel_params(local, local->scan.channel, &mode, + &chan); + } else + next_chan_same_mode(local, &mode, &chan); + } + else + next_chan_all_modes(local, &mode, &chan); + + conf->scan_channel = chan->chan; + conf->scan_freq = chan->freq; + conf->scan_channel_val = chan->val; + conf->scan_phymode = mode->mode; + conf->scan_power_level = chan->power_level; + conf->scan_antenna_max = chan->antenna_max; + conf->scan_time = 2 * local->hw.channel_change_time + + local->scan.time; /* 10ms scan time+hardware changes */ + conf->skb = local->scan.skb ? + skb_clone(local->scan.skb, GFP_ATOMIC) : NULL; + conf->tx_control = &local->scan.tx_control; +#if 0 + printk(KERN_DEBUG "%s: Doing scan on mode: %d freq: %d chan: %d " + "for %d ms\n", + local->mdev->name, conf->scan_phymode, conf->scan_freq, + conf->scan_channel, conf->scan_time); +#endif + local->scan.rx_packets = 0; + local->scan.rx_beacon = 0; + local->scan.freq = chan->freq; + local->scan.in_scan = 1; + + ieee80211_netif_oper(local_to_hw(local), NETIF_STOP); + + ret = local->ops->passive_scan(local_to_hw(local), + IEEE80211_SCAN_START, conf); + + if (ret == 0) { + long usec = local->hw.channel_change_time + + local->scan.time; + usec += 1000000L / HZ - 1; + usec /= 1000000L / HZ; + local->scan.timer.expires = jiffies + usec; + } else { + local->scan.in_scan = 0; + if (conf->skb) + dev_kfree_skb(conf->skb); + ieee80211_netif_oper(local_to_hw(local), NETIF_WAKE); + if (ret == -EAGAIN) { + local->scan.timer.expires = jiffies + + (local->scan.interval * HZ / 100); + local->scan.mode = old_mode; + local->scan.chan_idx = old_chan_idx; + } else { + printk(KERN_DEBUG "%s: Got unknown error from " + "passive_scan %d\n", local->mdev->name, ret); + local->scan.timer.expires = jiffies + + (local->scan.interval * HZ); + } + local->scan.in_scan = 0; + } + + add_timer(&local->scan.timer); +} + + +static void ieee80211_scan_stop(struct ieee80211_local *local, + struct ieee80211_scan_conf *conf) +{ + struct ieee80211_hw_mode *mode; + struct ieee80211_channel *chan; + int wait; + + if (!local->ops->passive_scan) + return; + + mode = local->scan.mode; + + if (local->scan.chan_idx >= mode->num_channels) + local->scan.chan_idx = 0; + + chan = &mode->channels[local->scan.chan_idx]; + + local->ops->passive_scan(local_to_hw(local), IEEE80211_SCAN_END, + conf); + +#ifdef CONFIG_MAC80211_VERBOSE_DEBUG + printk(KERN_DEBUG "%s: Did scan on mode: %d freq: %d chan: %d " + "GOT: %d Beacon: %d (%d)\n", + local->mdev->name, + mode->mode, chan->freq, chan->chan, + local->scan.rx_packets, local->scan.rx_beacon, + local->scan.tries); +#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ + local->scan.num_scans++; + + local->scan.in_scan = 0; + ieee80211_netif_oper(local_to_hw(local), NETIF_WAKE); + + local->scan.tries = 0; + /* Use random interval of scan.interval .. 2 * scan.interval */ + wait = (local->scan.interval * HZ * ((net_random() & 127) + 128)) / + 128; + local->scan.timer.expires = jiffies + wait; + + add_timer(&local->scan.timer); +} + + +static void ieee80211_scan_handler(unsigned long ullocal) +{ + struct ieee80211_local *local = (struct ieee80211_local *) ullocal; + struct ieee80211_scan_conf conf; + + if (local->scan.interval == 0 && !local->scan.in_scan) { + /* Passive scanning is disabled - keep the timer always + * running to make code cleaner. */ + local->scan.timer.expires = jiffies + 10 * HZ; + add_timer(&local->scan.timer); + return; + } + + memset(&conf, 0, sizeof(struct ieee80211_scan_conf)); + conf.running_freq = local->hw.conf.freq; + conf.running_channel = local->hw.conf.channel; + conf.running_phymode = local->hw.conf.phymode; + conf.running_channel_val = local->hw.conf.channel_val; + conf.running_power_level = local->hw.conf.power_level; + conf.running_antenna_max = local->hw.conf.antenna_max; + + if (local->scan.in_scan == 0) + ieee80211_scan_start(local, &conf); + else + ieee80211_scan_stop(local, &conf); +} + + +void ieee80211_init_scan(struct ieee80211_local *local) +{ + struct ieee80211_hdr hdr; + u16 fc; + int len = 10; + struct rate_control_extra extra; + + /* Only initialize passive scanning if the hardware supports it */ + if (!local->ops->passive_scan) { + local->scan.skb = NULL; + memset(&local->scan.tx_control, 0, + sizeof(local->scan.tx_control)); + printk(KERN_DEBUG "%s: Does not support passive scan, " + "disabled\n", local->mdev->name); + return; + } + + local->scan.interval = 0; + local->scan.our_mode_only = 1; + local->scan.time = 10000; + local->scan.timer.function = ieee80211_scan_handler; + local->scan.timer.data = (unsigned long) local; + local->scan.timer.expires = jiffies + local->scan.interval * HZ; + add_timer(&local->scan.timer); + + /* Create a CTS from for broadcasting before + * the low level changes channels */ + local->scan.skb = alloc_skb(len + local->hw.extra_tx_headroom, + GFP_KERNEL); + if (!local->scan.skb) { + printk(KERN_WARNING "%s: Failed to allocate CTS packet for " + "passive scan\n", local->mdev->name); + return; + } + skb_reserve(local->scan.skb, local->hw.extra_tx_headroom); + + fc = IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CTS; + hdr.frame_control = cpu_to_le16(fc); + hdr.duration_id = + cpu_to_le16(2 * local->hw.channel_change_time + + local->scan.time); + memcpy(hdr.addr1, local->mdev->dev_addr, ETH_ALEN); /* DA */ + hdr.seq_ctrl = 0; + + memcpy(skb_put(local->scan.skb, len), &hdr, len); + + memset(&local->scan.tx_control, 0, sizeof(local->scan.tx_control)); + local->scan.tx_control.key_idx = HW_KEY_IDX_INVALID; + local->scan.tx_control.flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT; + memset(&extra, 0, sizeof(extra)); + extra.mode = local->hw.conf.mode; + local->scan.tx_control.tx_rate = + rate_control_get_rate(local, local->mdev, + local->scan.skb, &extra)->val; + local->scan.tx_control.flags |= IEEE80211_TXCTL_NO_ACK; +} + + +void ieee80211_stop_scan(struct ieee80211_local *local) +{ + if (local->ops->passive_scan) { + del_timer_sync(&local->scan.timer); + dev_kfree_skb(local->scan.skb); + local->scan.skb = NULL; + } +} diff -puN /dev/null net/mac80211/ieee80211_sta.c --- /dev/null +++ a/net/mac80211/ieee80211_sta.c @@ -0,0 +1,3071 @@ +/* + * BSS client mode implementation + * Copyright 2003, Jouni Malinen + * Copyright 2004, Instant802 Networks, Inc. + * Copyright 2005, Devicescape Software, Inc. + * 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 "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) + +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 *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; + 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->mac.raw = skb->nh.raw = skb->h.raw = skb->data; + + 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; + + 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; + } + 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; + } + + 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; + + 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); +} + + +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; + } + + + 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); +} + + +void ieee80211_sta_rx_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; + + 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: + skb_queue_tail(&ifsta->skb_queue, skb); + schedule_work(&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; + } + + 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_if_sta *ifsta = (struct ieee80211_if_sta *) data; + set_bit(IEEE80211_STA_REQ_RUN, &ifsta->request); + schedule_work(&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; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + 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_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); + schedule_work(&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; + + if (!ifsta->auto_channel_sel && !ifsta->auto_bssid_sel && + !ifsta->auto_ssid_sel) { + ifsta->state = IEEE80211_AUTHENTICATE; + 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 (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; + 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; + } + 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; + + printk(KERN_DEBUG "%s: scan completed\n", dev->name); + 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); + + memset(&wrqu, 0, sizeof(wrqu)); + wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL); + + spin_lock_bh(&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->u.sta); + } + netif_wake_queue(sdata->dev); + } + spin_unlock_bh(&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) + schedule_delayed_work(&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; + } + + printk(KERN_DEBUG "%s: starting scan\n", dev->name); + + 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; + + spin_lock_bh(&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); + } + spin_unlock_bh(&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; + /* TODO: start scan as soon as all nullfunc frames are ACKed */ + schedule_delayed_work(&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); + schedule_work(&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->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) { + char *buf, *p; + int i; + buf = kmalloc(30 + bss->wpa_ie_len * 2, GFP_ATOMIC); + if (buf) { + p = buf; + p += sprintf(p, "wpa_ie="); + for (i = 0; i < bss->wpa_ie_len; i++) + p+= sprintf(p, "%02x", bss->wpa_ie[i]); + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(current_ev, end_buf, + &iwe, buf); + kfree(buf); + } + } + + if (bss && bss->rsn_ie) { + char *buf, *p; + int i; + buf = kmalloc(30 + bss->rsn_ie_len * 2, GFP_ATOMIC); + if (buf) { + p = buf; + p += sprintf(p, "rsn_ie="); + for (i = 0; i < bss->rsn_ie_len; i++) + p+= sprintf(p, "%02x", bss->rsn_ie[i]); + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(current_ev, end_buf, + &iwe, buf); + kfree(buf); + } + } + + 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, *p; + int i; + + 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, "rssi=%d", bss->rssi); + 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); + + /* dispaly all support rates in readable format */ + p = current_ev + IW_EV_LCP_LEN; + 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); + } + /* Check if we added any rate */ + if((p - current_ev) > IW_EV_LCP_LEN) + current_ev = p; + + 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; + struct net_device *sta_dev = 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; + } + + spin_lock_bh(&local->sub_if_lock); + list_for_each_entry(sdata, &local->sub_if_list, list) + if (sdata->type == IEEE80211_IF_TYPE_IBSS && + memcmp(bssid, sdata->u.sta.bssid, ETH_ALEN) == 0) { + sta_dev = sdata->dev; + break; + } + spin_unlock_bh(&local->sub_if_lock); + + if (!sta_dev) + return NULL; + + printk(KERN_DEBUG "%s: Adding new IBSS station " MAC_FMT " (dev=%s)\n", + dev->name, MAC_ARG(addr), sta_dev->name); + + sta = sta_info_add(local, dev, addr, GFP_ATOMIC); + if (!sta) + return NULL; + + sta->dev = sta_dev; + 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 -puN /dev/null net/mac80211/michael.c --- /dev/null +++ a/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 -puN /dev/null net/mac80211/michael.h --- /dev/null +++ a/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 -puN /dev/null net/mac80211/rc80211_simple.c --- /dev/null +++ a/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 -puN /dev/null net/mac80211/sta_info.c --- /dev/null +++ a/net/mac80211/sta_info.c @@ -0,0 +1,463 @@ +/* + * Copyright 2002-2005, 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 "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 */ + + if (!in_interrupt()) { + sta->sysfs_registered = 1; + ieee80211_sta_debugfs_add(sta); + rate_control_add_sta_debugfs(sta); + } else { + /* procfs entry adding might sleep, so schedule process context + * task for adding proc entry for STAs that do not yet have + * one. */ + schedule_work(&local->sta_proc_add); + } + + 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; + } + + if (in_atomic()) { + list_add(&sta->list, &local->deleted_sta_list); + schedule_work(&local->sta_proc_add); + } else + 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); +} + + +static void sta_info_proc_add_task(struct work_struct *work) +{ + struct ieee80211_local *local = + container_of(work, struct ieee80211_local, sta_proc_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->sysfs_registered) { + sta = tmp; + __sta_info_get(sta); + break; + } + } + spin_unlock_bh(&local->sta_lock); + + if (!sta) + break; + + sta->sysfs_registered = 1; + ieee80211_sta_debugfs_add(sta); + rate_control_add_sta_debugfs(sta); + sta_info_put(sta); + } +} + + +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; + + INIT_WORK(&local->sta_proc_add, sta_info_proc_add_task); +} + +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 sysfs 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 -puN /dev/null net/mac80211/sta_info.h --- /dev/null +++ a/net/mac80211/sta_info.h @@ -0,0 +1,164 @@ +/* + * 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_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 */ + + unsigned int sysfs_registered:1; + unsigned int assoc_ap:1; /* 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; + struct dentry *wme_rx_queue; + struct dentry *wme_tx_queue; + } 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 -puN /dev/null net/mac80211/tkip.c --- /dev/null +++ a/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 -puN /dev/null net/mac80211/tkip.h --- /dev/null +++ a/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 -puN /dev/null net/mac80211/wep.c --- /dev/null +++ a/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 -puN /dev/null net/mac80211/wep.h --- /dev/null +++ a/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 -puN /dev/null net/mac80211/wme.c --- /dev/null +++ a/net/mac80211/wme.c @@ -0,0 +1,679 @@ +/* + * 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" + +#define CHILD_QDISC_OPS pfifo_qdisc_ops + +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; + + /* 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 */ + tid = qc[0] & QOS_CONTROL_TID_MASK; + } 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 */ + } + } +#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; + /* 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; +} + + +/* 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; + +#ifdef CONFIG_NET_SCHED + 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; + } +#endif /* CONFIG_NET_SCHED */ + + /* 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); + 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) + 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; + struct tcf_proto *tp; + int queue; + + while ((tp = q->filter_list) != NULL) { + q->filter_list = tp->next; + tp->ops->destroy(tp); + } + + 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, &CHILD_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) +{ + /* printk(KERN_DEBUG "entering %s\n", __func__); */ +} + + +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; + /* printk(KERN_DEBUG "entering %s\n", __func__); */ + + 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; + /* printk(KERN_DEBUG "entering %s\n", __func__); */ + + 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; + /* printk(KERN_DEBUG "entering %s\n", __func__); */ + + 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; + /* printk(KERN_DEBUG "entering %s\n", __func__); */ + + 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); + /* printk("entering %s\n", __func__); */ + + 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_wme_register(void) +{ + int err = 0; + +#ifdef CONFIG_NET_SCHED + err = register_qdisc(&wme_qdisc_ops); +#endif + return err; +} + + +void ieee80211_wme_unregister(void) +{ +#ifdef CONFIG_NET_SCHED + unregister_qdisc(&wme_qdisc_ops); +#endif +} diff -puN /dev/null net/mac80211/wme.h --- /dev/null +++ a/net/mac80211/wme.h @@ -0,0 +1,38 @@ +/* + * 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); + +void ieee80211_install_qdisc(struct net_device *dev); + +int ieee80211_wme_register(void); +void ieee80211_wme_unregister(void); + +#endif diff -puN /dev/null net/mac80211/wpa.c --- /dev/null +++ a/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 -puN /dev/null net/mac80211/wpa.h --- /dev/null +++ a/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 -puN /dev/null net/wireless/Kconfig --- /dev/null +++ a/net/wireless/Kconfig @@ -0,0 +1,46 @@ +config CFG80211 + tristate "Improved wireless configuration API" + +config CFG80211_WEXT_COMPAT + bool "cfg80211 Wireless Extensions compatibility" + depends CFG80211 + default y + ---help--- + This option allows using devices whose drivers have been + converted to use the new cfg80211 with wireless extensions, + providing WE-20 compatibility. + + Note that cfg80211's "native" interface is nl80211 using + generic netlink. The wireless extensions are being + deprecated, but userspace tools may still be using them. + + If unsure, say Y. + +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 + ---help--- + This option enables the legacy wireless extensions + (wireless network interface configuration via ioctls.) + + Wireless extensions are being replaced by cfg80211 and + will be required only by legacy drivers that implement + 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 -puN /dev/null net/wireless/Makefile --- /dev/null +++ a/net/wireless/Makefile @@ -0,0 +1,17 @@ +obj-$(CONFIG_CFG80211) += cfg80211.o + +cfg80211-y += core.o sysfs.o +cfg80211-$(CONFIG_NL80211) += nl80211.o +cfg80211-$(CONFIG_CFG80211_WEXT_COMPAT) += wext-compat.o + +ifeq ($(CONFIG_CFG80211),m) +obj-$(CONFIG_CFG80211_WEXT_COMPAT) += wext-export.o +cfg80211-$(CONFIG_CFG80211_WEXT_COMPAT) += wext-mod.o +# we need something to tell us what's up... +# but we can't use #ifdef MODULE because we also need to +# know in the part that is built in (namely wext-common.c) +CFLAGS += -DCFG80211_MODULE +endif + +obj-$(CONFIG_WIRELESS_EXT) += wext-common.o wext-old.o +obj-$(CONFIG_CFG80211_WEXT_COMPAT) += wext-common.o diff -puN /dev/null net/wireless/core.c --- /dev/null +++ a/net/wireless/core.c @@ -0,0 +1,359 @@ +/* + * This is the linux wireless configuration interface. + * + * Copyright 2006, 2007 Johannes Berg + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nl80211.h" +#include "core.h" +#include "wext.h" +#include "sysfs.h" + +/* name for sysfs, %d is appended */ +#define PHY_NAME "phy" + +MODULE_AUTHOR("Johannes Berg"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("wireless configuration support"); + +/* RCU might be appropriate here since we usually + * only read the list, and that can happen quite + * often because we need to do it for each command */ +LIST_HEAD(cfg80211_drv_list); +DEFINE_MUTEX(cfg80211_drv_mutex); +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) +{ + struct cfg80211_registered_device *drv; + int alloc_size; + + alloc_size = sizeof(*drv) + sizeof_priv; + + drv = kzalloc(alloc_size, GFP_KERNEL); + if (!drv) + return NULL; + + drv->ops = ops; + + mutex_lock(&cfg80211_drv_mutex); + + if (unlikely(wiphy_counter<0)) { + /* ugh, wrapped! */ + kfree(drv); + return NULL; + } + drv->idx = wiphy_counter; + + /* give it a proper name */ + snprintf(drv->wiphy.dev.bus_id, BUS_ID_SIZE, + PHY_NAME "%d", drv->idx); + + /* now increase counter for the next time */ + wiphy_counter++; + mutex_unlock(&cfg80211_drv_mutex); + + mutex_init(&drv->mtx); + mutex_init(&drv->devlist_mtx); + INIT_LIST_HEAD(&drv->netdev_list); + + device_initialize(&drv->wiphy.dev); + drv->wiphy.dev.class = &ieee80211_class; + drv->wiphy.dev.platform_data = drv; + + return &drv->wiphy; +} +EXPORT_SYMBOL(wiphy_new); + +int wiphy_register(struct wiphy *wiphy) +{ + struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy); + int res; + + mutex_lock(&cfg80211_drv_mutex); + + + res = device_add(&drv->wiphy.dev); + if (res) + goto out_unlock; + + list_add(&drv->list, &cfg80211_drv_list); + + /* add to debugfs */ + drv->wiphy.debugfsdir = + debugfs_create_dir(wiphy_name(&drv->wiphy), + ieee80211_debugfs_dir); + + res = 0; + out_unlock: + mutex_unlock(&cfg80211_drv_mutex); + return res; +} +EXPORT_SYMBOL(wiphy_register); + +void wiphy_unregister(struct wiphy *wiphy) +{ + struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy); + + mutex_lock(&cfg80211_drv_mutex); + + /* hold registered driver mutex during list removal as well + * to make sure no commands are in progress at the moment */ + mutex_lock(&drv->mtx); + list_del(&drv->list); + mutex_unlock(&drv->mtx); + + device_del(&drv->wiphy.dev); + debugfs_remove(drv->wiphy.debugfsdir); + + mutex_unlock(&cfg80211_drv_mutex); +} +EXPORT_SYMBOL(wiphy_unregister); + +void cfg80211_dev_free(struct cfg80211_registered_device *drv) +{ + mutex_destroy(&drv->mtx); + mutex_destroy(&drv->devlist_mtx); + kfree(drv); +} + +void wiphy_free(struct wiphy *wiphy) +{ + put_device(&wiphy->dev); +} +EXPORT_SYMBOL(wiphy_free); + +static int cfg80211_netdev_notifier_call(struct notifier_block * nb, + unsigned long state, + void *ndev) +{ + struct net_device *dev = ndev; + struct cfg80211_registered_device *rdev; + + if (!dev->ieee80211_ptr) + return 0; + + rdev = wiphy_to_dev(dev->ieee80211_ptr->wiphy); + + switch (state) { + case NETDEV_REGISTER: + mutex_lock(&rdev->devlist_mtx); + list_add(&dev->ieee80211_ptr->list, &rdev->netdev_list); + if (sysfs_create_link(&dev->dev.kobj, &rdev->wiphy.dev.kobj, + "phy80211")) { + printk(KERN_ERR "wireless: failed to add phy80211 symlink to netdev!\n"); + } + dev->ieee80211_ptr->netdev = dev; + mutex_unlock(&rdev->devlist_mtx); + break; + case NETDEV_UNREGISTER: + mutex_lock(&rdev->devlist_mtx); + if (!list_empty(&dev->ieee80211_ptr->list)) { + sysfs_remove_link(&dev->dev.kobj, "phy80211"); + list_del_init(&dev->ieee80211_ptr->list); + } + mutex_unlock(&rdev->devlist_mtx); + break; + } + + return 0; +} + +static struct notifier_block cfg80211_netdev_notifier = { + .notifier_call = cfg80211_netdev_notifier_call, +}; + +static int cfg80211_init(void) +{ + int err = wiphy_sysfs_init(); + if (err) + goto out_fail_sysfs; + + err = register_netdevice_notifier(&cfg80211_netdev_notifier); + if (err) + goto out_fail_notifier; + + err = cfg80211_wext_init(); + if (err) + goto out_fail_wext; + + err = nl80211_init(); + if (err) + goto out_fail_nl80211; + + ieee80211_debugfs_dir = debugfs_create_dir("ieee80211", NULL); + + return 0; + + out_fail_nl80211: + cfg80211_wext_exit(); + out_fail_wext: + unregister_netdevice_notifier(&cfg80211_netdev_notifier); + out_fail_notifier: + wiphy_sysfs_exit(); + out_fail_sysfs: + return err; +} +module_init(cfg80211_init); + +static void cfg80211_exit(void) +{ + debugfs_remove(ieee80211_debugfs_dir); + nl80211_exit(); + cfg80211_wext_exit(); + unregister_netdevice_notifier(&cfg80211_netdev_notifier); + wiphy_sysfs_exit(); +} +module_exit(cfg80211_exit); diff -puN /dev/null net/wireless/core.h --- /dev/null +++ a/net/wireless/core.h @@ -0,0 +1,80 @@ +/* + * Wireless configuration interface internals. + * + * Copyright 2006 Johannes Berg + */ +#ifndef __NET_WIRELESS_CORE_H +#define __NET_WIRELESS_CORE_H +#include +#include +#include +#include +#include +#include + +struct cfg80211_registered_device { + struct cfg80211_ops *ops; + struct list_head list; + /* we hold this mutex during any call so that + * we cannot do multiple calls at once, and also + * to avoid the deregister call to proceed while + * any call is in progress */ + struct mutex mtx; + + /* wiphy index, internal only */ + int idx; + + /* associate netdev list */ + struct mutex devlist_mtx; + struct list_head netdev_list; + + /* must be last because of the way we do wiphy_priv(), + * and it should at least be aligned to NETDEV_ALIGN */ + struct wiphy wiphy __attribute__((__aligned__(NETDEV_ALIGN))); +}; + +static inline struct cfg80211_registered_device *wiphy_to_dev(struct wiphy *wiphy) +{ + BUG_ON(!wiphy); + return container_of(wiphy, 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 -puN /dev/null net/wireless/nl80211.c --- /dev/null +++ a/net/wireless/nl80211.c @@ -0,0 +1,982 @@ +/* + * This is the new netlink-based wireless configuration interface. + * + * Copyright 2006 Johannes Berg + */ + +#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_STRING, + .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_STRING, .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_STRING }, + [NL80211_ATTR_BEACON_TAIL] = { .type = NLA_STRING }, + [NL80211_ATTR_KEY_DATA] = { .type = NLA_STRING, .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(reassociate, REASSOCIATE); + 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; + } + + err = drv->ops->add_virtual_intf(&drv->wiphy, + nla_data(info->attrs[NL80211_ATTR_IFNAME]), type); + + 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; + } + + err = drv->ops->del_virtual_intf(&drv->wiphy, ifindex); + + 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; + } + + err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex, type); + + 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; + } + + err = drv->ops->get_association(&drv->wiphy, dev, bssid); + 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; + } + + err = drv->ops->associate(&drv->wiphy, dev, &assoc_params); + + 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; + } + + err = act(&drv->wiphy, dev); + 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; + err = drv->ops->get_auth_list(&drv->wiphy, dev, &cb, add_bssid); + 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; + + err = drv->ops->initiate_scan(&drv->wiphy, dev, ¶ms); + + 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; + } + + err = act(&drv->wiphy, dev, ¶ms); + + 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_REASSOCIATE, + .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 -puN /dev/null net/wireless/nl80211.h --- /dev/null +++ a/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 -puN /dev/null net/wireless/sysfs.c --- /dev/null +++ a/net/wireless/sysfs.c @@ -0,0 +1,125 @@ +/* + * This file provides /sys/class/ieee80211// + * and some default attributes. + * + * Copyright 2005-2006 Jiri Benc + * Copyright 2006 Johannes Berg + * + * This file is GPLv2 as found in COPYING. + */ + +#include +#include +#include +#include +#include +#include "sysfs.h" +#include "core.h" + +static inline struct cfg80211_registered_device *dev_to_rdev( + struct device *dev) +{ + return container_of(dev, struct cfg80211_registered_device, wiphy.dev); +} + +static ssize_t _show_index(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", dev_to_rdev(dev)->idx); +} + +static ssize_t _show_permaddr(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + char *addr = dev_to_rdev(dev)->wiphy.perm_addr; + + return sprintf(buf, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", + 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; + + res = rdev->ops->add_virtual_intf(&rdev->wiphy, (char*)buf, + NL80211_IFTYPE_UNSPECIFIED); + + 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); + + res = rdev->ops->del_virtual_intf(&rdev->wiphy, ifidx); + + 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), + {} +}; + +static void wiphy_dev_release(struct device *dev) +{ + struct cfg80211_registered_device *rdev = dev_to_rdev(dev); + + cfg80211_dev_free(rdev); +} + +static int wiphy_uevent(struct device *dev, char **envp, + int num_envp, char *buf, int size) +{ + /* TODO, we probably need stuff here */ + return 0; +} + +struct class ieee80211_class = { + .name = "ieee80211", + .owner = THIS_MODULE, + .dev_release = wiphy_dev_release, + .dev_attrs = ieee80211_dev_attrs, +#ifdef CONFIG_HOTPLUG + .dev_uevent = wiphy_uevent, +#endif +}; + +int wiphy_sysfs_init(void) +{ + return class_register(&ieee80211_class); +} + +void wiphy_sysfs_exit(void) +{ + class_unregister(&ieee80211_class); +} diff -puN /dev/null net/wireless/sysfs.h --- /dev/null +++ a/net/wireless/sysfs.h @@ -0,0 +1,9 @@ +#ifndef __WIRELESS_SYSFS_H +#define __WIRELESS_SYSFS_H + +extern int wiphy_sysfs_init(void); +extern void wiphy_sysfs_exit(void); + +extern struct class ieee80211_class; + +#endif /* __WIRELESS_SYSFS_H */ diff -puN /dev/null net/wireless/wext-common.c --- /dev/null +++ a/net/wireless/wext-common.c @@ -0,0 +1,663 @@ +/* + * common wext support routines, proc interface and events + * + * + * Most code is from the original wireless.c: + * Copyright 1997-2006 Jean Tourrilhes + * + * Copyright 2007 Johannes Berg + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wext.h" + +/* common data */ +/* + * Meta-data about all the standard Wireless Extension request we + * know about. + */ +const struct iw_ioctl_description wext_standard_ioctl[] = { + [SIOCSIWCOMMIT - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_NULL, + }, + [SIOCGIWNAME - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_CHAR, + .flags = IW_DESCR_FLAG_DUMP, + }, + [SIOCSIWNWID - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + .flags = IW_DESCR_FLAG_EVENT, + }, + [SIOCGIWNWID - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + .flags = IW_DESCR_FLAG_DUMP, + }, + [SIOCSIWFREQ - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_FREQ, + .flags = IW_DESCR_FLAG_EVENT, + }, + [SIOCGIWFREQ - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_FREQ, + .flags = IW_DESCR_FLAG_DUMP, + }, + [SIOCSIWMODE - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_UINT, + .flags = IW_DESCR_FLAG_EVENT, + }, + [SIOCGIWMODE - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_UINT, + .flags = IW_DESCR_FLAG_DUMP, + }, + [SIOCSIWSENS - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCGIWSENS - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCSIWRANGE - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_NULL, + }, + [SIOCGIWRANGE - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = sizeof(struct iw_range), + .flags = IW_DESCR_FLAG_DUMP, + }, + [SIOCSIWPRIV - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_NULL, + }, + [SIOCGIWPRIV - SIOCIWFIRST] = { /* (handled directly by us) */ + .header_type = IW_HEADER_TYPE_POINT, + .token_size = sizeof(struct iw_priv_args), + .max_tokens = 16, + .flags = IW_DESCR_FLAG_NOMAX, + }, + [SIOCSIWSTATS - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_NULL, + }, + [SIOCGIWSTATS - SIOCIWFIRST] = { /* (handled directly by us) */ + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = sizeof(struct iw_statistics), + .flags = IW_DESCR_FLAG_DUMP, + }, + [SIOCSIWSPY - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = sizeof(struct sockaddr), + .max_tokens = IW_MAX_SPY, + }, + [SIOCGIWSPY - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = sizeof(struct sockaddr) + + sizeof(struct iw_quality), + .max_tokens = IW_MAX_SPY, + }, + [SIOCSIWTHRSPY - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = sizeof(struct iw_thrspy), + .min_tokens = 1, + .max_tokens = 1, + }, + [SIOCGIWTHRSPY - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = sizeof(struct iw_thrspy), + .min_tokens = 1, + .max_tokens = 1, + }, + [SIOCSIWAP - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_ADDR, + }, + [SIOCGIWAP - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_ADDR, + .flags = IW_DESCR_FLAG_DUMP, + }, + [SIOCSIWMLME - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .min_tokens = sizeof(struct iw_mlme), + .max_tokens = sizeof(struct iw_mlme), + }, + [SIOCGIWAPLIST - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = sizeof(struct sockaddr) + + sizeof(struct iw_quality), + .max_tokens = IW_MAX_AP, + .flags = IW_DESCR_FLAG_NOMAX, + }, + [SIOCSIWSCAN - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .min_tokens = 0, + .max_tokens = sizeof(struct iw_scan_req), + }, + [SIOCGIWSCAN - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_SCAN_MAX_DATA, + .flags = IW_DESCR_FLAG_NOMAX, + }, + [SIOCSIWESSID - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_ESSID_MAX_SIZE, + .flags = IW_DESCR_FLAG_EVENT, + }, + [SIOCGIWESSID - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_ESSID_MAX_SIZE, + .flags = IW_DESCR_FLAG_DUMP, + }, + [SIOCSIWNICKN - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_ESSID_MAX_SIZE, + }, + [SIOCGIWNICKN - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_ESSID_MAX_SIZE, + }, + [SIOCSIWRATE - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCGIWRATE - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCSIWRTS - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCGIWRTS - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCSIWFRAG - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCGIWFRAG - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCSIWTXPOW - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCGIWTXPOW - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCSIWRETRY - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCGIWRETRY - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCSIWENCODE - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_ENCODING_TOKEN_MAX, + .flags = IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT, + }, + [SIOCGIWENCODE - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_ENCODING_TOKEN_MAX, + .flags = IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT, + }, + [SIOCSIWPOWER - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCGIWPOWER - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCSIWGENIE - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_GENERIC_IE_MAX, + }, + [SIOCGIWGENIE - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_GENERIC_IE_MAX, + }, + [SIOCSIWAUTH - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCGIWAUTH - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_PARAM, + }, + [SIOCSIWENCODEEXT - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .min_tokens = sizeof(struct iw_encode_ext), + .max_tokens = sizeof(struct iw_encode_ext) + + IW_ENCODING_TOKEN_MAX, + }, + [SIOCGIWENCODEEXT - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .min_tokens = sizeof(struct iw_encode_ext), + .max_tokens = sizeof(struct iw_encode_ext) + + IW_ENCODING_TOKEN_MAX, + }, + [SIOCSIWPMKSA - SIOCIWFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .min_tokens = sizeof(struct iw_pmksa), + .max_tokens = sizeof(struct iw_pmksa), + }, +}; +const unsigned wext_standard_ioctl_num = ARRAY_SIZE(wext_standard_ioctl); + +/* + * Meta-data about all the additional standard Wireless Extension events + * we know about. + */ +const struct iw_ioctl_description standard_event[] = { + [IWEVTXDROP - IWEVFIRST] = { + .header_type = IW_HEADER_TYPE_ADDR, + }, + [IWEVQUAL - IWEVFIRST] = { + .header_type = IW_HEADER_TYPE_QUAL, + }, + [IWEVCUSTOM - IWEVFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_CUSTOM_MAX, + }, + [IWEVREGISTERED - IWEVFIRST] = { + .header_type = IW_HEADER_TYPE_ADDR, + }, + [IWEVEXPIRED - IWEVFIRST] = { + .header_type = IW_HEADER_TYPE_ADDR, + }, + [IWEVGENIE - IWEVFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_GENERIC_IE_MAX, + }, + [IWEVMICHAELMICFAILURE - IWEVFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = sizeof(struct iw_michaelmicfailure), + }, + [IWEVASSOCREQIE - IWEVFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_GENERIC_IE_MAX, + }, + [IWEVASSOCRESPIE - IWEVFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = IW_GENERIC_IE_MAX, + }, + [IWEVPMKIDCAND - IWEVFIRST] = { + .header_type = IW_HEADER_TYPE_POINT, + .token_size = 1, + .max_tokens = sizeof(struct iw_pmkid_cand), + }, +}; +static unsigned standard_event_num = ARRAY_SIZE(standard_event); + +/* Size (in bytes) of various events */ +const int event_type_size[] = { + IW_EV_LCP_LEN, /* IW_HEADER_TYPE_NULL */ + 0, + IW_EV_CHAR_LEN, /* IW_HEADER_TYPE_CHAR */ + 0, + IW_EV_UINT_LEN, /* IW_HEADER_TYPE_UINT */ + IW_EV_FREQ_LEN, /* IW_HEADER_TYPE_FREQ */ + IW_EV_ADDR_LEN, /* IW_HEADER_TYPE_ADDR */ + 0, + IW_EV_POINT_LEN, /* Without variable payload */ + IW_EV_PARAM_LEN, /* IW_HEADER_TYPE_PARAM */ + IW_EV_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */ +}; + + +struct iw_statistics *get_wireless_stats(struct net_device *dev, + struct iw_statistics *out) +{ +#ifdef CONFIG_CFG80211 + if (dev->ieee80211_ptr && out) { + /* bah, just fake some stuff for now */ + memset(out, 0, sizeof(*out)); + return out; + } +#endif +#ifdef CONFIG_WIRELESS_EXT + if ((dev->wireless_handlers != NULL) && + (dev->wireless_handlers->get_wireless_stats != NULL)) + return dev->wireless_handlers->get_wireless_stats(dev); +#endif + return NULL; +} + +/* + * The /proc/net/wireless file is a human readable user-space interface + * exporting various wireless specific statistics from the wireless devices. + * This is the most popular part of the Wireless Extensions ;-) + * + * This interface is a pure clone of /proc/net/dev (in net/core/dev.c). + * The content of the file is basically the content of "struct iw_statistics". + */ + +#ifdef CONFIG_PROC_FS + +/* + * Print one entry (line) of /proc/net/wireless + */ +static void wireless_seq_printf_stats(struct seq_file *seq, + struct net_device *dev) +{ + /* Get stats from the driver */ + struct iw_statistics stats_buf; + struct iw_statistics *stats = get_wireless_stats(dev, &stats_buf); + + if (stats) { + seq_printf(seq, "%6s: %04x %3d%c %3d%c %3d%c %6d %6d %6d " + "%6d %6d %6d\n", + dev->name, stats->status, stats->qual.qual, + stats->qual.updated & IW_QUAL_QUAL_UPDATED + ? '.' : ' ', + ((__s32) stats->qual.level) - + ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0), + stats->qual.updated & IW_QUAL_LEVEL_UPDATED + ? '.' : ' ', + ((__s32) stats->qual.noise) - + ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0), + stats->qual.updated & IW_QUAL_NOISE_UPDATED + ? '.' : ' ', + stats->discard.nwid, stats->discard.code, + stats->discard.fragment, stats->discard.retries, + stats->discard.misc, stats->miss.beacon); + stats->qual.updated &= ~IW_QUAL_ALL_UPDATED; + } +} + +/* + * Print info for /proc/net/wireless (print all entries) + */ +static int wireless_seq_show(struct seq_file *seq, void *v) +{ + if (v == SEQ_START_TOKEN) + seq_printf(seq, "Inter-| sta-| Quality | Discarded " + "packets | Missed | WE\n" + " face | tus | link level noise | nwid " + "crypt frag retry misc | beacon | %d\n", + WIRELESS_EXT); + else + wireless_seq_printf_stats(seq, v); + return 0; +} + +static struct seq_operations wireless_seq_ops = { + .start = dev_seq_start, + .next = dev_seq_next, + .stop = dev_seq_stop, + .show = wireless_seq_show, +}; + +static int wireless_seq_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &wireless_seq_ops); +} + +static struct file_operations wireless_seq_fops = { + .owner = THIS_MODULE, + .open = wireless_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +int __init wireless_proc_init(void) +{ + /* Create /proc/net/wireless entry */ + if (!proc_net_fops_create("wireless", S_IRUGO, &wireless_seq_fops)) + return -ENOMEM; + + return 0; +} +#endif /* CONFIG_PROC_FS */ + +/* ---------------------------------------------------------------- */ +/* + * Locking... + * ---------- + * + * Thanks to Herbert Xu for fixing + * the locking issue in here and implementing this code ! + * + * The issue : wireless_send_event() is often called in interrupt context, + * while the Netlink layer can never be called in interrupt context. + * The fully formed RtNetlink events are queued, and then a tasklet is run + * to feed those to Netlink. + * The skb_queue is interrupt safe, and its lock is not held while calling + * Netlink, so there is no possibility of dealock. + * Jean II + */ + +static struct sk_buff_head wireless_nlevent_queue; + +static int __init wireless_nlevent_init(void) +{ + skb_queue_head_init(&wireless_nlevent_queue); + return 0; +} + +subsys_initcall(wireless_nlevent_init); + +static void wireless_nlevent_process(unsigned long data) +{ + struct sk_buff *skb; + + while ((skb = skb_dequeue(&wireless_nlevent_queue))) + rtnl_notify(skb, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC); +} + +static DECLARE_TASKLET(wireless_nlevent_tasklet, wireless_nlevent_process, 0); + +/* ---------------------------------------------------------------- */ +/* + * Fill a rtnetlink message with our event data. + * Note that we propage only the specified event and don't dump the + * current wireless config. Dumping the wireless config is far too + * expensive (for each parameter, the driver need to query the hardware). + */ +static inline int rtnetlink_fill_iwinfo(struct sk_buff * skb, + struct net_device * dev, + int type, + char * event, + int event_len) +{ + struct ifinfomsg *r; + struct nlmsghdr *nlh; + unsigned char *b = skb->tail; + + nlh = NLMSG_PUT(skb, 0, 0, type, sizeof(*r)); + r = NLMSG_DATA(nlh); + r->ifi_family = AF_UNSPEC; + r->__ifi_pad = 0; + r->ifi_type = dev->type; + r->ifi_index = dev->ifindex; + r->ifi_flags = dev_get_flags(dev); + r->ifi_change = 0; /* Wireless changes don't affect those flags */ + + /* Add the wireless events in the netlink packet */ + RTA_PUT(skb, IFLA_WIRELESS, event_len, event); + + nlh->nlmsg_len = skb->tail - b; + return skb->len; + +nlmsg_failure: +rtattr_failure: + skb_trim(skb, b - skb->data); + return -1; +} + +/* ---------------------------------------------------------------- */ +/* + * Create and broadcast and send it on the standard rtnetlink socket + * This is a pure clone rtmsg_ifinfo() in net/core/rtnetlink.c + * Andrzej Krzysztofowicz mandated that I used a IFLA_XXX field + * within a RTM_NEWLINK event. + */ +static inline void rtmsg_iwinfo(struct net_device * dev, + char * event, + int event_len) +{ + struct sk_buff *skb; + int size = NLMSG_GOODSIZE; + + skb = alloc_skb(size, GFP_ATOMIC); + if (!skb) + return; + + if (rtnetlink_fill_iwinfo(skb, dev, RTM_NEWLINK, + event, event_len) < 0) { + kfree_skb(skb); + return; + } + NETLINK_CB(skb).dst_group = RTNLGRP_LINK; + skb_queue_tail(&wireless_nlevent_queue, skb); + tasklet_schedule(&wireless_nlevent_tasklet); +} + +/* ---------------------------------------------------------------- */ +/* + * Main event dispatcher. Called from other parts and drivers. + * Send the event on the appropriate channels. + * May be called from interrupt context. + */ +void wireless_send_event(struct net_device * dev, + unsigned int cmd, + union iwreq_data * wrqu, + char * extra) +{ + const struct iw_ioctl_description * descr = NULL; + int extra_len = 0; + struct iw_event *event; /* Mallocated whole event */ + int event_len; /* Its size */ + int hdr_len; /* Size of the event header */ + int wrqu_off = 0; /* Offset in wrqu */ + /* Don't "optimise" the following variable, it will crash */ + unsigned cmd_index; /* *MUST* be unsigned */ + + /* Get the description of the Event */ + if(cmd <= SIOCIWLAST) { + cmd_index = cmd - SIOCIWFIRST; + if(cmd_index < wext_standard_ioctl_num) + descr = &(wext_standard_ioctl[cmd_index]); + } else { + cmd_index = cmd - IWEVFIRST; + if(cmd_index < standard_event_num) + descr = &(standard_event[cmd_index]); + } + /* Don't accept unknown events */ + if(descr == NULL) { + /* Note : we don't return an error to the driver, because + * the driver would not know what to do about it. It can't + * return an error to the user, because the event is not + * initiated by a user request. + * The best the driver could do is to log an error message. + * We will do it ourselves instead... + */ + printk(KERN_ERR "%s (WE) : Invalid/Unknown Wireless Event (0x%04X)\n", + dev->name, cmd); + return; + } + + /* Check extra parameters and set extra_len */ + if(descr->header_type == IW_HEADER_TYPE_POINT) { + /* Check if number of token fits within bounds */ + if(wrqu->data.length > descr->max_tokens) { + printk(KERN_ERR "%s (WE) : Wireless Event too big (%d)\n", dev->name, wrqu->data.length); + return; + } + if(wrqu->data.length < descr->min_tokens) { + printk(KERN_ERR "%s (WE) : Wireless Event too small (%d)\n", dev->name, wrqu->data.length); + return; + } + /* Calculate extra_len - extra is NULL for restricted events */ + if(extra != NULL) + extra_len = wrqu->data.length * descr->token_size; + /* Always at an offset in wrqu */ + wrqu_off = IW_EV_POINT_OFF; + } + + /* Total length of the event */ + hdr_len = event_type_size[descr->header_type]; + event_len = hdr_len + extra_len; + + /* Create temporary buffer to hold the event */ + event = kmalloc(event_len, GFP_ATOMIC); + if(event == NULL) + return; + + /* Fill event */ + event->len = event_len; + event->cmd = cmd; + memcpy(&event->u, ((char *) wrqu) + wrqu_off, hdr_len - IW_EV_LCP_LEN); + if(extra != NULL) + memcpy(((char *) event) + hdr_len, extra, extra_len); + + /* Send via the RtNetlink event channel */ + rtmsg_iwinfo(dev, (char *) event, event_len); + + /* Cleanup */ + kfree(event); + + return; /* Always success, I guess ;-) */ +} +EXPORT_SYMBOL(wireless_send_event); + +/* common code to handle wireless extension ioctls */ +int wext_ioctl(unsigned int cmd, struct ifreq *ifr, void __user *arg) +{ + int ret = -EINVAL; + + /* If command is `set a parameter', or `get the encoding parameters', + * check if the user is allowed to do it */ + if (IW_IS_SET(cmd) || cmd == SIOCGIWENCODE || cmd == SIOCGIWENCODEEXT) + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + +#ifdef CONFIG_WIRELESS_EXT + dev_load(ifr->ifr_name); + + /* we could change the code to not hold the rtnl but + * some callees might require it held */ + rtnl_lock(); + + /* Follow me in wext-old.c */ + ret = wireless_process_ioctl(ifr, cmd); + + rtnl_unlock(); + + /* haha, I cheat here by allowing a driver or stack to have both WE and + * CFG80211-WE for a little while during conversion... wext returns + * -EOPNOTSUPP if a handler is not assigned, so we can in that case try + * calling cfg80211's compat code instead. + */ + if (ret != -EOPNOTSUPP) + goto out; +#endif + +#ifdef CONFIG_CFG80211_WEXT_COMPAT + /* no need to hold rtnl lock here for the new stuff, + * we properly use dev_get_by_name() and dev_put() */ + ret = call_cfg80211_wext_ioctl(ifr, cmd); +#endif + + out: + if (IW_IS_GET(cmd) && copy_to_user(arg, ifr, sizeof(struct ifreq))) + ret = -EFAULT; + + return ret; +} diff -puN /dev/null net/wireless/wext-compat.c --- /dev/null +++ a/net/wireless/wext-compat.c @@ -0,0 +1,789 @@ +/* + * wireless extensions compatibility for cfg80211. + * + * Lots of code from the original wireless.c: + * Copyright 1997-2006 Jean Tourrilhes + * + * Copyright 2006,2007 Johannes Berg + * + * GPLv2. + * + * Theory of operation, so to speak: + * + * Commit is done some time after the last parameter was changed + * (with each parameter change simply (re-)schedule a timer) or + * if explicitly asked for. This is probably not what most people + * would expect, but perfectly fine in the WE API. + * + * another question I just thought about.. does wext expect to see + * the new config even if it wasn't committed... if so, we need to + * look at the pending config in various _get_ calls... + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "wext.h" + +/* internal API: use this function when changing + * some parameter that needs to be committed */ +static void cfg80211_wx_start_commit_timer(int ifindex) +{ + /* TODO: + * start a timer associate with this interface + * and then when it expires commit the pending + * data... + * This function must be callable when the timer + * is already running, and the timer must + * be able to deal with an unassigned + * dev->cfg80211_wext_pending_config pointer + * as well as taking the rtnl lock (due to wext)! */ +} + +static int cfg80211_wx_set_commit(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + return err; +} + +static int cfg80211_wx_get_name(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + return err; +} + +static int cfg80211_wx_set_nwid(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + return err; +} + +static int cfg80211_wx_get_nwid(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + return err; +} + +static int cfg80211_wx_set_freq(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + return err; +} + +static int cfg80211_wx_get_freq(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + return err; +} + +static int cfg80211_wx_set_mode(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + return err; +} + +static int cfg80211_wx_get_mode(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + return err; +} + +static int cfg80211_wx_set_sens(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + return err; +} + +static int cfg80211_wx_get_sens(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + return err; +} + +static int cfg80211_wx_set_range(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + return err; +} + +static int cfg80211_wx_get_range(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + return err; +} + +static int cfg80211_wx_set_ap(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + /* TODO: DO SOMETHING */ + /* SIOCSIWAP + * -> if bssid is all-ones: set roaming to kernel, reassociate + * -> if bssid is all-zeroes: set roaming to kernel + * -> otherwise: set roaming to userspace, set bssid + */ + + return err; +} + +static int cfg80211_wx_get_ap(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + /* TODO: DO SOMETHING */ + /* SIOCGIWAP + * -> get association parameters and fill return bssid appropriately + */ + + return err; +} + +static int cfg80211_wx_set_mlme(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + return err; +} + +static int cfg80211_wx_get_waplist(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + return err; +} + +static int cfg80211_wx_set_scan(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + return err; +} + +static int cfg80211_wx_get_scan(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + return err; +} + +static int cfg80211_wx_set_essid(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + return err; +} + +static int cfg80211_wx_get_essid(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + return err; +} + +static int cfg80211_wx_set_rate(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + return err; +} + +static int cfg80211_wx_get_rate(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + return err; +} + +static int cfg80211_wx_set_rts(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + return err; +} + +static int cfg80211_wx_get_rts(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + return err; +} + +static int cfg80211_wx_set_frag(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + return err; +} + +static int cfg80211_wx_get_frag(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + return err; +} + +static int cfg80211_wx_set_txpow(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + return err; +} + +static int cfg80211_wx_get_txpow(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + return err; +} + +static int cfg80211_wx_set_retry(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + return err; +} + +static int cfg80211_wx_get_retry(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + return err; +} + +static int cfg80211_wx_set_encode(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + return err; +} + +static int cfg80211_wx_get_encode(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + return err; +} + +static int cfg80211_wx_set_power(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + return err; +} + +static int cfg80211_wx_get_power(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + return err; +} + +static int cfg80211_wx_set_genie(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + return err; +} + +static int cfg80211_wx_get_genie(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + return err; +} + +static int cfg80211_wx_set_auth(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + return err; +} + +static int cfg80211_wx_get_auth(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + return err; +} + +static int cfg80211_wx_set_encodeext(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + return err; +} + +static int cfg80211_wx_get_encodeext(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + return err; +} + +static int cfg80211_wx_set_wpmksa(struct cfg80211_registered_device *drv, + struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + int err = -EOPNOTSUPP; + + return err; +} + + +typedef int (*iw_compat_handler)(struct cfg80211_registered_device *drv, + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra); + +/* operations array */ +#ifdef WX +# undef WX +#endif +#define WX(ioctl) [(ioctl) - SIOCIWFIRST] +static const iw_compat_handler cfg80211_wx_handlers[] = { + WX(SIOCSIWCOMMIT) = cfg80211_wx_set_commit, + WX(SIOCGIWNAME) = cfg80211_wx_get_name, + WX(SIOCSIWNWID) = cfg80211_wx_set_nwid, + WX(SIOCGIWNWID) = cfg80211_wx_get_nwid, + WX(SIOCSIWFREQ) = cfg80211_wx_set_freq, + WX(SIOCGIWFREQ) = cfg80211_wx_get_freq, + WX(SIOCSIWMODE) = cfg80211_wx_set_mode, + WX(SIOCGIWMODE) = cfg80211_wx_get_mode, + WX(SIOCSIWSENS) = cfg80211_wx_set_sens, + WX(SIOCGIWSENS) = cfg80211_wx_get_sens, + WX(SIOCSIWRANGE) = cfg80211_wx_set_range, + WX(SIOCGIWRANGE) = cfg80211_wx_get_range, + WX(SIOCSIWAP) = cfg80211_wx_set_ap, + WX(SIOCGIWAP) = cfg80211_wx_get_ap, + WX(SIOCSIWMLME) = cfg80211_wx_set_mlme, + WX(SIOCGIWAPLIST) = cfg80211_wx_get_waplist, + WX(SIOCSIWSCAN) = cfg80211_wx_set_scan, + WX(SIOCGIWSCAN) = cfg80211_wx_get_scan, + WX(SIOCSIWESSID) = cfg80211_wx_set_essid, + WX(SIOCGIWESSID) = cfg80211_wx_get_essid, + WX(SIOCSIWRATE) = cfg80211_wx_set_rate, + WX(SIOCGIWRATE) = cfg80211_wx_get_rate, + WX(SIOCSIWRTS) = cfg80211_wx_set_rts, + WX(SIOCGIWRTS) = cfg80211_wx_get_rts, + WX(SIOCSIWFRAG) = cfg80211_wx_set_frag, + WX(SIOCGIWFRAG) = cfg80211_wx_get_frag, + WX(SIOCSIWTXPOW) = cfg80211_wx_set_txpow, + WX(SIOCGIWTXPOW) = cfg80211_wx_get_txpow, + WX(SIOCSIWRETRY) = cfg80211_wx_set_retry, + WX(SIOCGIWRETRY) = cfg80211_wx_get_retry, + WX(SIOCSIWENCODE) = cfg80211_wx_set_encode, + WX(SIOCGIWENCODE) = cfg80211_wx_get_encode, + WX(SIOCSIWPOWER) = cfg80211_wx_set_power, + WX(SIOCGIWPOWER) = cfg80211_wx_get_power, + WX(SIOCSIWGENIE) = cfg80211_wx_set_genie, + WX(SIOCGIWGENIE) = cfg80211_wx_get_genie, + WX(SIOCSIWAUTH) = cfg80211_wx_set_auth, + WX(SIOCGIWAUTH) = cfg80211_wx_get_auth, + WX(SIOCSIWENCODEEXT) = cfg80211_wx_set_encodeext, + WX(SIOCGIWENCODEEXT) = cfg80211_wx_get_encodeext, + WX(SIOCSIWPMKSA) = cfg80211_wx_set_wpmksa, +}; + +/* dummy so I didn't have to change that much code... */ +static iw_compat_handler get_handler(unsigned int cmd) +{ + int idx = cmd - SIOCIWFIRST; + if (idx < ARRAY_SIZE(cfg80211_wx_handlers)) + return cfg80211_wx_handlers[idx]; + return NULL; +} + +/* + * this is sort of backwards and wouldn't need to call + * get_wireless_stats, but it was easier to just copy the code... + */ +static int iw_handler_get_iwstats(struct cfg80211_registered_device *drv, + struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + /* Get stats from the driver */ + struct iw_statistics stats_buf; + struct iw_statistics *stats; + + stats = get_wireless_stats(dev, &stats_buf); + if (stats != (struct iw_statistics *) NULL) { + + /* Copy statistics to extra */ + memcpy(extra, stats, sizeof(struct iw_statistics)); + wrqu->data.length = sizeof(struct iw_statistics); + + /* Check if we need to clear the updated flag */ + if(wrqu->data.flags != 0) + stats->qual.updated &= ~IW_QUAL_ALL_UPDATED; + return 0; + } else + return -EOPNOTSUPP; +} + +/* + * Wrapper to call a standard Wireless Extension handler. + * We do various checks and also take care of moving data between + * user space and kernel space. + */ +static int ioctl_standard_call(struct cfg80211_registered_device *drv, + struct net_device *dev, + struct ifreq *ifr, + unsigned int cmd, + iw_compat_handler handler) +{ + struct iwreq * iwr = (struct iwreq *) ifr; + const struct iw_ioctl_description * descr; + struct iw_request_info info; + int ret = -EINVAL; + + /* Get the description of the IOCTL */ + if((cmd - SIOCIWFIRST) >= wext_standard_ioctl_num) + return -EOPNOTSUPP; + descr = &(wext_standard_ioctl[cmd - SIOCIWFIRST]); + + /* Prepare the call */ + info.cmd = cmd; + info.flags = 0; + + /* Check if we have a pointer to user space data or not */ + if(descr->header_type != IW_HEADER_TYPE_POINT) { + + /* No extra arguments. Trivial to handle */ + ret = handler(drv, dev, &info, &(iwr->u), NULL); + + /* Generate an event to notify listeners of the change */ + if((descr->flags & IW_DESCR_FLAG_EVENT) && + ((ret == 0) || (ret == -EIWCOMMIT))) + wireless_send_event(dev, cmd, &(iwr->u), NULL); + } else { + char * extra; + int extra_size; + int user_length = 0; + int err; + + /* Calculate space needed by arguments. Always allocate + * for max space. Easier, and won't last long... */ + extra_size = descr->max_tokens * descr->token_size; + + /* Check what user space is giving us */ + if(IW_IS_SET(cmd)) { + /* Check NULL pointer */ + if((iwr->u.data.pointer == NULL) && + (iwr->u.data.length != 0)) + return -EFAULT; + /* Check if number of token fits within bounds */ + if(iwr->u.data.length > descr->max_tokens) + return -E2BIG; + if(iwr->u.data.length < descr->min_tokens) + return -EINVAL; + } else { + /* Check NULL pointer */ + if(iwr->u.data.pointer == NULL) + return -EFAULT; + /* Save user space buffer size for checking */ + user_length = iwr->u.data.length; + + /* Don't check if user_length > max to allow forward + * compatibility. The test user_length < min is + * implied by the test at the end. */ + + /* Support for very large requests */ + if((descr->flags & IW_DESCR_FLAG_NOMAX) && + (user_length > descr->max_tokens)) { + /* Allow userspace to GET more than max so + * we can support any size GET requests. + * There is still a limit : -ENOMEM. */ + extra_size = user_length * descr->token_size; + /* Note : user_length is originally a __u16, + * and token_size is controlled by us, + * so extra_size won't get negative and + * won't overflow... */ + } + } + + /* Create the kernel buffer */ + extra = kmalloc(extra_size, GFP_KERNEL); + if (extra == NULL) { + return -ENOMEM; + } + + /* If it is a SET, get all the extra data in here */ + if(IW_IS_SET(cmd) && (iwr->u.data.length != 0)) { + err = copy_from_user(extra, iwr->u.data.pointer, + iwr->u.data.length * + descr->token_size); + if (err) { + kfree(extra); + return -EFAULT; + } + } + + /* Call the handler */ + ret = handler(drv, dev, &info, &(iwr->u), extra); + + /* If we have something to return to the user */ + if (!ret && IW_IS_GET(cmd)) { + /* Check if there is enough buffer up there */ + if(user_length < iwr->u.data.length) { + kfree(extra); + return -E2BIG; + } + + err = copy_to_user(iwr->u.data.pointer, extra, + iwr->u.data.length * + descr->token_size); + if (err) + ret = -EFAULT; + } + + /* Generate an event to notify listeners of the change */ + if((descr->flags & IW_DESCR_FLAG_EVENT) && + ((ret == 0) || (ret == -EIWCOMMIT))) { + if(descr->flags & IW_DESCR_FLAG_RESTRICT) + /* If the event is restricted, don't + * export the payload */ + wireless_send_event(dev, cmd, &(iwr->u), NULL); + else + wireless_send_event(dev, cmd, &(iwr->u), + extra); + } + + /* Cleanup - I told you it wasn't that long ;-) */ + kfree(extra); + } + + return ret; +} + +/* and finally the ioctl wrapper */ +int cfg80211_wext_ioctl(struct ifreq *ifr, unsigned int cmd) +{ + struct net_device *dev; + iw_compat_handler handler; + struct cfg80211_registered_device *drv; + int err; + + /* Permissions are already checked in dev_ioctl() before calling us. + * The copy_to/from_user() of ifr is also dealt with in there */ + + dev = dev_get_by_name(ifr->ifr_name); + if (!dev) + return -ENODEV; + + drv = cfg80211_get_dev_from_ifindex(dev->ifindex); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out_put_dev; + } + + /* A bunch of special cases, then the generic case... + * Note that 'cmd' is already filtered in dev_ioctl() with + * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */ + switch(cmd) { + case SIOCGIWSTATS: + /* Get Wireless Stats */ + err = ioctl_standard_call(drv, dev, ifr, cmd, + &iw_handler_get_iwstats); + break; + case SIOCGIWPRIV: + err = -EOPNOTSUPP; + break; + default: + handler = get_handler(cmd); + if(cmd < SIOCIWFIRSTPRIV && handler != NULL) + err = ioctl_standard_call(drv, dev, ifr, cmd, + handler); + else + err = -EOPNOTSUPP; + } + + cfg80211_put_dev(drv); + out_put_dev: + dev_put(dev); + return err; +} diff -puN /dev/null net/wireless/wext-export.c --- /dev/null +++ a/net/wireless/wext-export.c @@ -0,0 +1,29 @@ +/* + * things we only need in the kernel when cfg80211 is modular. + * + * Copyright 2007 Johannes Berg + */ + +#include "wext.h" + +EXPORT_SYMBOL_GPL(wext_standard_ioctl); +EXPORT_SYMBOL_GPL(wext_standard_ioctl_num); +EXPORT_SYMBOL_GPL(get_wireless_stats); + +struct cfg80211_ioctl_ops cfg80211_ioctl_ops; +EXPORT_SYMBOL_GPL(cfg80211_ioctl_ops); + +int call_cfg80211_wext_ioctl(struct ifreq *ifr, unsigned int cmd) +{ + int err = -ENOSYS; + + if (!try_module_get(cfg80211_ioctl_ops.module)) + return -ENOSYS; + + if (cfg80211_ioctl_ops.do_wext_ioctl) + err = cfg80211_ioctl_ops.do_wext_ioctl(ifr, cmd); + + module_put(cfg80211_ioctl_ops.module); + + return err; +} diff -puN /dev/null net/wireless/wext-mod.c --- /dev/null +++ a/net/wireless/wext-mod.c @@ -0,0 +1,20 @@ +/* + * things we only need in cfg80211 when it is modular. + * + * Copyright 2007 Johannes Berg + */ + +#include "wext.h" + +int cfg80211_wext_init(void) +{ + cfg80211_ioctl_ops.do_wext_ioctl = cfg80211_wext_ioctl; + cfg80211_ioctl_ops.module = THIS_MODULE; + return 0; +} + +void cfg80211_wext_exit(void) +{ + cfg80211_ioctl_ops.module = NULL; + cfg80211_ioctl_ops.do_wext_ioctl = NULL; +} diff -puN /dev/null net/wireless/wext-old.c --- /dev/null +++ a/net/wireless/wext-old.c @@ -0,0 +1,853 @@ +/* + * This file implement the Wireless Extensions APIs. + * + * Authors : Jean Tourrilhes - HPL - + * Copyright (c) 1997-2006 Jean Tourrilhes, All Rights Reserved. + * + * (As all part of the Linux kernel, this file is GPL) + */ + +#include +#include /* off_t */ +#include /* struct ifreq, dev_get_by_name() */ +#include /* rtnetlink stuff */ +#include /* for __init */ +#include /* ARPHRD_ETHER */ +#include /* compare_ether_addr */ +#include + +#include /* Pretty obvious */ +#include /* New driver API */ +#include + +#include /* copy_to_user() */ + +#include "wext.h" + +/************************* GLOBAL VARIABLES *************************/ + +/* Size (in bytes) of the various private data types */ +static const char iw_priv_type_size[] = { + 0, /* IW_PRIV_TYPE_NONE */ + 1, /* IW_PRIV_TYPE_BYTE */ + 1, /* IW_PRIV_TYPE_CHAR */ + 0, /* Not defined */ + sizeof(__u32), /* IW_PRIV_TYPE_INT */ + sizeof(struct iw_freq), /* IW_PRIV_TYPE_FLOAT */ + sizeof(struct sockaddr), /* IW_PRIV_TYPE_ADDR */ + 0, /* Not defined */ +}; + +/************************ COMMON SUBROUTINES ************************/ +/* + * Stuff that may be used in various place or doesn't fit in one + * of the section below. + */ + +/* ---------------------------------------------------------------- */ +/* + * Return the driver handler associated with a specific Wireless Extension. + * Called from various place, so make sure it remains efficient. + */ +static inline iw_handler get_handler(struct net_device *dev, + unsigned int cmd) +{ + /* Don't "optimise" the following variable, it will crash */ + unsigned int index; /* *MUST* be unsigned */ + + /* Check if we have some wireless handlers defined */ + if(dev->wireless_handlers == NULL) + return NULL; + + /* Try as a standard command */ + index = cmd - SIOCIWFIRST; + if(index < dev->wireless_handlers->num_standard) + return dev->wireless_handlers->standard[index]; + + /* Try as a private command */ + index = cmd - SIOCIWFIRSTPRIV; + if(index < dev->wireless_handlers->num_private) + return dev->wireless_handlers->private[index]; + + /* Not found */ + return NULL; +} + +/* ---------------------------------------------------------------- */ +/* + * Call the commit handler in the driver + * (if exist and if conditions are right) + * + * Note : our current commit strategy is currently pretty dumb, + * but we will be able to improve on that... + * The goal is to try to agreagate as many changes as possible + * before doing the commit. Drivers that will define a commit handler + * are usually those that need a reset after changing parameters, so + * we want to minimise the number of reset. + * A cool idea is to use a timer : at each "set" command, we re-set the + * timer, when the timer eventually fires, we call the driver. + * Hopefully, more on that later. + * + * Also, I'm waiting to see how many people will complain about the + * netif_running(dev) test. I'm open on that one... + * Hopefully, the driver will remember to do a commit in "open()" ;-) + */ +static inline int call_commit_handler(struct net_device * dev) +{ + if((netif_running(dev)) && + (dev->wireless_handlers->standard[0] != NULL)) { + /* Call the commit handler on the driver */ + return dev->wireless_handlers->standard[0](dev, NULL, + NULL, NULL); + } else + return 0; /* Command completed successfully */ +} + +/* ---------------------------------------------------------------- */ +/* + * Calculate size of private arguments + */ +static inline int get_priv_size(__u16 args) +{ + int num = args & IW_PRIV_SIZE_MASK; + int type = (args & IW_PRIV_TYPE_MASK) >> 12; + + return num * iw_priv_type_size[type]; +} + +/* ---------------------------------------------------------------- */ +/* + * Re-calculate the size of private arguments + */ +static inline int adjust_priv_size(__u16 args, + union iwreq_data * wrqu) +{ + int num = wrqu->data.length; + int max = args & IW_PRIV_SIZE_MASK; + int type = (args & IW_PRIV_TYPE_MASK) >> 12; + + /* Make sure the driver doesn't goof up */ + if (max < num) + num = max; + + return num * iw_priv_type_size[type]; +} + +/* ---------------------------------------------------------------- */ +/* + * Standard Wireless Handler : get wireless stats + * Allow programatic access to /proc/net/wireless even if /proc + * doesn't exist... Also more efficient... + */ +static int iw_handler_get_iwstats(struct net_device * dev, + struct iw_request_info * info, + union iwreq_data * wrqu, + char * extra) +{ + /* Get stats from the driver */ + struct iw_statistics *stats; + + stats = get_wireless_stats(dev, NULL); + if (stats != (struct iw_statistics *) NULL) { + + /* Copy statistics to extra */ + memcpy(extra, stats, sizeof(struct iw_statistics)); + wrqu->data.length = sizeof(struct iw_statistics); + + /* Check if we need to clear the updated flag */ + if(wrqu->data.flags != 0) + stats->qual.updated &= ~IW_QUAL_ALL_UPDATED; + return 0; + } else + return -EOPNOTSUPP; +} + +/* ---------------------------------------------------------------- */ +/* + * Standard Wireless Handler : get iwpriv definitions + * Export the driver private handler definition + * They will be picked up by tools like iwpriv... + */ +static int iw_handler_get_private(struct net_device * dev, + struct iw_request_info * info, + union iwreq_data * wrqu, + char * extra) +{ + /* Check if the driver has something to export */ + if((dev->wireless_handlers->num_private_args == 0) || + (dev->wireless_handlers->private_args == NULL)) + return -EOPNOTSUPP; + + /* Check if there is enough buffer up there */ + if(wrqu->data.length < dev->wireless_handlers->num_private_args) { + /* User space can't know in advance how large the buffer + * needs to be. Give it a hint, so that we can support + * any size buffer we want somewhat efficiently... */ + wrqu->data.length = dev->wireless_handlers->num_private_args; + return -E2BIG; + } + + /* Set the number of available ioctls. */ + wrqu->data.length = dev->wireless_handlers->num_private_args; + + /* Copy structure to the user buffer. */ + memcpy(extra, dev->wireless_handlers->private_args, + sizeof(struct iw_priv_args) * wrqu->data.length); + + return 0; +} + +/************************** IOCTL SUPPORT **************************/ +/* + * The original user space API to configure all those Wireless Extensions + * is through IOCTLs. + * In there, we check if we need to call the new driver API (iw_handler) + * or just call the driver ioctl handler. + */ + +/* ---------------------------------------------------------------- */ +/* + * Wrapper to call a standard Wireless Extension handler. + * We do various checks and also take care of moving data between + * user space and kernel space. + */ +static int ioctl_standard_call(struct net_device * dev, + struct ifreq * ifr, + unsigned int cmd, + iw_handler handler) +{ + struct iwreq * iwr = (struct iwreq *) ifr; + const struct iw_ioctl_description * descr; + struct iw_request_info info; + int ret = -EINVAL; + + /* Get the description of the IOCTL */ + if((cmd - SIOCIWFIRST) >= wext_standard_ioctl_num) + return -EOPNOTSUPP; + descr = &(wext_standard_ioctl[cmd - SIOCIWFIRST]); + + /* Prepare the call */ + info.cmd = cmd; + info.flags = 0; + + /* Check if we have a pointer to user space data or not */ + if(descr->header_type != IW_HEADER_TYPE_POINT) { + + /* No extra arguments. Trivial to handle */ + ret = handler(dev, &info, &(iwr->u), NULL); + + /* Generate an event to notify listeners of the change */ + if((descr->flags & IW_DESCR_FLAG_EVENT) && + ((ret == 0) || (ret == -EIWCOMMIT))) + wireless_send_event(dev, cmd, &(iwr->u), NULL); + } else { + char * extra; + int extra_size; + int user_length = 0; + int err; + int essid_compat = 0; + + /* Calculate space needed by arguments. Always allocate + * for max space. Easier, and won't last long... */ + extra_size = descr->max_tokens * descr->token_size; + + /* Check need for ESSID compatibility for WE < 21 */ + switch (cmd) { + case SIOCSIWESSID: + case SIOCGIWESSID: + case SIOCSIWNICKN: + case SIOCGIWNICKN: + if (iwr->u.data.length == descr->max_tokens + 1) + essid_compat = 1; + else if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) { + char essid[IW_ESSID_MAX_SIZE + 1]; + + err = copy_from_user(essid, iwr->u.data.pointer, + iwr->u.data.length * + descr->token_size); + if (err) + return -EFAULT; + + if (essid[iwr->u.data.length - 1] == '\0') + essid_compat = 1; + } + break; + default: + break; + } + + iwr->u.data.length -= essid_compat; + + /* Check what user space is giving us */ + if(IW_IS_SET(cmd)) { + /* Check NULL pointer */ + if((iwr->u.data.pointer == NULL) && + (iwr->u.data.length != 0)) + return -EFAULT; + /* Check if number of token fits within bounds */ + if(iwr->u.data.length > descr->max_tokens) + return -E2BIG; + if(iwr->u.data.length < descr->min_tokens) + return -EINVAL; + } else { + /* Check NULL pointer */ + if(iwr->u.data.pointer == NULL) + return -EFAULT; + /* Save user space buffer size for checking */ + user_length = iwr->u.data.length; + + /* Don't check if user_length > max to allow forward + * compatibility. The test user_length < min is + * implied by the test at the end. */ + + /* Support for very large requests */ + if((descr->flags & IW_DESCR_FLAG_NOMAX) && + (user_length > descr->max_tokens)) { + /* Allow userspace to GET more than max so + * we can support any size GET requests. + * There is still a limit : -ENOMEM. */ + extra_size = user_length * descr->token_size; + /* Note : user_length is originally a __u16, + * and token_size is controlled by us, + * so extra_size won't get negative and + * won't overflow... */ + } + } + + /* Create the kernel buffer */ + /* kzalloc ensures NULL-termination for essid_compat */ + extra = kzalloc(extra_size, GFP_KERNEL); + if (extra == NULL) { + return -ENOMEM; + } + + /* If it is a SET, get all the extra data in here */ + if(IW_IS_SET(cmd) && (iwr->u.data.length != 0)) { + err = copy_from_user(extra, iwr->u.data.pointer, + iwr->u.data.length * + descr->token_size); + if (err) { + kfree(extra); + return -EFAULT; + } + } + + /* Call the handler */ + ret = handler(dev, &info, &(iwr->u), extra); + + iwr->u.data.length += essid_compat; + + /* If we have something to return to the user */ + if (!ret && IW_IS_GET(cmd)) { + /* Check if there is enough buffer up there */ + if(user_length < iwr->u.data.length) { + kfree(extra); + return -E2BIG; + } + + err = copy_to_user(iwr->u.data.pointer, extra, + iwr->u.data.length * + descr->token_size); + if (err) + ret = -EFAULT; + } + + /* Generate an event to notify listeners of the change */ + if((descr->flags & IW_DESCR_FLAG_EVENT) && + ((ret == 0) || (ret == -EIWCOMMIT))) { + if(descr->flags & IW_DESCR_FLAG_RESTRICT) + /* If the event is restricted, don't + * export the payload */ + wireless_send_event(dev, cmd, &(iwr->u), NULL); + else + wireless_send_event(dev, cmd, &(iwr->u), + extra); + } + + /* Cleanup - I told you it wasn't that long ;-) */ + kfree(extra); + } + + /* Call commit handler if needed and defined */ + if(ret == -EIWCOMMIT) + ret = call_commit_handler(dev); + + /* Here, we will generate the appropriate event if needed */ + + return ret; +} + +/* ---------------------------------------------------------------- */ +/* + * Wrapper to call a private Wireless Extension handler. + * We do various checks and also take care of moving data between + * user space and kernel space. + * It's not as nice and slimline as the standard wrapper. The cause + * is struct iw_priv_args, which was not really designed for the + * job we are going here. + * + * IMPORTANT : This function prevent to set and get data on the same + * IOCTL and enforce the SET/GET convention. Not doing it would be + * far too hairy... + * If you need to set and get data at the same time, please don't use + * a iw_handler but process it in your ioctl handler (i.e. use the + * old driver API). + */ +static inline int ioctl_private_call(struct net_device * dev, + struct ifreq * ifr, + unsigned int cmd, + iw_handler handler) +{ + struct iwreq * iwr = (struct iwreq *) ifr; + const struct iw_priv_args * descr = NULL; + struct iw_request_info info; + int extra_size = 0; + int i; + int ret = -EINVAL; + + /* Get the description of the IOCTL */ + for(i = 0; i < dev->wireless_handlers->num_private_args; i++) + if(cmd == dev->wireless_handlers->private_args[i].cmd) { + descr = &(dev->wireless_handlers->private_args[i]); + break; + } + + /* Compute the size of the set/get arguments */ + if(descr != NULL) { + if(IW_IS_SET(cmd)) { + int offset = 0; /* For sub-ioctls */ + /* Check for sub-ioctl handler */ + if(descr->name[0] == '\0') + /* Reserve one int for sub-ioctl index */ + offset = sizeof(__u32); + + /* Size of set arguments */ + extra_size = get_priv_size(descr->set_args); + + /* Does it fits in iwr ? */ + if((descr->set_args & IW_PRIV_SIZE_FIXED) && + ((extra_size + offset) <= IFNAMSIZ)) + extra_size = 0; + } else { + /* Size of get arguments */ + extra_size = get_priv_size(descr->get_args); + + /* Does it fits in iwr ? */ + if((descr->get_args & IW_PRIV_SIZE_FIXED) && + (extra_size <= IFNAMSIZ)) + extra_size = 0; + } + } + + /* Prepare the call */ + info.cmd = cmd; + info.flags = 0; + + /* Check if we have a pointer to user space data or not. */ + if(extra_size == 0) { + /* No extra arguments. Trivial to handle */ + ret = handler(dev, &info, &(iwr->u), (char *) &(iwr->u)); + } else { + char * extra; + int err; + + /* Check what user space is giving us */ + if(IW_IS_SET(cmd)) { + /* Check NULL pointer */ + if((iwr->u.data.pointer == NULL) && + (iwr->u.data.length != 0)) + return -EFAULT; + + /* Does it fits within bounds ? */ + if(iwr->u.data.length > (descr->set_args & + IW_PRIV_SIZE_MASK)) + return -E2BIG; + } else { + /* Check NULL pointer */ + if(iwr->u.data.pointer == NULL) + return -EFAULT; + } + + /* Always allocate for max space. Easier, and won't last + * long... */ + extra = kmalloc(extra_size, GFP_KERNEL); + if (extra == NULL) { + return -ENOMEM; + } + + /* If it is a SET, get all the extra data in here */ + if(IW_IS_SET(cmd) && (iwr->u.data.length != 0)) { + err = copy_from_user(extra, iwr->u.data.pointer, + extra_size); + if (err) { + kfree(extra); + return -EFAULT; + } + } + + /* Call the handler */ + ret = handler(dev, &info, &(iwr->u), extra); + + /* If we have something to return to the user */ + if (!ret && IW_IS_GET(cmd)) { + + /* Adjust for the actual length if it's variable, + * avoid leaking kernel bits outside. */ + if (!(descr->get_args & IW_PRIV_SIZE_FIXED)) { + extra_size = adjust_priv_size(descr->get_args, + &(iwr->u)); + } + + err = copy_to_user(iwr->u.data.pointer, extra, + extra_size); + if (err) + ret = -EFAULT; + } + + /* Cleanup - I told you it wasn't that long ;-) */ + kfree(extra); + } + + + /* Call commit handler if needed and defined */ + if(ret == -EIWCOMMIT) + ret = call_commit_handler(dev); + + return ret; +} + +/* ---------------------------------------------------------------- */ +/* + * Main IOCTl dispatcher. Called from the main networking code + * (dev_ioctl() in net/core/dev.c). + * Check the type of IOCTL and call the appropriate wrapper... + */ +int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd) +{ + struct net_device *dev; + iw_handler handler; + + /* Permissions are already checked in dev_ioctl() before calling us. + * The copy_to/from_user() of ifr is also dealt with in there */ + + /* Make sure the device exist */ + if ((dev = __dev_get_by_name(ifr->ifr_name)) == NULL) + return -ENODEV; + + /* A bunch of special cases, then the generic case... + * Note that 'cmd' is already filtered in dev_ioctl() with + * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */ + switch(cmd) { + case SIOCGIWSTATS: + /* Get Wireless Stats */ + return ioctl_standard_call(dev, + ifr, + cmd, + &iw_handler_get_iwstats); + + case SIOCGIWPRIV: + /* Check if we have some wireless handlers defined */ + if(dev->wireless_handlers != NULL) { + /* We export to user space the definition of + * the private handler ourselves */ + return ioctl_standard_call(dev, + ifr, + cmd, + &iw_handler_get_private); + } + // ## Fall-through for old API ## + default: + /* Generic IOCTL */ + /* Basic check */ + if (!netif_device_present(dev)) + return -ENODEV; + /* New driver API : try to find the handler */ + handler = get_handler(dev, cmd); + if(handler != NULL) { + /* Standard and private are not the same */ + if(cmd < SIOCIWFIRSTPRIV) + return ioctl_standard_call(dev, + ifr, + cmd, + handler); + else + return ioctl_private_call(dev, + ifr, + cmd, + handler); + } + /* Old driver API : call driver ioctl handler */ + if (dev->do_ioctl) { + return dev->do_ioctl(dev, ifr, cmd); + } + return -EOPNOTSUPP; + } + /* Not reached */ + return -EINVAL; +} + +/********************** RTNETLINK REQUEST API **********************/ +/* + * The alternate user space API to configure all those Wireless Extensions + * is through RtNetlink. + * This API support only the new driver API (iw_handler). + * + * This RtNetlink API use the same query/reply model as the ioctl API. + * Maximum effort has been done to fit in the RtNetlink model, and + * we support both RtNetlink Set and RtNelink Get operations. + * On the other hand, we don't offer Dump operations because of the + * following reasons : + * o Large number of parameters, most optional + * o Large size of some parameters (> 100 bytes) + * o Each parameters need to be extracted from hardware + * o Scan requests can take seconds and disable network activity. + * Because of this high cost/overhead, we want to return only the + * parameters the user application is really interested in. + * We could offer partial Dump using the IW_DESCR_FLAG_DUMP flag. + * + * The API uses the standard RtNetlink socket. When the RtNetlink code + * find a IFLA_WIRELESS field in a RtNetlink SET_LINK request, + * it calls here. + */ + +/********************** ENHANCED IWSPY SUPPORT **********************/ +/* + * In the old days, the driver was handling spy support all by itself. + * Now, the driver can delegate this task to Wireless Extensions. + * It needs to use those standard spy iw_handler in struct iw_handler_def, + * push data to us via wireless_spy_update() and include struct iw_spy_data + * in its private part (and export it in net_device->wireless_data->spy_data). + * One of the main advantage of centralising spy support here is that + * it becomes much easier to improve and extend it without having to touch + * the drivers. One example is the addition of the Spy-Threshold events. + */ + +/* ---------------------------------------------------------------- */ +/* + * Return the pointer to the spy data in the driver. + * Because this is called on the Rx path via wireless_spy_update(), + * we want it to be efficient... + */ +static inline struct iw_spy_data * get_spydata(struct net_device *dev) +{ + /* This is the new way */ + if(dev->wireless_data) + return(dev->wireless_data->spy_data); + return NULL; +} + +/*------------------------------------------------------------------*/ +/* + * Standard Wireless Handler : set Spy List + */ +int iw_handler_set_spy(struct net_device * dev, + struct iw_request_info * info, + union iwreq_data * wrqu, + char * extra) +{ + struct iw_spy_data * spydata = get_spydata(dev); + struct sockaddr * address = (struct sockaddr *) extra; + + /* Make sure driver is not buggy or using the old API */ + if(!spydata) + return -EOPNOTSUPP; + + /* Disable spy collection while we copy the addresses. + * While we copy addresses, any call to wireless_spy_update() + * will NOP. This is OK, as anyway the addresses are changing. */ + spydata->spy_number = 0; + + /* We want to operate without locking, because wireless_spy_update() + * most likely will happen in the interrupt handler, and therefore + * have its own locking constraints and needs performance. + * The rtnl_lock() make sure we don't race with the other iw_handlers. + * This make sure wireless_spy_update() "see" that the spy list + * is temporarily disabled. */ + smp_wmb(); + + /* Are there are addresses to copy? */ + if(wrqu->data.length > 0) { + int i; + + /* Copy addresses */ + for(i = 0; i < wrqu->data.length; i++) + memcpy(spydata->spy_address[i], address[i].sa_data, + ETH_ALEN); + /* Reset stats */ + memset(spydata->spy_stat, 0, + sizeof(struct iw_quality) * IW_MAX_SPY); + } + + /* Make sure above is updated before re-enabling */ + smp_wmb(); + + /* Enable addresses */ + spydata->spy_number = wrqu->data.length; + + return 0; +} +EXPORT_SYMBOL(iw_handler_set_spy); + +/*------------------------------------------------------------------*/ +/* + * Standard Wireless Handler : get Spy List + */ +int iw_handler_get_spy(struct net_device * dev, + struct iw_request_info * info, + union iwreq_data * wrqu, + char * extra) +{ + struct iw_spy_data * spydata = get_spydata(dev); + struct sockaddr * address = (struct sockaddr *) extra; + int i; + + /* Make sure driver is not buggy or using the old API */ + if(!spydata) + return -EOPNOTSUPP; + + wrqu->data.length = spydata->spy_number; + + /* Copy addresses. */ + for(i = 0; i < spydata->spy_number; i++) { + memcpy(address[i].sa_data, spydata->spy_address[i], ETH_ALEN); + address[i].sa_family = AF_UNIX; + } + /* Copy stats to the user buffer (just after). */ + if(spydata->spy_number > 0) + memcpy(extra + (sizeof(struct sockaddr) *spydata->spy_number), + spydata->spy_stat, + sizeof(struct iw_quality) * spydata->spy_number); + /* Reset updated flags. */ + for(i = 0; i < spydata->spy_number; i++) + spydata->spy_stat[i].updated &= ~IW_QUAL_ALL_UPDATED; + return 0; +} +EXPORT_SYMBOL(iw_handler_get_spy); + +/*------------------------------------------------------------------*/ +/* + * Standard Wireless Handler : set spy threshold + */ +int iw_handler_set_thrspy(struct net_device * dev, + struct iw_request_info *info, + union iwreq_data * wrqu, + char * extra) +{ + struct iw_spy_data * spydata = get_spydata(dev); + struct iw_thrspy * threshold = (struct iw_thrspy *) extra; + + /* Make sure driver is not buggy or using the old API */ + if(!spydata) + return -EOPNOTSUPP; + + /* Just do it */ + memcpy(&(spydata->spy_thr_low), &(threshold->low), + 2 * sizeof(struct iw_quality)); + + /* Clear flag */ + memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under)); + + return 0; +} +EXPORT_SYMBOL(iw_handler_set_thrspy); + +/*------------------------------------------------------------------*/ +/* + * Standard Wireless Handler : get spy threshold + */ +int iw_handler_get_thrspy(struct net_device * dev, + struct iw_request_info *info, + union iwreq_data * wrqu, + char * extra) +{ + struct iw_spy_data * spydata = get_spydata(dev); + struct iw_thrspy * threshold = (struct iw_thrspy *) extra; + + /* Make sure driver is not buggy or using the old API */ + if(!spydata) + return -EOPNOTSUPP; + + /* Just do it */ + memcpy(&(threshold->low), &(spydata->spy_thr_low), + 2 * sizeof(struct iw_quality)); + + return 0; +} +EXPORT_SYMBOL(iw_handler_get_thrspy); + +/*------------------------------------------------------------------*/ +/* + * Prepare and send a Spy Threshold event + */ +static void iw_send_thrspy_event(struct net_device * dev, + struct iw_spy_data * spydata, + unsigned char * address, + struct iw_quality * wstats) +{ + union iwreq_data wrqu; + struct iw_thrspy threshold; + + /* Init */ + wrqu.data.length = 1; + wrqu.data.flags = 0; + /* Copy address */ + memcpy(threshold.addr.sa_data, address, ETH_ALEN); + threshold.addr.sa_family = ARPHRD_ETHER; + /* Copy stats */ + memcpy(&(threshold.qual), wstats, sizeof(struct iw_quality)); + /* Copy also thresholds */ + memcpy(&(threshold.low), &(spydata->spy_thr_low), + 2 * sizeof(struct iw_quality)); + + /* Send event to user space */ + wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold); +} + +/* ---------------------------------------------------------------- */ +/* + * Call for the driver to update the spy data. + * For now, the spy data is a simple array. As the size of the array is + * small, this is good enough. If we wanted to support larger number of + * spy addresses, we should use something more efficient... + */ +void wireless_spy_update(struct net_device * dev, + unsigned char * address, + struct iw_quality * wstats) +{ + struct iw_spy_data * spydata = get_spydata(dev); + int i; + int match = -1; + + /* Make sure driver is not buggy or using the old API */ + if(!spydata) + return; + + /* Update all records that match */ + for(i = 0; i < spydata->spy_number; i++) + if(!compare_ether_addr(address, spydata->spy_address[i])) { + memcpy(&(spydata->spy_stat[i]), wstats, + sizeof(struct iw_quality)); + match = i; + } + + /* Generate an event if we cross the spy threshold. + * To avoid event storms, we have a simple hysteresis : we generate + * event only when we go under the low threshold or above the + * high threshold. */ + if(match >= 0) { + if(spydata->spy_thr_under[match]) { + if(wstats->level > spydata->spy_thr_high.level) { + spydata->spy_thr_under[match] = 0; + iw_send_thrspy_event(dev, spydata, + address, wstats); + } + } else { + if(wstats->level < spydata->spy_thr_low.level) { + spydata->spy_thr_under[match] = 1; + iw_send_thrspy_event(dev, spydata, + address, wstats); + } + } + } +} +EXPORT_SYMBOL(wireless_spy_update); diff -puN /dev/null net/wireless/wext.h --- /dev/null +++ a/net/wireless/wext.h @@ -0,0 +1,51 @@ +/* + * some foo for wext compat/wext interoperability + */ +#ifndef _WEXT_H +#define _WEXT_H +#include +#include +#include +#include + +/* wext compatibility must be compiled in... + * this extern is in wext-compat.c */ +struct cfg80211_ioctl_ops { + /* used to make sure the module isn't going away + * can't really happen, except if no driver has cfg80211 + * in use, but in that case */ + struct module *module; + + /* and finally this is used to do work */ + int (*do_wext_ioctl)(struct ifreq *ifr, unsigned int cmd); +}; +extern struct cfg80211_ioctl_ops cfg80211_ioctl_ops; + + +struct iw_statistics *get_wireless_stats(struct net_device *dev, + struct iw_statistics *out); +int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd); + +int cfg80211_wext_ioctl(struct ifreq *ifr, unsigned int cmd); +#ifdef CFG80211_MODULE +int call_cfg80211_wext_ioctl(struct ifreq *ifr, unsigned int cmd); +#else +#define call_cfg80211_wext_ioctl cfg80211_wext_ioctl +#endif + +#if defined(CONFIG_CFG80211_WEXT_COMPAT) && defined(CFG80211_MODULE) +int cfg80211_wext_init(void); +void cfg80211_wext_exit(void); +#else +static inline int cfg80211_wext_init(void) +{ + return 0; +} +static inline void cfg80211_wext_exit(void) {} +#endif + +extern const struct iw_ioctl_description wext_standard_ioctl[]; +extern const unsigned wext_standard_ioctl_num; +extern const struct iw_ioctl_description standard_event[]; +extern const int event_type_size[]; +#endif /* _WEXT_H */ _