Kalle Valo says: ==================== wireless-next patches for v6.8 The first features pull request for v6.8. Not so big in number of commits but we removed quite a few ancient drivers: libertas 16-bit PCMCIA support, atmel, hostap, zd1201, orinoco, ray_cs, wl3501 and rndis_wlan. Major changes: cfg80211/mac80211 - extend support for scanning while Multi-Link Operation (MLO) connected * tag 'wireless-next-2023-11-27' of git://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next: (68 commits) wifi: nl80211: Documentation update for NL80211_CMD_PORT_AUTHORIZED event wifi: mac80211: Extend support for scanning while MLO connected wifi: cfg80211: Extend support for scanning while MLO connected wifi: ieee80211: fix PV1 frame control field name rfkill: return ENOTTY on invalid ioctl MAINTAINERS: update iwlwifi maintainers wifi: rtw89: 8922a: read efuse content from physical map wifi: rtw89: 8922a: read efuse content via efuse map struct from logic map wifi: rtw89: 8852c: read RX gain offset from efuse for 6GHz channels wifi: rtw89: mac: add to access efuse for WiFi 7 chips wifi: rtw89: mac: use mac_gen pointer to access about efuse wifi: rtw89: 8922a: add 8922A basic chip info wifi: rtlwifi: drop unused const_amdpci_aspm wifi: mwifiex: mwifiex_process_sleep_confirm_resp(): remove unused priv variable wifi: rtw89: regd: update regulatory map to R65-R44 wifi: rtw89: regd: handle policy of 6 GHz according to BIOS wifi: rtw89: acpi: process 6 GHz band policy from DSM wifi: rtlwifi: simplify rtl_action_proc() and rtl_tx_agg_start() wifi: rtw89: pci: update interrupt mitigation register for 8922AE wifi: rtw89: pci: correct interrupt mitigation register for 8852CE ... ====================
Unified · Split
| @@ -10,7 +10,6 @@ Contents: · 9 unmodified lines | |||
| 10 | 10 | ||
| 11 | intel/ipw2100 | 11 | intel/ipw2100 |
| 12 | intel/ipw2200 | 12 | intel/ipw2200 |
| 13 | ray_cs | ||
| 14 | 13 | ||
| 15 | .. only:: subproject and html | 14 | .. only:: subproject and html |
| 16 | 15 | ||
| @@ -1,165 +0,0 @@ | |||
| 1 | .. SPDX-License-Identifier: GPL-2.0 | ||
| 2 | |||
| 3 | .. include:: <isonum.txt> | ||
| 4 | |||
| 5 | ========================= | ||
| 6 | Raylink wireless LAN card | ||
| 7 | ========================= | ||
| 8 | |||
| 9 | September 21, 1999 | ||
| 10 | |||
| 11 | Copyright |copy| 1998 Corey Thomas (corey@world.std.com) | ||
| 12 | |||
| 13 | This file is the documentation for the Raylink Wireless LAN card driver for | ||
| 14 | Linux. The Raylink wireless LAN card is a PCMCIA card which provides IEEE | ||
| 15 | 802.11 compatible wireless network connectivity at 1 and 2 megabits/second. | ||
| 16 | See http://www.raytheon.com/micro/raylink/ for more information on the Raylink | ||
| 17 | card. This driver is in early development and does have bugs. See the known | ||
| 18 | bugs and limitations at the end of this document for more information. | ||
| 19 | This driver also works with WebGear's Aviator 2.4 and Aviator Pro | ||
| 20 | wireless LAN cards. | ||
| 21 | |||
| 22 | As of kernel 2.3.18, the ray_cs driver is part of the Linux kernel | ||
| 23 | source. My web page for the development of ray_cs is at | ||
| 24 | http://web.ralinktech.com/ralink/Home/Support/Linux.html | ||
| 25 | and I can be emailed at corey@world.std.com | ||
| 26 | |||
| 27 | The kernel driver is based on ray_cs-1.62.tgz | ||
| 28 | |||
| 29 | The driver at my web page is intended to be used as an add on to | ||
| 30 | David Hinds pcmcia package. All the command line parameters are | ||
| 31 | available when compiled as a module. When built into the kernel, only | ||
| 32 | the essid= string parameter is available via the kernel command line. | ||
| 33 | This will change after the method of sorting out parameters for all | ||
| 34 | the PCMCIA drivers is agreed upon. If you must have a built in driver | ||
| 35 | with nondefault parameters, they can be edited in | ||
| 36 | /usr/src/linux/drivers/net/pcmcia/ray_cs.c. Searching for module_param | ||
| 37 | will find them all. | ||
| 38 | |||
| 39 | Information on card services is available at: | ||
| 40 | |||
| 41 | http://pcmcia-cs.sourceforge.net/ | ||
| 42 | |||
| 43 | |||
| 44 | Card services user programs are still required for PCMCIA devices. | ||
| 45 | pcmcia-cs-3.1.1 or greater is required for the kernel version of | ||
| 46 | the driver. | ||
| 47 | |||
| 48 | Currently, ray_cs is not part of David Hinds card services package, | ||
| 49 | so the following magic is required. | ||
| 50 | |||
| 51 | At the end of the /etc/pcmcia/config.opts file, add the line: | ||
| 52 | source ./ray_cs.opts | ||
| 53 | This will make card services read the ray_cs.opts file | ||
| 54 | when starting. Create the file /etc/pcmcia/ray_cs.opts containing the | ||
| 55 | following:: | ||
| 56 | |||
| 57 | #### start of /etc/pcmcia/ray_cs.opts ################### | ||
| 58 | # Configuration options for Raylink Wireless LAN PCMCIA card | ||
| 59 | device "ray_cs" | ||
| 60 | class "network" module "misc/ray_cs" | ||
| 61 | |||
| 62 | card "RayLink PC Card WLAN Adapter" | ||
| 63 | manfid 0x01a6, 0x0000 | ||
| 64 | bind "ray_cs" | ||
| 65 | |||
| 66 | module "misc/ray_cs" opts "" | ||
| 67 | #### end of /etc/pcmcia/ray_cs.opts ##################### | ||
| 68 | |||
| 69 | |||
| 70 | To join an existing network with | ||
| 71 | different parameters, contact the network administrator for the | ||
| 72 | configuration information, and edit /etc/pcmcia/ray_cs.opts. | ||
| 73 | Add the parameters below between the empty quotes. | ||
| 74 | |||
| 75 | Parameters for ray_cs driver which may be specified in ray_cs.opts: | ||
| 76 | |||
| 77 | =============== =============== ============================================= | ||
| 78 | bc integer 0 = normal mode (802.11 timing), | ||
| 79 | 1 = slow down inter frame timing to allow | ||
| 80 | operation with older breezecom access | ||
| 81 | points. | ||
| 82 | |||
| 83 | beacon_period integer beacon period in Kilo-microseconds, | ||
| 84 | |||
| 85 | legal values = must be integer multiple | ||
| 86 | of hop dwell | ||
| 87 | |||
| 88 | default = 256 | ||
| 89 | |||
| 90 | country integer 1 = USA (default), | ||
| 91 | 2 = Europe, | ||
| 92 | 3 = Japan, | ||
| 93 | 4 = Korea, | ||
| 94 | 5 = Spain, | ||
| 95 | 6 = France, | ||
| 96 | 7 = Israel, | ||
| 97 | 8 = Australia | ||
| 98 | |||
| 99 | essid string ESS ID - network name to join | ||
| 100 | |||
| 101 | string with maximum length of 32 chars | ||
| 102 | default value = "ADHOC_ESSID" | ||
| 103 | |||
| 104 | hop_dwell integer hop dwell time in Kilo-microseconds | ||
| 105 | |||
| 106 | legal values = 16,32,64,128(default),256 | ||
| 107 | |||
| 108 | irq_mask integer linux standard 16 bit value 1bit/IRQ | ||
| 109 | |||
| 110 | lsb is IRQ 0, bit 1 is IRQ 1 etc. | ||
| 111 | Used to restrict choice of IRQ's to use. | ||
| 112 | Recommended method for controlling | ||
| 113 | interrupts is in /etc/pcmcia/config.opts | ||
| 114 | |||
| 115 | net_type integer 0 (default) = adhoc network, | ||
| 116 | 1 = infrastructure | ||
| 117 | |||
| 118 | phy_addr string string containing new MAC address in | ||
| 119 | hex, must start with x eg | ||
| 120 | x00008f123456 | ||
| 121 | |||
| 122 | psm integer 0 = continuously active, | ||
| 123 | 1 = power save mode (not useful yet) | ||
| 124 | |||
| 125 | pc_debug integer (0-5) larger values for more verbose | ||
| 126 | logging. Replaces ray_debug. | ||
| 127 | |||
| 128 | ray_debug integer Replaced with pc_debug | ||
| 129 | |||
| 130 | ray_mem_speed integer defaults to 500 | ||
| 131 | |||
| 132 | sniffer integer 0 = not sniffer (default), | ||
| 133 | 1 = sniffer which can be used to record all | ||
| 134 | network traffic using tcpdump or similar, | ||
| 135 | but no normal network use is allowed. | ||
| 136 | |||
| 137 | translate integer 0 = no translation (encapsulate frames), | ||
| 138 | 1 = translation (RFC1042/802.1) | ||
| 139 | =============== =============== ============================================= | ||
| 140 | |||
| 141 | More on sniffer mode: | ||
| 142 | |||
| 143 | tcpdump does not understand 802.11 headers, so it can't | ||
| 144 | interpret the contents, but it can record to a file. This is only | ||
| 145 | useful for debugging 802.11 lowlevel protocols that are not visible to | ||
| 146 | linux. If you want to watch ftp xfers, or do similar things, you | ||
| 147 | don't need to use sniffer mode. Also, some packet types are never | ||
| 148 | sent up by the card, so you will never see them (ack, rts, cts, probe | ||
| 149 | etc.) There is a simple program (showcap) included in the ray_cs | ||
| 150 | package which parses the 802.11 headers. | ||
| 151 | |||
| 152 | Known Problems and missing features | ||
| 153 | |||
| 154 | Does not work with non x86 | ||
| 155 | |||
| 156 | Does not work with SMP | ||
| 157 | |||
| 158 | Support for defragmenting frames is not yet debugged, and in | ||
| 159 | fact is known to not work. I have never encountered a net set | ||
| 160 | up to fragment, but still, it should be fixed. | ||
| 161 | |||
| 162 | The ioctl support is incomplete. The hardware address cannot be set | ||
| 163 | using ifconfig yet. If a different hardware address is needed, it may | ||
| 164 | be set using the phy_addr parameter in ray_cs.opts. This requires | ||
| 165 | a card insertion to take effect. | ||
| @@ -2405,7 +2405,6 @@ F: drivers/memory/atmel* · 2404 unmodified lines | |||
| 2405 | F: drivers/watchdog/sama5d4_wdt.c | 2405 | F: drivers/watchdog/sama5d4_wdt.c |
| 2406 | F: include/soc/at91/ | 2406 | F: include/soc/at91/ |
| 2407 | X: drivers/input/touchscreen/atmel_mxt_ts.c | 2407 | X: drivers/input/touchscreen/atmel_mxt_ts.c |
| 2408 | X: drivers/net/wireless/atmel/ | ||
| 2409 | N: at91 | 2408 | N: at91 |
| 2410 | N: atmel | 2409 | N: atmel |
| 2411 | 2410 | ||
| @@ -3309,13 +3308,6 @@ T: git git://github.com/ndyer/linux.git · 897 unmodified lines | |||
| 3309 | F: Documentation/devicetree/bindings/input/atmel,maxtouch.yaml | 3308 | F: Documentation/devicetree/bindings/input/atmel,maxtouch.yaml |
| 3310 | F: drivers/input/touchscreen/atmel_mxt_ts.c | 3309 | F: drivers/input/touchscreen/atmel_mxt_ts.c |
| 3311 | 3310 | ||
| 3312 | ATMEL WIRELESS DRIVER | ||
| 3313 | L: linux-wireless@vger.kernel.org | ||
| 3314 | S: Orphan | ||
| 3315 | W: http://www.thekelleys.org.uk/atmel | ||
| 3316 | W: http://atmelwlandriver.sourceforge.net/ | ||
| 3317 | F: drivers/net/wireless/atmel/atmel* | ||
| 3318 | |||
| 3319 | ATOMIC INFRASTRUCTURE | 3311 | ATOMIC INFRASTRUCTURE |
| 3320 | M: Will Deacon <will@kernel.org> | 3312 | M: Will Deacon <will@kernel.org> |
| 3321 | M: Peter Zijlstra <peterz@infradead.org> | 3313 | M: Peter Zijlstra <peterz@infradead.org> |
| @@ -9709,11 +9701,6 @@ S: Maintained · 6387 unmodified lines | |||
| 9709 | F: Documentation/devicetree/bindings/iio/pressure/honeywell,mprls0025pa.yaml | 9701 | F: Documentation/devicetree/bindings/iio/pressure/honeywell,mprls0025pa.yaml |
| 9710 | F: drivers/iio/pressure/mprls0025pa.c | 9702 | F: drivers/iio/pressure/mprls0025pa.c |
| 9711 | 9703 | ||
| 9712 | HOST AP DRIVER | ||
| 9713 | L: linux-wireless@vger.kernel.org | ||
| 9714 | S: Obsolete | ||
| 9715 | F: drivers/net/wireless/intersil/hostap/ | ||
| 9716 | |||
| 9717 | HP BIOSCFG DRIVER | 9704 | HP BIOSCFG DRIVER |
| 9718 | M: Jorge Lopez <jorge.lopez2@hp.com> | 9705 | M: Jorge Lopez <jorge.lopez2@hp.com> |
| 9719 | L: platform-driver-x86@vger.kernel.org | 9706 | L: platform-driver-x86@vger.kernel.org |
| @@ -11017,6 +11004,7 @@ F: drivers/net/wireless/intel/iwlegacy/ · 1297 unmodified lines | |||
| 11017 | 11004 | ||
| 11018 | INTEL WIRELESS WIFI LINK (iwlwifi) | 11005 | INTEL WIRELESS WIFI LINK (iwlwifi) |
| 11019 | M: Gregory Greenman <gregory.greenman@intel.com> | 11006 | M: Gregory Greenman <gregory.greenman@intel.com> |
| 11007 | M: Miri Korenblit <miriam.rachel.korenblit@intel.com> | ||
| 11020 | L: linux-wireless@vger.kernel.org | 11008 | L: linux-wireless@vger.kernel.org |
| 11021 | S: Supported | 11009 | S: Supported |
| 11022 | W: https://wireless.wiki.kernel.org/en/users/drivers/iwlwifi | 11010 | W: https://wireless.wiki.kernel.org/en/users/drivers/iwlwifi |
| @@ -16269,13 +16257,6 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/hubcap/linux.git · 5246 unmodified lines | |||
| 16269 | F: Documentation/filesystems/orangefs.rst | 16257 | F: Documentation/filesystems/orangefs.rst |
| 16270 | F: fs/orangefs/ | 16258 | F: fs/orangefs/ |
| 16271 | 16259 | ||
| 16272 | ORINOCO DRIVER | ||
| 16273 | L: linux-wireless@vger.kernel.org | ||
| 16274 | S: Orphan | ||
| 16275 | W: https://wireless.wiki.kernel.org/en/users/Drivers/orinoco | ||
| 16276 | W: http://www.nongnu.org/orinoco/ | ||
| 16277 | F: drivers/net/wireless/intersil/orinoco/ | ||
| 16278 | |||
| 16279 | OV2659 OMNIVISION SENSOR DRIVER | 16260 | OV2659 OMNIVISION SENSOR DRIVER |
| 16280 | M: "Lad, Prabhakar" <prabhakar.csengg@gmail.com> | 16261 | M: "Lad, Prabhakar" <prabhakar.csengg@gmail.com> |
| 16281 | L: linux-media@vger.kernel.org | 16262 | L: linux-media@vger.kernel.org |
| @@ -18169,11 +18150,6 @@ F: drivers/ras/ · 1887 unmodified lines | |||
| 18169 | F: include/linux/ras.h | 18150 | F: include/linux/ras.h |
| 18170 | F: include/ras/ras_event.h | 18151 | F: include/ras/ras_event.h |
| 18171 | 18152 | ||
| 18172 | RAYLINK/WEBGEAR 802.11 WIRELESS LAN DRIVER | ||
| 18173 | L: linux-wireless@vger.kernel.org | ||
| 18174 | S: Orphan | ||
| 18175 | F: drivers/net/wireless/legacy/ray* | ||
| 18176 | |||
| 18177 | RC-CORE / LIRC FRAMEWORK | 18153 | RC-CORE / LIRC FRAMEWORK |
| 18178 | M: Sean Young <sean@mess.org> | 18154 | M: Sean Young <sean@mess.org> |
| 18179 | L: linux-media@vger.kernel.org | 18155 | L: linux-media@vger.kernel.org |
| @@ -22680,11 +22656,6 @@ F: drivers/usb/gadget/function/*uvc* · 4500 unmodified lines | |||
| 22680 | F: drivers/usb/gadget/legacy/webcam.c | 22656 | F: drivers/usb/gadget/legacy/webcam.c |
| 22681 | F: include/uapi/linux/usb/g_uvc.h | 22657 | F: include/uapi/linux/usb/g_uvc.h |
| 22682 | 22658 | ||
| 22683 | USB WIRELESS RNDIS DRIVER (rndis_wlan) | ||
| 22684 | L: linux-wireless@vger.kernel.org | ||
| 22685 | S: Orphan | ||
| 22686 | F: drivers/net/wireless/legacy/rndis_wlan.c | ||
| 22687 | |||
| 22688 | USB XHCI DRIVER | 22659 | USB XHCI DRIVER |
| 22689 | M: Mathias Nyman <mathias.nyman@intel.com> | 22660 | M: Mathias Nyman <mathias.nyman@intel.com> |
| 22690 | L: linux-usb@vger.kernel.org | 22661 | L: linux-usb@vger.kernel.org |
| @@ -22692,12 +22663,6 @@ S: Supported · 1 unmodified line | |||
| 22692 | F: drivers/usb/host/pci-quirks* | 22663 | F: drivers/usb/host/pci-quirks* |
| 22693 | F: drivers/usb/host/xhci* | 22664 | F: drivers/usb/host/xhci* |
| 22694 | 22665 | ||
| 22695 | USB ZD1201 DRIVER | ||
| 22696 | L: linux-wireless@vger.kernel.org | ||
| 22697 | S: Orphan | ||
| 22698 | W: http://linux-lc100020.sourceforge.net | ||
| 22699 | F: drivers/net/wireless/zydas/zd1201.* | ||
| 22700 | |||
| 22701 | USER DATAGRAM PROTOCOL (UDP) | 22666 | USER DATAGRAM PROTOCOL (UDP) |
| 22702 | M: Willem de Bruijn <willemdebruijn.kernel@gmail.com> | 22667 | M: Willem de Bruijn <willemdebruijn.kernel@gmail.com> |
| 22703 | S: Maintained | 22668 | S: Maintained |
| @@ -23499,11 +23464,6 @@ M: Miloslav Trmac <mitr@volny.cz> · 795 unmodified lines | |||
| 23499 | S: Maintained | 23464 | S: Maintained |
| 23500 | F: drivers/input/misc/wistron_btns.c | 23465 | F: drivers/input/misc/wistron_btns.c |
| 23501 | 23466 | ||
| 23502 | WL3501 WIRELESS PCMCIA CARD DRIVER | ||
| 23503 | L: linux-wireless@vger.kernel.org | ||
| 23504 | S: Orphan | ||
| 23505 | F: drivers/net/wireless/legacy/wl3501* | ||
| 23506 | |||
| 23507 | WMI BINARY MOF DRIVER | 23467 | WMI BINARY MOF DRIVER |
| 23508 | M: Armin Wolf <W_Armin@gmx.de> | 23468 | M: Armin Wolf <W_Armin@gmx.de> |
| 23509 | R: Thomas Weißschuh <linux@weissschuh.net> | 23469 | R: Thomas Weißschuh <linux@weissschuh.net> |
| @@ -22,7 +22,6 @@ source "drivers/net/wireless/admtek/Kconfig" · 21 unmodified lines | |||
| 22 | source "drivers/net/wireless/ath/Kconfig" | 22 | source "drivers/net/wireless/ath/Kconfig" |
| 23 | source "drivers/net/wireless/atmel/Kconfig" | 23 | source "drivers/net/wireless/atmel/Kconfig" |
| 24 | source "drivers/net/wireless/broadcom/Kconfig" | 24 | source "drivers/net/wireless/broadcom/Kconfig" |
| 25 | source "drivers/net/wireless/cisco/Kconfig" | ||
| 26 | source "drivers/net/wireless/intel/Kconfig" | 25 | source "drivers/net/wireless/intel/Kconfig" |
| 27 | source "drivers/net/wireless/intersil/Kconfig" | 26 | source "drivers/net/wireless/intersil/Kconfig" |
| 28 | source "drivers/net/wireless/marvell/Kconfig" | 27 | source "drivers/net/wireless/marvell/Kconfig" |
| @@ -38,8 +37,6 @@ source "drivers/net/wireless/ti/Kconfig" · 9 unmodified lines | |||
| 38 | source "drivers/net/wireless/zydas/Kconfig" | 37 | source "drivers/net/wireless/zydas/Kconfig" |
| 39 | source "drivers/net/wireless/quantenna/Kconfig" | 38 | source "drivers/net/wireless/quantenna/Kconfig" |
| 40 | 39 | ||
| 41 | source "drivers/net/wireless/legacy/Kconfig" | ||
| 42 | |||
| 43 | source "drivers/net/wireless/virtual/Kconfig" | 40 | source "drivers/net/wireless/virtual/Kconfig" |
| 44 | 41 | ||
| 45 | endif # WLAN | 42 | endif # WLAN |
| @@ -7,7 +7,6 @@ obj-$(CONFIG_WLAN_VENDOR_ADMTEK) += admtek/ · 6 unmodified lines | |||
| 7 | obj-$(CONFIG_WLAN_VENDOR_ATH) += ath/ | 7 | obj-$(CONFIG_WLAN_VENDOR_ATH) += ath/ |
| 8 | obj-$(CONFIG_WLAN_VENDOR_ATMEL) += atmel/ | 8 | obj-$(CONFIG_WLAN_VENDOR_ATMEL) += atmel/ |
| 9 | obj-$(CONFIG_WLAN_VENDOR_BROADCOM) += broadcom/ | 9 | obj-$(CONFIG_WLAN_VENDOR_BROADCOM) += broadcom/ |
| 10 | obj-$(CONFIG_WLAN_VENDOR_CISCO) += cisco/ | ||
| 11 | obj-$(CONFIG_WLAN_VENDOR_INTEL) += intel/ | 10 | obj-$(CONFIG_WLAN_VENDOR_INTEL) += intel/ |
| 12 | obj-$(CONFIG_WLAN_VENDOR_INTERSIL) += intersil/ | 11 | obj-$(CONFIG_WLAN_VENDOR_INTERSIL) += intersil/ |
| 13 | obj-$(CONFIG_WLAN_VENDOR_MARVELL) += marvell/ | 12 | obj-$(CONFIG_WLAN_VENDOR_MARVELL) += marvell/ |
| @@ -23,5 +22,4 @@ obj-$(CONFIG_WLAN_VENDOR_ST) += st/ · 9 unmodified lines | |||
| 23 | obj-$(CONFIG_WLAN_VENDOR_TI) += ti/ | 22 | obj-$(CONFIG_WLAN_VENDOR_TI) += ti/ |
| 24 | obj-$(CONFIG_WLAN_VENDOR_ZYDAS) += zydas/ | 23 | obj-$(CONFIG_WLAN_VENDOR_ZYDAS) += zydas/ |
| 25 | 24 | ||
| 26 | obj-$(CONFIG_WLAN) += legacy/ | ||
| 27 | obj-$(CONFIG_WLAN) += virtual/ | 25 | obj-$(CONFIG_WLAN) += virtual/ |
| @@ -12,41 +12,6 @@ config WLAN_VENDOR_ATMEL · 11 unmodified lines | |||
| 12 | 12 | ||
| 13 | if WLAN_VENDOR_ATMEL | 13 | if WLAN_VENDOR_ATMEL |
| 14 | 14 | ||
| 15 | config ATMEL | ||
| 16 | tristate "Atmel at76c50x chipset 802.11b support" | ||
| 17 | depends on CFG80211 && (PCI || PCMCIA) && HAS_IOPORT | ||
| 18 | select WIRELESS_EXT | ||
| 19 | select WEXT_PRIV | ||
| 20 | select FW_LOADER | ||
| 21 | select CRC32 | ||
| 22 | help | ||
| 23 | A driver 802.11b wireless cards based on the Atmel fast-vnet | ||
| 24 | chips. This driver supports standard Linux wireless extensions. | ||
| 25 | |||
| 26 | Many cards based on this chipset do not have flash memory | ||
| 27 | and need their firmware loaded at start-up. If yours is | ||
| 28 | one of these, you will need to provide a firmware image | ||
| 29 | to be loaded into the card by the driver. The Atmel | ||
| 30 | firmware package can be downloaded from | ||
| 31 | <http://www.thekelleys.org.uk/atmel> | ||
| 32 | |||
| 33 | config PCI_ATMEL | ||
| 34 | tristate "Atmel at76c506 PCI cards" | ||
| 35 | depends on ATMEL && PCI | ||
| 36 | help | ||
| 37 | Enable support for PCI and mini-PCI cards containing the | ||
| 38 | Atmel at76c506 chip. | ||
| 39 | |||
| 40 | config PCMCIA_ATMEL | ||
| 41 | tristate "Atmel at76c502/at76c504 PCMCIA cards" | ||
| 42 | depends on ATMEL && PCMCIA | ||
| 43 | select WIRELESS_EXT | ||
| 44 | select FW_LOADER | ||
| 45 | select CRC32 | ||
| 46 | help | ||
| 47 | Enable support for PCMCIA cards containing the | ||
| 48 | Atmel at76c502 and at76c504 chips. | ||
| 49 | |||
| 50 | config AT76C50X_USB | 15 | config AT76C50X_USB |
| 51 | tristate "Atmel at76c503/at76c505/at76c505a USB cards" | 16 | tristate "Atmel at76c503/at76c505/at76c505a USB cards" |
| 52 | depends on MAC80211 && USB | 17 | depends on MAC80211 && USB |
| @@ -1,6 +1,2 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0-only | 1 | # SPDX-License-Identifier: GPL-2.0-only |
| 2 | obj-$(CONFIG_ATMEL) += atmel.o | ||
| 3 | obj-$(CONFIG_PCI_ATMEL) += atmel_pci.o | ||
| 4 | obj-$(CONFIG_PCMCIA_ATMEL) += atmel_cs.o | ||
| 5 | |||
| 6 | obj-$(CONFIG_AT76C50X_USB) += at76c50x-usb.o | 2 | obj-$(CONFIG_AT76C50X_USB) += at76c50x-usb.o |
| @@ -1,4452 +0,0 @@ | |||
| 1 | /*** -*- linux-c -*- ********************************************************** | ||
| 2 | |||
| 3 | Driver for Atmel at76c502 at76c504 and at76c506 wireless cards. | ||
| 4 | |||
| 5 | Copyright 2000-2001 ATMEL Corporation. | ||
| 6 | Copyright 2003-2004 Simon Kelley. | ||
| 7 | |||
| 8 | This code was developed from version 2.1.1 of the Atmel drivers, | ||
| 9 | released by Atmel corp. under the GPL in December 2002. It also | ||
| 10 | includes code from the Linux aironet drivers (C) Benjamin Reed, | ||
| 11 | and the Linux PCMCIA package, (C) David Hinds and the Linux wireless | ||
| 12 | extensions, (C) Jean Tourrilhes. | ||
| 13 | |||
| 14 | The firmware module for reading the MAC address of the card comes from | ||
| 15 | net.russotto.AtmelMACFW, written by Matthew T. Russotto and copyright | ||
| 16 | by him. net.russotto.AtmelMACFW is used under the GPL license version 2. | ||
| 17 | This file contains the module in binary form and, under the terms | ||
| 18 | of the GPL, in source form. The source is located at the end of the file. | ||
| 19 | |||
| 20 | This program is free software; you can redistribute it and/or modify | ||
| 21 | it under the terms of the GNU General Public License as published by | ||
| 22 | the Free Software Foundation; either version 2 of the License, or | ||
| 23 | (at your option) any later version. | ||
| 24 | |||
| 25 | This software is distributed in the hope that it will be useful, | ||
| 26 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 27 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 28 | GNU General Public License for more details. | ||
| 29 | |||
| 30 | You should have received a copy of the GNU General Public License | ||
| 31 | along with Atmel wireless lan drivers; if not, see | ||
| 32 | <http://www.gnu.org/licenses/>. | ||
| 33 | |||
| 34 | For all queries about this code, please contact the current author, | ||
| 35 | Simon Kelley <simon@thekelleys.org.uk> and not Atmel Corporation. | ||
| 36 | |||
| 37 | Credit is due to HP UK and Cambridge Online Systems Ltd for supplying | ||
| 38 | hardware used during development of this driver. | ||
| 39 | |||
| 40 | ******************************************************************************/ | ||
| 41 | |||
| 42 | #include <linux/interrupt.h> | ||
| 43 | |||
| 44 | #include <linux/kernel.h> | ||
| 45 | #include <linux/ptrace.h> | ||
| 46 | #include <linux/slab.h> | ||
| 47 | #include <linux/string.h> | ||
| 48 | #include <linux/timer.h> | ||
| 49 | #include <asm/byteorder.h> | ||
| 50 | #include <asm/io.h> | ||
| 51 | #include <linux/uaccess.h> | ||
| 52 | #include <linux/module.h> | ||
| 53 | #include <linux/netdevice.h> | ||
| 54 | #include <linux/etherdevice.h> | ||
| 55 | #include <linux/skbuff.h> | ||
| 56 | #include <linux/if_arp.h> | ||
| 57 | #include <linux/ioport.h> | ||
| 58 | #include <linux/fcntl.h> | ||
| 59 | #include <linux/delay.h> | ||
| 60 | #include <linux/wireless.h> | ||
| 61 | #include <net/iw_handler.h> | ||
| 62 | #include <linux/crc32.h> | ||
| 63 | #include <linux/proc_fs.h> | ||
| 64 | #include <linux/seq_file.h> | ||
| 65 | #include <linux/device.h> | ||
| 66 | #include <linux/moduleparam.h> | ||
| 67 | #include <linux/firmware.h> | ||
| 68 | #include <linux/jiffies.h> | ||
| 69 | #include <net/cfg80211.h> | ||
| 70 | #include "atmel.h" | ||
| 71 | |||
| 72 | #define DRIVER_MAJOR 0 | ||
| 73 | #define DRIVER_MINOR 98 | ||
| 74 | |||
| 75 | MODULE_AUTHOR("Simon Kelley"); | ||
| 76 | MODULE_DESCRIPTION("Support for Atmel at76c50x 802.11 wireless ethernet cards."); | ||
| 77 | MODULE_LICENSE("GPL"); | ||
| 78 | |||
| 79 | /* The name of the firmware file to be loaded | ||
| 80 | over-rides any automatic selection */ | ||
| 81 | static char *firmware = NULL; | ||
| 82 | module_param(firmware, charp, 0); | ||
| 83 | |||
| 84 | /* table of firmware file names */ | ||
| 85 | static struct { | ||
| 86 | AtmelFWType fw_type; | ||
| 87 | const char *fw_file; | ||
| 88 | const char *fw_file_ext; | ||
| 89 | } fw_table[] = { | ||
| 90 | { ATMEL_FW_TYPE_502, "atmel_at76c502", "bin" }, | ||
| 91 | { ATMEL_FW_TYPE_502D, "atmel_at76c502d", "bin" }, | ||
| 92 | { ATMEL_FW_TYPE_502E, "atmel_at76c502e", "bin" }, | ||
| 93 | { ATMEL_FW_TYPE_502_3COM, "atmel_at76c502_3com", "bin" }, | ||
| 94 | { ATMEL_FW_TYPE_504, "atmel_at76c504", "bin" }, | ||
| 95 | { ATMEL_FW_TYPE_504_2958, "atmel_at76c504_2958", "bin" }, | ||
| 96 | { ATMEL_FW_TYPE_504A_2958, "atmel_at76c504a_2958", "bin" }, | ||
| 97 | { ATMEL_FW_TYPE_506, "atmel_at76c506", "bin" }, | ||
| 98 | { ATMEL_FW_TYPE_NONE, NULL, NULL } | ||
| 99 | }; | ||
| 100 | MODULE_FIRMWARE("atmel_at76c502-wpa.bin"); | ||
| 101 | MODULE_FIRMWARE("atmel_at76c502.bin"); | ||
| 102 | MODULE_FIRMWARE("atmel_at76c502d-wpa.bin"); | ||
| 103 | MODULE_FIRMWARE("atmel_at76c502d.bin"); | ||
| 104 | MODULE_FIRMWARE("atmel_at76c502e-wpa.bin"); | ||
| 105 | MODULE_FIRMWARE("atmel_at76c502e.bin"); | ||
| 106 | MODULE_FIRMWARE("atmel_at76c502_3com-wpa.bin"); | ||
| 107 | MODULE_FIRMWARE("atmel_at76c502_3com.bin"); | ||
| 108 | MODULE_FIRMWARE("atmel_at76c504-wpa.bin"); | ||
| 109 | MODULE_FIRMWARE("atmel_at76c504.bin"); | ||
| 110 | MODULE_FIRMWARE("atmel_at76c504_2958-wpa.bin"); | ||
| 111 | MODULE_FIRMWARE("atmel_at76c504_2958.bin"); | ||
| 112 | MODULE_FIRMWARE("atmel_at76c504a_2958-wpa.bin"); | ||
| 113 | MODULE_FIRMWARE("atmel_at76c504a_2958.bin"); | ||
| 114 | MODULE_FIRMWARE("atmel_at76c506-wpa.bin"); | ||
| 115 | MODULE_FIRMWARE("atmel_at76c506.bin"); | ||
| 116 | |||
| 117 | #define MAX_SSID_LENGTH 32 | ||
| 118 | #define MGMT_JIFFIES (256 * HZ / 100) | ||
| 119 | |||
| 120 | #define MAX_BSS_ENTRIES 64 | ||
| 121 | |||
| 122 | /* registers */ | ||
| 123 | #define GCR 0x00 /* (SIR0) General Configuration Register */ | ||
| 124 | #define BSR 0x02 /* (SIR1) Bank Switching Select Register */ | ||
| 125 | #define AR 0x04 | ||
| 126 | #define DR 0x08 | ||
| 127 | #define MR1 0x12 /* Mirror Register 1 */ | ||
| 128 | #define MR2 0x14 /* Mirror Register 2 */ | ||
| 129 | #define MR3 0x16 /* Mirror Register 3 */ | ||
| 130 | #define MR4 0x18 /* Mirror Register 4 */ | ||
| 131 | |||
| 132 | #define GPR1 0x0c | ||
| 133 | #define GPR2 0x0e | ||
| 134 | #define GPR3 0x10 | ||
| 135 | /* | ||
| 136 | * Constants for the GCR register. | ||
| 137 | */ | ||
| 138 | #define GCR_REMAP 0x0400 /* Remap internal SRAM to 0 */ | ||
| 139 | #define GCR_SWRES 0x0080 /* BIU reset (ARM and PAI are NOT reset) */ | ||
| 140 | #define GCR_CORES 0x0060 /* Core Reset (ARM and PAI are reset) */ | ||
| 141 | #define GCR_ENINT 0x0002 /* Enable Interrupts */ | ||
| 142 | #define GCR_ACKINT 0x0008 /* Acknowledge Interrupts */ | ||
| 143 | |||
| 144 | #define BSS_SRAM 0x0200 /* AMBA module selection --> SRAM */ | ||
| 145 | #define BSS_IRAM 0x0100 /* AMBA module selection --> IRAM */ | ||
| 146 | /* | ||
| 147 | *Constants for the MR registers. | ||
| 148 | */ | ||
| 149 | #define MAC_INIT_COMPLETE 0x0001 /* MAC init has been completed */ | ||
| 150 | #define MAC_BOOT_COMPLETE 0x0010 /* MAC boot has been completed */ | ||
| 151 | #define MAC_INIT_OK 0x0002 /* MAC boot has been completed */ | ||
| 152 | |||
| 153 | #define MIB_MAX_DATA_BYTES 212 | ||
| 154 | #define MIB_HEADER_SIZE 4 /* first four fields */ | ||
| 155 | |||
| 156 | struct get_set_mib { | ||
| 157 | u8 type; | ||
| 158 | u8 size; | ||
| 159 | u8 index; | ||
| 160 | u8 reserved; | ||
| 161 | u8 data[MIB_MAX_DATA_BYTES]; | ||
| 162 | }; | ||
| 163 | |||
| 164 | struct rx_desc { | ||
| 165 | u32 Next; | ||
| 166 | u16 MsduPos; | ||
| 167 | u16 MsduSize; | ||
| 168 | |||
| 169 | u8 State; | ||
| 170 | u8 Status; | ||
| 171 | u8 Rate; | ||
| 172 | u8 Rssi; | ||
| 173 | u8 LinkQuality; | ||
| 174 | u8 PreambleType; | ||
| 175 | u16 Duration; | ||
| 176 | u32 RxTime; | ||
| 177 | }; | ||
| 178 | |||
| 179 | #define RX_DESC_FLAG_VALID 0x80 | ||
| 180 | #define RX_DESC_FLAG_CONSUMED 0x40 | ||
| 181 | #define RX_DESC_FLAG_IDLE 0x00 | ||
| 182 | |||
| 183 | #define RX_STATUS_SUCCESS 0x00 | ||
| 184 | |||
| 185 | #define RX_DESC_MSDU_POS_OFFSET 4 | ||
| 186 | #define RX_DESC_MSDU_SIZE_OFFSET 6 | ||
| 187 | #define RX_DESC_FLAGS_OFFSET 8 | ||
| 188 | #define RX_DESC_STATUS_OFFSET 9 | ||
| 189 | #define RX_DESC_RSSI_OFFSET 11 | ||
| 190 | #define RX_DESC_LINK_QUALITY_OFFSET 12 | ||
| 191 | #define RX_DESC_PREAMBLE_TYPE_OFFSET 13 | ||
| 192 | #define RX_DESC_DURATION_OFFSET 14 | ||
| 193 | #define RX_DESC_RX_TIME_OFFSET 16 | ||
| 194 | |||
| 195 | struct tx_desc { | ||
| 196 | u32 NextDescriptor; | ||
| 197 | u16 TxStartOfFrame; | ||
| 198 | u16 TxLength; | ||
| 199 | |||
| 200 | u8 TxState; | ||
| 201 | u8 TxStatus; | ||
| 202 | u8 RetryCount; | ||
| 203 | |||
| 204 | u8 TxRate; | ||
| 205 | |||
| 206 | u8 KeyIndex; | ||
| 207 | u8 ChiperType; | ||
| 208 | u8 ChipreLength; | ||
| 209 | u8 Reserved1; | ||
| 210 | |||
| 211 | u8 Reserved; | ||
| 212 | u8 PacketType; | ||
| 213 | u16 HostTxLength; | ||
| 214 | }; | ||
| 215 | |||
| 216 | #define TX_DESC_NEXT_OFFSET 0 | ||
| 217 | #define TX_DESC_POS_OFFSET 4 | ||
| 218 | #define TX_DESC_SIZE_OFFSET 6 | ||
| 219 | #define TX_DESC_FLAGS_OFFSET 8 | ||
| 220 | #define TX_DESC_STATUS_OFFSET 9 | ||
| 221 | #define TX_DESC_RETRY_OFFSET 10 | ||
| 222 | #define TX_DESC_RATE_OFFSET 11 | ||
| 223 | #define TX_DESC_KEY_INDEX_OFFSET 12 | ||
| 224 | #define TX_DESC_CIPHER_TYPE_OFFSET 13 | ||
| 225 | #define TX_DESC_CIPHER_LENGTH_OFFSET 14 | ||
| 226 | #define TX_DESC_PACKET_TYPE_OFFSET 17 | ||
| 227 | #define TX_DESC_HOST_LENGTH_OFFSET 18 | ||
| 228 | |||
| 229 | /* | ||
| 230 | * Host-MAC interface | ||
| 231 | */ | ||
| 232 | |||
| 233 | #define TX_STATUS_SUCCESS 0x00 | ||
| 234 | |||
| 235 | #define TX_FIRM_OWN 0x80 | ||
| 236 | #define TX_DONE 0x40 | ||
| 237 | |||
| 238 | #define TX_ERROR 0x01 | ||
| 239 | |||
| 240 | #define TX_PACKET_TYPE_DATA 0x01 | ||
| 241 | #define TX_PACKET_TYPE_MGMT 0x02 | ||
| 242 | |||
| 243 | #define ISR_EMPTY 0x00 /* no bits set in ISR */ | ||
| 244 | #define ISR_TxCOMPLETE 0x01 /* packet transmitted */ | ||
| 245 | #define ISR_RxCOMPLETE 0x02 /* packet received */ | ||
| 246 | #define ISR_RxFRAMELOST 0x04 /* Rx Frame lost */ | ||
| 247 | #define ISR_FATAL_ERROR 0x08 /* Fatal error */ | ||
| 248 | #define ISR_COMMAND_COMPLETE 0x10 /* command completed */ | ||
| 249 | #define ISR_OUT_OF_RANGE 0x20 /* command completed */ | ||
| 250 | #define ISR_IBSS_MERGE 0x40 /* (4.1.2.30): IBSS merge */ | ||
| 251 | #define ISR_GENERIC_IRQ 0x80 | ||
| 252 | |||
| 253 | #define Local_Mib_Type 0x01 | ||
| 254 | #define Mac_Address_Mib_Type 0x02 | ||
| 255 | #define Mac_Mib_Type 0x03 | ||
| 256 | #define Statistics_Mib_Type 0x04 | ||
| 257 | #define Mac_Mgmt_Mib_Type 0x05 | ||
| 258 | #define Mac_Wep_Mib_Type 0x06 | ||
| 259 | #define Phy_Mib_Type 0x07 | ||
| 260 | #define Multi_Domain_MIB 0x08 | ||
| 261 | |||
| 262 | #define MAC_MGMT_MIB_CUR_BSSID_POS 14 | ||
| 263 | #define MAC_MIB_FRAG_THRESHOLD_POS 8 | ||
| 264 | #define MAC_MIB_RTS_THRESHOLD_POS 10 | ||
| 265 | #define MAC_MIB_SHORT_RETRY_POS 16 | ||
| 266 | #define MAC_MIB_LONG_RETRY_POS 17 | ||
| 267 | #define MAC_MIB_SHORT_RETRY_LIMIT_POS 16 | ||
| 268 | #define MAC_MGMT_MIB_BEACON_PER_POS 0 | ||
| 269 | #define MAC_MGMT_MIB_STATION_ID_POS 6 | ||
| 270 | #define MAC_MGMT_MIB_CUR_PRIVACY_POS 11 | ||
| 271 | #define MAC_MGMT_MIB_CUR_BSSID_POS 14 | ||
| 272 | #define MAC_MGMT_MIB_PS_MODE_POS 53 | ||
| 273 | #define MAC_MGMT_MIB_LISTEN_INTERVAL_POS 54 | ||
| 274 | #define MAC_MGMT_MIB_MULTI_DOMAIN_IMPLEMENTED 56 | ||
| 275 | #define MAC_MGMT_MIB_MULTI_DOMAIN_ENABLED 57 | ||
| 276 | #define PHY_MIB_CHANNEL_POS 14 | ||
| 277 | #define PHY_MIB_RATE_SET_POS 20 | ||
| 278 | #define PHY_MIB_REG_DOMAIN_POS 26 | ||
| 279 | #define LOCAL_MIB_AUTO_TX_RATE_POS 3 | ||
| 280 | #define LOCAL_MIB_SSID_SIZE 5 | ||
| 281 | #define LOCAL_MIB_TX_PROMISCUOUS_POS 6 | ||
| 282 | #define LOCAL_MIB_TX_MGMT_RATE_POS 7 | ||
| 283 | #define LOCAL_MIB_TX_CONTROL_RATE_POS 8 | ||
| 284 | #define LOCAL_MIB_PREAMBLE_TYPE 9 | ||
| 285 | #define MAC_ADDR_MIB_MAC_ADDR_POS 0 | ||
| 286 | |||
| 287 | #define CMD_Set_MIB_Vars 0x01 | ||
| 288 | #define CMD_Get_MIB_Vars 0x02 | ||
| 289 | #define CMD_Scan 0x03 | ||
| 290 | #define CMD_Join 0x04 | ||
| 291 | #define CMD_Start 0x05 | ||
| 292 | #define CMD_EnableRadio 0x06 | ||
| 293 | #define CMD_DisableRadio 0x07 | ||
| 294 | #define CMD_SiteSurvey 0x0B | ||
| 295 | |||
| 296 | #define CMD_STATUS_IDLE 0x00 | ||
| 297 | #define CMD_STATUS_COMPLETE 0x01 | ||
| 298 | #define CMD_STATUS_UNKNOWN 0x02 | ||
| 299 | #define CMD_STATUS_INVALID_PARAMETER 0x03 | ||
| 300 | #define CMD_STATUS_FUNCTION_NOT_SUPPORTED 0x04 | ||
| 301 | #define CMD_STATUS_TIME_OUT 0x07 | ||
| 302 | #define CMD_STATUS_IN_PROGRESS 0x08 | ||
| 303 | #define CMD_STATUS_REJECTED_RADIO_OFF 0x09 | ||
| 304 | #define CMD_STATUS_HOST_ERROR 0xFF | ||
| 305 | #define CMD_STATUS_BUSY 0xFE | ||
| 306 | |||
| 307 | #define CMD_BLOCK_COMMAND_OFFSET 0 | ||
| 308 | #define CMD_BLOCK_STATUS_OFFSET 1 | ||
| 309 | #define CMD_BLOCK_PARAMETERS_OFFSET 4 | ||
| 310 | |||
| 311 | #define SCAN_OPTIONS_SITE_SURVEY 0x80 | ||
| 312 | |||
| 313 | #define MGMT_FRAME_BODY_OFFSET 24 | ||
| 314 | #define MAX_AUTHENTICATION_RETRIES 3 | ||
| 315 | #define MAX_ASSOCIATION_RETRIES 3 | ||
| 316 | |||
| 317 | #define AUTHENTICATION_RESPONSE_TIME_OUT 1000 | ||
| 318 | |||
| 319 | #define MAX_WIRELESS_BODY 2316 /* mtu is 2312, CRC is 4 */ | ||
| 320 | #define LOOP_RETRY_LIMIT 500000 | ||
| 321 | |||
| 322 | #define ACTIVE_MODE 1 | ||
| 323 | #define PS_MODE 2 | ||
| 324 | |||
| 325 | #define MAX_ENCRYPTION_KEYS 4 | ||
| 326 | #define MAX_ENCRYPTION_KEY_SIZE 40 | ||
| 327 | |||
| 328 | /* | ||
| 329 | * 802.11 related definitions | ||
| 330 | */ | ||
| 331 | |||
| 332 | /* | ||
| 333 | * Regulatory Domains | ||
| 334 | */ | ||
| 335 | |||
| 336 | #define REG_DOMAIN_FCC 0x10 /* Channels 1-11 USA */ | ||
| 337 | #define REG_DOMAIN_DOC 0x20 /* Channel 1-11 Canada */ | ||
| 338 | #define REG_DOMAIN_ETSI 0x30 /* Channel 1-13 Europe (ex Spain/France) */ | ||
| 339 | #define REG_DOMAIN_SPAIN 0x31 /* Channel 10-11 Spain */ | ||
| 340 | #define REG_DOMAIN_FRANCE 0x32 /* Channel 10-13 France */ | ||
| 341 | #define REG_DOMAIN_MKK 0x40 /* Channel 14 Japan */ | ||
| 342 | #define REG_DOMAIN_MKK1 0x41 /* Channel 1-14 Japan(MKK1) */ | ||
| 343 | #define REG_DOMAIN_ISRAEL 0x50 /* Channel 3-9 ISRAEL */ | ||
| 344 | |||
| 345 | #define BSS_TYPE_AD_HOC 1 | ||
| 346 | #define BSS_TYPE_INFRASTRUCTURE 2 | ||
| 347 | |||
| 348 | #define SCAN_TYPE_ACTIVE 0 | ||
| 349 | #define SCAN_TYPE_PASSIVE 1 | ||
| 350 | |||
| 351 | #define LONG_PREAMBLE 0 | ||
| 352 | #define SHORT_PREAMBLE 1 | ||
| 353 | #define AUTO_PREAMBLE 2 | ||
| 354 | |||
| 355 | #define DATA_FRAME_WS_HEADER_SIZE 30 | ||
| 356 | |||
| 357 | /* promiscuous mode control */ | ||
| 358 | #define PROM_MODE_OFF 0x0 | ||
| 359 | #define PROM_MODE_UNKNOWN 0x1 | ||
| 360 | #define PROM_MODE_CRC_FAILED 0x2 | ||
| 361 | #define PROM_MODE_DUPLICATED 0x4 | ||
| 362 | #define PROM_MODE_MGMT 0x8 | ||
| 363 | #define PROM_MODE_CTRL 0x10 | ||
| 364 | #define PROM_MODE_BAD_PROTOCOL 0x20 | ||
| 365 | |||
| 366 | #define IFACE_INT_STATUS_OFFSET 0 | ||
| 367 | #define IFACE_INT_MASK_OFFSET 1 | ||
| 368 | #define IFACE_LOCKOUT_HOST_OFFSET 2 | ||
| 369 | #define IFACE_LOCKOUT_MAC_OFFSET 3 | ||
| 370 | #define IFACE_FUNC_CTRL_OFFSET 28 | ||
| 371 | #define IFACE_MAC_STAT_OFFSET 30 | ||
| 372 | #define IFACE_GENERIC_INT_TYPE_OFFSET 32 | ||
| 373 | |||
| 374 | #define CIPHER_SUITE_NONE 0 | ||
| 375 | #define CIPHER_SUITE_WEP_64 1 | ||
| 376 | #define CIPHER_SUITE_TKIP 2 | ||
| 377 | #define CIPHER_SUITE_AES 3 | ||
| 378 | #define CIPHER_SUITE_CCX 4 | ||
| 379 | #define CIPHER_SUITE_WEP_128 5 | ||
| 380 | |||
| 381 | /* | ||
| 382 | * IFACE MACROS & definitions | ||
| 383 | */ | ||
| 384 | |||
| 385 | /* | ||
| 386 | * FuncCtrl field: | ||
| 387 | */ | ||
| 388 | #define FUNC_CTRL_TxENABLE 0x10 | ||
| 389 | #define FUNC_CTRL_RxENABLE 0x20 | ||
| 390 | #define FUNC_CTRL_INIT_COMPLETE 0x01 | ||
| 391 | |||
| 392 | /* A stub firmware image which reads the MAC address from NVRAM on the card. | ||
| 393 | For copyright information and source see the end of this file. */ | ||
| 394 | static u8 mac_reader[] = { | ||
| 395 | 0x06, 0x00, 0x00, 0xea, 0x04, 0x00, 0x00, 0xea, 0x03, 0x00, 0x00, 0xea, 0x02, 0x00, 0x00, 0xea, | ||
| 396 | 0x01, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00, 0xea, 0xff, 0xff, 0xff, 0xea, 0xfe, 0xff, 0xff, 0xea, | ||
| 397 | 0xd3, 0x00, 0xa0, 0xe3, 0x00, 0xf0, 0x21, 0xe1, 0x0e, 0x04, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3, | ||
| 398 | 0x81, 0x11, 0xa0, 0xe1, 0x00, 0x10, 0x81, 0xe3, 0x00, 0x10, 0x80, 0xe5, 0x1c, 0x10, 0x90, 0xe5, | ||
| 399 | 0x10, 0x10, 0xc1, 0xe3, 0x1c, 0x10, 0x80, 0xe5, 0x01, 0x10, 0xa0, 0xe3, 0x08, 0x10, 0x80, 0xe5, | ||
| 400 | 0x02, 0x03, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3, 0xb0, 0x10, 0xc0, 0xe1, 0xb4, 0x10, 0xc0, 0xe1, | ||
| 401 | 0xb8, 0x10, 0xc0, 0xe1, 0xbc, 0x10, 0xc0, 0xe1, 0x56, 0xdc, 0xa0, 0xe3, 0x21, 0x00, 0x00, 0xeb, | ||
| 402 | 0x0a, 0x00, 0xa0, 0xe3, 0x1a, 0x00, 0x00, 0xeb, 0x10, 0x00, 0x00, 0xeb, 0x07, 0x00, 0x00, 0xeb, | ||
| 403 | 0x02, 0x03, 0xa0, 0xe3, 0x02, 0x14, 0xa0, 0xe3, 0xb4, 0x10, 0xc0, 0xe1, 0x4c, 0x10, 0x9f, 0xe5, | ||
| 404 | 0xbc, 0x10, 0xc0, 0xe1, 0x10, 0x10, 0xa0, 0xe3, 0xb8, 0x10, 0xc0, 0xe1, 0xfe, 0xff, 0xff, 0xea, | ||
| 405 | 0x00, 0x40, 0x2d, 0xe9, 0x00, 0x20, 0xa0, 0xe3, 0x02, 0x3c, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3, | ||
| 406 | 0x28, 0x00, 0x9f, 0xe5, 0x37, 0x00, 0x00, 0xeb, 0x00, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, | ||
| 407 | 0x00, 0x40, 0x2d, 0xe9, 0x12, 0x2e, 0xa0, 0xe3, 0x06, 0x30, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3, | ||
| 408 | 0x02, 0x04, 0xa0, 0xe3, 0x2f, 0x00, 0x00, 0xeb, 0x00, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, | ||
| 409 | 0x00, 0x02, 0x00, 0x02, 0x80, 0x01, 0x90, 0xe0, 0x01, 0x00, 0x00, 0x0a, 0x01, 0x00, 0x50, 0xe2, | ||
| 410 | 0xfc, 0xff, 0xff, 0xea, 0x1e, 0xff, 0x2f, 0xe1, 0x80, 0x10, 0xa0, 0xe3, 0xf3, 0x06, 0xa0, 0xe3, | ||
| 411 | 0x00, 0x10, 0x80, 0xe5, 0x00, 0x10, 0xa0, 0xe3, 0x00, 0x10, 0x80, 0xe5, 0x01, 0x10, 0xa0, 0xe3, | ||
| 412 | 0x04, 0x10, 0x80, 0xe5, 0x00, 0x10, 0x80, 0xe5, 0x0e, 0x34, 0xa0, 0xe3, 0x1c, 0x10, 0x93, 0xe5, | ||
| 413 | 0x02, 0x1a, 0x81, 0xe3, 0x1c, 0x10, 0x83, 0xe5, 0x58, 0x11, 0x9f, 0xe5, 0x30, 0x10, 0x80, 0xe5, | ||
| 414 | 0x54, 0x11, 0x9f, 0xe5, 0x34, 0x10, 0x80, 0xe5, 0x38, 0x10, 0x80, 0xe5, 0x3c, 0x10, 0x80, 0xe5, | ||
| 415 | 0x10, 0x10, 0x90, 0xe5, 0x08, 0x00, 0x90, 0xe5, 0x1e, 0xff, 0x2f, 0xe1, 0xf3, 0x16, 0xa0, 0xe3, | ||
| 416 | 0x08, 0x00, 0x91, 0xe5, 0x05, 0x00, 0xa0, 0xe3, 0x0c, 0x00, 0x81, 0xe5, 0x10, 0x00, 0x91, 0xe5, | ||
| 417 | 0x02, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0xff, 0x00, 0xa0, 0xe3, 0x0c, 0x00, 0x81, 0xe5, | ||
| 418 | 0x10, 0x00, 0x91, 0xe5, 0x02, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0x08, 0x00, 0x91, 0xe5, | ||
| 419 | 0x10, 0x00, 0x91, 0xe5, 0x01, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0x08, 0x00, 0x91, 0xe5, | ||
| 420 | 0xff, 0x00, 0x00, 0xe2, 0x1e, 0xff, 0x2f, 0xe1, 0x30, 0x40, 0x2d, 0xe9, 0x00, 0x50, 0xa0, 0xe1, | ||
| 421 | 0x03, 0x40, 0xa0, 0xe1, 0xa2, 0x02, 0xa0, 0xe1, 0x08, 0x00, 0x00, 0xe2, 0x03, 0x00, 0x80, 0xe2, | ||
| 422 | 0xd8, 0x10, 0x9f, 0xe5, 0x00, 0x00, 0xc1, 0xe5, 0x01, 0x20, 0xc1, 0xe5, 0xe2, 0xff, 0xff, 0xeb, | ||
| 423 | 0x01, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x1a, 0x14, 0x00, 0xa0, 0xe3, 0xc4, 0xff, 0xff, 0xeb, | ||
| 424 | 0x04, 0x20, 0xa0, 0xe1, 0x05, 0x10, 0xa0, 0xe1, 0x02, 0x00, 0xa0, 0xe3, 0x01, 0x00, 0x00, 0xeb, | ||
| 425 | 0x30, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 0x70, 0x40, 0x2d, 0xe9, 0xf3, 0x46, 0xa0, 0xe3, | ||
| 426 | 0x00, 0x30, 0xa0, 0xe3, 0x00, 0x00, 0x50, 0xe3, 0x08, 0x00, 0x00, 0x9a, 0x8c, 0x50, 0x9f, 0xe5, | ||
| 427 | 0x03, 0x60, 0xd5, 0xe7, 0x0c, 0x60, 0x84, 0xe5, 0x10, 0x60, 0x94, 0xe5, 0x02, 0x00, 0x16, 0xe3, | ||
| 428 | 0xfc, 0xff, 0xff, 0x0a, 0x01, 0x30, 0x83, 0xe2, 0x00, 0x00, 0x53, 0xe1, 0xf7, 0xff, 0xff, 0x3a, | ||
| 429 | 0xff, 0x30, 0xa0, 0xe3, 0x0c, 0x30, 0x84, 0xe5, 0x08, 0x00, 0x94, 0xe5, 0x10, 0x00, 0x94, 0xe5, | ||
| 430 | 0x01, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0x08, 0x00, 0x94, 0xe5, 0x00, 0x00, 0xa0, 0xe3, | ||
| 431 | 0x00, 0x00, 0x52, 0xe3, 0x0b, 0x00, 0x00, 0x9a, 0x10, 0x50, 0x94, 0xe5, 0x02, 0x00, 0x15, 0xe3, | ||
| 432 | 0xfc, 0xff, 0xff, 0x0a, 0x0c, 0x30, 0x84, 0xe5, 0x10, 0x50, 0x94, 0xe5, 0x01, 0x00, 0x15, 0xe3, | ||
| 433 | 0xfc, 0xff, 0xff, 0x0a, 0x08, 0x50, 0x94, 0xe5, 0x01, 0x50, 0xc1, 0xe4, 0x01, 0x00, 0x80, 0xe2, | ||
| 434 | 0x02, 0x00, 0x50, 0xe1, 0xf3, 0xff, 0xff, 0x3a, 0xc8, 0x00, 0xa0, 0xe3, 0x98, 0xff, 0xff, 0xeb, | ||
| 435 | 0x70, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 0x01, 0x0c, 0x00, 0x02, 0x01, 0x02, 0x00, 0x02, | ||
| 436 | 0x00, 0x01, 0x00, 0x02 | ||
| 437 | }; | ||
| 438 | |||
| 439 | struct atmel_private { | ||
| 440 | void *card; /* Bus dependent structure varies for PCcard */ | ||
| 441 | int (*present_callback)(void *); /* And callback which uses it */ | ||
| 442 | char firmware_id[32]; | ||
| 443 | AtmelFWType firmware_type; | ||
| 444 | u8 *firmware; | ||
| 445 | int firmware_length; | ||
| 446 | struct timer_list management_timer; | ||
| 447 | struct net_device *dev; | ||
| 448 | struct device *sys_dev; | ||
| 449 | struct iw_statistics wstats; | ||
| 450 | spinlock_t irqlock, timerlock; /* spinlocks */ | ||
| 451 | enum { BUS_TYPE_PCCARD, BUS_TYPE_PCI } bus_type; | ||
| 452 | enum { | ||
| 453 | CARD_TYPE_PARALLEL_FLASH, | ||
| 454 | CARD_TYPE_SPI_FLASH, | ||
| 455 | CARD_TYPE_EEPROM | ||
| 456 | } card_type; | ||
| 457 | int do_rx_crc; /* If we need to CRC incoming packets */ | ||
| 458 | int probe_crc; /* set if we don't yet know */ | ||
| 459 | int crc_ok_cnt, crc_ko_cnt; /* counters for probing */ | ||
| 460 | u16 rx_desc_head; | ||
| 461 | u16 tx_desc_free, tx_desc_head, tx_desc_tail, tx_desc_previous; | ||
| 462 | u16 tx_free_mem, tx_buff_head, tx_buff_tail; | ||
| 463 | |||
| 464 | u16 frag_seq, frag_len, frag_no; | ||
| 465 | u8 frag_source[6]; | ||
| 466 | |||
| 467 | u8 wep_is_on, default_key, exclude_unencrypted, encryption_level; | ||
| 468 | u8 group_cipher_suite, pairwise_cipher_suite; | ||
| 469 | u8 wep_keys[MAX_ENCRYPTION_KEYS][MAX_ENCRYPTION_KEY_SIZE]; | ||
| 470 | int wep_key_len[MAX_ENCRYPTION_KEYS]; | ||
| 471 | int use_wpa, radio_on_broken; /* firmware dependent stuff. */ | ||
| 472 | |||
| 473 | u16 host_info_base; | ||
| 474 | struct host_info_struct { | ||
| 475 | /* NB this is matched to the hardware, don't change. */ | ||
| 476 | u8 volatile int_status; | ||
| 477 | u8 volatile int_mask; | ||
| 478 | u8 volatile lockout_host; | ||
| 479 | u8 volatile lockout_mac; | ||
| 480 | |||
| 481 | u16 tx_buff_pos; | ||
| 482 | u16 tx_buff_size; | ||
| 483 | u16 tx_desc_pos; | ||
| 484 | u16 tx_desc_count; | ||
| 485 | |||
| 486 | u16 rx_buff_pos; | ||
| 487 | u16 rx_buff_size; | ||
| 488 | u16 rx_desc_pos; | ||
| 489 | u16 rx_desc_count; | ||
| 490 | |||
| 491 | u16 build_version; | ||
| 492 | u16 command_pos; | ||
| 493 | |||
| 494 | u16 major_version; | ||
| 495 | u16 minor_version; | ||
| 496 | |||
| 497 | u16 func_ctrl; | ||
| 498 | u16 mac_status; | ||
| 499 | u16 generic_IRQ_type; | ||
| 500 | u8 reserved[2]; | ||
| 501 | } host_info; | ||
| 502 | |||
| 503 | enum { | ||
| 504 | STATION_STATE_SCANNING, | ||
| 505 | STATION_STATE_JOINNING, | ||
| 506 | STATION_STATE_AUTHENTICATING, | ||
| 507 | STATION_STATE_ASSOCIATING, | ||
| 508 | STATION_STATE_READY, | ||
| 509 | STATION_STATE_REASSOCIATING, | ||
| 510 | STATION_STATE_DOWN, | ||
| 511 | STATION_STATE_MGMT_ERROR | ||
| 512 | } station_state; | ||
| 513 | |||
| 514 | int operating_mode, power_mode; | ||
| 515 | unsigned long last_qual; | ||
| 516 | int beacons_this_sec; | ||
| 517 | int channel; | ||
| 518 | int reg_domain, config_reg_domain; | ||
| 519 | int tx_rate; | ||
| 520 | int auto_tx_rate; | ||
| 521 | int rts_threshold; | ||
| 522 | int frag_threshold; | ||
| 523 | int long_retry, short_retry; | ||
| 524 | int preamble; | ||
| 525 | int default_beacon_period, beacon_period, listen_interval; | ||
| 526 | int CurrentAuthentTransactionSeqNum, ExpectedAuthentTransactionSeqNum; | ||
| 527 | int AuthenticationRequestRetryCnt, AssociationRequestRetryCnt, ReAssociationRequestRetryCnt; | ||
| 528 | enum { | ||
| 529 | SITE_SURVEY_IDLE, | ||
| 530 | SITE_SURVEY_IN_PROGRESS, | ||
| 531 | SITE_SURVEY_COMPLETED | ||
| 532 | } site_survey_state; | ||
| 533 | unsigned long last_survey; | ||
| 534 | |||
| 535 | int station_was_associated, station_is_associated; | ||
| 536 | int fast_scan; | ||
| 537 | |||
| 538 | struct bss_info { | ||
| 539 | int channel; | ||
| 540 | int SSIDsize; | ||
| 541 | int RSSI; | ||
| 542 | int UsingWEP; | ||
| 543 | int preamble; | ||
| 544 | int beacon_period; | ||
| 545 | int BSStype; | ||
| 546 | u8 BSSID[6]; | ||
| 547 | u8 SSID[MAX_SSID_LENGTH]; | ||
| 548 | } BSSinfo[MAX_BSS_ENTRIES]; | ||
| 549 | int BSS_list_entries, current_BSS; | ||
| 550 | int connect_to_any_BSS; | ||
| 551 | int SSID_size, new_SSID_size; | ||
| 552 | u8 CurrentBSSID[6], BSSID[6]; | ||
| 553 | u8 SSID[MAX_SSID_LENGTH], new_SSID[MAX_SSID_LENGTH]; | ||
| 554 | u64 last_beacon_timestamp; | ||
| 555 | u8 rx_buf[MAX_WIRELESS_BODY]; | ||
| 556 | }; | ||
| 557 | |||
| 558 | static u8 atmel_basic_rates[4] = {0x82, 0x84, 0x0b, 0x16}; | ||
| 559 | |||
| 560 | static const struct { | ||
| 561 | int reg_domain; | ||
| 562 | int min, max; | ||
| 563 | char *name; | ||
| 564 | } channel_table[] = { { REG_DOMAIN_FCC, 1, 11, "USA" }, | ||
| 565 | { REG_DOMAIN_DOC, 1, 11, "Canada" }, | ||
| 566 | { REG_DOMAIN_ETSI, 1, 13, "Europe" }, | ||
| 567 | { REG_DOMAIN_SPAIN, 10, 11, "Spain" }, | ||
| 568 | { REG_DOMAIN_FRANCE, 10, 13, "France" }, | ||
| 569 | { REG_DOMAIN_MKK, 14, 14, "MKK" }, | ||
| 570 | { REG_DOMAIN_MKK1, 1, 14, "MKK1" }, | ||
| 571 | { REG_DOMAIN_ISRAEL, 3, 9, "Israel"} }; | ||
| 572 | |||
| 573 | static void build_wpa_mib(struct atmel_private *priv); | ||
| 574 | static void atmel_copy_to_card(struct net_device *dev, u16 dest, | ||
| 575 | const unsigned char *src, u16 len); | ||
| 576 | static void atmel_copy_to_host(struct net_device *dev, unsigned char *dest, | ||
| 577 | u16 src, u16 len); | ||
| 578 | static void atmel_set_gcr(struct net_device *dev, u16 mask); | ||
| 579 | static void atmel_clear_gcr(struct net_device *dev, u16 mask); | ||
| 580 | static int atmel_lock_mac(struct atmel_private *priv); | ||
| 581 | static void atmel_wmem32(struct atmel_private *priv, u16 pos, u32 data); | ||
| 582 | static void atmel_command_irq(struct atmel_private *priv); | ||
| 583 | static int atmel_validate_channel(struct atmel_private *priv, int channel); | ||
| 584 | static void atmel_management_frame(struct atmel_private *priv, | ||
| 585 | struct ieee80211_hdr *header, | ||
| 586 | u16 frame_len, u8 rssi); | ||
| 587 | static void atmel_management_timer(struct timer_list *t); | ||
| 588 | static void atmel_send_command(struct atmel_private *priv, int command, | ||
| 589 | void *cmd, int cmd_size); | ||
| 590 | static int atmel_send_command_wait(struct atmel_private *priv, int command, | ||
| 591 | void *cmd, int cmd_size); | ||
| 592 | static void atmel_transmit_management_frame(struct atmel_private *priv, | ||
| 593 | struct ieee80211_hdr *header, | ||
| 594 | u8 *body, int body_len); | ||
| 595 | |||
| 596 | static u8 atmel_get_mib8(struct atmel_private *priv, u8 type, u8 index); | ||
| 597 | static void atmel_set_mib8(struct atmel_private *priv, u8 type, u8 index, | ||
| 598 | u8 data); | ||
| 599 | static void atmel_set_mib16(struct atmel_private *priv, u8 type, u8 index, | ||
| 600 | u16 data); | ||
| 601 | static void atmel_set_mib(struct atmel_private *priv, u8 type, u8 index, | ||
| 602 | const u8 *data, int data_len); | ||
| 603 | static void atmel_get_mib(struct atmel_private *priv, u8 type, u8 index, | ||
| 604 | u8 *data, int data_len); | ||
| 605 | static void atmel_scan(struct atmel_private *priv, int specific_ssid); | ||
| 606 | static void atmel_join_bss(struct atmel_private *priv, int bss_index); | ||
| 607 | static void atmel_smooth_qual(struct atmel_private *priv); | ||
| 608 | static void atmel_writeAR(struct net_device *dev, u16 data); | ||
| 609 | static int probe_atmel_card(struct net_device *dev); | ||
| 610 | static int reset_atmel_card(struct net_device *dev); | ||
| 611 | static void atmel_enter_state(struct atmel_private *priv, int new_state); | ||
| 612 | int atmel_open (struct net_device *dev); | ||
| 613 | |||
| 614 | static inline u16 atmel_hi(struct atmel_private *priv, u16 offset) | ||
| 615 | { | ||
| 616 | return priv->host_info_base + offset; | ||
| 617 | } | ||
| 618 | |||
| 619 | static inline u16 atmel_co(struct atmel_private *priv, u16 offset) | ||
| 620 | { | ||
| 621 | return priv->host_info.command_pos + offset; | ||
| 622 | } | ||
| 623 | |||
| 624 | static inline u16 atmel_rx(struct atmel_private *priv, u16 offset, u16 desc) | ||
| 625 | { | ||
| 626 | return priv->host_info.rx_desc_pos + (sizeof(struct rx_desc) * desc) + offset; | ||
| 627 | } | ||
| 628 | |||
| 629 | static inline u16 atmel_tx(struct atmel_private *priv, u16 offset, u16 desc) | ||
| 630 | { | ||
| 631 | return priv->host_info.tx_desc_pos + (sizeof(struct tx_desc) * desc) + offset; | ||
| 632 | } | ||
| 633 | |||
| 634 | static inline u8 atmel_read8(struct net_device *dev, u16 offset) | ||
| 635 | { | ||
| 636 | return inb(dev->base_addr + offset); | ||
| 637 | } | ||
| 638 | |||
| 639 | static inline void atmel_write8(struct net_device *dev, u16 offset, u8 data) | ||
| 640 | { | ||
| 641 | outb(data, dev->base_addr + offset); | ||
| 642 | } | ||
| 643 | |||
| 644 | static inline u16 atmel_read16(struct net_device *dev, u16 offset) | ||
| 645 | { | ||
| 646 | return inw(dev->base_addr + offset); | ||
| 647 | } | ||
| 648 | |||
| 649 | static inline void atmel_write16(struct net_device *dev, u16 offset, u16 data) | ||
| 650 | { | ||
| 651 | outw(data, dev->base_addr + offset); | ||
| 652 | } | ||
| 653 | |||
| 654 | static inline u8 atmel_rmem8(struct atmel_private *priv, u16 pos) | ||
| 655 | { | ||
| 656 | atmel_writeAR(priv->dev, pos); | ||
| 657 | return atmel_read8(priv->dev, DR); | ||
| 658 | } | ||
| 659 | |||
| 660 | static inline void atmel_wmem8(struct atmel_private *priv, u16 pos, u16 data) | ||
| 661 | { | ||
| 662 | atmel_writeAR(priv->dev, pos); | ||
| 663 | atmel_write8(priv->dev, DR, data); | ||
| 664 | } | ||
| 665 | |||
| 666 | static inline u16 atmel_rmem16(struct atmel_private *priv, u16 pos) | ||
| 667 | { | ||
| 668 | atmel_writeAR(priv->dev, pos); | ||
| 669 | return atmel_read16(priv->dev, DR); | ||
| 670 | } | ||
| 671 | |||
| 672 | static inline void atmel_wmem16(struct atmel_private *priv, u16 pos, u16 data) | ||
| 673 | { | ||
| 674 | atmel_writeAR(priv->dev, pos); | ||
| 675 | atmel_write16(priv->dev, DR, data); | ||
| 676 | } | ||
| 677 | |||
| 678 | static const struct iw_handler_def atmel_handler_def; | ||
| 679 | |||
| 680 | static void tx_done_irq(struct atmel_private *priv) | ||
| 681 | { | ||
| 682 | int i; | ||
| 683 | |||
| 684 | for (i = 0; | ||
| 685 | atmel_rmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, priv->tx_desc_head)) == TX_DONE && | ||
| 686 | i < priv->host_info.tx_desc_count; | ||
| 687 | i++) { | ||
| 688 | u8 status = atmel_rmem8(priv, atmel_tx(priv, TX_DESC_STATUS_OFFSET, priv->tx_desc_head)); | ||
| 689 | u16 msdu_size = atmel_rmem16(priv, atmel_tx(priv, TX_DESC_SIZE_OFFSET, priv->tx_desc_head)); | ||
| 690 | u8 type = atmel_rmem8(priv, atmel_tx(priv, TX_DESC_PACKET_TYPE_OFFSET, priv->tx_desc_head)); | ||
| 691 | |||
| 692 | atmel_wmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, priv->tx_desc_head), 0); | ||
| 693 | |||
| 694 | priv->tx_free_mem += msdu_size; | ||
| 695 | priv->tx_desc_free++; | ||
| 696 | |||
| 697 | if (priv->tx_buff_head + msdu_size > (priv->host_info.tx_buff_pos + priv->host_info.tx_buff_size)) | ||
| 698 | priv->tx_buff_head = 0; | ||
| 699 | else | ||
| 700 | priv->tx_buff_head += msdu_size; | ||
| 701 | |||
| 702 | if (priv->tx_desc_head < (priv->host_info.tx_desc_count - 1)) | ||
| 703 | priv->tx_desc_head++ ; | ||
| 704 | else | ||
| 705 | priv->tx_desc_head = 0; | ||
| 706 | |||
| 707 | if (type == TX_PACKET_TYPE_DATA) { | ||
| 708 | if (status == TX_STATUS_SUCCESS) | ||
| 709 | priv->dev->stats.tx_packets++; | ||
| 710 | else | ||
| 711 | priv->dev->stats.tx_errors++; | ||
| 712 | netif_wake_queue(priv->dev); | ||
| 713 | } | ||
| 714 | } | ||
| 715 | } | ||
| 716 | |||
| 717 | static u16 find_tx_buff(struct atmel_private *priv, u16 len) | ||
| 718 | { | ||
| 719 | u16 bottom_free = priv->host_info.tx_buff_size - priv->tx_buff_tail; | ||
| 720 | |||
| 721 | if (priv->tx_desc_free == 3 || priv->tx_free_mem < len) | ||
| 722 | return 0; | ||
| 723 | |||
| 724 | if (bottom_free >= len) | ||
| 725 | return priv->host_info.tx_buff_pos + priv->tx_buff_tail; | ||
| 726 | |||
| 727 | if (priv->tx_free_mem - bottom_free >= len) { | ||
| 728 | priv->tx_buff_tail = 0; | ||
| 729 | return priv->host_info.tx_buff_pos; | ||
| 730 | } | ||
| 731 | |||
| 732 | return 0; | ||
| 733 | } | ||
| 734 | |||
| 735 | static void tx_update_descriptor(struct atmel_private *priv, int is_bcast, | ||
| 736 | u16 len, u16 buff, u8 type) | ||
| 737 | { | ||
| 738 | atmel_wmem16(priv, atmel_tx(priv, TX_DESC_POS_OFFSET, priv->tx_desc_tail), buff); | ||
| 739 | atmel_wmem16(priv, atmel_tx(priv, TX_DESC_SIZE_OFFSET, priv->tx_desc_tail), len); | ||
| 740 | if (!priv->use_wpa) | ||
| 741 | atmel_wmem16(priv, atmel_tx(priv, TX_DESC_HOST_LENGTH_OFFSET, priv->tx_desc_tail), len); | ||
| 742 | atmel_wmem8(priv, atmel_tx(priv, TX_DESC_PACKET_TYPE_OFFSET, priv->tx_desc_tail), type); | ||
| 743 | atmel_wmem8(priv, atmel_tx(priv, TX_DESC_RATE_OFFSET, priv->tx_desc_tail), priv->tx_rate); | ||
| 744 | atmel_wmem8(priv, atmel_tx(priv, TX_DESC_RETRY_OFFSET, priv->tx_desc_tail), 0); | ||
| 745 | if (priv->use_wpa) { | ||
| 746 | int cipher_type, cipher_length; | ||
| 747 | if (is_bcast) { | ||
| 748 | cipher_type = priv->group_cipher_suite; | ||
| 749 | if (cipher_type == CIPHER_SUITE_WEP_64 || | ||
| 750 | cipher_type == CIPHER_SUITE_WEP_128) | ||
| 751 | cipher_length = 8; | ||
| 752 | else if (cipher_type == CIPHER_SUITE_TKIP) | ||
| 753 | cipher_length = 12; | ||
| 754 | else if (priv->pairwise_cipher_suite == CIPHER_SUITE_WEP_64 || | ||
| 755 | priv->pairwise_cipher_suite == CIPHER_SUITE_WEP_128) { | ||
| 756 | cipher_type = priv->pairwise_cipher_suite; | ||
| 757 | cipher_length = 8; | ||
| 758 | } else { | ||
| 759 | cipher_type = CIPHER_SUITE_NONE; | ||
| 760 | cipher_length = 0; | ||
| 761 | } | ||
| 762 | } else { | ||
| 763 | cipher_type = priv->pairwise_cipher_suite; | ||
| 764 | if (cipher_type == CIPHER_SUITE_WEP_64 || | ||
| 765 | cipher_type == CIPHER_SUITE_WEP_128) | ||
| 766 | cipher_length = 8; | ||
| 767 | else if (cipher_type == CIPHER_SUITE_TKIP) | ||
| 768 | cipher_length = 12; | ||
| 769 | else if (priv->group_cipher_suite == CIPHER_SUITE_WEP_64 || | ||
| 770 | priv->group_cipher_suite == CIPHER_SUITE_WEP_128) { | ||
| 771 | cipher_type = priv->group_cipher_suite; | ||
| 772 | cipher_length = 8; | ||
| 773 | } else { | ||
| 774 | cipher_type = CIPHER_SUITE_NONE; | ||
| 775 | cipher_length = 0; | ||
| 776 | } | ||
| 777 | } | ||
| 778 | |||
| 779 | atmel_wmem8(priv, atmel_tx(priv, TX_DESC_CIPHER_TYPE_OFFSET, priv->tx_desc_tail), | ||
| 780 | cipher_type); | ||
| 781 | atmel_wmem8(priv, atmel_tx(priv, TX_DESC_CIPHER_LENGTH_OFFSET, priv->tx_desc_tail), | ||
| 782 | cipher_length); | ||
| 783 | } | ||
| 784 | atmel_wmem32(priv, atmel_tx(priv, TX_DESC_NEXT_OFFSET, priv->tx_desc_tail), 0x80000000L); | ||
| 785 | atmel_wmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, priv->tx_desc_tail), TX_FIRM_OWN); | ||
| 786 | if (priv->tx_desc_previous != priv->tx_desc_tail) | ||
| 787 | atmel_wmem32(priv, atmel_tx(priv, TX_DESC_NEXT_OFFSET, priv->tx_desc_previous), 0); | ||
| 788 | priv->tx_desc_previous = priv->tx_desc_tail; | ||
| 789 | if (priv->tx_desc_tail < (priv->host_info.tx_desc_count - 1)) | ||
| 790 | priv->tx_desc_tail++; | ||
| 791 | else | ||
| 792 | priv->tx_desc_tail = 0; | ||
| 793 | priv->tx_desc_free--; | ||
| 794 | priv->tx_free_mem -= len; | ||
| 795 | } | ||
| 796 | |||
| 797 | static netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev) | ||
| 798 | { | ||
| 799 | struct atmel_private *priv = netdev_priv(dev); | ||
| 800 | struct ieee80211_hdr header; | ||
| 801 | unsigned long flags; | ||
| 802 | u16 buff, frame_ctl, len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN; | ||
| 803 | |||
| 804 | if (priv->card && priv->present_callback && | ||
| 805 | !(*priv->present_callback)(priv->card)) { | ||
| 806 | dev->stats.tx_errors++; | ||
| 807 | dev_kfree_skb(skb); | ||
| 808 | return NETDEV_TX_OK; | ||
| 809 | } | ||
| 810 | |||
| 811 | if (priv->station_state != STATION_STATE_READY) { | ||
| 812 | dev->stats.tx_errors++; | ||
| 813 | dev_kfree_skb(skb); | ||
| 814 | return NETDEV_TX_OK; | ||
| 815 | } | ||
| 816 | |||
| 817 | /* first ensure the timer func cannot run */ | ||
| 818 | spin_lock_bh(&priv->timerlock); | ||
| 819 | /* then stop the hardware ISR */ | ||
| 820 | spin_lock_irqsave(&priv->irqlock, flags); | ||
| 821 | /* nb doing the above in the opposite order will deadlock */ | ||
| 822 | |||
| 823 | /* The Wireless Header is 30 bytes. In the Ethernet packet we "cut" the | ||
| 824 | 12 first bytes (containing DA/SA) and put them in the appropriate | ||
| 825 | fields of the Wireless Header. Thus the packet length is then the | ||
| 826 | initial + 18 (+30-12) */ | ||
| 827 | |||
| 828 | if (!(buff = find_tx_buff(priv, len + 18))) { | ||
| 829 | dev->stats.tx_dropped++; | ||
| 830 | spin_unlock_irqrestore(&priv->irqlock, flags); | ||
| 831 | spin_unlock_bh(&priv->timerlock); | ||
| 832 | netif_stop_queue(dev); | ||
| 833 | return NETDEV_TX_BUSY; | ||
| 834 | } | ||
| 835 | |||
| 836 | frame_ctl = IEEE80211_FTYPE_DATA; | ||
| 837 | header.duration_id = 0; | ||
| 838 | header.seq_ctrl = 0; | ||
| 839 | if (priv->wep_is_on) | ||
| 840 | frame_ctl |= IEEE80211_FCTL_PROTECTED; | ||
| 841 | if (priv->operating_mode == IW_MODE_ADHOC) { | ||
| 842 | skb_copy_from_linear_data(skb, &header.addr1, ETH_ALEN); | ||
| 843 | memcpy(&header.addr2, dev->dev_addr, ETH_ALEN); | ||
| 844 | memcpy(&header.addr3, priv->BSSID, ETH_ALEN); | ||
| 845 | } else { | ||
| 846 | frame_ctl |= IEEE80211_FCTL_TODS; | ||
| 847 | memcpy(&header.addr1, priv->CurrentBSSID, ETH_ALEN); | ||
| 848 | memcpy(&header.addr2, dev->dev_addr, ETH_ALEN); | ||
| 849 | skb_copy_from_linear_data(skb, &header.addr3, ETH_ALEN); | ||
| 850 | } | ||
| 851 | |||
| 852 | if (priv->use_wpa) | ||
| 853 | memcpy(&header.addr4, rfc1042_header, ETH_ALEN); | ||
| 854 | |||
| 855 | header.frame_control = cpu_to_le16(frame_ctl); | ||
| 856 | /* Copy the wireless header into the card */ | ||
| 857 | atmel_copy_to_card(dev, buff, (unsigned char *)&header, DATA_FRAME_WS_HEADER_SIZE); | ||
| 858 | /* Copy the packet sans its 802.3 header addresses which have been replaced */ | ||
| 859 | atmel_copy_to_card(dev, buff + DATA_FRAME_WS_HEADER_SIZE, skb->data + 12, len - 12); | ||
| 860 | priv->tx_buff_tail += len - 12 + DATA_FRAME_WS_HEADER_SIZE; | ||
| 861 | |||
| 862 | /* low bit of first byte of destination tells us if broadcast */ | ||
| 863 | tx_update_descriptor(priv, *(skb->data) & 0x01, len + 18, buff, TX_PACKET_TYPE_DATA); | ||
| 864 | dev->stats.tx_bytes += len; | ||
| 865 | |||
| 866 | spin_unlock_irqrestore(&priv->irqlock, flags); | ||
| 867 | spin_unlock_bh(&priv->timerlock); | ||
| 868 | dev_kfree_skb(skb); | ||
| 869 | |||
| 870 | return NETDEV_TX_OK; | ||
| 871 | } | ||
| 872 | |||
| 873 | static void atmel_transmit_management_frame(struct atmel_private *priv, | ||
| 874 | struct ieee80211_hdr *header, | ||
| 875 | u8 *body, int body_len) | ||
| 876 | { | ||
| 877 | u16 buff; | ||
| 878 | int len = MGMT_FRAME_BODY_OFFSET + body_len; | ||
| 879 | |||
| 880 | if (!(buff = find_tx_buff(priv, len))) | ||
| 881 | return; | ||
| 882 | |||
| 883 | atmel_copy_to_card(priv->dev, buff, (u8 *)header, MGMT_FRAME_BODY_OFFSET); | ||
| 884 | atmel_copy_to_card(priv->dev, buff + MGMT_FRAME_BODY_OFFSET, body, body_len); | ||
| 885 | priv->tx_buff_tail += len; | ||
| 886 | tx_update_descriptor(priv, header->addr1[0] & 0x01, len, buff, TX_PACKET_TYPE_MGMT); | ||
| 887 | } | ||
| 888 | |||
| 889 | static void fast_rx_path(struct atmel_private *priv, | ||
| 890 | struct ieee80211_hdr *header, | ||
| 891 | u16 msdu_size, u16 rx_packet_loc, u32 crc) | ||
| 892 | { | ||
| 893 | /* fast path: unfragmented packet copy directly into skbuf */ | ||
| 894 | u8 mac4[6]; | ||
| 895 | struct sk_buff *skb; | ||
| 896 | unsigned char *skbp; | ||
| 897 | |||
| 898 | /* get the final, mac 4 header field, this tells us encapsulation */ | ||
| 899 | atmel_copy_to_host(priv->dev, mac4, rx_packet_loc + 24, 6); | ||
| 900 | msdu_size -= 6; | ||
| 901 | |||
| 902 | if (priv->do_rx_crc) { | ||
| 903 | crc = crc32_le(crc, mac4, 6); | ||
| 904 | msdu_size -= 4; | ||
| 905 | } | ||
| 906 | |||
| 907 | if (!(skb = dev_alloc_skb(msdu_size + 14))) { | ||
| 908 | priv->dev->stats.rx_dropped++; | ||
| 909 | return; | ||
| 910 | } | ||
| 911 | |||
| 912 | skb_reserve(skb, 2); | ||
| 913 | skbp = skb_put(skb, msdu_size + 12); | ||
| 914 | atmel_copy_to_host(priv->dev, skbp + 12, rx_packet_loc + 30, msdu_size); | ||
| 915 | |||
| 916 | if (priv->do_rx_crc) { | ||
| 917 | u32 netcrc; | ||
| 918 | crc = crc32_le(crc, skbp + 12, msdu_size); | ||
| 919 | atmel_copy_to_host(priv->dev, (void *)&netcrc, rx_packet_loc + 30 + msdu_size, 4); | ||
| 920 | if ((crc ^ 0xffffffff) != netcrc) { | ||
| 921 | priv->dev->stats.rx_crc_errors++; | ||
| 922 | dev_kfree_skb(skb); | ||
| 923 | return; | ||
| 924 | } | ||
| 925 | } | ||
| 926 | |||
| 927 | memcpy(skbp, header->addr1, ETH_ALEN); /* destination address */ | ||
| 928 | if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS) | ||
| 929 | memcpy(&skbp[ETH_ALEN], header->addr3, ETH_ALEN); | ||
| 930 | else | ||
| 931 | memcpy(&skbp[ETH_ALEN], header->addr2, ETH_ALEN); /* source address */ | ||
| 932 | |||
| 933 | skb->protocol = eth_type_trans(skb, priv->dev); | ||
| 934 | skb->ip_summed = CHECKSUM_NONE; | ||
| 935 | netif_rx(skb); | ||
| 936 | priv->dev->stats.rx_bytes += 12 + msdu_size; | ||
| 937 | priv->dev->stats.rx_packets++; | ||
| 938 | } | ||
| 939 | |||
| 940 | /* Test to see if the packet in card memory at packet_loc has a valid CRC | ||
| 941 | It doesn't matter that this is slow: it is only used to proble the first few | ||
| 942 | packets. */ | ||
| 943 | static int probe_crc(struct atmel_private *priv, u16 packet_loc, u16 msdu_size) | ||
| 944 | { | ||
| 945 | int i = msdu_size - 4; | ||
| 946 | u32 netcrc, crc = 0xffffffff; | ||
| 947 | |||
| 948 | if (msdu_size < 4) | ||
| 949 | return 0; | ||
| 950 | |||
| 951 | atmel_copy_to_host(priv->dev, (void *)&netcrc, packet_loc + i, 4); | ||
| 952 | |||
| 953 | atmel_writeAR(priv->dev, packet_loc); | ||
| 954 | while (i--) { | ||
| 955 | u8 octet = atmel_read8(priv->dev, DR); | ||
| 956 | crc = crc32_le(crc, &octet, 1); | ||
| 957 | } | ||
| 958 | |||
| 959 | return (crc ^ 0xffffffff) == netcrc; | ||
| 960 | } | ||
| 961 | |||
| 962 | static void frag_rx_path(struct atmel_private *priv, | ||
| 963 | struct ieee80211_hdr *header, | ||
| 964 | u16 msdu_size, u16 rx_packet_loc, u32 crc, u16 seq_no, | ||
| 965 | u8 frag_no, int more_frags) | ||
| 966 | { | ||
| 967 | u8 mac4[ETH_ALEN]; | ||
| 968 | u8 source[ETH_ALEN]; | ||
| 969 | struct sk_buff *skb; | ||
| 970 | |||
| 971 | if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS) | ||
| 972 | memcpy(source, header->addr3, ETH_ALEN); | ||
| 973 | else | ||
| 974 | memcpy(source, header->addr2, ETH_ALEN); | ||
| 975 | |||
| 976 | rx_packet_loc += 24; /* skip header */ | ||
| 977 | |||
| 978 | if (priv->do_rx_crc) | ||
| 979 | msdu_size -= 4; | ||
| 980 | |||
| 981 | if (frag_no == 0) { /* first fragment */ | ||
| 982 | atmel_copy_to_host(priv->dev, mac4, rx_packet_loc, ETH_ALEN); | ||
| 983 | msdu_size -= ETH_ALEN; | ||
| 984 | rx_packet_loc += ETH_ALEN; | ||
| 985 | |||
| 986 | if (priv->do_rx_crc) | ||
| 987 | crc = crc32_le(crc, mac4, 6); | ||
| 988 | |||
| 989 | priv->frag_seq = seq_no; | ||
| 990 | priv->frag_no = 1; | ||
| 991 | priv->frag_len = msdu_size; | ||
| 992 | memcpy(priv->frag_source, source, ETH_ALEN); | ||
| 993 | memcpy(&priv->rx_buf[ETH_ALEN], source, ETH_ALEN); | ||
| 994 | memcpy(priv->rx_buf, header->addr1, ETH_ALEN); | ||
| 995 | |||
| 996 | atmel_copy_to_host(priv->dev, &priv->rx_buf[12], rx_packet_loc, msdu_size); | ||
| 997 | |||
| 998 | if (priv->do_rx_crc) { | ||
| 999 | u32 netcrc; | ||
| 1000 | crc = crc32_le(crc, &priv->rx_buf[12], msdu_size); | ||
| 1001 | atmel_copy_to_host(priv->dev, (void *)&netcrc, rx_packet_loc + msdu_size, 4); | ||
| 1002 | if ((crc ^ 0xffffffff) != netcrc) { | ||
| 1003 | priv->dev->stats.rx_crc_errors++; | ||
| 1004 | eth_broadcast_addr(priv->frag_source); | ||
| 1005 | } | ||
| 1006 | } | ||
| 1007 | |||
| 1008 | } else if (priv->frag_no == frag_no && | ||
| 1009 | priv->frag_seq == seq_no && | ||
| 1010 | memcmp(priv->frag_source, source, ETH_ALEN) == 0) { | ||
| 1011 | |||
| 1012 | atmel_copy_to_host(priv->dev, &priv->rx_buf[12 + priv->frag_len], | ||
| 1013 | rx_packet_loc, msdu_size); | ||
| 1014 | if (priv->do_rx_crc) { | ||
| 1015 | u32 netcrc; | ||
| 1016 | crc = crc32_le(crc, | ||
| 1017 | &priv->rx_buf[12 + priv->frag_len], | ||
| 1018 | msdu_size); | ||
| 1019 | atmel_copy_to_host(priv->dev, (void *)&netcrc, rx_packet_loc + msdu_size, 4); | ||
| 1020 | if ((crc ^ 0xffffffff) != netcrc) { | ||
| 1021 | priv->dev->stats.rx_crc_errors++; | ||
| 1022 | eth_broadcast_addr(priv->frag_source); | ||
| 1023 | more_frags = 1; /* don't send broken assembly */ | ||
| 1024 | } | ||
| 1025 | } | ||
| 1026 | |||
| 1027 | priv->frag_len += msdu_size; | ||
| 1028 | priv->frag_no++; | ||
| 1029 | |||
| 1030 | if (!more_frags) { /* last one */ | ||
| 1031 | eth_broadcast_addr(priv->frag_source); | ||
| 1032 | if (!(skb = dev_alloc_skb(priv->frag_len + 14))) { | ||
| 1033 | priv->dev->stats.rx_dropped++; | ||
| 1034 | } else { | ||
| 1035 | skb_reserve(skb, 2); | ||
| 1036 | skb_put_data(skb, priv->rx_buf, | ||
| 1037 | priv->frag_len + 12); | ||
| 1038 | skb->protocol = eth_type_trans(skb, priv->dev); | ||
| 1039 | skb->ip_summed = CHECKSUM_NONE; | ||
| 1040 | netif_rx(skb); | ||
| 1041 | priv->dev->stats.rx_bytes += priv->frag_len + 12; | ||
| 1042 | priv->dev->stats.rx_packets++; | ||
| 1043 | } | ||
| 1044 | } | ||
| 1045 | } else | ||
| 1046 | priv->wstats.discard.fragment++; | ||
| 1047 | } | ||
| 1048 | |||
| 1049 | static void rx_done_irq(struct atmel_private *priv) | ||
| 1050 | { | ||
| 1051 | int i; | ||
| 1052 | struct ieee80211_hdr header; | ||
| 1053 | |||
| 1054 | for (i = 0; | ||
| 1055 | atmel_rmem8(priv, atmel_rx(priv, RX_DESC_FLAGS_OFFSET, priv->rx_desc_head)) == RX_DESC_FLAG_VALID && | ||
| 1056 | i < priv->host_info.rx_desc_count; | ||
| 1057 | i++) { | ||
| 1058 | |||
| 1059 | u16 msdu_size, rx_packet_loc, frame_ctl, seq_control; | ||
| 1060 | u8 status = atmel_rmem8(priv, atmel_rx(priv, RX_DESC_STATUS_OFFSET, priv->rx_desc_head)); | ||
| 1061 | u32 crc = 0xffffffff; | ||
| 1062 | |||
| 1063 | if (status != RX_STATUS_SUCCESS) { | ||
| 1064 | if (status == 0xc1) /* determined by experiment */ | ||
| 1065 | priv->wstats.discard.nwid++; | ||
| 1066 | else | ||
| 1067 | priv->dev->stats.rx_errors++; | ||
| 1068 | goto next; | ||
| 1069 | } | ||
| 1070 | |||
| 1071 | msdu_size = atmel_rmem16(priv, atmel_rx(priv, RX_DESC_MSDU_SIZE_OFFSET, priv->rx_desc_head)); | ||
| 1072 | rx_packet_loc = atmel_rmem16(priv, atmel_rx(priv, RX_DESC_MSDU_POS_OFFSET, priv->rx_desc_head)); | ||
| 1073 | |||
| 1074 | if (msdu_size < 30) { | ||
| 1075 | priv->dev->stats.rx_errors++; | ||
| 1076 | goto next; | ||
| 1077 | } | ||
| 1078 | |||
| 1079 | /* Get header as far as end of seq_ctrl */ | ||
| 1080 | atmel_copy_to_host(priv->dev, (char *)&header, rx_packet_loc, 24); | ||
| 1081 | frame_ctl = le16_to_cpu(header.frame_control); | ||
| 1082 | seq_control = le16_to_cpu(header.seq_ctrl); | ||
| 1083 | |||
| 1084 | /* probe for CRC use here if needed once five packets have | ||
| 1085 | arrived with the same crc status, we assume we know what's | ||
| 1086 | happening and stop probing */ | ||
| 1087 | if (priv->probe_crc) { | ||
| 1088 | if (!priv->wep_is_on || !(frame_ctl & IEEE80211_FCTL_PROTECTED)) { | ||
| 1089 | priv->do_rx_crc = probe_crc(priv, rx_packet_loc, msdu_size); | ||
| 1090 | } else { | ||
| 1091 | priv->do_rx_crc = probe_crc(priv, rx_packet_loc + 24, msdu_size - 24); | ||
| 1092 | } | ||
| 1093 | if (priv->do_rx_crc) { | ||
| 1094 | if (priv->crc_ok_cnt++ > 5) | ||
| 1095 | priv->probe_crc = 0; | ||
| 1096 | } else { | ||
| 1097 | if (priv->crc_ko_cnt++ > 5) | ||
| 1098 | priv->probe_crc = 0; | ||
| 1099 | } | ||
| 1100 | } | ||
| 1101 | |||
| 1102 | /* don't CRC header when WEP in use */ | ||
| 1103 | if (priv->do_rx_crc && (!priv->wep_is_on || !(frame_ctl & IEEE80211_FCTL_PROTECTED))) { | ||
| 1104 | crc = crc32_le(0xffffffff, (unsigned char *)&header, 24); | ||
| 1105 | } | ||
| 1106 | msdu_size -= 24; /* header */ | ||
| 1107 | |||
| 1108 | if ((frame_ctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) { | ||
| 1109 | int more_fragments = frame_ctl & IEEE80211_FCTL_MOREFRAGS; | ||
| 1110 | u8 packet_fragment_no = seq_control & IEEE80211_SCTL_FRAG; | ||
| 1111 | u16 packet_sequence_no = (seq_control & IEEE80211_SCTL_SEQ) >> 4; | ||
| 1112 | |||
| 1113 | if (!more_fragments && packet_fragment_no == 0) { | ||
| 1114 | fast_rx_path(priv, &header, msdu_size, rx_packet_loc, crc); | ||
| 1115 | } else { | ||
| 1116 | frag_rx_path(priv, &header, msdu_size, rx_packet_loc, crc, | ||
| 1117 | packet_sequence_no, packet_fragment_no, more_fragments); | ||
| 1118 | } | ||
| 1119 | } | ||
| 1120 | |||
| 1121 | if ((frame_ctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) { | ||
| 1122 | /* copy rest of packet into buffer */ | ||
| 1123 | atmel_copy_to_host(priv->dev, (unsigned char *)&priv->rx_buf, rx_packet_loc + 24, msdu_size); | ||
| 1124 | |||
| 1125 | /* we use the same buffer for frag reassembly and control packets */ | ||
| 1126 | eth_broadcast_addr(priv->frag_source); | ||
| 1127 | |||
| 1128 | if (priv->do_rx_crc) { | ||
| 1129 | /* last 4 octets is crc */ | ||
| 1130 | msdu_size -= 4; | ||
| 1131 | crc = crc32_le(crc, (unsigned char *)&priv->rx_buf, msdu_size); | ||
| 1132 | if ((crc ^ 0xffffffff) != (*((u32 *)&priv->rx_buf[msdu_size]))) { | ||
| 1133 | priv->dev->stats.rx_crc_errors++; | ||
| 1134 | goto next; | ||
| 1135 | } | ||
| 1136 | } | ||
| 1137 | |||
| 1138 | atmel_management_frame(priv, &header, msdu_size, | ||
| 1139 | atmel_rmem8(priv, atmel_rx(priv, RX_DESC_RSSI_OFFSET, priv->rx_desc_head))); | ||
| 1140 | } | ||
| 1141 | |||
| 1142 | next: | ||
| 1143 | /* release descriptor */ | ||
| 1144 | atmel_wmem8(priv, atmel_rx(priv, RX_DESC_FLAGS_OFFSET, priv->rx_desc_head), RX_DESC_FLAG_CONSUMED); | ||
| 1145 | |||
| 1146 | if (priv->rx_desc_head < (priv->host_info.rx_desc_count - 1)) | ||
| 1147 | priv->rx_desc_head++; | ||
| 1148 | else | ||
| 1149 | priv->rx_desc_head = 0; | ||
| 1150 | } | ||
| 1151 | } | ||
| 1152 | |||
| 1153 | static irqreturn_t service_interrupt(int irq, void *dev_id) | ||
| 1154 | { | ||
| 1155 | struct net_device *dev = (struct net_device *) dev_id; | ||
| 1156 | struct atmel_private *priv = netdev_priv(dev); | ||
| 1157 | u8 isr; | ||
| 1158 | int i = -1; | ||
| 1159 | static const u8 irq_order[] = { | ||
| 1160 | ISR_OUT_OF_RANGE, | ||
| 1161 | ISR_RxCOMPLETE, | ||
| 1162 | ISR_TxCOMPLETE, | ||
| 1163 | ISR_RxFRAMELOST, | ||
| 1164 | ISR_FATAL_ERROR, | ||
| 1165 | ISR_COMMAND_COMPLETE, | ||
| 1166 | ISR_IBSS_MERGE, | ||
| 1167 | ISR_GENERIC_IRQ | ||
| 1168 | }; | ||
| 1169 | |||
| 1170 | if (priv->card && priv->present_callback && | ||
| 1171 | !(*priv->present_callback)(priv->card)) | ||
| 1172 | return IRQ_HANDLED; | ||
| 1173 | |||
| 1174 | /* In this state upper-level code assumes it can mess with | ||
| 1175 | the card unhampered by interrupts which may change register state. | ||
| 1176 | Note that even though the card shouldn't generate interrupts | ||
| 1177 | the inturrupt line may be shared. This allows card setup | ||
| 1178 | to go on without disabling interrupts for a long time. */ | ||
| 1179 | if (priv->station_state == STATION_STATE_DOWN) | ||
| 1180 | return IRQ_NONE; | ||
| 1181 | |||
| 1182 | atmel_clear_gcr(dev, GCR_ENINT); /* disable interrupts */ | ||
| 1183 | |||
| 1184 | while (1) { | ||
| 1185 | if (!atmel_lock_mac(priv)) { | ||
| 1186 | /* failed to contact card */ | ||
| 1187 | printk(KERN_ALERT "%s: failed to contact MAC.\n", dev->name); | ||
| 1188 | return IRQ_HANDLED; | ||
| 1189 | } | ||
| 1190 | |||
| 1191 | isr = atmel_rmem8(priv, atmel_hi(priv, IFACE_INT_STATUS_OFFSET)); | ||
| 1192 | atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 0); | ||
| 1193 | |||
| 1194 | if (!isr) { | ||
| 1195 | atmel_set_gcr(dev, GCR_ENINT); /* enable interrupts */ | ||
| 1196 | return i == -1 ? IRQ_NONE : IRQ_HANDLED; | ||
| 1197 | } | ||
| 1198 | |||
| 1199 | atmel_set_gcr(dev, GCR_ACKINT); /* acknowledge interrupt */ | ||
| 1200 | |||
| 1201 | for (i = 0; i < ARRAY_SIZE(irq_order); i++) | ||
| 1202 | if (isr & irq_order[i]) | ||
| 1203 | break; | ||
| 1204 | |||
| 1205 | if (!atmel_lock_mac(priv)) { | ||
| 1206 | /* failed to contact card */ | ||
| 1207 | printk(KERN_ALERT "%s: failed to contact MAC.\n", dev->name); | ||
| 1208 | return IRQ_HANDLED; | ||
| 1209 | } | ||
| 1210 | |||
| 1211 | isr = atmel_rmem8(priv, atmel_hi(priv, IFACE_INT_STATUS_OFFSET)); | ||
| 1212 | isr ^= irq_order[i]; | ||
| 1213 | atmel_wmem8(priv, atmel_hi(priv, IFACE_INT_STATUS_OFFSET), isr); | ||
| 1214 | atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 0); | ||
| 1215 | |||
| 1216 | switch (irq_order[i]) { | ||
| 1217 | |||
| 1218 | case ISR_OUT_OF_RANGE: | ||
| 1219 | if (priv->operating_mode == IW_MODE_INFRA && | ||
| 1220 | priv->station_state == STATION_STATE_READY) { | ||
| 1221 | priv->station_is_associated = 0; | ||
| 1222 | atmel_scan(priv, 1); | ||
| 1223 | } | ||
| 1224 | break; | ||
| 1225 | |||
| 1226 | case ISR_RxFRAMELOST: | ||
| 1227 | priv->wstats.discard.misc++; | ||
| 1228 | fallthrough; | ||
| 1229 | case ISR_RxCOMPLETE: | ||
| 1230 | rx_done_irq(priv); | ||
| 1231 | break; | ||
| 1232 | |||
| 1233 | case ISR_TxCOMPLETE: | ||
| 1234 | tx_done_irq(priv); | ||
| 1235 | break; | ||
| 1236 | |||
| 1237 | case ISR_FATAL_ERROR: | ||
| 1238 | printk(KERN_ALERT "%s: *** FATAL error interrupt ***\n", dev->name); | ||
| 1239 | atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); | ||
| 1240 | break; | ||
| 1241 | |||
| 1242 | case ISR_COMMAND_COMPLETE: | ||
| 1243 | atmel_command_irq(priv); | ||
| 1244 | break; | ||
| 1245 | |||
| 1246 | case ISR_IBSS_MERGE: | ||
| 1247 | atmel_get_mib(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_CUR_BSSID_POS, | ||
| 1248 | priv->CurrentBSSID, 6); | ||
| 1249 | /* The WPA stuff cares about the current AP address */ | ||
| 1250 | if (priv->use_wpa) | ||
| 1251 | build_wpa_mib(priv); | ||
| 1252 | break; | ||
| 1253 | case ISR_GENERIC_IRQ: | ||
| 1254 | printk(KERN_INFO "%s: Generic_irq received.\n", dev->name); | ||
| 1255 | break; | ||
| 1256 | } | ||
| 1257 | } | ||
| 1258 | } | ||
| 1259 | |||
| 1260 | static struct iw_statistics *atmel_get_wireless_stats(struct net_device *dev) | ||
| 1261 | { | ||
| 1262 | struct atmel_private *priv = netdev_priv(dev); | ||
| 1263 | |||
| 1264 | /* update the link quality here in case we are seeing no beacons | ||
| 1265 | at all to drive the process */ | ||
| 1266 | atmel_smooth_qual(priv); | ||
| 1267 | |||
| 1268 | priv->wstats.status = priv->station_state; | ||
| 1269 | |||
| 1270 | if (priv->operating_mode == IW_MODE_INFRA) { | ||
| 1271 | if (priv->station_state != STATION_STATE_READY) { | ||
| 1272 | priv->wstats.qual.qual = 0; | ||
| 1273 | priv->wstats.qual.level = 0; | ||
| 1274 | priv->wstats.qual.updated = (IW_QUAL_QUAL_INVALID | ||
| 1275 | | IW_QUAL_LEVEL_INVALID); | ||
| 1276 | } | ||
| 1277 | priv->wstats.qual.noise = 0; | ||
| 1278 | priv->wstats.qual.updated |= IW_QUAL_NOISE_INVALID; | ||
| 1279 | } else { | ||
| 1280 | /* Quality levels cannot be determined in ad-hoc mode, | ||
| 1281 | because we can 'hear' more that one remote station. */ | ||
| 1282 | priv->wstats.qual.qual = 0; | ||
| 1283 | priv->wstats.qual.level = 0; | ||
| 1284 | priv->wstats.qual.noise = 0; | ||
| 1285 | priv->wstats.qual.updated = IW_QUAL_QUAL_INVALID | ||
| 1286 | | IW_QUAL_LEVEL_INVALID | ||
| 1287 | | IW_QUAL_NOISE_INVALID; | ||
| 1288 | priv->wstats.miss.beacon = 0; | ||
| 1289 | } | ||
| 1290 | |||
| 1291 | return &priv->wstats; | ||
| 1292 | } | ||
| 1293 | |||
| 1294 | static int atmel_set_mac_address(struct net_device *dev, void *p) | ||
| 1295 | { | ||
| 1296 | struct sockaddr *addr = p; | ||
| 1297 | |||
| 1298 | eth_hw_addr_set(dev, addr->sa_data); | ||
| 1299 | return atmel_open(dev); | ||
| 1300 | } | ||
| 1301 | |||
| 1302 | EXPORT_SYMBOL(atmel_open); | ||
| 1303 | |||
| 1304 | int atmel_open(struct net_device *dev) | ||
| 1305 | { | ||
| 1306 | struct atmel_private *priv = netdev_priv(dev); | ||
| 1307 | int i, channel, err; | ||
| 1308 | |||
| 1309 | /* any scheduled timer is no longer needed and might screw things up.. */ | ||
| 1310 | del_timer_sync(&priv->management_timer); | ||
| 1311 | |||
| 1312 | /* Interrupts will not touch the card once in this state... */ | ||
| 1313 | priv->station_state = STATION_STATE_DOWN; | ||
| 1314 | |||
| 1315 | if (priv->new_SSID_size) { | ||
| 1316 | memcpy(priv->SSID, priv->new_SSID, priv->new_SSID_size); | ||
| 1317 | priv->SSID_size = priv->new_SSID_size; | ||
| 1318 | priv->new_SSID_size = 0; | ||
| 1319 | } | ||
| 1320 | priv->BSS_list_entries = 0; | ||
| 1321 | |||
| 1322 | priv->AuthenticationRequestRetryCnt = 0; | ||
| 1323 | priv->AssociationRequestRetryCnt = 0; | ||
| 1324 | priv->ReAssociationRequestRetryCnt = 0; | ||
| 1325 | priv->CurrentAuthentTransactionSeqNum = 0x0001; | ||
| 1326 | priv->ExpectedAuthentTransactionSeqNum = 0x0002; | ||
| 1327 | |||
| 1328 | priv->site_survey_state = SITE_SURVEY_IDLE; | ||
| 1329 | priv->station_is_associated = 0; | ||
| 1330 | |||
| 1331 | err = reset_atmel_card(dev); | ||
| 1332 | if (err) | ||
| 1333 | return err; | ||
| 1334 | |||
| 1335 | if (priv->config_reg_domain) { | ||
| 1336 | priv->reg_domain = priv->config_reg_domain; | ||
| 1337 | atmel_set_mib8(priv, Phy_Mib_Type, PHY_MIB_REG_DOMAIN_POS, priv->reg_domain); | ||
| 1338 | } else { | ||
| 1339 | priv->reg_domain = atmel_get_mib8(priv, Phy_Mib_Type, PHY_MIB_REG_DOMAIN_POS); | ||
| 1340 | for (i = 0; i < ARRAY_SIZE(channel_table); i++) | ||
| 1341 | if (priv->reg_domain == channel_table[i].reg_domain) | ||
| 1342 | break; | ||
| 1343 | if (i == ARRAY_SIZE(channel_table)) { | ||
| 1344 | priv->reg_domain = REG_DOMAIN_MKK1; | ||
| 1345 | printk(KERN_ALERT "%s: failed to get regulatory domain: assuming MKK1.\n", dev->name); | ||
| 1346 | } | ||
| 1347 | } | ||
| 1348 | |||
| 1349 | if ((channel = atmel_validate_channel(priv, priv->channel))) | ||
| 1350 | priv->channel = channel; | ||
| 1351 | |||
| 1352 | /* this moves station_state on.... */ | ||
| 1353 | atmel_scan(priv, 1); | ||
| 1354 | |||
| 1355 | atmel_set_gcr(priv->dev, GCR_ENINT); /* enable interrupts */ | ||
| 1356 | return 0; | ||
| 1357 | } | ||
| 1358 | |||
| 1359 | static int atmel_close(struct net_device *dev) | ||
| 1360 | { | ||
| 1361 | struct atmel_private *priv = netdev_priv(dev); | ||
| 1362 | |||
| 1363 | /* Send event to userspace that we are disassociating */ | ||
| 1364 | if (priv->station_state == STATION_STATE_READY) { | ||
| 1365 | union iwreq_data wrqu; | ||
| 1366 | |||
| 1367 | wrqu.data.length = 0; | ||
| 1368 | wrqu.data.flags = 0; | ||
| 1369 | wrqu.ap_addr.sa_family = ARPHRD_ETHER; | ||
| 1370 | eth_zero_addr(wrqu.ap_addr.sa_data); | ||
| 1371 | wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL); | ||
| 1372 | } | ||
| 1373 | |||
| 1374 | atmel_enter_state(priv, STATION_STATE_DOWN); | ||
| 1375 | |||
| 1376 | if (priv->bus_type == BUS_TYPE_PCCARD) | ||
| 1377 | atmel_write16(dev, GCR, 0x0060); | ||
| 1378 | atmel_write16(dev, GCR, 0x0040); | ||
| 1379 | return 0; | ||
| 1380 | } | ||
| 1381 | |||
| 1382 | static int atmel_validate_channel(struct atmel_private *priv, int channel) | ||
| 1383 | { | ||
| 1384 | /* check that channel is OK, if so return zero, | ||
| 1385 | else return suitable default channel */ | ||
| 1386 | int i; | ||
| 1387 | |||
| 1388 | for (i = 0; i < ARRAY_SIZE(channel_table); i++) | ||
| 1389 | if (priv->reg_domain == channel_table[i].reg_domain) { | ||
| 1390 | if (channel >= channel_table[i].min && | ||
| 1391 | channel <= channel_table[i].max) | ||
| 1392 | return 0; | ||
| 1393 | else | ||
| 1394 | return channel_table[i].min; | ||
| 1395 | } | ||
| 1396 | return 0; | ||
| 1397 | } | ||
| 1398 | |||
| 1399 | #ifdef CONFIG_PROC_FS | ||
| 1400 | static int atmel_proc_show(struct seq_file *m, void *v) | ||
| 1401 | { | ||
| 1402 | struct atmel_private *priv = m->private; | ||
| 1403 | int i; | ||
| 1404 | char *s, *r, *c; | ||
| 1405 | |||
| 1406 | seq_printf(m, "Driver version:\t\t%d.%d\n", DRIVER_MAJOR, DRIVER_MINOR); | ||
| 1407 | |||
| 1408 | if (priv->station_state != STATION_STATE_DOWN) { | ||
| 1409 | seq_printf(m, | ||
| 1410 | "Firmware version:\t%d.%d build %d\n" | ||
| 1411 | "Firmware location:\t", | ||
| 1412 | priv->host_info.major_version, | ||
| 1413 | priv->host_info.minor_version, | ||
| 1414 | priv->host_info.build_version); | ||
| 1415 | |||
| 1416 | if (priv->card_type != CARD_TYPE_EEPROM) | ||
| 1417 | seq_puts(m, "on card\n"); | ||
| 1418 | else if (priv->firmware) | ||
| 1419 | seq_printf(m, "%s loaded by host\n", priv->firmware_id); | ||
| 1420 | else | ||
| 1421 | seq_printf(m, "%s loaded by hotplug\n", priv->firmware_id); | ||
| 1422 | |||
| 1423 | switch (priv->card_type) { | ||
| 1424 | case CARD_TYPE_PARALLEL_FLASH: | ||
| 1425 | c = "Parallel flash"; | ||
| 1426 | break; | ||
| 1427 | case CARD_TYPE_SPI_FLASH: | ||
| 1428 | c = "SPI flash\n"; | ||
| 1429 | break; | ||
| 1430 | case CARD_TYPE_EEPROM: | ||
| 1431 | c = "EEPROM"; | ||
| 1432 | break; | ||
| 1433 | default: | ||
| 1434 | c = "<unknown>"; | ||
| 1435 | } | ||
| 1436 | |||
| 1437 | r = "<unknown>"; | ||
| 1438 | for (i = 0; i < ARRAY_SIZE(channel_table); i++) | ||
| 1439 | if (priv->reg_domain == channel_table[i].reg_domain) | ||
| 1440 | r = channel_table[i].name; | ||
| 1441 | |||
| 1442 | seq_printf(m, "MAC memory type:\t%s\n", c); | ||
| 1443 | seq_printf(m, "Regulatory domain:\t%s\n", r); | ||
| 1444 | seq_printf(m, "Host CRC checking:\t%s\n", | ||
| 1445 | priv->do_rx_crc ? "On" : "Off"); | ||
| 1446 | seq_printf(m, "WPA-capable firmware:\t%s\n", | ||
| 1447 | priv->use_wpa ? "Yes" : "No"); | ||
| 1448 | } | ||
| 1449 | |||
| 1450 | switch (priv->station_state) { | ||
| 1451 | case STATION_STATE_SCANNING: | ||
| 1452 | s = "Scanning"; | ||
| 1453 | break; | ||
| 1454 | case STATION_STATE_JOINNING: | ||
| 1455 | s = "Joining"; | ||
| 1456 | break; | ||
| 1457 | case STATION_STATE_AUTHENTICATING: | ||
| 1458 | s = "Authenticating"; | ||
| 1459 | break; | ||
| 1460 | case STATION_STATE_ASSOCIATING: | ||
| 1461 | s = "Associating"; | ||
| 1462 | break; | ||
| 1463 | case STATION_STATE_READY: | ||
| 1464 | s = "Ready"; | ||
| 1465 | break; | ||
| 1466 | case STATION_STATE_REASSOCIATING: | ||
| 1467 | s = "Reassociating"; | ||
| 1468 | break; | ||
| 1469 | case STATION_STATE_MGMT_ERROR: | ||
| 1470 | s = "Management error"; | ||
| 1471 | break; | ||
| 1472 | case STATION_STATE_DOWN: | ||
| 1473 | s = "Down"; | ||
| 1474 | break; | ||
| 1475 | default: | ||
| 1476 | s = "<unknown>"; | ||
| 1477 | } | ||
| 1478 | |||
| 1479 | seq_printf(m, "Current state:\t\t%s\n", s); | ||
| 1480 | return 0; | ||
| 1481 | } | ||
| 1482 | #endif | ||
| 1483 | |||
| 1484 | static const struct net_device_ops atmel_netdev_ops = { | ||
| 1485 | .ndo_open = atmel_open, | ||
| 1486 | .ndo_stop = atmel_close, | ||
| 1487 | .ndo_set_mac_address = atmel_set_mac_address, | ||
| 1488 | .ndo_start_xmit = start_tx, | ||
| 1489 | .ndo_validate_addr = eth_validate_addr, | ||
| 1490 | }; | ||
| 1491 | |||
| 1492 | struct net_device *init_atmel_card(unsigned short irq, unsigned long port, | ||
| 1493 | const AtmelFWType fw_type, | ||
| 1494 | struct device *sys_dev, | ||
| 1495 | int (*card_present)(void *), void *card) | ||
| 1496 | { | ||
| 1497 | struct net_device *dev; | ||
| 1498 | struct atmel_private *priv; | ||
| 1499 | int rc; | ||
| 1500 | |||
| 1501 | /* Create the network device object. */ | ||
| 1502 | dev = alloc_etherdev(sizeof(*priv)); | ||
| 1503 | if (!dev) | ||
| 1504 | return NULL; | ||
| 1505 | |||
| 1506 | if (dev_alloc_name(dev, dev->name) < 0) { | ||
| 1507 | printk(KERN_ERR "atmel: Couldn't get name!\n"); | ||
| 1508 | goto err_out_free; | ||
| 1509 | } | ||
| 1510 | |||
| 1511 | priv = netdev_priv(dev); | ||
| 1512 | priv->dev = dev; | ||
| 1513 | priv->sys_dev = sys_dev; | ||
| 1514 | priv->present_callback = card_present; | ||
| 1515 | priv->card = card; | ||
| 1516 | priv->firmware = NULL; | ||
| 1517 | priv->firmware_type = fw_type; | ||
| 1518 | if (firmware) /* module parameter */ | ||
| 1519 | strscpy(priv->firmware_id, firmware, sizeof(priv->firmware_id)); | ||
| 1520 | priv->bus_type = card_present ? BUS_TYPE_PCCARD : BUS_TYPE_PCI; | ||
| 1521 | priv->station_state = STATION_STATE_DOWN; | ||
| 1522 | priv->do_rx_crc = 0; | ||
| 1523 | /* For PCMCIA cards, some chips need CRC, some don't | ||
| 1524 | so we have to probe. */ | ||
| 1525 | if (priv->bus_type == BUS_TYPE_PCCARD) { | ||
| 1526 | priv->probe_crc = 1; | ||
| 1527 | priv->crc_ok_cnt = priv->crc_ko_cnt = 0; | ||
| 1528 | } else | ||
| 1529 | priv->probe_crc = 0; | ||
| 1530 | priv->last_qual = jiffies; | ||
| 1531 | priv->last_beacon_timestamp = 0; | ||
| 1532 | memset(priv->frag_source, 0xff, sizeof(priv->frag_source)); | ||
| 1533 | eth_zero_addr(priv->BSSID); | ||
| 1534 | priv->CurrentBSSID[0] = 0xFF; /* Initialize to something invalid.... */ | ||
| 1535 | priv->station_was_associated = 0; | ||
| 1536 | |||
| 1537 | priv->last_survey = jiffies; | ||
| 1538 | priv->preamble = LONG_PREAMBLE; | ||
| 1539 | priv->operating_mode = IW_MODE_INFRA; | ||
| 1540 | priv->connect_to_any_BSS = 0; | ||
| 1541 | priv->config_reg_domain = 0; | ||
| 1542 | priv->reg_domain = 0; | ||
| 1543 | priv->tx_rate = 3; | ||
| 1544 | priv->auto_tx_rate = 1; | ||
| 1545 | priv->channel = 4; | ||
| 1546 | priv->power_mode = 0; | ||
| 1547 | priv->SSID[0] = '\0'; | ||
| 1548 | priv->SSID_size = 0; | ||
| 1549 | priv->new_SSID_size = 0; | ||
| 1550 | priv->frag_threshold = 2346; | ||
| 1551 | priv->rts_threshold = 2347; | ||
| 1552 | priv->short_retry = 7; | ||
| 1553 | priv->long_retry = 4; | ||
| 1554 | |||
| 1555 | priv->wep_is_on = 0; | ||
| 1556 | priv->default_key = 0; | ||
| 1557 | priv->encryption_level = 0; | ||
| 1558 | priv->exclude_unencrypted = 0; | ||
| 1559 | priv->group_cipher_suite = priv->pairwise_cipher_suite = CIPHER_SUITE_NONE; | ||
| 1560 | priv->use_wpa = 0; | ||
| 1561 | memset(priv->wep_keys, 0, sizeof(priv->wep_keys)); | ||
| 1562 | memset(priv->wep_key_len, 0, sizeof(priv->wep_key_len)); | ||
| 1563 | |||
| 1564 | priv->default_beacon_period = priv->beacon_period = 100; | ||
| 1565 | priv->listen_interval = 1; | ||
| 1566 | |||
| 1567 | timer_setup(&priv->management_timer, atmel_management_timer, 0); | ||
| 1568 | spin_lock_init(&priv->irqlock); | ||
| 1569 | spin_lock_init(&priv->timerlock); | ||
| 1570 | |||
| 1571 | dev->netdev_ops = &atmel_netdev_ops; | ||
| 1572 | dev->wireless_handlers = &atmel_handler_def; | ||
| 1573 | dev->irq = irq; | ||
| 1574 | dev->base_addr = port; | ||
| 1575 | |||
| 1576 | /* MTU range: 68 - 2312 */ | ||
| 1577 | dev->min_mtu = 68; | ||
| 1578 | dev->max_mtu = MAX_WIRELESS_BODY - ETH_FCS_LEN; | ||
| 1579 | |||
| 1580 | SET_NETDEV_DEV(dev, sys_dev); | ||
| 1581 | |||
| 1582 | if ((rc = request_irq(dev->irq, service_interrupt, IRQF_SHARED, dev->name, dev))) { | ||
| 1583 | printk(KERN_ERR "%s: register interrupt %d failed, rc %d\n", dev->name, irq, rc); | ||
| 1584 | goto err_out_free; | ||
| 1585 | } | ||
| 1586 | |||
| 1587 | if (!request_region(dev->base_addr, 32, | ||
| 1588 | priv->bus_type == BUS_TYPE_PCCARD ? "atmel_cs" : "atmel_pci")) { | ||
| 1589 | goto err_out_irq; | ||
| 1590 | } | ||
| 1591 | |||
| 1592 | if (register_netdev(dev)) | ||
| 1593 | goto err_out_res; | ||
| 1594 | |||
| 1595 | if (!probe_atmel_card(dev)) { | ||
| 1596 | unregister_netdev(dev); | ||
| 1597 | goto err_out_res; | ||
| 1598 | } | ||
| 1599 | |||
| 1600 | netif_carrier_off(dev); | ||
| 1601 | |||
| 1602 | if (!proc_create_single_data("driver/atmel", 0, NULL, atmel_proc_show, | ||
| 1603 | priv)) | ||
| 1604 | printk(KERN_WARNING "atmel: unable to create /proc entry.\n"); | ||
| 1605 | |||
| 1606 | printk(KERN_INFO "%s: Atmel at76c50x. Version %d.%d. MAC %pM\n", | ||
| 1607 | dev->name, DRIVER_MAJOR, DRIVER_MINOR, dev->dev_addr); | ||
| 1608 | |||
| 1609 | return dev; | ||
| 1610 | |||
| 1611 | err_out_res: | ||
| 1612 | release_region(dev->base_addr, 32); | ||
| 1613 | err_out_irq: | ||
| 1614 | free_irq(dev->irq, dev); | ||
| 1615 | err_out_free: | ||
| 1616 | free_netdev(dev); | ||
| 1617 | return NULL; | ||
| 1618 | } | ||
| 1619 | |||
| 1620 | EXPORT_SYMBOL(init_atmel_card); | ||
| 1621 | |||
| 1622 | void stop_atmel_card(struct net_device *dev) | ||
| 1623 | { | ||
| 1624 | struct atmel_private *priv = netdev_priv(dev); | ||
| 1625 | |||
| 1626 | /* put a brick on it... */ | ||
| 1627 | if (priv->bus_type == BUS_TYPE_PCCARD) | ||
| 1628 | atmel_write16(dev, GCR, 0x0060); | ||
| 1629 | atmel_write16(dev, GCR, 0x0040); | ||
| 1630 | |||
| 1631 | del_timer_sync(&priv->management_timer); | ||
| 1632 | unregister_netdev(dev); | ||
| 1633 | remove_proc_entry("driver/atmel", NULL); | ||
| 1634 | free_irq(dev->irq, dev); | ||
| 1635 | kfree(priv->firmware); | ||
| 1636 | release_region(dev->base_addr, 32); | ||
| 1637 | free_netdev(dev); | ||
| 1638 | } | ||
| 1639 | |||
| 1640 | EXPORT_SYMBOL(stop_atmel_card); | ||
| 1641 | |||
| 1642 | static int atmel_set_essid(struct net_device *dev, | ||
| 1643 | struct iw_request_info *info, | ||
| 1644 | union iwreq_data *wrqu, | ||
| 1645 | char *extra) | ||
| 1646 | { | ||
| 1647 | struct iw_point *dwrq = &wrqu->essid; | ||
| 1648 | struct atmel_private *priv = netdev_priv(dev); | ||
| 1649 | |||
| 1650 | /* Check if we asked for `any' */ | ||
| 1651 | if (dwrq->flags == 0) { | ||
| 1652 | priv->connect_to_any_BSS = 1; | ||
| 1653 | } else { | ||
| 1654 | int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; | ||
| 1655 | |||
| 1656 | priv->connect_to_any_BSS = 0; | ||
| 1657 | |||
| 1658 | /* Check the size of the string */ | ||
| 1659 | if (dwrq->length > MAX_SSID_LENGTH) | ||
| 1660 | return -E2BIG; | ||
| 1661 | if (index != 0) | ||
| 1662 | return -EINVAL; | ||
| 1663 | |||
| 1664 | memcpy(priv->new_SSID, extra, dwrq->length); | ||
| 1665 | priv->new_SSID_size = dwrq->length; | ||
| 1666 | } | ||
| 1667 | |||
| 1668 | return -EINPROGRESS; | ||
| 1669 | } | ||
| 1670 | |||
| 1671 | static int atmel_get_essid(struct net_device *dev, | ||
| 1672 | struct iw_request_info *info, | ||
| 1673 | union iwreq_data *wrqu, | ||
| 1674 | char *extra) | ||
| 1675 | { | ||
| 1676 | struct iw_point *dwrq = &wrqu->essid; | ||
| 1677 | struct atmel_private *priv = netdev_priv(dev); | ||
| 1678 | |||
| 1679 | /* Get the current SSID */ | ||
| 1680 | if (priv->new_SSID_size != 0) { | ||
| 1681 | memcpy(extra, priv->new_SSID, priv->new_SSID_size); | ||
| 1682 | dwrq->length = priv->new_SSID_size; | ||
| 1683 | } else { | ||
| 1684 | memcpy(extra, priv->SSID, priv->SSID_size); | ||
| 1685 | dwrq->length = priv->SSID_size; | ||
| 1686 | } | ||
| 1687 | |||
| 1688 | dwrq->flags = !priv->connect_to_any_BSS; /* active */ | ||
| 1689 | |||
| 1690 | return 0; | ||
| 1691 | } | ||
| 1692 | |||
| 1693 | static int atmel_get_wap(struct net_device *dev, | ||
| 1694 | struct iw_request_info *info, | ||
| 1695 | union iwreq_data *wrqu, | ||
| 1696 | char *extra) | ||
| 1697 | { | ||
| 1698 | struct sockaddr *awrq = &wrqu->ap_addr; | ||
| 1699 | struct atmel_private *priv = netdev_priv(dev); | ||
| 1700 | memcpy(awrq->sa_data, priv->CurrentBSSID, ETH_ALEN); | ||
| 1701 | awrq->sa_family = ARPHRD_ETHER; | ||
| 1702 | |||
| 1703 | return 0; | ||
| 1704 | } | ||
| 1705 | |||
| 1706 | static int atmel_set_encode(struct net_device *dev, | ||
| 1707 | struct iw_request_info *info, | ||
| 1708 | union iwreq_data *wrqu, | ||
| 1709 | char *extra) | ||
| 1710 | { | ||
| 1711 | struct iw_point *dwrq = &wrqu->encoding; | ||
| 1712 | struct atmel_private *priv = netdev_priv(dev); | ||
| 1713 | |||
| 1714 | /* Basic checking: do we have a key to set ? | ||
| 1715 | * Note : with the new API, it's impossible to get a NULL pointer. | ||
| 1716 | * Therefore, we need to check a key size == 0 instead. | ||
| 1717 | * New version of iwconfig properly set the IW_ENCODE_NOKEY flag | ||
| 1718 | * when no key is present (only change flags), but older versions | ||
| 1719 | * don't do it. - Jean II */ | ||
| 1720 | if (dwrq->length > 0) { | ||
| 1721 | int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; | ||
| 1722 | int current_index = priv->default_key; | ||
| 1723 | /* Check the size of the key */ | ||
| 1724 | if (dwrq->length > 13) { | ||
| 1725 | return -EINVAL; | ||
| 1726 | } | ||
| 1727 | /* Check the index (none -> use current) */ | ||
| 1728 | if (index < 0 || index >= 4) | ||
| 1729 | index = current_index; | ||
| 1730 | else | ||
| 1731 | priv->default_key = index; | ||
| 1732 | /* Set the length */ | ||
| 1733 | if (dwrq->length > 5) | ||
| 1734 | priv->wep_key_len[index] = 13; | ||
| 1735 | else | ||
| 1736 | if (dwrq->length > 0) | ||
| 1737 | priv->wep_key_len[index] = 5; | ||
| 1738 | else | ||
| 1739 | /* Disable the key */ | ||
| 1740 | priv->wep_key_len[index] = 0; | ||
| 1741 | /* Check if the key is not marked as invalid */ | ||
| 1742 | if (!(dwrq->flags & IW_ENCODE_NOKEY)) { | ||
| 1743 | /* Cleanup */ | ||
| 1744 | memset(priv->wep_keys[index], 0, 13); | ||
| 1745 | /* Copy the key in the driver */ | ||
| 1746 | memcpy(priv->wep_keys[index], extra, dwrq->length); | ||
| 1747 | } | ||
| 1748 | /* WE specify that if a valid key is set, encryption | ||
| 1749 | * should be enabled (user may turn it off later) | ||
| 1750 | * This is also how "iwconfig ethX key on" works */ | ||
| 1751 | if (index == current_index && | ||
| 1752 | priv->wep_key_len[index] > 0) { | ||
| 1753 | priv->wep_is_on = 1; | ||
| 1754 | priv->exclude_unencrypted = 1; | ||
| 1755 | if (priv->wep_key_len[index] > 5) { | ||
| 1756 | priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_128; | ||
| 1757 | priv->encryption_level = 2; | ||
| 1758 | } else { | ||
| 1759 | priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_64; | ||
| 1760 | priv->encryption_level = 1; | ||
| 1761 | } | ||
| 1762 | } | ||
| 1763 | } else { | ||
| 1764 | /* Do we want to just set the transmit key index ? */ | ||
| 1765 | int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; | ||
| 1766 | if (index >= 0 && index < 4) { | ||
| 1767 | priv->default_key = index; | ||
| 1768 | } else | ||
| 1769 | /* Don't complain if only change the mode */ | ||
| 1770 | if (!(dwrq->flags & IW_ENCODE_MODE)) | ||
| 1771 | return -EINVAL; | ||
| 1772 | } | ||
| 1773 | /* Read the flags */ | ||
| 1774 | if (dwrq->flags & IW_ENCODE_DISABLED) { | ||
| 1775 | priv->wep_is_on = 0; | ||
| 1776 | priv->encryption_level = 0; | ||
| 1777 | priv->pairwise_cipher_suite = CIPHER_SUITE_NONE; | ||
| 1778 | } else { | ||
| 1779 | priv->wep_is_on = 1; | ||
| 1780 | if (priv->wep_key_len[priv->default_key] > 5) { | ||
| 1781 | priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_128; | ||
| 1782 | priv->encryption_level = 2; | ||
| 1783 | } else { | ||
| 1784 | priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_64; | ||
| 1785 | priv->encryption_level = 1; | ||
| 1786 | } | ||
| 1787 | } | ||
| 1788 | if (dwrq->flags & IW_ENCODE_RESTRICTED) | ||
| 1789 | priv->exclude_unencrypted = 1; | ||
| 1790 | if (dwrq->flags & IW_ENCODE_OPEN) | ||
| 1791 | priv->exclude_unencrypted = 0; | ||
| 1792 | |||
| 1793 | return -EINPROGRESS; /* Call commit handler */ | ||
| 1794 | } | ||
| 1795 | |||
| 1796 | static int atmel_get_encode(struct net_device *dev, | ||
| 1797 | struct iw_request_info *info, | ||
| 1798 | union iwreq_data *wrqu, | ||
| 1799 | char *extra) | ||
| 1800 | { | ||
| 1801 | struct iw_point *dwrq = &wrqu->encoding; | ||
| 1802 | struct atmel_private *priv = netdev_priv(dev); | ||
| 1803 | int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; | ||
| 1804 | |||
| 1805 | if (!priv->wep_is_on) | ||
| 1806 | dwrq->flags = IW_ENCODE_DISABLED; | ||
| 1807 | else { | ||
| 1808 | if (priv->exclude_unencrypted) | ||
| 1809 | dwrq->flags = IW_ENCODE_RESTRICTED; | ||
| 1810 | else | ||
| 1811 | dwrq->flags = IW_ENCODE_OPEN; | ||
| 1812 | } | ||
| 1813 | /* Which key do we want ? -1 -> tx index */ | ||
| 1814 | if (index < 0 || index >= 4) | ||
| 1815 | index = priv->default_key; | ||
| 1816 | dwrq->flags |= index + 1; | ||
| 1817 | /* Copy the key to the user buffer */ | ||
| 1818 | dwrq->length = priv->wep_key_len[index]; | ||
| 1819 | if (dwrq->length > 16) { | ||
| 1820 | dwrq->length = 0; | ||
| 1821 | } else { | ||
| 1822 | memset(extra, 0, 16); | ||
| 1823 | memcpy(extra, priv->wep_keys[index], dwrq->length); | ||
| 1824 | } | ||
| 1825 | |||
| 1826 | return 0; | ||
| 1827 | } | ||
| 1828 | |||
| 1829 | static int atmel_set_encodeext(struct net_device *dev, | ||
| 1830 | struct iw_request_info *info, | ||
| 1831 | union iwreq_data *wrqu, | ||
| 1832 | char *extra) | ||
| 1833 | { | ||
| 1834 | struct atmel_private *priv = netdev_priv(dev); | ||
| 1835 | struct iw_point *encoding = &wrqu->encoding; | ||
| 1836 | struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; | ||
| 1837 | int idx, key_len, alg = ext->alg, set_key = 1; | ||
| 1838 | |||
| 1839 | /* Determine and validate the key index */ | ||
| 1840 | idx = encoding->flags & IW_ENCODE_INDEX; | ||
| 1841 | if (idx) { | ||
| 1842 | if (idx < 1 || idx > 4) | ||
| 1843 | return -EINVAL; | ||
| 1844 | idx--; | ||
| 1845 | } else | ||
| 1846 | idx = priv->default_key; | ||
| 1847 | |||
| 1848 | if (encoding->flags & IW_ENCODE_DISABLED) | ||
| 1849 | alg = IW_ENCODE_ALG_NONE; | ||
| 1850 | |||
| 1851 | if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { | ||
| 1852 | priv->default_key = idx; | ||
| 1853 | set_key = ext->key_len > 0 ? 1 : 0; | ||
| 1854 | } | ||
| 1855 | |||
| 1856 | if (set_key) { | ||
| 1857 | /* Set the requested key first */ | ||
| 1858 | switch (alg) { | ||
| 1859 | case IW_ENCODE_ALG_NONE: | ||
| 1860 | priv->wep_is_on = 0; | ||
| 1861 | priv->encryption_level = 0; | ||
| 1862 | priv->pairwise_cipher_suite = CIPHER_SUITE_NONE; | ||
| 1863 | break; | ||
| 1864 | case IW_ENCODE_ALG_WEP: | ||
| 1865 | if (ext->key_len > 5) { | ||
| 1866 | priv->wep_key_len[idx] = 13; | ||
| 1867 | priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_128; | ||
| 1868 | priv->encryption_level = 2; | ||
| 1869 | } else if (ext->key_len > 0) { | ||
| 1870 | priv->wep_key_len[idx] = 5; | ||
| 1871 | priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_64; | ||
| 1872 | priv->encryption_level = 1; | ||
| 1873 | } else { | ||
| 1874 | return -EINVAL; | ||
| 1875 | } | ||
| 1876 | priv->wep_is_on = 1; | ||
| 1877 | memset(priv->wep_keys[idx], 0, 13); | ||
| 1878 | key_len = min ((int)ext->key_len, priv->wep_key_len[idx]); | ||
| 1879 | memcpy(priv->wep_keys[idx], ext->key, key_len); | ||
| 1880 | break; | ||
| 1881 | default: | ||
| 1882 | return -EINVAL; | ||
| 1883 | } | ||
| 1884 | } | ||
| 1885 | |||
| 1886 | return -EINPROGRESS; | ||
| 1887 | } | ||
| 1888 | |||
| 1889 | static int atmel_get_encodeext(struct net_device *dev, | ||
| 1890 | struct iw_request_info *info, | ||
| 1891 | union iwreq_data *wrqu, | ||
| 1892 | char *extra) | ||
| 1893 | { | ||
| 1894 | struct atmel_private *priv = netdev_priv(dev); | ||
| 1895 | struct iw_point *encoding = &wrqu->encoding; | ||
| 1896 | struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; | ||
| 1897 | int idx, max_key_len; | ||
| 1898 | |||
| 1899 | max_key_len = encoding->length - sizeof(*ext); | ||
| 1900 | if (max_key_len < 0) | ||
| 1901 | return -EINVAL; | ||
| 1902 | |||
| 1903 | idx = encoding->flags & IW_ENCODE_INDEX; | ||
| 1904 | if (idx) { | ||
| 1905 | if (idx < 1 || idx > 4) | ||
| 1906 | return -EINVAL; | ||
| 1907 | idx--; | ||
| 1908 | } else | ||
| 1909 | idx = priv->default_key; | ||
| 1910 | |||
| 1911 | encoding->flags = idx + 1; | ||
| 1912 | memset(ext, 0, sizeof(*ext)); | ||
| 1913 | |||
| 1914 | if (!priv->wep_is_on) { | ||
| 1915 | ext->alg = IW_ENCODE_ALG_NONE; | ||
| 1916 | ext->key_len = 0; | ||
| 1917 | encoding->flags |= IW_ENCODE_DISABLED; | ||
| 1918 | } else { | ||
| 1919 | if (priv->encryption_level > 0) | ||
| 1920 | ext->alg = IW_ENCODE_ALG_WEP; | ||
| 1921 | else | ||
| 1922 | return -EINVAL; | ||
| 1923 | |||
| 1924 | ext->key_len = priv->wep_key_len[idx]; | ||
| 1925 | memcpy(ext->key, priv->wep_keys[idx], ext->key_len); | ||
| 1926 | encoding->flags |= IW_ENCODE_ENABLED; | ||
| 1927 | } | ||
| 1928 | |||
| 1929 | return 0; | ||
| 1930 | } | ||
| 1931 | |||
| 1932 | static int atmel_set_auth(struct net_device *dev, | ||
| 1933 | struct iw_request_info *info, | ||
| 1934 | union iwreq_data *wrqu, char *extra) | ||
| 1935 | { | ||
| 1936 | struct atmel_private *priv = netdev_priv(dev); | ||
| 1937 | struct iw_param *param = &wrqu->param; | ||
| 1938 | |||
| 1939 | switch (param->flags & IW_AUTH_INDEX) { | ||
| 1940 | case IW_AUTH_WPA_VERSION: | ||
| 1941 | case IW_AUTH_CIPHER_PAIRWISE: | ||
| 1942 | case IW_AUTH_CIPHER_GROUP: | ||
| 1943 | case IW_AUTH_KEY_MGMT: | ||
| 1944 | case IW_AUTH_RX_UNENCRYPTED_EAPOL: | ||
| 1945 | case IW_AUTH_PRIVACY_INVOKED: | ||
| 1946 | /* | ||
| 1947 | * atmel does not use these parameters | ||
| 1948 | */ | ||
| 1949 | break; | ||
| 1950 | |||
| 1951 | case IW_AUTH_DROP_UNENCRYPTED: | ||
| 1952 | priv->exclude_unencrypted = param->value ? 1 : 0; | ||
| 1953 | break; | ||
| 1954 | |||
| 1955 | case IW_AUTH_80211_AUTH_ALG: { | ||
| 1956 | if (param->value & IW_AUTH_ALG_SHARED_KEY) { | ||
| 1957 | priv->exclude_unencrypted = 1; | ||
| 1958 | } else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) { | ||
| 1959 | priv->exclude_unencrypted = 0; | ||
| 1960 | } else | ||
| 1961 | return -EINVAL; | ||
| 1962 | break; | ||
| 1963 | } | ||
| 1964 | |||
| 1965 | case IW_AUTH_WPA_ENABLED: | ||
| 1966 | /* Silently accept disable of WPA */ | ||
| 1967 | if (param->value > 0) | ||
| 1968 | return -EOPNOTSUPP; | ||
| 1969 | break; | ||
| 1970 | |||
| 1971 | default: | ||
| 1972 | return -EOPNOTSUPP; | ||
| 1973 | } | ||
| 1974 | return -EINPROGRESS; | ||
| 1975 | } | ||
| 1976 | |||
| 1977 | static int atmel_get_auth(struct net_device *dev, | ||
| 1978 | struct iw_request_info *info, | ||
| 1979 | union iwreq_data *wrqu, char *extra) | ||
| 1980 | { | ||
| 1981 | struct atmel_private *priv = netdev_priv(dev); | ||
| 1982 | struct iw_param *param = &wrqu->param; | ||
| 1983 | |||
| 1984 | switch (param->flags & IW_AUTH_INDEX) { | ||
| 1985 | case IW_AUTH_DROP_UNENCRYPTED: | ||
| 1986 | param->value = priv->exclude_unencrypted; | ||
| 1987 | break; | ||
| 1988 | |||
| 1989 | case IW_AUTH_80211_AUTH_ALG: | ||
| 1990 | if (priv->exclude_unencrypted == 1) | ||
| 1991 | param->value = IW_AUTH_ALG_SHARED_KEY; | ||
| 1992 | else | ||
| 1993 | param->value = IW_AUTH_ALG_OPEN_SYSTEM; | ||
| 1994 | break; | ||
| 1995 | |||
| 1996 | case IW_AUTH_WPA_ENABLED: | ||
| 1997 | param->value = 0; | ||
| 1998 | break; | ||
| 1999 | |||
| 2000 | default: | ||
| 2001 | return -EOPNOTSUPP; | ||
| 2002 | } | ||
| 2003 | return 0; | ||
| 2004 | } | ||
| 2005 | |||
| 2006 | |||
| 2007 | static int atmel_get_name(struct net_device *dev, | ||
| 2008 | struct iw_request_info *info, | ||
| 2009 | union iwreq_data *wrqu, | ||
| 2010 | char *extra) | ||
| 2011 | { | ||
| 2012 | strcpy(wrqu->name, "IEEE 802.11-DS"); | ||
| 2013 | return 0; | ||
| 2014 | } | ||
| 2015 | |||
| 2016 | static int atmel_set_rate(struct net_device *dev, | ||
| 2017 | struct iw_request_info *info, | ||
| 2018 | union iwreq_data *wrqu, | ||
| 2019 | char *extra) | ||
| 2020 | { | ||
| 2021 | struct iw_param *vwrq = &wrqu->bitrate; | ||
| 2022 | struct atmel_private *priv = netdev_priv(dev); | ||
| 2023 | |||
| 2024 | if (vwrq->fixed == 0) { | ||
| 2025 | priv->tx_rate = 3; | ||
| 2026 | priv->auto_tx_rate = 1; | ||
| 2027 | } else { | ||
| 2028 | priv->auto_tx_rate = 0; | ||
| 2029 | |||
| 2030 | /* Which type of value ? */ | ||
| 2031 | if ((vwrq->value < 4) && (vwrq->value >= 0)) { | ||
| 2032 | /* Setting by rate index */ | ||
| 2033 | priv->tx_rate = vwrq->value; | ||
| 2034 | } else { | ||
| 2035 | /* Setting by frequency value */ | ||
| 2036 | switch (vwrq->value) { | ||
| 2037 | case 1000000: | ||
| 2038 | priv->tx_rate = 0; | ||
| 2039 | break; | ||
| 2040 | case 2000000: | ||
| 2041 | priv->tx_rate = 1; | ||
| 2042 | break; | ||
| 2043 | case 5500000: | ||
| 2044 | priv->tx_rate = 2; | ||
| 2045 | break; | ||
| 2046 | case 11000000: | ||
| 2047 | priv->tx_rate = 3; | ||
| 2048 | break; | ||
| 2049 | default: | ||
| 2050 | return -EINVAL; | ||
| 2051 | } | ||
| 2052 | } | ||
| 2053 | } | ||
| 2054 | |||
| 2055 | return -EINPROGRESS; | ||
| 2056 | } | ||
| 2057 | |||
| 2058 | static int atmel_set_mode(struct net_device *dev, | ||
| 2059 | struct iw_request_info *info, | ||
| 2060 | union iwreq_data *wrqu, | ||
| 2061 | char *extra) | ||
| 2062 | { | ||
| 2063 | __u32 *uwrq = &wrqu->mode; | ||
| 2064 | struct atmel_private *priv = netdev_priv(dev); | ||
| 2065 | |||
| 2066 | if (*uwrq != IW_MODE_ADHOC && *uwrq != IW_MODE_INFRA) | ||
| 2067 | return -EINVAL; | ||
| 2068 | |||
| 2069 | priv->operating_mode = *uwrq; | ||
| 2070 | return -EINPROGRESS; | ||
| 2071 | } | ||
| 2072 | |||
| 2073 | static int atmel_get_mode(struct net_device *dev, | ||
| 2074 | struct iw_request_info *info, | ||
| 2075 | union iwreq_data *wrqu, | ||
| 2076 | char *extra) | ||
| 2077 | { | ||
| 2078 | __u32 *uwrq = &wrqu->mode; | ||
| 2079 | struct atmel_private *priv = netdev_priv(dev); | ||
| 2080 | |||
| 2081 | *uwrq = priv->operating_mode; | ||
| 2082 | return 0; | ||
| 2083 | } | ||
| 2084 | |||
| 2085 | static int atmel_get_rate(struct net_device *dev, | ||
| 2086 | struct iw_request_info *info, | ||
| 2087 | union iwreq_data *wrqu, | ||
| 2088 | char *extra) | ||
| 2089 | { | ||
| 2090 | struct iw_param *vwrq = &wrqu->bitrate; | ||
| 2091 | struct atmel_private *priv = netdev_priv(dev); | ||
| 2092 | |||
| 2093 | if (priv->auto_tx_rate) { | ||
| 2094 | vwrq->fixed = 0; | ||
| 2095 | vwrq->value = 11000000; | ||
| 2096 | } else { | ||
| 2097 | vwrq->fixed = 1; | ||
| 2098 | switch (priv->tx_rate) { | ||
| 2099 | case 0: | ||
| 2100 | vwrq->value = 1000000; | ||
| 2101 | break; | ||
| 2102 | case 1: | ||
| 2103 | vwrq->value = 2000000; | ||
| 2104 | break; | ||
| 2105 | case 2: | ||
| 2106 | vwrq->value = 5500000; | ||
| 2107 | break; | ||
| 2108 | case 3: | ||
| 2109 | vwrq->value = 11000000; | ||
| 2110 | break; | ||
| 2111 | } | ||
| 2112 | } | ||
| 2113 | return 0; | ||
| 2114 | } | ||
| 2115 | |||
| 2116 | static int atmel_set_power(struct net_device *dev, | ||
| 2117 | struct iw_request_info *info, | ||
| 2118 | union iwreq_data *wrqu, | ||
| 2119 | char *extra) | ||
| 2120 | { | ||
| 2121 | struct iw_param *vwrq = &wrqu->power; | ||
| 2122 | struct atmel_private *priv = netdev_priv(dev); | ||
| 2123 | priv->power_mode = vwrq->disabled ? 0 : 1; | ||
| 2124 | return -EINPROGRESS; | ||
| 2125 | } | ||
| 2126 | |||
| 2127 | static int atmel_get_power(struct net_device *dev, | ||
| 2128 | struct iw_request_info *info, | ||
| 2129 | union iwreq_data *wrqu, | ||
| 2130 | char *extra) | ||
| 2131 | { | ||
| 2132 | struct iw_param *vwrq = &wrqu->power; | ||
| 2133 | struct atmel_private *priv = netdev_priv(dev); | ||
| 2134 | vwrq->disabled = priv->power_mode ? 0 : 1; | ||
| 2135 | vwrq->flags = IW_POWER_ON; | ||
| 2136 | return 0; | ||
| 2137 | } | ||
| 2138 | |||
| 2139 | static int atmel_set_retry(struct net_device *dev, | ||
| 2140 | struct iw_request_info *info, | ||
| 2141 | union iwreq_data *wrqu, | ||
| 2142 | char *extra) | ||
| 2143 | { | ||
| 2144 | struct iw_param *vwrq = &wrqu->retry; | ||
| 2145 | struct atmel_private *priv = netdev_priv(dev); | ||
| 2146 | |||
| 2147 | if (!vwrq->disabled && (vwrq->flags & IW_RETRY_LIMIT)) { | ||
| 2148 | if (vwrq->flags & IW_RETRY_LONG) | ||
| 2149 | priv->long_retry = vwrq->value; | ||
| 2150 | else if (vwrq->flags & IW_RETRY_SHORT) | ||
| 2151 | priv->short_retry = vwrq->value; | ||
| 2152 | else { | ||
| 2153 | /* No modifier : set both */ | ||
| 2154 | priv->long_retry = vwrq->value; | ||
| 2155 | priv->short_retry = vwrq->value; | ||
| 2156 | } | ||
| 2157 | return -EINPROGRESS; | ||
| 2158 | } | ||
| 2159 | |||
| 2160 | return -EINVAL; | ||
| 2161 | } | ||
| 2162 | |||
| 2163 | static int atmel_get_retry(struct net_device *dev, | ||
| 2164 | struct iw_request_info *info, | ||
| 2165 | union iwreq_data *wrqu, | ||
| 2166 | char *extra) | ||
| 2167 | { | ||
| 2168 | struct iw_param *vwrq = &wrqu->retry; | ||
| 2169 | struct atmel_private *priv = netdev_priv(dev); | ||
| 2170 | |||
| 2171 | vwrq->disabled = 0; /* Can't be disabled */ | ||
| 2172 | |||
| 2173 | /* Note : by default, display the short retry number */ | ||
| 2174 | if (vwrq->flags & IW_RETRY_LONG) { | ||
| 2175 | vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG; | ||
| 2176 | vwrq->value = priv->long_retry; | ||
| 2177 | } else { | ||
| 2178 | vwrq->flags = IW_RETRY_LIMIT; | ||
| 2179 | vwrq->value = priv->short_retry; | ||
| 2180 | if (priv->long_retry != priv->short_retry) | ||
| 2181 | vwrq->flags |= IW_RETRY_SHORT; | ||
| 2182 | } | ||
| 2183 | |||
| 2184 | return 0; | ||
| 2185 | } | ||
| 2186 | |||
| 2187 | static int atmel_set_rts(struct net_device *dev, | ||
| 2188 | struct iw_request_info *info, | ||
| 2189 | union iwreq_data *wrqu, | ||
| 2190 | char *extra) | ||
| 2191 | { | ||
| 2192 | struct iw_param *vwrq = &wrqu->rts; | ||
| 2193 | struct atmel_private *priv = netdev_priv(dev); | ||
| 2194 | int rthr = vwrq->value; | ||
| 2195 | |||
| 2196 | if (vwrq->disabled) | ||
| 2197 | rthr = 2347; | ||
| 2198 | if ((rthr < 0) || (rthr > 2347)) { | ||
| 2199 | return -EINVAL; | ||
| 2200 | } | ||
| 2201 | priv->rts_threshold = rthr; | ||
| 2202 | |||
| 2203 | return -EINPROGRESS; /* Call commit handler */ | ||
| 2204 | } | ||
| 2205 | |||
| 2206 | static int atmel_get_rts(struct net_device *dev, | ||
| 2207 | struct iw_request_info *info, | ||
| 2208 | union iwreq_data *wrqu, | ||
| 2209 | char *extra) | ||
| 2210 | { | ||
| 2211 | struct iw_param *vwrq = &wrqu->rts; | ||
| 2212 | struct atmel_private *priv = netdev_priv(dev); | ||
| 2213 | |||
| 2214 | vwrq->value = priv->rts_threshold; | ||
| 2215 | vwrq->disabled = (vwrq->value >= 2347); | ||
| 2216 | vwrq->fixed = 1; | ||
| 2217 | |||
| 2218 | return 0; | ||
| 2219 | } | ||
| 2220 | |||
| 2221 | static int atmel_set_frag(struct net_device *dev, | ||
| 2222 | struct iw_request_info *info, | ||
| 2223 | union iwreq_data *wrqu, | ||
| 2224 | char *extra) | ||
| 2225 | { | ||
| 2226 | struct iw_param *vwrq = &wrqu->frag; | ||
| 2227 | struct atmel_private *priv = netdev_priv(dev); | ||
| 2228 | int fthr = vwrq->value; | ||
| 2229 | |||
| 2230 | if (vwrq->disabled) | ||
| 2231 | fthr = 2346; | ||
| 2232 | if ((fthr < 256) || (fthr > 2346)) { | ||
| 2233 | return -EINVAL; | ||
| 2234 | } | ||
| 2235 | fthr &= ~0x1; /* Get an even value - is it really needed ??? */ | ||
| 2236 | priv->frag_threshold = fthr; | ||
| 2237 | |||
| 2238 | return -EINPROGRESS; /* Call commit handler */ | ||
| 2239 | } | ||
| 2240 | |||
| 2241 | static int atmel_get_frag(struct net_device *dev, | ||
| 2242 | struct iw_request_info *info, | ||
| 2243 | union iwreq_data *wrqu, | ||
| 2244 | char *extra) | ||
| 2245 | { | ||
| 2246 | struct iw_param *vwrq = &wrqu->frag; | ||
| 2247 | struct atmel_private *priv = netdev_priv(dev); | ||
| 2248 | |||
| 2249 | vwrq->value = priv->frag_threshold; | ||
| 2250 | vwrq->disabled = (vwrq->value >= 2346); | ||
| 2251 | vwrq->fixed = 1; | ||
| 2252 | |||
| 2253 | return 0; | ||
| 2254 | } | ||
| 2255 | |||
| 2256 | static int atmel_set_freq(struct net_device *dev, | ||
| 2257 | struct iw_request_info *info, | ||
| 2258 | union iwreq_data *wrqu, | ||
| 2259 | char *extra) | ||
| 2260 | { | ||
| 2261 | struct iw_freq *fwrq = &wrqu->freq; | ||
| 2262 | struct atmel_private *priv = netdev_priv(dev); | ||
| 2263 | int rc = -EINPROGRESS; /* Call commit handler */ | ||
| 2264 | |||
| 2265 | /* If setting by frequency, convert to a channel */ | ||
| 2266 | if (fwrq->e == 1) { | ||
| 2267 | int f = fwrq->m / 100000; | ||
| 2268 | |||
| 2269 | /* Hack to fall through... */ | ||
| 2270 | fwrq->e = 0; | ||
| 2271 | fwrq->m = ieee80211_frequency_to_channel(f); | ||
| 2272 | } | ||
| 2273 | /* Setting by channel number */ | ||
| 2274 | if (fwrq->m < 0 || fwrq->m > 1000 || fwrq->e > 0) | ||
| 2275 | rc = -EOPNOTSUPP; | ||
| 2276 | else { | ||
| 2277 | int channel = fwrq->m; | ||
| 2278 | if (atmel_validate_channel(priv, channel) == 0) { | ||
| 2279 | priv->channel = channel; | ||
| 2280 | } else { | ||
| 2281 | rc = -EINVAL; | ||
| 2282 | } | ||
| 2283 | } | ||
| 2284 | return rc; | ||
| 2285 | } | ||
| 2286 | |||
| 2287 | static int atmel_get_freq(struct net_device *dev, | ||
| 2288 | struct iw_request_info *info, | ||
| 2289 | union iwreq_data *wrqu, | ||
| 2290 | char *extra) | ||
| 2291 | { | ||
| 2292 | struct iw_freq *fwrq = &wrqu->freq; | ||
| 2293 | struct atmel_private *priv = netdev_priv(dev); | ||
| 2294 | |||
| 2295 | fwrq->m = priv->channel; | ||
| 2296 | fwrq->e = 0; | ||
| 2297 | return 0; | ||
| 2298 | } | ||
| 2299 | |||
| 2300 | static int atmel_set_scan(struct net_device *dev, | ||
| 2301 | struct iw_request_info *info, | ||
| 2302 | union iwreq_data *dwrq, | ||
| 2303 | char *extra) | ||
| 2304 | { | ||
| 2305 | struct atmel_private *priv = netdev_priv(dev); | ||
| 2306 | unsigned long flags; | ||
| 2307 | |||
| 2308 | /* Note : you may have realised that, as this is a SET operation, | ||
| 2309 | * this is privileged and therefore a normal user can't | ||
| 2310 | * perform scanning. | ||
| 2311 | * This is not an error, while the device perform scanning, | ||
| 2312 | * traffic doesn't flow, so it's a perfect DoS... | ||
| 2313 | * Jean II */ | ||
| 2314 | |||
| 2315 | if (priv->station_state == STATION_STATE_DOWN) | ||
| 2316 | return -EAGAIN; | ||
| 2317 | |||
| 2318 | /* Timeout old surveys. */ | ||
| 2319 | if (time_after(jiffies, priv->last_survey + 20 * HZ)) | ||
| 2320 | priv->site_survey_state = SITE_SURVEY_IDLE; | ||
| 2321 | priv->last_survey = jiffies; | ||
| 2322 | |||
| 2323 | /* Initiate a scan command */ | ||
| 2324 | if (priv->site_survey_state == SITE_SURVEY_IN_PROGRESS) | ||
| 2325 | return -EBUSY; | ||
| 2326 | |||
| 2327 | del_timer_sync(&priv->management_timer); | ||
| 2328 | spin_lock_irqsave(&priv->irqlock, flags); | ||
| 2329 | |||
| 2330 | priv->site_survey_state = SITE_SURVEY_IN_PROGRESS; | ||
| 2331 | priv->fast_scan = 0; | ||
| 2332 | atmel_scan(priv, 0); | ||
| 2333 | spin_unlock_irqrestore(&priv->irqlock, flags); | ||
| 2334 | |||
| 2335 | return 0; | ||
| 2336 | } | ||
| 2337 | |||
| 2338 | static int atmel_get_scan(struct net_device *dev, | ||
| 2339 | struct iw_request_info *info, | ||
| 2340 | union iwreq_data *wrqu, | ||
| 2341 | char *extra) | ||
| 2342 | { | ||
| 2343 | struct iw_point *dwrq = &wrqu->data; | ||
| 2344 | struct atmel_private *priv = netdev_priv(dev); | ||
| 2345 | int i; | ||
| 2346 | char *current_ev = extra; | ||
| 2347 | struct iw_event iwe; | ||
| 2348 | |||
| 2349 | if (priv->site_survey_state != SITE_SURVEY_COMPLETED) | ||
| 2350 | return -EAGAIN; | ||
| 2351 | |||
| 2352 | for (i = 0; i < priv->BSS_list_entries; i++) { | ||
| 2353 | iwe.cmd = SIOCGIWAP; | ||
| 2354 | iwe.u.ap_addr.sa_family = ARPHRD_ETHER; | ||
| 2355 | memcpy(iwe.u.ap_addr.sa_data, priv->BSSinfo[i].BSSID, ETH_ALEN); | ||
| 2356 | current_ev = iwe_stream_add_event(info, current_ev, | ||
| 2357 | extra + IW_SCAN_MAX_DATA, | ||
| 2358 | &iwe, IW_EV_ADDR_LEN); | ||
| 2359 | |||
| 2360 | iwe.u.data.length = priv->BSSinfo[i].SSIDsize; | ||
| 2361 | if (iwe.u.data.length > 32) | ||
| 2362 | iwe.u.data.length = 32; | ||
| 2363 | iwe.cmd = SIOCGIWESSID; | ||
| 2364 | iwe.u.data.flags = 1; | ||
| 2365 | current_ev = iwe_stream_add_point(info, current_ev, | ||
| 2366 | extra + IW_SCAN_MAX_DATA, | ||
| 2367 | &iwe, priv->BSSinfo[i].SSID); | ||
| 2368 | |||
| 2369 | iwe.cmd = SIOCGIWMODE; | ||
| 2370 | iwe.u.mode = priv->BSSinfo[i].BSStype; | ||
| 2371 | current_ev = iwe_stream_add_event(info, current_ev, | ||
| 2372 | extra + IW_SCAN_MAX_DATA, | ||
| 2373 | &iwe, IW_EV_UINT_LEN); | ||
| 2374 | |||
| 2375 | iwe.cmd = SIOCGIWFREQ; | ||
| 2376 | iwe.u.freq.m = priv->BSSinfo[i].channel; | ||
| 2377 | iwe.u.freq.e = 0; | ||
| 2378 | current_ev = iwe_stream_add_event(info, current_ev, | ||
| 2379 | extra + IW_SCAN_MAX_DATA, | ||
| 2380 | &iwe, IW_EV_FREQ_LEN); | ||
| 2381 | |||
| 2382 | /* Add quality statistics */ | ||
| 2383 | iwe.cmd = IWEVQUAL; | ||
| 2384 | iwe.u.qual.level = priv->BSSinfo[i].RSSI; | ||
| 2385 | iwe.u.qual.qual = iwe.u.qual.level; | ||
| 2386 | /* iwe.u.qual.noise = SOMETHING */ | ||
| 2387 | current_ev = iwe_stream_add_event(info, current_ev, | ||
| 2388 | extra + IW_SCAN_MAX_DATA, | ||
| 2389 | &iwe, IW_EV_QUAL_LEN); | ||
| 2390 | |||
| 2391 | |||
| 2392 | iwe.cmd = SIOCGIWENCODE; | ||
| 2393 | if (priv->BSSinfo[i].UsingWEP) | ||
| 2394 | iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; | ||
| 2395 | else | ||
| 2396 | iwe.u.data.flags = IW_ENCODE_DISABLED; | ||
| 2397 | iwe.u.data.length = 0; | ||
| 2398 | current_ev = iwe_stream_add_point(info, current_ev, | ||
| 2399 | extra + IW_SCAN_MAX_DATA, | ||
| 2400 | &iwe, NULL); | ||
| 2401 | } | ||
| 2402 | |||
| 2403 | /* Length of data */ | ||
| 2404 | dwrq->length = (current_ev - extra); | ||
| 2405 | dwrq->flags = 0; | ||
| 2406 | |||
| 2407 | return 0; | ||
| 2408 | } | ||
| 2409 | |||
| 2410 | static int atmel_get_range(struct net_device *dev, | ||
| 2411 | struct iw_request_info *info, | ||
| 2412 | union iwreq_data *wrqu, | ||
| 2413 | char *extra) | ||
| 2414 | { | ||
| 2415 | struct iw_point *dwrq = &wrqu->data; | ||
| 2416 | struct atmel_private *priv = netdev_priv(dev); | ||
| 2417 | struct iw_range *range = (struct iw_range *) extra; | ||
| 2418 | int k, i, j; | ||
| 2419 | |||
| 2420 | dwrq->length = sizeof(struct iw_range); | ||
| 2421 | memset(range, 0, sizeof(struct iw_range)); | ||
| 2422 | range->min_nwid = 0x0000; | ||
| 2423 | range->max_nwid = 0x0000; | ||
| 2424 | range->num_channels = 0; | ||
| 2425 | for (j = 0; j < ARRAY_SIZE(channel_table); j++) | ||
| 2426 | if (priv->reg_domain == channel_table[j].reg_domain) { | ||
| 2427 | range->num_channels = channel_table[j].max - channel_table[j].min + 1; | ||
| 2428 | break; | ||
| 2429 | } | ||
| 2430 | if (range->num_channels != 0) { | ||
| 2431 | for (k = 0, i = channel_table[j].min; i <= channel_table[j].max; i++) { | ||
| 2432 | range->freq[k].i = i; /* List index */ | ||
| 2433 | |||
| 2434 | /* Values in MHz -> * 10^5 * 10 */ | ||
| 2435 | range->freq[k].m = 100000 * | ||
| 2436 | ieee80211_channel_to_frequency(i, NL80211_BAND_2GHZ); | ||
| 2437 | range->freq[k++].e = 1; | ||
| 2438 | } | ||
| 2439 | range->num_frequency = k; | ||
| 2440 | } | ||
| 2441 | |||
| 2442 | range->max_qual.qual = 100; | ||
| 2443 | range->max_qual.level = 100; | ||
| 2444 | range->max_qual.noise = 0; | ||
| 2445 | range->max_qual.updated = IW_QUAL_NOISE_INVALID; | ||
| 2446 | |||
| 2447 | range->avg_qual.qual = 50; | ||
| 2448 | range->avg_qual.level = 50; | ||
| 2449 | range->avg_qual.noise = 0; | ||
| 2450 | range->avg_qual.updated = IW_QUAL_NOISE_INVALID; | ||
| 2451 | |||
| 2452 | range->sensitivity = 0; | ||
| 2453 | |||
| 2454 | range->bitrate[0] = 1000000; | ||
| 2455 | range->bitrate[1] = 2000000; | ||
| 2456 | range->bitrate[2] = 5500000; | ||
| 2457 | range->bitrate[3] = 11000000; | ||
| 2458 | range->num_bitrates = 4; | ||
| 2459 | |||
| 2460 | range->min_rts = 0; | ||
| 2461 | range->max_rts = 2347; | ||
| 2462 | range->min_frag = 256; | ||
| 2463 | range->max_frag = 2346; | ||
| 2464 | |||
| 2465 | range->encoding_size[0] = 5; | ||
| 2466 | range->encoding_size[1] = 13; | ||
| 2467 | range->num_encoding_sizes = 2; | ||
| 2468 | range->max_encoding_tokens = 4; | ||
| 2469 | |||
| 2470 | range->pmp_flags = IW_POWER_ON; | ||
| 2471 | range->pmt_flags = IW_POWER_ON; | ||
| 2472 | range->pm_capa = 0; | ||
| 2473 | |||
| 2474 | range->we_version_source = WIRELESS_EXT; | ||
| 2475 | range->we_version_compiled = WIRELESS_EXT; | ||
| 2476 | range->retry_capa = IW_RETRY_LIMIT ; | ||
| 2477 | range->retry_flags = IW_RETRY_LIMIT; | ||
| 2478 | range->r_time_flags = 0; | ||
| 2479 | range->min_retry = 1; | ||
| 2480 | range->max_retry = 65535; | ||
| 2481 | |||
| 2482 | return 0; | ||
| 2483 | } | ||
| 2484 | |||
| 2485 | static int atmel_set_wap(struct net_device *dev, | ||
| 2486 | struct iw_request_info *info, | ||
| 2487 | union iwreq_data *wrqu, | ||
| 2488 | char *extra) | ||
| 2489 | { | ||
| 2490 | struct sockaddr *awrq = &wrqu->ap_addr; | ||
| 2491 | struct atmel_private *priv = netdev_priv(dev); | ||
| 2492 | int i; | ||
| 2493 | static const u8 any[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; | ||
| 2494 | static const u8 off[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; | ||
| 2495 | unsigned long flags; | ||
| 2496 | |||
| 2497 | if (awrq->sa_family != ARPHRD_ETHER) | ||
| 2498 | return -EINVAL; | ||
| 2499 | |||
| 2500 | if (!memcmp(any, awrq->sa_data, 6) || | ||
| 2501 | !memcmp(off, awrq->sa_data, 6)) { | ||
| 2502 | del_timer_sync(&priv->management_timer); | ||
| 2503 | spin_lock_irqsave(&priv->irqlock, flags); | ||
| 2504 | atmel_scan(priv, 1); | ||
| 2505 | spin_unlock_irqrestore(&priv->irqlock, flags); | ||
| 2506 | return 0; | ||
| 2507 | } | ||
| 2508 | |||
| 2509 | for (i = 0; i < priv->BSS_list_entries; i++) { | ||
| 2510 | if (memcmp(priv->BSSinfo[i].BSSID, awrq->sa_data, 6) == 0) { | ||
| 2511 | if (!priv->wep_is_on && priv->BSSinfo[i].UsingWEP) { | ||
| 2512 | return -EINVAL; | ||
| 2513 | } else if (priv->wep_is_on && !priv->BSSinfo[i].UsingWEP) { | ||
| 2514 | return -EINVAL; | ||
| 2515 | } else { | ||
| 2516 | del_timer_sync(&priv->management_timer); | ||
| 2517 | spin_lock_irqsave(&priv->irqlock, flags); | ||
| 2518 | atmel_join_bss(priv, i); | ||
| 2519 | spin_unlock_irqrestore(&priv->irqlock, flags); | ||
| 2520 | return 0; | ||
| 2521 | } | ||
| 2522 | } | ||
| 2523 | } | ||
| 2524 | |||
| 2525 | return -EINVAL; | ||
| 2526 | } | ||
| 2527 | |||
| 2528 | static int atmel_config_commit(struct net_device *dev, | ||
| 2529 | struct iw_request_info *info, /* NULL */ | ||
| 2530 | union iwreq_data *zwrq, /* NULL */ | ||
| 2531 | char *extra) /* NULL */ | ||
| 2532 | { | ||
| 2533 | return atmel_open(dev); | ||
| 2534 | } | ||
| 2535 | |||
| 2536 | static const iw_handler atmel_handler[] = | ||
| 2537 | { | ||
| 2538 | IW_HANDLER(SIOCSIWCOMMIT, atmel_config_commit), | ||
| 2539 | IW_HANDLER(SIOCGIWNAME, atmel_get_name), | ||
| 2540 | IW_HANDLER(SIOCSIWFREQ, atmel_set_freq), | ||
| 2541 | IW_HANDLER(SIOCGIWFREQ, atmel_get_freq), | ||
| 2542 | IW_HANDLER(SIOCSIWMODE, atmel_set_mode), | ||
| 2543 | IW_HANDLER(SIOCGIWMODE, atmel_get_mode), | ||
| 2544 | IW_HANDLER(SIOCGIWRANGE, atmel_get_range), | ||
| 2545 | IW_HANDLER(SIOCSIWAP, atmel_set_wap), | ||
| 2546 | IW_HANDLER(SIOCGIWAP, atmel_get_wap), | ||
| 2547 | IW_HANDLER(SIOCSIWSCAN, atmel_set_scan), | ||
| 2548 | IW_HANDLER(SIOCGIWSCAN, atmel_get_scan), | ||
| 2549 | IW_HANDLER(SIOCSIWESSID, atmel_set_essid), | ||
| 2550 | IW_HANDLER(SIOCGIWESSID, atmel_get_essid), | ||
| 2551 | IW_HANDLER(SIOCSIWRATE, atmel_set_rate), | ||
| 2552 | IW_HANDLER(SIOCGIWRATE, atmel_get_rate), | ||
| 2553 | IW_HANDLER(SIOCSIWRTS, atmel_set_rts), | ||
| 2554 | IW_HANDLER(SIOCGIWRTS, atmel_get_rts), | ||
| 2555 | IW_HANDLER(SIOCSIWFRAG, atmel_set_frag), | ||
| 2556 | IW_HANDLER(SIOCGIWFRAG, atmel_get_frag), | ||
| 2557 | IW_HANDLER(SIOCSIWRETRY, atmel_set_retry), | ||
| 2558 | IW_HANDLER(SIOCGIWRETRY, atmel_get_retry), | ||
| 2559 | IW_HANDLER(SIOCSIWENCODE, atmel_set_encode), | ||
| 2560 | IW_HANDLER(SIOCGIWENCODE, atmel_get_encode), | ||
| 2561 | IW_HANDLER(SIOCSIWPOWER, atmel_set_power), | ||
| 2562 | IW_HANDLER(SIOCGIWPOWER, atmel_get_power), | ||
| 2563 | IW_HANDLER(SIOCSIWAUTH, atmel_set_auth), | ||
| 2564 | IW_HANDLER(SIOCGIWAUTH, atmel_get_auth), | ||
| 2565 | IW_HANDLER(SIOCSIWENCODEEXT, atmel_set_encodeext), | ||
| 2566 | IW_HANDLER(SIOCGIWENCODEEXT, atmel_get_encodeext), | ||
| 2567 | }; | ||
| 2568 | |||
| 2569 | static const iw_handler atmel_private_handler[] = | ||
| 2570 | { | ||
| 2571 | NULL, /* SIOCIWFIRSTPRIV */ | ||
| 2572 | }; | ||
| 2573 | |||
| 2574 | struct atmel_priv_ioctl { | ||
| 2575 | char id[32]; | ||
| 2576 | unsigned char __user *data; | ||
| 2577 | unsigned short len; | ||
| 2578 | }; | ||
| 2579 | |||
| 2580 | #define ATMELFWL SIOCIWFIRSTPRIV | ||
| 2581 | #define ATMELIDIFC ATMELFWL + 1 | ||
| 2582 | #define ATMELRD ATMELFWL + 2 | ||
| 2583 | #define ATMELMAGIC 0x51807 | ||
| 2584 | #define REGDOMAINSZ 20 | ||
| 2585 | |||
| 2586 | static const struct iw_priv_args atmel_private_args[] = { | ||
| 2587 | { | ||
| 2588 | .cmd = ATMELFWL, | ||
| 2589 | .set_args = IW_PRIV_TYPE_BYTE | ||
| 2590 | | IW_PRIV_SIZE_FIXED | ||
| 2591 | | sizeof(struct atmel_priv_ioctl), | ||
| 2592 | .get_args = IW_PRIV_TYPE_NONE, | ||
| 2593 | .name = "atmelfwl" | ||
| 2594 | }, { | ||
| 2595 | .cmd = ATMELIDIFC, | ||
| 2596 | .set_args = IW_PRIV_TYPE_NONE, | ||
| 2597 | .get_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, | ||
| 2598 | .name = "atmelidifc" | ||
| 2599 | }, { | ||
| 2600 | .cmd = ATMELRD, | ||
| 2601 | .set_args = IW_PRIV_TYPE_CHAR | REGDOMAINSZ, | ||
| 2602 | .get_args = IW_PRIV_TYPE_NONE, | ||
| 2603 | .name = "regdomain" | ||
| 2604 | }, | ||
| 2605 | }; | ||
| 2606 | |||
| 2607 | static const struct iw_handler_def atmel_handler_def = { | ||
| 2608 | .num_standard = ARRAY_SIZE(atmel_handler), | ||
| 2609 | .num_private = ARRAY_SIZE(atmel_private_handler), | ||
| 2610 | .num_private_args = ARRAY_SIZE(atmel_private_args), | ||
| 2611 | .standard = atmel_handler, | ||
| 2612 | .private = atmel_private_handler, | ||
| 2613 | .private_args = (struct iw_priv_args *) atmel_private_args, | ||
| 2614 | .get_wireless_stats = atmel_get_wireless_stats | ||
| 2615 | }; | ||
| 2616 | |||
| 2617 | struct auth_body { | ||
| 2618 | __le16 alg; | ||
| 2619 | __le16 trans_seq; | ||
| 2620 | __le16 status; | ||
| 2621 | u8 el_id; | ||
| 2622 | u8 chall_text_len; | ||
| 2623 | u8 chall_text[253]; | ||
| 2624 | }; | ||
| 2625 | |||
| 2626 | static void atmel_enter_state(struct atmel_private *priv, int new_state) | ||
| 2627 | { | ||
| 2628 | int old_state = priv->station_state; | ||
| 2629 | |||
| 2630 | if (new_state == old_state) | ||
| 2631 | return; | ||
| 2632 | |||
| 2633 | priv->station_state = new_state; | ||
| 2634 | |||
| 2635 | if (new_state == STATION_STATE_READY) { | ||
| 2636 | netif_start_queue(priv->dev); | ||
| 2637 | netif_carrier_on(priv->dev); | ||
| 2638 | } | ||
| 2639 | |||
| 2640 | if (old_state == STATION_STATE_READY) { | ||
| 2641 | netif_carrier_off(priv->dev); | ||
| 2642 | if (netif_running(priv->dev)) | ||
| 2643 | netif_stop_queue(priv->dev); | ||
| 2644 | priv->last_beacon_timestamp = 0; | ||
| 2645 | } | ||
| 2646 | } | ||
| 2647 | |||
| 2648 | static void atmel_scan(struct atmel_private *priv, int specific_ssid) | ||
| 2649 | { | ||
| 2650 | struct { | ||
| 2651 | u8 BSSID[ETH_ALEN]; | ||
| 2652 | u8 SSID[MAX_SSID_LENGTH]; | ||
| 2653 | u8 scan_type; | ||
| 2654 | u8 channel; | ||
| 2655 | __le16 BSS_type; | ||
| 2656 | __le16 min_channel_time; | ||
| 2657 | __le16 max_channel_time; | ||
| 2658 | u8 options; | ||
| 2659 | u8 SSID_size; | ||
| 2660 | } cmd; | ||
| 2661 | |||
| 2662 | eth_broadcast_addr(cmd.BSSID); | ||
| 2663 | |||
| 2664 | if (priv->fast_scan) { | ||
| 2665 | cmd.SSID_size = priv->SSID_size; | ||
| 2666 | memcpy(cmd.SSID, priv->SSID, priv->SSID_size); | ||
| 2667 | cmd.min_channel_time = cpu_to_le16(10); | ||
| 2668 | cmd.max_channel_time = cpu_to_le16(50); | ||
| 2669 | } else { | ||
| 2670 | priv->BSS_list_entries = 0; | ||
| 2671 | cmd.SSID_size = 0; | ||
| 2672 | cmd.min_channel_time = cpu_to_le16(10); | ||
| 2673 | cmd.max_channel_time = cpu_to_le16(120); | ||
| 2674 | } | ||
| 2675 | |||
| 2676 | cmd.options = 0; | ||
| 2677 | |||
| 2678 | if (!specific_ssid) | ||
| 2679 | cmd.options |= SCAN_OPTIONS_SITE_SURVEY; | ||
| 2680 | |||
| 2681 | cmd.channel = (priv->channel & 0x7f); | ||
| 2682 | cmd.scan_type = SCAN_TYPE_ACTIVE; | ||
| 2683 | cmd.BSS_type = cpu_to_le16(priv->operating_mode == IW_MODE_ADHOC ? | ||
| 2684 | BSS_TYPE_AD_HOC : BSS_TYPE_INFRASTRUCTURE); | ||
| 2685 | |||
| 2686 | atmel_send_command(priv, CMD_Scan, &cmd, sizeof(cmd)); | ||
| 2687 | |||
| 2688 | /* This must come after all hardware access to avoid being messed up | ||
| 2689 | by stuff happening in interrupt context after we leave STATE_DOWN */ | ||
| 2690 | atmel_enter_state(priv, STATION_STATE_SCANNING); | ||
| 2691 | } | ||
| 2692 | |||
| 2693 | static void join(struct atmel_private *priv, int type) | ||
| 2694 | { | ||
| 2695 | struct { | ||
| 2696 | u8 BSSID[6]; | ||
| 2697 | u8 SSID[MAX_SSID_LENGTH]; | ||
| 2698 | u8 BSS_type; /* this is a short in a scan command - weird */ | ||
| 2699 | u8 channel; | ||
| 2700 | __le16 timeout; | ||
| 2701 | u8 SSID_size; | ||
| 2702 | u8 reserved; | ||
| 2703 | } cmd; | ||
| 2704 | |||
| 2705 | cmd.SSID_size = priv->SSID_size; | ||
| 2706 | memcpy(cmd.SSID, priv->SSID, priv->SSID_size); | ||
| 2707 | memcpy(cmd.BSSID, priv->CurrentBSSID, ETH_ALEN); | ||
| 2708 | cmd.channel = (priv->channel & 0x7f); | ||
| 2709 | cmd.BSS_type = type; | ||
| 2710 | cmd.timeout = cpu_to_le16(2000); | ||
| 2711 | |||
| 2712 | atmel_send_command(priv, CMD_Join, &cmd, sizeof(cmd)); | ||
| 2713 | } | ||
| 2714 | |||
| 2715 | static void start(struct atmel_private *priv, int type) | ||
| 2716 | { | ||
| 2717 | struct { | ||
| 2718 | u8 BSSID[6]; | ||
| 2719 | u8 SSID[MAX_SSID_LENGTH]; | ||
| 2720 | u8 BSS_type; | ||
| 2721 | u8 channel; | ||
| 2722 | u8 SSID_size; | ||
| 2723 | u8 reserved[3]; | ||
| 2724 | } cmd; | ||
| 2725 | |||
| 2726 | cmd.SSID_size = priv->SSID_size; | ||
| 2727 | memcpy(cmd.SSID, priv->SSID, priv->SSID_size); | ||
| 2728 | memcpy(cmd.BSSID, priv->BSSID, ETH_ALEN); | ||
| 2729 | cmd.BSS_type = type; | ||
| 2730 | cmd.channel = (priv->channel & 0x7f); | ||
| 2731 | |||
| 2732 | atmel_send_command(priv, CMD_Start, &cmd, sizeof(cmd)); | ||
| 2733 | } | ||
| 2734 | |||
| 2735 | static void handle_beacon_probe(struct atmel_private *priv, u16 capability, | ||
| 2736 | u8 channel) | ||
| 2737 | { | ||
| 2738 | int rejoin = 0; | ||
| 2739 | int new = capability & WLAN_CAPABILITY_SHORT_PREAMBLE ? | ||
| 2740 | SHORT_PREAMBLE : LONG_PREAMBLE; | ||
| 2741 | |||
| 2742 | if (priv->preamble != new) { | ||
| 2743 | priv->preamble = new; | ||
| 2744 | rejoin = 1; | ||
| 2745 | atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_PREAMBLE_TYPE, new); | ||
| 2746 | } | ||
| 2747 | |||
| 2748 | if (priv->channel != channel) { | ||
| 2749 | priv->channel = channel; | ||
| 2750 | rejoin = 1; | ||
| 2751 | atmel_set_mib8(priv, Phy_Mib_Type, PHY_MIB_CHANNEL_POS, channel); | ||
| 2752 | } | ||
| 2753 | |||
| 2754 | if (rejoin) { | ||
| 2755 | priv->station_is_associated = 0; | ||
| 2756 | atmel_enter_state(priv, STATION_STATE_JOINNING); | ||
| 2757 | |||
| 2758 | if (priv->operating_mode == IW_MODE_INFRA) | ||
| 2759 | join(priv, BSS_TYPE_INFRASTRUCTURE); | ||
| 2760 | else | ||
| 2761 | join(priv, BSS_TYPE_AD_HOC); | ||
| 2762 | } | ||
| 2763 | } | ||
| 2764 | |||
| 2765 | static void send_authentication_request(struct atmel_private *priv, u16 system, | ||
| 2766 | u8 *challenge, int challenge_len) | ||
| 2767 | { | ||
| 2768 | struct ieee80211_hdr header; | ||
| 2769 | struct auth_body auth; | ||
| 2770 | |||
| 2771 | header.frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH); | ||
| 2772 | header.duration_id = cpu_to_le16(0x8000); | ||
| 2773 | header.seq_ctrl = 0; | ||
| 2774 | memcpy(header.addr1, priv->CurrentBSSID, ETH_ALEN); | ||
| 2775 | memcpy(header.addr2, priv->dev->dev_addr, ETH_ALEN); | ||
| 2776 | memcpy(header.addr3, priv->CurrentBSSID, ETH_ALEN); | ||
| 2777 | |||
| 2778 | if (priv->wep_is_on && priv->CurrentAuthentTransactionSeqNum != 1) | ||
| 2779 | /* no WEP for authentication frames with TrSeqNo 1 */ | ||
| 2780 | header.frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); | ||
| 2781 | |||
| 2782 | auth.alg = cpu_to_le16(system); | ||
| 2783 | |||
| 2784 | auth.status = 0; | ||
| 2785 | auth.trans_seq = cpu_to_le16(priv->CurrentAuthentTransactionSeqNum); | ||
| 2786 | priv->ExpectedAuthentTransactionSeqNum = priv->CurrentAuthentTransactionSeqNum+1; | ||
| 2787 | priv->CurrentAuthentTransactionSeqNum += 2; | ||
| 2788 | |||
| 2789 | if (challenge_len != 0) { | ||
| 2790 | auth.el_id = 16; /* challenge_text */ | ||
| 2791 | auth.chall_text_len = challenge_len; | ||
| 2792 | memcpy(auth.chall_text, challenge, challenge_len); | ||
| 2793 | atmel_transmit_management_frame(priv, &header, (u8 *)&auth, 8 + challenge_len); | ||
| 2794 | } else { | ||
| 2795 | atmel_transmit_management_frame(priv, &header, (u8 *)&auth, 6); | ||
| 2796 | } | ||
| 2797 | } | ||
| 2798 | |||
| 2799 | static void send_association_request(struct atmel_private *priv, int is_reassoc) | ||
| 2800 | { | ||
| 2801 | u8 *ssid_el_p; | ||
| 2802 | int bodysize; | ||
| 2803 | struct ieee80211_hdr header; | ||
| 2804 | struct ass_req_format { | ||
| 2805 | __le16 capability; | ||
| 2806 | __le16 listen_interval; | ||
| 2807 | u8 ap[ETH_ALEN]; /* nothing after here directly accessible */ | ||
| 2808 | u8 ssid_el_id; | ||
| 2809 | u8 ssid_len; | ||
| 2810 | u8 ssid[MAX_SSID_LENGTH]; | ||
| 2811 | u8 sup_rates_el_id; | ||
| 2812 | u8 sup_rates_len; | ||
| 2813 | u8 rates[4]; | ||
| 2814 | } body; | ||
| 2815 | |||
| 2816 | header.frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | | ||
| 2817 | (is_reassoc ? IEEE80211_STYPE_REASSOC_REQ : IEEE80211_STYPE_ASSOC_REQ)); | ||
| 2818 | header.duration_id = cpu_to_le16(0x8000); | ||
| 2819 | header.seq_ctrl = 0; | ||
| 2820 | |||
| 2821 | memcpy(header.addr1, priv->CurrentBSSID, ETH_ALEN); | ||
| 2822 | memcpy(header.addr2, priv->dev->dev_addr, ETH_ALEN); | ||
| 2823 | memcpy(header.addr3, priv->CurrentBSSID, ETH_ALEN); | ||
| 2824 | |||
| 2825 | body.capability = cpu_to_le16(WLAN_CAPABILITY_ESS); | ||
| 2826 | if (priv->wep_is_on) | ||
| 2827 | body.capability |= cpu_to_le16(WLAN_CAPABILITY_PRIVACY); | ||
| 2828 | if (priv->preamble == SHORT_PREAMBLE) | ||
| 2829 | body.capability |= cpu_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE); | ||
| 2830 | |||
| 2831 | body.listen_interval = cpu_to_le16(priv->listen_interval * priv->beacon_period); | ||
| 2832 | |||
| 2833 | /* current AP address - only in reassoc frame */ | ||
| 2834 | if (is_reassoc) { | ||
| 2835 | memcpy(body.ap, priv->CurrentBSSID, ETH_ALEN); | ||
| 2836 | ssid_el_p = &body.ssid_el_id; | ||
| 2837 | bodysize = 18 + priv->SSID_size; | ||
| 2838 | } else { | ||
| 2839 | ssid_el_p = &body.ap[0]; | ||
| 2840 | bodysize = 12 + priv->SSID_size; | ||
| 2841 | } | ||
| 2842 | |||
| 2843 | ssid_el_p[0] = WLAN_EID_SSID; | ||
| 2844 | ssid_el_p[1] = priv->SSID_size; | ||
| 2845 | memcpy(ssid_el_p + 2, priv->SSID, priv->SSID_size); | ||
| 2846 | ssid_el_p[2 + priv->SSID_size] = WLAN_EID_SUPP_RATES; | ||
| 2847 | ssid_el_p[3 + priv->SSID_size] = 4; /* len of supported rates */ | ||
| 2848 | memcpy(ssid_el_p + 4 + priv->SSID_size, atmel_basic_rates, 4); | ||
| 2849 | |||
| 2850 | atmel_transmit_management_frame(priv, &header, (void *)&body, bodysize); | ||
| 2851 | } | ||
| 2852 | |||
| 2853 | static int is_frame_from_current_bss(struct atmel_private *priv, | ||
| 2854 | struct ieee80211_hdr *header) | ||
| 2855 | { | ||
| 2856 | if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS) | ||
| 2857 | return memcmp(header->addr3, priv->CurrentBSSID, 6) == 0; | ||
| 2858 | else | ||
| 2859 | return memcmp(header->addr2, priv->CurrentBSSID, 6) == 0; | ||
| 2860 | } | ||
| 2861 | |||
| 2862 | static int retrieve_bss(struct atmel_private *priv) | ||
| 2863 | { | ||
| 2864 | int i; | ||
| 2865 | int max_rssi = -128; | ||
| 2866 | int max_index = -1; | ||
| 2867 | |||
| 2868 | if (priv->BSS_list_entries == 0) | ||
| 2869 | return -1; | ||
| 2870 | |||
| 2871 | if (priv->connect_to_any_BSS) { | ||
| 2872 | /* Select a BSS with the max-RSSI but of the same type and of | ||
| 2873 | the same WEP mode and that it is not marked as 'bad' (i.e. | ||
| 2874 | we had previously failed to connect to this BSS with the | ||
| 2875 | settings that we currently use) */ | ||
| 2876 | priv->current_BSS = 0; | ||
| 2877 | for (i = 0; i < priv->BSS_list_entries; i++) { | ||
| 2878 | if (priv->operating_mode == priv->BSSinfo[i].BSStype && | ||
| 2879 | ((!priv->wep_is_on && !priv->BSSinfo[i].UsingWEP) || | ||
| 2880 | (priv->wep_is_on && priv->BSSinfo[i].UsingWEP)) && | ||
| 2881 | !(priv->BSSinfo[i].channel & 0x80)) { | ||
| 2882 | max_rssi = priv->BSSinfo[i].RSSI; | ||
| 2883 | priv->current_BSS = max_index = i; | ||
| 2884 | } | ||
| 2885 | } | ||
| 2886 | return max_index; | ||
| 2887 | } | ||
| 2888 | |||
| 2889 | for (i = 0; i < priv->BSS_list_entries; i++) { | ||
| 2890 | if (priv->SSID_size == priv->BSSinfo[i].SSIDsize && | ||
| 2891 | memcmp(priv->SSID, priv->BSSinfo[i].SSID, priv->SSID_size) == 0 && | ||
| 2892 | priv->operating_mode == priv->BSSinfo[i].BSStype && | ||
| 2893 | atmel_validate_channel(priv, priv->BSSinfo[i].channel) == 0) { | ||
| 2894 | if (priv->BSSinfo[i].RSSI >= max_rssi) { | ||
| 2895 | max_rssi = priv->BSSinfo[i].RSSI; | ||
| 2896 | max_index = i; | ||
| 2897 | } | ||
| 2898 | } | ||
| 2899 | } | ||
| 2900 | return max_index; | ||
| 2901 | } | ||
| 2902 | |||
| 2903 | static void store_bss_info(struct atmel_private *priv, | ||
| 2904 | struct ieee80211_hdr *header, u16 capability, | ||
| 2905 | u16 beacon_period, u8 channel, u8 rssi, u8 ssid_len, | ||
| 2906 | u8 *ssid, int is_beacon) | ||
| 2907 | { | ||
| 2908 | u8 *bss = capability & WLAN_CAPABILITY_ESS ? header->addr2 : header->addr3; | ||
| 2909 | int i, index; | ||
| 2910 | |||
| 2911 | for (index = -1, i = 0; i < priv->BSS_list_entries; i++) | ||
| 2912 | if (memcmp(bss, priv->BSSinfo[i].BSSID, ETH_ALEN) == 0) | ||
| 2913 | index = i; | ||
| 2914 | |||
| 2915 | /* If we process a probe and an entry from this BSS exists | ||
| 2916 | we will update the BSS entry with the info from this BSS. | ||
| 2917 | If we process a beacon we will only update RSSI */ | ||
| 2918 | |||
| 2919 | if (index == -1) { | ||
| 2920 | if (priv->BSS_list_entries == MAX_BSS_ENTRIES) | ||
| 2921 | return; | ||
| 2922 | index = priv->BSS_list_entries++; | ||
| 2923 | memcpy(priv->BSSinfo[index].BSSID, bss, ETH_ALEN); | ||
| 2924 | priv->BSSinfo[index].RSSI = rssi; | ||
| 2925 | } else { | ||
| 2926 | if (rssi > priv->BSSinfo[index].RSSI) | ||
| 2927 | priv->BSSinfo[index].RSSI = rssi; | ||
| 2928 | if (is_beacon) | ||
| 2929 | return; | ||
| 2930 | } | ||
| 2931 | |||
| 2932 | priv->BSSinfo[index].channel = channel; | ||
| 2933 | priv->BSSinfo[index].beacon_period = beacon_period; | ||
| 2934 | priv->BSSinfo[index].UsingWEP = capability & WLAN_CAPABILITY_PRIVACY; | ||
| 2935 | memcpy(priv->BSSinfo[index].SSID, ssid, ssid_len); | ||
| 2936 | priv->BSSinfo[index].SSIDsize = ssid_len; | ||
| 2937 | |||
| 2938 | if (capability & WLAN_CAPABILITY_IBSS) | ||
| 2939 | priv->BSSinfo[index].BSStype = IW_MODE_ADHOC; | ||
| 2940 | else if (capability & WLAN_CAPABILITY_ESS) | ||
| 2941 | priv->BSSinfo[index].BSStype = IW_MODE_INFRA; | ||
| 2942 | |||
| 2943 | priv->BSSinfo[index].preamble = capability & WLAN_CAPABILITY_SHORT_PREAMBLE ? | ||
| 2944 | SHORT_PREAMBLE : LONG_PREAMBLE; | ||
| 2945 | } | ||
| 2946 | |||
| 2947 | static void authenticate(struct atmel_private *priv, u16 frame_len) | ||
| 2948 | { | ||
| 2949 | struct auth_body *auth = (struct auth_body *)priv->rx_buf; | ||
| 2950 | u16 status = le16_to_cpu(auth->status); | ||
| 2951 | u16 trans_seq_no = le16_to_cpu(auth->trans_seq); | ||
| 2952 | u16 system = le16_to_cpu(auth->alg); | ||
| 2953 | |||
| 2954 | if (status == WLAN_STATUS_SUCCESS && !priv->wep_is_on) { | ||
| 2955 | /* no WEP */ | ||
| 2956 | if (priv->station_was_associated) { | ||
| 2957 | atmel_enter_state(priv, STATION_STATE_REASSOCIATING); | ||
| 2958 | send_association_request(priv, 1); | ||
| 2959 | return; | ||
| 2960 | } else { | ||
| 2961 | atmel_enter_state(priv, STATION_STATE_ASSOCIATING); | ||
| 2962 | send_association_request(priv, 0); | ||
| 2963 | return; | ||
| 2964 | } | ||
| 2965 | } | ||
| 2966 | |||
| 2967 | if (status == WLAN_STATUS_SUCCESS && priv->wep_is_on) { | ||
| 2968 | int should_associate = 0; | ||
| 2969 | /* WEP */ | ||
| 2970 | if (trans_seq_no != priv->ExpectedAuthentTransactionSeqNum) | ||
| 2971 | return; | ||
| 2972 | |||
| 2973 | if (system == WLAN_AUTH_OPEN) { | ||
| 2974 | if (trans_seq_no == 0x0002) { | ||
| 2975 | should_associate = 1; | ||
| 2976 | } | ||
| 2977 | } else if (system == WLAN_AUTH_SHARED_KEY) { | ||
| 2978 | if (trans_seq_no == 0x0002 && | ||
| 2979 | auth->el_id == WLAN_EID_CHALLENGE) { | ||
| 2980 | send_authentication_request(priv, system, auth->chall_text, auth->chall_text_len); | ||
| 2981 | return; | ||
| 2982 | } else if (trans_seq_no == 0x0004) { | ||
| 2983 | should_associate = 1; | ||
| 2984 | } | ||
| 2985 | } | ||
| 2986 | |||
| 2987 | if (should_associate) { | ||
| 2988 | if (priv->station_was_associated) { | ||
| 2989 | atmel_enter_state(priv, STATION_STATE_REASSOCIATING); | ||
| 2990 | send_association_request(priv, 1); | ||
| 2991 | return; | ||
| 2992 | } else { | ||
| 2993 | atmel_enter_state(priv, STATION_STATE_ASSOCIATING); | ||
| 2994 | send_association_request(priv, 0); | ||
| 2995 | return; | ||
| 2996 | } | ||
| 2997 | } | ||
| 2998 | } | ||
| 2999 | |||
| 3000 | if (status == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG) { | ||
| 3001 | /* Flip back and forth between WEP auth modes until the max | ||
| 3002 | * authentication tries has been exceeded. | ||
| 3003 | */ | ||
| 3004 | if (system == WLAN_AUTH_OPEN) { | ||
| 3005 | priv->CurrentAuthentTransactionSeqNum = 0x001; | ||
| 3006 | priv->exclude_unencrypted = 1; | ||
| 3007 | send_authentication_request(priv, WLAN_AUTH_SHARED_KEY, NULL, 0); | ||
| 3008 | return; | ||
| 3009 | } else if (system == WLAN_AUTH_SHARED_KEY | ||
| 3010 | && priv->wep_is_on) { | ||
| 3011 | priv->CurrentAuthentTransactionSeqNum = 0x001; | ||
| 3012 | priv->exclude_unencrypted = 0; | ||
| 3013 | send_authentication_request(priv, WLAN_AUTH_OPEN, NULL, 0); | ||
| 3014 | return; | ||
| 3015 | } else if (priv->connect_to_any_BSS) { | ||
| 3016 | int bss_index; | ||
| 3017 | |||
| 3018 | priv->BSSinfo[(int)(priv->current_BSS)].channel |= 0x80; | ||
| 3019 | |||
| 3020 | if ((bss_index = retrieve_bss(priv)) != -1) { | ||
| 3021 | atmel_join_bss(priv, bss_index); | ||
| 3022 | return; | ||
| 3023 | } | ||
| 3024 | } | ||
| 3025 | } | ||
| 3026 | |||
| 3027 | priv->AuthenticationRequestRetryCnt = 0; | ||
| 3028 | atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); | ||
| 3029 | priv->station_is_associated = 0; | ||
| 3030 | } | ||
| 3031 | |||
| 3032 | static void associate(struct atmel_private *priv, u16 frame_len, u16 subtype) | ||
| 3033 | { | ||
| 3034 | struct ass_resp_format { | ||
| 3035 | __le16 capability; | ||
| 3036 | __le16 status; | ||
| 3037 | __le16 ass_id; | ||
| 3038 | u8 el_id; | ||
| 3039 | u8 length; | ||
| 3040 | u8 rates[4]; | ||
| 3041 | } *ass_resp = (struct ass_resp_format *)priv->rx_buf; | ||
| 3042 | |||
| 3043 | u16 status = le16_to_cpu(ass_resp->status); | ||
| 3044 | u16 ass_id = le16_to_cpu(ass_resp->ass_id); | ||
| 3045 | u16 rates_len = ass_resp->length > 4 ? 4 : ass_resp->length; | ||
| 3046 | |||
| 3047 | union iwreq_data wrqu; | ||
| 3048 | |||
| 3049 | if (frame_len < 8 + rates_len) | ||
| 3050 | return; | ||
| 3051 | |||
| 3052 | if (status == WLAN_STATUS_SUCCESS) { | ||
| 3053 | if (subtype == IEEE80211_STYPE_ASSOC_RESP) | ||
| 3054 | priv->AssociationRequestRetryCnt = 0; | ||
| 3055 | else | ||
| 3056 | priv->ReAssociationRequestRetryCnt = 0; | ||
| 3057 | |||
| 3058 | atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, | ||
| 3059 | MAC_MGMT_MIB_STATION_ID_POS, ass_id & 0x3fff); | ||
| 3060 | atmel_set_mib(priv, Phy_Mib_Type, | ||
| 3061 | PHY_MIB_RATE_SET_POS, ass_resp->rates, rates_len); | ||
| 3062 | if (priv->power_mode == 0) { | ||
| 3063 | priv->listen_interval = 1; | ||
| 3064 | atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, | ||
| 3065 | MAC_MGMT_MIB_PS_MODE_POS, ACTIVE_MODE); | ||
| 3066 | atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, | ||
| 3067 | MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 1); | ||
| 3068 | } else { | ||
| 3069 | priv->listen_interval = 2; | ||
| 3070 | atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, | ||
| 3071 | MAC_MGMT_MIB_PS_MODE_POS, PS_MODE); | ||
| 3072 | atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, | ||
| 3073 | MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 2); | ||
| 3074 | } | ||
| 3075 | |||
| 3076 | priv->station_is_associated = 1; | ||
| 3077 | priv->station_was_associated = 1; | ||
| 3078 | atmel_enter_state(priv, STATION_STATE_READY); | ||
| 3079 | |||
| 3080 | /* Send association event to userspace */ | ||
| 3081 | wrqu.data.length = 0; | ||
| 3082 | wrqu.data.flags = 0; | ||
| 3083 | memcpy(wrqu.ap_addr.sa_data, priv->CurrentBSSID, ETH_ALEN); | ||
| 3084 | wrqu.ap_addr.sa_family = ARPHRD_ETHER; | ||
| 3085 | wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL); | ||
| 3086 | |||
| 3087 | return; | ||
| 3088 | } | ||
| 3089 | |||
| 3090 | if (subtype == IEEE80211_STYPE_ASSOC_RESP && | ||
| 3091 | status != WLAN_STATUS_ASSOC_DENIED_RATES && | ||
| 3092 | status != WLAN_STATUS_CAPS_UNSUPPORTED && | ||
| 3093 | priv->AssociationRequestRetryCnt < MAX_ASSOCIATION_RETRIES) { | ||
| 3094 | mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); | ||
| 3095 | priv->AssociationRequestRetryCnt++; | ||
| 3096 | send_association_request(priv, 0); | ||
| 3097 | return; | ||
| 3098 | } | ||
| 3099 | |||
| 3100 | if (subtype == IEEE80211_STYPE_REASSOC_RESP && | ||
| 3101 | status != WLAN_STATUS_ASSOC_DENIED_RATES && | ||
| 3102 | status != WLAN_STATUS_CAPS_UNSUPPORTED && | ||
| 3103 | priv->ReAssociationRequestRetryCnt < MAX_ASSOCIATION_RETRIES) { | ||
| 3104 | mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); | ||
| 3105 | priv->ReAssociationRequestRetryCnt++; | ||
| 3106 | send_association_request(priv, 1); | ||
| 3107 | return; | ||
| 3108 | } | ||
| 3109 | |||
| 3110 | atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); | ||
| 3111 | priv->station_is_associated = 0; | ||
| 3112 | |||
| 3113 | if (priv->connect_to_any_BSS) { | ||
| 3114 | int bss_index; | ||
| 3115 | priv->BSSinfo[(int)(priv->current_BSS)].channel |= 0x80; | ||
| 3116 | |||
| 3117 | if ((bss_index = retrieve_bss(priv)) != -1) | ||
| 3118 | atmel_join_bss(priv, bss_index); | ||
| 3119 | } | ||
| 3120 | } | ||
| 3121 | |||
| 3122 | static void atmel_join_bss(struct atmel_private *priv, int bss_index) | ||
| 3123 | { | ||
| 3124 | struct bss_info *bss = &priv->BSSinfo[bss_index]; | ||
| 3125 | |||
| 3126 | memcpy(priv->CurrentBSSID, bss->BSSID, ETH_ALEN); | ||
| 3127 | memcpy(priv->SSID, bss->SSID, priv->SSID_size = bss->SSIDsize); | ||
| 3128 | |||
| 3129 | /* The WPA stuff cares about the current AP address */ | ||
| 3130 | if (priv->use_wpa) | ||
| 3131 | build_wpa_mib(priv); | ||
| 3132 | |||
| 3133 | /* When switching to AdHoc turn OFF Power Save if needed */ | ||
| 3134 | |||
| 3135 | if (bss->BSStype == IW_MODE_ADHOC && | ||
| 3136 | priv->operating_mode != IW_MODE_ADHOC && | ||
| 3137 | priv->power_mode) { | ||
| 3138 | priv->power_mode = 0; | ||
| 3139 | priv->listen_interval = 1; | ||
| 3140 | atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, | ||
| 3141 | MAC_MGMT_MIB_PS_MODE_POS, ACTIVE_MODE); | ||
| 3142 | atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, | ||
| 3143 | MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 1); | ||
| 3144 | } | ||
| 3145 | |||
| 3146 | priv->operating_mode = bss->BSStype; | ||
| 3147 | priv->channel = bss->channel & 0x7f; | ||
| 3148 | priv->beacon_period = bss->beacon_period; | ||
| 3149 | |||
| 3150 | if (priv->preamble != bss->preamble) { | ||
| 3151 | priv->preamble = bss->preamble; | ||
| 3152 | atmel_set_mib8(priv, Local_Mib_Type, | ||
| 3153 | LOCAL_MIB_PREAMBLE_TYPE, bss->preamble); | ||
| 3154 | } | ||
| 3155 | |||
| 3156 | if (!priv->wep_is_on && bss->UsingWEP) { | ||
| 3157 | atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); | ||
| 3158 | priv->station_is_associated = 0; | ||
| 3159 | return; | ||
| 3160 | } | ||
| 3161 | |||
| 3162 | if (priv->wep_is_on && !bss->UsingWEP) { | ||
| 3163 | atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); | ||
| 3164 | priv->station_is_associated = 0; | ||
| 3165 | return; | ||
| 3166 | } | ||
| 3167 | |||
| 3168 | atmel_enter_state(priv, STATION_STATE_JOINNING); | ||
| 3169 | |||
| 3170 | if (priv->operating_mode == IW_MODE_INFRA) | ||
| 3171 | join(priv, BSS_TYPE_INFRASTRUCTURE); | ||
| 3172 | else | ||
| 3173 | join(priv, BSS_TYPE_AD_HOC); | ||
| 3174 | } | ||
| 3175 | |||
| 3176 | static void restart_search(struct atmel_private *priv) | ||
| 3177 | { | ||
| 3178 | int bss_index; | ||
| 3179 | |||
| 3180 | if (!priv->connect_to_any_BSS) { | ||
| 3181 | atmel_scan(priv, 1); | ||
| 3182 | } else { | ||
| 3183 | priv->BSSinfo[(int)(priv->current_BSS)].channel |= 0x80; | ||
| 3184 | |||
| 3185 | if ((bss_index = retrieve_bss(priv)) != -1) | ||
| 3186 | atmel_join_bss(priv, bss_index); | ||
| 3187 | else | ||
| 3188 | atmel_scan(priv, 0); | ||
| 3189 | } | ||
| 3190 | } | ||
| 3191 | |||
| 3192 | static void smooth_rssi(struct atmel_private *priv, u8 rssi) | ||
| 3193 | { | ||
| 3194 | u8 old = priv->wstats.qual.level; | ||
| 3195 | u8 max_rssi = 42; /* 502-rmfd-revd max by experiment, default for now */ | ||
| 3196 | |||
| 3197 | switch (priv->firmware_type) { | ||
| 3198 | case ATMEL_FW_TYPE_502E: | ||
| 3199 | max_rssi = 63; /* 502-rmfd-reve max by experiment */ | ||
| 3200 | break; | ||
| 3201 | default: | ||
| 3202 | break; | ||
| 3203 | } | ||
| 3204 | |||
| 3205 | rssi = rssi * 100 / max_rssi; | ||
| 3206 | if ((rssi + old) % 2) | ||
| 3207 | priv->wstats.qual.level = (rssi + old) / 2 + 1; | ||
| 3208 | else | ||
| 3209 | priv->wstats.qual.level = (rssi + old) / 2; | ||
| 3210 | priv->wstats.qual.updated |= IW_QUAL_LEVEL_UPDATED; | ||
| 3211 | priv->wstats.qual.updated &= ~IW_QUAL_LEVEL_INVALID; | ||
| 3212 | } | ||
| 3213 | |||
| 3214 | static void atmel_smooth_qual(struct atmel_private *priv) | ||
| 3215 | { | ||
| 3216 | unsigned long time_diff = (jiffies - priv->last_qual) / HZ; | ||
| 3217 | while (time_diff--) { | ||
| 3218 | priv->last_qual += HZ; | ||
| 3219 | priv->wstats.qual.qual = priv->wstats.qual.qual / 2; | ||
| 3220 | priv->wstats.qual.qual += | ||
| 3221 | priv->beacons_this_sec * priv->beacon_period * (priv->wstats.qual.level + 100) / 4000; | ||
| 3222 | priv->beacons_this_sec = 0; | ||
| 3223 | } | ||
| 3224 | priv->wstats.qual.updated |= IW_QUAL_QUAL_UPDATED; | ||
| 3225 | priv->wstats.qual.updated &= ~IW_QUAL_QUAL_INVALID; | ||
| 3226 | } | ||
| 3227 | |||
| 3228 | /* deals with incoming management frames. */ | ||
| 3229 | static void atmel_management_frame(struct atmel_private *priv, | ||
| 3230 | struct ieee80211_hdr *header, | ||
| 3231 | u16 frame_len, u8 rssi) | ||
| 3232 | { | ||
| 3233 | u16 subtype; | ||
| 3234 | |||
| 3235 | subtype = le16_to_cpu(header->frame_control) & IEEE80211_FCTL_STYPE; | ||
| 3236 | switch (subtype) { | ||
| 3237 | case IEEE80211_STYPE_BEACON: | ||
| 3238 | case IEEE80211_STYPE_PROBE_RESP: | ||
| 3239 | |||
| 3240 | /* beacon frame has multiple variable-length fields - | ||
| 3241 | never let an engineer loose with a data structure design. */ | ||
| 3242 | { | ||
| 3243 | struct beacon_format { | ||
| 3244 | __le64 timestamp; | ||
| 3245 | __le16 interval; | ||
| 3246 | __le16 capability; | ||
| 3247 | u8 ssid_el_id; | ||
| 3248 | u8 ssid_length; | ||
| 3249 | /* ssid here */ | ||
| 3250 | u8 rates_el_id; | ||
| 3251 | u8 rates_length; | ||
| 3252 | /* rates here */ | ||
| 3253 | u8 ds_el_id; | ||
| 3254 | u8 ds_length; | ||
| 3255 | /* ds here */ | ||
| 3256 | } *beacon = (struct beacon_format *)priv->rx_buf; | ||
| 3257 | |||
| 3258 | u8 channel, rates_length, ssid_length; | ||
| 3259 | u64 timestamp = le64_to_cpu(beacon->timestamp); | ||
| 3260 | u16 beacon_interval = le16_to_cpu(beacon->interval); | ||
| 3261 | u16 capability = le16_to_cpu(beacon->capability); | ||
| 3262 | u8 *beaconp = priv->rx_buf; | ||
| 3263 | ssid_length = beacon->ssid_length; | ||
| 3264 | /* this blows chunks. */ | ||
| 3265 | if (frame_len < 14 || frame_len < ssid_length + 15) | ||
| 3266 | return; | ||
| 3267 | rates_length = beaconp[beacon->ssid_length + 15]; | ||
| 3268 | if (frame_len < ssid_length + rates_length + 18) | ||
| 3269 | return; | ||
| 3270 | if (ssid_length > MAX_SSID_LENGTH) | ||
| 3271 | return; | ||
| 3272 | channel = beaconp[ssid_length + rates_length + 18]; | ||
| 3273 | |||
| 3274 | if (priv->station_state == STATION_STATE_READY) { | ||
| 3275 | smooth_rssi(priv, rssi); | ||
| 3276 | if (is_frame_from_current_bss(priv, header)) { | ||
| 3277 | priv->beacons_this_sec++; | ||
| 3278 | atmel_smooth_qual(priv); | ||
| 3279 | if (priv->last_beacon_timestamp) { | ||
| 3280 | /* Note truncate this to 32 bits - kernel can't divide a long */ | ||
| 3281 | u32 beacon_delay = timestamp - priv->last_beacon_timestamp; | ||
| 3282 | int beacons = beacon_delay / (beacon_interval * 1000); | ||
| 3283 | if (beacons > 1) | ||
| 3284 | priv->wstats.miss.beacon += beacons - 1; | ||
| 3285 | } | ||
| 3286 | priv->last_beacon_timestamp = timestamp; | ||
| 3287 | handle_beacon_probe(priv, capability, channel); | ||
| 3288 | } | ||
| 3289 | } | ||
| 3290 | |||
| 3291 | if (priv->station_state == STATION_STATE_SCANNING) | ||
| 3292 | store_bss_info(priv, header, capability, | ||
| 3293 | beacon_interval, channel, rssi, | ||
| 3294 | ssid_length, | ||
| 3295 | &beacon->rates_el_id, | ||
| 3296 | subtype == IEEE80211_STYPE_BEACON); | ||
| 3297 | } | ||
| 3298 | break; | ||
| 3299 | |||
| 3300 | case IEEE80211_STYPE_AUTH: | ||
| 3301 | |||
| 3302 | if (priv->station_state == STATION_STATE_AUTHENTICATING) | ||
| 3303 | authenticate(priv, frame_len); | ||
| 3304 | |||
| 3305 | break; | ||
| 3306 | |||
| 3307 | case IEEE80211_STYPE_ASSOC_RESP: | ||
| 3308 | case IEEE80211_STYPE_REASSOC_RESP: | ||
| 3309 | |||
| 3310 | if (priv->station_state == STATION_STATE_ASSOCIATING || | ||
| 3311 | priv->station_state == STATION_STATE_REASSOCIATING) | ||
| 3312 | associate(priv, frame_len, subtype); | ||
| 3313 | |||
| 3314 | break; | ||
| 3315 | |||
| 3316 | case IEEE80211_STYPE_DISASSOC: | ||
| 3317 | if (priv->station_is_associated && | ||
| 3318 | priv->operating_mode == IW_MODE_INFRA && | ||
| 3319 | is_frame_from_current_bss(priv, header)) { | ||
| 3320 | priv->station_was_associated = 0; | ||
| 3321 | priv->station_is_associated = 0; | ||
| 3322 | |||
| 3323 | atmel_enter_state(priv, STATION_STATE_JOINNING); | ||
| 3324 | join(priv, BSS_TYPE_INFRASTRUCTURE); | ||
| 3325 | } | ||
| 3326 | |||
| 3327 | break; | ||
| 3328 | |||
| 3329 | case IEEE80211_STYPE_DEAUTH: | ||
| 3330 | if (priv->operating_mode == IW_MODE_INFRA && | ||
| 3331 | is_frame_from_current_bss(priv, header)) { | ||
| 3332 | priv->station_was_associated = 0; | ||
| 3333 | |||
| 3334 | atmel_enter_state(priv, STATION_STATE_JOINNING); | ||
| 3335 | join(priv, BSS_TYPE_INFRASTRUCTURE); | ||
| 3336 | } | ||
| 3337 | |||
| 3338 | break; | ||
| 3339 | } | ||
| 3340 | } | ||
| 3341 | |||
| 3342 | /* run when timer expires */ | ||
| 3343 | static void atmel_management_timer(struct timer_list *t) | ||
| 3344 | { | ||
| 3345 | struct atmel_private *priv = from_timer(priv, t, management_timer); | ||
| 3346 | unsigned long flags; | ||
| 3347 | |||
| 3348 | /* Check if the card has been yanked. */ | ||
| 3349 | if (priv->card && priv->present_callback && | ||
| 3350 | !(*priv->present_callback)(priv->card)) | ||
| 3351 | return; | ||
| 3352 | |||
| 3353 | spin_lock_irqsave(&priv->irqlock, flags); | ||
| 3354 | |||
| 3355 | switch (priv->station_state) { | ||
| 3356 | |||
| 3357 | case STATION_STATE_AUTHENTICATING: | ||
| 3358 | if (priv->AuthenticationRequestRetryCnt >= MAX_AUTHENTICATION_RETRIES) { | ||
| 3359 | atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); | ||
| 3360 | priv->station_is_associated = 0; | ||
| 3361 | priv->AuthenticationRequestRetryCnt = 0; | ||
| 3362 | restart_search(priv); | ||
| 3363 | } else { | ||
| 3364 | int auth = WLAN_AUTH_OPEN; | ||
| 3365 | priv->AuthenticationRequestRetryCnt++; | ||
| 3366 | priv->CurrentAuthentTransactionSeqNum = 0x0001; | ||
| 3367 | mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); | ||
| 3368 | if (priv->wep_is_on && priv->exclude_unencrypted) | ||
| 3369 | auth = WLAN_AUTH_SHARED_KEY; | ||
| 3370 | send_authentication_request(priv, auth, NULL, 0); | ||
| 3371 | } | ||
| 3372 | break; | ||
| 3373 | |||
| 3374 | case STATION_STATE_ASSOCIATING: | ||
| 3375 | if (priv->AssociationRequestRetryCnt == MAX_ASSOCIATION_RETRIES) { | ||
| 3376 | atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); | ||
| 3377 | priv->station_is_associated = 0; | ||
| 3378 | priv->AssociationRequestRetryCnt = 0; | ||
| 3379 | restart_search(priv); | ||
| 3380 | } else { | ||
| 3381 | priv->AssociationRequestRetryCnt++; | ||
| 3382 | mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); | ||
| 3383 | send_association_request(priv, 0); | ||
| 3384 | } | ||
| 3385 | break; | ||
| 3386 | |||
| 3387 | case STATION_STATE_REASSOCIATING: | ||
| 3388 | if (priv->ReAssociationRequestRetryCnt == MAX_ASSOCIATION_RETRIES) { | ||
| 3389 | atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); | ||
| 3390 | priv->station_is_associated = 0; | ||
| 3391 | priv->ReAssociationRequestRetryCnt = 0; | ||
| 3392 | restart_search(priv); | ||
| 3393 | } else { | ||
| 3394 | priv->ReAssociationRequestRetryCnt++; | ||
| 3395 | mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); | ||
| 3396 | send_association_request(priv, 1); | ||
| 3397 | } | ||
| 3398 | break; | ||
| 3399 | |||
| 3400 | default: | ||
| 3401 | break; | ||
| 3402 | } | ||
| 3403 | |||
| 3404 | spin_unlock_irqrestore(&priv->irqlock, flags); | ||
| 3405 | } | ||
| 3406 | |||
| 3407 | static void atmel_command_irq(struct atmel_private *priv) | ||
| 3408 | { | ||
| 3409 | u8 status = atmel_rmem8(priv, atmel_co(priv, CMD_BLOCK_STATUS_OFFSET)); | ||
| 3410 | u8 command = atmel_rmem8(priv, atmel_co(priv, CMD_BLOCK_COMMAND_OFFSET)); | ||
| 3411 | int fast_scan; | ||
| 3412 | union iwreq_data wrqu; | ||
| 3413 | |||
| 3414 | if (status == CMD_STATUS_IDLE || | ||
| 3415 | status == CMD_STATUS_IN_PROGRESS) | ||
| 3416 | return; | ||
| 3417 | |||
| 3418 | switch (command) { | ||
| 3419 | case CMD_Start: | ||
| 3420 | if (status == CMD_STATUS_COMPLETE) { | ||
| 3421 | priv->station_was_associated = priv->station_is_associated; | ||
| 3422 | atmel_get_mib(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_CUR_BSSID_POS, | ||
| 3423 | (u8 *)priv->CurrentBSSID, 6); | ||
| 3424 | atmel_enter_state(priv, STATION_STATE_READY); | ||
| 3425 | } | ||
| 3426 | break; | ||
| 3427 | |||
| 3428 | case CMD_Scan: | ||
| 3429 | fast_scan = priv->fast_scan; | ||
| 3430 | priv->fast_scan = 0; | ||
| 3431 | |||
| 3432 | if (status != CMD_STATUS_COMPLETE) { | ||
| 3433 | atmel_scan(priv, 1); | ||
| 3434 | } else { | ||
| 3435 | int bss_index = retrieve_bss(priv); | ||
| 3436 | int notify_scan_complete = 1; | ||
| 3437 | if (bss_index != -1) { | ||
| 3438 | atmel_join_bss(priv, bss_index); | ||
| 3439 | } else if (priv->operating_mode == IW_MODE_ADHOC && | ||
| 3440 | priv->SSID_size != 0) { | ||
| 3441 | start(priv, BSS_TYPE_AD_HOC); | ||
| 3442 | } else { | ||
| 3443 | priv->fast_scan = !fast_scan; | ||
| 3444 | atmel_scan(priv, 1); | ||
| 3445 | notify_scan_complete = 0; | ||
| 3446 | } | ||
| 3447 | priv->site_survey_state = SITE_SURVEY_COMPLETED; | ||
| 3448 | if (notify_scan_complete) { | ||
| 3449 | wrqu.data.length = 0; | ||
| 3450 | wrqu.data.flags = 0; | ||
| 3451 | wireless_send_event(priv->dev, SIOCGIWSCAN, &wrqu, NULL); | ||
| 3452 | } | ||
| 3453 | } | ||
| 3454 | break; | ||
| 3455 | |||
| 3456 | case CMD_SiteSurvey: | ||
| 3457 | priv->fast_scan = 0; | ||
| 3458 | |||
| 3459 | if (status != CMD_STATUS_COMPLETE) | ||
| 3460 | return; | ||
| 3461 | |||
| 3462 | priv->site_survey_state = SITE_SURVEY_COMPLETED; | ||
| 3463 | if (priv->station_is_associated) { | ||
| 3464 | atmel_enter_state(priv, STATION_STATE_READY); | ||
| 3465 | wrqu.data.length = 0; | ||
| 3466 | wrqu.data.flags = 0; | ||
| 3467 | wireless_send_event(priv->dev, SIOCGIWSCAN, &wrqu, NULL); | ||
| 3468 | } else { | ||
| 3469 | atmel_scan(priv, 1); | ||
| 3470 | } | ||
| 3471 | break; | ||
| 3472 | |||
| 3473 | case CMD_Join: | ||
| 3474 | if (status == CMD_STATUS_COMPLETE) { | ||
| 3475 | if (priv->operating_mode == IW_MODE_ADHOC) { | ||
| 3476 | priv->station_was_associated = priv->station_is_associated; | ||
| 3477 | atmel_enter_state(priv, STATION_STATE_READY); | ||
| 3478 | } else { | ||
| 3479 | int auth = WLAN_AUTH_OPEN; | ||
| 3480 | priv->AuthenticationRequestRetryCnt = 0; | ||
| 3481 | atmel_enter_state(priv, STATION_STATE_AUTHENTICATING); | ||
| 3482 | |||
| 3483 | mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); | ||
| 3484 | priv->CurrentAuthentTransactionSeqNum = 0x0001; | ||
| 3485 | if (priv->wep_is_on && priv->exclude_unencrypted) | ||
| 3486 | auth = WLAN_AUTH_SHARED_KEY; | ||
| 3487 | send_authentication_request(priv, auth, NULL, 0); | ||
| 3488 | } | ||
| 3489 | return; | ||
| 3490 | } | ||
| 3491 | |||
| 3492 | atmel_scan(priv, 1); | ||
| 3493 | } | ||
| 3494 | } | ||
| 3495 | |||
| 3496 | static int atmel_wakeup_firmware(struct atmel_private *priv) | ||
| 3497 | { | ||
| 3498 | struct host_info_struct *iface = &priv->host_info; | ||
| 3499 | u16 mr1, mr3; | ||
| 3500 | int i; | ||
| 3501 | |||
| 3502 | if (priv->card_type == CARD_TYPE_SPI_FLASH) | ||
| 3503 | atmel_set_gcr(priv->dev, GCR_REMAP); | ||
| 3504 | |||
| 3505 | /* wake up on-board processor */ | ||
| 3506 | atmel_clear_gcr(priv->dev, 0x0040); | ||
| 3507 | atmel_write16(priv->dev, BSR, BSS_SRAM); | ||
| 3508 | |||
| 3509 | if (priv->card_type == CARD_TYPE_SPI_FLASH) | ||
| 3510 | mdelay(100); | ||
| 3511 | |||
| 3512 | /* and wait for it */ | ||
| 3513 | for (i = LOOP_RETRY_LIMIT; i; i--) { | ||
| 3514 | mr1 = atmel_read16(priv->dev, MR1); | ||
| 3515 | mr3 = atmel_read16(priv->dev, MR3); | ||
| 3516 | |||
| 3517 | if (mr3 & MAC_BOOT_COMPLETE) | ||
| 3518 | break; | ||
| 3519 | if (mr1 & MAC_BOOT_COMPLETE && | ||
| 3520 | priv->bus_type == BUS_TYPE_PCCARD) | ||
| 3521 | break; | ||
| 3522 | } | ||
| 3523 | |||
| 3524 | if (i == 0) { | ||
| 3525 | printk(KERN_ALERT "%s: MAC failed to boot.\n", priv->dev->name); | ||
| 3526 | return -EIO; | ||
| 3527 | } | ||
| 3528 | |||
| 3529 | if ((priv->host_info_base = atmel_read16(priv->dev, MR2)) == 0xffff) { | ||
| 3530 | printk(KERN_ALERT "%s: card missing.\n", priv->dev->name); | ||
| 3531 | return -ENODEV; | ||
| 3532 | } | ||
| 3533 | |||
| 3534 | /* now check for completion of MAC initialization through | ||
| 3535 | the FunCtrl field of the IFACE, poll MR1 to detect completion of | ||
| 3536 | MAC initialization, check completion status, set interrupt mask, | ||
| 3537 | enables interrupts and calls Tx and Rx initialization functions */ | ||
| 3538 | |||
| 3539 | atmel_wmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET), FUNC_CTRL_INIT_COMPLETE); | ||
| 3540 | |||
| 3541 | for (i = LOOP_RETRY_LIMIT; i; i--) { | ||
| 3542 | mr1 = atmel_read16(priv->dev, MR1); | ||
| 3543 | mr3 = atmel_read16(priv->dev, MR3); | ||
| 3544 | |||
| 3545 | if (mr3 & MAC_INIT_COMPLETE) | ||
| 3546 | break; | ||
| 3547 | if (mr1 & MAC_INIT_COMPLETE && | ||
| 3548 | priv->bus_type == BUS_TYPE_PCCARD) | ||
| 3549 | break; | ||
| 3550 | } | ||
| 3551 | |||
| 3552 | if (i == 0) { | ||
| 3553 | printk(KERN_ALERT "%s: MAC failed to initialise.\n", | ||
| 3554 | priv->dev->name); | ||
| 3555 | return -EIO; | ||
| 3556 | } | ||
| 3557 | |||
| 3558 | /* Check for MAC_INIT_OK only on the register that the MAC_INIT_OK was set */ | ||
| 3559 | if ((mr3 & MAC_INIT_COMPLETE) && | ||
| 3560 | !(atmel_read16(priv->dev, MR3) & MAC_INIT_OK)) { | ||
| 3561 | printk(KERN_ALERT "%s: MAC failed MR3 self-test.\n", priv->dev->name); | ||
| 3562 | return -EIO; | ||
| 3563 | } | ||
| 3564 | if ((mr1 & MAC_INIT_COMPLETE) && | ||
| 3565 | !(atmel_read16(priv->dev, MR1) & MAC_INIT_OK)) { | ||
| 3566 | printk(KERN_ALERT "%s: MAC failed MR1 self-test.\n", priv->dev->name); | ||
| 3567 | return -EIO; | ||
| 3568 | } | ||
| 3569 | |||
| 3570 | atmel_copy_to_host(priv->dev, (unsigned char *)iface, | ||
| 3571 | priv->host_info_base, sizeof(*iface)); | ||
| 3572 | |||
| 3573 | iface->tx_buff_pos = le16_to_cpu(iface->tx_buff_pos); | ||
| 3574 | iface->tx_buff_size = le16_to_cpu(iface->tx_buff_size); | ||
| 3575 | iface->tx_desc_pos = le16_to_cpu(iface->tx_desc_pos); | ||
| 3576 | iface->tx_desc_count = le16_to_cpu(iface->tx_desc_count); | ||
| 3577 | iface->rx_buff_pos = le16_to_cpu(iface->rx_buff_pos); | ||
| 3578 | iface->rx_buff_size = le16_to_cpu(iface->rx_buff_size); | ||
| 3579 | iface->rx_desc_pos = le16_to_cpu(iface->rx_desc_pos); | ||
| 3580 | iface->rx_desc_count = le16_to_cpu(iface->rx_desc_count); | ||
| 3581 | iface->build_version = le16_to_cpu(iface->build_version); | ||
| 3582 | iface->command_pos = le16_to_cpu(iface->command_pos); | ||
| 3583 | iface->major_version = le16_to_cpu(iface->major_version); | ||
| 3584 | iface->minor_version = le16_to_cpu(iface->minor_version); | ||
| 3585 | iface->func_ctrl = le16_to_cpu(iface->func_ctrl); | ||
| 3586 | iface->mac_status = le16_to_cpu(iface->mac_status); | ||
| 3587 | |||
| 3588 | return 0; | ||
| 3589 | } | ||
| 3590 | |||
| 3591 | /* determine type of memory and MAC address */ | ||
| 3592 | static int probe_atmel_card(struct net_device *dev) | ||
| 3593 | { | ||
| 3594 | int rc = 0; | ||
| 3595 | struct atmel_private *priv = netdev_priv(dev); | ||
| 3596 | u8 addr[ETH_ALEN] = {}; | ||
| 3597 | |||
| 3598 | /* reset pccard */ | ||
| 3599 | if (priv->bus_type == BUS_TYPE_PCCARD) | ||
| 3600 | atmel_write16(dev, GCR, 0x0060); | ||
| 3601 | |||
| 3602 | atmel_write16(dev, GCR, 0x0040); | ||
| 3603 | msleep(500); | ||
| 3604 | |||
| 3605 | if (atmel_read16(dev, MR2) == 0) { | ||
| 3606 | /* No stored firmware so load a small stub which just | ||
| 3607 | tells us the MAC address */ | ||
| 3608 | int i; | ||
| 3609 | priv->card_type = CARD_TYPE_EEPROM; | ||
| 3610 | atmel_write16(dev, BSR, BSS_IRAM); | ||
| 3611 | atmel_copy_to_card(dev, 0, mac_reader, sizeof(mac_reader)); | ||
| 3612 | atmel_set_gcr(dev, GCR_REMAP); | ||
| 3613 | atmel_clear_gcr(priv->dev, 0x0040); | ||
| 3614 | atmel_write16(dev, BSR, BSS_SRAM); | ||
| 3615 | for (i = LOOP_RETRY_LIMIT; i; i--) | ||
| 3616 | if (atmel_read16(dev, MR3) & MAC_BOOT_COMPLETE) | ||
| 3617 | break; | ||
| 3618 | if (i == 0) { | ||
| 3619 | printk(KERN_ALERT "%s: MAC failed to boot MAC address reader.\n", dev->name); | ||
| 3620 | } else { | ||
| 3621 | |||
| 3622 | atmel_copy_to_host(dev, addr, atmel_read16(dev, MR2), 6); | ||
| 3623 | eth_hw_addr_set(dev, addr); | ||
| 3624 | /* got address, now squash it again until the network | ||
| 3625 | interface is opened */ | ||
| 3626 | if (priv->bus_type == BUS_TYPE_PCCARD) | ||
| 3627 | atmel_write16(dev, GCR, 0x0060); | ||
| 3628 | atmel_write16(dev, GCR, 0x0040); | ||
| 3629 | rc = 1; | ||
| 3630 | } | ||
| 3631 | } else if (atmel_read16(dev, MR4) == 0) { | ||
| 3632 | /* Mac address easy in this case. */ | ||
| 3633 | priv->card_type = CARD_TYPE_PARALLEL_FLASH; | ||
| 3634 | atmel_write16(dev, BSR, 1); | ||
| 3635 | atmel_copy_to_host(dev, addr, 0xc000, 6); | ||
| 3636 | eth_hw_addr_set(dev, addr); | ||
| 3637 | atmel_write16(dev, BSR, 0x200); | ||
| 3638 | rc = 1; | ||
| 3639 | } else { | ||
| 3640 | /* Standard firmware in flash, boot it up and ask | ||
| 3641 | for the Mac Address */ | ||
| 3642 | priv->card_type = CARD_TYPE_SPI_FLASH; | ||
| 3643 | if (atmel_wakeup_firmware(priv) == 0) { | ||
| 3644 | atmel_get_mib(priv, Mac_Address_Mib_Type, 0, addr, 6); | ||
| 3645 | eth_hw_addr_set(dev, addr); | ||
| 3646 | |||
| 3647 | /* got address, now squash it again until the network | ||
| 3648 | interface is opened */ | ||
| 3649 | if (priv->bus_type == BUS_TYPE_PCCARD) | ||
| 3650 | atmel_write16(dev, GCR, 0x0060); | ||
| 3651 | atmel_write16(dev, GCR, 0x0040); | ||
| 3652 | rc = 1; | ||
| 3653 | } | ||
| 3654 | } | ||
| 3655 | |||
| 3656 | if (rc) { | ||
| 3657 | if (dev->dev_addr[0] == 0xFF) { | ||
| 3658 | static const u8 default_mac[] = { | ||
| 3659 | 0x00, 0x04, 0x25, 0x00, 0x00, 0x00 | ||
| 3660 | }; | ||
| 3661 | printk(KERN_ALERT "%s: *** Invalid MAC address. UPGRADE Firmware ****\n", dev->name); | ||
| 3662 | eth_hw_addr_set(dev, default_mac); | ||
| 3663 | } | ||
| 3664 | } | ||
| 3665 | |||
| 3666 | return rc; | ||
| 3667 | } | ||
| 3668 | |||
| 3669 | /* Move the encyption information on the MIB structure. | ||
| 3670 | This routine is for the pre-WPA firmware: later firmware has | ||
| 3671 | a different format MIB and a different routine. */ | ||
| 3672 | static void build_wep_mib(struct atmel_private *priv) | ||
| 3673 | { | ||
| 3674 | struct { /* NB this is matched to the hardware, don't change. */ | ||
| 3675 | u8 wep_is_on; | ||
| 3676 | u8 default_key; /* 0..3 */ | ||
| 3677 | u8 reserved; | ||
| 3678 | u8 exclude_unencrypted; | ||
| 3679 | |||
| 3680 | u32 WEPICV_error_count; | ||
| 3681 | u32 WEP_excluded_count; | ||
| 3682 | |||
| 3683 | u8 wep_keys[MAX_ENCRYPTION_KEYS][13]; | ||
| 3684 | u8 encryption_level; /* 0, 1, 2 */ | ||
| 3685 | u8 reserved2[3]; | ||
| 3686 | } mib; | ||
| 3687 | int i; | ||
| 3688 | |||
| 3689 | mib.wep_is_on = priv->wep_is_on; | ||
| 3690 | if (priv->wep_is_on) { | ||
| 3691 | if (priv->wep_key_len[priv->default_key] > 5) | ||
| 3692 | mib.encryption_level = 2; | ||
| 3693 | else | ||
| 3694 | mib.encryption_level = 1; | ||
| 3695 | } else { | ||
| 3696 | mib.encryption_level = 0; | ||
| 3697 | } | ||
| 3698 | |||
| 3699 | mib.default_key = priv->default_key; | ||
| 3700 | mib.exclude_unencrypted = priv->exclude_unencrypted; | ||
| 3701 | |||
| 3702 | for (i = 0; i < MAX_ENCRYPTION_KEYS; i++) | ||
| 3703 | memcpy(mib.wep_keys[i], priv->wep_keys[i], 13); | ||
| 3704 | |||
| 3705 | atmel_set_mib(priv, Mac_Wep_Mib_Type, 0, (u8 *)&mib, sizeof(mib)); | ||
| 3706 | } | ||
| 3707 | |||
| 3708 | static void build_wpa_mib(struct atmel_private *priv) | ||
| 3709 | { | ||
| 3710 | /* This is for the later (WPA enabled) firmware. */ | ||
| 3711 | |||
| 3712 | struct { /* NB this is matched to the hardware, don't change. */ | ||
| 3713 | u8 cipher_default_key_value[MAX_ENCRYPTION_KEYS][MAX_ENCRYPTION_KEY_SIZE]; | ||
| 3714 | u8 receiver_address[ETH_ALEN]; | ||
| 3715 | u8 wep_is_on; | ||
| 3716 | u8 default_key; /* 0..3 */ | ||
| 3717 | u8 group_key; | ||
| 3718 | u8 exclude_unencrypted; | ||
| 3719 | u8 encryption_type; | ||
| 3720 | u8 reserved; | ||
| 3721 | |||
| 3722 | u32 WEPICV_error_count; | ||
| 3723 | u32 WEP_excluded_count; | ||
| 3724 | |||
| 3725 | u8 key_RSC[4][8]; | ||
| 3726 | } mib; | ||
| 3727 | |||
| 3728 | int i; | ||
| 3729 | |||
| 3730 | mib.wep_is_on = priv->wep_is_on; | ||
| 3731 | mib.exclude_unencrypted = priv->exclude_unencrypted; | ||
| 3732 | memcpy(mib.receiver_address, priv->CurrentBSSID, ETH_ALEN); | ||
| 3733 | |||
| 3734 | /* zero all the keys before adding in valid ones. */ | ||
| 3735 | memset(mib.cipher_default_key_value, 0, sizeof(mib.cipher_default_key_value)); | ||
| 3736 | |||
| 3737 | if (priv->wep_is_on) { | ||
| 3738 | /* There's a comment in the Atmel code to the effect that this | ||
| 3739 | is only valid when still using WEP, it may need to be set to | ||
| 3740 | something to use WPA */ | ||
| 3741 | memset(mib.key_RSC, 0, sizeof(mib.key_RSC)); | ||
| 3742 | |||
| 3743 | mib.default_key = mib.group_key = 255; | ||
| 3744 | for (i = 0; i < MAX_ENCRYPTION_KEYS; i++) { | ||
| 3745 | if (priv->wep_key_len[i] > 0) { | ||
| 3746 | memcpy(mib.cipher_default_key_value[i], priv->wep_keys[i], MAX_ENCRYPTION_KEY_SIZE); | ||
| 3747 | if (i == priv->default_key) { | ||
| 3748 | mib.default_key = i; | ||
| 3749 | mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-1] = 7; | ||
| 3750 | mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-2] = priv->pairwise_cipher_suite; | ||
| 3751 | } else { | ||
| 3752 | mib.group_key = i; | ||
| 3753 | priv->group_cipher_suite = priv->pairwise_cipher_suite; | ||
| 3754 | mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-1] = 1; | ||
| 3755 | mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-2] = priv->group_cipher_suite; | ||
| 3756 | } | ||
| 3757 | } | ||
| 3758 | } | ||
| 3759 | if (mib.default_key == 255) | ||
| 3760 | mib.default_key = mib.group_key != 255 ? mib.group_key : 0; | ||
| 3761 | if (mib.group_key == 255) | ||
| 3762 | mib.group_key = mib.default_key; | ||
| 3763 | |||
| 3764 | } | ||
| 3765 | |||
| 3766 | atmel_set_mib(priv, Mac_Wep_Mib_Type, 0, (u8 *)&mib, sizeof(mib)); | ||
| 3767 | } | ||
| 3768 | |||
| 3769 | static int reset_atmel_card(struct net_device *dev) | ||
| 3770 | { | ||
| 3771 | /* do everything necessary to wake up the hardware, including | ||
| 3772 | waiting for the lightning strike and throwing the knife switch.... | ||
| 3773 | |||
| 3774 | set all the Mib values which matter in the card to match | ||
| 3775 | their settings in the atmel_private structure. Some of these | ||
| 3776 | can be altered on the fly, but many (WEP, infrastructure or ad-hoc) | ||
| 3777 | can only be changed by tearing down the world and coming back through | ||
| 3778 | here. | ||
| 3779 | |||
| 3780 | This routine is also responsible for initialising some | ||
| 3781 | hardware-specific fields in the atmel_private structure, | ||
| 3782 | including a copy of the firmware's hostinfo structure | ||
| 3783 | which is the route into the rest of the firmware datastructures. */ | ||
| 3784 | |||
| 3785 | struct atmel_private *priv = netdev_priv(dev); | ||
| 3786 | u8 configuration; | ||
| 3787 | int old_state = priv->station_state; | ||
| 3788 | int err = 0; | ||
| 3789 | |||
| 3790 | /* data to add to the firmware names, in priority order | ||
| 3791 | this implemenents firmware versioning */ | ||
| 3792 | |||
| 3793 | static char *firmware_modifier[] = { | ||
| 3794 | "-wpa", | ||
| 3795 | "", | ||
| 3796 | NULL | ||
| 3797 | }; | ||
| 3798 | |||
| 3799 | /* reset pccard */ | ||
| 3800 | if (priv->bus_type == BUS_TYPE_PCCARD) | ||
| 3801 | atmel_write16(priv->dev, GCR, 0x0060); | ||
| 3802 | |||
| 3803 | /* stop card , disable interrupts */ | ||
| 3804 | atmel_write16(priv->dev, GCR, 0x0040); | ||
| 3805 | |||
| 3806 | if (priv->card_type == CARD_TYPE_EEPROM) { | ||
| 3807 | /* copy in firmware if needed */ | ||
| 3808 | const struct firmware *fw_entry = NULL; | ||
| 3809 | const unsigned char *fw; | ||
| 3810 | int len = priv->firmware_length; | ||
| 3811 | if (!(fw = priv->firmware)) { | ||
| 3812 | if (priv->firmware_type == ATMEL_FW_TYPE_NONE) { | ||
| 3813 | if (strlen(priv->firmware_id) == 0) { | ||
| 3814 | printk(KERN_INFO | ||
| 3815 | "%s: card type is unknown: assuming at76c502 firmware is OK.\n", | ||
| 3816 | dev->name); | ||
| 3817 | printk(KERN_INFO | ||
| 3818 | "%s: if not, use the firmware= module parameter.\n", | ||
| 3819 | dev->name); | ||
| 3820 | strcpy(priv->firmware_id, "atmel_at76c502.bin"); | ||
| 3821 | } | ||
| 3822 | err = request_firmware(&fw_entry, priv->firmware_id, priv->sys_dev); | ||
| 3823 | if (err != 0) { | ||
| 3824 | printk(KERN_ALERT | ||
| 3825 | "%s: firmware %s is missing, cannot continue.\n", | ||
| 3826 | dev->name, priv->firmware_id); | ||
| 3827 | return err; | ||
| 3828 | } | ||
| 3829 | } else { | ||
| 3830 | int fw_index = 0; | ||
| 3831 | int success = 0; | ||
| 3832 | |||
| 3833 | /* get firmware filename entry based on firmware type ID */ | ||
| 3834 | while (fw_table[fw_index].fw_type != priv->firmware_type | ||
| 3835 | && fw_table[fw_index].fw_type != ATMEL_FW_TYPE_NONE) | ||
| 3836 | fw_index++; | ||
| 3837 | |||
| 3838 | /* construct the actual firmware file name */ | ||
| 3839 | if (fw_table[fw_index].fw_type != ATMEL_FW_TYPE_NONE) { | ||
| 3840 | int i; | ||
| 3841 | for (i = 0; firmware_modifier[i]; i++) { | ||
| 3842 | snprintf(priv->firmware_id, 32, "%s%s.%s", fw_table[fw_index].fw_file, | ||
| 3843 | firmware_modifier[i], fw_table[fw_index].fw_file_ext); | ||
| 3844 | priv->firmware_id[31] = '\0'; | ||
| 3845 | if (request_firmware(&fw_entry, priv->firmware_id, priv->sys_dev) == 0) { | ||
| 3846 | success = 1; | ||
| 3847 | break; | ||
| 3848 | } | ||
| 3849 | } | ||
| 3850 | } | ||
| 3851 | if (!success) { | ||
| 3852 | printk(KERN_ALERT | ||
| 3853 | "%s: firmware %s is missing, cannot start.\n", | ||
| 3854 | dev->name, priv->firmware_id); | ||
| 3855 | priv->firmware_id[0] = '\0'; | ||
| 3856 | return -ENOENT; | ||
| 3857 | } | ||
| 3858 | } | ||
| 3859 | |||
| 3860 | fw = fw_entry->data; | ||
| 3861 | len = fw_entry->size; | ||
| 3862 | } | ||
| 3863 | |||
| 3864 | if (len <= 0x6000) { | ||
| 3865 | atmel_write16(priv->dev, BSR, BSS_IRAM); | ||
| 3866 | atmel_copy_to_card(priv->dev, 0, fw, len); | ||
| 3867 | atmel_set_gcr(priv->dev, GCR_REMAP); | ||
| 3868 | } else { | ||
| 3869 | /* Remap */ | ||
| 3870 | atmel_set_gcr(priv->dev, GCR_REMAP); | ||
| 3871 | atmel_write16(priv->dev, BSR, BSS_IRAM); | ||
| 3872 | atmel_copy_to_card(priv->dev, 0, fw, 0x6000); | ||
| 3873 | atmel_write16(priv->dev, BSR, 0x2ff); | ||
| 3874 | atmel_copy_to_card(priv->dev, 0x8000, &fw[0x6000], len - 0x6000); | ||
| 3875 | } | ||
| 3876 | |||
| 3877 | release_firmware(fw_entry); | ||
| 3878 | } | ||
| 3879 | |||
| 3880 | err = atmel_wakeup_firmware(priv); | ||
| 3881 | if (err != 0) | ||
| 3882 | return err; | ||
| 3883 | |||
| 3884 | /* Check the version and set the correct flag for wpa stuff, | ||
| 3885 | old and new firmware is incompatible. | ||
| 3886 | The pre-wpa 3com firmware reports major version 5, | ||
| 3887 | the wpa 3com firmware is major version 4 and doesn't need | ||
| 3888 | the 3com broken-ness filter. */ | ||
| 3889 | priv->use_wpa = (priv->host_info.major_version == 4); | ||
| 3890 | priv->radio_on_broken = (priv->host_info.major_version == 5); | ||
| 3891 | |||
| 3892 | /* unmask all irq sources */ | ||
| 3893 | atmel_wmem8(priv, atmel_hi(priv, IFACE_INT_MASK_OFFSET), 0xff); | ||
| 3894 | |||
| 3895 | /* int Tx system and enable Tx */ | ||
| 3896 | atmel_wmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, 0), 0); | ||
| 3897 | atmel_wmem32(priv, atmel_tx(priv, TX_DESC_NEXT_OFFSET, 0), 0x80000000L); | ||
| 3898 | atmel_wmem16(priv, atmel_tx(priv, TX_DESC_POS_OFFSET, 0), 0); | ||
| 3899 | atmel_wmem16(priv, atmel_tx(priv, TX_DESC_SIZE_OFFSET, 0), 0); | ||
| 3900 | |||
| 3901 | priv->tx_desc_free = priv->host_info.tx_desc_count; | ||
| 3902 | priv->tx_desc_head = 0; | ||
| 3903 | priv->tx_desc_tail = 0; | ||
| 3904 | priv->tx_desc_previous = 0; | ||
| 3905 | priv->tx_free_mem = priv->host_info.tx_buff_size; | ||
| 3906 | priv->tx_buff_head = 0; | ||
| 3907 | priv->tx_buff_tail = 0; | ||
| 3908 | |||
| 3909 | configuration = atmel_rmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET)); | ||
| 3910 | atmel_wmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET), | ||
| 3911 | configuration | FUNC_CTRL_TxENABLE); | ||
| 3912 | |||
| 3913 | /* init Rx system and enable */ | ||
| 3914 | priv->rx_desc_head = 0; | ||
| 3915 | |||
| 3916 | configuration = atmel_rmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET)); | ||
| 3917 | atmel_wmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET), | ||
| 3918 | configuration | FUNC_CTRL_RxENABLE); | ||
| 3919 | |||
| 3920 | if (!priv->radio_on_broken) { | ||
| 3921 | if (atmel_send_command_wait(priv, CMD_EnableRadio, NULL, 0) == | ||
| 3922 | CMD_STATUS_REJECTED_RADIO_OFF) { | ||
| 3923 | printk(KERN_INFO "%s: cannot turn the radio on.\n", | ||
| 3924 | dev->name); | ||
| 3925 | return -EIO; | ||
| 3926 | } | ||
| 3927 | } | ||
| 3928 | |||
| 3929 | /* set up enough MIB values to run. */ | ||
| 3930 | atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_AUTO_TX_RATE_POS, priv->auto_tx_rate); | ||
| 3931 | atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_TX_PROMISCUOUS_POS, PROM_MODE_OFF); | ||
| 3932 | atmel_set_mib16(priv, Mac_Mib_Type, MAC_MIB_RTS_THRESHOLD_POS, priv->rts_threshold); | ||
| 3933 | atmel_set_mib16(priv, Mac_Mib_Type, MAC_MIB_FRAG_THRESHOLD_POS, priv->frag_threshold); | ||
| 3934 | atmel_set_mib8(priv, Mac_Mib_Type, MAC_MIB_SHORT_RETRY_POS, priv->short_retry); | ||
| 3935 | atmel_set_mib8(priv, Mac_Mib_Type, MAC_MIB_LONG_RETRY_POS, priv->long_retry); | ||
| 3936 | atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_PREAMBLE_TYPE, priv->preamble); | ||
| 3937 | atmel_set_mib(priv, Mac_Address_Mib_Type, MAC_ADDR_MIB_MAC_ADDR_POS, | ||
| 3938 | priv->dev->dev_addr, 6); | ||
| 3939 | atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_PS_MODE_POS, ACTIVE_MODE); | ||
| 3940 | atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 1); | ||
| 3941 | atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_BEACON_PER_POS, priv->default_beacon_period); | ||
| 3942 | atmel_set_mib(priv, Phy_Mib_Type, PHY_MIB_RATE_SET_POS, atmel_basic_rates, 4); | ||
| 3943 | atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_CUR_PRIVACY_POS, priv->wep_is_on); | ||
| 3944 | if (priv->use_wpa) | ||
| 3945 | build_wpa_mib(priv); | ||
| 3946 | else | ||
| 3947 | build_wep_mib(priv); | ||
| 3948 | |||
| 3949 | if (old_state == STATION_STATE_READY) { | ||
| 3950 | union iwreq_data wrqu; | ||
| 3951 | |||
| 3952 | wrqu.data.length = 0; | ||
| 3953 | wrqu.data.flags = 0; | ||
| 3954 | wrqu.ap_addr.sa_family = ARPHRD_ETHER; | ||
| 3955 | eth_zero_addr(wrqu.ap_addr.sa_data); | ||
| 3956 | wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL); | ||
| 3957 | } | ||
| 3958 | |||
| 3959 | return 0; | ||
| 3960 | } | ||
| 3961 | |||
| 3962 | static void atmel_send_command(struct atmel_private *priv, int command, | ||
| 3963 | void *cmd, int cmd_size) | ||
| 3964 | { | ||
| 3965 | if (cmd) | ||
| 3966 | atmel_copy_to_card(priv->dev, atmel_co(priv, CMD_BLOCK_PARAMETERS_OFFSET), | ||
| 3967 | cmd, cmd_size); | ||
| 3968 | |||
| 3969 | atmel_wmem8(priv, atmel_co(priv, CMD_BLOCK_COMMAND_OFFSET), command); | ||
| 3970 | atmel_wmem8(priv, atmel_co(priv, CMD_BLOCK_STATUS_OFFSET), 0); | ||
| 3971 | } | ||
| 3972 | |||
| 3973 | static int atmel_send_command_wait(struct atmel_private *priv, int command, | ||
| 3974 | void *cmd, int cmd_size) | ||
| 3975 | { | ||
| 3976 | int i, status; | ||
| 3977 | |||
| 3978 | atmel_send_command(priv, command, cmd, cmd_size); | ||
| 3979 | |||
| 3980 | for (i = 5000; i; i--) { | ||
| 3981 | status = atmel_rmem8(priv, atmel_co(priv, CMD_BLOCK_STATUS_OFFSET)); | ||
| 3982 | if (status != CMD_STATUS_IDLE && | ||
| 3983 | status != CMD_STATUS_IN_PROGRESS) | ||
| 3984 | break; | ||
| 3985 | udelay(20); | ||
| 3986 | } | ||
| 3987 | |||
| 3988 | if (i == 0) { | ||
| 3989 | printk(KERN_ALERT "%s: failed to contact MAC.\n", priv->dev->name); | ||
| 3990 | status = CMD_STATUS_HOST_ERROR; | ||
| 3991 | } else { | ||
| 3992 | if (command != CMD_EnableRadio) | ||
| 3993 | status = CMD_STATUS_COMPLETE; | ||
| 3994 | } | ||
| 3995 | |||
| 3996 | return status; | ||
| 3997 | } | ||
| 3998 | |||
| 3999 | static u8 atmel_get_mib8(struct atmel_private *priv, u8 type, u8 index) | ||
| 4000 | { | ||
| 4001 | struct get_set_mib m; | ||
| 4002 | m.type = type; | ||
| 4003 | m.size = 1; | ||
| 4004 | m.index = index; | ||
| 4005 | |||
| 4006 | atmel_send_command_wait(priv, CMD_Get_MIB_Vars, &m, MIB_HEADER_SIZE + 1); | ||
| 4007 | return atmel_rmem8(priv, atmel_co(priv, CMD_BLOCK_PARAMETERS_OFFSET + MIB_HEADER_SIZE)); | ||
| 4008 | } | ||
| 4009 | |||
| 4010 | static void atmel_set_mib8(struct atmel_private *priv, u8 type, u8 index, u8 data) | ||
| 4011 | { | ||
| 4012 | struct get_set_mib m; | ||
| 4013 | m.type = type; | ||
| 4014 | m.size = 1; | ||
| 4015 | m.index = index; | ||
| 4016 | m.data[0] = data; | ||
| 4017 | |||
| 4018 | atmel_send_command_wait(priv, CMD_Set_MIB_Vars, &m, MIB_HEADER_SIZE + 1); | ||
| 4019 | } | ||
| 4020 | |||
| 4021 | static void atmel_set_mib16(struct atmel_private *priv, u8 type, u8 index, | ||
| 4022 | u16 data) | ||
| 4023 | { | ||
| 4024 | struct get_set_mib m; | ||
| 4025 | m.type = type; | ||
| 4026 | m.size = 2; | ||
| 4027 | m.index = index; | ||
| 4028 | m.data[0] = data; | ||
| 4029 | m.data[1] = data >> 8; | ||
| 4030 | |||
| 4031 | atmel_send_command_wait(priv, CMD_Set_MIB_Vars, &m, MIB_HEADER_SIZE + 2); | ||
| 4032 | } | ||
| 4033 | |||
| 4034 | static void atmel_set_mib(struct atmel_private *priv, u8 type, u8 index, | ||
| 4035 | const u8 *data, int data_len) | ||
| 4036 | { | ||
| 4037 | struct get_set_mib m; | ||
| 4038 | m.type = type; | ||
| 4039 | m.size = data_len; | ||
| 4040 | m.index = index; | ||
| 4041 | |||
| 4042 | if (data_len > MIB_MAX_DATA_BYTES) | ||
| 4043 | printk(KERN_ALERT "%s: MIB buffer too small.\n", priv->dev->name); | ||
| 4044 | |||
| 4045 | memcpy(m.data, data, data_len); | ||
| 4046 | atmel_send_command_wait(priv, CMD_Set_MIB_Vars, &m, MIB_HEADER_SIZE + data_len); | ||
| 4047 | } | ||
| 4048 | |||
| 4049 | static void atmel_get_mib(struct atmel_private *priv, u8 type, u8 index, | ||
| 4050 | u8 *data, int data_len) | ||
| 4051 | { | ||
| 4052 | struct get_set_mib m; | ||
| 4053 | m.type = type; | ||
| 4054 | m.size = data_len; | ||
| 4055 | m.index = index; | ||
| 4056 | |||
| 4057 | if (data_len > MIB_MAX_DATA_BYTES) | ||
| 4058 | printk(KERN_ALERT "%s: MIB buffer too small.\n", priv->dev->name); | ||
| 4059 | |||
| 4060 | atmel_send_command_wait(priv, CMD_Get_MIB_Vars, &m, MIB_HEADER_SIZE + data_len); | ||
| 4061 | atmel_copy_to_host(priv->dev, data, | ||
| 4062 | atmel_co(priv, CMD_BLOCK_PARAMETERS_OFFSET + MIB_HEADER_SIZE), data_len); | ||
| 4063 | } | ||
| 4064 | |||
| 4065 | static void atmel_writeAR(struct net_device *dev, u16 data) | ||
| 4066 | { | ||
| 4067 | int i; | ||
| 4068 | outw(data, dev->base_addr + AR); | ||
| 4069 | /* Address register appears to need some convincing..... */ | ||
| 4070 | for (i = 0; data != inw(dev->base_addr + AR) && i < 10; i++) | ||
| 4071 | outw(data, dev->base_addr + AR); | ||
| 4072 | } | ||
| 4073 | |||
| 4074 | static void atmel_copy_to_card(struct net_device *dev, u16 dest, | ||
| 4075 | const unsigned char *src, u16 len) | ||
| 4076 | { | ||
| 4077 | int i; | ||
| 4078 | atmel_writeAR(dev, dest); | ||
| 4079 | if (dest % 2) { | ||
| 4080 | atmel_write8(dev, DR, *src); | ||
| 4081 | src++; len--; | ||
| 4082 | } | ||
| 4083 | for (i = len; i > 1 ; i -= 2) { | ||
| 4084 | u8 lb = *src++; | ||
| 4085 | u8 hb = *src++; | ||
| 4086 | atmel_write16(dev, DR, lb | (hb << 8)); | ||
| 4087 | } | ||
| 4088 | if (i) | ||
| 4089 | atmel_write8(dev, DR, *src); | ||
| 4090 | } | ||
| 4091 | |||
| 4092 | static void atmel_copy_to_host(struct net_device *dev, unsigned char *dest, | ||
| 4093 | u16 src, u16 len) | ||
| 4094 | { | ||
| 4095 | int i; | ||
| 4096 | atmel_writeAR(dev, src); | ||
| 4097 | if (src % 2) { | ||
| 4098 | *dest = atmel_read8(dev, DR); | ||
| 4099 | dest++; len--; | ||
| 4100 | } | ||
| 4101 | for (i = len; i > 1 ; i -= 2) { | ||
| 4102 | u16 hw = atmel_read16(dev, DR); | ||
| 4103 | *dest++ = hw; | ||
| 4104 | *dest++ = hw >> 8; | ||
| 4105 | } | ||
| 4106 | if (i) | ||
| 4107 | *dest = atmel_read8(dev, DR); | ||
| 4108 | } | ||
| 4109 | |||
| 4110 | static void atmel_set_gcr(struct net_device *dev, u16 mask) | ||
| 4111 | { | ||
| 4112 | outw(inw(dev->base_addr + GCR) | mask, dev->base_addr + GCR); | ||
| 4113 | } | ||
| 4114 | |||
| 4115 | static void atmel_clear_gcr(struct net_device *dev, u16 mask) | ||
| 4116 | { | ||
| 4117 | outw(inw(dev->base_addr + GCR) & ~mask, dev->base_addr + GCR); | ||
| 4118 | } | ||
| 4119 | |||
| 4120 | static int atmel_lock_mac(struct atmel_private *priv) | ||
| 4121 | { | ||
| 4122 | int i, j = 20; | ||
| 4123 | retry: | ||
| 4124 | for (i = 5000; i; i--) { | ||
| 4125 | if (!atmel_rmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_HOST_OFFSET))) | ||
| 4126 | break; | ||
| 4127 | udelay(20); | ||
| 4128 | } | ||
| 4129 | |||
| 4130 | if (!i) | ||
| 4131 | return 0; /* timed out */ | ||
| 4132 | |||
| 4133 | atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 1); | ||
| 4134 | if (atmel_rmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_HOST_OFFSET))) { | ||
| 4135 | atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 0); | ||
| 4136 | if (!j--) | ||
| 4137 | return 0; /* timed out */ | ||
| 4138 | goto retry; | ||
| 4139 | } | ||
| 4140 | |||
| 4141 | return 1; | ||
| 4142 | } | ||
| 4143 | |||
| 4144 | static void atmel_wmem32(struct atmel_private *priv, u16 pos, u32 data) | ||
| 4145 | { | ||
| 4146 | atmel_writeAR(priv->dev, pos); | ||
| 4147 | atmel_write16(priv->dev, DR, data); /* card is little-endian */ | ||
| 4148 | atmel_write16(priv->dev, DR, data >> 16); | ||
| 4149 | } | ||
| 4150 | |||
| 4151 | /***************************************************************************/ | ||
| 4152 | /* There follows the source form of the MAC address reading firmware */ | ||
| 4153 | /***************************************************************************/ | ||
| 4154 | #if 0 | ||
| 4155 | |||
| 4156 | /* Copyright 2003 Matthew T. Russotto */ | ||
| 4157 | /* But derived from the Atmel 76C502 firmware written by Atmel and */ | ||
| 4158 | /* included in "atmel wireless lan drivers" package */ | ||
| 4159 | /* | ||
| 4160 | This file is part of net.russotto.AtmelMACFW, hereto referred to | ||
| 4161 | as AtmelMACFW | ||
| 4162 | |||
| 4163 | AtmelMACFW is free software; you can redistribute it and/or modify | ||
| 4164 | it under the terms of the GNU General Public License version 2 | ||
| 4165 | as published by the Free Software Foundation. | ||
| 4166 | |||
| 4167 | AtmelMACFW is distributed in the hope that it will be useful, | ||
| 4168 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 4169 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 4170 | GNU General Public License for more details. | ||
| 4171 | |||
| 4172 | You should have received a copy of the GNU General Public License | ||
| 4173 | along with AtmelMACFW; if not, see <http://www.gnu.org/licenses/>. | ||
| 4174 | |||
| 4175 | ****************************************************************************/ | ||
| 4176 | /* This firmware should work on the 76C502 RFMD, RFMD_D, and RFMD_E */ | ||
| 4177 | /* It will probably work on the 76C504 and 76C502 RFMD_3COM */ | ||
| 4178 | /* It only works on SPI EEPROM versions of the card. */ | ||
| 4179 | |||
| 4180 | /* This firmware initializes the SPI controller and clock, reads the MAC */ | ||
| 4181 | /* address from the EEPROM into SRAM, and puts the SRAM offset of the MAC */ | ||
| 4182 | /* address in MR2, and sets MR3 to 0x10 to indicate it is done */ | ||
| 4183 | /* It also puts a complete copy of the EEPROM in SRAM with the offset in */ | ||
| 4184 | /* MR4, for investigational purposes (maybe we can determine chip type */ | ||
| 4185 | /* from that?) */ | ||
| 4186 | |||
| 4187 | .org 0 | ||
| 4188 | .set MRBASE, 0x8000000 | ||
| 4189 | .set CPSR_INITIAL, 0xD3 /* IRQ/FIQ disabled, ARM mode, Supervisor state */ | ||
| 4190 | .set CPSR_USER, 0xD1 /* IRQ/FIQ disabled, ARM mode, USER state */ | ||
| 4191 | .set SRAM_BASE, 0x02000000 | ||
| 4192 | .set SP_BASE, 0x0F300000 | ||
| 4193 | .set UNK_BASE, 0x0F000000 /* Some internal device, but which one? */ | ||
| 4194 | .set SPI_CGEN_BASE, 0x0E000000 /* Some internal device, but which one? */ | ||
| 4195 | .set UNK3_BASE, 0x02014000 /* Some internal device, but which one? */ | ||
| 4196 | .set STACK_BASE, 0x5600 | ||
| 4197 | .set SP_SR, 0x10 | ||
| 4198 | .set SP_TDRE, 2 /* status register bit -- TDR empty */ | ||
| 4199 | .set SP_RDRF, 1 /* status register bit -- RDR full */ | ||
| 4200 | .set SP_SWRST, 0x80 | ||
| 4201 | .set SP_SPIEN, 0x1 | ||
| 4202 | .set SP_CR, 0 /* control register */ | ||
| 4203 | .set SP_MR, 4 /* mode register */ | ||
| 4204 | .set SP_RDR, 0x08 /* Read Data Register */ | ||
| 4205 | .set SP_TDR, 0x0C /* Transmit Data Register */ | ||
| 4206 | .set SP_CSR0, 0x30 /* chip select registers */ | ||
| 4207 | .set SP_CSR1, 0x34 | ||
| 4208 | .set SP_CSR2, 0x38 | ||
| 4209 | .set SP_CSR3, 0x3C | ||
| 4210 | .set NVRAM_CMD_RDSR, 5 /* read status register */ | ||
| 4211 | .set NVRAM_CMD_READ, 3 /* read data */ | ||
| 4212 | .set NVRAM_SR_RDY, 1 /* RDY bit. This bit is inverted */ | ||
| 4213 | .set SPI_8CLOCKS, 0xFF /* Writing this to the TDR doesn't do anything to the | ||
| 4214 | serial output, since SO is normally high. But it | ||
| 4215 | does cause 8 clock cycles and thus 8 bits to be | ||
| 4216 | clocked in to the chip. See Atmel's SPI | ||
| 4217 | controller (e.g. AT91M55800) timing and 4K | ||
| 4218 | SPI EEPROM manuals */ | ||
| 4219 | |||
| 4220 | .set NVRAM_SCRATCH, 0x02000100 /* arbitrary area for scratchpad memory */ | ||
| 4221 | .set NVRAM_IMAGE, 0x02000200 | ||
| 4222 | .set NVRAM_LENGTH, 0x0200 | ||
| 4223 | .set MAC_ADDRESS_MIB, SRAM_BASE | ||
| 4224 | .set MAC_ADDRESS_LENGTH, 6 | ||
| 4225 | .set MAC_BOOT_FLAG, 0x10 | ||
| 4226 | .set MR1, 0 | ||
| 4227 | .set MR2, 4 | ||
| 4228 | .set MR3, 8 | ||
| 4229 | .set MR4, 0xC | ||
| 4230 | RESET_VECTOR: | ||
| 4231 | b RESET_HANDLER | ||
| 4232 | UNDEF_VECTOR: | ||
| 4233 | b HALT1 | ||
| 4234 | SWI_VECTOR: | ||
| 4235 | b HALT1 | ||
| 4236 | IABORT_VECTOR: | ||
| 4237 | b HALT1 | ||
| 4238 | DABORT_VECTOR: | ||
| 4239 | RESERVED_VECTOR: | ||
| 4240 | b HALT1 | ||
| 4241 | IRQ_VECTOR: | ||
| 4242 | b HALT1 | ||
| 4243 | FIQ_VECTOR: | ||
| 4244 | b HALT1 | ||
| 4245 | HALT1: b HALT1 | ||
| 4246 | RESET_HANDLER: | ||
| 4247 | mov r0, #CPSR_INITIAL | ||
| 4248 | msr CPSR_c, r0 /* This is probably unnecessary */ | ||
| 4249 | |||
| 4250 | /* I'm guessing this is initializing clock generator electronics for SPI */ | ||
| 4251 | ldr r0, =SPI_CGEN_BASE | ||
| 4252 | mov r1, #0 | ||
| 4253 | mov r1, r1, lsl #3 | ||
| 4254 | orr r1, r1, #0 | ||
| 4255 | str r1, [r0] | ||
| 4256 | ldr r1, [r0, #28] | ||
| 4257 | bic r1, r1, #16 | ||
| 4258 | str r1, [r0, #28] | ||
| 4259 | mov r1, #1 | ||
| 4260 | str r1, [r0, #8] | ||
| 4261 | |||
| 4262 | ldr r0, =MRBASE | ||
| 4263 | mov r1, #0 | ||
| 4264 | strh r1, [r0, #MR1] | ||
| 4265 | strh r1, [r0, #MR2] | ||
| 4266 | strh r1, [r0, #MR3] | ||
| 4267 | strh r1, [r0, #MR4] | ||
| 4268 | |||
| 4269 | mov sp, #STACK_BASE | ||
| 4270 | bl SP_INIT | ||
| 4271 | mov r0, #10 | ||
| 4272 | bl DELAY9 | ||
| 4273 | bl GET_MAC_ADDR | ||
| 4274 | bl GET_WHOLE_NVRAM | ||
| 4275 | ldr r0, =MRBASE | ||
| 4276 | ldr r1, =MAC_ADDRESS_MIB | ||
| 4277 | strh r1, [r0, #MR2] | ||
| 4278 | ldr r1, =NVRAM_IMAGE | ||
| 4279 | strh r1, [r0, #MR4] | ||
| 4280 | mov r1, #MAC_BOOT_FLAG | ||
| 4281 | strh r1, [r0, #MR3] | ||
| 4282 | HALT2: b HALT2 | ||
| 4283 | .func Get_Whole_NVRAM, GET_WHOLE_NVRAM | ||
| 4284 | GET_WHOLE_NVRAM: | ||
| 4285 | stmdb sp!, {lr} | ||
| 4286 | mov r2, #0 /* 0th bytes of NVRAM */ | ||
| 4287 | mov r3, #NVRAM_LENGTH | ||
| 4288 | mov r1, #0 /* not used in routine */ | ||
| 4289 | ldr r0, =NVRAM_IMAGE | ||
| 4290 | bl NVRAM_XFER | ||
| 4291 | ldmia sp!, {lr} | ||
| 4292 | bx lr | ||
| 4293 | .endfunc | ||
| 4294 | |||
| 4295 | .func Get_MAC_Addr, GET_MAC_ADDR | ||
| 4296 | GET_MAC_ADDR: | ||
| 4297 | stmdb sp!, {lr} | ||
| 4298 | mov r2, #0x120 /* address of MAC Address within NVRAM */ | ||
| 4299 | mov r3, #MAC_ADDRESS_LENGTH | ||
| 4300 | mov r1, #0 /* not used in routine */ | ||
| 4301 | ldr r0, =MAC_ADDRESS_MIB | ||
| 4302 | bl NVRAM_XFER | ||
| 4303 | ldmia sp!, {lr} | ||
| 4304 | bx lr | ||
| 4305 | .endfunc | ||
| 4306 | .ltorg | ||
| 4307 | .func Delay9, DELAY9 | ||
| 4308 | DELAY9: | ||
| 4309 | adds r0, r0, r0, LSL #3 /* r0 = r0 * 9 */ | ||
| 4310 | DELAYLOOP: | ||
| 4311 | beq DELAY9_done | ||
| 4312 | subs r0, r0, #1 | ||
| 4313 | b DELAYLOOP | ||
| 4314 | DELAY9_done: | ||
| 4315 | bx lr | ||
| 4316 | .endfunc | ||
| 4317 | |||
| 4318 | .func SP_Init, SP_INIT | ||
| 4319 | SP_INIT: | ||
| 4320 | mov r1, #SP_SWRST | ||
| 4321 | ldr r0, =SP_BASE | ||
| 4322 | str r1, [r0, #SP_CR] /* reset the SPI */ | ||
| 4323 | mov r1, #0 | ||
| 4324 | str r1, [r0, #SP_CR] /* release SPI from reset state */ | ||
| 4325 | mov r1, #SP_SPIEN | ||
| 4326 | str r1, [r0, #SP_MR] /* set the SPI to MASTER mode*/ | ||
| 4327 | str r1, [r0, #SP_CR] /* enable the SPI */ | ||
| 4328 | |||
| 4329 | /* My guess would be this turns on the SPI clock */ | ||
| 4330 | ldr r3, =SPI_CGEN_BASE | ||
| 4331 | ldr r1, [r3, #28] | ||
| 4332 | orr r1, r1, #0x2000 | ||
| 4333 | str r1, [r3, #28] | ||
| 4334 | |||
| 4335 | ldr r1, =0x2000c01 | ||
| 4336 | str r1, [r0, #SP_CSR0] | ||
| 4337 | ldr r1, =0x2000201 | ||
| 4338 | str r1, [r0, #SP_CSR1] | ||
| 4339 | str r1, [r0, #SP_CSR2] | ||
| 4340 | str r1, [r0, #SP_CSR3] | ||
| 4341 | ldr r1, [r0, #SP_SR] | ||
| 4342 | ldr r0, [r0, #SP_RDR] | ||
| 4343 | bx lr | ||
| 4344 | .endfunc | ||
| 4345 | .func NVRAM_Init, NVRAM_INIT | ||
| 4346 | NVRAM_INIT: | ||
| 4347 | ldr r1, =SP_BASE | ||
| 4348 | ldr r0, [r1, #SP_RDR] | ||
| 4349 | mov r0, #NVRAM_CMD_RDSR | ||
| 4350 | str r0, [r1, #SP_TDR] | ||
| 4351 | SP_loop1: | ||
| 4352 | ldr r0, [r1, #SP_SR] | ||
| 4353 | tst r0, #SP_TDRE | ||
| 4354 | beq SP_loop1 | ||
| 4355 | |||
| 4356 | mov r0, #SPI_8CLOCKS | ||
| 4357 | str r0, [r1, #SP_TDR] | ||
| 4358 | SP_loop2: | ||
| 4359 | ldr r0, [r1, #SP_SR] | ||
| 4360 | tst r0, #SP_TDRE | ||
| 4361 | beq SP_loop2 | ||
| 4362 | |||
| 4363 | ldr r0, [r1, #SP_RDR] | ||
| 4364 | SP_loop3: | ||
| 4365 | ldr r0, [r1, #SP_SR] | ||
| 4366 | tst r0, #SP_RDRF | ||
| 4367 | beq SP_loop3 | ||
| 4368 | |||
| 4369 | ldr r0, [r1, #SP_RDR] | ||
| 4370 | and r0, r0, #255 | ||
| 4371 | bx lr | ||
| 4372 | .endfunc | ||
| 4373 | |||
| 4374 | .func NVRAM_Xfer, NVRAM_XFER | ||
| 4375 | /* r0 = dest address */ | ||
| 4376 | /* r1 = not used */ | ||
| 4377 | /* r2 = src address within NVRAM */ | ||
| 4378 | /* r3 = length */ | ||
| 4379 | NVRAM_XFER: | ||
| 4380 | stmdb sp!, {r4, r5, lr} | ||
| 4381 | mov r5, r0 /* save r0 (dest address) */ | ||
| 4382 | mov r4, r3 /* save r3 (length) */ | ||
| 4383 | mov r0, r2, LSR #5 /* SPI memories put A8 in the command field */ | ||
| 4384 | and r0, r0, #8 | ||
| 4385 | add r0, r0, #NVRAM_CMD_READ | ||
| 4386 | ldr r1, =NVRAM_SCRATCH | ||
| 4387 | strb r0, [r1, #0] /* save command in NVRAM_SCRATCH[0] */ | ||
| 4388 | strb r2, [r1, #1] /* save low byte of source address in NVRAM_SCRATCH[1] */ | ||
| 4389 | _local1: | ||
| 4390 | bl NVRAM_INIT | ||
| 4391 | tst r0, #NVRAM_SR_RDY | ||
| 4392 | bne _local1 | ||
| 4393 | mov r0, #20 | ||
| 4394 | bl DELAY9 | ||
| 4395 | mov r2, r4 /* length */ | ||
| 4396 | mov r1, r5 /* dest address */ | ||
| 4397 | mov r0, #2 /* bytes to transfer in command */ | ||
| 4398 | bl NVRAM_XFER2 | ||
| 4399 | ldmia sp!, {r4, r5, lr} | ||
| 4400 | bx lr | ||
| 4401 | .endfunc | ||
| 4402 | |||
| 4403 | .func NVRAM_Xfer2, NVRAM_XFER2 | ||
| 4404 | NVRAM_XFER2: | ||
| 4405 | stmdb sp!, {r4, r5, r6, lr} | ||
| 4406 | ldr r4, =SP_BASE | ||
| 4407 | mov r3, #0 | ||
| 4408 | cmp r0, #0 | ||
| 4409 | bls _local2 | ||
| 4410 | ldr r5, =NVRAM_SCRATCH | ||
| 4411 | _local4: | ||
| 4412 | ldrb r6, [r5, r3] | ||
| 4413 | str r6, [r4, #SP_TDR] | ||
| 4414 | _local3: | ||
| 4415 | ldr r6, [r4, #SP_SR] | ||
| 4416 | tst r6, #SP_TDRE | ||
| 4417 | beq _local3 | ||
| 4418 | add r3, r3, #1 | ||
| 4419 | cmp r3, r0 /* r0 is # of bytes to send out (command+addr) */ | ||
| 4420 | blo _local4 | ||
| 4421 | _local2: | ||
| 4422 | mov r3, #SPI_8CLOCKS | ||
| 4423 | str r3, [r4, #SP_TDR] | ||
| 4424 | ldr r0, [r4, #SP_RDR] | ||
| 4425 | _local5: | ||
| 4426 | ldr r0, [r4, #SP_SR] | ||
| 4427 | tst r0, #SP_RDRF | ||
| 4428 | beq _local5 | ||
| 4429 | ldr r0, [r4, #SP_RDR] /* what's this byte? It's the byte read while writing the TDR -- nonsense, because the NVRAM doesn't read and write at the same time */ | ||
| 4430 | mov r0, #0 | ||
| 4431 | cmp r2, #0 /* r2 is # of bytes to copy in */ | ||
| 4432 | bls _local6 | ||
| 4433 | _local7: | ||
| 4434 | ldr r5, [r4, #SP_SR] | ||
| 4435 | tst r5, #SP_TDRE | ||
| 4436 | beq _local7 | ||
| 4437 | str r3, [r4, #SP_TDR] /* r3 has SPI_8CLOCKS */ | ||
| 4438 | _local8: | ||
| 4439 | ldr r5, [r4, #SP_SR] | ||
| 4440 | tst r5, #SP_RDRF | ||
| 4441 | beq _local8 | ||
| 4442 | ldr r5, [r4, #SP_RDR] /* but didn't we read this byte above? */ | ||
| 4443 | strb r5, [r1], #1 /* postindexed */ | ||
| 4444 | add r0, r0, #1 | ||
| 4445 | cmp r0, r2 | ||
| 4446 | blo _local7 /* since we don't send another address, the NVRAM must be capable of sequential reads */ | ||
| 4447 | _local6: | ||
| 4448 | mov r0, #200 | ||
| 4449 | bl DELAY9 | ||
| 4450 | ldmia sp!, {r4, r5, r6, lr} | ||
| 4451 | bx lr | ||
| 4452 | #endif | ||
| @@ -1,31 +0,0 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ | ||
| 2 | /*** -*- linux-c -*- ********************************************************** | ||
| 3 | |||
| 4 | Driver for Atmel at76c502 at76c504 and at76c506 wireless cards. | ||
| 5 | |||
| 6 | Copyright 2005 Dan Williams and Red Hat, Inc. | ||
| 7 | |||
| 8 | |||
| 9 | ******************************************************************************/ | ||
| 10 | |||
| 11 | #ifndef _ATMEL_H | ||
| 12 | #define _ATMEL_H | ||
| 13 | |||
| 14 | typedef enum { | ||
| 15 | ATMEL_FW_TYPE_NONE = 0, | ||
| 16 | ATMEL_FW_TYPE_502, | ||
| 17 | ATMEL_FW_TYPE_502D, | ||
| 18 | ATMEL_FW_TYPE_502E, | ||
| 19 | ATMEL_FW_TYPE_502_3COM, | ||
| 20 | ATMEL_FW_TYPE_504, | ||
| 21 | ATMEL_FW_TYPE_504_2958, | ||
| 22 | ATMEL_FW_TYPE_504A_2958, | ||
| 23 | ATMEL_FW_TYPE_506 | ||
| 24 | } AtmelFWType; | ||
| 25 | |||
| 26 | struct net_device *init_atmel_card(unsigned short, unsigned long, const AtmelFWType, struct device *, | ||
| 27 | int (*present_func)(void *), void * ); | ||
| 28 | void stop_atmel_card( struct net_device *); | ||
| 29 | int atmel_open( struct net_device * ); | ||
| 30 | |||
| 31 | #endif | ||
| @@ -1,292 +0,0 @@ | |||
| 1 | /*** -*- linux-c -*- ********************************************************** | ||
| 2 | |||
| 3 | Driver for Atmel at76c502 at76c504 and at76c506 wireless cards. | ||
| 4 | |||
| 5 | Copyright 2000-2001 ATMEL Corporation. | ||
| 6 | Copyright 2003 Simon Kelley. | ||
| 7 | |||
| 8 | This code was developed from version 2.1.1 of the Atmel drivers, | ||
| 9 | released by Atmel corp. under the GPL in December 2002. It also | ||
| 10 | includes code from the Linux aironet drivers (C) Benjamin Reed, | ||
| 11 | and the Linux PCMCIA package, (C) David Hinds. | ||
| 12 | |||
| 13 | For all queries about this code, please contact the current author, | ||
| 14 | Simon Kelley <simon@thekelleys.org.uk> and not Atmel Corporation. | ||
| 15 | |||
| 16 | This program is free software; you can redistribute it and/or modify | ||
| 17 | it under the terms of the GNU General Public License as published by | ||
| 18 | the Free Software Foundation; either version 2 of the License, or | ||
| 19 | (at your option) any later version. | ||
| 20 | |||
| 21 | This software is distributed in the hope that it will be useful, | ||
| 22 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 23 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 24 | GNU General Public License for more details. | ||
| 25 | |||
| 26 | You should have received a copy of the GNU General Public License | ||
| 27 | along with Atmel wireless lan drivers; if not, see | ||
| 28 | <http://www.gnu.org/licenses/>. | ||
| 29 | |||
| 30 | ******************************************************************************/ | ||
| 31 | |||
| 32 | #ifdef __IN_PCMCIA_PACKAGE__ | ||
| 33 | #include <pcmcia/k_compat.h> | ||
| 34 | #endif | ||
| 35 | #include <linux/kernel.h> | ||
| 36 | #include <linux/module.h> | ||
| 37 | #include <linux/ptrace.h> | ||
| 38 | #include <linux/slab.h> | ||
| 39 | #include <linux/string.h> | ||
| 40 | #include <linux/netdevice.h> | ||
| 41 | #include <linux/moduleparam.h> | ||
| 42 | #include <linux/device.h> | ||
| 43 | |||
| 44 | #include <pcmcia/cistpl.h> | ||
| 45 | #include <pcmcia/cisreg.h> | ||
| 46 | #include <pcmcia/ds.h> | ||
| 47 | #include <pcmcia/ciscode.h> | ||
| 48 | |||
| 49 | #include <asm/io.h> | ||
| 50 | #include <linux/wireless.h> | ||
| 51 | |||
| 52 | #include "atmel.h" | ||
| 53 | |||
| 54 | |||
| 55 | /*====================================================================*/ | ||
| 56 | |||
| 57 | MODULE_AUTHOR("Simon Kelley"); | ||
| 58 | MODULE_DESCRIPTION("Support for Atmel at76c50x 802.11 wireless ethernet cards."); | ||
| 59 | MODULE_LICENSE("GPL"); | ||
| 60 | |||
| 61 | /*====================================================================*/ | ||
| 62 | |||
| 63 | static int atmel_config(struct pcmcia_device *link); | ||
| 64 | static void atmel_release(struct pcmcia_device *link); | ||
| 65 | |||
| 66 | static void atmel_detach(struct pcmcia_device *p_dev); | ||
| 67 | |||
| 68 | struct local_info { | ||
| 69 | struct net_device *eth_dev; | ||
| 70 | }; | ||
| 71 | |||
| 72 | static int atmel_probe(struct pcmcia_device *p_dev) | ||
| 73 | { | ||
| 74 | struct local_info *local; | ||
| 75 | int ret; | ||
| 76 | |||
| 77 | dev_dbg(&p_dev->dev, "atmel_attach()\n"); | ||
| 78 | |||
| 79 | /* Allocate space for private device-specific data */ | ||
| 80 | local = kzalloc(sizeof(*local), GFP_KERNEL); | ||
| 81 | if (!local) | ||
| 82 | return -ENOMEM; | ||
| 83 | |||
| 84 | p_dev->priv = local; | ||
| 85 | |||
| 86 | ret = atmel_config(p_dev); | ||
| 87 | if (ret) | ||
| 88 | goto err_free_priv; | ||
| 89 | |||
| 90 | return 0; | ||
| 91 | |||
| 92 | err_free_priv: | ||
| 93 | kfree(p_dev->priv); | ||
| 94 | return ret; | ||
| 95 | } | ||
| 96 | |||
| 97 | static void atmel_detach(struct pcmcia_device *link) | ||
| 98 | { | ||
| 99 | dev_dbg(&link->dev, "atmel_detach\n"); | ||
| 100 | |||
| 101 | atmel_release(link); | ||
| 102 | |||
| 103 | kfree(link->priv); | ||
| 104 | } | ||
| 105 | |||
| 106 | /* Call-back function to interrogate PCMCIA-specific information | ||
| 107 | about the current existence of the card */ | ||
| 108 | static int card_present(void *arg) | ||
| 109 | { | ||
| 110 | struct pcmcia_device *link = (struct pcmcia_device *)arg; | ||
| 111 | |||
| 112 | if (pcmcia_dev_present(link)) | ||
| 113 | return 1; | ||
| 114 | |||
| 115 | return 0; | ||
| 116 | } | ||
| 117 | |||
| 118 | static int atmel_config_check(struct pcmcia_device *p_dev, void *priv_data) | ||
| 119 | { | ||
| 120 | if (p_dev->config_index == 0) | ||
| 121 | return -EINVAL; | ||
| 122 | |||
| 123 | return pcmcia_request_io(p_dev); | ||
| 124 | } | ||
| 125 | |||
| 126 | static int atmel_config(struct pcmcia_device *link) | ||
| 127 | { | ||
| 128 | int ret; | ||
| 129 | const struct pcmcia_device_id *did; | ||
| 130 | |||
| 131 | did = dev_get_drvdata(&link->dev); | ||
| 132 | |||
| 133 | dev_dbg(&link->dev, "atmel_config\n"); | ||
| 134 | |||
| 135 | link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_VPP | | ||
| 136 | CONF_AUTO_AUDIO | CONF_AUTO_SET_IO; | ||
| 137 | |||
| 138 | if (pcmcia_loop_config(link, atmel_config_check, NULL)) | ||
| 139 | goto failed; | ||
| 140 | |||
| 141 | if (!link->irq) { | ||
| 142 | dev_err(&link->dev, "atmel: cannot assign IRQ: check that CONFIG_ISA is set in kernel config."); | ||
| 143 | goto failed; | ||
| 144 | } | ||
| 145 | |||
| 146 | ret = pcmcia_enable_device(link); | ||
| 147 | if (ret) | ||
| 148 | goto failed; | ||
| 149 | |||
| 150 | ((struct local_info *)link->priv)->eth_dev = | ||
| 151 | init_atmel_card(link->irq, | ||
| 152 | link->resource[0]->start, | ||
| 153 | did ? did->driver_info : ATMEL_FW_TYPE_NONE, | ||
| 154 | &link->dev, | ||
| 155 | card_present, | ||
| 156 | link); | ||
| 157 | if (!((struct local_info *)link->priv)->eth_dev) | ||
| 158 | goto failed; | ||
| 159 | |||
| 160 | |||
| 161 | return 0; | ||
| 162 | |||
| 163 | failed: | ||
| 164 | atmel_release(link); | ||
| 165 | return -ENODEV; | ||
| 166 | } | ||
| 167 | |||
| 168 | static void atmel_release(struct pcmcia_device *link) | ||
| 169 | { | ||
| 170 | struct net_device *dev = ((struct local_info *)link->priv)->eth_dev; | ||
| 171 | |||
| 172 | dev_dbg(&link->dev, "atmel_release\n"); | ||
| 173 | |||
| 174 | if (dev) | ||
| 175 | stop_atmel_card(dev); | ||
| 176 | ((struct local_info *)link->priv)->eth_dev = NULL; | ||
| 177 | |||
| 178 | pcmcia_disable_device(link); | ||
| 179 | } | ||
| 180 | |||
| 181 | static int atmel_suspend(struct pcmcia_device *link) | ||
| 182 | { | ||
| 183 | struct local_info *local = link->priv; | ||
| 184 | |||
| 185 | netif_device_detach(local->eth_dev); | ||
| 186 | |||
| 187 | return 0; | ||
| 188 | } | ||
| 189 | |||
| 190 | static int atmel_resume(struct pcmcia_device *link) | ||
| 191 | { | ||
| 192 | struct local_info *local = link->priv; | ||
| 193 | |||
| 194 | atmel_open(local->eth_dev); | ||
| 195 | netif_device_attach(local->eth_dev); | ||
| 196 | |||
| 197 | return 0; | ||
| 198 | } | ||
| 199 | |||
| 200 | /*====================================================================*/ | ||
| 201 | /* We use the driver_info field to store the correct firmware type for a card. */ | ||
| 202 | |||
| 203 | #define PCMCIA_DEVICE_MANF_CARD_INFO(manf, card, info) { \ | ||
| 204 | .match_flags = PCMCIA_DEV_ID_MATCH_MANF_ID| \ | ||
| 205 | PCMCIA_DEV_ID_MATCH_CARD_ID, \ | ||
| 206 | .manf_id = (manf), \ | ||
| 207 | .card_id = (card), \ | ||
| 208 | .driver_info = (kernel_ulong_t)(info), } | ||
| 209 | |||
| 210 | #define PCMCIA_DEVICE_PROD_ID12_INFO(v1, v2, vh1, vh2, info) { \ | ||
| 211 | .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \ | ||
| 212 | PCMCIA_DEV_ID_MATCH_PROD_ID2, \ | ||
| 213 | .prod_id = { (v1), (v2), NULL, NULL }, \ | ||
| 214 | .prod_id_hash = { (vh1), (vh2), 0, 0 }, \ | ||
| 215 | .driver_info = (kernel_ulong_t)(info), } | ||
| 216 | |||
| 217 | static const struct pcmcia_device_id atmel_ids[] = { | ||
| 218 | PCMCIA_DEVICE_MANF_CARD_INFO(0x0101, 0x0620, ATMEL_FW_TYPE_502_3COM), | ||
| 219 | PCMCIA_DEVICE_MANF_CARD_INFO(0x0101, 0x0696, ATMEL_FW_TYPE_502_3COM), | ||
| 220 | PCMCIA_DEVICE_MANF_CARD_INFO(0x01bf, 0x3302, ATMEL_FW_TYPE_502E), | ||
| 221 | PCMCIA_DEVICE_MANF_CARD_INFO(0xd601, 0x0007, ATMEL_FW_TYPE_502), | ||
| 222 | PCMCIA_DEVICE_PROD_ID12_INFO("11WAVE", "11WP611AL-E", 0x9eb2da1f, 0xc9a0d3f9, ATMEL_FW_TYPE_502E), | ||
| 223 | PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C502AR", 0xabda4164, 0x41b37e1f, ATMEL_FW_TYPE_502), | ||
| 224 | PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C502AR_D", 0xabda4164, 0x3675d704, ATMEL_FW_TYPE_502D), | ||
| 225 | PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C502AR_E", 0xabda4164, 0x4172e792, ATMEL_FW_TYPE_502E), | ||
| 226 | PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C504_R", 0xabda4164, 0x917f3d72, ATMEL_FW_TYPE_504_2958), | ||
| 227 | PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C504", 0xabda4164, 0x5040670a, ATMEL_FW_TYPE_504), | ||
| 228 | PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C504A", 0xabda4164, 0xe15ed87f, ATMEL_FW_TYPE_504A_2958), | ||
| 229 | PCMCIA_DEVICE_PROD_ID12_INFO("BT", "Voyager 1020 Laptop Adapter", 0xae49b86a, 0x1e957cd5, ATMEL_FW_TYPE_502), | ||
| 230 | PCMCIA_DEVICE_PROD_ID12_INFO("CNet", "CNWLC 11Mbps Wireless PC Card V-5", 0xbc477dde, 0x502fae6b, ATMEL_FW_TYPE_502E), | ||
| 231 | PCMCIA_DEVICE_PROD_ID12_INFO("IEEE 802.11b", "Wireless LAN PC Card", 0x5b878724, 0x122f1df6, ATMEL_FW_TYPE_502), | ||
| 232 | PCMCIA_DEVICE_PROD_ID12_INFO("IEEE 802.11b", "Wireless LAN Card S", 0x5b878724, 0x5fba533a, ATMEL_FW_TYPE_504_2958), | ||
| 233 | PCMCIA_DEVICE_PROD_ID12_INFO("OEM", "11Mbps Wireless LAN PC Card V-3", 0xfea54c90, 0x1c5b0f68, ATMEL_FW_TYPE_502), | ||
| 234 | PCMCIA_DEVICE_PROD_ID12_INFO("SMC", "2632W", 0xc4f8b18b, 0x30f38774, ATMEL_FW_TYPE_502D), | ||
| 235 | PCMCIA_DEVICE_PROD_ID12_INFO("SMC", "2632W-V2", 0xc4f8b18b, 0x172d1377, ATMEL_FW_TYPE_502), | ||
| 236 | PCMCIA_DEVICE_PROD_ID12_INFO("Wireless", "PC_CARD", 0xa407ecdd, 0x119f6314, ATMEL_FW_TYPE_502D), | ||
| 237 | PCMCIA_DEVICE_PROD_ID12_INFO("WLAN", "802.11b PC CARD", 0x575c516c, 0xb1f6dbc4, ATMEL_FW_TYPE_502D), | ||
| 238 | PCMCIA_DEVICE_PROD_ID12_INFO("LG", "LW2100N", 0xb474d43a, 0x6b1fec94, ATMEL_FW_TYPE_502E), | ||
| 239 | PCMCIA_DEVICE_NULL | ||
| 240 | }; | ||
| 241 | |||
| 242 | MODULE_DEVICE_TABLE(pcmcia, atmel_ids); | ||
| 243 | |||
| 244 | static struct pcmcia_driver atmel_driver = { | ||
| 245 | .owner = THIS_MODULE, | ||
| 246 | .name = "atmel_cs", | ||
| 247 | .probe = atmel_probe, | ||
| 248 | .remove = atmel_detach, | ||
| 249 | .id_table = atmel_ids, | ||
| 250 | .suspend = atmel_suspend, | ||
| 251 | .resume = atmel_resume, | ||
| 252 | }; | ||
| 253 | module_pcmcia_driver(atmel_driver); | ||
| 254 | |||
| 255 | /* | ||
| 256 | This program is free software; you can redistribute it and/or | ||
| 257 | modify it under the terms of the GNU General Public License | ||
| 258 | as published by the Free Software Foundation; either version 2 | ||
| 259 | of the License, or (at your option) any later version. | ||
| 260 | |||
| 261 | This program is distributed in the hope that it will be useful, | ||
| 262 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 263 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 264 | GNU General Public License for more details. | ||
| 265 | |||
| 266 | In addition: | ||
| 267 | |||
| 268 | Redistribution and use in source and binary forms, with or without | ||
| 269 | modification, are permitted provided that the following conditions | ||
| 270 | are met: | ||
| 271 | |||
| 272 | 1. Redistributions of source code must retain the above copyright | ||
| 273 | notice, this list of conditions and the following disclaimer. | ||
| 274 | 2. Redistributions in binary form must reproduce the above copyright | ||
| 275 | notice, this list of conditions and the following disclaimer in the | ||
| 276 | documentation and/or other materials provided with the distribution. | ||
| 277 | 3. The name of the author may not be used to endorse or promote | ||
| 278 | products derived from this software without specific prior written | ||
| 279 | permission. | ||
| 280 | |||
| 281 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | ||
| 282 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
| 283 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
| 284 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, | ||
| 285 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
| 286 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||
| 287 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
| 288 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | ||
| 289 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING | ||
| 290 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| 291 | POSSIBILITY OF SUCH DAMAGE. | ||
| 292 | */ | ||
| @@ -1,65 +0,0 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 2 | /*** -*- linux-c -*- ********************************************************** | ||
| 3 | |||
| 4 | Driver for Atmel at76c502 at76c504 and at76c506 wireless cards. | ||
| 5 | |||
| 6 | Copyright 2004 Simon Kelley. | ||
| 7 | |||
| 8 | |||
| 9 | ******************************************************************************/ | ||
| 10 | #include <linux/pci.h> | ||
| 11 | #include <linux/kernel.h> | ||
| 12 | #include <linux/module.h> | ||
| 13 | #include <linux/netdevice.h> | ||
| 14 | #include "atmel.h" | ||
| 15 | |||
| 16 | MODULE_AUTHOR("Simon Kelley"); | ||
| 17 | MODULE_DESCRIPTION("Support for Atmel at76c50x 802.11 wireless ethernet cards."); | ||
| 18 | MODULE_LICENSE("GPL"); | ||
| 19 | |||
| 20 | static const struct pci_device_id card_ids[] = { | ||
| 21 | { 0x1114, 0x0506, PCI_ANY_ID, PCI_ANY_ID }, | ||
| 22 | { 0, } | ||
| 23 | }; | ||
| 24 | |||
| 25 | MODULE_DEVICE_TABLE(pci, card_ids); | ||
| 26 | |||
| 27 | static int atmel_pci_probe(struct pci_dev *, const struct pci_device_id *); | ||
| 28 | static void atmel_pci_remove(struct pci_dev *); | ||
| 29 | |||
| 30 | static struct pci_driver atmel_driver = { | ||
| 31 | .name = "atmel", | ||
| 32 | .id_table = card_ids, | ||
| 33 | .probe = atmel_pci_probe, | ||
| 34 | .remove = atmel_pci_remove, | ||
| 35 | }; | ||
| 36 | |||
| 37 | |||
| 38 | static int atmel_pci_probe(struct pci_dev *pdev, | ||
| 39 | const struct pci_device_id *pent) | ||
| 40 | { | ||
| 41 | struct net_device *dev; | ||
| 42 | |||
| 43 | if (pci_enable_device(pdev)) | ||
| 44 | return -ENODEV; | ||
| 45 | |||
| 46 | pci_set_master(pdev); | ||
| 47 | |||
| 48 | dev = init_atmel_card(pdev->irq, pdev->resource[1].start, | ||
| 49 | ATMEL_FW_TYPE_506, | ||
| 50 | &pdev->dev, NULL, NULL); | ||
| 51 | if (!dev) { | ||
| 52 | pci_disable_device(pdev); | ||
| 53 | return -ENODEV; | ||
| 54 | } | ||
| 55 | |||
| 56 | pci_set_drvdata(pdev, dev); | ||
| 57 | return 0; | ||
| 58 | } | ||
| 59 | |||
| 60 | static void atmel_pci_remove(struct pci_dev *pdev) | ||
| 61 | { | ||
| 62 | stop_atmel_card(pci_get_drvdata(pdev)); | ||
| 63 | } | ||
| 64 | |||
| 65 | module_pci_driver(atmel_driver); | ||
| @@ -866,7 +866,7 @@ struct wireless_dev *brcmf_apsta_add_vif(struct wiphy *wiphy, const char *name, · 865 unmodified lines | |||
| 866 | goto fail; | 866 | goto fail; |
| 867 | } | 867 | } |
| 868 | 868 | ||
| 869 | strncpy(ifp->ndev->name, name, sizeof(ifp->ndev->name) - 1); | 869 | strscpy(ifp->ndev->name, name, sizeof(ifp->ndev->name)); |
| 870 | err = brcmf_net_attach(ifp, true); | 870 | err = brcmf_net_attach(ifp, true); |
| 871 | if (err) { | 871 | if (err) { |
| 872 | bphy_err(drvr, "Registering netdevice failed\n"); | 872 | bphy_err(drvr, "Registering netdevice failed\n"); |
| @@ -2334,7 +2334,7 @@ struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name, · 2333 unmodified lines | |||
| 2334 | goto fail; | 2334 | goto fail; |
| 2335 | } | 2335 | } |
| 2336 | 2336 | ||
| 2337 | strncpy(ifp->ndev->name, name, sizeof(ifp->ndev->name) - 1); | 2337 | strscpy(ifp->ndev->name, name, sizeof(ifp->ndev->name)); |
| 2338 | ifp->ndev->name_assign_type = name_assign_type; | 2338 | ifp->ndev->name_assign_type = name_assign_type; |
| 2339 | err = brcmf_net_attach(ifp, true); | 2339 | err = brcmf_net_attach(ifp, true); |
| 2340 | if (err) { | 2340 | if (err) { |
| @@ -341,7 +341,7 @@ struct brcms_cm_info *brcms_c_channel_mgr_attach(struct brcms_c_info *wlc) · 340 unmodified lines | |||
| 341 | /* store the country code for passing up as a regulatory hint */ | 341 | /* store the country code for passing up as a regulatory hint */ |
| 342 | wlc_cm->world_regd = brcms_world_regd(ccode, ccode_len); | 342 | wlc_cm->world_regd = brcms_world_regd(ccode, ccode_len); |
| 343 | if (brcms_c_country_valid(ccode)) | 343 | if (brcms_c_country_valid(ccode)) |
| 344 | strncpy(wlc->pub->srom_ccode, ccode, ccode_len); | 344 | memcpy(wlc->pub->srom_ccode, ccode, ccode_len); |
| 345 | 345 | ||
| 346 | /* | 346 | /* |
| 347 | * If no custom world domain is found in the SROM, use the | 347 | * If no custom world domain is found in the SROM, use the |
| @@ -354,10 +354,10 @@ struct brcms_cm_info *brcms_c_channel_mgr_attach(struct brcms_c_info *wlc) · 6 unmodified lines | |||
| 354 | } | 354 | } |
| 355 | 355 | ||
| 356 | /* save default country for exiting 11d regulatory mode */ | 356 | /* save default country for exiting 11d regulatory mode */ |
| 357 | strncpy(wlc->country_default, ccode, ccode_len); | 357 | memcpy(wlc->country_default, ccode, ccode_len); |
| 358 | 358 | ||
| 359 | /* initialize autocountry_default to driver default */ | 359 | /* initialize autocountry_default to driver default */ |
| 360 | strncpy(wlc->autocountry_default, ccode, ccode_len); | 360 | memcpy(wlc->autocountry_default, ccode, ccode_len); |
| 361 | 361 | ||
| 362 | brcms_c_set_country(wlc_cm, wlc_cm->world_regd); | 362 | brcms_c_set_country(wlc_cm, wlc_cm->world_regd); |
| 363 | 363 | ||
| @@ -584,8 +584,7 @@ struct dma_pub *dma_attach(char *name, struct brcms_c_info *wlc, · 583 unmodified lines | |||
| 584 | rxextheadroom, nrxpost, rxoffset, txregbase, rxregbase); | 584 | rxextheadroom, nrxpost, rxoffset, txregbase, rxregbase); |
| 585 | 585 | ||
| 586 | /* make a private copy of our callers name */ | 586 | /* make a private copy of our callers name */ |
| 587 | strncpy(di->name, name, MAXNAMEL); | 587 | strscpy(di->name, name, sizeof(di->name)); |
| 588 | di->name[MAXNAMEL - 1] = '\0'; | ||
| 589 | 588 | ||
| 590 | di->dmadev = core->dma_dev; | 589 | di->dmadev = core->dma_dev; |
| 591 | 590 | ||
| @@ -5551,8 +5551,8 @@ int brcms_c_module_register(struct brcms_pub *pub, · 5550 unmodified lines | |||
| 5551 | /* find an empty entry and just add, no duplication check! */ | 5551 | /* find an empty entry and just add, no duplication check! */ |
| 5552 | for (i = 0; i < BRCMS_MAXMODULES; i++) { | 5552 | for (i = 0; i < BRCMS_MAXMODULES; i++) { |
| 5553 | if (wlc->modulecb[i].name[0] == '\0') { | 5553 | if (wlc->modulecb[i].name[0] == '\0') { |
| 5554 | strncpy(wlc->modulecb[i].name, name, | 5554 | strscpy(wlc->modulecb[i].name, name, |
| 5555 | sizeof(wlc->modulecb[i].name) - 1); | 5555 | sizeof(wlc->modulecb[i].name)); |
| 5556 | wlc->modulecb[i].hdl = hdl; | 5556 | wlc->modulecb[i].hdl = hdl; |
| 5557 | wlc->modulecb[i].down_fn = d_fn; | 5557 | wlc->modulecb[i].down_fn = d_fn; |
| 5558 | return 0; | 5558 | return 0; |
| @@ -1,59 +0,0 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0-only | ||
| 2 | config WLAN_VENDOR_CISCO | ||
| 3 | bool "Cisco devices" | ||
| 4 | default y | ||
| 5 | help | ||
| 6 | If you have a wireless card belonging to this class, say Y. | ||
| 7 | |||
| 8 | Note that the answer to this question doesn't directly affect the | ||
| 9 | kernel: saying N will just cause the configurator to skip all the | ||
| 10 | questions about these cards. If you say Y, you will be asked for | ||
| 11 | your specific card in the following questions. | ||
| 12 | |||
| 13 | if WLAN_VENDOR_CISCO | ||
| 14 | |||
| 15 | config AIRO | ||
| 16 | tristate "Cisco/Aironet 34X/35X/4500/4800 ISA and PCI cards" | ||
| 17 | depends on CFG80211 && (PCI || BROKEN) | ||
| 18 | select WIRELESS_EXT | ||
| 19 | select CRYPTO | ||
| 20 | select CRYPTO_SKCIPHER | ||
| 21 | select WEXT_SPY | ||
| 22 | select WEXT_PRIV | ||
| 23 | help | ||
| 24 | This is the standard Linux driver to support Cisco/Aironet ISA and | ||
| 25 | PCI 802.11 wireless cards. | ||
| 26 | It supports the new 802.11b cards from Cisco (Cisco 34X, Cisco 35X | ||
| 27 | - with or without encryption) as well as card before the Cisco | ||
| 28 | acquisition (Aironet 4500, Aironet 4800, Aironet 4800B). | ||
| 29 | |||
| 30 | This driver support both the standard Linux Wireless Extensions | ||
| 31 | and Cisco proprietary API, so both the Linux Wireless Tools and the | ||
| 32 | Cisco Linux utilities can be used to configure the card. | ||
| 33 | |||
| 34 | The driver can be compiled as a module and will be named "airo". | ||
| 35 | |||
| 36 | config AIRO_CS | ||
| 37 | tristate "Cisco/Aironet 34X/35X/4500/4800 PCMCIA cards" | ||
| 38 | depends on CFG80211 && PCMCIA | ||
| 39 | select WIRELESS_EXT | ||
| 40 | select WEXT_SPY | ||
| 41 | select WEXT_PRIV | ||
| 42 | select CRYPTO | ||
| 43 | select CRYPTO_AES | ||
| 44 | select CRYPTO_CTR | ||
| 45 | help | ||
| 46 | This is the standard Linux driver to support Cisco/Aironet PCMCIA | ||
| 47 | 802.11 wireless cards. This driver is the same as the Aironet | ||
| 48 | driver part of the Linux Pcmcia package. | ||
| 49 | It supports the new 802.11b cards from Cisco (Cisco 34X, Cisco 35X | ||
| 50 | - with or without encryption) as well as card before the Cisco | ||
| 51 | acquisition (Aironet 4500, Aironet 4800, Aironet 4800B). It also | ||
| 52 | supports OEM of Cisco such as the DELL TrueMobile 4800 and Xircom | ||
| 53 | 802.11b cards. | ||
| 54 | |||
| 55 | This driver support both the standard Linux Wireless Extensions | ||
| 56 | and Cisco proprietary API, so both the Linux Wireless Tools and the | ||
| 57 | Cisco Linux utilities can be used to configure the card. | ||
| 58 | |||
| 59 | endif # WLAN_VENDOR_CISCO | ||
| @@ -1,3 +0,0 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0-only | ||
| 2 | obj-$(CONFIG_AIRO) += airo.o | ||
| 3 | obj-$(CONFIG_AIRO_CS) += airo_cs.o airo.o | ||
| @@ -1,8288 +0,0 @@ | |||
| 1 | /*====================================================================== | ||
| 2 | |||
| 3 | Aironet driver for 4500 and 4800 series cards | ||
| 4 | |||
| 5 | This code is released under both the GPL version 2 and BSD licenses. | ||
| 6 | Either license may be used. The respective licenses are found at | ||
| 7 | the end of this file. | ||
| 8 | |||
| 9 | This code was developed by Benjamin Reed <breed@users.sourceforge.net> | ||
| 10 | including portions of which come from the Aironet PC4500 | ||
| 11 | Developer's Reference Manual and used with permission. Copyright | ||
| 12 | (C) 1999 Benjamin Reed. All Rights Reserved. Permission to use | ||
| 13 | code in the Developer's manual was granted for this driver by | ||
| 14 | Aironet. Major code contributions were received from Javier Achirica | ||
| 15 | <achirica@users.sourceforge.net> and Jean Tourrilhes <jt@hpl.hp.com>. | ||
| 16 | Code was also integrated from the Cisco Aironet driver for Linux. | ||
| 17 | Support for MPI350 cards was added by Fabrice Bellet | ||
| 18 | <fabrice@bellet.info>. | ||
| 19 | |||
| 20 | ======================================================================*/ | ||
| 21 | |||
| 22 | #include <linux/err.h> | ||
| 23 | #include <linux/init.h> | ||
| 24 | |||
| 25 | #include <linux/kernel.h> | ||
| 26 | #include <linux/module.h> | ||
| 27 | #include <linux/proc_fs.h> | ||
| 28 | |||
| 29 | #include <linux/sched.h> | ||
| 30 | #include <linux/ptrace.h> | ||
| 31 | #include <linux/slab.h> | ||
| 32 | #include <linux/string.h> | ||
| 33 | #include <linux/timer.h> | ||
| 34 | #include <linux/interrupt.h> | ||
| 35 | #include <linux/in.h> | ||
| 36 | #include <linux/bitops.h> | ||
| 37 | #include <linux/scatterlist.h> | ||
| 38 | #include <linux/crypto.h> | ||
| 39 | #include <linux/io.h> | ||
| 40 | #include <asm/unaligned.h> | ||
| 41 | |||
| 42 | #include <linux/netdevice.h> | ||
| 43 | #include <linux/etherdevice.h> | ||
| 44 | #include <linux/skbuff.h> | ||
| 45 | #include <linux/if_arp.h> | ||
| 46 | #include <linux/ioport.h> | ||
| 47 | #include <linux/pci.h> | ||
| 48 | #include <linux/uaccess.h> | ||
| 49 | #include <linux/kthread.h> | ||
| 50 | #include <linux/freezer.h> | ||
| 51 | |||
| 52 | #include <crypto/aes.h> | ||
| 53 | #include <crypto/skcipher.h> | ||
| 54 | |||
| 55 | #include <net/cfg80211.h> | ||
| 56 | #include <net/iw_handler.h> | ||
| 57 | |||
| 58 | #include "airo.h" | ||
| 59 | |||
| 60 | #define DRV_NAME "airo" | ||
| 61 | |||
| 62 | #ifdef CONFIG_PCI | ||
| 63 | static const struct pci_device_id card_ids[] = { | ||
| 64 | { 0x14b9, 1, PCI_ANY_ID, PCI_ANY_ID, }, | ||
| 65 | { 0x14b9, 0x4500, PCI_ANY_ID, PCI_ANY_ID }, | ||
| 66 | { 0x14b9, 0x4800, PCI_ANY_ID, PCI_ANY_ID, }, | ||
| 67 | { 0x14b9, 0x0340, PCI_ANY_ID, PCI_ANY_ID, }, | ||
| 68 | { 0x14b9, 0x0350, PCI_ANY_ID, PCI_ANY_ID, }, | ||
| 69 | { 0x14b9, 0x5000, PCI_ANY_ID, PCI_ANY_ID, }, | ||
| 70 | { 0x14b9, 0xa504, PCI_ANY_ID, PCI_ANY_ID, }, | ||
| 71 | { 0, } | ||
| 72 | }; | ||
| 73 | MODULE_DEVICE_TABLE(pci, card_ids); | ||
| 74 | |||
| 75 | static int airo_pci_probe(struct pci_dev *, const struct pci_device_id *); | ||
| 76 | static void airo_pci_remove(struct pci_dev *); | ||
| 77 | static int __maybe_unused airo_pci_suspend(struct device *dev); | ||
| 78 | static int __maybe_unused airo_pci_resume(struct device *dev); | ||
| 79 | |||
| 80 | static SIMPLE_DEV_PM_OPS(airo_pci_pm_ops, | ||
| 81 | airo_pci_suspend, | ||
| 82 | airo_pci_resume); | ||
| 83 | |||
| 84 | static struct pci_driver airo_driver = { | ||
| 85 | .name = DRV_NAME, | ||
| 86 | .id_table = card_ids, | ||
| 87 | .probe = airo_pci_probe, | ||
| 88 | .remove = airo_pci_remove, | ||
| 89 | .driver.pm = &airo_pci_pm_ops, | ||
| 90 | }; | ||
| 91 | #endif /* CONFIG_PCI */ | ||
| 92 | |||
| 93 | /* Include Wireless Extension definition and check version - Jean II */ | ||
| 94 | #include <linux/wireless.h> | ||
| 95 | #define WIRELESS_SPY /* enable iwspy support */ | ||
| 96 | |||
| 97 | #define CISCO_EXT /* enable Cisco extensions */ | ||
| 98 | #ifdef CISCO_EXT | ||
| 99 | #include <linux/delay.h> | ||
| 100 | #endif | ||
| 101 | |||
| 102 | /* Hack to do some power saving */ | ||
| 103 | #define POWER_ON_DOWN | ||
| 104 | |||
| 105 | /* As you can see this list is HUGH! | ||
| 106 | I really don't know what a lot of these counts are about, but they | ||
| 107 | are all here for completeness. If the IGNLABEL macro is put in | ||
| 108 | infront of the label, that statistic will not be included in the list | ||
| 109 | of statistics in the /proc filesystem */ | ||
| 110 | |||
| 111 | #define IGNLABEL(comment) NULL | ||
| 112 | static const char *statsLabels[] = { | ||
| 113 | "RxOverrun", | ||
| 114 | IGNLABEL("RxPlcpCrcErr"), | ||
| 115 | IGNLABEL("RxPlcpFormatErr"), | ||
| 116 | IGNLABEL("RxPlcpLengthErr"), | ||
| 117 | "RxMacCrcErr", | ||
| 118 | "RxMacCrcOk", | ||
| 119 | "RxWepErr", | ||
| 120 | "RxWepOk", | ||
| 121 | "RetryLong", | ||
| 122 | "RetryShort", | ||
| 123 | "MaxRetries", | ||
| 124 | "NoAck", | ||
| 125 | "NoCts", | ||
| 126 | "RxAck", | ||
| 127 | "RxCts", | ||
| 128 | "TxAck", | ||
| 129 | "TxRts", | ||
| 130 | "TxCts", | ||
| 131 | "TxMc", | ||
| 132 | "TxBc", | ||
| 133 | "TxUcFrags", | ||
| 134 | "TxUcPackets", | ||
| 135 | "TxBeacon", | ||
| 136 | "RxBeacon", | ||
| 137 | "TxSinColl", | ||
| 138 | "TxMulColl", | ||
| 139 | "DefersNo", | ||
| 140 | "DefersProt", | ||
| 141 | "DefersEngy", | ||
| 142 | "DupFram", | ||
| 143 | "RxFragDisc", | ||
| 144 | "TxAged", | ||
| 145 | "RxAged", | ||
| 146 | "LostSync-MaxRetry", | ||
| 147 | "LostSync-MissedBeacons", | ||
| 148 | "LostSync-ArlExceeded", | ||
| 149 | "LostSync-Deauth", | ||
| 150 | "LostSync-Disassoced", | ||
| 151 | "LostSync-TsfTiming", | ||
| 152 | "HostTxMc", | ||
| 153 | "HostTxBc", | ||
| 154 | "HostTxUc", | ||
| 155 | "HostTxFail", | ||
| 156 | "HostRxMc", | ||
| 157 | "HostRxBc", | ||
| 158 | "HostRxUc", | ||
| 159 | "HostRxDiscard", | ||
| 160 | IGNLABEL("HmacTxMc"), | ||
| 161 | IGNLABEL("HmacTxBc"), | ||
| 162 | IGNLABEL("HmacTxUc"), | ||
| 163 | IGNLABEL("HmacTxFail"), | ||
| 164 | IGNLABEL("HmacRxMc"), | ||
| 165 | IGNLABEL("HmacRxBc"), | ||
| 166 | IGNLABEL("HmacRxUc"), | ||
| 167 | IGNLABEL("HmacRxDiscard"), | ||
| 168 | IGNLABEL("HmacRxAccepted"), | ||
| 169 | "SsidMismatch", | ||
| 170 | "ApMismatch", | ||
| 171 | "RatesMismatch", | ||
| 172 | "AuthReject", | ||
| 173 | "AuthTimeout", | ||
| 174 | "AssocReject", | ||
| 175 | "AssocTimeout", | ||
| 176 | IGNLABEL("ReasonOutsideTable"), | ||
| 177 | IGNLABEL("ReasonStatus1"), | ||
| 178 | IGNLABEL("ReasonStatus2"), | ||
| 179 | IGNLABEL("ReasonStatus3"), | ||
| 180 | IGNLABEL("ReasonStatus4"), | ||
| 181 | IGNLABEL("ReasonStatus5"), | ||
| 182 | IGNLABEL("ReasonStatus6"), | ||
| 183 | IGNLABEL("ReasonStatus7"), | ||
| 184 | IGNLABEL("ReasonStatus8"), | ||
| 185 | IGNLABEL("ReasonStatus9"), | ||
| 186 | IGNLABEL("ReasonStatus10"), | ||
| 187 | IGNLABEL("ReasonStatus11"), | ||
| 188 | IGNLABEL("ReasonStatus12"), | ||
| 189 | IGNLABEL("ReasonStatus13"), | ||
| 190 | IGNLABEL("ReasonStatus14"), | ||
| 191 | IGNLABEL("ReasonStatus15"), | ||
| 192 | IGNLABEL("ReasonStatus16"), | ||
| 193 | IGNLABEL("ReasonStatus17"), | ||
| 194 | IGNLABEL("ReasonStatus18"), | ||
| 195 | IGNLABEL("ReasonStatus19"), | ||
| 196 | "RxMan", | ||
| 197 | "TxMan", | ||
| 198 | "RxRefresh", | ||
| 199 | "TxRefresh", | ||
| 200 | "RxPoll", | ||
| 201 | "TxPoll", | ||
| 202 | "HostRetries", | ||
| 203 | "LostSync-HostReq", | ||
| 204 | "HostTxBytes", | ||
| 205 | "HostRxBytes", | ||
| 206 | "ElapsedUsec", | ||
| 207 | "ElapsedSec", | ||
| 208 | "LostSyncBetterAP", | ||
| 209 | "PrivacyMismatch", | ||
| 210 | "Jammed", | ||
| 211 | "DiscRxNotWepped", | ||
| 212 | "PhyEleMismatch", | ||
| 213 | (char*)-1 }; | ||
| 214 | #ifndef RUN_AT | ||
| 215 | #define RUN_AT(x) (jiffies+(x)) | ||
| 216 | #endif | ||
| 217 | |||
| 218 | |||
| 219 | /* These variables are for insmod, since it seems that the rates | ||
| 220 | can only be set in setup_card. Rates should be a comma separated | ||
| 221 | (no spaces) list of rates (up to 8). */ | ||
| 222 | |||
| 223 | static int rates[8]; | ||
| 224 | static char *ssids[3]; | ||
| 225 | |||
| 226 | static int io[4]; | ||
| 227 | static int irq[4]; | ||
| 228 | |||
| 229 | static | ||
| 230 | int maxencrypt /* = 0 */; /* The highest rate that the card can encrypt at. | ||
| 231 | 0 means no limit. For old cards this was 4 */ | ||
| 232 | |||
| 233 | static int auto_wep /* = 0 */; /* If set, it tries to figure out the wep mode */ | ||
| 234 | static int aux_bap /* = 0 */; /* Checks to see if the aux ports are needed to read | ||
| 235 | the bap, needed on some older cards and buses. */ | ||
| 236 | static int adhoc; | ||
| 237 | |||
| 238 | static int probe = 1; | ||
| 239 | |||
| 240 | static kuid_t proc_kuid; | ||
| 241 | static int proc_uid /* = 0 */; | ||
| 242 | |||
| 243 | static kgid_t proc_kgid; | ||
| 244 | static int proc_gid /* = 0 */; | ||
| 245 | |||
| 246 | static int airo_perm = 0555; | ||
| 247 | |||
| 248 | static int proc_perm = 0644; | ||
| 249 | |||
| 250 | MODULE_AUTHOR("Benjamin Reed"); | ||
| 251 | MODULE_DESCRIPTION("Support for Cisco/Aironet 802.11 wireless ethernet cards. " | ||
| 252 | "Direct support for ISA/PCI/MPI cards and support for PCMCIA when used with airo_cs."); | ||
| 253 | MODULE_LICENSE("Dual BSD/GPL"); | ||
| 254 | module_param_hw_array(io, int, ioport, NULL, 0); | ||
| 255 | module_param_hw_array(irq, int, irq, NULL, 0); | ||
| 256 | module_param_array(rates, int, NULL, 0); | ||
| 257 | module_param_array(ssids, charp, NULL, 0); | ||
| 258 | module_param(auto_wep, int, 0); | ||
| 259 | MODULE_PARM_DESC(auto_wep, | ||
| 260 | "If non-zero, the driver will keep looping through the authentication options until an association is made. " | ||
| 261 | "The value of auto_wep is number of the wep keys to check. " | ||
| 262 | "A value of 2 will try using the key at index 0 and index 1."); | ||
| 263 | module_param(aux_bap, int, 0); | ||
| 264 | MODULE_PARM_DESC(aux_bap, | ||
| 265 | "If non-zero, the driver will switch into a mode that seems to work better for older cards with some older buses. " | ||
| 266 | "Before switching it checks that the switch is needed."); | ||
| 267 | module_param(maxencrypt, int, 0); | ||
| 268 | MODULE_PARM_DESC(maxencrypt, | ||
| 269 | "The maximum speed that the card can do encryption. " | ||
| 270 | "Units are in 512kbs. " | ||
| 271 | "Zero (default) means there is no limit. " | ||
| 272 | "Older cards used to be limited to 2mbs (4)."); | ||
| 273 | module_param(adhoc, int, 0); | ||
| 274 | MODULE_PARM_DESC(adhoc, "If non-zero, the card will start in adhoc mode."); | ||
| 275 | module_param(probe, int, 0); | ||
| 276 | MODULE_PARM_DESC(probe, "If zero, the driver won't start the card."); | ||
| 277 | |||
| 278 | module_param(proc_uid, int, 0); | ||
| 279 | MODULE_PARM_DESC(proc_uid, "The uid that the /proc files will belong to."); | ||
| 280 | module_param(proc_gid, int, 0); | ||
| 281 | MODULE_PARM_DESC(proc_gid, "The gid that the /proc files will belong to."); | ||
| 282 | module_param(airo_perm, int, 0); | ||
| 283 | MODULE_PARM_DESC(airo_perm, "The permission bits of /proc/[driver/]aironet."); | ||
| 284 | module_param(proc_perm, int, 0); | ||
| 285 | MODULE_PARM_DESC(proc_perm, "The permission bits of the files in /proc"); | ||
| 286 | |||
| 287 | /* This is a kind of sloppy hack to get this information to OUT4500 and | ||
| 288 | IN4500. I would be extremely interested in the situation where this | ||
| 289 | doesn't work though!!! */ | ||
| 290 | static int do8bitIO /* = 0 */; | ||
| 291 | |||
| 292 | /* Return codes */ | ||
| 293 | #define SUCCESS 0 | ||
| 294 | #define ERROR -1 | ||
| 295 | #define NO_PACKET -2 | ||
| 296 | |||
| 297 | /* Commands */ | ||
| 298 | #define NOP2 0x0000 | ||
| 299 | #define MAC_ENABLE 0x0001 | ||
| 300 | #define MAC_DISABLE 0x0002 | ||
| 301 | #define CMD_LOSE_SYNC 0x0003 /* Not sure what this does... */ | ||
| 302 | #define CMD_SOFTRESET 0x0004 | ||
| 303 | #define HOSTSLEEP 0x0005 | ||
| 304 | #define CMD_MAGIC_PKT 0x0006 | ||
| 305 | #define CMD_SETWAKEMASK 0x0007 | ||
| 306 | #define CMD_READCFG 0x0008 | ||
| 307 | #define CMD_SETMODE 0x0009 | ||
| 308 | #define CMD_ALLOCATETX 0x000a | ||
| 309 | #define CMD_TRANSMIT 0x000b | ||
| 310 | #define CMD_DEALLOCATETX 0x000c | ||
| 311 | #define NOP 0x0010 | ||
| 312 | #define CMD_WORKAROUND 0x0011 | ||
| 313 | #define CMD_ALLOCATEAUX 0x0020 | ||
| 314 | #define CMD_ACCESS 0x0021 | ||
| 315 | #define CMD_PCIBAP 0x0022 | ||
| 316 | #define CMD_PCIAUX 0x0023 | ||
| 317 | #define CMD_ALLOCBUF 0x0028 | ||
| 318 | #define CMD_GETTLV 0x0029 | ||
| 319 | #define CMD_PUTTLV 0x002a | ||
| 320 | #define CMD_DELTLV 0x002b | ||
| 321 | #define CMD_FINDNEXTTLV 0x002c | ||
| 322 | #define CMD_PSPNODES 0x0030 | ||
| 323 | #define CMD_SETCW 0x0031 | ||
| 324 | #define CMD_SETPCF 0x0032 | ||
| 325 | #define CMD_SETPHYREG 0x003e | ||
| 326 | #define CMD_TXTEST 0x003f | ||
| 327 | #define MAC_ENABLETX 0x0101 | ||
| 328 | #define CMD_LISTBSS 0x0103 | ||
| 329 | #define CMD_SAVECFG 0x0108 | ||
| 330 | #define CMD_ENABLEAUX 0x0111 | ||
| 331 | #define CMD_WRITERID 0x0121 | ||
| 332 | #define CMD_USEPSPNODES 0x0130 | ||
| 333 | #define MAC_ENABLERX 0x0201 | ||
| 334 | |||
| 335 | /* Command errors */ | ||
| 336 | #define ERROR_QUALIF 0x00 | ||
| 337 | #define ERROR_ILLCMD 0x01 | ||
| 338 | #define ERROR_ILLFMT 0x02 | ||
| 339 | #define ERROR_INVFID 0x03 | ||
| 340 | #define ERROR_INVRID 0x04 | ||
| 341 | #define ERROR_LARGE 0x05 | ||
| 342 | #define ERROR_NDISABL 0x06 | ||
| 343 | #define ERROR_ALLOCBSY 0x07 | ||
| 344 | #define ERROR_NORD 0x0B | ||
| 345 | #define ERROR_NOWR 0x0C | ||
| 346 | #define ERROR_INVFIDTX 0x0D | ||
| 347 | #define ERROR_TESTACT 0x0E | ||
| 348 | #define ERROR_TAGNFND 0x12 | ||
| 349 | #define ERROR_DECODE 0x20 | ||
| 350 | #define ERROR_DESCUNAV 0x21 | ||
| 351 | #define ERROR_BADLEN 0x22 | ||
| 352 | #define ERROR_MODE 0x80 | ||
| 353 | #define ERROR_HOP 0x81 | ||
| 354 | #define ERROR_BINTER 0x82 | ||
| 355 | #define ERROR_RXMODE 0x83 | ||
| 356 | #define ERROR_MACADDR 0x84 | ||
| 357 | #define ERROR_RATES 0x85 | ||
| 358 | #define ERROR_ORDER 0x86 | ||
| 359 | #define ERROR_SCAN 0x87 | ||
| 360 | #define ERROR_AUTH 0x88 | ||
| 361 | #define ERROR_PSMODE 0x89 | ||
| 362 | #define ERROR_RTYPE 0x8A | ||
| 363 | #define ERROR_DIVER 0x8B | ||
| 364 | #define ERROR_SSID 0x8C | ||
| 365 | #define ERROR_APLIST 0x8D | ||
| 366 | #define ERROR_AUTOWAKE 0x8E | ||
| 367 | #define ERROR_LEAP 0x8F | ||
| 368 | |||
| 369 | /* Registers */ | ||
| 370 | #define COMMAND 0x00 | ||
| 371 | #define PARAM0 0x02 | ||
| 372 | #define PARAM1 0x04 | ||
| 373 | #define PARAM2 0x06 | ||
| 374 | #define STATUS 0x08 | ||
| 375 | #define RESP0 0x0a | ||
| 376 | #define RESP1 0x0c | ||
| 377 | #define RESP2 0x0e | ||
| 378 | #define LINKSTAT 0x10 | ||
| 379 | #define SELECT0 0x18 | ||
| 380 | #define OFFSET0 0x1c | ||
| 381 | #define RXFID 0x20 | ||
| 382 | #define TXALLOCFID 0x22 | ||
| 383 | #define TXCOMPLFID 0x24 | ||
| 384 | #define DATA0 0x36 | ||
| 385 | #define EVSTAT 0x30 | ||
| 386 | #define EVINTEN 0x32 | ||
| 387 | #define EVACK 0x34 | ||
| 388 | #define SWS0 0x28 | ||
| 389 | #define SWS1 0x2a | ||
| 390 | #define SWS2 0x2c | ||
| 391 | #define SWS3 0x2e | ||
| 392 | #define AUXPAGE 0x3A | ||
| 393 | #define AUXOFF 0x3C | ||
| 394 | #define AUXDATA 0x3E | ||
| 395 | |||
| 396 | #define FID_TX 1 | ||
| 397 | #define FID_RX 2 | ||
| 398 | /* Offset into aux memory for descriptors */ | ||
| 399 | #define AUX_OFFSET 0x800 | ||
| 400 | /* Size of allocated packets */ | ||
| 401 | #define PKTSIZE 1840 | ||
| 402 | #define RIDSIZE 2048 | ||
| 403 | /* Size of the transmit queue */ | ||
| 404 | #define MAXTXQ 64 | ||
| 405 | |||
| 406 | /* BAP selectors */ | ||
| 407 | #define BAP0 0 /* Used for receiving packets */ | ||
| 408 | #define BAP1 2 /* Used for xmiting packets and working with RIDS */ | ||
| 409 | |||
| 410 | /* Flags */ | ||
| 411 | #define COMMAND_BUSY 0x8000 | ||
| 412 | |||
| 413 | #define BAP_BUSY 0x8000 | ||
| 414 | #define BAP_ERR 0x4000 | ||
| 415 | #define BAP_DONE 0x2000 | ||
| 416 | |||
| 417 | #define PROMISC 0xffff | ||
| 418 | #define NOPROMISC 0x0000 | ||
| 419 | |||
| 420 | #define EV_CMD 0x10 | ||
| 421 | #define EV_CLEARCOMMANDBUSY 0x4000 | ||
| 422 | #define EV_RX 0x01 | ||
| 423 | #define EV_TX 0x02 | ||
| 424 | #define EV_TXEXC 0x04 | ||
| 425 | #define EV_ALLOC 0x08 | ||
| 426 | #define EV_LINK 0x80 | ||
| 427 | #define EV_AWAKE 0x100 | ||
| 428 | #define EV_TXCPY 0x400 | ||
| 429 | #define EV_UNKNOWN 0x800 | ||
| 430 | #define EV_MIC 0x1000 /* Message Integrity Check Interrupt */ | ||
| 431 | #define EV_AWAKEN 0x2000 | ||
| 432 | #define STATUS_INTS (EV_AWAKE|EV_LINK|EV_TXEXC|EV_TX|EV_TXCPY|EV_RX|EV_MIC) | ||
| 433 | |||
| 434 | #ifdef CHECK_UNKNOWN_INTS | ||
| 435 | #define IGNORE_INTS (EV_CMD | EV_UNKNOWN) | ||
| 436 | #else | ||
| 437 | #define IGNORE_INTS (~STATUS_INTS) | ||
| 438 | #endif | ||
| 439 | |||
| 440 | /* RID TYPES */ | ||
| 441 | #define RID_RW 0x20 | ||
| 442 | |||
| 443 | /* The RIDs */ | ||
| 444 | #define RID_CAPABILITIES 0xFF00 | ||
| 445 | #define RID_APINFO 0xFF01 | ||
| 446 | #define RID_RADIOINFO 0xFF02 | ||
| 447 | #define RID_UNKNOWN3 0xFF03 | ||
| 448 | #define RID_RSSI 0xFF04 | ||
| 449 | #define RID_CONFIG 0xFF10 | ||
| 450 | #define RID_SSID 0xFF11 | ||
| 451 | #define RID_APLIST 0xFF12 | ||
| 452 | #define RID_DRVNAME 0xFF13 | ||
| 453 | #define RID_ETHERENCAP 0xFF14 | ||
| 454 | #define RID_WEP_TEMP 0xFF15 | ||
| 455 | #define RID_WEP_PERM 0xFF16 | ||
| 456 | #define RID_MODULATION 0xFF17 | ||
| 457 | #define RID_OPTIONS 0xFF18 | ||
| 458 | #define RID_ACTUALCONFIG 0xFF20 /*readonly*/ | ||
| 459 | #define RID_FACTORYCONFIG 0xFF21 | ||
| 460 | #define RID_UNKNOWN22 0xFF22 | ||
| 461 | #define RID_LEAPUSERNAME 0xFF23 | ||
| 462 | #define RID_LEAPPASSWORD 0xFF24 | ||
| 463 | #define RID_STATUS 0xFF50 | ||
| 464 | #define RID_BEACON_HST 0xFF51 | ||
| 465 | #define RID_BUSY_HST 0xFF52 | ||
| 466 | #define RID_RETRIES_HST 0xFF53 | ||
| 467 | #define RID_UNKNOWN54 0xFF54 | ||
| 468 | #define RID_UNKNOWN55 0xFF55 | ||
| 469 | #define RID_UNKNOWN56 0xFF56 | ||
| 470 | #define RID_MIC 0xFF57 | ||
| 471 | #define RID_STATS16 0xFF60 | ||
| 472 | #define RID_STATS16DELTA 0xFF61 | ||
| 473 | #define RID_STATS16DELTACLEAR 0xFF62 | ||
| 474 | #define RID_STATS 0xFF68 | ||
| 475 | #define RID_STATSDELTA 0xFF69 | ||
| 476 | #define RID_STATSDELTACLEAR 0xFF6A | ||
| 477 | #define RID_ECHOTEST_RID 0xFF70 | ||
| 478 | #define RID_ECHOTEST_RESULTS 0xFF71 | ||
| 479 | #define RID_BSSLISTFIRST 0xFF72 | ||
| 480 | #define RID_BSSLISTNEXT 0xFF73 | ||
| 481 | #define RID_WPA_BSSLISTFIRST 0xFF74 | ||
| 482 | #define RID_WPA_BSSLISTNEXT 0xFF75 | ||
| 483 | |||
| 484 | typedef struct { | ||
| 485 | u16 cmd; | ||
| 486 | u16 parm0; | ||
| 487 | u16 parm1; | ||
| 488 | u16 parm2; | ||
| 489 | } Cmd; | ||
| 490 | |||
| 491 | typedef struct { | ||
| 492 | u16 status; | ||
| 493 | u16 rsp0; | ||
| 494 | u16 rsp1; | ||
| 495 | u16 rsp2; | ||
| 496 | } Resp; | ||
| 497 | |||
| 498 | /* | ||
| 499 | * Rids and endian-ness: The Rids will always be in cpu endian, since | ||
| 500 | * this all the patches from the big-endian guys end up doing that. | ||
| 501 | * so all rid access should use the read/writeXXXRid routines. | ||
| 502 | */ | ||
| 503 | |||
| 504 | /* This structure came from an email sent to me from an engineer at | ||
| 505 | aironet for inclusion into this driver */ | ||
| 506 | typedef struct WepKeyRid WepKeyRid; | ||
| 507 | struct WepKeyRid { | ||
| 508 | __le16 len; | ||
| 509 | __le16 kindex; | ||
| 510 | u8 mac[ETH_ALEN]; | ||
| 511 | __le16 klen; | ||
| 512 | u8 key[16]; | ||
| 513 | } __packed; | ||
| 514 | |||
| 515 | /* These structures are from the Aironet's PC4500 Developers Manual */ | ||
| 516 | typedef struct Ssid Ssid; | ||
| 517 | struct Ssid { | ||
| 518 | __le16 len; | ||
| 519 | u8 ssid[32]; | ||
| 520 | } __packed; | ||
| 521 | |||
| 522 | typedef struct SsidRid SsidRid; | ||
| 523 | struct SsidRid { | ||
| 524 | __le16 len; | ||
| 525 | Ssid ssids[3]; | ||
| 526 | } __packed; | ||
| 527 | |||
| 528 | typedef struct ModulationRid ModulationRid; | ||
| 529 | struct ModulationRid { | ||
| 530 | __le16 len; | ||
| 531 | __le16 modulation; | ||
| 532 | #define MOD_DEFAULT cpu_to_le16(0) | ||
| 533 | #define MOD_CCK cpu_to_le16(1) | ||
| 534 | #define MOD_MOK cpu_to_le16(2) | ||
| 535 | } __packed; | ||
| 536 | |||
| 537 | typedef struct ConfigRid ConfigRid; | ||
| 538 | struct ConfigRid { | ||
| 539 | __le16 len; /* sizeof(ConfigRid) */ | ||
| 540 | __le16 opmode; /* operating mode */ | ||
| 541 | #define MODE_STA_IBSS cpu_to_le16(0) | ||
| 542 | #define MODE_STA_ESS cpu_to_le16(1) | ||
| 543 | #define MODE_AP cpu_to_le16(2) | ||
| 544 | #define MODE_AP_RPTR cpu_to_le16(3) | ||
| 545 | #define MODE_CFG_MASK cpu_to_le16(0xff) | ||
| 546 | #define MODE_ETHERNET_HOST cpu_to_le16(0<<8) /* rx payloads converted */ | ||
| 547 | #define MODE_LLC_HOST cpu_to_le16(1<<8) /* rx payloads left as is */ | ||
| 548 | #define MODE_AIRONET_EXTEND cpu_to_le16(1<<9) /* enable Aironet extensions */ | ||
| 549 | #define MODE_AP_INTERFACE cpu_to_le16(1<<10) /* enable ap interface extensions */ | ||
| 550 | #define MODE_ANTENNA_ALIGN cpu_to_le16(1<<11) /* enable antenna alignment */ | ||
| 551 | #define MODE_ETHER_LLC cpu_to_le16(1<<12) /* enable ethernet LLC */ | ||
| 552 | #define MODE_LEAF_NODE cpu_to_le16(1<<13) /* enable leaf node bridge */ | ||
| 553 | #define MODE_CF_POLLABLE cpu_to_le16(1<<14) /* enable CF pollable */ | ||
| 554 | #define MODE_MIC cpu_to_le16(1<<15) /* enable MIC */ | ||
| 555 | __le16 rmode; /* receive mode */ | ||
| 556 | #define RXMODE_BC_MC_ADDR cpu_to_le16(0) | ||
| 557 | #define RXMODE_BC_ADDR cpu_to_le16(1) /* ignore multicasts */ | ||
| 558 | #define RXMODE_ADDR cpu_to_le16(2) /* ignore multicast and broadcast */ | ||
| 559 | #define RXMODE_RFMON cpu_to_le16(3) /* wireless monitor mode */ | ||
| 560 | #define RXMODE_RFMON_ANYBSS cpu_to_le16(4) | ||
| 561 | #define RXMODE_LANMON cpu_to_le16(5) /* lan style monitor -- data packets only */ | ||
| 562 | #define RXMODE_MASK cpu_to_le16(255) | ||
| 563 | #define RXMODE_DISABLE_802_3_HEADER cpu_to_le16(1<<8) /* disables 802.3 header on rx */ | ||
| 564 | #define RXMODE_FULL_MASK (RXMODE_MASK | RXMODE_DISABLE_802_3_HEADER) | ||
| 565 | #define RXMODE_NORMALIZED_RSSI cpu_to_le16(1<<9) /* return normalized RSSI */ | ||
| 566 | __le16 fragThresh; | ||
| 567 | __le16 rtsThres; | ||
| 568 | u8 macAddr[ETH_ALEN]; | ||
| 569 | u8 rates[8]; | ||
| 570 | __le16 shortRetryLimit; | ||
| 571 | __le16 longRetryLimit; | ||
| 572 | __le16 txLifetime; /* in kusec */ | ||
| 573 | __le16 rxLifetime; /* in kusec */ | ||
| 574 | __le16 stationary; | ||
| 575 | __le16 ordering; | ||
| 576 | __le16 u16deviceType; /* for overriding device type */ | ||
| 577 | __le16 cfpRate; | ||
| 578 | __le16 cfpDuration; | ||
| 579 | __le16 _reserved1[3]; | ||
| 580 | /*---------- Scanning/Associating ----------*/ | ||
| 581 | __le16 scanMode; | ||
| 582 | #define SCANMODE_ACTIVE cpu_to_le16(0) | ||
| 583 | #define SCANMODE_PASSIVE cpu_to_le16(1) | ||
| 584 | #define SCANMODE_AIROSCAN cpu_to_le16(2) | ||
| 585 | __le16 probeDelay; /* in kusec */ | ||
| 586 | __le16 probeEnergyTimeout; /* in kusec */ | ||
| 587 | __le16 probeResponseTimeout; | ||
| 588 | __le16 beaconListenTimeout; | ||
| 589 | __le16 joinNetTimeout; | ||
| 590 | __le16 authTimeout; | ||
| 591 | __le16 authType; | ||
| 592 | #define AUTH_OPEN cpu_to_le16(0x1) | ||
| 593 | #define AUTH_ENCRYPT cpu_to_le16(0x101) | ||
| 594 | #define AUTH_SHAREDKEY cpu_to_le16(0x102) | ||
| 595 | #define AUTH_ALLOW_UNENCRYPTED cpu_to_le16(0x200) | ||
| 596 | __le16 associationTimeout; | ||
| 597 | __le16 specifiedApTimeout; | ||
| 598 | __le16 offlineScanInterval; | ||
| 599 | __le16 offlineScanDuration; | ||
| 600 | __le16 linkLossDelay; | ||
| 601 | __le16 maxBeaconLostTime; | ||
| 602 | __le16 refreshInterval; | ||
| 603 | #define DISABLE_REFRESH cpu_to_le16(0xFFFF) | ||
| 604 | __le16 _reserved1a[1]; | ||
| 605 | /*---------- Power save operation ----------*/ | ||
| 606 | __le16 powerSaveMode; | ||
| 607 | #define POWERSAVE_CAM cpu_to_le16(0) | ||
| 608 | #define POWERSAVE_PSP cpu_to_le16(1) | ||
| 609 | #define POWERSAVE_PSPCAM cpu_to_le16(2) | ||
| 610 | __le16 sleepForDtims; | ||
| 611 | __le16 listenInterval; | ||
| 612 | __le16 fastListenInterval; | ||
| 613 | __le16 listenDecay; | ||
| 614 | __le16 fastListenDelay; | ||
| 615 | __le16 _reserved2[2]; | ||
| 616 | /*---------- Ap/Ibss config items ----------*/ | ||
| 617 | __le16 beaconPeriod; | ||
| 618 | __le16 atimDuration; | ||
| 619 | __le16 hopPeriod; | ||
| 620 | __le16 channelSet; | ||
| 621 | __le16 channel; | ||
| 622 | __le16 dtimPeriod; | ||
| 623 | __le16 bridgeDistance; | ||
| 624 | __le16 radioID; | ||
| 625 | /*---------- Radio configuration ----------*/ | ||
| 626 | __le16 radioType; | ||
| 627 | #define RADIOTYPE_DEFAULT cpu_to_le16(0) | ||
| 628 | #define RADIOTYPE_802_11 cpu_to_le16(1) | ||
| 629 | #define RADIOTYPE_LEGACY cpu_to_le16(2) | ||
| 630 | u8 rxDiversity; | ||
| 631 | u8 txDiversity; | ||
| 632 | __le16 txPower; | ||
| 633 | #define TXPOWER_DEFAULT 0 | ||
| 634 | __le16 rssiThreshold; | ||
| 635 | #define RSSI_DEFAULT 0 | ||
| 636 | __le16 modulation; | ||
| 637 | #define PREAMBLE_AUTO cpu_to_le16(0) | ||
| 638 | #define PREAMBLE_LONG cpu_to_le16(1) | ||
| 639 | #define PREAMBLE_SHORT cpu_to_le16(2) | ||
| 640 | __le16 preamble; | ||
| 641 | __le16 homeProduct; | ||
| 642 | __le16 radioSpecific; | ||
| 643 | /*---------- Aironet Extensions ----------*/ | ||
| 644 | u8 nodeName[16]; | ||
| 645 | __le16 arlThreshold; | ||
| 646 | __le16 arlDecay; | ||
| 647 | __le16 arlDelay; | ||
| 648 | __le16 _reserved4[1]; | ||
| 649 | /*---------- Aironet Extensions ----------*/ | ||
| 650 | u8 magicAction; | ||
| 651 | #define MAGIC_ACTION_STSCHG 1 | ||
| 652 | #define MAGIC_ACTION_RESUME 2 | ||
| 653 | #define MAGIC_IGNORE_MCAST (1<<8) | ||
| 654 | #define MAGIC_IGNORE_BCAST (1<<9) | ||
| 655 | #define MAGIC_SWITCH_TO_PSP (0<<10) | ||
| 656 | #define MAGIC_STAY_IN_CAM (1<<10) | ||
| 657 | u8 magicControl; | ||
| 658 | __le16 autoWake; | ||
| 659 | } __packed; | ||
| 660 | |||
| 661 | typedef struct StatusRid StatusRid; | ||
| 662 | struct StatusRid { | ||
| 663 | __le16 len; | ||
| 664 | u8 mac[ETH_ALEN]; | ||
| 665 | __le16 mode; | ||
| 666 | __le16 errorCode; | ||
| 667 | __le16 sigQuality; | ||
| 668 | __le16 SSIDlen; | ||
| 669 | char SSID[32]; | ||
| 670 | char apName[16]; | ||
| 671 | u8 bssid[4][ETH_ALEN]; | ||
| 672 | __le16 beaconPeriod; | ||
| 673 | __le16 dimPeriod; | ||
| 674 | __le16 atimDuration; | ||
| 675 | __le16 hopPeriod; | ||
| 676 | __le16 channelSet; | ||
| 677 | __le16 channel; | ||
| 678 | __le16 hopsToBackbone; | ||
| 679 | __le16 apTotalLoad; | ||
| 680 | __le16 generatedLoad; | ||
| 681 | __le16 accumulatedArl; | ||
| 682 | __le16 signalQuality; | ||
| 683 | __le16 currentXmitRate; | ||
| 684 | __le16 apDevExtensions; | ||
| 685 | __le16 normalizedSignalStrength; | ||
| 686 | __le16 shortPreamble; | ||
| 687 | u8 apIP[4]; | ||
| 688 | u8 noisePercent; /* Noise percent in last second */ | ||
| 689 | u8 noisedBm; /* Noise dBm in last second */ | ||
| 690 | u8 noiseAvePercent; /* Noise percent in last minute */ | ||
| 691 | u8 noiseAvedBm; /* Noise dBm in last minute */ | ||
| 692 | u8 noiseMaxPercent; /* Highest noise percent in last minute */ | ||
| 693 | u8 noiseMaxdBm; /* Highest noise dbm in last minute */ | ||
| 694 | __le16 load; | ||
| 695 | u8 carrier[4]; | ||
| 696 | __le16 assocStatus; | ||
| 697 | #define STAT_NOPACKETS 0 | ||
| 698 | #define STAT_NOCARRIERSET 10 | ||
| 699 | #define STAT_GOTCARRIERSET 11 | ||
| 700 | #define STAT_WRONGSSID 20 | ||
| 701 | #define STAT_BADCHANNEL 25 | ||
| 702 | #define STAT_BADBITRATES 30 | ||
| 703 | #define STAT_BADPRIVACY 35 | ||
| 704 | #define STAT_APFOUND 40 | ||
| 705 | #define STAT_APREJECTED 50 | ||
| 706 | #define STAT_AUTHENTICATING 60 | ||
| 707 | #define STAT_DEAUTHENTICATED 61 | ||
| 708 | #define STAT_AUTHTIMEOUT 62 | ||
| 709 | #define STAT_ASSOCIATING 70 | ||
| 710 | #define STAT_DEASSOCIATED 71 | ||
| 711 | #define STAT_ASSOCTIMEOUT 72 | ||
| 712 | #define STAT_NOTAIROAP 73 | ||
| 713 | #define STAT_ASSOCIATED 80 | ||
| 714 | #define STAT_LEAPING 90 | ||
| 715 | #define STAT_LEAPFAILED 91 | ||
| 716 | #define STAT_LEAPTIMEDOUT 92 | ||
| 717 | #define STAT_LEAPCOMPLETE 93 | ||
| 718 | } __packed; | ||
| 719 | |||
| 720 | typedef struct StatsRid StatsRid; | ||
| 721 | struct StatsRid { | ||
| 722 | __le16 len; | ||
| 723 | __le16 spacer; | ||
| 724 | __le32 vals[100]; | ||
| 725 | } __packed; | ||
| 726 | |||
| 727 | typedef struct APListRid APListRid; | ||
| 728 | struct APListRid { | ||
| 729 | __le16 len; | ||
| 730 | u8 ap[4][ETH_ALEN]; | ||
| 731 | } __packed; | ||
| 732 | |||
| 733 | typedef struct CapabilityRid CapabilityRid; | ||
| 734 | struct CapabilityRid { | ||
| 735 | __le16 len; | ||
| 736 | char oui[3]; | ||
| 737 | char zero; | ||
| 738 | __le16 prodNum; | ||
| 739 | char manName[32]; | ||
| 740 | char prodName[16]; | ||
| 741 | char prodVer[8]; | ||
| 742 | char factoryAddr[ETH_ALEN]; | ||
| 743 | char aironetAddr[ETH_ALEN]; | ||
| 744 | __le16 radioType; | ||
| 745 | __le16 country; | ||
| 746 | char callid[ETH_ALEN]; | ||
| 747 | char supportedRates[8]; | ||
| 748 | char rxDiversity; | ||
| 749 | char txDiversity; | ||
| 750 | __le16 txPowerLevels[8]; | ||
| 751 | __le16 hardVer; | ||
| 752 | __le16 hardCap; | ||
| 753 | __le16 tempRange; | ||
| 754 | __le16 softVer; | ||
| 755 | __le16 softSubVer; | ||
| 756 | __le16 interfaceVer; | ||
| 757 | __le16 softCap; | ||
| 758 | __le16 bootBlockVer; | ||
| 759 | __le16 requiredHard; | ||
| 760 | __le16 extSoftCap; | ||
| 761 | } __packed; | ||
| 762 | |||
| 763 | /* Only present on firmware >= 5.30.17 */ | ||
| 764 | typedef struct BSSListRidExtra BSSListRidExtra; | ||
| 765 | struct BSSListRidExtra { | ||
| 766 | __le16 unknown[4]; | ||
| 767 | u8 fixed[12]; /* WLAN management frame */ | ||
| 768 | u8 iep[624]; | ||
| 769 | } __packed; | ||
| 770 | |||
| 771 | typedef struct BSSListRid BSSListRid; | ||
| 772 | struct BSSListRid { | ||
| 773 | __le16 len; | ||
| 774 | __le16 index; /* First is 0 and 0xffff means end of list */ | ||
| 775 | #define RADIO_FH 1 /* Frequency hopping radio type */ | ||
| 776 | #define RADIO_DS 2 /* Direct sequence radio type */ | ||
| 777 | #define RADIO_TMA 4 /* Proprietary radio used in old cards (2500) */ | ||
| 778 | __le16 radioType; | ||
| 779 | u8 bssid[ETH_ALEN]; /* Mac address of the BSS */ | ||
| 780 | u8 zero; | ||
| 781 | u8 ssidLen; | ||
| 782 | u8 ssid[32]; | ||
| 783 | __le16 dBm; | ||
| 784 | #define CAP_ESS cpu_to_le16(1<<0) | ||
| 785 | #define CAP_IBSS cpu_to_le16(1<<1) | ||
| 786 | #define CAP_PRIVACY cpu_to_le16(1<<4) | ||
| 787 | #define CAP_SHORTHDR cpu_to_le16(1<<5) | ||
| 788 | __le16 cap; | ||
| 789 | __le16 beaconInterval; | ||
| 790 | u8 rates[8]; /* Same as rates for config rid */ | ||
| 791 | struct { /* For frequency hopping only */ | ||
| 792 | __le16 dwell; | ||
| 793 | u8 hopSet; | ||
| 794 | u8 hopPattern; | ||
| 795 | u8 hopIndex; | ||
| 796 | u8 fill; | ||
| 797 | } fh; | ||
| 798 | __le16 dsChannel; | ||
| 799 | __le16 atimWindow; | ||
| 800 | |||
| 801 | /* Only present on firmware >= 5.30.17 */ | ||
| 802 | BSSListRidExtra extra; | ||
| 803 | } __packed; | ||
| 804 | |||
| 805 | typedef struct { | ||
| 806 | BSSListRid bss; | ||
| 807 | struct list_head list; | ||
| 808 | } BSSListElement; | ||
| 809 | |||
| 810 | typedef struct tdsRssiEntry tdsRssiEntry; | ||
| 811 | struct tdsRssiEntry { | ||
| 812 | u8 rssipct; | ||
| 813 | u8 rssidBm; | ||
| 814 | } __packed; | ||
| 815 | |||
| 816 | typedef struct tdsRssiRid tdsRssiRid; | ||
| 817 | struct tdsRssiRid { | ||
| 818 | u16 len; | ||
| 819 | tdsRssiEntry x[256]; | ||
| 820 | } __packed; | ||
| 821 | |||
| 822 | typedef struct MICRid MICRid; | ||
| 823 | struct MICRid { | ||
| 824 | __le16 len; | ||
| 825 | __le16 state; | ||
| 826 | __le16 multicastValid; | ||
| 827 | u8 multicast[16]; | ||
| 828 | __le16 unicastValid; | ||
| 829 | u8 unicast[16]; | ||
| 830 | } __packed; | ||
| 831 | |||
| 832 | typedef struct MICBuffer MICBuffer; | ||
| 833 | struct MICBuffer { | ||
| 834 | __be16 typelen; | ||
| 835 | |||
| 836 | union { | ||
| 837 | u8 snap[8]; | ||
| 838 | struct { | ||
| 839 | u8 dsap; | ||
| 840 | u8 ssap; | ||
| 841 | u8 control; | ||
| 842 | u8 orgcode[3]; | ||
| 843 | u8 fieldtype[2]; | ||
| 844 | } llc; | ||
| 845 | } u; | ||
| 846 | __be32 mic; | ||
| 847 | __be32 seq; | ||
| 848 | } __packed; | ||
| 849 | |||
| 850 | typedef struct { | ||
| 851 | u8 da[ETH_ALEN]; | ||
| 852 | u8 sa[ETH_ALEN]; | ||
| 853 | } etherHead; | ||
| 854 | |||
| 855 | #define TXCTL_TXOK (1<<1) /* report if tx is ok */ | ||
| 856 | #define TXCTL_TXEX (1<<2) /* report if tx fails */ | ||
| 857 | #define TXCTL_802_3 (0<<3) /* 802.3 packet */ | ||
| 858 | #define TXCTL_802_11 (1<<3) /* 802.11 mac packet */ | ||
| 859 | #define TXCTL_ETHERNET (0<<4) /* payload has ethertype */ | ||
| 860 | #define TXCTL_LLC (1<<4) /* payload is llc */ | ||
| 861 | #define TXCTL_RELEASE (0<<5) /* release after completion */ | ||
| 862 | #define TXCTL_NORELEASE (1<<5) /* on completion returns to host */ | ||
| 863 | |||
| 864 | #define BUSY_FID 0x10000 | ||
| 865 | |||
| 866 | #ifdef CISCO_EXT | ||
| 867 | #define AIROMAGIC 0xa55a | ||
| 868 | /* Warning : SIOCDEVPRIVATE may disapear during 2.5.X - Jean II */ | ||
| 869 | #ifdef SIOCIWFIRSTPRIV | ||
| 870 | #ifdef SIOCDEVPRIVATE | ||
| 871 | #define AIROOLDIOCTL SIOCDEVPRIVATE | ||
| 872 | #define AIROOLDIDIFC AIROOLDIOCTL + 1 | ||
| 873 | #endif /* SIOCDEVPRIVATE */ | ||
| 874 | #else /* SIOCIWFIRSTPRIV */ | ||
| 875 | #define SIOCIWFIRSTPRIV SIOCDEVPRIVATE | ||
| 876 | #endif /* SIOCIWFIRSTPRIV */ | ||
| 877 | /* This may be wrong. When using the new SIOCIWFIRSTPRIV range, we probably | ||
| 878 | * should use only "GET" ioctls (last bit set to 1). "SET" ioctls are root | ||
| 879 | * only and don't return the modified struct ifreq to the application which | ||
| 880 | * is usually a problem. - Jean II */ | ||
| 881 | #define AIROIOCTL SIOCIWFIRSTPRIV | ||
| 882 | #define AIROIDIFC AIROIOCTL + 1 | ||
| 883 | |||
| 884 | /* Ioctl constants to be used in airo_ioctl.command */ | ||
| 885 | |||
| 886 | #define AIROGCAP 0 // Capability rid | ||
| 887 | #define AIROGCFG 1 // USED A LOT | ||
| 888 | #define AIROGSLIST 2 // System ID list | ||
| 889 | #define AIROGVLIST 3 // List of specified AP's | ||
| 890 | #define AIROGDRVNAM 4 // NOTUSED | ||
| 891 | #define AIROGEHTENC 5 // NOTUSED | ||
| 892 | #define AIROGWEPKTMP 6 | ||
| 893 | #define AIROGWEPKNV 7 | ||
| 894 | #define AIROGSTAT 8 | ||
| 895 | #define AIROGSTATSC32 9 | ||
| 896 | #define AIROGSTATSD32 10 | ||
| 897 | #define AIROGMICRID 11 | ||
| 898 | #define AIROGMICSTATS 12 | ||
| 899 | #define AIROGFLAGS 13 | ||
| 900 | #define AIROGID 14 | ||
| 901 | #define AIRORRID 15 | ||
| 902 | #define AIRORSWVERSION 17 | ||
| 903 | |||
| 904 | /* Leave gap of 40 commands after AIROGSTATSD32 for future */ | ||
| 905 | |||
| 906 | #define AIROPCAP AIROGSTATSD32 + 40 | ||
| 907 | #define AIROPVLIST AIROPCAP + 1 | ||
| 908 | #define AIROPSLIST AIROPVLIST + 1 | ||
| 909 | #define AIROPCFG AIROPSLIST + 1 | ||
| 910 | #define AIROPSIDS AIROPCFG + 1 | ||
| 911 | #define AIROPAPLIST AIROPSIDS + 1 | ||
| 912 | #define AIROPMACON AIROPAPLIST + 1 /* Enable mac */ | ||
| 913 | #define AIROPMACOFF AIROPMACON + 1 /* Disable mac */ | ||
| 914 | #define AIROPSTCLR AIROPMACOFF + 1 | ||
| 915 | #define AIROPWEPKEY AIROPSTCLR + 1 | ||
| 916 | #define AIROPWEPKEYNV AIROPWEPKEY + 1 | ||
| 917 | #define AIROPLEAPPWD AIROPWEPKEYNV + 1 | ||
| 918 | #define AIROPLEAPUSR AIROPLEAPPWD + 1 | ||
| 919 | |||
| 920 | /* Flash codes */ | ||
| 921 | |||
| 922 | #define AIROFLSHRST AIROPWEPKEYNV + 40 | ||
| 923 | #define AIROFLSHGCHR AIROFLSHRST + 1 | ||
| 924 | #define AIROFLSHSTFL AIROFLSHGCHR + 1 | ||
| 925 | #define AIROFLSHPCHR AIROFLSHSTFL + 1 | ||
| 926 | #define AIROFLPUTBUF AIROFLSHPCHR + 1 | ||
| 927 | #define AIRORESTART AIROFLPUTBUF + 1 | ||
| 928 | |||
| 929 | #define FLASHSIZE 32768 | ||
| 930 | #define AUXMEMSIZE (256 * 1024) | ||
| 931 | |||
| 932 | typedef struct aironet_ioctl { | ||
| 933 | unsigned short command; // What to do | ||
| 934 | unsigned short len; // Len of data | ||
| 935 | unsigned short ridnum; // rid number | ||
| 936 | unsigned char __user *data; // d-data | ||
| 937 | } aironet_ioctl; | ||
| 938 | |||
| 939 | static const char swversion[] = "2.1"; | ||
| 940 | #endif /* CISCO_EXT */ | ||
| 941 | |||
| 942 | #define NUM_MODULES 2 | ||
| 943 | #define MIC_MSGLEN_MAX 2400 | ||
| 944 | #define EMMH32_MSGLEN_MAX MIC_MSGLEN_MAX | ||
| 945 | #define AIRO_DEF_MTU 2312 | ||
| 946 | |||
| 947 | typedef struct { | ||
| 948 | u32 size; // size | ||
| 949 | u8 enabled; // MIC enabled or not | ||
| 950 | u32 rxSuccess; // successful packets received | ||
| 951 | u32 rxIncorrectMIC; // pkts dropped due to incorrect MIC comparison | ||
| 952 | u32 rxNotMICed; // pkts dropped due to not being MIC'd | ||
| 953 | u32 rxMICPlummed; // pkts dropped due to not having a MIC plummed | ||
| 954 | u32 rxWrongSequence; // pkts dropped due to sequence number violation | ||
| 955 | u32 reserve[32]; | ||
| 956 | } mic_statistics; | ||
| 957 | |||
| 958 | typedef struct { | ||
| 959 | __be32 coeff[((EMMH32_MSGLEN_MAX)+3)>>2]; | ||
| 960 | u64 accum; // accumulated mic, reduced to u32 in final() | ||
| 961 | int position; // current position (byte offset) in message | ||
| 962 | union { | ||
| 963 | u8 d8[4]; | ||
| 964 | __be32 d32; | ||
| 965 | } part; // saves partial message word across update() calls | ||
| 966 | } emmh32_context; | ||
| 967 | |||
| 968 | typedef struct { | ||
| 969 | emmh32_context seed; // Context - the seed | ||
| 970 | u32 rx; // Received sequence number | ||
| 971 | u32 tx; // Tx sequence number | ||
| 972 | u32 window; // Start of window | ||
| 973 | u8 valid; // Flag to say if context is valid or not | ||
| 974 | u8 key[16]; | ||
| 975 | } miccntx; | ||
| 976 | |||
| 977 | typedef struct { | ||
| 978 | miccntx mCtx; // Multicast context | ||
| 979 | miccntx uCtx; // Unicast context | ||
| 980 | } mic_module; | ||
| 981 | |||
| 982 | typedef struct { | ||
| 983 | unsigned int rid: 16; | ||
| 984 | unsigned int len: 15; | ||
| 985 | unsigned int valid: 1; | ||
| 986 | dma_addr_t host_addr; | ||
| 987 | } Rid; | ||
| 988 | |||
| 989 | typedef struct { | ||
| 990 | unsigned int offset: 15; | ||
| 991 | unsigned int eoc: 1; | ||
| 992 | unsigned int len: 15; | ||
| 993 | unsigned int valid: 1; | ||
| 994 | dma_addr_t host_addr; | ||
| 995 | } TxFid; | ||
| 996 | |||
| 997 | struct rx_hdr { | ||
| 998 | __le16 status, len; | ||
| 999 | u8 rssi[2]; | ||
| 1000 | u8 rate; | ||
| 1001 | u8 freq; | ||
| 1002 | __le16 tmp[4]; | ||
| 1003 | } __packed; | ||
| 1004 | |||
| 1005 | typedef struct { | ||
| 1006 | unsigned int ctl: 15; | ||
| 1007 | unsigned int rdy: 1; | ||
| 1008 | unsigned int len: 15; | ||
| 1009 | unsigned int valid: 1; | ||
| 1010 | dma_addr_t host_addr; | ||
| 1011 | } RxFid; | ||
| 1012 | |||
| 1013 | /* | ||
| 1014 | * Host receive descriptor | ||
| 1015 | */ | ||
| 1016 | typedef struct { | ||
| 1017 | unsigned char __iomem *card_ram_off; /* offset into card memory of the | ||
| 1018 | desc */ | ||
| 1019 | RxFid rx_desc; /* card receive descriptor */ | ||
| 1020 | char *virtual_host_addr; /* virtual address of host receive | ||
| 1021 | buffer */ | ||
| 1022 | int pending; | ||
| 1023 | } HostRxDesc; | ||
| 1024 | |||
| 1025 | /* | ||
| 1026 | * Host transmit descriptor | ||
| 1027 | */ | ||
| 1028 | typedef struct { | ||
| 1029 | unsigned char __iomem *card_ram_off; /* offset into card memory of the | ||
| 1030 | desc */ | ||
| 1031 | TxFid tx_desc; /* card transmit descriptor */ | ||
| 1032 | char *virtual_host_addr; /* virtual address of host receive | ||
| 1033 | buffer */ | ||
| 1034 | int pending; | ||
| 1035 | } HostTxDesc; | ||
| 1036 | |||
| 1037 | /* | ||
| 1038 | * Host RID descriptor | ||
| 1039 | */ | ||
| 1040 | typedef struct { | ||
| 1041 | unsigned char __iomem *card_ram_off; /* offset into card memory of the | ||
| 1042 | descriptor */ | ||
| 1043 | Rid rid_desc; /* card RID descriptor */ | ||
| 1044 | char *virtual_host_addr; /* virtual address of host receive | ||
| 1045 | buffer */ | ||
| 1046 | } HostRidDesc; | ||
| 1047 | |||
| 1048 | typedef struct { | ||
| 1049 | u16 sw0; | ||
| 1050 | u16 sw1; | ||
| 1051 | u16 status; | ||
| 1052 | u16 len; | ||
| 1053 | #define HOST_SET (1 << 0) | ||
| 1054 | #define HOST_INT_TX (1 << 1) /* Interrupt on successful TX */ | ||
| 1055 | #define HOST_INT_TXERR (1 << 2) /* Interrupt on unseccessful TX */ | ||
| 1056 | #define HOST_LCC_PAYLOAD (1 << 4) /* LLC payload, 0 = Ethertype */ | ||
| 1057 | #define HOST_DONT_RLSE (1 << 5) /* Don't release buffer when done */ | ||
| 1058 | #define HOST_DONT_RETRY (1 << 6) /* Don't retry trasmit */ | ||
| 1059 | #define HOST_CLR_AID (1 << 7) /* clear AID failure */ | ||
| 1060 | #define HOST_RTS (1 << 9) /* Force RTS use */ | ||
| 1061 | #define HOST_SHORT (1 << 10) /* Do short preamble */ | ||
| 1062 | u16 ctl; | ||
| 1063 | u16 aid; | ||
| 1064 | u16 retries; | ||
| 1065 | u16 fill; | ||
| 1066 | } TxCtlHdr; | ||
| 1067 | |||
| 1068 | typedef struct { | ||
| 1069 | u16 ctl; | ||
| 1070 | u16 duration; | ||
| 1071 | char addr1[6]; | ||
| 1072 | char addr2[6]; | ||
| 1073 | char addr3[6]; | ||
| 1074 | u16 seq; | ||
| 1075 | char addr4[6]; | ||
| 1076 | } WifiHdr; | ||
| 1077 | |||
| 1078 | |||
| 1079 | typedef struct { | ||
| 1080 | TxCtlHdr ctlhdr; | ||
| 1081 | u16 fill1; | ||
| 1082 | u16 fill2; | ||
| 1083 | WifiHdr wifihdr; | ||
| 1084 | u16 gaplen; | ||
| 1085 | u16 status; | ||
| 1086 | } WifiCtlHdr; | ||
| 1087 | |||
| 1088 | static WifiCtlHdr wifictlhdr8023 = { | ||
| 1089 | .ctlhdr = { | ||
| 1090 | .ctl = HOST_DONT_RLSE, | ||
| 1091 | } | ||
| 1092 | }; | ||
| 1093 | |||
| 1094 | // A few details needed for WEP (Wireless Equivalent Privacy) | ||
| 1095 | #define MAX_KEY_SIZE 13 // 128 (?) bits | ||
| 1096 | #define MIN_KEY_SIZE 5 // 40 bits RC4 - WEP | ||
| 1097 | typedef struct wep_key_t { | ||
| 1098 | u16 len; | ||
| 1099 | u8 key[16]; /* 40-bit and 104-bit keys */ | ||
| 1100 | } wep_key_t; | ||
| 1101 | |||
| 1102 | /* List of Wireless Handlers (new API) */ | ||
| 1103 | static const struct iw_handler_def airo_handler_def; | ||
| 1104 | |||
| 1105 | static const char version[] = "airo.c 0.6 (Ben Reed & Javier Achirica)"; | ||
| 1106 | |||
| 1107 | struct airo_info; | ||
| 1108 | |||
| 1109 | static int get_dec_u16(char *buffer, int *start, int limit); | ||
| 1110 | static void OUT4500(struct airo_info *, u16 reg, u16 value); | ||
| 1111 | static unsigned short IN4500(struct airo_info *, u16 reg); | ||
| 1112 | static u16 setup_card(struct airo_info*, struct net_device *dev, int lock); | ||
| 1113 | static int enable_MAC(struct airo_info *ai, int lock); | ||
| 1114 | static void disable_MAC(struct airo_info *ai, int lock); | ||
| 1115 | static void enable_interrupts(struct airo_info*); | ||
| 1116 | static void disable_interrupts(struct airo_info*); | ||
| 1117 | static u16 issuecommand(struct airo_info*, Cmd *pCmd, Resp *pRsp, | ||
| 1118 | bool may_sleep); | ||
| 1119 | static int bap_setup(struct airo_info*, u16 rid, u16 offset, int whichbap); | ||
| 1120 | static int aux_bap_read(struct airo_info*, __le16 *pu16Dst, int bytelen, | ||
| 1121 | int whichbap); | ||
| 1122 | static int fast_bap_read(struct airo_info*, __le16 *pu16Dst, int bytelen, | ||
| 1123 | int whichbap); | ||
| 1124 | static int bap_write(struct airo_info*, const __le16 *pu16Src, int bytelen, | ||
| 1125 | int whichbap); | ||
| 1126 | static int PC4500_accessrid(struct airo_info*, u16 rid, u16 accmd); | ||
| 1127 | static int PC4500_readrid(struct airo_info*, u16 rid, void *pBuf, int len, int lock); | ||
| 1128 | static int PC4500_writerid(struct airo_info*, u16 rid, const void | ||
| 1129 | *pBuf, int len, int lock); | ||
| 1130 | static int do_writerid(struct airo_info*, u16 rid, const void *rid_data, | ||
| 1131 | int len, int dummy); | ||
| 1132 | static u16 transmit_allocate(struct airo_info*, int lenPayload, int raw); | ||
| 1133 | static int transmit_802_3_packet(struct airo_info*, int len, char *pPacket, | ||
| 1134 | bool may_sleep); | ||
| 1135 | static int transmit_802_11_packet(struct airo_info*, int len, char *pPacket, | ||
| 1136 | bool may_sleep); | ||
| 1137 | |||
| 1138 | static int mpi_send_packet(struct net_device *dev); | ||
| 1139 | static void mpi_unmap_card(struct pci_dev *pci); | ||
| 1140 | static void mpi_receive_802_3(struct airo_info *ai); | ||
| 1141 | static void mpi_receive_802_11(struct airo_info *ai); | ||
| 1142 | static int waitbusy(struct airo_info *ai); | ||
| 1143 | |||
| 1144 | static irqreturn_t airo_interrupt(int irq, void* dev_id); | ||
| 1145 | static int airo_thread(void *data); | ||
| 1146 | static void timer_func(struct net_device *dev); | ||
| 1147 | static int airo_siocdevprivate(struct net_device *dev, struct ifreq *rq, void __user *, int cmd); | ||
| 1148 | static struct iw_statistics *airo_get_wireless_stats(struct net_device *dev); | ||
| 1149 | #ifdef CISCO_EXT | ||
| 1150 | static int readrids(struct net_device *dev, aironet_ioctl *comp); | ||
| 1151 | static int writerids(struct net_device *dev, aironet_ioctl *comp); | ||
| 1152 | static int flashcard(struct net_device *dev, aironet_ioctl *comp); | ||
| 1153 | #endif /* CISCO_EXT */ | ||
| 1154 | static void micinit(struct airo_info *ai); | ||
| 1155 | static int micsetup(struct airo_info *ai); | ||
| 1156 | static int encapsulate(struct airo_info *ai, etherHead *pPacket, MICBuffer *buffer, int len); | ||
| 1157 | static int decapsulate(struct airo_info *ai, MICBuffer *mic, etherHead *pPacket, u16 payLen); | ||
| 1158 | |||
| 1159 | static u8 airo_rssi_to_dbm(tdsRssiEntry *rssi_rid, u8 rssi); | ||
| 1160 | static u8 airo_dbm_to_pct(tdsRssiEntry *rssi_rid, u8 dbm); | ||
| 1161 | |||
| 1162 | static void airo_networks_free(struct airo_info *ai); | ||
| 1163 | |||
| 1164 | struct airo_info { | ||
| 1165 | struct net_device *dev; | ||
| 1166 | struct list_head dev_list; | ||
| 1167 | /* Note, we can have MAX_FIDS outstanding. FIDs are 16-bits, so we | ||
| 1168 | use the high bit to mark whether it is in use. */ | ||
| 1169 | #define MAX_FIDS 6 | ||
| 1170 | #define MPI_MAX_FIDS 1 | ||
| 1171 | u32 fids[MAX_FIDS]; | ||
| 1172 | ConfigRid config; | ||
| 1173 | char keyindex; // Used with auto wep | ||
| 1174 | char defindex; // Used with auto wep | ||
| 1175 | struct proc_dir_entry *proc_entry; | ||
| 1176 | spinlock_t aux_lock; | ||
| 1177 | #define FLAG_RADIO_OFF 0 /* User disabling of MAC */ | ||
| 1178 | #define FLAG_RADIO_DOWN 1 /* ifup/ifdown disabling of MAC */ | ||
| 1179 | #define FLAG_RADIO_MASK 0x03 | ||
| 1180 | #define FLAG_ENABLED 2 | ||
| 1181 | #define FLAG_ADHOC 3 /* Needed by MIC */ | ||
| 1182 | #define FLAG_MIC_CAPABLE 4 | ||
| 1183 | #define FLAG_UPDATE_MULTI 5 | ||
| 1184 | #define FLAG_UPDATE_UNI 6 | ||
| 1185 | #define FLAG_802_11 7 | ||
| 1186 | #define FLAG_PROMISC 8 /* IFF_PROMISC 0x100 - include/linux/if.h */ | ||
| 1187 | #define FLAG_PENDING_XMIT 9 | ||
| 1188 | #define FLAG_PENDING_XMIT11 10 | ||
| 1189 | #define FLAG_MPI 11 | ||
| 1190 | #define FLAG_REGISTERED 12 | ||
| 1191 | #define FLAG_COMMIT 13 | ||
| 1192 | #define FLAG_RESET 14 | ||
| 1193 | #define FLAG_FLASHING 15 | ||
| 1194 | #define FLAG_WPA_CAPABLE 16 | ||
| 1195 | unsigned long flags; | ||
| 1196 | #define JOB_DIE 0 | ||
| 1197 | #define JOB_XMIT 1 | ||
| 1198 | #define JOB_XMIT11 2 | ||
| 1199 | #define JOB_STATS 3 | ||
| 1200 | #define JOB_PROMISC 4 | ||
| 1201 | #define JOB_MIC 5 | ||
| 1202 | #define JOB_EVENT 6 | ||
| 1203 | #define JOB_AUTOWEP 7 | ||
| 1204 | #define JOB_SCAN_RESULTS 9 | ||
| 1205 | unsigned long jobs; | ||
| 1206 | int (*bap_read)(struct airo_info*, __le16 *pu16Dst, int bytelen, | ||
| 1207 | int whichbap); | ||
| 1208 | unsigned short *flash; | ||
| 1209 | tdsRssiEntry *rssi; | ||
| 1210 | struct task_struct *list_bss_task; | ||
| 1211 | struct task_struct *airo_thread_task; | ||
| 1212 | struct semaphore sem; | ||
| 1213 | wait_queue_head_t thr_wait; | ||
| 1214 | unsigned long expires; | ||
| 1215 | struct { | ||
| 1216 | struct sk_buff *skb; | ||
| 1217 | int fid; | ||
| 1218 | } xmit, xmit11; | ||
| 1219 | struct net_device *wifidev; | ||
| 1220 | struct iw_statistics wstats; // wireless stats | ||
| 1221 | unsigned long scan_timeout; /* Time scan should be read */ | ||
| 1222 | struct iw_spy_data spy_data; | ||
| 1223 | struct iw_public_data wireless_data; | ||
| 1224 | /* MIC stuff */ | ||
| 1225 | struct crypto_sync_skcipher *tfm; | ||
| 1226 | mic_module mod[2]; | ||
| 1227 | mic_statistics micstats; | ||
| 1228 | HostRxDesc rxfids[MPI_MAX_FIDS]; // rx/tx/config MPI350 descriptors | ||
| 1229 | HostTxDesc txfids[MPI_MAX_FIDS]; | ||
| 1230 | HostRidDesc config_desc; | ||
| 1231 | unsigned long ridbus; // phys addr of config_desc | ||
| 1232 | struct sk_buff_head txq;// tx queue used by mpi350 code | ||
| 1233 | struct pci_dev *pci; | ||
| 1234 | unsigned char __iomem *pcimem; | ||
| 1235 | unsigned char __iomem *pciaux; | ||
| 1236 | unsigned char *shared; | ||
| 1237 | dma_addr_t shared_dma; | ||
| 1238 | pm_message_t power; | ||
| 1239 | SsidRid *SSID; | ||
| 1240 | APListRid APList; | ||
| 1241 | #define PCI_SHARED_LEN 2*MPI_MAX_FIDS*PKTSIZE+RIDSIZE | ||
| 1242 | char proc_name[IFNAMSIZ]; | ||
| 1243 | |||
| 1244 | int wep_capable; | ||
| 1245 | int max_wep_idx; | ||
| 1246 | int last_auth; | ||
| 1247 | |||
| 1248 | /* WPA-related stuff */ | ||
| 1249 | unsigned int bssListFirst; | ||
| 1250 | unsigned int bssListNext; | ||
| 1251 | unsigned int bssListRidLen; | ||
| 1252 | |||
| 1253 | struct list_head network_list; | ||
| 1254 | struct list_head network_free_list; | ||
| 1255 | BSSListElement *networks; | ||
| 1256 | }; | ||
| 1257 | |||
| 1258 | static inline int bap_read(struct airo_info *ai, __le16 *pu16Dst, int bytelen, | ||
| 1259 | int whichbap) | ||
| 1260 | { | ||
| 1261 | return ai->bap_read(ai, pu16Dst, bytelen, whichbap); | ||
| 1262 | } | ||
| 1263 | |||
| 1264 | static int setup_proc_entry(struct net_device *dev, | ||
| 1265 | struct airo_info *apriv); | ||
| 1266 | static int takedown_proc_entry(struct net_device *dev, | ||
| 1267 | struct airo_info *apriv); | ||
| 1268 | |||
| 1269 | static int cmdreset(struct airo_info *ai); | ||
| 1270 | static int setflashmode(struct airo_info *ai); | ||
| 1271 | static int flashgchar(struct airo_info *ai, int matchbyte, int dwelltime); | ||
| 1272 | static int flashputbuf(struct airo_info *ai); | ||
| 1273 | static int flashrestart(struct airo_info *ai, struct net_device *dev); | ||
| 1274 | |||
| 1275 | #define airo_print(type, name, fmt, args...) \ | ||
| 1276 | printk(type DRV_NAME "(%s): " fmt "\n", name, ##args) | ||
| 1277 | |||
| 1278 | #define airo_print_info(name, fmt, args...) \ | ||
| 1279 | airo_print(KERN_INFO, name, fmt, ##args) | ||
| 1280 | |||
| 1281 | #define airo_print_dbg(name, fmt, args...) \ | ||
| 1282 | airo_print(KERN_DEBUG, name, fmt, ##args) | ||
| 1283 | |||
| 1284 | #define airo_print_warn(name, fmt, args...) \ | ||
| 1285 | airo_print(KERN_WARNING, name, fmt, ##args) | ||
| 1286 | |||
| 1287 | #define airo_print_err(name, fmt, args...) \ | ||
| 1288 | airo_print(KERN_ERR, name, fmt, ##args) | ||
| 1289 | |||
| 1290 | #define AIRO_FLASH(dev) (((struct airo_info *)dev->ml_priv)->flash) | ||
| 1291 | |||
| 1292 | /*********************************************************************** | ||
| 1293 | * MIC ROUTINES * | ||
| 1294 | *********************************************************************** | ||
| 1295 | */ | ||
| 1296 | |||
| 1297 | static int RxSeqValid(struct airo_info *ai, miccntx *context, int mcast, u32 micSeq); | ||
| 1298 | static void MoveWindow(miccntx *context, u32 micSeq); | ||
| 1299 | static void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen, | ||
| 1300 | struct crypto_sync_skcipher *tfm); | ||
| 1301 | static void emmh32_init(emmh32_context *context); | ||
| 1302 | static void emmh32_update(emmh32_context *context, u8 *pOctets, int len); | ||
| 1303 | static void emmh32_final(emmh32_context *context, u8 digest[4]); | ||
| 1304 | static int flashpchar(struct airo_info *ai, int byte, int dwelltime); | ||
| 1305 | |||
| 1306 | static void age_mic_context(miccntx *cur, miccntx *old, u8 *key, int key_len, | ||
| 1307 | struct crypto_sync_skcipher *tfm) | ||
| 1308 | { | ||
| 1309 | /* If the current MIC context is valid and its key is the same as | ||
| 1310 | * the MIC register, there's nothing to do. | ||
| 1311 | */ | ||
| 1312 | if (cur->valid && (memcmp(cur->key, key, key_len) == 0)) | ||
| 1313 | return; | ||
| 1314 | |||
| 1315 | /* Age current mic Context */ | ||
| 1316 | memcpy(old, cur, sizeof(*cur)); | ||
| 1317 | |||
| 1318 | /* Initialize new context */ | ||
| 1319 | memcpy(cur->key, key, key_len); | ||
| 1320 | cur->window = 33; /* Window always points to the middle */ | ||
| 1321 | cur->rx = 0; /* Rx Sequence numbers */ | ||
| 1322 | cur->tx = 0; /* Tx sequence numbers */ | ||
| 1323 | cur->valid = 1; /* Key is now valid */ | ||
| 1324 | |||
| 1325 | /* Give key to mic seed */ | ||
| 1326 | emmh32_setseed(&cur->seed, key, key_len, tfm); | ||
| 1327 | } | ||
| 1328 | |||
| 1329 | /* micinit - Initialize mic seed */ | ||
| 1330 | |||
| 1331 | static void micinit(struct airo_info *ai) | ||
| 1332 | { | ||
| 1333 | MICRid mic_rid; | ||
| 1334 | |||
| 1335 | clear_bit(JOB_MIC, &ai->jobs); | ||
| 1336 | PC4500_readrid(ai, RID_MIC, &mic_rid, sizeof(mic_rid), 0); | ||
| 1337 | up(&ai->sem); | ||
| 1338 | |||
| 1339 | ai->micstats.enabled = (le16_to_cpu(mic_rid.state) & 0x00FF) ? 1 : 0; | ||
| 1340 | if (!ai->micstats.enabled) { | ||
| 1341 | /* So next time we have a valid key and mic is enabled, we will | ||
| 1342 | * update the sequence number if the key is the same as before. | ||
| 1343 | */ | ||
| 1344 | ai->mod[0].uCtx.valid = 0; | ||
| 1345 | ai->mod[0].mCtx.valid = 0; | ||
| 1346 | return; | ||
| 1347 | } | ||
| 1348 | |||
| 1349 | if (mic_rid.multicastValid) { | ||
| 1350 | age_mic_context(&ai->mod[0].mCtx, &ai->mod[1].mCtx, | ||
| 1351 | mic_rid.multicast, sizeof(mic_rid.multicast), | ||
| 1352 | ai->tfm); | ||
| 1353 | } | ||
| 1354 | |||
| 1355 | if (mic_rid.unicastValid) { | ||
| 1356 | age_mic_context(&ai->mod[0].uCtx, &ai->mod[1].uCtx, | ||
| 1357 | mic_rid.unicast, sizeof(mic_rid.unicast), | ||
| 1358 | ai->tfm); | ||
| 1359 | } | ||
| 1360 | } | ||
| 1361 | |||
| 1362 | /* micsetup - Get ready for business */ | ||
| 1363 | |||
| 1364 | static int micsetup(struct airo_info *ai) | ||
| 1365 | { | ||
| 1366 | int i; | ||
| 1367 | |||
| 1368 | if (ai->tfm == NULL) | ||
| 1369 | ai->tfm = crypto_alloc_sync_skcipher("ctr(aes)", 0, 0); | ||
| 1370 | |||
| 1371 | if (IS_ERR(ai->tfm)) { | ||
| 1372 | airo_print_err(ai->dev->name, "failed to load transform for AES"); | ||
| 1373 | ai->tfm = NULL; | ||
| 1374 | return ERROR; | ||
| 1375 | } | ||
| 1376 | |||
| 1377 | for (i = 0; i < NUM_MODULES; i++) { | ||
| 1378 | memset(&ai->mod[i].mCtx, 0, sizeof(miccntx)); | ||
| 1379 | memset(&ai->mod[i].uCtx, 0, sizeof(miccntx)); | ||
| 1380 | } | ||
| 1381 | return SUCCESS; | ||
| 1382 | } | ||
| 1383 | |||
| 1384 | static const u8 micsnap[] = {0xAA, 0xAA, 0x03, 0x00, 0x40, 0x96, 0x00, 0x02}; | ||
| 1385 | |||
| 1386 | /*=========================================================================== | ||
| 1387 | * Description: Mic a packet | ||
| 1388 | * | ||
| 1389 | * Inputs: etherHead * pointer to an 802.3 frame | ||
| 1390 | * | ||
| 1391 | * Returns: BOOLEAN if successful, otherwise false. | ||
| 1392 | * PacketTxLen will be updated with the mic'd packets size. | ||
| 1393 | * | ||
| 1394 | * Caveats: It is assumed that the frame buffer will already | ||
| 1395 | * be big enough to hold the largets mic message possible. | ||
| 1396 | * (No memory allocation is done here). | ||
| 1397 | * | ||
| 1398 | * Author: sbraneky (10/15/01) | ||
| 1399 | * Merciless hacks by rwilcher (1/14/02) | ||
| 1400 | */ | ||
| 1401 | |||
| 1402 | static int encapsulate(struct airo_info *ai, etherHead *frame, MICBuffer *mic, int payLen) | ||
| 1403 | { | ||
| 1404 | miccntx *context; | ||
| 1405 | |||
| 1406 | // Determine correct context | ||
| 1407 | // If not adhoc, always use unicast key | ||
| 1408 | |||
| 1409 | if (test_bit(FLAG_ADHOC, &ai->flags) && (frame->da[0] & 0x1)) | ||
| 1410 | context = &ai->mod[0].mCtx; | ||
| 1411 | else | ||
| 1412 | context = &ai->mod[0].uCtx; | ||
| 1413 | |||
| 1414 | if (!context->valid) | ||
| 1415 | return ERROR; | ||
| 1416 | |||
| 1417 | mic->typelen = htons(payLen + 16); //Length of Mic'd packet | ||
| 1418 | |||
| 1419 | memcpy(&mic->u.snap, micsnap, sizeof(micsnap)); // Add Snap | ||
| 1420 | |||
| 1421 | // Add Tx sequence | ||
| 1422 | mic->seq = htonl(context->tx); | ||
| 1423 | context->tx += 2; | ||
| 1424 | |||
| 1425 | emmh32_init(&context->seed); // Mic the packet | ||
| 1426 | emmh32_update(&context->seed, frame->da, ETH_ALEN * 2); // DA, SA | ||
| 1427 | emmh32_update(&context->seed, (u8*)&mic->typelen, 10); // Type/Length and Snap | ||
| 1428 | emmh32_update(&context->seed, (u8*)&mic->seq, sizeof(mic->seq)); //SEQ | ||
| 1429 | emmh32_update(&context->seed, (u8*)(frame + 1), payLen); //payload | ||
| 1430 | emmh32_final(&context->seed, (u8*)&mic->mic); | ||
| 1431 | |||
| 1432 | /* New Type/length ?????????? */ | ||
| 1433 | mic->typelen = 0; //Let NIC know it could be an oversized packet | ||
| 1434 | return SUCCESS; | ||
| 1435 | } | ||
| 1436 | |||
| 1437 | typedef enum { | ||
| 1438 | NONE, | ||
| 1439 | NOMIC, | ||
| 1440 | NOMICPLUMMED, | ||
| 1441 | SEQUENCE, | ||
| 1442 | INCORRECTMIC, | ||
| 1443 | } mic_error; | ||
| 1444 | |||
| 1445 | /*=========================================================================== | ||
| 1446 | * Description: Decapsulates a MIC'd packet and returns the 802.3 packet | ||
| 1447 | * (removes the MIC stuff) if packet is a valid packet. | ||
| 1448 | * | ||
| 1449 | * Inputs: etherHead pointer to the 802.3 packet | ||
| 1450 | * | ||
| 1451 | * Returns: BOOLEAN - TRUE if packet should be dropped otherwise FALSE | ||
| 1452 | * | ||
| 1453 | * Author: sbraneky (10/15/01) | ||
| 1454 | * Merciless hacks by rwilcher (1/14/02) | ||
| 1455 | *--------------------------------------------------------------------------- | ||
| 1456 | */ | ||
| 1457 | |||
| 1458 | static int decapsulate(struct airo_info *ai, MICBuffer *mic, etherHead *eth, u16 payLen) | ||
| 1459 | { | ||
| 1460 | int i; | ||
| 1461 | u32 micSEQ; | ||
| 1462 | miccntx *context; | ||
| 1463 | u8 digest[4]; | ||
| 1464 | mic_error micError = NONE; | ||
| 1465 | |||
| 1466 | // Check if the packet is a Mic'd packet | ||
| 1467 | |||
| 1468 | if (!ai->micstats.enabled) { | ||
| 1469 | //No Mic set or Mic OFF but we received a MIC'd packet. | ||
| 1470 | if (memcmp ((u8*)eth + 14, micsnap, sizeof(micsnap)) == 0) { | ||
| 1471 | ai->micstats.rxMICPlummed++; | ||
| 1472 | return ERROR; | ||
| 1473 | } | ||
| 1474 | return SUCCESS; | ||
| 1475 | } | ||
| 1476 | |||
| 1477 | if (ntohs(mic->typelen) == 0x888E) | ||
| 1478 | return SUCCESS; | ||
| 1479 | |||
| 1480 | if (memcmp (mic->u.snap, micsnap, sizeof(micsnap)) != 0) { | ||
| 1481 | // Mic enabled but packet isn't Mic'd | ||
| 1482 | ai->micstats.rxMICPlummed++; | ||
| 1483 | return ERROR; | ||
| 1484 | } | ||
| 1485 | |||
| 1486 | micSEQ = ntohl(mic->seq); //store SEQ as CPU order | ||
| 1487 | |||
| 1488 | //At this point we a have a mic'd packet and mic is enabled | ||
| 1489 | //Now do the mic error checking. | ||
| 1490 | |||
| 1491 | //Receive seq must be odd | ||
| 1492 | if ((micSEQ & 1) == 0) { | ||
| 1493 | ai->micstats.rxWrongSequence++; | ||
| 1494 | return ERROR; | ||
| 1495 | } | ||
| 1496 | |||
| 1497 | for (i = 0; i < NUM_MODULES; i++) { | ||
| 1498 | int mcast = eth->da[0] & 1; | ||
| 1499 | //Determine proper context | ||
| 1500 | context = mcast ? &ai->mod[i].mCtx : &ai->mod[i].uCtx; | ||
| 1501 | |||
| 1502 | //Make sure context is valid | ||
| 1503 | if (!context->valid) { | ||
| 1504 | if (i == 0) | ||
| 1505 | micError = NOMICPLUMMED; | ||
| 1506 | continue; | ||
| 1507 | } | ||
| 1508 | //DeMic it | ||
| 1509 | |||
| 1510 | if (!mic->typelen) | ||
| 1511 | mic->typelen = htons(payLen + sizeof(MICBuffer) - 2); | ||
| 1512 | |||
| 1513 | emmh32_init(&context->seed); | ||
| 1514 | emmh32_update(&context->seed, eth->da, ETH_ALEN*2); | ||
| 1515 | emmh32_update(&context->seed, (u8 *)&mic->typelen, sizeof(mic->typelen)+sizeof(mic->u.snap)); | ||
| 1516 | emmh32_update(&context->seed, (u8 *)&mic->seq, sizeof(mic->seq)); | ||
| 1517 | emmh32_update(&context->seed, (u8 *)(eth + 1), payLen); | ||
| 1518 | //Calculate MIC | ||
| 1519 | emmh32_final(&context->seed, digest); | ||
| 1520 | |||
| 1521 | if (memcmp(digest, &mic->mic, 4)) { //Make sure the mics match | ||
| 1522 | //Invalid Mic | ||
| 1523 | if (i == 0) | ||
| 1524 | micError = INCORRECTMIC; | ||
| 1525 | continue; | ||
| 1526 | } | ||
| 1527 | |||
| 1528 | //Check Sequence number if mics pass | ||
| 1529 | if (RxSeqValid(ai, context, mcast, micSEQ) == SUCCESS) { | ||
| 1530 | ai->micstats.rxSuccess++; | ||
| 1531 | return SUCCESS; | ||
| 1532 | } | ||
| 1533 | if (i == 0) | ||
| 1534 | micError = SEQUENCE; | ||
| 1535 | } | ||
| 1536 | |||
| 1537 | // Update statistics | ||
| 1538 | switch (micError) { | ||
| 1539 | case NOMICPLUMMED: ai->micstats.rxMICPlummed++; break; | ||
| 1540 | case SEQUENCE: ai->micstats.rxWrongSequence++; break; | ||
| 1541 | case INCORRECTMIC: ai->micstats.rxIncorrectMIC++; break; | ||
| 1542 | case NONE: break; | ||
| 1543 | case NOMIC: break; | ||
| 1544 | } | ||
| 1545 | return ERROR; | ||
| 1546 | } | ||
| 1547 | |||
| 1548 | /*=========================================================================== | ||
| 1549 | * Description: Checks the Rx Seq number to make sure it is valid | ||
| 1550 | * and hasn't already been received | ||
| 1551 | * | ||
| 1552 | * Inputs: miccntx - mic context to check seq against | ||
| 1553 | * micSeq - the Mic seq number | ||
| 1554 | * | ||
| 1555 | * Returns: TRUE if valid otherwise FALSE. | ||
| 1556 | * | ||
| 1557 | * Author: sbraneky (10/15/01) | ||
| 1558 | * Merciless hacks by rwilcher (1/14/02) | ||
| 1559 | *--------------------------------------------------------------------------- | ||
| 1560 | */ | ||
| 1561 | |||
| 1562 | static int RxSeqValid(struct airo_info *ai, miccntx *context, int mcast, u32 micSeq) | ||
| 1563 | { | ||
| 1564 | u32 seq, index; | ||
| 1565 | |||
| 1566 | //Allow for the ap being rebooted - if it is then use the next | ||
| 1567 | //sequence number of the current sequence number - might go backwards | ||
| 1568 | |||
| 1569 | if (mcast) { | ||
| 1570 | if (test_bit(FLAG_UPDATE_MULTI, &ai->flags)) { | ||
| 1571 | clear_bit (FLAG_UPDATE_MULTI, &ai->flags); | ||
| 1572 | context->window = (micSeq > 33) ? micSeq : 33; | ||
| 1573 | context->rx = 0; // Reset rx | ||
| 1574 | } | ||
| 1575 | } else if (test_bit(FLAG_UPDATE_UNI, &ai->flags)) { | ||
| 1576 | clear_bit (FLAG_UPDATE_UNI, &ai->flags); | ||
| 1577 | context->window = (micSeq > 33) ? micSeq : 33; // Move window | ||
| 1578 | context->rx = 0; // Reset rx | ||
| 1579 | } | ||
| 1580 | |||
| 1581 | //Make sequence number relative to START of window | ||
| 1582 | seq = micSeq - (context->window - 33); | ||
| 1583 | |||
| 1584 | //Too old of a SEQ number to check. | ||
| 1585 | if ((s32)seq < 0) | ||
| 1586 | return ERROR; | ||
| 1587 | |||
| 1588 | if (seq > 64) { | ||
| 1589 | //Window is infinite forward | ||
| 1590 | MoveWindow(context, micSeq); | ||
| 1591 | return SUCCESS; | ||
| 1592 | } | ||
| 1593 | |||
| 1594 | // We are in the window. Now check the context rx bit to see if it was already sent | ||
| 1595 | seq >>= 1; //divide by 2 because we only have odd numbers | ||
| 1596 | index = 1 << seq; //Get an index number | ||
| 1597 | |||
| 1598 | if (!(context->rx & index)) { | ||
| 1599 | //micSEQ falls inside the window. | ||
| 1600 | //Add seqence number to the list of received numbers. | ||
| 1601 | context->rx |= index; | ||
| 1602 | |||
| 1603 | MoveWindow(context, micSeq); | ||
| 1604 | |||
| 1605 | return SUCCESS; | ||
| 1606 | } | ||
| 1607 | return ERROR; | ||
| 1608 | } | ||
| 1609 | |||
| 1610 | static void MoveWindow(miccntx *context, u32 micSeq) | ||
| 1611 | { | ||
| 1612 | u32 shift; | ||
| 1613 | |||
| 1614 | //Move window if seq greater than the middle of the window | ||
| 1615 | if (micSeq > context->window) { | ||
| 1616 | shift = (micSeq - context->window) >> 1; | ||
| 1617 | |||
| 1618 | //Shift out old | ||
| 1619 | if (shift < 32) | ||
| 1620 | context->rx >>= shift; | ||
| 1621 | else | ||
| 1622 | context->rx = 0; | ||
| 1623 | |||
| 1624 | context->window = micSeq; //Move window | ||
| 1625 | } | ||
| 1626 | } | ||
| 1627 | |||
| 1628 | /*==============================================*/ | ||
| 1629 | /*========== EMMH ROUTINES ====================*/ | ||
| 1630 | /*==============================================*/ | ||
| 1631 | |||
| 1632 | /* mic accumulate */ | ||
| 1633 | #define MIC_ACCUM(val) \ | ||
| 1634 | context->accum += (u64)(val) * be32_to_cpu(context->coeff[coeff_position++]); | ||
| 1635 | |||
| 1636 | /* expand the key to fill the MMH coefficient array */ | ||
| 1637 | static void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen, | ||
| 1638 | struct crypto_sync_skcipher *tfm) | ||
| 1639 | { | ||
| 1640 | /* take the keying material, expand if necessary, truncate at 16-bytes */ | ||
| 1641 | /* run through AES counter mode to generate context->coeff[] */ | ||
| 1642 | |||
| 1643 | SYNC_SKCIPHER_REQUEST_ON_STACK(req, tfm); | ||
| 1644 | struct scatterlist sg; | ||
| 1645 | u8 iv[AES_BLOCK_SIZE] = {}; | ||
| 1646 | int ret; | ||
| 1647 | |||
| 1648 | crypto_sync_skcipher_setkey(tfm, pkey, 16); | ||
| 1649 | |||
| 1650 | memset(context->coeff, 0, sizeof(context->coeff)); | ||
| 1651 | sg_init_one(&sg, context->coeff, sizeof(context->coeff)); | ||
| 1652 | |||
| 1653 | skcipher_request_set_sync_tfm(req, tfm); | ||
| 1654 | skcipher_request_set_callback(req, 0, NULL, NULL); | ||
| 1655 | skcipher_request_set_crypt(req, &sg, &sg, sizeof(context->coeff), iv); | ||
| 1656 | |||
| 1657 | ret = crypto_skcipher_encrypt(req); | ||
| 1658 | WARN_ON_ONCE(ret); | ||
| 1659 | } | ||
| 1660 | |||
| 1661 | /* prepare for calculation of a new mic */ | ||
| 1662 | static void emmh32_init(emmh32_context *context) | ||
| 1663 | { | ||
| 1664 | /* prepare for new mic calculation */ | ||
| 1665 | context->accum = 0; | ||
| 1666 | context->position = 0; | ||
| 1667 | } | ||
| 1668 | |||
| 1669 | /* add some bytes to the mic calculation */ | ||
| 1670 | static void emmh32_update(emmh32_context *context, u8 *pOctets, int len) | ||
| 1671 | { | ||
| 1672 | int coeff_position, byte_position; | ||
| 1673 | |||
| 1674 | if (len == 0) return; | ||
| 1675 | |||
| 1676 | coeff_position = context->position >> 2; | ||
| 1677 | |||
| 1678 | /* deal with partial 32-bit word left over from last update */ | ||
| 1679 | byte_position = context->position & 3; | ||
| 1680 | if (byte_position) { | ||
| 1681 | /* have a partial word in part to deal with */ | ||
| 1682 | do { | ||
| 1683 | if (len == 0) return; | ||
| 1684 | context->part.d8[byte_position++] = *pOctets++; | ||
| 1685 | context->position++; | ||
| 1686 | len--; | ||
| 1687 | } while (byte_position < 4); | ||
| 1688 | MIC_ACCUM(ntohl(context->part.d32)); | ||
| 1689 | } | ||
| 1690 | |||
| 1691 | /* deal with full 32-bit words */ | ||
| 1692 | while (len >= 4) { | ||
| 1693 | MIC_ACCUM(ntohl(*(__be32 *)pOctets)); | ||
| 1694 | context->position += 4; | ||
| 1695 | pOctets += 4; | ||
| 1696 | len -= 4; | ||
| 1697 | } | ||
| 1698 | |||
| 1699 | /* deal with partial 32-bit word that will be left over from this update */ | ||
| 1700 | byte_position = 0; | ||
| 1701 | while (len > 0) { | ||
| 1702 | context->part.d8[byte_position++] = *pOctets++; | ||
| 1703 | context->position++; | ||
| 1704 | len--; | ||
| 1705 | } | ||
| 1706 | } | ||
| 1707 | |||
| 1708 | /* mask used to zero empty bytes for final partial word */ | ||
| 1709 | static u32 mask32[4] = { 0x00000000L, 0xFF000000L, 0xFFFF0000L, 0xFFFFFF00L }; | ||
| 1710 | |||
| 1711 | /* calculate the mic */ | ||
| 1712 | static void emmh32_final(emmh32_context *context, u8 digest[4]) | ||
| 1713 | { | ||
| 1714 | int coeff_position, byte_position; | ||
| 1715 | u32 val; | ||
| 1716 | |||
| 1717 | u64 sum, utmp; | ||
| 1718 | s64 stmp; | ||
| 1719 | |||
| 1720 | coeff_position = context->position >> 2; | ||
| 1721 | |||
| 1722 | /* deal with partial 32-bit word left over from last update */ | ||
| 1723 | byte_position = context->position & 3; | ||
| 1724 | if (byte_position) { | ||
| 1725 | /* have a partial word in part to deal with */ | ||
| 1726 | val = ntohl(context->part.d32); | ||
| 1727 | MIC_ACCUM(val & mask32[byte_position]); /* zero empty bytes */ | ||
| 1728 | } | ||
| 1729 | |||
| 1730 | /* reduce the accumulated u64 to a 32-bit MIC */ | ||
| 1731 | sum = context->accum; | ||
| 1732 | stmp = (sum & 0xffffffffLL) - ((sum >> 32) * 15); | ||
| 1733 | utmp = (stmp & 0xffffffffLL) - ((stmp >> 32) * 15); | ||
| 1734 | sum = utmp & 0xffffffffLL; | ||
| 1735 | if (utmp > 0x10000000fLL) | ||
| 1736 | sum -= 15; | ||
| 1737 | |||
| 1738 | val = (u32)sum; | ||
| 1739 | digest[0] = (val>>24) & 0xFF; | ||
| 1740 | digest[1] = (val>>16) & 0xFF; | ||
| 1741 | digest[2] = (val>>8) & 0xFF; | ||
| 1742 | digest[3] = val & 0xFF; | ||
| 1743 | } | ||
| 1744 | |||
| 1745 | static int readBSSListRid(struct airo_info *ai, int first, | ||
| 1746 | BSSListRid *list) | ||
| 1747 | { | ||
| 1748 | Cmd cmd; | ||
| 1749 | Resp rsp; | ||
| 1750 | |||
| 1751 | if (first == 1) { | ||
| 1752 | if (ai->flags & FLAG_RADIO_MASK) return -ENETDOWN; | ||
| 1753 | memset(&cmd, 0, sizeof(cmd)); | ||
| 1754 | cmd.cmd = CMD_LISTBSS; | ||
| 1755 | if (down_interruptible(&ai->sem)) | ||
| 1756 | return -ERESTARTSYS; | ||
| 1757 | ai->list_bss_task = current; | ||
| 1758 | issuecommand(ai, &cmd, &rsp, true); | ||
| 1759 | up(&ai->sem); | ||
| 1760 | /* Let the command take effect */ | ||
| 1761 | schedule_timeout_uninterruptible(3 * HZ); | ||
| 1762 | ai->list_bss_task = NULL; | ||
| 1763 | } | ||
| 1764 | return PC4500_readrid(ai, first ? ai->bssListFirst : ai->bssListNext, | ||
| 1765 | list, ai->bssListRidLen, 1); | ||
| 1766 | } | ||
| 1767 | |||
| 1768 | static int readWepKeyRid(struct airo_info *ai, WepKeyRid *wkr, int temp, int lock) | ||
| 1769 | { | ||
| 1770 | return PC4500_readrid(ai, temp ? RID_WEP_TEMP : RID_WEP_PERM, | ||
| 1771 | wkr, sizeof(*wkr), lock); | ||
| 1772 | } | ||
| 1773 | |||
| 1774 | static int writeWepKeyRid(struct airo_info *ai, WepKeyRid *wkr, int perm, int lock) | ||
| 1775 | { | ||
| 1776 | int rc; | ||
| 1777 | rc = PC4500_writerid(ai, RID_WEP_TEMP, wkr, sizeof(*wkr), lock); | ||
| 1778 | if (rc!=SUCCESS) | ||
| 1779 | airo_print_err(ai->dev->name, "WEP_TEMP set %x", rc); | ||
| 1780 | if (perm) { | ||
| 1781 | rc = PC4500_writerid(ai, RID_WEP_PERM, wkr, sizeof(*wkr), lock); | ||
| 1782 | if (rc!=SUCCESS) | ||
| 1783 | airo_print_err(ai->dev->name, "WEP_PERM set %x", rc); | ||
| 1784 | } | ||
| 1785 | return rc; | ||
| 1786 | } | ||
| 1787 | |||
| 1788 | static int readSsidRid(struct airo_info*ai, SsidRid *ssidr) | ||
| 1789 | { | ||
| 1790 | return PC4500_readrid(ai, RID_SSID, ssidr, sizeof(*ssidr), 1); | ||
| 1791 | } | ||
| 1792 | |||
| 1793 | static int writeSsidRid(struct airo_info*ai, SsidRid *pssidr, int lock) | ||
| 1794 | { | ||
| 1795 | return PC4500_writerid(ai, RID_SSID, pssidr, sizeof(*pssidr), lock); | ||
| 1796 | } | ||
| 1797 | |||
| 1798 | static int readConfigRid(struct airo_info *ai, int lock) | ||
| 1799 | { | ||
| 1800 | int rc; | ||
| 1801 | ConfigRid cfg; | ||
| 1802 | |||
| 1803 | if (ai->config.len) | ||
| 1804 | return SUCCESS; | ||
| 1805 | |||
| 1806 | rc = PC4500_readrid(ai, RID_ACTUALCONFIG, &cfg, sizeof(cfg), lock); | ||
| 1807 | if (rc != SUCCESS) | ||
| 1808 | return rc; | ||
| 1809 | |||
| 1810 | ai->config = cfg; | ||
| 1811 | return SUCCESS; | ||
| 1812 | } | ||
| 1813 | |||
| 1814 | static inline void checkThrottle(struct airo_info *ai) | ||
| 1815 | { | ||
| 1816 | int i; | ||
| 1817 | /* Old hardware had a limit on encryption speed */ | ||
| 1818 | if (ai->config.authType != AUTH_OPEN && maxencrypt) { | ||
| 1819 | for (i = 0; i<8; i++) { | ||
| 1820 | if (ai->config.rates[i] > maxencrypt) { | ||
| 1821 | ai->config.rates[i] = 0; | ||
| 1822 | } | ||
| 1823 | } | ||
| 1824 | } | ||
| 1825 | } | ||
| 1826 | |||
| 1827 | static int writeConfigRid(struct airo_info *ai, int lock) | ||
| 1828 | { | ||
| 1829 | ConfigRid cfgr; | ||
| 1830 | |||
| 1831 | if (!test_bit (FLAG_COMMIT, &ai->flags)) | ||
| 1832 | return SUCCESS; | ||
| 1833 | |||
| 1834 | clear_bit (FLAG_COMMIT, &ai->flags); | ||
| 1835 | clear_bit (FLAG_RESET, &ai->flags); | ||
| 1836 | checkThrottle(ai); | ||
| 1837 | cfgr = ai->config; | ||
| 1838 | |||
| 1839 | if ((cfgr.opmode & MODE_CFG_MASK) == MODE_STA_IBSS) | ||
| 1840 | set_bit(FLAG_ADHOC, &ai->flags); | ||
| 1841 | else | ||
| 1842 | clear_bit(FLAG_ADHOC, &ai->flags); | ||
| 1843 | |||
| 1844 | return PC4500_writerid(ai, RID_CONFIG, &cfgr, sizeof(cfgr), lock); | ||
| 1845 | } | ||
| 1846 | |||
| 1847 | static int readStatusRid(struct airo_info *ai, StatusRid *statr, int lock) | ||
| 1848 | { | ||
| 1849 | return PC4500_readrid(ai, RID_STATUS, statr, sizeof(*statr), lock); | ||
| 1850 | } | ||
| 1851 | |||
| 1852 | static int writeAPListRid(struct airo_info *ai, APListRid *aplr, int lock) | ||
| 1853 | { | ||
| 1854 | return PC4500_writerid(ai, RID_APLIST, aplr, sizeof(*aplr), lock); | ||
| 1855 | } | ||
| 1856 | |||
| 1857 | static int readCapabilityRid(struct airo_info *ai, CapabilityRid *capr, int lock) | ||
| 1858 | { | ||
| 1859 | return PC4500_readrid(ai, RID_CAPABILITIES, capr, sizeof(*capr), lock); | ||
| 1860 | } | ||
| 1861 | |||
| 1862 | static int readStatsRid(struct airo_info*ai, StatsRid *sr, int rid, int lock) | ||
| 1863 | { | ||
| 1864 | return PC4500_readrid(ai, rid, sr, sizeof(*sr), lock); | ||
| 1865 | } | ||
| 1866 | |||
| 1867 | static void try_auto_wep(struct airo_info *ai) | ||
| 1868 | { | ||
| 1869 | if (auto_wep && !test_bit(FLAG_RADIO_DOWN, &ai->flags)) { | ||
| 1870 | ai->expires = RUN_AT(3*HZ); | ||
| 1871 | wake_up_interruptible(&ai->thr_wait); | ||
| 1872 | } | ||
| 1873 | } | ||
| 1874 | |||
| 1875 | static int airo_open(struct net_device *dev) | ||
| 1876 | { | ||
| 1877 | struct airo_info *ai = dev->ml_priv; | ||
| 1878 | int rc = 0; | ||
| 1879 | |||
| 1880 | if (test_bit(FLAG_FLASHING, &ai->flags)) | ||
| 1881 | return -EIO; | ||
| 1882 | |||
| 1883 | /* Make sure the card is configured. | ||
| 1884 | * Wireless Extensions may postpone config changes until the card | ||
| 1885 | * is open (to pipeline changes and speed-up card setup). If | ||
| 1886 | * those changes are not yet committed, do it now - Jean II */ | ||
| 1887 | if (test_bit(FLAG_COMMIT, &ai->flags)) { | ||
| 1888 | disable_MAC(ai, 1); | ||
| 1889 | writeConfigRid(ai, 1); | ||
| 1890 | } | ||
| 1891 | |||
| 1892 | if (ai->wifidev != dev) { | ||
| 1893 | clear_bit(JOB_DIE, &ai->jobs); | ||
| 1894 | ai->airo_thread_task = kthread_run(airo_thread, dev, "%s", | ||
| 1895 | dev->name); | ||
| 1896 | if (IS_ERR(ai->airo_thread_task)) | ||
| 1897 | return (int)PTR_ERR(ai->airo_thread_task); | ||
| 1898 | |||
| 1899 | rc = request_irq(dev->irq, airo_interrupt, IRQF_SHARED, | ||
| 1900 | dev->name, dev); | ||
| 1901 | if (rc) { | ||
| 1902 | airo_print_err(dev->name, | ||
| 1903 | "register interrupt %d failed, rc %d", | ||
| 1904 | dev->irq, rc); | ||
| 1905 | set_bit(JOB_DIE, &ai->jobs); | ||
| 1906 | kthread_stop(ai->airo_thread_task); | ||
| 1907 | return rc; | ||
| 1908 | } | ||
| 1909 | |||
| 1910 | /* Power on the MAC controller (which may have been disabled) */ | ||
| 1911 | clear_bit(FLAG_RADIO_DOWN, &ai->flags); | ||
| 1912 | enable_interrupts(ai); | ||
| 1913 | |||
| 1914 | try_auto_wep(ai); | ||
| 1915 | } | ||
| 1916 | enable_MAC(ai, 1); | ||
| 1917 | |||
| 1918 | netif_start_queue(dev); | ||
| 1919 | return 0; | ||
| 1920 | } | ||
| 1921 | |||
| 1922 | static netdev_tx_t mpi_start_xmit(struct sk_buff *skb, | ||
| 1923 | struct net_device *dev) | ||
| 1924 | { | ||
| 1925 | int npacks, pending; | ||
| 1926 | unsigned long flags; | ||
| 1927 | struct airo_info *ai = dev->ml_priv; | ||
| 1928 | |||
| 1929 | if (!skb) { | ||
| 1930 | airo_print_err(dev->name, "%s: skb == NULL!",__func__); | ||
| 1931 | return NETDEV_TX_OK; | ||
| 1932 | } | ||
| 1933 | if (skb_padto(skb, ETH_ZLEN)) { | ||
| 1934 | dev->stats.tx_dropped++; | ||
| 1935 | return NETDEV_TX_OK; | ||
| 1936 | } | ||
| 1937 | npacks = skb_queue_len (&ai->txq); | ||
| 1938 | |||
| 1939 | if (npacks >= MAXTXQ - 1) { | ||
| 1940 | netif_stop_queue (dev); | ||
| 1941 | if (npacks > MAXTXQ) { | ||
| 1942 | dev->stats.tx_fifo_errors++; | ||
| 1943 | return NETDEV_TX_BUSY; | ||
| 1944 | } | ||
| 1945 | skb_queue_tail (&ai->txq, skb); | ||
| 1946 | return NETDEV_TX_OK; | ||
| 1947 | } | ||
| 1948 | |||
| 1949 | spin_lock_irqsave(&ai->aux_lock, flags); | ||
| 1950 | skb_queue_tail (&ai->txq, skb); | ||
| 1951 | pending = test_bit(FLAG_PENDING_XMIT, &ai->flags); | ||
| 1952 | spin_unlock_irqrestore(&ai->aux_lock, flags); | ||
| 1953 | netif_wake_queue (dev); | ||
| 1954 | |||
| 1955 | if (pending == 0) { | ||
| 1956 | set_bit(FLAG_PENDING_XMIT, &ai->flags); | ||
| 1957 | mpi_send_packet (dev); | ||
| 1958 | } | ||
| 1959 | return NETDEV_TX_OK; | ||
| 1960 | } | ||
| 1961 | |||
| 1962 | /* | ||
| 1963 | * @mpi_send_packet | ||
| 1964 | * | ||
| 1965 | * Attempt to transmit a packet. Can be called from interrupt | ||
| 1966 | * or transmit . return number of packets we tried to send | ||
| 1967 | */ | ||
| 1968 | |||
| 1969 | static int mpi_send_packet (struct net_device *dev) | ||
| 1970 | { | ||
| 1971 | struct sk_buff *skb; | ||
| 1972 | unsigned char *buffer; | ||
| 1973 | s16 len; | ||
| 1974 | __le16 *payloadLen; | ||
| 1975 | struct airo_info *ai = dev->ml_priv; | ||
| 1976 | u8 *sendbuf; | ||
| 1977 | |||
| 1978 | /* get a packet to send */ | ||
| 1979 | |||
| 1980 | if ((skb = skb_dequeue(&ai->txq)) == NULL) { | ||
| 1981 | airo_print_err(dev->name, | ||
| 1982 | "%s: Dequeue'd zero in send_packet()", | ||
| 1983 | __func__); | ||
| 1984 | return 0; | ||
| 1985 | } | ||
| 1986 | |||
| 1987 | /* check min length*/ | ||
| 1988 | len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; | ||
| 1989 | buffer = skb->data; | ||
| 1990 | |||
| 1991 | ai->txfids[0].tx_desc.offset = 0; | ||
| 1992 | ai->txfids[0].tx_desc.valid = 1; | ||
| 1993 | ai->txfids[0].tx_desc.eoc = 1; | ||
| 1994 | ai->txfids[0].tx_desc.len =len+sizeof(WifiHdr); | ||
| 1995 | |||
| 1996 | /* | ||
| 1997 | * Magic, the cards firmware needs a length count (2 bytes) in the host buffer | ||
| 1998 | * right after TXFID_HDR.The TXFID_HDR contains the status short so payloadlen | ||
| 1999 | * is immediately after it. ------------------------------------------------ | ||
| 2000 | * |TXFIDHDR+STATUS|PAYLOADLEN|802.3HDR|PACKETDATA| | ||
| 2001 | * ------------------------------------------------ | ||
| 2002 | */ | ||
| 2003 | |||
| 2004 | memcpy(ai->txfids[0].virtual_host_addr, | ||
| 2005 | (char *)&wifictlhdr8023, sizeof(wifictlhdr8023)); | ||
| 2006 | |||
| 2007 | payloadLen = (__le16 *)(ai->txfids[0].virtual_host_addr + | ||
| 2008 | sizeof(wifictlhdr8023)); | ||
| 2009 | sendbuf = ai->txfids[0].virtual_host_addr + | ||
| 2010 | sizeof(wifictlhdr8023) + 2 ; | ||
| 2011 | |||
| 2012 | /* | ||
| 2013 | * Firmware automatically puts 802 header on so | ||
| 2014 | * we don't need to account for it in the length | ||
| 2015 | */ | ||
| 2016 | if (test_bit(FLAG_MIC_CAPABLE, &ai->flags) && ai->micstats.enabled && | ||
| 2017 | (ntohs(((__be16 *)buffer)[6]) != 0x888E)) { | ||
| 2018 | MICBuffer pMic; | ||
| 2019 | |||
| 2020 | if (encapsulate(ai, (etherHead *)buffer, &pMic, len - sizeof(etherHead)) != SUCCESS) | ||
| 2021 | return ERROR; | ||
| 2022 | |||
| 2023 | *payloadLen = cpu_to_le16(len-sizeof(etherHead)+sizeof(pMic)); | ||
| 2024 | ai->txfids[0].tx_desc.len += sizeof(pMic); | ||
| 2025 | /* copy data into airo dma buffer */ | ||
| 2026 | memcpy (sendbuf, buffer, sizeof(etherHead)); | ||
| 2027 | buffer += sizeof(etherHead); | ||
| 2028 | sendbuf += sizeof(etherHead); | ||
| 2029 | memcpy (sendbuf, &pMic, sizeof(pMic)); | ||
| 2030 | sendbuf += sizeof(pMic); | ||
| 2031 | memcpy (sendbuf, buffer, len - sizeof(etherHead)); | ||
| 2032 | } else { | ||
| 2033 | *payloadLen = cpu_to_le16(len - sizeof(etherHead)); | ||
| 2034 | |||
| 2035 | netif_trans_update(dev); | ||
| 2036 | |||
| 2037 | /* copy data into airo dma buffer */ | ||
| 2038 | memcpy(sendbuf, buffer, len); | ||
| 2039 | } | ||
| 2040 | |||
| 2041 | memcpy_toio(ai->txfids[0].card_ram_off, | ||
| 2042 | &ai->txfids[0].tx_desc, sizeof(TxFid)); | ||
| 2043 | |||
| 2044 | OUT4500(ai, EVACK, 8); | ||
| 2045 | |||
| 2046 | dev_kfree_skb_any(skb); | ||
| 2047 | return 1; | ||
| 2048 | } | ||
| 2049 | |||
| 2050 | static void get_tx_error(struct airo_info *ai, s32 fid) | ||
| 2051 | { | ||
| 2052 | __le16 status; | ||
| 2053 | |||
| 2054 | if (fid < 0) | ||
| 2055 | status = ((WifiCtlHdr *)ai->txfids[0].virtual_host_addr)->ctlhdr.status; | ||
| 2056 | else { | ||
| 2057 | if (bap_setup(ai, ai->fids[fid] & 0xffff, 4, BAP0) != SUCCESS) | ||
| 2058 | return; | ||
| 2059 | bap_read(ai, &status, 2, BAP0); | ||
| 2060 | } | ||
| 2061 | if (le16_to_cpu(status) & 2) /* Too many retries */ | ||
| 2062 | ai->dev->stats.tx_aborted_errors++; | ||
| 2063 | if (le16_to_cpu(status) & 4) /* Transmit lifetime exceeded */ | ||
| 2064 | ai->dev->stats.tx_heartbeat_errors++; | ||
| 2065 | if (le16_to_cpu(status) & 8) /* Aid fail */ | ||
| 2066 | { } | ||
| 2067 | if (le16_to_cpu(status) & 0x10) /* MAC disabled */ | ||
| 2068 | ai->dev->stats.tx_carrier_errors++; | ||
| 2069 | if (le16_to_cpu(status) & 0x20) /* Association lost */ | ||
| 2070 | { } | ||
| 2071 | /* We produce a TXDROP event only for retry or lifetime | ||
| 2072 | * exceeded, because that's the only status that really mean | ||
| 2073 | * that this particular node went away. | ||
| 2074 | * Other errors means that *we* screwed up. - Jean II */ | ||
| 2075 | if ((le16_to_cpu(status) & 2) || | ||
| 2076 | (le16_to_cpu(status) & 4)) { | ||
| 2077 | union iwreq_data wrqu; | ||
| 2078 | char junk[0x18]; | ||
| 2079 | |||
| 2080 | /* Faster to skip over useless data than to do | ||
| 2081 | * another bap_setup(). We are at offset 0x6 and | ||
| 2082 | * need to go to 0x18 and read 6 bytes - Jean II */ | ||
| 2083 | bap_read(ai, (__le16 *) junk, 0x18, BAP0); | ||
| 2084 | |||
| 2085 | /* Copy 802.11 dest address. | ||
| 2086 | * We use the 802.11 header because the frame may | ||
| 2087 | * not be 802.3 or may be mangled... | ||
| 2088 | * In Ad-Hoc mode, it will be the node address. | ||
| 2089 | * In managed mode, it will be most likely the AP addr | ||
| 2090 | * User space will figure out how to convert it to | ||
| 2091 | * whatever it needs (IP address or else). | ||
| 2092 | * - Jean II */ | ||
| 2093 | memcpy(wrqu.addr.sa_data, junk + 0x12, ETH_ALEN); | ||
| 2094 | wrqu.addr.sa_family = ARPHRD_ETHER; | ||
| 2095 | |||
| 2096 | /* Send event to user space */ | ||
| 2097 | wireless_send_event(ai->dev, IWEVTXDROP, &wrqu, NULL); | ||
| 2098 | } | ||
| 2099 | } | ||
| 2100 | |||
| 2101 | static void airo_end_xmit(struct net_device *dev, bool may_sleep) | ||
| 2102 | { | ||
| 2103 | u16 status; | ||
| 2104 | int i; | ||
| 2105 | struct airo_info *priv = dev->ml_priv; | ||
| 2106 | struct sk_buff *skb = priv->xmit.skb; | ||
| 2107 | int fid = priv->xmit.fid; | ||
| 2108 | u32 *fids = priv->fids; | ||
| 2109 | |||
| 2110 | clear_bit(JOB_XMIT, &priv->jobs); | ||
| 2111 | clear_bit(FLAG_PENDING_XMIT, &priv->flags); | ||
| 2112 | status = transmit_802_3_packet(priv, fids[fid], skb->data, may_sleep); | ||
| 2113 | up(&priv->sem); | ||
| 2114 | |||
| 2115 | i = 0; | ||
| 2116 | if (status == SUCCESS) { | ||
| 2117 | netif_trans_update(dev); | ||
| 2118 | for (; i < MAX_FIDS / 2 && (priv->fids[i] & 0xffff0000); i++); | ||
| 2119 | } else { | ||
| 2120 | priv->fids[fid] &= 0xffff; | ||
| 2121 | dev->stats.tx_window_errors++; | ||
| 2122 | } | ||
| 2123 | if (i < MAX_FIDS / 2) | ||
| 2124 | netif_wake_queue(dev); | ||
| 2125 | dev_kfree_skb(skb); | ||
| 2126 | } | ||
| 2127 | |||
| 2128 | static netdev_tx_t airo_start_xmit(struct sk_buff *skb, | ||
| 2129 | struct net_device *dev) | ||
| 2130 | { | ||
| 2131 | s16 len; | ||
| 2132 | int i, j; | ||
| 2133 | struct airo_info *priv = dev->ml_priv; | ||
| 2134 | u32 *fids = priv->fids; | ||
| 2135 | |||
| 2136 | if (skb == NULL) { | ||
| 2137 | airo_print_err(dev->name, "%s: skb == NULL!", __func__); | ||
| 2138 | return NETDEV_TX_OK; | ||
| 2139 | } | ||
| 2140 | if (skb_padto(skb, ETH_ZLEN)) { | ||
| 2141 | dev->stats.tx_dropped++; | ||
| 2142 | return NETDEV_TX_OK; | ||
| 2143 | } | ||
| 2144 | |||
| 2145 | /* Find a vacant FID */ | ||
| 2146 | for (i = 0; i < MAX_FIDS / 2 && (fids[i] & 0xffff0000); i++); | ||
| 2147 | for (j = i + 1; j < MAX_FIDS / 2 && (fids[j] & 0xffff0000); j++); | ||
| 2148 | |||
| 2149 | if (j >= MAX_FIDS / 2) { | ||
| 2150 | netif_stop_queue(dev); | ||
| 2151 | |||
| 2152 | if (i == MAX_FIDS / 2) { | ||
| 2153 | dev->stats.tx_fifo_errors++; | ||
| 2154 | return NETDEV_TX_BUSY; | ||
| 2155 | } | ||
| 2156 | } | ||
| 2157 | /* check min length*/ | ||
| 2158 | len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; | ||
| 2159 | /* Mark fid as used & save length for later */ | ||
| 2160 | fids[i] |= (len << 16); | ||
| 2161 | priv->xmit.skb = skb; | ||
| 2162 | priv->xmit.fid = i; | ||
| 2163 | if (down_trylock(&priv->sem) != 0) { | ||
| 2164 | set_bit(FLAG_PENDING_XMIT, &priv->flags); | ||
| 2165 | netif_stop_queue(dev); | ||
| 2166 | set_bit(JOB_XMIT, &priv->jobs); | ||
| 2167 | wake_up_interruptible(&priv->thr_wait); | ||
| 2168 | } else | ||
| 2169 | airo_end_xmit(dev, false); | ||
| 2170 | return NETDEV_TX_OK; | ||
| 2171 | } | ||
| 2172 | |||
| 2173 | static void airo_end_xmit11(struct net_device *dev, bool may_sleep) | ||
| 2174 | { | ||
| 2175 | u16 status; | ||
| 2176 | int i; | ||
| 2177 | struct airo_info *priv = dev->ml_priv; | ||
| 2178 | struct sk_buff *skb = priv->xmit11.skb; | ||
| 2179 | int fid = priv->xmit11.fid; | ||
| 2180 | u32 *fids = priv->fids; | ||
| 2181 | |||
| 2182 | clear_bit(JOB_XMIT11, &priv->jobs); | ||
| 2183 | clear_bit(FLAG_PENDING_XMIT11, &priv->flags); | ||
| 2184 | status = transmit_802_11_packet(priv, fids[fid], skb->data, may_sleep); | ||
| 2185 | up(&priv->sem); | ||
| 2186 | |||
| 2187 | i = MAX_FIDS / 2; | ||
| 2188 | if (status == SUCCESS) { | ||
| 2189 | netif_trans_update(dev); | ||
| 2190 | for (; i < MAX_FIDS && (priv->fids[i] & 0xffff0000); i++); | ||
| 2191 | } else { | ||
| 2192 | priv->fids[fid] &= 0xffff; | ||
| 2193 | dev->stats.tx_window_errors++; | ||
| 2194 | } | ||
| 2195 | if (i < MAX_FIDS) | ||
| 2196 | netif_wake_queue(dev); | ||
| 2197 | dev_kfree_skb(skb); | ||
| 2198 | } | ||
| 2199 | |||
| 2200 | static netdev_tx_t airo_start_xmit11(struct sk_buff *skb, | ||
| 2201 | struct net_device *dev) | ||
| 2202 | { | ||
| 2203 | s16 len; | ||
| 2204 | int i, j; | ||
| 2205 | struct airo_info *priv = dev->ml_priv; | ||
| 2206 | u32 *fids = priv->fids; | ||
| 2207 | |||
| 2208 | if (test_bit(FLAG_MPI, &priv->flags)) { | ||
| 2209 | /* Not implemented yet for MPI350 */ | ||
| 2210 | netif_stop_queue(dev); | ||
| 2211 | dev_kfree_skb_any(skb); | ||
| 2212 | return NETDEV_TX_OK; | ||
| 2213 | } | ||
| 2214 | |||
| 2215 | if (skb == NULL) { | ||
| 2216 | airo_print_err(dev->name, "%s: skb == NULL!", __func__); | ||
| 2217 | return NETDEV_TX_OK; | ||
| 2218 | } | ||
| 2219 | if (skb_padto(skb, ETH_ZLEN)) { | ||
| 2220 | dev->stats.tx_dropped++; | ||
| 2221 | return NETDEV_TX_OK; | ||
| 2222 | } | ||
| 2223 | |||
| 2224 | /* Find a vacant FID */ | ||
| 2225 | for (i = MAX_FIDS / 2; i < MAX_FIDS && (fids[i] & 0xffff0000); i++); | ||
| 2226 | for (j = i + 1; j < MAX_FIDS && (fids[j] & 0xffff0000); j++); | ||
| 2227 | |||
| 2228 | if (j >= MAX_FIDS) { | ||
| 2229 | netif_stop_queue(dev); | ||
| 2230 | |||
| 2231 | if (i == MAX_FIDS) { | ||
| 2232 | dev->stats.tx_fifo_errors++; | ||
| 2233 | return NETDEV_TX_BUSY; | ||
| 2234 | } | ||
| 2235 | } | ||
| 2236 | /* check min length*/ | ||
| 2237 | len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; | ||
| 2238 | /* Mark fid as used & save length for later */ | ||
| 2239 | fids[i] |= (len << 16); | ||
| 2240 | priv->xmit11.skb = skb; | ||
| 2241 | priv->xmit11.fid = i; | ||
| 2242 | if (down_trylock(&priv->sem) != 0) { | ||
| 2243 | set_bit(FLAG_PENDING_XMIT11, &priv->flags); | ||
| 2244 | netif_stop_queue(dev); | ||
| 2245 | set_bit(JOB_XMIT11, &priv->jobs); | ||
| 2246 | wake_up_interruptible(&priv->thr_wait); | ||
| 2247 | } else | ||
| 2248 | airo_end_xmit11(dev, false); | ||
| 2249 | return NETDEV_TX_OK; | ||
| 2250 | } | ||
| 2251 | |||
| 2252 | static void airo_read_stats(struct net_device *dev) | ||
| 2253 | { | ||
| 2254 | struct airo_info *ai = dev->ml_priv; | ||
| 2255 | StatsRid stats_rid; | ||
| 2256 | __le32 *vals = stats_rid.vals; | ||
| 2257 | |||
| 2258 | clear_bit(JOB_STATS, &ai->jobs); | ||
| 2259 | if (ai->power.event) { | ||
| 2260 | up(&ai->sem); | ||
| 2261 | return; | ||
| 2262 | } | ||
| 2263 | readStatsRid(ai, &stats_rid, RID_STATS, 0); | ||
| 2264 | up(&ai->sem); | ||
| 2265 | |||
| 2266 | dev->stats.rx_packets = le32_to_cpu(vals[43]) + le32_to_cpu(vals[44]) + | ||
| 2267 | le32_to_cpu(vals[45]); | ||
| 2268 | dev->stats.tx_packets = le32_to_cpu(vals[39]) + le32_to_cpu(vals[40]) + | ||
| 2269 | le32_to_cpu(vals[41]); | ||
| 2270 | dev->stats.rx_bytes = le32_to_cpu(vals[92]); | ||
| 2271 | dev->stats.tx_bytes = le32_to_cpu(vals[91]); | ||
| 2272 | dev->stats.rx_errors = le32_to_cpu(vals[0]) + le32_to_cpu(vals[2]) + | ||
| 2273 | le32_to_cpu(vals[3]) + le32_to_cpu(vals[4]); | ||
| 2274 | dev->stats.tx_errors = le32_to_cpu(vals[42]) + | ||
| 2275 | dev->stats.tx_fifo_errors; | ||
| 2276 | dev->stats.multicast = le32_to_cpu(vals[43]); | ||
| 2277 | dev->stats.collisions = le32_to_cpu(vals[89]); | ||
| 2278 | |||
| 2279 | /* detailed rx_errors: */ | ||
| 2280 | dev->stats.rx_length_errors = le32_to_cpu(vals[3]); | ||
| 2281 | dev->stats.rx_crc_errors = le32_to_cpu(vals[4]); | ||
| 2282 | dev->stats.rx_frame_errors = le32_to_cpu(vals[2]); | ||
| 2283 | dev->stats.rx_fifo_errors = le32_to_cpu(vals[0]); | ||
| 2284 | } | ||
| 2285 | |||
| 2286 | static struct net_device_stats *airo_get_stats(struct net_device *dev) | ||
| 2287 | { | ||
| 2288 | struct airo_info *local = dev->ml_priv; | ||
| 2289 | |||
| 2290 | if (!test_bit(JOB_STATS, &local->jobs)) { | ||
| 2291 | set_bit(JOB_STATS, &local->jobs); | ||
| 2292 | wake_up_interruptible(&local->thr_wait); | ||
| 2293 | } | ||
| 2294 | |||
| 2295 | return &dev->stats; | ||
| 2296 | } | ||
| 2297 | |||
| 2298 | static void airo_set_promisc(struct airo_info *ai, bool may_sleep) | ||
| 2299 | { | ||
| 2300 | Cmd cmd; | ||
| 2301 | Resp rsp; | ||
| 2302 | |||
| 2303 | memset(&cmd, 0, sizeof(cmd)); | ||
| 2304 | cmd.cmd = CMD_SETMODE; | ||
| 2305 | clear_bit(JOB_PROMISC, &ai->jobs); | ||
| 2306 | cmd.parm0=(ai->flags&IFF_PROMISC) ? PROMISC : NOPROMISC; | ||
| 2307 | issuecommand(ai, &cmd, &rsp, may_sleep); | ||
| 2308 | up(&ai->sem); | ||
| 2309 | } | ||
| 2310 | |||
| 2311 | static void airo_set_multicast_list(struct net_device *dev) | ||
| 2312 | { | ||
| 2313 | struct airo_info *ai = dev->ml_priv; | ||
| 2314 | |||
| 2315 | if ((dev->flags ^ ai->flags) & IFF_PROMISC) { | ||
| 2316 | change_bit(FLAG_PROMISC, &ai->flags); | ||
| 2317 | if (down_trylock(&ai->sem) != 0) { | ||
| 2318 | set_bit(JOB_PROMISC, &ai->jobs); | ||
| 2319 | wake_up_interruptible(&ai->thr_wait); | ||
| 2320 | } else | ||
| 2321 | airo_set_promisc(ai, false); | ||
| 2322 | } | ||
| 2323 | |||
| 2324 | if ((dev->flags&IFF_ALLMULTI) || !netdev_mc_empty(dev)) { | ||
| 2325 | /* Turn on multicast. (Should be already setup...) */ | ||
| 2326 | } | ||
| 2327 | } | ||
| 2328 | |||
| 2329 | static int airo_set_mac_address(struct net_device *dev, void *p) | ||
| 2330 | { | ||
| 2331 | struct airo_info *ai = dev->ml_priv; | ||
| 2332 | struct sockaddr *addr = p; | ||
| 2333 | |||
| 2334 | readConfigRid(ai, 1); | ||
| 2335 | memcpy (ai->config.macAddr, addr->sa_data, dev->addr_len); | ||
| 2336 | set_bit (FLAG_COMMIT, &ai->flags); | ||
| 2337 | disable_MAC(ai, 1); | ||
| 2338 | writeConfigRid (ai, 1); | ||
| 2339 | enable_MAC(ai, 1); | ||
| 2340 | dev_addr_set(ai->dev, addr->sa_data); | ||
| 2341 | if (ai->wifidev) | ||
| 2342 | dev_addr_set(ai->wifidev, addr->sa_data); | ||
| 2343 | return 0; | ||
| 2344 | } | ||
| 2345 | |||
| 2346 | static LIST_HEAD(airo_devices); | ||
| 2347 | |||
| 2348 | static void add_airo_dev(struct airo_info *ai) | ||
| 2349 | { | ||
| 2350 | /* Upper layers already keep track of PCI devices, | ||
| 2351 | * so we only need to remember our non-PCI cards. */ | ||
| 2352 | if (!ai->pci) | ||
| 2353 | list_add_tail(&ai->dev_list, &airo_devices); | ||
| 2354 | } | ||
| 2355 | |||
| 2356 | static void del_airo_dev(struct airo_info *ai) | ||
| 2357 | { | ||
| 2358 | if (!ai->pci) | ||
| 2359 | list_del(&ai->dev_list); | ||
| 2360 | } | ||
| 2361 | |||
| 2362 | static int airo_close(struct net_device *dev) | ||
| 2363 | { | ||
| 2364 | struct airo_info *ai = dev->ml_priv; | ||
| 2365 | |||
| 2366 | netif_stop_queue(dev); | ||
| 2367 | |||
| 2368 | if (ai->wifidev != dev) { | ||
| 2369 | #ifdef POWER_ON_DOWN | ||
| 2370 | /* Shut power to the card. The idea is that the user can save | ||
| 2371 | * power when he doesn't need the card with "ifconfig down". | ||
| 2372 | * That's the method that is most friendly towards the network | ||
| 2373 | * stack (i.e. the network stack won't try to broadcast | ||
| 2374 | * anything on the interface and routes are gone. Jean II */ | ||
| 2375 | set_bit(FLAG_RADIO_DOWN, &ai->flags); | ||
| 2376 | disable_MAC(ai, 1); | ||
| 2377 | #endif | ||
| 2378 | disable_interrupts(ai); | ||
| 2379 | |||
| 2380 | free_irq(dev->irq, dev); | ||
| 2381 | |||
| 2382 | set_bit(JOB_DIE, &ai->jobs); | ||
| 2383 | kthread_stop(ai->airo_thread_task); | ||
| 2384 | } | ||
| 2385 | return 0; | ||
| 2386 | } | ||
| 2387 | |||
| 2388 | void stop_airo_card(struct net_device *dev, int freeres) | ||
| 2389 | { | ||
| 2390 | struct airo_info *ai = dev->ml_priv; | ||
| 2391 | |||
| 2392 | set_bit(FLAG_RADIO_DOWN, &ai->flags); | ||
| 2393 | disable_MAC(ai, 1); | ||
| 2394 | disable_interrupts(ai); | ||
| 2395 | takedown_proc_entry(dev, ai); | ||
| 2396 | if (test_bit(FLAG_REGISTERED, &ai->flags)) { | ||
| 2397 | unregister_netdev(dev); | ||
| 2398 | if (ai->wifidev) { | ||
| 2399 | unregister_netdev(ai->wifidev); | ||
| 2400 | free_netdev(ai->wifidev); | ||
| 2401 | ai->wifidev = NULL; | ||
| 2402 | } | ||
| 2403 | clear_bit(FLAG_REGISTERED, &ai->flags); | ||
| 2404 | } | ||
| 2405 | /* | ||
| 2406 | * Clean out tx queue | ||
| 2407 | */ | ||
| 2408 | if (test_bit(FLAG_MPI, &ai->flags) && !skb_queue_empty(&ai->txq)) { | ||
| 2409 | struct sk_buff *skb = NULL; | ||
| 2410 | for (;(skb = skb_dequeue(&ai->txq));) | ||
| 2411 | dev_kfree_skb(skb); | ||
| 2412 | } | ||
| 2413 | |||
| 2414 | airo_networks_free (ai); | ||
| 2415 | |||
| 2416 | kfree(ai->flash); | ||
| 2417 | kfree(ai->rssi); | ||
| 2418 | kfree(ai->SSID); | ||
| 2419 | if (freeres) { | ||
| 2420 | /* PCMCIA frees this stuff, so only for PCI and ISA */ | ||
| 2421 | release_region(dev->base_addr, 64); | ||
| 2422 | if (test_bit(FLAG_MPI, &ai->flags)) { | ||
| 2423 | if (ai->pci) | ||
| 2424 | mpi_unmap_card(ai->pci); | ||
| 2425 | if (ai->pcimem) | ||
| 2426 | iounmap(ai->pcimem); | ||
| 2427 | if (ai->pciaux) | ||
| 2428 | iounmap(ai->pciaux); | ||
| 2429 | dma_free_coherent(&ai->pci->dev, PCI_SHARED_LEN, | ||
| 2430 | ai->shared, ai->shared_dma); | ||
| 2431 | } | ||
| 2432 | } | ||
| 2433 | crypto_free_sync_skcipher(ai->tfm); | ||
| 2434 | del_airo_dev(ai); | ||
| 2435 | free_netdev(dev); | ||
| 2436 | } | ||
| 2437 | |||
| 2438 | EXPORT_SYMBOL(stop_airo_card); | ||
| 2439 | |||
| 2440 | static int wll_header_parse(const struct sk_buff *skb, unsigned char *haddr) | ||
| 2441 | { | ||
| 2442 | memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN); | ||
| 2443 | return ETH_ALEN; | ||
| 2444 | } | ||
| 2445 | |||
| 2446 | static void mpi_unmap_card(struct pci_dev *pci) | ||
| 2447 | { | ||
| 2448 | unsigned long mem_start = pci_resource_start(pci, 1); | ||
| 2449 | unsigned long mem_len = pci_resource_len(pci, 1); | ||
| 2450 | unsigned long aux_start = pci_resource_start(pci, 2); | ||
| 2451 | unsigned long aux_len = AUXMEMSIZE; | ||
| 2452 | |||
| 2453 | release_mem_region(aux_start, aux_len); | ||
| 2454 | release_mem_region(mem_start, mem_len); | ||
| 2455 | } | ||
| 2456 | |||
| 2457 | /************************************************************* | ||
| 2458 | * This routine assumes that descriptors have been setup . | ||
| 2459 | * Run at insmod time or after reset when the descriptors | ||
| 2460 | * have been initialized . Returns 0 if all is well nz | ||
| 2461 | * otherwise . Does not allocate memory but sets up card | ||
| 2462 | * using previously allocated descriptors. | ||
| 2463 | */ | ||
| 2464 | static int mpi_init_descriptors (struct airo_info *ai) | ||
| 2465 | { | ||
| 2466 | Cmd cmd; | ||
| 2467 | Resp rsp; | ||
| 2468 | int i; | ||
| 2469 | int rc = SUCCESS; | ||
| 2470 | |||
| 2471 | /* Alloc card RX descriptors */ | ||
| 2472 | netif_stop_queue(ai->dev); | ||
| 2473 | |||
| 2474 | memset(&rsp, 0, sizeof(rsp)); | ||
| 2475 | memset(&cmd, 0, sizeof(cmd)); | ||
| 2476 | |||
| 2477 | cmd.cmd = CMD_ALLOCATEAUX; | ||
| 2478 | cmd.parm0 = FID_RX; | ||
| 2479 | cmd.parm1 = (ai->rxfids[0].card_ram_off - ai->pciaux); | ||
| 2480 | cmd.parm2 = MPI_MAX_FIDS; | ||
| 2481 | rc = issuecommand(ai, &cmd, &rsp, true); | ||
| 2482 | if (rc != SUCCESS) { | ||
| 2483 | airo_print_err(ai->dev->name, "Couldn't allocate RX FID"); | ||
| 2484 | return rc; | ||
| 2485 | } | ||
| 2486 | |||
| 2487 | for (i = 0; i<MPI_MAX_FIDS; i++) { | ||
| 2488 | memcpy_toio(ai->rxfids[i].card_ram_off, | ||
| 2489 | &ai->rxfids[i].rx_desc, sizeof(RxFid)); | ||
| 2490 | } | ||
| 2491 | |||
| 2492 | /* Alloc card TX descriptors */ | ||
| 2493 | |||
| 2494 | memset(&rsp, 0, sizeof(rsp)); | ||
| 2495 | memset(&cmd, 0, sizeof(cmd)); | ||
| 2496 | |||
| 2497 | cmd.cmd = CMD_ALLOCATEAUX; | ||
| 2498 | cmd.parm0 = FID_TX; | ||
| 2499 | cmd.parm1 = (ai->txfids[0].card_ram_off - ai->pciaux); | ||
| 2500 | cmd.parm2 = MPI_MAX_FIDS; | ||
| 2501 | |||
| 2502 | for (i = 0; i<MPI_MAX_FIDS; i++) { | ||
| 2503 | ai->txfids[i].tx_desc.valid = 1; | ||
| 2504 | memcpy_toio(ai->txfids[i].card_ram_off, | ||
| 2505 | &ai->txfids[i].tx_desc, sizeof(TxFid)); | ||
| 2506 | } | ||
| 2507 | ai->txfids[i-1].tx_desc.eoc = 1; /* Last descriptor has EOC set */ | ||
| 2508 | |||
| 2509 | rc = issuecommand(ai, &cmd, &rsp, true); | ||
| 2510 | if (rc != SUCCESS) { | ||
| 2511 | airo_print_err(ai->dev->name, "Couldn't allocate TX FID"); | ||
| 2512 | return rc; | ||
| 2513 | } | ||
| 2514 | |||
| 2515 | /* Alloc card Rid descriptor */ | ||
| 2516 | memset(&rsp, 0, sizeof(rsp)); | ||
| 2517 | memset(&cmd, 0, sizeof(cmd)); | ||
| 2518 | |||
| 2519 | cmd.cmd = CMD_ALLOCATEAUX; | ||
| 2520 | cmd.parm0 = RID_RW; | ||
| 2521 | cmd.parm1 = (ai->config_desc.card_ram_off - ai->pciaux); | ||
| 2522 | cmd.parm2 = 1; /* Magic number... */ | ||
| 2523 | rc = issuecommand(ai, &cmd, &rsp, true); | ||
| 2524 | if (rc != SUCCESS) { | ||
| 2525 | airo_print_err(ai->dev->name, "Couldn't allocate RID"); | ||
| 2526 | return rc; | ||
| 2527 | } | ||
| 2528 | |||
| 2529 | memcpy_toio(ai->config_desc.card_ram_off, | ||
| 2530 | &ai->config_desc.rid_desc, sizeof(Rid)); | ||
| 2531 | |||
| 2532 | return rc; | ||
| 2533 | } | ||
| 2534 | |||
| 2535 | /* | ||
| 2536 | * We are setting up three things here: | ||
| 2537 | * 1) Map AUX memory for descriptors: Rid, TxFid, or RxFid. | ||
| 2538 | * 2) Map PCI memory for issuing commands. | ||
| 2539 | * 3) Allocate memory (shared) to send and receive ethernet frames. | ||
| 2540 | */ | ||
| 2541 | static int mpi_map_card(struct airo_info *ai, struct pci_dev *pci) | ||
| 2542 | { | ||
| 2543 | unsigned long mem_start, mem_len, aux_start, aux_len; | ||
| 2544 | int rc = -1; | ||
| 2545 | int i; | ||
| 2546 | dma_addr_t busaddroff; | ||
| 2547 | unsigned char *vpackoff; | ||
| 2548 | unsigned char __iomem *pciaddroff; | ||
| 2549 | |||
| 2550 | mem_start = pci_resource_start(pci, 1); | ||
| 2551 | mem_len = pci_resource_len(pci, 1); | ||
| 2552 | aux_start = pci_resource_start(pci, 2); | ||
| 2553 | aux_len = AUXMEMSIZE; | ||
| 2554 | |||
| 2555 | if (!request_mem_region(mem_start, mem_len, DRV_NAME)) { | ||
| 2556 | airo_print_err("", "Couldn't get region %x[%x]", | ||
| 2557 | (int)mem_start, (int)mem_len); | ||
| 2558 | goto out; | ||
| 2559 | } | ||
| 2560 | if (!request_mem_region(aux_start, aux_len, DRV_NAME)) { | ||
| 2561 | airo_print_err("", "Couldn't get region %x[%x]", | ||
| 2562 | (int)aux_start, (int)aux_len); | ||
| 2563 | goto free_region1; | ||
| 2564 | } | ||
| 2565 | |||
| 2566 | ai->pcimem = ioremap(mem_start, mem_len); | ||
| 2567 | if (!ai->pcimem) { | ||
| 2568 | airo_print_err("", "Couldn't map region %x[%x]", | ||
| 2569 | (int)mem_start, (int)mem_len); | ||
| 2570 | goto free_region2; | ||
| 2571 | } | ||
| 2572 | ai->pciaux = ioremap(aux_start, aux_len); | ||
| 2573 | if (!ai->pciaux) { | ||
| 2574 | airo_print_err("", "Couldn't map region %x[%x]", | ||
| 2575 | (int)aux_start, (int)aux_len); | ||
| 2576 | goto free_memmap; | ||
| 2577 | } | ||
| 2578 | |||
| 2579 | /* Reserve PKTSIZE for each fid and 2K for the Rids */ | ||
| 2580 | ai->shared = dma_alloc_coherent(&pci->dev, PCI_SHARED_LEN, | ||
| 2581 | &ai->shared_dma, GFP_KERNEL); | ||
| 2582 | if (!ai->shared) { | ||
| 2583 | airo_print_err("", "Couldn't alloc_coherent %d", | ||
| 2584 | PCI_SHARED_LEN); | ||
| 2585 | goto free_auxmap; | ||
| 2586 | } | ||
| 2587 | |||
| 2588 | /* | ||
| 2589 | * Setup descriptor RX, TX, CONFIG | ||
| 2590 | */ | ||
| 2591 | busaddroff = ai->shared_dma; | ||
| 2592 | pciaddroff = ai->pciaux + AUX_OFFSET; | ||
| 2593 | vpackoff = ai->shared; | ||
| 2594 | |||
| 2595 | /* RX descriptor setup */ | ||
| 2596 | for (i = 0; i < MPI_MAX_FIDS; i++) { | ||
| 2597 | ai->rxfids[i].pending = 0; | ||
| 2598 | ai->rxfids[i].card_ram_off = pciaddroff; | ||
| 2599 | ai->rxfids[i].virtual_host_addr = vpackoff; | ||
| 2600 | ai->rxfids[i].rx_desc.host_addr = busaddroff; | ||
| 2601 | ai->rxfids[i].rx_desc.valid = 1; | ||
| 2602 | ai->rxfids[i].rx_desc.len = PKTSIZE; | ||
| 2603 | ai->rxfids[i].rx_desc.rdy = 0; | ||
| 2604 | |||
| 2605 | pciaddroff += sizeof(RxFid); | ||
| 2606 | busaddroff += PKTSIZE; | ||
| 2607 | vpackoff += PKTSIZE; | ||
| 2608 | } | ||
| 2609 | |||
| 2610 | /* TX descriptor setup */ | ||
| 2611 | for (i = 0; i < MPI_MAX_FIDS; i++) { | ||
| 2612 | ai->txfids[i].card_ram_off = pciaddroff; | ||
| 2613 | ai->txfids[i].virtual_host_addr = vpackoff; | ||
| 2614 | ai->txfids[i].tx_desc.valid = 1; | ||
| 2615 | ai->txfids[i].tx_desc.host_addr = busaddroff; | ||
| 2616 | memcpy(ai->txfids[i].virtual_host_addr, | ||
| 2617 | &wifictlhdr8023, sizeof(wifictlhdr8023)); | ||
| 2618 | |||
| 2619 | pciaddroff += sizeof(TxFid); | ||
| 2620 | busaddroff += PKTSIZE; | ||
| 2621 | vpackoff += PKTSIZE; | ||
| 2622 | } | ||
| 2623 | ai->txfids[i-1].tx_desc.eoc = 1; /* Last descriptor has EOC set */ | ||
| 2624 | |||
| 2625 | /* Rid descriptor setup */ | ||
| 2626 | ai->config_desc.card_ram_off = pciaddroff; | ||
| 2627 | ai->config_desc.virtual_host_addr = vpackoff; | ||
| 2628 | ai->config_desc.rid_desc.host_addr = busaddroff; | ||
| 2629 | ai->ridbus = busaddroff; | ||
| 2630 | ai->config_desc.rid_desc.rid = 0; | ||
| 2631 | ai->config_desc.rid_desc.len = RIDSIZE; | ||
| 2632 | ai->config_desc.rid_desc.valid = 1; | ||
| 2633 | pciaddroff += sizeof(Rid); | ||
| 2634 | busaddroff += RIDSIZE; | ||
| 2635 | vpackoff += RIDSIZE; | ||
| 2636 | |||
| 2637 | /* Tell card about descriptors */ | ||
| 2638 | if (mpi_init_descriptors (ai) != SUCCESS) | ||
| 2639 | goto free_shared; | ||
| 2640 | |||
| 2641 | return 0; | ||
| 2642 | free_shared: | ||
| 2643 | dma_free_coherent(&pci->dev, PCI_SHARED_LEN, ai->shared, | ||
| 2644 | ai->shared_dma); | ||
| 2645 | free_auxmap: | ||
| 2646 | iounmap(ai->pciaux); | ||
| 2647 | free_memmap: | ||
| 2648 | iounmap(ai->pcimem); | ||
| 2649 | free_region2: | ||
| 2650 | release_mem_region(aux_start, aux_len); | ||
| 2651 | free_region1: | ||
| 2652 | release_mem_region(mem_start, mem_len); | ||
| 2653 | out: | ||
| 2654 | return rc; | ||
| 2655 | } | ||
| 2656 | |||
| 2657 | static const struct header_ops airo_header_ops = { | ||
| 2658 | .parse = wll_header_parse, | ||
| 2659 | }; | ||
| 2660 | |||
| 2661 | static const struct net_device_ops airo11_netdev_ops = { | ||
| 2662 | .ndo_open = airo_open, | ||
| 2663 | .ndo_stop = airo_close, | ||
| 2664 | .ndo_start_xmit = airo_start_xmit11, | ||
| 2665 | .ndo_get_stats = airo_get_stats, | ||
| 2666 | .ndo_set_mac_address = airo_set_mac_address, | ||
| 2667 | .ndo_siocdevprivate = airo_siocdevprivate, | ||
| 2668 | }; | ||
| 2669 | |||
| 2670 | static void wifi_setup(struct net_device *dev) | ||
| 2671 | { | ||
| 2672 | dev->netdev_ops = &airo11_netdev_ops; | ||
| 2673 | dev->header_ops = &airo_header_ops; | ||
| 2674 | dev->wireless_handlers = &airo_handler_def; | ||
| 2675 | |||
| 2676 | dev->type = ARPHRD_IEEE80211; | ||
| 2677 | dev->hard_header_len = ETH_HLEN; | ||
| 2678 | dev->mtu = AIRO_DEF_MTU; | ||
| 2679 | dev->min_mtu = 68; | ||
| 2680 | dev->max_mtu = MIC_MSGLEN_MAX; | ||
| 2681 | dev->addr_len = ETH_ALEN; | ||
| 2682 | dev->tx_queue_len = 100; | ||
| 2683 | |||
| 2684 | eth_broadcast_addr(dev->broadcast); | ||
| 2685 | |||
| 2686 | dev->flags = IFF_BROADCAST|IFF_MULTICAST; | ||
| 2687 | } | ||
| 2688 | |||
| 2689 | static struct net_device *init_wifidev(struct airo_info *ai, | ||
| 2690 | struct net_device *ethdev) | ||
| 2691 | { | ||
| 2692 | int err; | ||
| 2693 | struct net_device *dev = alloc_netdev(0, "wifi%d", NET_NAME_UNKNOWN, | ||
| 2694 | wifi_setup); | ||
| 2695 | if (!dev) | ||
| 2696 | return NULL; | ||
| 2697 | dev->ml_priv = ethdev->ml_priv; | ||
| 2698 | dev->irq = ethdev->irq; | ||
| 2699 | dev->base_addr = ethdev->base_addr; | ||
| 2700 | dev->wireless_data = ethdev->wireless_data; | ||
| 2701 | SET_NETDEV_DEV(dev, ethdev->dev.parent); | ||
| 2702 | eth_hw_addr_inherit(dev, ethdev); | ||
| 2703 | err = register_netdev(dev); | ||
| 2704 | if (err<0) { | ||
| 2705 | free_netdev(dev); | ||
| 2706 | return NULL; | ||
| 2707 | } | ||
| 2708 | return dev; | ||
| 2709 | } | ||
| 2710 | |||
| 2711 | static int reset_card(struct net_device *dev, int lock) | ||
| 2712 | { | ||
| 2713 | struct airo_info *ai = dev->ml_priv; | ||
| 2714 | |||
| 2715 | if (lock && down_interruptible(&ai->sem)) | ||
| 2716 | return -1; | ||
| 2717 | waitbusy (ai); | ||
| 2718 | OUT4500(ai, COMMAND, CMD_SOFTRESET); | ||
| 2719 | msleep(200); | ||
| 2720 | waitbusy (ai); | ||
| 2721 | msleep(200); | ||
| 2722 | if (lock) | ||
| 2723 | up(&ai->sem); | ||
| 2724 | return 0; | ||
| 2725 | } | ||
| 2726 | |||
| 2727 | #define AIRO_MAX_NETWORK_COUNT 64 | ||
| 2728 | static int airo_networks_allocate(struct airo_info *ai) | ||
| 2729 | { | ||
| 2730 | if (ai->networks) | ||
| 2731 | return 0; | ||
| 2732 | |||
| 2733 | ai->networks = kcalloc(AIRO_MAX_NETWORK_COUNT, sizeof(BSSListElement), | ||
| 2734 | GFP_KERNEL); | ||
| 2735 | if (!ai->networks) { | ||
| 2736 | airo_print_warn("", "Out of memory allocating beacons"); | ||
| 2737 | return -ENOMEM; | ||
| 2738 | } | ||
| 2739 | |||
| 2740 | return 0; | ||
| 2741 | } | ||
| 2742 | |||
| 2743 | static void airo_networks_free(struct airo_info *ai) | ||
| 2744 | { | ||
| 2745 | kfree(ai->networks); | ||
| 2746 | ai->networks = NULL; | ||
| 2747 | } | ||
| 2748 | |||
| 2749 | static void airo_networks_initialize(struct airo_info *ai) | ||
| 2750 | { | ||
| 2751 | int i; | ||
| 2752 | |||
| 2753 | INIT_LIST_HEAD(&ai->network_free_list); | ||
| 2754 | INIT_LIST_HEAD(&ai->network_list); | ||
| 2755 | for (i = 0; i < AIRO_MAX_NETWORK_COUNT; i++) | ||
| 2756 | list_add_tail(&ai->networks[i].list, | ||
| 2757 | &ai->network_free_list); | ||
| 2758 | } | ||
| 2759 | |||
| 2760 | static const struct net_device_ops airo_netdev_ops = { | ||
| 2761 | .ndo_open = airo_open, | ||
| 2762 | .ndo_stop = airo_close, | ||
| 2763 | .ndo_start_xmit = airo_start_xmit, | ||
| 2764 | .ndo_get_stats = airo_get_stats, | ||
| 2765 | .ndo_set_rx_mode = airo_set_multicast_list, | ||
| 2766 | .ndo_set_mac_address = airo_set_mac_address, | ||
| 2767 | .ndo_siocdevprivate = airo_siocdevprivate, | ||
| 2768 | .ndo_validate_addr = eth_validate_addr, | ||
| 2769 | }; | ||
| 2770 | |||
| 2771 | static const struct net_device_ops mpi_netdev_ops = { | ||
| 2772 | .ndo_open = airo_open, | ||
| 2773 | .ndo_stop = airo_close, | ||
| 2774 | .ndo_start_xmit = mpi_start_xmit, | ||
| 2775 | .ndo_get_stats = airo_get_stats, | ||
| 2776 | .ndo_set_rx_mode = airo_set_multicast_list, | ||
| 2777 | .ndo_set_mac_address = airo_set_mac_address, | ||
| 2778 | .ndo_siocdevprivate = airo_siocdevprivate, | ||
| 2779 | .ndo_validate_addr = eth_validate_addr, | ||
| 2780 | }; | ||
| 2781 | |||
| 2782 | |||
| 2783 | static struct net_device *_init_airo_card(unsigned short irq, int port, | ||
| 2784 | int is_pcmcia, struct pci_dev *pci, | ||
| 2785 | struct device *dmdev) | ||
| 2786 | { | ||
| 2787 | struct net_device *dev; | ||
| 2788 | struct airo_info *ai; | ||
| 2789 | int i, rc; | ||
| 2790 | CapabilityRid cap_rid; | ||
| 2791 | |||
| 2792 | /* Create the network device object. */ | ||
| 2793 | dev = alloc_netdev(sizeof(*ai), "", NET_NAME_UNKNOWN, ether_setup); | ||
| 2794 | if (!dev) { | ||
| 2795 | airo_print_err("", "Couldn't alloc_etherdev"); | ||
| 2796 | return NULL; | ||
| 2797 | } | ||
| 2798 | |||
| 2799 | ai = dev->ml_priv = netdev_priv(dev); | ||
| 2800 | ai->wifidev = NULL; | ||
| 2801 | ai->flags = 1 << FLAG_RADIO_DOWN; | ||
| 2802 | ai->jobs = 0; | ||
| 2803 | ai->dev = dev; | ||
| 2804 | if (pci && (pci->device == 0x5000 || pci->device == 0xa504)) { | ||
| 2805 | airo_print_dbg("", "Found an MPI350 card"); | ||
| 2806 | set_bit(FLAG_MPI, &ai->flags); | ||
| 2807 | } | ||
| 2808 | spin_lock_init(&ai->aux_lock); | ||
| 2809 | sema_init(&ai->sem, 1); | ||
| 2810 | ai->config.len = 0; | ||
| 2811 | ai->pci = pci; | ||
| 2812 | init_waitqueue_head (&ai->thr_wait); | ||
| 2813 | ai->tfm = NULL; | ||
| 2814 | add_airo_dev(ai); | ||
| 2815 | ai->APList.len = cpu_to_le16(sizeof(struct APListRid)); | ||
| 2816 | |||
| 2817 | if (airo_networks_allocate (ai)) | ||
| 2818 | goto err_out_free; | ||
| 2819 | airo_networks_initialize (ai); | ||
| 2820 | |||
| 2821 | skb_queue_head_init (&ai->txq); | ||
| 2822 | |||
| 2823 | /* The Airo-specific entries in the device structure. */ | ||
| 2824 | if (test_bit(FLAG_MPI,&ai->flags)) | ||
| 2825 | dev->netdev_ops = &mpi_netdev_ops; | ||
| 2826 | else | ||
| 2827 | dev->netdev_ops = &airo_netdev_ops; | ||
| 2828 | dev->wireless_handlers = &airo_handler_def; | ||
| 2829 | ai->wireless_data.spy_data = &ai->spy_data; | ||
| 2830 | dev->wireless_data = &ai->wireless_data; | ||
| 2831 | dev->irq = irq; | ||
| 2832 | dev->base_addr = port; | ||
| 2833 | dev->priv_flags &= ~IFF_TX_SKB_SHARING; | ||
| 2834 | dev->max_mtu = MIC_MSGLEN_MAX; | ||
| 2835 | |||
| 2836 | SET_NETDEV_DEV(dev, dmdev); | ||
| 2837 | |||
| 2838 | reset_card (dev, 1); | ||
| 2839 | msleep(400); | ||
| 2840 | |||
| 2841 | if (!is_pcmcia) { | ||
| 2842 | if (!request_region(dev->base_addr, 64, DRV_NAME)) { | ||
| 2843 | rc = -EBUSY; | ||
| 2844 | airo_print_err(dev->name, "Couldn't request region"); | ||
| 2845 | goto err_out_nets; | ||
| 2846 | } | ||
| 2847 | } | ||
| 2848 | |||
| 2849 | if (test_bit(FLAG_MPI,&ai->flags)) { | ||
| 2850 | if (mpi_map_card(ai, pci)) { | ||
| 2851 | airo_print_err("", "Could not map memory"); | ||
| 2852 | goto err_out_res; | ||
| 2853 | } | ||
| 2854 | } | ||
| 2855 | |||
| 2856 | if (probe) { | ||
| 2857 | if (setup_card(ai, dev, 1) != SUCCESS) { | ||
| 2858 | airo_print_err(dev->name, "MAC could not be enabled"); | ||
| 2859 | rc = -EIO; | ||
| 2860 | goto err_out_map; | ||
| 2861 | } | ||
| 2862 | } else if (!test_bit(FLAG_MPI,&ai->flags)) { | ||
| 2863 | ai->bap_read = fast_bap_read; | ||
| 2864 | set_bit(FLAG_FLASHING, &ai->flags); | ||
| 2865 | } | ||
| 2866 | |||
| 2867 | strcpy(dev->name, "eth%d"); | ||
| 2868 | rc = register_netdev(dev); | ||
| 2869 | if (rc) { | ||
| 2870 | airo_print_err(dev->name, "Couldn't register_netdev"); | ||
| 2871 | goto err_out_map; | ||
| 2872 | } | ||
| 2873 | ai->wifidev = init_wifidev(ai, dev); | ||
| 2874 | if (!ai->wifidev) | ||
| 2875 | goto err_out_reg; | ||
| 2876 | |||
| 2877 | rc = readCapabilityRid(ai, &cap_rid, 1); | ||
| 2878 | if (rc != SUCCESS) { | ||
| 2879 | rc = -EIO; | ||
| 2880 | goto err_out_wifi; | ||
| 2881 | } | ||
| 2882 | /* WEP capability discovery */ | ||
| 2883 | ai->wep_capable = (cap_rid.softCap & cpu_to_le16(0x02)) ? 1 : 0; | ||
| 2884 | ai->max_wep_idx = (cap_rid.softCap & cpu_to_le16(0x80)) ? 3 : 0; | ||
| 2885 | |||
| 2886 | airo_print_info(dev->name, "Firmware version %x.%x.%02d", | ||
| 2887 | ((le16_to_cpu(cap_rid.softVer) >> 8) & 0xF), | ||
| 2888 | (le16_to_cpu(cap_rid.softVer) & 0xFF), | ||
| 2889 | le16_to_cpu(cap_rid.softSubVer)); | ||
| 2890 | |||
| 2891 | /* Test for WPA support */ | ||
| 2892 | /* Only firmware versions 5.30.17 or better can do WPA */ | ||
| 2893 | if (le16_to_cpu(cap_rid.softVer) > 0x530 | ||
| 2894 | || (le16_to_cpu(cap_rid.softVer) == 0x530 | ||
| 2895 | && le16_to_cpu(cap_rid.softSubVer) >= 17)) { | ||
| 2896 | airo_print_info(ai->dev->name, "WPA supported."); | ||
| 2897 | |||
| 2898 | set_bit(FLAG_WPA_CAPABLE, &ai->flags); | ||
| 2899 | ai->bssListFirst = RID_WPA_BSSLISTFIRST; | ||
| 2900 | ai->bssListNext = RID_WPA_BSSLISTNEXT; | ||
| 2901 | ai->bssListRidLen = sizeof(BSSListRid); | ||
| 2902 | } else { | ||
| 2903 | airo_print_info(ai->dev->name, "WPA unsupported with firmware " | ||
| 2904 | "versions older than 5.30.17."); | ||
| 2905 | |||
| 2906 | ai->bssListFirst = RID_BSSLISTFIRST; | ||
| 2907 | ai->bssListNext = RID_BSSLISTNEXT; | ||
| 2908 | ai->bssListRidLen = sizeof(BSSListRid) - sizeof(BSSListRidExtra); | ||
| 2909 | } | ||
| 2910 | |||
| 2911 | set_bit(FLAG_REGISTERED,&ai->flags); | ||
| 2912 | airo_print_info(dev->name, "MAC enabled %pM", dev->dev_addr); | ||
| 2913 | |||
| 2914 | /* Allocate the transmit buffers */ | ||
| 2915 | if (probe && !test_bit(FLAG_MPI,&ai->flags)) | ||
| 2916 | for (i = 0; i < MAX_FIDS; i++) | ||
| 2917 | ai->fids[i] = transmit_allocate(ai, AIRO_DEF_MTU, i>=MAX_FIDS/2); | ||
| 2918 | |||
| 2919 | if (setup_proc_entry(dev, dev->ml_priv) < 0) | ||
| 2920 | goto err_out_wifi; | ||
| 2921 | |||
| 2922 | return dev; | ||
| 2923 | |||
| 2924 | err_out_wifi: | ||
| 2925 | unregister_netdev(ai->wifidev); | ||
| 2926 | free_netdev(ai->wifidev); | ||
| 2927 | err_out_reg: | ||
| 2928 | unregister_netdev(dev); | ||
| 2929 | err_out_map: | ||
| 2930 | if (test_bit(FLAG_MPI,&ai->flags) && pci) { | ||
| 2931 | dma_free_coherent(&pci->dev, PCI_SHARED_LEN, ai->shared, | ||
| 2932 | ai->shared_dma); | ||
| 2933 | iounmap(ai->pciaux); | ||
| 2934 | iounmap(ai->pcimem); | ||
| 2935 | mpi_unmap_card(ai->pci); | ||
| 2936 | } | ||
| 2937 | err_out_res: | ||
| 2938 | if (!is_pcmcia) | ||
| 2939 | release_region(dev->base_addr, 64); | ||
| 2940 | err_out_nets: | ||
| 2941 | airo_networks_free(ai); | ||
| 2942 | err_out_free: | ||
| 2943 | del_airo_dev(ai); | ||
| 2944 | free_netdev(dev); | ||
| 2945 | return NULL; | ||
| 2946 | } | ||
| 2947 | |||
| 2948 | struct net_device *init_airo_card(unsigned short irq, int port, int is_pcmcia, | ||
| 2949 | struct device *dmdev) | ||
| 2950 | { | ||
| 2951 | return _init_airo_card (irq, port, is_pcmcia, NULL, dmdev); | ||
| 2952 | } | ||
| 2953 | |||
| 2954 | EXPORT_SYMBOL(init_airo_card); | ||
| 2955 | |||
| 2956 | static int waitbusy (struct airo_info *ai) | ||
| 2957 | { | ||
| 2958 | int delay = 0; | ||
| 2959 | while ((IN4500(ai, COMMAND) & COMMAND_BUSY) && (delay < 10000)) { | ||
| 2960 | udelay (10); | ||
| 2961 | if ((++delay % 20) == 0) | ||
| 2962 | OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY); | ||
| 2963 | } | ||
| 2964 | return delay < 10000; | ||
| 2965 | } | ||
| 2966 | |||
| 2967 | int reset_airo_card(struct net_device *dev) | ||
| 2968 | { | ||
| 2969 | int i; | ||
| 2970 | struct airo_info *ai = dev->ml_priv; | ||
| 2971 | |||
| 2972 | if (reset_card (dev, 1)) | ||
| 2973 | return -1; | ||
| 2974 | |||
| 2975 | if (setup_card(ai, dev, 1) != SUCCESS) { | ||
| 2976 | airo_print_err(dev->name, "MAC could not be enabled"); | ||
| 2977 | return -1; | ||
| 2978 | } | ||
| 2979 | airo_print_info(dev->name, "MAC enabled %pM", dev->dev_addr); | ||
| 2980 | /* Allocate the transmit buffers if needed */ | ||
| 2981 | if (!test_bit(FLAG_MPI,&ai->flags)) | ||
| 2982 | for (i = 0; i < MAX_FIDS; i++) | ||
| 2983 | ai->fids[i] = transmit_allocate (ai, AIRO_DEF_MTU, i>=MAX_FIDS/2); | ||
| 2984 | |||
| 2985 | enable_interrupts(ai); | ||
| 2986 | netif_wake_queue(dev); | ||
| 2987 | return 0; | ||
| 2988 | } | ||
| 2989 | |||
| 2990 | EXPORT_SYMBOL(reset_airo_card); | ||
| 2991 | |||
| 2992 | static void airo_send_event(struct net_device *dev) | ||
| 2993 | { | ||
| 2994 | struct airo_info *ai = dev->ml_priv; | ||
| 2995 | union iwreq_data wrqu; | ||
| 2996 | StatusRid status_rid; | ||
| 2997 | |||
| 2998 | clear_bit(JOB_EVENT, &ai->jobs); | ||
| 2999 | PC4500_readrid(ai, RID_STATUS, &status_rid, sizeof(status_rid), 0); | ||
| 3000 | up(&ai->sem); | ||
| 3001 | wrqu.data.length = 0; | ||
| 3002 | wrqu.data.flags = 0; | ||
| 3003 | memcpy(wrqu.ap_addr.sa_data, status_rid.bssid[0], ETH_ALEN); | ||
| 3004 | wrqu.ap_addr.sa_family = ARPHRD_ETHER; | ||
| 3005 | |||
| 3006 | /* Send event to user space */ | ||
| 3007 | wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); | ||
| 3008 | } | ||
| 3009 | |||
| 3010 | static void airo_process_scan_results (struct airo_info *ai) | ||
| 3011 | { | ||
| 3012 | union iwreq_data wrqu; | ||
| 3013 | BSSListRid bss; | ||
| 3014 | int rc; | ||
| 3015 | BSSListElement * loop_net; | ||
| 3016 | BSSListElement * tmp_net; | ||
| 3017 | |||
| 3018 | /* Blow away current list of scan results */ | ||
| 3019 | list_for_each_entry_safe (loop_net, tmp_net, &ai->network_list, list) { | ||
| 3020 | list_move_tail (&loop_net->list, &ai->network_free_list); | ||
| 3021 | /* Don't blow away ->list, just BSS data */ | ||
| 3022 | memset (loop_net, 0, sizeof (loop_net->bss)); | ||
| 3023 | } | ||
| 3024 | |||
| 3025 | /* Try to read the first entry of the scan result */ | ||
| 3026 | rc = PC4500_readrid(ai, ai->bssListFirst, &bss, ai->bssListRidLen, 0); | ||
| 3027 | if ((rc) || (bss.index == cpu_to_le16(0xffff))) { | ||
| 3028 | /* No scan results */ | ||
| 3029 | goto out; | ||
| 3030 | } | ||
| 3031 | |||
| 3032 | /* Read and parse all entries */ | ||
| 3033 | tmp_net = NULL; | ||
| 3034 | while ((!rc) && (bss.index != cpu_to_le16(0xffff))) { | ||
| 3035 | /* Grab a network off the free list */ | ||
| 3036 | if (!list_empty(&ai->network_free_list)) { | ||
| 3037 | tmp_net = list_entry(ai->network_free_list.next, | ||
| 3038 | BSSListElement, list); | ||
| 3039 | list_del(ai->network_free_list.next); | ||
| 3040 | } | ||
| 3041 | |||
| 3042 | if (tmp_net != NULL) { | ||
| 3043 | memcpy(tmp_net, &bss, sizeof(tmp_net->bss)); | ||
| 3044 | list_add_tail(&tmp_net->list, &ai->network_list); | ||
| 3045 | tmp_net = NULL; | ||
| 3046 | } | ||
| 3047 | |||
| 3048 | /* Read next entry */ | ||
| 3049 | rc = PC4500_readrid(ai, ai->bssListNext, | ||
| 3050 | &bss, ai->bssListRidLen, 0); | ||
| 3051 | } | ||
| 3052 | |||
| 3053 | out: | ||
| 3054 | /* write APList back (we cleared it in airo_set_scan) */ | ||
| 3055 | disable_MAC(ai, 2); | ||
| 3056 | writeAPListRid(ai, &ai->APList, 0); | ||
| 3057 | enable_MAC(ai, 0); | ||
| 3058 | |||
| 3059 | ai->scan_timeout = 0; | ||
| 3060 | clear_bit(JOB_SCAN_RESULTS, &ai->jobs); | ||
| 3061 | up(&ai->sem); | ||
| 3062 | |||
| 3063 | /* Send an empty event to user space. | ||
| 3064 | * We don't send the received data on | ||
| 3065 | * the event because it would require | ||
| 3066 | * us to do complex transcoding, and | ||
| 3067 | * we want to minimise the work done in | ||
| 3068 | * the irq handler. Use a request to | ||
| 3069 | * extract the data - Jean II */ | ||
| 3070 | wrqu.data.length = 0; | ||
| 3071 | wrqu.data.flags = 0; | ||
| 3072 | wireless_send_event(ai->dev, SIOCGIWSCAN, &wrqu, NULL); | ||
| 3073 | } | ||
| 3074 | |||
| 3075 | static int airo_thread(void *data) | ||
| 3076 | { | ||
| 3077 | struct net_device *dev = data; | ||
| 3078 | struct airo_info *ai = dev->ml_priv; | ||
| 3079 | int locked; | ||
| 3080 | |||
| 3081 | set_freezable(); | ||
| 3082 | while (1) { | ||
| 3083 | /* make swsusp happy with our thread */ | ||
| 3084 | try_to_freeze(); | ||
| 3085 | |||
| 3086 | if (test_bit(JOB_DIE, &ai->jobs)) | ||
| 3087 | break; | ||
| 3088 | |||
| 3089 | if (ai->jobs) { | ||
| 3090 | locked = down_interruptible(&ai->sem); | ||
| 3091 | } else { | ||
| 3092 | wait_queue_entry_t wait; | ||
| 3093 | |||
| 3094 | init_waitqueue_entry(&wait, current); | ||
| 3095 | add_wait_queue(&ai->thr_wait, &wait); | ||
| 3096 | for (;;) { | ||
| 3097 | set_current_state(TASK_INTERRUPTIBLE); | ||
| 3098 | if (ai->jobs) | ||
| 3099 | break; | ||
| 3100 | if (ai->expires || ai->scan_timeout) { | ||
| 3101 | if (ai->scan_timeout && | ||
| 3102 | time_after_eq(jiffies, ai->scan_timeout)) { | ||
| 3103 | set_bit(JOB_SCAN_RESULTS, &ai->jobs); | ||
| 3104 | break; | ||
| 3105 | } else if (ai->expires && | ||
| 3106 | time_after_eq(jiffies, ai->expires)) { | ||
| 3107 | set_bit(JOB_AUTOWEP, &ai->jobs); | ||
| 3108 | break; | ||
| 3109 | } | ||
| 3110 | if (!kthread_should_stop() && | ||
| 3111 | !freezing(current)) { | ||
| 3112 | unsigned long wake_at; | ||
| 3113 | if (!ai->expires || !ai->scan_timeout) { | ||
| 3114 | wake_at = max(ai->expires, | ||
| 3115 | ai->scan_timeout); | ||
| 3116 | } else { | ||
| 3117 | wake_at = min(ai->expires, | ||
| 3118 | ai->scan_timeout); | ||
| 3119 | } | ||
| 3120 | schedule_timeout(wake_at - jiffies); | ||
| 3121 | continue; | ||
| 3122 | } | ||
| 3123 | } else if (!kthread_should_stop() && | ||
| 3124 | !freezing(current)) { | ||
| 3125 | schedule(); | ||
| 3126 | continue; | ||
| 3127 | } | ||
| 3128 | break; | ||
| 3129 | } | ||
| 3130 | __set_current_state(TASK_RUNNING); | ||
| 3131 | remove_wait_queue(&ai->thr_wait, &wait); | ||
| 3132 | locked = 1; | ||
| 3133 | } | ||
| 3134 | |||
| 3135 | if (locked) | ||
| 3136 | continue; | ||
| 3137 | |||
| 3138 | if (test_bit(JOB_DIE, &ai->jobs)) { | ||
| 3139 | up(&ai->sem); | ||
| 3140 | break; | ||
| 3141 | } | ||
| 3142 | |||
| 3143 | if (ai->power.event || test_bit(FLAG_FLASHING, &ai->flags)) { | ||
| 3144 | up(&ai->sem); | ||
| 3145 | continue; | ||
| 3146 | } | ||
| 3147 | |||
| 3148 | if (test_bit(JOB_XMIT, &ai->jobs)) | ||
| 3149 | airo_end_xmit(dev, true); | ||
| 3150 | else if (test_bit(JOB_XMIT11, &ai->jobs)) | ||
| 3151 | airo_end_xmit11(dev, true); | ||
| 3152 | else if (test_bit(JOB_STATS, &ai->jobs)) | ||
| 3153 | airo_read_stats(dev); | ||
| 3154 | else if (test_bit(JOB_PROMISC, &ai->jobs)) | ||
| 3155 | airo_set_promisc(ai, true); | ||
| 3156 | else if (test_bit(JOB_MIC, &ai->jobs)) | ||
| 3157 | micinit(ai); | ||
| 3158 | else if (test_bit(JOB_EVENT, &ai->jobs)) | ||
| 3159 | airo_send_event(dev); | ||
| 3160 | else if (test_bit(JOB_AUTOWEP, &ai->jobs)) | ||
| 3161 | timer_func(dev); | ||
| 3162 | else if (test_bit(JOB_SCAN_RESULTS, &ai->jobs)) | ||
| 3163 | airo_process_scan_results(ai); | ||
| 3164 | else /* Shouldn't get here, but we make sure to unlock */ | ||
| 3165 | up(&ai->sem); | ||
| 3166 | } | ||
| 3167 | |||
| 3168 | return 0; | ||
| 3169 | } | ||
| 3170 | |||
| 3171 | static int header_len(__le16 ctl) | ||
| 3172 | { | ||
| 3173 | u16 fc = le16_to_cpu(ctl); | ||
| 3174 | switch (fc & 0xc) { | ||
| 3175 | case 4: | ||
| 3176 | if ((fc & 0xe0) == 0xc0) | ||
| 3177 | return 10; /* one-address control packet */ | ||
| 3178 | return 16; /* two-address control packet */ | ||
| 3179 | case 8: | ||
| 3180 | if ((fc & 0x300) == 0x300) | ||
| 3181 | return 30; /* WDS packet */ | ||
| 3182 | } | ||
| 3183 | return 24; | ||
| 3184 | } | ||
| 3185 | |||
| 3186 | static void airo_handle_cisco_mic(struct airo_info *ai) | ||
| 3187 | { | ||
| 3188 | if (test_bit(FLAG_MIC_CAPABLE, &ai->flags)) { | ||
| 3189 | set_bit(JOB_MIC, &ai->jobs); | ||
| 3190 | wake_up_interruptible(&ai->thr_wait); | ||
| 3191 | } | ||
| 3192 | } | ||
| 3193 | |||
| 3194 | /* Airo Status codes */ | ||
| 3195 | #define STAT_NOBEACON 0x8000 /* Loss of sync - missed beacons */ | ||
| 3196 | #define STAT_MAXRETRIES 0x8001 /* Loss of sync - max retries */ | ||
| 3197 | #define STAT_MAXARL 0x8002 /* Loss of sync - average retry level exceeded*/ | ||
| 3198 | #define STAT_FORCELOSS 0x8003 /* Loss of sync - host request */ | ||
| 3199 | #define STAT_TSFSYNC 0x8004 /* Loss of sync - TSF synchronization */ | ||
| 3200 | #define STAT_DEAUTH 0x8100 /* low byte is 802.11 reason code */ | ||
| 3201 | #define STAT_DISASSOC 0x8200 /* low byte is 802.11 reason code */ | ||
| 3202 | #define STAT_ASSOC_FAIL 0x8400 /* low byte is 802.11 reason code */ | ||
| 3203 | #define STAT_AUTH_FAIL 0x0300 /* low byte is 802.11 reason code */ | ||
| 3204 | #define STAT_ASSOC 0x0400 /* Associated */ | ||
| 3205 | #define STAT_REASSOC 0x0600 /* Reassociated? Only on firmware >= 5.30.17 */ | ||
| 3206 | |||
| 3207 | static void airo_print_status(const char *devname, u16 status) | ||
| 3208 | { | ||
| 3209 | u8 reason = status & 0xFF; | ||
| 3210 | |||
| 3211 | switch (status & 0xFF00) { | ||
| 3212 | case STAT_NOBEACON: | ||
| 3213 | switch (status) { | ||
| 3214 | case STAT_NOBEACON: | ||
| 3215 | airo_print_dbg(devname, "link lost (missed beacons)"); | ||
| 3216 | break; | ||
| 3217 | case STAT_MAXRETRIES: | ||
| 3218 | case STAT_MAXARL: | ||
| 3219 | airo_print_dbg(devname, "link lost (max retries)"); | ||
| 3220 | break; | ||
| 3221 | case STAT_FORCELOSS: | ||
| 3222 | airo_print_dbg(devname, "link lost (local choice)"); | ||
| 3223 | break; | ||
| 3224 | case STAT_TSFSYNC: | ||
| 3225 | airo_print_dbg(devname, "link lost (TSF sync lost)"); | ||
| 3226 | break; | ||
| 3227 | default: | ||
| 3228 | airo_print_dbg(devname, "unknown status %x\n", status); | ||
| 3229 | break; | ||
| 3230 | } | ||
| 3231 | break; | ||
| 3232 | case STAT_DEAUTH: | ||
| 3233 | airo_print_dbg(devname, "deauthenticated (reason: %d)", reason); | ||
| 3234 | break; | ||
| 3235 | case STAT_DISASSOC: | ||
| 3236 | airo_print_dbg(devname, "disassociated (reason: %d)", reason); | ||
| 3237 | break; | ||
| 3238 | case STAT_ASSOC_FAIL: | ||
| 3239 | airo_print_dbg(devname, "association failed (reason: %d)", | ||
| 3240 | reason); | ||
| 3241 | break; | ||
| 3242 | case STAT_AUTH_FAIL: | ||
| 3243 | airo_print_dbg(devname, "authentication failed (reason: %d)", | ||
| 3244 | reason); | ||
| 3245 | break; | ||
| 3246 | case STAT_ASSOC: | ||
| 3247 | case STAT_REASSOC: | ||
| 3248 | break; | ||
| 3249 | default: | ||
| 3250 | airo_print_dbg(devname, "unknown status %x\n", status); | ||
| 3251 | break; | ||
| 3252 | } | ||
| 3253 | } | ||
| 3254 | |||
| 3255 | static void airo_handle_link(struct airo_info *ai) | ||
| 3256 | { | ||
| 3257 | union iwreq_data wrqu; | ||
| 3258 | int scan_forceloss = 0; | ||
| 3259 | u16 status; | ||
| 3260 | |||
| 3261 | /* Get new status and acknowledge the link change */ | ||
| 3262 | status = le16_to_cpu(IN4500(ai, LINKSTAT)); | ||
| 3263 | OUT4500(ai, EVACK, EV_LINK); | ||
| 3264 | |||
| 3265 | if ((status == STAT_FORCELOSS) && (ai->scan_timeout > 0)) | ||
| 3266 | scan_forceloss = 1; | ||
| 3267 | |||
| 3268 | airo_print_status(ai->dev->name, status); | ||
| 3269 | |||
| 3270 | if ((status == STAT_ASSOC) || (status == STAT_REASSOC)) { | ||
| 3271 | if (auto_wep) | ||
| 3272 | ai->expires = 0; | ||
| 3273 | if (ai->list_bss_task) | ||
| 3274 | wake_up_process(ai->list_bss_task); | ||
| 3275 | set_bit(FLAG_UPDATE_UNI, &ai->flags); | ||
| 3276 | set_bit(FLAG_UPDATE_MULTI, &ai->flags); | ||
| 3277 | |||
| 3278 | set_bit(JOB_EVENT, &ai->jobs); | ||
| 3279 | wake_up_interruptible(&ai->thr_wait); | ||
| 3280 | |||
| 3281 | netif_carrier_on(ai->dev); | ||
| 3282 | } else if (!scan_forceloss) { | ||
| 3283 | if (auto_wep && !ai->expires) { | ||
| 3284 | ai->expires = RUN_AT(3*HZ); | ||
| 3285 | wake_up_interruptible(&ai->thr_wait); | ||
| 3286 | } | ||
| 3287 | |||
| 3288 | /* Send event to user space */ | ||
| 3289 | eth_zero_addr(wrqu.ap_addr.sa_data); | ||
| 3290 | wrqu.ap_addr.sa_family = ARPHRD_ETHER; | ||
| 3291 | wireless_send_event(ai->dev, SIOCGIWAP, &wrqu, NULL); | ||
| 3292 | netif_carrier_off(ai->dev); | ||
| 3293 | } else { | ||
| 3294 | netif_carrier_off(ai->dev); | ||
| 3295 | } | ||
| 3296 | } | ||
| 3297 | |||
| 3298 | static void airo_handle_rx(struct airo_info *ai) | ||
| 3299 | { | ||
| 3300 | struct sk_buff *skb = NULL; | ||
| 3301 | __le16 fc, v, *buffer, tmpbuf[4]; | ||
| 3302 | u16 len, hdrlen = 0, gap, fid; | ||
| 3303 | struct rx_hdr hdr; | ||
| 3304 | int success = 0; | ||
| 3305 | |||
| 3306 | if (test_bit(FLAG_MPI, &ai->flags)) { | ||
| 3307 | if (test_bit(FLAG_802_11, &ai->flags)) | ||
| 3308 | mpi_receive_802_11(ai); | ||
| 3309 | else | ||
| 3310 | mpi_receive_802_3(ai); | ||
| 3311 | OUT4500(ai, EVACK, EV_RX); | ||
| 3312 | return; | ||
| 3313 | } | ||
| 3314 | |||
| 3315 | fid = IN4500(ai, RXFID); | ||
| 3316 | |||
| 3317 | /* Get the packet length */ | ||
| 3318 | if (test_bit(FLAG_802_11, &ai->flags)) { | ||
| 3319 | bap_setup (ai, fid, 4, BAP0); | ||
| 3320 | bap_read (ai, (__le16*)&hdr, sizeof(hdr), BAP0); | ||
| 3321 | /* Bad CRC. Ignore packet */ | ||
| 3322 | if (le16_to_cpu(hdr.status) & 2) | ||
| 3323 | hdr.len = 0; | ||
| 3324 | if (ai->wifidev == NULL) | ||
| 3325 | hdr.len = 0; | ||
| 3326 | } else { | ||
| 3327 | bap_setup(ai, fid, 0x36, BAP0); | ||
| 3328 | bap_read(ai, &hdr.len, 2, BAP0); | ||
| 3329 | } | ||
| 3330 | len = le16_to_cpu(hdr.len); | ||
| 3331 | |||
| 3332 | if (len > AIRO_DEF_MTU) { | ||
| 3333 | airo_print_err(ai->dev->name, "Bad size %d", len); | ||
| 3334 | goto done; | ||
| 3335 | } | ||
| 3336 | if (len == 0) | ||
| 3337 | goto done; | ||
| 3338 | |||
| 3339 | if (test_bit(FLAG_802_11, &ai->flags)) { | ||
| 3340 | bap_read(ai, &fc, sizeof (fc), BAP0); | ||
| 3341 | hdrlen = header_len(fc); | ||
| 3342 | } else | ||
| 3343 | hdrlen = ETH_ALEN * 2; | ||
| 3344 | |||
| 3345 | skb = dev_alloc_skb(len + hdrlen + 2 + 2); | ||
| 3346 | if (!skb) { | ||
| 3347 | ai->dev->stats.rx_dropped++; | ||
| 3348 | goto done; | ||
| 3349 | } | ||
| 3350 | |||
| 3351 | skb_reserve(skb, 2); /* This way the IP header is aligned */ | ||
| 3352 | buffer = skb_put(skb, len + hdrlen); | ||
| 3353 | if (test_bit(FLAG_802_11, &ai->flags)) { | ||
| 3354 | buffer[0] = fc; | ||
| 3355 | bap_read(ai, buffer + 1, hdrlen - 2, BAP0); | ||
| 3356 | if (hdrlen == 24) | ||
| 3357 | bap_read(ai, tmpbuf, 6, BAP0); | ||
| 3358 | |||
| 3359 | bap_read(ai, &v, sizeof(v), BAP0); | ||
| 3360 | gap = le16_to_cpu(v); | ||
| 3361 | if (gap) { | ||
| 3362 | if (gap <= 8) { | ||
| 3363 | bap_read(ai, tmpbuf, gap, BAP0); | ||
| 3364 | } else { | ||
| 3365 | airo_print_err(ai->dev->name, "gaplen too " | ||
| 3366 | "big. Problems will follow..."); | ||
| 3367 | } | ||
| 3368 | } | ||
| 3369 | bap_read(ai, buffer + hdrlen/2, len, BAP0); | ||
| 3370 | } else { | ||
| 3371 | MICBuffer micbuf; | ||
| 3372 | |||
| 3373 | bap_read(ai, buffer, ETH_ALEN * 2, BAP0); | ||
| 3374 | if (ai->micstats.enabled) { | ||
| 3375 | bap_read(ai, (__le16 *) &micbuf, sizeof (micbuf), BAP0); | ||
| 3376 | if (ntohs(micbuf.typelen) > 0x05DC) | ||
| 3377 | bap_setup(ai, fid, 0x44, BAP0); | ||
| 3378 | else { | ||
| 3379 | if (len <= sizeof (micbuf)) { | ||
| 3380 | dev_kfree_skb_irq(skb); | ||
| 3381 | goto done; | ||
| 3382 | } | ||
| 3383 | |||
| 3384 | len -= sizeof(micbuf); | ||
| 3385 | skb_trim(skb, len + hdrlen); | ||
| 3386 | } | ||
| 3387 | } | ||
| 3388 | |||
| 3389 | bap_read(ai, buffer + ETH_ALEN, len, BAP0); | ||
| 3390 | if (decapsulate(ai, &micbuf, (etherHead*) buffer, len)) | ||
| 3391 | dev_kfree_skb_irq (skb); | ||
| 3392 | else | ||
| 3393 | success = 1; | ||
| 3394 | } | ||
| 3395 | |||
| 3396 | #ifdef WIRELESS_SPY | ||
| 3397 | if (success && (ai->spy_data.spy_number > 0)) { | ||
| 3398 | char *sa; | ||
| 3399 | struct iw_quality wstats; | ||
| 3400 | |||
| 3401 | /* Prepare spy data : addr + qual */ | ||
| 3402 | if (!test_bit(FLAG_802_11, &ai->flags)) { | ||
| 3403 | sa = (char *) buffer + 6; | ||
| 3404 | bap_setup(ai, fid, 8, BAP0); | ||
| 3405 | bap_read(ai, (__le16 *) hdr.rssi, 2, BAP0); | ||
| 3406 | } else | ||
| 3407 | sa = (char *) buffer + 10; | ||
| 3408 | wstats.qual = hdr.rssi[0]; | ||
| 3409 | if (ai->rssi) | ||
| 3410 | wstats.level = 0x100 - ai->rssi[hdr.rssi[1]].rssidBm; | ||
| 3411 | else | ||
| 3412 | wstats.level = (hdr.rssi[1] + 321) / 2; | ||
| 3413 | wstats.noise = ai->wstats.qual.noise; | ||
| 3414 | wstats.updated = IW_QUAL_LEVEL_UPDATED | ||
| 3415 | | IW_QUAL_QUAL_UPDATED | ||
| 3416 | | IW_QUAL_DBM; | ||
| 3417 | /* Update spy records */ | ||
| 3418 | wireless_spy_update(ai->dev, sa, &wstats); | ||
| 3419 | } | ||
| 3420 | #endif /* WIRELESS_SPY */ | ||
| 3421 | |||
| 3422 | done: | ||
| 3423 | OUT4500(ai, EVACK, EV_RX); | ||
| 3424 | |||
| 3425 | if (success) { | ||
| 3426 | if (test_bit(FLAG_802_11, &ai->flags)) { | ||
| 3427 | skb_reset_mac_header(skb); | ||
| 3428 | skb->pkt_type = PACKET_OTHERHOST; | ||
| 3429 | skb->dev = ai->wifidev; | ||
| 3430 | skb->protocol = htons(ETH_P_802_2); | ||
| 3431 | } else | ||
| 3432 | skb->protocol = eth_type_trans(skb, ai->dev); | ||
| 3433 | skb->ip_summed = CHECKSUM_NONE; | ||
| 3434 | |||
| 3435 | netif_rx(skb); | ||
| 3436 | } | ||
| 3437 | } | ||
| 3438 | |||
| 3439 | static void airo_handle_tx(struct airo_info *ai, u16 status) | ||
| 3440 | { | ||
| 3441 | int i, index = -1; | ||
| 3442 | u16 fid; | ||
| 3443 | |||
| 3444 | if (test_bit(FLAG_MPI, &ai->flags)) { | ||
| 3445 | unsigned long flags; | ||
| 3446 | |||
| 3447 | if (status & EV_TXEXC) | ||
| 3448 | get_tx_error(ai, -1); | ||
| 3449 | |||
| 3450 | spin_lock_irqsave(&ai->aux_lock, flags); | ||
| 3451 | if (!skb_queue_empty(&ai->txq)) { | ||
| 3452 | spin_unlock_irqrestore(&ai->aux_lock, flags); | ||
| 3453 | mpi_send_packet(ai->dev); | ||
| 3454 | } else { | ||
| 3455 | clear_bit(FLAG_PENDING_XMIT, &ai->flags); | ||
| 3456 | spin_unlock_irqrestore(&ai->aux_lock, flags); | ||
| 3457 | netif_wake_queue(ai->dev); | ||
| 3458 | } | ||
| 3459 | OUT4500(ai, EVACK, status & (EV_TX | EV_TXCPY | EV_TXEXC)); | ||
| 3460 | return; | ||
| 3461 | } | ||
| 3462 | |||
| 3463 | fid = IN4500(ai, TXCOMPLFID); | ||
| 3464 | |||
| 3465 | for (i = 0; i < MAX_FIDS; i++) { | ||
| 3466 | if ((ai->fids[i] & 0xffff) == fid) | ||
| 3467 | index = i; | ||
| 3468 | } | ||
| 3469 | |||
| 3470 | if (index != -1) { | ||
| 3471 | if (status & EV_TXEXC) | ||
| 3472 | get_tx_error(ai, index); | ||
| 3473 | |||
| 3474 | OUT4500(ai, EVACK, status & (EV_TX | EV_TXEXC)); | ||
| 3475 | |||
| 3476 | /* Set up to be used again */ | ||
| 3477 | ai->fids[index] &= 0xffff; | ||
| 3478 | if (index < MAX_FIDS / 2) { | ||
| 3479 | if (!test_bit(FLAG_PENDING_XMIT, &ai->flags)) | ||
| 3480 | netif_wake_queue(ai->dev); | ||
| 3481 | } else { | ||
| 3482 | if (!test_bit(FLAG_PENDING_XMIT11, &ai->flags)) | ||
| 3483 | netif_wake_queue(ai->wifidev); | ||
| 3484 | } | ||
| 3485 | } else { | ||
| 3486 | OUT4500(ai, EVACK, status & (EV_TX | EV_TXCPY | EV_TXEXC)); | ||
| 3487 | airo_print_err(ai->dev->name, "Unallocated FID was used to xmit"); | ||
| 3488 | } | ||
| 3489 | } | ||
| 3490 | |||
| 3491 | static irqreturn_t airo_interrupt(int irq, void *dev_id) | ||
| 3492 | { | ||
| 3493 | struct net_device *dev = dev_id; | ||
| 3494 | u16 status, savedInterrupts = 0; | ||
| 3495 | struct airo_info *ai = dev->ml_priv; | ||
| 3496 | int handled = 0; | ||
| 3497 | |||
| 3498 | if (!netif_device_present(dev)) | ||
| 3499 | return IRQ_NONE; | ||
| 3500 | |||
| 3501 | for (;;) { | ||
| 3502 | status = IN4500(ai, EVSTAT); | ||
| 3503 | if (!(status & STATUS_INTS) || (status == 0xffff)) | ||
| 3504 | break; | ||
| 3505 | |||
| 3506 | handled = 1; | ||
| 3507 | |||
| 3508 | if (status & EV_AWAKE) { | ||
| 3509 | OUT4500(ai, EVACK, EV_AWAKE); | ||
| 3510 | OUT4500(ai, EVACK, EV_AWAKE); | ||
| 3511 | } | ||
| 3512 | |||
| 3513 | if (!savedInterrupts) { | ||
| 3514 | savedInterrupts = IN4500(ai, EVINTEN); | ||
| 3515 | OUT4500(ai, EVINTEN, 0); | ||
| 3516 | } | ||
| 3517 | |||
| 3518 | if (status & EV_MIC) { | ||
| 3519 | OUT4500(ai, EVACK, EV_MIC); | ||
| 3520 | airo_handle_cisco_mic(ai); | ||
| 3521 | } | ||
| 3522 | |||
| 3523 | if (status & EV_LINK) { | ||
| 3524 | /* Link status changed */ | ||
| 3525 | airo_handle_link(ai); | ||
| 3526 | } | ||
| 3527 | |||
| 3528 | /* Check to see if there is something to receive */ | ||
| 3529 | if (status & EV_RX) | ||
| 3530 | airo_handle_rx(ai); | ||
| 3531 | |||
| 3532 | /* Check to see if a packet has been transmitted */ | ||
| 3533 | if (status & (EV_TX | EV_TXCPY | EV_TXEXC)) | ||
| 3534 | airo_handle_tx(ai, status); | ||
| 3535 | |||
| 3536 | if (status & ~STATUS_INTS & ~IGNORE_INTS) { | ||
| 3537 | airo_print_warn(ai->dev->name, "Got weird status %x", | ||
| 3538 | status & ~STATUS_INTS & ~IGNORE_INTS); | ||
| 3539 | } | ||
| 3540 | } | ||
| 3541 | |||
| 3542 | if (savedInterrupts) | ||
| 3543 | OUT4500(ai, EVINTEN, savedInterrupts); | ||
| 3544 | |||
| 3545 | return IRQ_RETVAL(handled); | ||
| 3546 | } | ||
| 3547 | |||
| 3548 | /* | ||
| 3549 | * Routines to talk to the card | ||
| 3550 | */ | ||
| 3551 | |||
| 3552 | /* | ||
| 3553 | * This was originally written for the 4500, hence the name | ||
| 3554 | * NOTE: If use with 8bit mode and SMP bad things will happen! | ||
| 3555 | * Why would some one do 8 bit IO in an SMP machine?!? | ||
| 3556 | */ | ||
| 3557 | static void OUT4500(struct airo_info *ai, u16 reg, u16 val) | ||
| 3558 | { | ||
| 3559 | if (test_bit(FLAG_MPI,&ai->flags)) | ||
| 3560 | reg <<= 1; | ||
| 3561 | if (!do8bitIO) | ||
| 3562 | outw(val, ai->dev->base_addr + reg); | ||
| 3563 | else { | ||
| 3564 | outb(val & 0xff, ai->dev->base_addr + reg); | ||
| 3565 | outb(val >> 8, ai->dev->base_addr + reg + 1); | ||
| 3566 | } | ||
| 3567 | } | ||
| 3568 | |||
| 3569 | static u16 IN4500(struct airo_info *ai, u16 reg) | ||
| 3570 | { | ||
| 3571 | unsigned short rc; | ||
| 3572 | |||
| 3573 | if (test_bit(FLAG_MPI,&ai->flags)) | ||
| 3574 | reg <<= 1; | ||
| 3575 | if (!do8bitIO) | ||
| 3576 | rc = inw(ai->dev->base_addr + reg); | ||
| 3577 | else { | ||
| 3578 | rc = inb(ai->dev->base_addr + reg); | ||
| 3579 | rc += ((int)inb(ai->dev->base_addr + reg + 1)) << 8; | ||
| 3580 | } | ||
| 3581 | return rc; | ||
| 3582 | } | ||
| 3583 | |||
| 3584 | static int enable_MAC(struct airo_info *ai, int lock) | ||
| 3585 | { | ||
| 3586 | int rc; | ||
| 3587 | Cmd cmd; | ||
| 3588 | Resp rsp; | ||
| 3589 | |||
| 3590 | /* FLAG_RADIO_OFF : Radio disabled via /proc or Wireless Extensions | ||
| 3591 | * FLAG_RADIO_DOWN : Radio disabled via "ifconfig ethX down" | ||
| 3592 | * Note : we could try to use !netif_running(dev) in enable_MAC() | ||
| 3593 | * instead of this flag, but I don't trust it *within* the | ||
| 3594 | * open/close functions, and testing both flags together is | ||
| 3595 | * "cheaper" - Jean II */ | ||
| 3596 | if (ai->flags & FLAG_RADIO_MASK) return SUCCESS; | ||
| 3597 | |||
| 3598 | if (lock && down_interruptible(&ai->sem)) | ||
| 3599 | return -ERESTARTSYS; | ||
| 3600 | |||
| 3601 | if (!test_bit(FLAG_ENABLED, &ai->flags)) { | ||
| 3602 | memset(&cmd, 0, sizeof(cmd)); | ||
| 3603 | cmd.cmd = MAC_ENABLE; | ||
| 3604 | rc = issuecommand(ai, &cmd, &rsp, true); | ||
| 3605 | if (rc == SUCCESS) | ||
| 3606 | set_bit(FLAG_ENABLED, &ai->flags); | ||
| 3607 | } else | ||
| 3608 | rc = SUCCESS; | ||
| 3609 | |||
| 3610 | if (lock) | ||
| 3611 | up(&ai->sem); | ||
| 3612 | |||
| 3613 | if (rc) | ||
| 3614 | airo_print_err(ai->dev->name, "Cannot enable MAC"); | ||
| 3615 | else if ((rsp.status & 0xFF00) != 0) { | ||
| 3616 | airo_print_err(ai->dev->name, "Bad MAC enable reason=%x, " | ||
| 3617 | "rid=%x, offset=%d", rsp.rsp0, rsp.rsp1, rsp.rsp2); | ||
| 3618 | rc = ERROR; | ||
| 3619 | } | ||
| 3620 | return rc; | ||
| 3621 | } | ||
| 3622 | |||
| 3623 | static void disable_MAC(struct airo_info *ai, int lock) | ||
| 3624 | { | ||
| 3625 | Cmd cmd; | ||
| 3626 | Resp rsp; | ||
| 3627 | |||
| 3628 | if (lock == 1 && down_interruptible(&ai->sem)) | ||
| 3629 | return; | ||
| 3630 | |||
| 3631 | if (test_bit(FLAG_ENABLED, &ai->flags)) { | ||
| 3632 | if (lock != 2) /* lock == 2 means don't disable carrier */ | ||
| 3633 | netif_carrier_off(ai->dev); | ||
| 3634 | memset(&cmd, 0, sizeof(cmd)); | ||
| 3635 | cmd.cmd = MAC_DISABLE; // disable in case already enabled | ||
| 3636 | issuecommand(ai, &cmd, &rsp, true); | ||
| 3637 | clear_bit(FLAG_ENABLED, &ai->flags); | ||
| 3638 | } | ||
| 3639 | if (lock == 1) | ||
| 3640 | up(&ai->sem); | ||
| 3641 | } | ||
| 3642 | |||
| 3643 | static void enable_interrupts(struct airo_info *ai) | ||
| 3644 | { | ||
| 3645 | /* Enable the interrupts */ | ||
| 3646 | OUT4500(ai, EVINTEN, STATUS_INTS); | ||
| 3647 | } | ||
| 3648 | |||
| 3649 | static void disable_interrupts(struct airo_info *ai) | ||
| 3650 | { | ||
| 3651 | OUT4500(ai, EVINTEN, 0); | ||
| 3652 | } | ||
| 3653 | |||
| 3654 | static void mpi_receive_802_3(struct airo_info *ai) | ||
| 3655 | { | ||
| 3656 | RxFid rxd; | ||
| 3657 | int len = 0; | ||
| 3658 | struct sk_buff *skb; | ||
| 3659 | char *buffer; | ||
| 3660 | int off = 0; | ||
| 3661 | MICBuffer micbuf; | ||
| 3662 | |||
| 3663 | memcpy_fromio(&rxd, ai->rxfids[0].card_ram_off, sizeof(rxd)); | ||
| 3664 | /* Make sure we got something */ | ||
| 3665 | if (rxd.rdy && rxd.valid == 0) { | ||
| 3666 | len = rxd.len + 12; | ||
| 3667 | if (len < 12 || len > 2048) | ||
| 3668 | goto badrx; | ||
| 3669 | |||
| 3670 | skb = dev_alloc_skb(len); | ||
| 3671 | if (!skb) { | ||
| 3672 | ai->dev->stats.rx_dropped++; | ||
| 3673 | goto badrx; | ||
| 3674 | } | ||
| 3675 | buffer = skb_put(skb, len); | ||
| 3676 | memcpy(buffer, ai->rxfids[0].virtual_host_addr, ETH_ALEN * 2); | ||
| 3677 | if (ai->micstats.enabled) { | ||
| 3678 | memcpy(&micbuf, | ||
| 3679 | ai->rxfids[0].virtual_host_addr + ETH_ALEN * 2, | ||
| 3680 | sizeof(micbuf)); | ||
| 3681 | if (ntohs(micbuf.typelen) <= 0x05DC) { | ||
| 3682 | if (len <= sizeof(micbuf) + ETH_ALEN * 2) | ||
| 3683 | goto badmic; | ||
| 3684 | |||
| 3685 | off = sizeof(micbuf); | ||
| 3686 | skb_trim (skb, len - off); | ||
| 3687 | } | ||
| 3688 | } | ||
| 3689 | memcpy(buffer + ETH_ALEN * 2, | ||
| 3690 | ai->rxfids[0].virtual_host_addr + ETH_ALEN * 2 + off, | ||
| 3691 | len - ETH_ALEN * 2 - off); | ||
| 3692 | if (decapsulate (ai, &micbuf, (etherHead*)buffer, len - off - ETH_ALEN * 2)) { | ||
| 3693 | badmic: | ||
| 3694 | dev_kfree_skb_irq (skb); | ||
| 3695 | goto badrx; | ||
| 3696 | } | ||
| 3697 | #ifdef WIRELESS_SPY | ||
| 3698 | if (ai->spy_data.spy_number > 0) { | ||
| 3699 | char *sa; | ||
| 3700 | struct iw_quality wstats; | ||
| 3701 | /* Prepare spy data : addr + qual */ | ||
| 3702 | sa = buffer + ETH_ALEN; | ||
| 3703 | wstats.qual = 0; /* XXX Where do I get that info from ??? */ | ||
| 3704 | wstats.level = 0; | ||
| 3705 | wstats.updated = 0; | ||
| 3706 | /* Update spy records */ | ||
| 3707 | wireless_spy_update(ai->dev, sa, &wstats); | ||
| 3708 | } | ||
| 3709 | #endif /* WIRELESS_SPY */ | ||
| 3710 | |||
| 3711 | skb->ip_summed = CHECKSUM_NONE; | ||
| 3712 | skb->protocol = eth_type_trans(skb, ai->dev); | ||
| 3713 | netif_rx(skb); | ||
| 3714 | } | ||
| 3715 | badrx: | ||
| 3716 | if (rxd.valid == 0) { | ||
| 3717 | rxd.valid = 1; | ||
| 3718 | rxd.rdy = 0; | ||
| 3719 | rxd.len = PKTSIZE; | ||
| 3720 | memcpy_toio(ai->rxfids[0].card_ram_off, &rxd, sizeof(rxd)); | ||
| 3721 | } | ||
| 3722 | } | ||
| 3723 | |||
| 3724 | static void mpi_receive_802_11(struct airo_info *ai) | ||
| 3725 | { | ||
| 3726 | RxFid rxd; | ||
| 3727 | struct sk_buff *skb = NULL; | ||
| 3728 | u16 len, hdrlen = 0; | ||
| 3729 | __le16 fc; | ||
| 3730 | struct rx_hdr hdr; | ||
| 3731 | u16 gap; | ||
| 3732 | u16 *buffer; | ||
| 3733 | char *ptr = ai->rxfids[0].virtual_host_addr + 4; | ||
| 3734 | |||
| 3735 | memcpy_fromio(&rxd, ai->rxfids[0].card_ram_off, sizeof(rxd)); | ||
| 3736 | memcpy ((char *)&hdr, ptr, sizeof(hdr)); | ||
| 3737 | ptr += sizeof(hdr); | ||
| 3738 | /* Bad CRC. Ignore packet */ | ||
| 3739 | if (le16_to_cpu(hdr.status) & 2) | ||
| 3740 | hdr.len = 0; | ||
| 3741 | if (ai->wifidev == NULL) | ||
| 3742 | hdr.len = 0; | ||
| 3743 | len = le16_to_cpu(hdr.len); | ||
| 3744 | if (len > AIRO_DEF_MTU) { | ||
| 3745 | airo_print_err(ai->dev->name, "Bad size %d", len); | ||
| 3746 | goto badrx; | ||
| 3747 | } | ||
| 3748 | if (len == 0) | ||
| 3749 | goto badrx; | ||
| 3750 | |||
| 3751 | fc = get_unaligned((__le16 *)ptr); | ||
| 3752 | hdrlen = header_len(fc); | ||
| 3753 | |||
| 3754 | skb = dev_alloc_skb(len + hdrlen + 2); | ||
| 3755 | if (!skb) { | ||
| 3756 | ai->dev->stats.rx_dropped++; | ||
| 3757 | goto badrx; | ||
| 3758 | } | ||
| 3759 | buffer = skb_put(skb, len + hdrlen); | ||
| 3760 | memcpy ((char *)buffer, ptr, hdrlen); | ||
| 3761 | ptr += hdrlen; | ||
| 3762 | if (hdrlen == 24) | ||
| 3763 | ptr += 6; | ||
| 3764 | gap = get_unaligned_le16(ptr); | ||
| 3765 | ptr += sizeof(__le16); | ||
| 3766 | if (gap) { | ||
| 3767 | if (gap <= 8) | ||
| 3768 | ptr += gap; | ||
| 3769 | else | ||
| 3770 | airo_print_err(ai->dev->name, | ||
| 3771 | "gaplen too big. Problems will follow..."); | ||
| 3772 | } | ||
| 3773 | memcpy ((char *)buffer + hdrlen, ptr, len); | ||
| 3774 | ptr += len; | ||
| 3775 | #ifdef IW_WIRELESS_SPY /* defined in iw_handler.h */ | ||
| 3776 | if (ai->spy_data.spy_number > 0) { | ||
| 3777 | char *sa; | ||
| 3778 | struct iw_quality wstats; | ||
| 3779 | /* Prepare spy data : addr + qual */ | ||
| 3780 | sa = (char*)buffer + 10; | ||
| 3781 | wstats.qual = hdr.rssi[0]; | ||
| 3782 | if (ai->rssi) | ||
| 3783 | wstats.level = 0x100 - ai->rssi[hdr.rssi[1]].rssidBm; | ||
| 3784 | else | ||
| 3785 | wstats.level = (hdr.rssi[1] + 321) / 2; | ||
| 3786 | wstats.noise = ai->wstats.qual.noise; | ||
| 3787 | wstats.updated = IW_QUAL_QUAL_UPDATED | ||
| 3788 | | IW_QUAL_LEVEL_UPDATED | ||
| 3789 | | IW_QUAL_DBM; | ||
| 3790 | /* Update spy records */ | ||
| 3791 | wireless_spy_update(ai->dev, sa, &wstats); | ||
| 3792 | } | ||
| 3793 | #endif /* IW_WIRELESS_SPY */ | ||
| 3794 | skb_reset_mac_header(skb); | ||
| 3795 | skb->pkt_type = PACKET_OTHERHOST; | ||
| 3796 | skb->dev = ai->wifidev; | ||
| 3797 | skb->protocol = htons(ETH_P_802_2); | ||
| 3798 | skb->ip_summed = CHECKSUM_NONE; | ||
| 3799 | netif_rx(skb); | ||
| 3800 | |||
| 3801 | badrx: | ||
| 3802 | if (rxd.valid == 0) { | ||
| 3803 | rxd.valid = 1; | ||
| 3804 | rxd.rdy = 0; | ||
| 3805 | rxd.len = PKTSIZE; | ||
| 3806 | memcpy_toio(ai->rxfids[0].card_ram_off, &rxd, sizeof(rxd)); | ||
| 3807 | } | ||
| 3808 | } | ||
| 3809 | |||
| 3810 | static inline void set_auth_type(struct airo_info *local, int auth_type) | ||
| 3811 | { | ||
| 3812 | local->config.authType = auth_type; | ||
| 3813 | /* Cache the last auth type used (of AUTH_OPEN and AUTH_ENCRYPT). | ||
| 3814 | * Used by airo_set_auth() | ||
| 3815 | */ | ||
| 3816 | if (auth_type == AUTH_OPEN || auth_type == AUTH_ENCRYPT) | ||
| 3817 | local->last_auth = auth_type; | ||
| 3818 | } | ||
| 3819 | |||
| 3820 | static int noinline_for_stack airo_readconfig(struct airo_info *ai, | ||
| 3821 | struct net_device *dev, int lock) | ||
| 3822 | { | ||
| 3823 | int i, status; | ||
| 3824 | /* large variables, so don't inline this function, | ||
| 3825 | * maybe change to kmalloc | ||
| 3826 | */ | ||
| 3827 | tdsRssiRid rssi_rid; | ||
| 3828 | CapabilityRid cap_rid; | ||
| 3829 | |||
| 3830 | kfree(ai->SSID); | ||
| 3831 | ai->SSID = NULL; | ||
| 3832 | // general configuration (read/modify/write) | ||
| 3833 | status = readConfigRid(ai, lock); | ||
| 3834 | if (status != SUCCESS) return ERROR; | ||
| 3835 | |||
| 3836 | status = readCapabilityRid(ai, &cap_rid, lock); | ||
| 3837 | if (status != SUCCESS) return ERROR; | ||
| 3838 | |||
| 3839 | status = PC4500_readrid(ai, RID_RSSI, &rssi_rid, sizeof(rssi_rid), lock); | ||
| 3840 | if (status == SUCCESS) { | ||
| 3841 | if (ai->rssi || (ai->rssi = kmalloc(512, GFP_KERNEL)) != NULL) | ||
| 3842 | memcpy(ai->rssi, (u8*)&rssi_rid + 2, 512); /* Skip RID length member */ | ||
| 3843 | } | ||
| 3844 | else { | ||
| 3845 | kfree(ai->rssi); | ||
| 3846 | ai->rssi = NULL; | ||
| 3847 | if (cap_rid.softCap & cpu_to_le16(8)) | ||
| 3848 | ai->config.rmode |= RXMODE_NORMALIZED_RSSI; | ||
| 3849 | else | ||
| 3850 | airo_print_warn(ai->dev->name, "unknown received signal " | ||
| 3851 | "level scale"); | ||
| 3852 | } | ||
| 3853 | ai->config.opmode = adhoc ? MODE_STA_IBSS : MODE_STA_ESS; | ||
| 3854 | set_auth_type(ai, AUTH_OPEN); | ||
| 3855 | ai->config.modulation = MOD_CCK; | ||
| 3856 | |||
| 3857 | if (le16_to_cpu(cap_rid.len) >= sizeof(cap_rid) && | ||
| 3858 | (cap_rid.extSoftCap & cpu_to_le16(1)) && | ||
| 3859 | micsetup(ai) == SUCCESS) { | ||
| 3860 | ai->config.opmode |= MODE_MIC; | ||
| 3861 | set_bit(FLAG_MIC_CAPABLE, &ai->flags); | ||
| 3862 | } | ||
| 3863 | |||
| 3864 | /* Save off the MAC */ | ||
| 3865 | eth_hw_addr_set(dev, ai->config.macAddr); | ||
| 3866 | |||
| 3867 | /* Check to see if there are any insmod configured | ||
| 3868 | rates to add */ | ||
| 3869 | if (rates[0]) { | ||
| 3870 | memset(ai->config.rates, 0, sizeof(ai->config.rates)); | ||
| 3871 | for (i = 0; i < 8 && rates[i]; i++) { | ||
| 3872 | ai->config.rates[i] = rates[i]; | ||
| 3873 | } | ||
| 3874 | } | ||
| 3875 | set_bit (FLAG_COMMIT, &ai->flags); | ||
| 3876 | |||
| 3877 | return SUCCESS; | ||
| 3878 | } | ||
| 3879 | |||
| 3880 | |||
| 3881 | static u16 setup_card(struct airo_info *ai, struct net_device *dev, int lock) | ||
| 3882 | { | ||
| 3883 | Cmd cmd; | ||
| 3884 | Resp rsp; | ||
| 3885 | int status; | ||
| 3886 | SsidRid mySsid; | ||
| 3887 | __le16 lastindex; | ||
| 3888 | WepKeyRid wkr; | ||
| 3889 | int rc; | ||
| 3890 | |||
| 3891 | memset(&mySsid, 0, sizeof(mySsid)); | ||
| 3892 | kfree (ai->flash); | ||
| 3893 | ai->flash = NULL; | ||
| 3894 | |||
| 3895 | /* The NOP is the first step in getting the card going */ | ||
| 3896 | cmd.cmd = NOP; | ||
| 3897 | cmd.parm0 = cmd.parm1 = cmd.parm2 = 0; | ||
| 3898 | if (lock && down_interruptible(&ai->sem)) | ||
| 3899 | return ERROR; | ||
| 3900 | if (issuecommand(ai, &cmd, &rsp, true) != SUCCESS) { | ||
| 3901 | if (lock) | ||
| 3902 | up(&ai->sem); | ||
| 3903 | return ERROR; | ||
| 3904 | } | ||
| 3905 | disable_MAC(ai, 0); | ||
| 3906 | |||
| 3907 | // Let's figure out if we need to use the AUX port | ||
| 3908 | if (!test_bit(FLAG_MPI,&ai->flags)) { | ||
| 3909 | cmd.cmd = CMD_ENABLEAUX; | ||
| 3910 | if (issuecommand(ai, &cmd, &rsp, true) != SUCCESS) { | ||
| 3911 | if (lock) | ||
| 3912 | up(&ai->sem); | ||
| 3913 | airo_print_err(ai->dev->name, "Error checking for AUX port"); | ||
| 3914 | return ERROR; | ||
| 3915 | } | ||
| 3916 | if (!aux_bap || rsp.status & 0xff00) { | ||
| 3917 | ai->bap_read = fast_bap_read; | ||
| 3918 | airo_print_dbg(ai->dev->name, "Doing fast bap_reads"); | ||
| 3919 | } else { | ||
| 3920 | ai->bap_read = aux_bap_read; | ||
| 3921 | airo_print_dbg(ai->dev->name, "Doing AUX bap_reads"); | ||
| 3922 | } | ||
| 3923 | } | ||
| 3924 | if (lock) | ||
| 3925 | up(&ai->sem); | ||
| 3926 | if (ai->config.len == 0) { | ||
| 3927 | status = airo_readconfig(ai, dev, lock); | ||
| 3928 | if (status != SUCCESS) | ||
| 3929 | return ERROR; | ||
| 3930 | } | ||
| 3931 | |||
| 3932 | /* Setup the SSIDs if present */ | ||
| 3933 | if (ssids[0]) { | ||
| 3934 | int i; | ||
| 3935 | for (i = 0; i < 3 && ssids[i]; i++) { | ||
| 3936 | size_t len = strlen(ssids[i]); | ||
| 3937 | if (len > 32) | ||
| 3938 | len = 32; | ||
| 3939 | mySsid.ssids[i].len = cpu_to_le16(len); | ||
| 3940 | memcpy(mySsid.ssids[i].ssid, ssids[i], len); | ||
| 3941 | } | ||
| 3942 | mySsid.len = cpu_to_le16(sizeof(mySsid)); | ||
| 3943 | } | ||
| 3944 | |||
| 3945 | status = writeConfigRid(ai, lock); | ||
| 3946 | if (status != SUCCESS) return ERROR; | ||
| 3947 | |||
| 3948 | /* Set up the SSID list */ | ||
| 3949 | if (ssids[0]) { | ||
| 3950 | status = writeSsidRid(ai, &mySsid, lock); | ||
| 3951 | if (status != SUCCESS) return ERROR; | ||
| 3952 | } | ||
| 3953 | |||
| 3954 | status = enable_MAC(ai, lock); | ||
| 3955 | if (status != SUCCESS) | ||
| 3956 | return ERROR; | ||
| 3957 | |||
| 3958 | /* Grab the initial wep key, we gotta save it for auto_wep */ | ||
| 3959 | rc = readWepKeyRid(ai, &wkr, 1, lock); | ||
| 3960 | if (rc == SUCCESS) do { | ||
| 3961 | lastindex = wkr.kindex; | ||
| 3962 | if (wkr.kindex == cpu_to_le16(0xffff)) { | ||
| 3963 | ai->defindex = wkr.mac[0]; | ||
| 3964 | } | ||
| 3965 | rc = readWepKeyRid(ai, &wkr, 0, lock); | ||
| 3966 | } while (lastindex != wkr.kindex); | ||
| 3967 | |||
| 3968 | try_auto_wep(ai); | ||
| 3969 | |||
| 3970 | return SUCCESS; | ||
| 3971 | } | ||
| 3972 | |||
| 3973 | static u16 issuecommand(struct airo_info *ai, Cmd *pCmd, Resp *pRsp, | ||
| 3974 | bool may_sleep) | ||
| 3975 | { | ||
| 3976 | // Im really paranoid about letting it run forever! | ||
| 3977 | int max_tries = 600000; | ||
| 3978 | |||
| 3979 | if (IN4500(ai, EVSTAT) & EV_CMD) | ||
| 3980 | OUT4500(ai, EVACK, EV_CMD); | ||
| 3981 | |||
| 3982 | OUT4500(ai, PARAM0, pCmd->parm0); | ||
| 3983 | OUT4500(ai, PARAM1, pCmd->parm1); | ||
| 3984 | OUT4500(ai, PARAM2, pCmd->parm2); | ||
| 3985 | OUT4500(ai, COMMAND, pCmd->cmd); | ||
| 3986 | |||
| 3987 | while (max_tries-- && (IN4500(ai, EVSTAT) & EV_CMD) == 0) { | ||
| 3988 | if ((IN4500(ai, COMMAND)) == pCmd->cmd) | ||
| 3989 | // PC4500 didn't notice command, try again | ||
| 3990 | OUT4500(ai, COMMAND, pCmd->cmd); | ||
| 3991 | if (may_sleep && (max_tries & 255) == 0) | ||
| 3992 | cond_resched(); | ||
| 3993 | } | ||
| 3994 | |||
| 3995 | if (max_tries == -1) { | ||
| 3996 | airo_print_err(ai->dev->name, | ||
| 3997 | "Max tries exceeded when issuing command"); | ||
| 3998 | if (IN4500(ai, COMMAND) & COMMAND_BUSY) | ||
| 3999 | OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY); | ||
| 4000 | return ERROR; | ||
| 4001 | } | ||
| 4002 | |||
| 4003 | // command completed | ||
| 4004 | pRsp->status = IN4500(ai, STATUS); | ||
| 4005 | pRsp->rsp0 = IN4500(ai, RESP0); | ||
| 4006 | pRsp->rsp1 = IN4500(ai, RESP1); | ||
| 4007 | pRsp->rsp2 = IN4500(ai, RESP2); | ||
| 4008 | if ((pRsp->status & 0xff00)!=0 && pCmd->cmd != CMD_SOFTRESET) | ||
| 4009 | airo_print_err(ai->dev->name, | ||
| 4010 | "cmd:%x status:%x rsp0:%x rsp1:%x rsp2:%x", | ||
| 4011 | pCmd->cmd, pRsp->status, pRsp->rsp0, pRsp->rsp1, | ||
| 4012 | pRsp->rsp2); | ||
| 4013 | |||
| 4014 | // clear stuck command busy if necessary | ||
| 4015 | if (IN4500(ai, COMMAND) & COMMAND_BUSY) { | ||
| 4016 | OUT4500(ai, EVACK, EV_CLEARCOMMANDBUSY); | ||
| 4017 | } | ||
| 4018 | // acknowledge processing the status/response | ||
| 4019 | OUT4500(ai, EVACK, EV_CMD); | ||
| 4020 | |||
| 4021 | return SUCCESS; | ||
| 4022 | } | ||
| 4023 | |||
| 4024 | /* Sets up the bap to start exchange data. whichbap should | ||
| 4025 | * be one of the BAP0 or BAP1 defines. Locks should be held before | ||
| 4026 | * calling! */ | ||
| 4027 | static int bap_setup(struct airo_info *ai, u16 rid, u16 offset, int whichbap) | ||
| 4028 | { | ||
| 4029 | int timeout = 50; | ||
| 4030 | int max_tries = 3; | ||
| 4031 | |||
| 4032 | OUT4500(ai, SELECT0+whichbap, rid); | ||
| 4033 | OUT4500(ai, OFFSET0+whichbap, offset); | ||
| 4034 | while (1) { | ||
| 4035 | int status = IN4500(ai, OFFSET0+whichbap); | ||
| 4036 | if (status & BAP_BUSY) { | ||
| 4037 | /* This isn't really a timeout, but its kinda | ||
| 4038 | close */ | ||
| 4039 | if (timeout--) { | ||
| 4040 | continue; | ||
| 4041 | } | ||
| 4042 | } else if (status & BAP_ERR) { | ||
| 4043 | /* invalid rid or offset */ | ||
| 4044 | airo_print_err(ai->dev->name, "BAP error %x %d", | ||
| 4045 | status, whichbap); | ||
| 4046 | return ERROR; | ||
| 4047 | } else if (status & BAP_DONE) { // success | ||
| 4048 | return SUCCESS; | ||
| 4049 | } | ||
| 4050 | if (!(max_tries--)) { | ||
| 4051 | airo_print_err(ai->dev->name, | ||
| 4052 | "BAP setup error too many retries\n"); | ||
| 4053 | return ERROR; | ||
| 4054 | } | ||
| 4055 | // -- PC4500 missed it, try again | ||
| 4056 | OUT4500(ai, SELECT0+whichbap, rid); | ||
| 4057 | OUT4500(ai, OFFSET0+whichbap, offset); | ||
| 4058 | timeout = 50; | ||
| 4059 | } | ||
| 4060 | } | ||
| 4061 | |||
| 4062 | /* should only be called by aux_bap_read. This aux function and the | ||
| 4063 | following use concepts not documented in the developers guide. I | ||
| 4064 | got them from a patch given to my by Aironet */ | ||
| 4065 | static u16 aux_setup(struct airo_info *ai, u16 page, | ||
| 4066 | u16 offset, u16 *len) | ||
| 4067 | { | ||
| 4068 | u16 next; | ||
| 4069 | |||
| 4070 | OUT4500(ai, AUXPAGE, page); | ||
| 4071 | OUT4500(ai, AUXOFF, 0); | ||
| 4072 | next = IN4500(ai, AUXDATA); | ||
| 4073 | *len = IN4500(ai, AUXDATA)&0xff; | ||
| 4074 | if (offset != 4) OUT4500(ai, AUXOFF, offset); | ||
| 4075 | return next; | ||
| 4076 | } | ||
| 4077 | |||
| 4078 | /* requires call to bap_setup() first */ | ||
| 4079 | static int aux_bap_read(struct airo_info *ai, __le16 *pu16Dst, | ||
| 4080 | int bytelen, int whichbap) | ||
| 4081 | { | ||
| 4082 | u16 len; | ||
| 4083 | u16 page; | ||
| 4084 | u16 offset; | ||
| 4085 | u16 next; | ||
| 4086 | int words; | ||
| 4087 | int i; | ||
| 4088 | unsigned long flags; | ||
| 4089 | |||
| 4090 | spin_lock_irqsave(&ai->aux_lock, flags); | ||
| 4091 | page = IN4500(ai, SWS0+whichbap); | ||
| 4092 | offset = IN4500(ai, SWS2+whichbap); | ||
| 4093 | next = aux_setup(ai, page, offset, &len); | ||
| 4094 | words = (bytelen+1)>>1; | ||
| 4095 | |||
| 4096 | for (i = 0; i<words;) { | ||
| 4097 | int count; | ||
| 4098 | count = (len>>1) < (words-i) ? (len>>1) : (words-i); | ||
| 4099 | if (!do8bitIO) | ||
| 4100 | insw(ai->dev->base_addr+DATA0+whichbap, | ||
| 4101 | pu16Dst+i, count); | ||
| 4102 | else | ||
| 4103 | insb(ai->dev->base_addr+DATA0+whichbap, | ||
| 4104 | pu16Dst+i, count << 1); | ||
| 4105 | i += count; | ||
| 4106 | if (i<words) { | ||
| 4107 | next = aux_setup(ai, next, 4, &len); | ||
| 4108 | } | ||
| 4109 | } | ||
| 4110 | spin_unlock_irqrestore(&ai->aux_lock, flags); | ||
| 4111 | return SUCCESS; | ||
| 4112 | } | ||
| 4113 | |||
| 4114 | |||
| 4115 | /* requires call to bap_setup() first */ | ||
| 4116 | static int fast_bap_read(struct airo_info *ai, __le16 *pu16Dst, | ||
| 4117 | int bytelen, int whichbap) | ||
| 4118 | { | ||
| 4119 | bytelen = (bytelen + 1) & (~1); // round up to even value | ||
| 4120 | if (!do8bitIO) | ||
| 4121 | insw(ai->dev->base_addr+DATA0+whichbap, pu16Dst, bytelen>>1); | ||
| 4122 | else | ||
| 4123 | insb(ai->dev->base_addr+DATA0+whichbap, pu16Dst, bytelen); | ||
| 4124 | return SUCCESS; | ||
| 4125 | } | ||
| 4126 | |||
| 4127 | /* requires call to bap_setup() first */ | ||
| 4128 | static int bap_write(struct airo_info *ai, const __le16 *pu16Src, | ||
| 4129 | int bytelen, int whichbap) | ||
| 4130 | { | ||
| 4131 | bytelen = (bytelen + 1) & (~1); // round up to even value | ||
| 4132 | if (!do8bitIO) | ||
| 4133 | outsw(ai->dev->base_addr+DATA0+whichbap, | ||
| 4134 | pu16Src, bytelen>>1); | ||
| 4135 | else | ||
| 4136 | outsb(ai->dev->base_addr+DATA0+whichbap, pu16Src, bytelen); | ||
| 4137 | return SUCCESS; | ||
| 4138 | } | ||
| 4139 | |||
| 4140 | static int PC4500_accessrid(struct airo_info *ai, u16 rid, u16 accmd) | ||
| 4141 | { | ||
| 4142 | Cmd cmd; /* for issuing commands */ | ||
| 4143 | Resp rsp; /* response from commands */ | ||
| 4144 | u16 status; | ||
| 4145 | |||
| 4146 | memset(&cmd, 0, sizeof(cmd)); | ||
| 4147 | cmd.cmd = accmd; | ||
| 4148 | cmd.parm0 = rid; | ||
| 4149 | status = issuecommand(ai, &cmd, &rsp, true); | ||
| 4150 | if (status != 0) return status; | ||
| 4151 | if ((rsp.status & 0x7F00) != 0) { | ||
| 4152 | return (accmd << 8) + (rsp.rsp0 & 0xFF); | ||
| 4153 | } | ||
| 4154 | return 0; | ||
| 4155 | } | ||
| 4156 | |||
| 4157 | /* Note, that we are using BAP1 which is also used by transmit, so | ||
| 4158 | * we must get a lock. */ | ||
| 4159 | static int PC4500_readrid(struct airo_info *ai, u16 rid, void *pBuf, int len, int lock) | ||
| 4160 | { | ||
| 4161 | u16 status; | ||
| 4162 | int rc = SUCCESS; | ||
| 4163 | |||
| 4164 | if (lock) { | ||
| 4165 | if (down_interruptible(&ai->sem)) | ||
| 4166 | return ERROR; | ||
| 4167 | } | ||
| 4168 | if (test_bit(FLAG_MPI,&ai->flags)) { | ||
| 4169 | Cmd cmd; | ||
| 4170 | Resp rsp; | ||
| 4171 | |||
| 4172 | memset(&cmd, 0, sizeof(cmd)); | ||
| 4173 | memset(&rsp, 0, sizeof(rsp)); | ||
| 4174 | ai->config_desc.rid_desc.valid = 1; | ||
| 4175 | ai->config_desc.rid_desc.len = RIDSIZE; | ||
| 4176 | ai->config_desc.rid_desc.rid = 0; | ||
| 4177 | ai->config_desc.rid_desc.host_addr = ai->ridbus; | ||
| 4178 | |||
| 4179 | cmd.cmd = CMD_ACCESS; | ||
| 4180 | cmd.parm0 = rid; | ||
| 4181 | |||
| 4182 | memcpy_toio(ai->config_desc.card_ram_off, | ||
| 4183 | &ai->config_desc.rid_desc, sizeof(Rid)); | ||
| 4184 | |||
| 4185 | rc = issuecommand(ai, &cmd, &rsp, true); | ||
| 4186 | |||
| 4187 | if (rsp.status & 0x7f00) | ||
| 4188 | rc = rsp.rsp0; | ||
| 4189 | if (!rc) | ||
| 4190 | memcpy(pBuf, ai->config_desc.virtual_host_addr, len); | ||
| 4191 | goto done; | ||
| 4192 | } else { | ||
| 4193 | if ((status = PC4500_accessrid(ai, rid, CMD_ACCESS))!=SUCCESS) { | ||
| 4194 | rc = status; | ||
| 4195 | goto done; | ||
| 4196 | } | ||
| 4197 | if (bap_setup(ai, rid, 0, BAP1) != SUCCESS) { | ||
| 4198 | rc = ERROR; | ||
| 4199 | goto done; | ||
| 4200 | } | ||
| 4201 | // read the rid length field | ||
| 4202 | bap_read(ai, pBuf, 2, BAP1); | ||
| 4203 | // length for remaining part of rid | ||
| 4204 | len = min(len, (int)le16_to_cpu(*(__le16*)pBuf)) - 2; | ||
| 4205 | |||
| 4206 | if (len <= 2) { | ||
| 4207 | airo_print_err(ai->dev->name, | ||
| 4208 | "Rid %x has a length of %d which is too short", | ||
| 4209 | (int)rid, (int)len); | ||
| 4210 | rc = ERROR; | ||
| 4211 | goto done; | ||
| 4212 | } | ||
| 4213 | // read remainder of the rid | ||
| 4214 | rc = bap_read(ai, ((__le16*)pBuf)+1, len, BAP1); | ||
| 4215 | } | ||
| 4216 | done: | ||
| 4217 | if (lock) | ||
| 4218 | up(&ai->sem); | ||
| 4219 | return rc; | ||
| 4220 | } | ||
| 4221 | |||
| 4222 | /* Note, that we are using BAP1 which is also used by transmit, so | ||
| 4223 | * make sure this isn't called when a transmit is happening */ | ||
| 4224 | static int PC4500_writerid(struct airo_info *ai, u16 rid, | ||
| 4225 | const void *pBuf, int len, int lock) | ||
| 4226 | { | ||
| 4227 | u16 status; | ||
| 4228 | int rc = SUCCESS; | ||
| 4229 | |||
| 4230 | *(__le16*)pBuf = cpu_to_le16((u16)len); | ||
| 4231 | |||
| 4232 | if (lock) { | ||
| 4233 | if (down_interruptible(&ai->sem)) | ||
| 4234 | return ERROR; | ||
| 4235 | } | ||
| 4236 | if (test_bit(FLAG_MPI,&ai->flags)) { | ||
| 4237 | Cmd cmd; | ||
| 4238 | Resp rsp; | ||
| 4239 | |||
| 4240 | if (test_bit(FLAG_ENABLED, &ai->flags) && (RID_WEP_TEMP != rid)) | ||
| 4241 | airo_print_err(ai->dev->name, | ||
| 4242 | "%s: MAC should be disabled (rid=%04x)", | ||
| 4243 | __func__, rid); | ||
| 4244 | memset(&cmd, 0, sizeof(cmd)); | ||
| 4245 | memset(&rsp, 0, sizeof(rsp)); | ||
| 4246 | |||
| 4247 | ai->config_desc.rid_desc.valid = 1; | ||
| 4248 | ai->config_desc.rid_desc.len = *((u16 *)pBuf); | ||
| 4249 | ai->config_desc.rid_desc.rid = 0; | ||
| 4250 | |||
| 4251 | cmd.cmd = CMD_WRITERID; | ||
| 4252 | cmd.parm0 = rid; | ||
| 4253 | |||
| 4254 | memcpy_toio(ai->config_desc.card_ram_off, | ||
| 4255 | &ai->config_desc.rid_desc, sizeof(Rid)); | ||
| 4256 | |||
| 4257 | if (len < 4 || len > 2047) { | ||
| 4258 | airo_print_err(ai->dev->name, "%s: len=%d", __func__, len); | ||
| 4259 | rc = -1; | ||
| 4260 | } else { | ||
| 4261 | memcpy(ai->config_desc.virtual_host_addr, | ||
| 4262 | pBuf, len); | ||
| 4263 | |||
| 4264 | rc = issuecommand(ai, &cmd, &rsp, true); | ||
| 4265 | if ((rc & 0xff00) != 0) { | ||
| 4266 | airo_print_err(ai->dev->name, "%s: Write rid Error %d", | ||
| 4267 | __func__, rc); | ||
| 4268 | airo_print_err(ai->dev->name, "%s: Cmd=%04x", | ||
| 4269 | __func__, cmd.cmd); | ||
| 4270 | } | ||
| 4271 | |||
| 4272 | if ((rsp.status & 0x7f00)) | ||
| 4273 | rc = rsp.rsp0; | ||
| 4274 | } | ||
| 4275 | } else { | ||
| 4276 | // --- first access so that we can write the rid data | ||
| 4277 | if ((status = PC4500_accessrid(ai, rid, CMD_ACCESS)) != 0) { | ||
| 4278 | rc = status; | ||
| 4279 | goto done; | ||
| 4280 | } | ||
| 4281 | // --- now write the rid data | ||
| 4282 | if (bap_setup(ai, rid, 0, BAP1) != SUCCESS) { | ||
| 4283 | rc = ERROR; | ||
| 4284 | goto done; | ||
| 4285 | } | ||
| 4286 | bap_write(ai, pBuf, len, BAP1); | ||
| 4287 | // ---now commit the rid data | ||
| 4288 | rc = PC4500_accessrid(ai, rid, 0x100|CMD_ACCESS); | ||
| 4289 | } | ||
| 4290 | done: | ||
| 4291 | if (lock) | ||
| 4292 | up(&ai->sem); | ||
| 4293 | return rc; | ||
| 4294 | } | ||
| 4295 | |||
| 4296 | /* Allocates a FID to be used for transmitting packets. We only use | ||
| 4297 | one for now. */ | ||
| 4298 | static u16 transmit_allocate(struct airo_info *ai, int lenPayload, int raw) | ||
| 4299 | { | ||
| 4300 | unsigned int loop = 3000; | ||
| 4301 | Cmd cmd; | ||
| 4302 | Resp rsp; | ||
| 4303 | u16 txFid; | ||
| 4304 | __le16 txControl; | ||
| 4305 | |||
| 4306 | cmd.cmd = CMD_ALLOCATETX; | ||
| 4307 | cmd.parm0 = lenPayload; | ||
| 4308 | if (down_interruptible(&ai->sem)) | ||
| 4309 | return ERROR; | ||
| 4310 | if (issuecommand(ai, &cmd, &rsp, true) != SUCCESS) { | ||
| 4311 | txFid = ERROR; | ||
| 4312 | goto done; | ||
| 4313 | } | ||
| 4314 | if ((rsp.status & 0xFF00) != 0) { | ||
| 4315 | txFid = ERROR; | ||
| 4316 | goto done; | ||
| 4317 | } | ||
| 4318 | /* wait for the allocate event/indication | ||
| 4319 | * It makes me kind of nervous that this can just sit here and spin, | ||
| 4320 | * but in practice it only loops like four times. */ | ||
| 4321 | while (((IN4500(ai, EVSTAT) & EV_ALLOC) == 0) && --loop); | ||
| 4322 | if (!loop) { | ||
| 4323 | txFid = ERROR; | ||
| 4324 | goto done; | ||
| 4325 | } | ||
| 4326 | |||
| 4327 | // get the allocated fid and acknowledge | ||
| 4328 | txFid = IN4500(ai, TXALLOCFID); | ||
| 4329 | OUT4500(ai, EVACK, EV_ALLOC); | ||
| 4330 | |||
| 4331 | /* The CARD is pretty cool since it converts the ethernet packet | ||
| 4332 | * into 802.11. Also note that we don't release the FID since we | ||
| 4333 | * will be using the same one over and over again. */ | ||
| 4334 | /* We only have to setup the control once since we are not | ||
| 4335 | * releasing the fid. */ | ||
| 4336 | if (raw) | ||
| 4337 | txControl = cpu_to_le16(TXCTL_TXOK | TXCTL_TXEX | TXCTL_802_11 | ||
| 4338 | | TXCTL_ETHERNET | TXCTL_NORELEASE); | ||
| 4339 | else | ||
| 4340 | txControl = cpu_to_le16(TXCTL_TXOK | TXCTL_TXEX | TXCTL_802_3 | ||
| 4341 | | TXCTL_ETHERNET | TXCTL_NORELEASE); | ||
| 4342 | if (bap_setup(ai, txFid, 0x0008, BAP1) != SUCCESS) | ||
| 4343 | txFid = ERROR; | ||
| 4344 | else | ||
| 4345 | bap_write(ai, &txControl, sizeof(txControl), BAP1); | ||
| 4346 | |||
| 4347 | done: | ||
| 4348 | up(&ai->sem); | ||
| 4349 | |||
| 4350 | return txFid; | ||
| 4351 | } | ||
| 4352 | |||
| 4353 | /* In general BAP1 is dedicated to transmiting packets. However, | ||
| 4354 | since we need a BAP when accessing RIDs, we also use BAP1 for that. | ||
| 4355 | Make sure the BAP1 spinlock is held when this is called. */ | ||
| 4356 | static int transmit_802_3_packet(struct airo_info *ai, int len, char *pPacket, | ||
| 4357 | bool may_sleep) | ||
| 4358 | { | ||
| 4359 | __le16 payloadLen; | ||
| 4360 | Cmd cmd; | ||
| 4361 | Resp rsp; | ||
| 4362 | int miclen = 0; | ||
| 4363 | u16 txFid = len; | ||
| 4364 | MICBuffer pMic; | ||
| 4365 | |||
| 4366 | len >>= 16; | ||
| 4367 | |||
| 4368 | if (len <= ETH_ALEN * 2) { | ||
| 4369 | airo_print_warn(ai->dev->name, "Short packet %d", len); | ||
| 4370 | return ERROR; | ||
| 4371 | } | ||
| 4372 | len -= ETH_ALEN * 2; | ||
| 4373 | |||
| 4374 | if (test_bit(FLAG_MIC_CAPABLE, &ai->flags) && ai->micstats.enabled && | ||
| 4375 | (ntohs(((__be16 *)pPacket)[6]) != 0x888E)) { | ||
| 4376 | if (encapsulate(ai, (etherHead *)pPacket,&pMic, len) != SUCCESS) | ||
| 4377 | return ERROR; | ||
| 4378 | miclen = sizeof(pMic); | ||
| 4379 | } | ||
| 4380 | // packet is destination[6], source[6], payload[len-12] | ||
| 4381 | // write the payload length and dst/src/payload | ||
| 4382 | if (bap_setup(ai, txFid, 0x0036, BAP1) != SUCCESS) return ERROR; | ||
| 4383 | /* The hardware addresses aren't counted as part of the payload, so | ||
| 4384 | * we have to subtract the 12 bytes for the addresses off */ | ||
| 4385 | payloadLen = cpu_to_le16(len + miclen); | ||
| 4386 | bap_write(ai, &payloadLen, sizeof(payloadLen), BAP1); | ||
| 4387 | bap_write(ai, (__le16*)pPacket, sizeof(etherHead), BAP1); | ||
| 4388 | if (miclen) | ||
| 4389 | bap_write(ai, (__le16*)&pMic, miclen, BAP1); | ||
| 4390 | bap_write(ai, (__le16*)(pPacket + sizeof(etherHead)), len, BAP1); | ||
| 4391 | // issue the transmit command | ||
| 4392 | memset(&cmd, 0, sizeof(cmd)); | ||
| 4393 | cmd.cmd = CMD_TRANSMIT; | ||
| 4394 | cmd.parm0 = txFid; | ||
| 4395 | if (issuecommand(ai, &cmd, &rsp, may_sleep) != SUCCESS) | ||
| 4396 | return ERROR; | ||
| 4397 | if ((rsp.status & 0xFF00) != 0) return ERROR; | ||
| 4398 | return SUCCESS; | ||
| 4399 | } | ||
| 4400 | |||
| 4401 | static int transmit_802_11_packet(struct airo_info *ai, int len, char *pPacket, | ||
| 4402 | bool may_sleep) | ||
| 4403 | { | ||
| 4404 | __le16 fc, payloadLen; | ||
| 4405 | Cmd cmd; | ||
| 4406 | Resp rsp; | ||
| 4407 | int hdrlen; | ||
| 4408 | static u8 tail[(30-10) + 2 + 6] = {[30-10] = 6}; | ||
| 4409 | /* padding of header to full size + le16 gaplen (6) + gaplen bytes */ | ||
| 4410 | u16 txFid = len; | ||
| 4411 | len >>= 16; | ||
| 4412 | |||
| 4413 | fc = *(__le16*)pPacket; | ||
| 4414 | hdrlen = header_len(fc); | ||
| 4415 | |||
| 4416 | if (len < hdrlen) { | ||
| 4417 | airo_print_warn(ai->dev->name, "Short packet %d", len); | ||
| 4418 | return ERROR; | ||
| 4419 | } | ||
| 4420 | |||
| 4421 | /* packet is 802.11 header + payload | ||
| 4422 | * write the payload length and dst/src/payload */ | ||
| 4423 | if (bap_setup(ai, txFid, 6, BAP1) != SUCCESS) return ERROR; | ||
| 4424 | /* The 802.11 header aren't counted as part of the payload, so | ||
| 4425 | * we have to subtract the header bytes off */ | ||
| 4426 | payloadLen = cpu_to_le16(len-hdrlen); | ||
| 4427 | bap_write(ai, &payloadLen, sizeof(payloadLen), BAP1); | ||
| 4428 | if (bap_setup(ai, txFid, 0x0014, BAP1) != SUCCESS) return ERROR; | ||
| 4429 | bap_write(ai, (__le16 *)pPacket, hdrlen, BAP1); | ||
| 4430 | bap_write(ai, (__le16 *)(tail + (hdrlen - 10)), 38 - hdrlen, BAP1); | ||
| 4431 | |||
| 4432 | bap_write(ai, (__le16 *)(pPacket + hdrlen), len - hdrlen, BAP1); | ||
| 4433 | // issue the transmit command | ||
| 4434 | memset(&cmd, 0, sizeof(cmd)); | ||
| 4435 | cmd.cmd = CMD_TRANSMIT; | ||
| 4436 | cmd.parm0 = txFid; | ||
| 4437 | if (issuecommand(ai, &cmd, &rsp, may_sleep) != SUCCESS) | ||
| 4438 | return ERROR; | ||
| 4439 | if ((rsp.status & 0xFF00) != 0) return ERROR; | ||
| 4440 | return SUCCESS; | ||
| 4441 | } | ||
| 4442 | |||
| 4443 | /* | ||
| 4444 | * This is the proc_fs routines. It is a bit messier than I would | ||
| 4445 | * like! Feel free to clean it up! | ||
| 4446 | */ | ||
| 4447 | |||
| 4448 | static ssize_t proc_read(struct file *file, | ||
| 4449 | char __user *buffer, | ||
| 4450 | size_t len, | ||
| 4451 | loff_t *offset); | ||
| 4452 | |||
| 4453 | static ssize_t proc_write(struct file *file, | ||
| 4454 | const char __user *buffer, | ||
| 4455 | size_t len, | ||
| 4456 | loff_t *offset); | ||
| 4457 | static int proc_close(struct inode *inode, struct file *file); | ||
| 4458 | |||
| 4459 | static int proc_stats_open(struct inode *inode, struct file *file); | ||
| 4460 | static int proc_statsdelta_open(struct inode *inode, struct file *file); | ||
| 4461 | static int proc_status_open(struct inode *inode, struct file *file); | ||
| 4462 | static int proc_SSID_open(struct inode *inode, struct file *file); | ||
| 4463 | static int proc_APList_open(struct inode *inode, struct file *file); | ||
| 4464 | static int proc_BSSList_open(struct inode *inode, struct file *file); | ||
| 4465 | static int proc_config_open(struct inode *inode, struct file *file); | ||
| 4466 | static int proc_wepkey_open(struct inode *inode, struct file *file); | ||
| 4467 | |||
| 4468 | static const struct proc_ops proc_statsdelta_ops = { | ||
| 4469 | .proc_read = proc_read, | ||
| 4470 | .proc_open = proc_statsdelta_open, | ||
| 4471 | .proc_release = proc_close, | ||
| 4472 | .proc_lseek = default_llseek, | ||
| 4473 | }; | ||
| 4474 | |||
| 4475 | static const struct proc_ops proc_stats_ops = { | ||
| 4476 | .proc_read = proc_read, | ||
| 4477 | .proc_open = proc_stats_open, | ||
| 4478 | .proc_release = proc_close, | ||
| 4479 | .proc_lseek = default_llseek, | ||
| 4480 | }; | ||
| 4481 | |||
| 4482 | static const struct proc_ops proc_status_ops = { | ||
| 4483 | .proc_read = proc_read, | ||
| 4484 | .proc_open = proc_status_open, | ||
| 4485 | .proc_release = proc_close, | ||
| 4486 | .proc_lseek = default_llseek, | ||
| 4487 | }; | ||
| 4488 | |||
| 4489 | static const struct proc_ops proc_SSID_ops = { | ||
| 4490 | .proc_read = proc_read, | ||
| 4491 | .proc_write = proc_write, | ||
| 4492 | .proc_open = proc_SSID_open, | ||
| 4493 | .proc_release = proc_close, | ||
| 4494 | .proc_lseek = default_llseek, | ||
| 4495 | }; | ||
| 4496 | |||
| 4497 | static const struct proc_ops proc_BSSList_ops = { | ||
| 4498 | .proc_read = proc_read, | ||
| 4499 | .proc_write = proc_write, | ||
| 4500 | .proc_open = proc_BSSList_open, | ||
| 4501 | .proc_release = proc_close, | ||
| 4502 | .proc_lseek = default_llseek, | ||
| 4503 | }; | ||
| 4504 | |||
| 4505 | static const struct proc_ops proc_APList_ops = { | ||
| 4506 | .proc_read = proc_read, | ||
| 4507 | .proc_write = proc_write, | ||
| 4508 | .proc_open = proc_APList_open, | ||
| 4509 | .proc_release = proc_close, | ||
| 4510 | .proc_lseek = default_llseek, | ||
| 4511 | }; | ||
| 4512 | |||
| 4513 | static const struct proc_ops proc_config_ops = { | ||
| 4514 | .proc_read = proc_read, | ||
| 4515 | .proc_write = proc_write, | ||
| 4516 | .proc_open = proc_config_open, | ||
| 4517 | .proc_release = proc_close, | ||
| 4518 | .proc_lseek = default_llseek, | ||
| 4519 | }; | ||
| 4520 | |||
| 4521 | static const struct proc_ops proc_wepkey_ops = { | ||
| 4522 | .proc_read = proc_read, | ||
| 4523 | .proc_write = proc_write, | ||
| 4524 | .proc_open = proc_wepkey_open, | ||
| 4525 | .proc_release = proc_close, | ||
| 4526 | .proc_lseek = default_llseek, | ||
| 4527 | }; | ||
| 4528 | |||
| 4529 | static struct proc_dir_entry *airo_entry; | ||
| 4530 | |||
| 4531 | struct proc_data { | ||
| 4532 | int release_buffer; | ||
| 4533 | int readlen; | ||
| 4534 | char *rbuffer; | ||
| 4535 | int writelen; | ||
| 4536 | int maxwritelen; | ||
| 4537 | char *wbuffer; | ||
| 4538 | void (*on_close) (struct inode *, struct file *); | ||
| 4539 | }; | ||
| 4540 | |||
| 4541 | static int setup_proc_entry(struct net_device *dev, | ||
| 4542 | struct airo_info *apriv) | ||
| 4543 | { | ||
| 4544 | struct proc_dir_entry *entry; | ||
| 4545 | |||
| 4546 | /* First setup the device directory */ | ||
| 4547 | strcpy(apriv->proc_name, dev->name); | ||
| 4548 | apriv->proc_entry = proc_mkdir_mode(apriv->proc_name, airo_perm, | ||
| 4549 | airo_entry); | ||
| 4550 | if (!apriv->proc_entry) | ||
| 4551 | return -ENOMEM; | ||
| 4552 | proc_set_user(apriv->proc_entry, proc_kuid, proc_kgid); | ||
| 4553 | |||
| 4554 | /* Setup the StatsDelta */ | ||
| 4555 | entry = proc_create_data("StatsDelta", 0444 & proc_perm, | ||
| 4556 | apriv->proc_entry, &proc_statsdelta_ops, dev); | ||
| 4557 | if (!entry) | ||
| 4558 | goto fail; | ||
| 4559 | proc_set_user(entry, proc_kuid, proc_kgid); | ||
| 4560 | |||
| 4561 | /* Setup the Stats */ | ||
| 4562 | entry = proc_create_data("Stats", 0444 & proc_perm, | ||
| 4563 | apriv->proc_entry, &proc_stats_ops, dev); | ||
| 4564 | if (!entry) | ||
| 4565 | goto fail; | ||
| 4566 | proc_set_user(entry, proc_kuid, proc_kgid); | ||
| 4567 | |||
| 4568 | /* Setup the Status */ | ||
| 4569 | entry = proc_create_data("Status", 0444 & proc_perm, | ||
| 4570 | apriv->proc_entry, &proc_status_ops, dev); | ||
| 4571 | if (!entry) | ||
| 4572 | goto fail; | ||
| 4573 | proc_set_user(entry, proc_kuid, proc_kgid); | ||
| 4574 | |||
| 4575 | /* Setup the Config */ | ||
| 4576 | entry = proc_create_data("Config", proc_perm, | ||
| 4577 | apriv->proc_entry, &proc_config_ops, dev); | ||
| 4578 | if (!entry) | ||
| 4579 | goto fail; | ||
| 4580 | proc_set_user(entry, proc_kuid, proc_kgid); | ||
| 4581 | |||
| 4582 | /* Setup the SSID */ | ||
| 4583 | entry = proc_create_data("SSID", proc_perm, | ||
| 4584 | apriv->proc_entry, &proc_SSID_ops, dev); | ||
| 4585 | if (!entry) | ||
| 4586 | goto fail; | ||
| 4587 | proc_set_user(entry, proc_kuid, proc_kgid); | ||
| 4588 | |||
| 4589 | /* Setup the APList */ | ||
| 4590 | entry = proc_create_data("APList", proc_perm, | ||
| 4591 | apriv->proc_entry, &proc_APList_ops, dev); | ||
| 4592 | if (!entry) | ||
| 4593 | goto fail; | ||
| 4594 | proc_set_user(entry, proc_kuid, proc_kgid); | ||
| 4595 | |||
| 4596 | /* Setup the BSSList */ | ||
| 4597 | entry = proc_create_data("BSSList", proc_perm, | ||
| 4598 | apriv->proc_entry, &proc_BSSList_ops, dev); | ||
| 4599 | if (!entry) | ||
| 4600 | goto fail; | ||
| 4601 | proc_set_user(entry, proc_kuid, proc_kgid); | ||
| 4602 | |||
| 4603 | /* Setup the WepKey */ | ||
| 4604 | entry = proc_create_data("WepKey", proc_perm, | ||
| 4605 | apriv->proc_entry, &proc_wepkey_ops, dev); | ||
| 4606 | if (!entry) | ||
| 4607 | goto fail; | ||
| 4608 | proc_set_user(entry, proc_kuid, proc_kgid); | ||
| 4609 | return 0; | ||
| 4610 | |||
| 4611 | fail: | ||
| 4612 | remove_proc_subtree(apriv->proc_name, airo_entry); | ||
| 4613 | return -ENOMEM; | ||
| 4614 | } | ||
| 4615 | |||
| 4616 | static int takedown_proc_entry(struct net_device *dev, | ||
| 4617 | struct airo_info *apriv) | ||
| 4618 | { | ||
| 4619 | remove_proc_subtree(apriv->proc_name, airo_entry); | ||
| 4620 | return 0; | ||
| 4621 | } | ||
| 4622 | |||
| 4623 | /* | ||
| 4624 | * What we want from the proc_fs is to be able to efficiently read | ||
| 4625 | * and write the configuration. To do this, we want to read the | ||
| 4626 | * configuration when the file is opened and write it when the file is | ||
| 4627 | * closed. So basically we allocate a read buffer at open and fill it | ||
| 4628 | * with data, and allocate a write buffer and read it at close. | ||
| 4629 | */ | ||
| 4630 | |||
| 4631 | /* | ||
| 4632 | * The read routine is generic, it relies on the preallocated rbuffer | ||
| 4633 | * to supply the data. | ||
| 4634 | */ | ||
| 4635 | static ssize_t proc_read(struct file *file, | ||
| 4636 | char __user *buffer, | ||
| 4637 | size_t len, | ||
| 4638 | loff_t *offset) | ||
| 4639 | { | ||
| 4640 | struct proc_data *priv = file->private_data; | ||
| 4641 | |||
| 4642 | if (!priv->rbuffer) | ||
| 4643 | return -EINVAL; | ||
| 4644 | |||
| 4645 | return simple_read_from_buffer(buffer, len, offset, priv->rbuffer, | ||
| 4646 | priv->readlen); | ||
| 4647 | } | ||
| 4648 | |||
| 4649 | /* | ||
| 4650 | * The write routine is generic, it fills in a preallocated rbuffer | ||
| 4651 | * to supply the data. | ||
| 4652 | */ | ||
| 4653 | static ssize_t proc_write(struct file *file, | ||
| 4654 | const char __user *buffer, | ||
| 4655 | size_t len, | ||
| 4656 | loff_t *offset) | ||
| 4657 | { | ||
| 4658 | ssize_t ret; | ||
| 4659 | struct proc_data *priv = file->private_data; | ||
| 4660 | |||
| 4661 | if (!priv->wbuffer) | ||
| 4662 | return -EINVAL; | ||
| 4663 | |||
| 4664 | ret = simple_write_to_buffer(priv->wbuffer, priv->maxwritelen, offset, | ||
| 4665 | buffer, len); | ||
| 4666 | if (ret > 0) | ||
| 4667 | priv->writelen = max_t(int, priv->writelen, *offset); | ||
| 4668 | |||
| 4669 | return ret; | ||
| 4670 | } | ||
| 4671 | |||
| 4672 | static int proc_status_open(struct inode *inode, struct file *file) | ||
| 4673 | { | ||
| 4674 | struct proc_data *data; | ||
| 4675 | struct net_device *dev = pde_data(inode); | ||
| 4676 | struct airo_info *apriv = dev->ml_priv; | ||
| 4677 | CapabilityRid cap_rid; | ||
| 4678 | StatusRid status_rid; | ||
| 4679 | u16 mode; | ||
| 4680 | int i; | ||
| 4681 | |||
| 4682 | if ((file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL)) == NULL) | ||
| 4683 | return -ENOMEM; | ||
| 4684 | data = file->private_data; | ||
| 4685 | if ((data->rbuffer = kmalloc(2048, GFP_KERNEL)) == NULL) { | ||
| 4686 | kfree (file->private_data); | ||
| 4687 | return -ENOMEM; | ||
| 4688 | } | ||
| 4689 | |||
| 4690 | readStatusRid(apriv, &status_rid, 1); | ||
| 4691 | readCapabilityRid(apriv, &cap_rid, 1); | ||
| 4692 | |||
| 4693 | mode = le16_to_cpu(status_rid.mode); | ||
| 4694 | |||
| 4695 | i = sprintf(data->rbuffer, "Status: %s%s%s%s%s%s%s%s%s\n", | ||
| 4696 | mode & 1 ? "CFG ": "", | ||
| 4697 | mode & 2 ? "ACT ": "", | ||
| 4698 | mode & 0x10 ? "SYN ": "", | ||
| 4699 | mode & 0x20 ? "LNK ": "", | ||
| 4700 | mode & 0x40 ? "LEAP ": "", | ||
| 4701 | mode & 0x80 ? "PRIV ": "", | ||
| 4702 | mode & 0x100 ? "KEY ": "", | ||
| 4703 | mode & 0x200 ? "WEP ": "", | ||
| 4704 | mode & 0x8000 ? "ERR ": ""); | ||
| 4705 | sprintf(data->rbuffer+i, "Mode: %x\n" | ||
| 4706 | "Signal Strength: %d\n" | ||
| 4707 | "Signal Quality: %d\n" | ||
| 4708 | "SSID: %-.*s\n" | ||
| 4709 | "AP: %-.16s\n" | ||
| 4710 | "Freq: %d\n" | ||
| 4711 | "BitRate: %dmbs\n" | ||
| 4712 | "Driver Version: %s\n" | ||
| 4713 | "Device: %s\nManufacturer: %s\nFirmware Version: %s\n" | ||
| 4714 | "Radio type: %x\nCountry: %x\nHardware Version: %x\n" | ||
| 4715 | "Software Version: %x\nSoftware Subversion: %x\n" | ||
| 4716 | "Boot block version: %x\n", | ||
| 4717 | le16_to_cpu(status_rid.mode), | ||
| 4718 | le16_to_cpu(status_rid.normalizedSignalStrength), | ||
| 4719 | le16_to_cpu(status_rid.signalQuality), | ||
| 4720 | le16_to_cpu(status_rid.SSIDlen), | ||
| 4721 | status_rid.SSID, | ||
| 4722 | status_rid.apName, | ||
| 4723 | le16_to_cpu(status_rid.channel), | ||
| 4724 | le16_to_cpu(status_rid.currentXmitRate) / 2, | ||
| 4725 | version, | ||
| 4726 | cap_rid.prodName, | ||
| 4727 | cap_rid.manName, | ||
| 4728 | cap_rid.prodVer, | ||
| 4729 | le16_to_cpu(cap_rid.radioType), | ||
| 4730 | le16_to_cpu(cap_rid.country), | ||
| 4731 | le16_to_cpu(cap_rid.hardVer), | ||
| 4732 | le16_to_cpu(cap_rid.softVer), | ||
| 4733 | le16_to_cpu(cap_rid.softSubVer), | ||
| 4734 | le16_to_cpu(cap_rid.bootBlockVer)); | ||
| 4735 | data->readlen = strlen(data->rbuffer); | ||
| 4736 | return 0; | ||
| 4737 | } | ||
| 4738 | |||
| 4739 | static int proc_stats_rid_open(struct inode*, struct file*, u16); | ||
| 4740 | static int proc_statsdelta_open(struct inode *inode, | ||
| 4741 | struct file *file) | ||
| 4742 | { | ||
| 4743 | if (file->f_mode&FMODE_WRITE) { | ||
| 4744 | return proc_stats_rid_open(inode, file, RID_STATSDELTACLEAR); | ||
| 4745 | } | ||
| 4746 | return proc_stats_rid_open(inode, file, RID_STATSDELTA); | ||
| 4747 | } | ||
| 4748 | |||
| 4749 | static int proc_stats_open(struct inode *inode, struct file *file) | ||
| 4750 | { | ||
| 4751 | return proc_stats_rid_open(inode, file, RID_STATS); | ||
| 4752 | } | ||
| 4753 | |||
| 4754 | static int proc_stats_rid_open(struct inode *inode, | ||
| 4755 | struct file *file, | ||
| 4756 | u16 rid) | ||
| 4757 | { | ||
| 4758 | struct proc_data *data; | ||
| 4759 | struct net_device *dev = pde_data(inode); | ||
| 4760 | struct airo_info *apriv = dev->ml_priv; | ||
| 4761 | StatsRid stats; | ||
| 4762 | int i, j; | ||
| 4763 | __le32 *vals = stats.vals; | ||
| 4764 | int len; | ||
| 4765 | |||
| 4766 | if ((file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL)) == NULL) | ||
| 4767 | return -ENOMEM; | ||
| 4768 | data = file->private_data; | ||
| 4769 | if ((data->rbuffer = kmalloc(4096, GFP_KERNEL)) == NULL) { | ||
| 4770 | kfree (file->private_data); | ||
| 4771 | return -ENOMEM; | ||
| 4772 | } | ||
| 4773 | |||
| 4774 | readStatsRid(apriv, &stats, rid, 1); | ||
| 4775 | len = le16_to_cpu(stats.len); | ||
| 4776 | |||
| 4777 | j = 0; | ||
| 4778 | for (i = 0; statsLabels[i]!=(char *)-1 && i*4<len; i++) { | ||
| 4779 | if (!statsLabels[i]) continue; | ||
| 4780 | if (j+strlen(statsLabels[i])+16>4096) { | ||
| 4781 | airo_print_warn(apriv->dev->name, | ||
| 4782 | "Potentially disastrous buffer overflow averted!"); | ||
| 4783 | break; | ||
| 4784 | } | ||
| 4785 | j+=sprintf(data->rbuffer+j, "%s: %u\n", statsLabels[i], | ||
| 4786 | le32_to_cpu(vals[i])); | ||
| 4787 | } | ||
| 4788 | if (i*4 >= len) { | ||
| 4789 | airo_print_warn(apriv->dev->name, "Got a short rid"); | ||
| 4790 | } | ||
| 4791 | data->readlen = j; | ||
| 4792 | return 0; | ||
| 4793 | } | ||
| 4794 | |||
| 4795 | static int get_dec_u16(char *buffer, int *start, int limit) | ||
| 4796 | { | ||
| 4797 | u16 value; | ||
| 4798 | int valid = 0; | ||
| 4799 | for (value = 0; *start < limit && buffer[*start] >= '0' && | ||
| 4800 | buffer[*start] <= '9'; (*start)++) { | ||
| 4801 | valid = 1; | ||
| 4802 | value *= 10; | ||
| 4803 | value += buffer[*start] - '0'; | ||
| 4804 | } | ||
| 4805 | if (!valid) return -1; | ||
| 4806 | return value; | ||
| 4807 | } | ||
| 4808 | |||
| 4809 | static int airo_config_commit(struct net_device *dev, | ||
| 4810 | struct iw_request_info *info, | ||
| 4811 | union iwreq_data *wrqu, | ||
| 4812 | char *extra); | ||
| 4813 | |||
| 4814 | static inline int sniffing_mode(struct airo_info *ai) | ||
| 4815 | { | ||
| 4816 | return (le16_to_cpu(ai->config.rmode) & le16_to_cpu(RXMODE_MASK)) >= | ||
| 4817 | le16_to_cpu(RXMODE_RFMON); | ||
| 4818 | } | ||
| 4819 | |||
| 4820 | static void proc_config_on_close(struct inode *inode, struct file *file) | ||
| 4821 | { | ||
| 4822 | struct proc_data *data = file->private_data; | ||
| 4823 | struct net_device *dev = pde_data(inode); | ||
| 4824 | struct airo_info *ai = dev->ml_priv; | ||
| 4825 | char *line; | ||
| 4826 | |||
| 4827 | if (!data->writelen) return; | ||
| 4828 | |||
| 4829 | readConfigRid(ai, 1); | ||
| 4830 | set_bit (FLAG_COMMIT, &ai->flags); | ||
| 4831 | |||
| 4832 | line = data->wbuffer; | ||
| 4833 | while (line[0]) { | ||
| 4834 | /*** Mode processing */ | ||
| 4835 | if (!strncmp(line, "Mode: ", 6)) { | ||
| 4836 | line += 6; | ||
| 4837 | if (sniffing_mode(ai)) | ||
| 4838 | set_bit (FLAG_RESET, &ai->flags); | ||
| 4839 | ai->config.rmode &= ~RXMODE_FULL_MASK; | ||
| 4840 | clear_bit (FLAG_802_11, &ai->flags); | ||
| 4841 | ai->config.opmode &= ~MODE_CFG_MASK; | ||
| 4842 | ai->config.scanMode = SCANMODE_ACTIVE; | ||
| 4843 | if (line[0] == 'a') { | ||
| 4844 | ai->config.opmode |= MODE_STA_IBSS; | ||
| 4845 | } else { | ||
| 4846 | ai->config.opmode |= MODE_STA_ESS; | ||
| 4847 | if (line[0] == 'r') { | ||
| 4848 | ai->config.rmode |= RXMODE_RFMON | RXMODE_DISABLE_802_3_HEADER; | ||
| 4849 | ai->config.scanMode = SCANMODE_PASSIVE; | ||
| 4850 | set_bit (FLAG_802_11, &ai->flags); | ||
| 4851 | } else if (line[0] == 'y') { | ||
| 4852 | ai->config.rmode |= RXMODE_RFMON_ANYBSS | RXMODE_DISABLE_802_3_HEADER; | ||
| 4853 | ai->config.scanMode = SCANMODE_PASSIVE; | ||
| 4854 | set_bit (FLAG_802_11, &ai->flags); | ||
| 4855 | } else if (line[0] == 'l') | ||
| 4856 | ai->config.rmode |= RXMODE_LANMON; | ||
| 4857 | } | ||
| 4858 | set_bit (FLAG_COMMIT, &ai->flags); | ||
| 4859 | } | ||
| 4860 | |||
| 4861 | /*** Radio status */ | ||
| 4862 | else if (!strncmp(line,"Radio: ", 7)) { | ||
| 4863 | line += 7; | ||
| 4864 | if (!strncmp(line,"off", 3)) { | ||
| 4865 | set_bit (FLAG_RADIO_OFF, &ai->flags); | ||
| 4866 | } else { | ||
| 4867 | clear_bit (FLAG_RADIO_OFF, &ai->flags); | ||
| 4868 | } | ||
| 4869 | } | ||
| 4870 | /*** NodeName processing */ | ||
| 4871 | else if (!strncmp(line, "NodeName: ", 10)) { | ||
| 4872 | int j; | ||
| 4873 | |||
| 4874 | line += 10; | ||
| 4875 | memset(ai->config.nodeName, 0, 16); | ||
| 4876 | /* Do the name, assume a space between the mode and node name */ | ||
| 4877 | for (j = 0; j < 16 && line[j] != '\n'; j++) { | ||
| 4878 | ai->config.nodeName[j] = line[j]; | ||
| 4879 | } | ||
| 4880 | set_bit (FLAG_COMMIT, &ai->flags); | ||
| 4881 | } | ||
| 4882 | |||
| 4883 | /*** PowerMode processing */ | ||
| 4884 | else if (!strncmp(line, "PowerMode: ", 11)) { | ||
| 4885 | line += 11; | ||
| 4886 | if (!strncmp(line, "PSPCAM", 6)) { | ||
| 4887 | ai->config.powerSaveMode = POWERSAVE_PSPCAM; | ||
| 4888 | set_bit (FLAG_COMMIT, &ai->flags); | ||
| 4889 | } else if (!strncmp(line, "PSP", 3)) { | ||
| 4890 | ai->config.powerSaveMode = POWERSAVE_PSP; | ||
| 4891 | set_bit (FLAG_COMMIT, &ai->flags); | ||
| 4892 | } else { | ||
| 4893 | ai->config.powerSaveMode = POWERSAVE_CAM; | ||
| 4894 | set_bit (FLAG_COMMIT, &ai->flags); | ||
| 4895 | } | ||
| 4896 | } else if (!strncmp(line, "DataRates: ", 11)) { | ||
| 4897 | int v, i = 0, k = 0; /* i is index into line, | ||
| 4898 | k is index to rates */ | ||
| 4899 | |||
| 4900 | line += 11; | ||
| 4901 | while ((v = get_dec_u16(line, &i, 3))!=-1) { | ||
| 4902 | ai->config.rates[k++] = (u8)v; | ||
| 4903 | line += i + 1; | ||
| 4904 | i = 0; | ||
| 4905 | } | ||
| 4906 | set_bit (FLAG_COMMIT, &ai->flags); | ||
| 4907 | } else if (!strncmp(line, "Channel: ", 9)) { | ||
| 4908 | int v, i = 0; | ||
| 4909 | line += 9; | ||
| 4910 | v = get_dec_u16(line, &i, i+3); | ||
| 4911 | if (v != -1) { | ||
| 4912 | ai->config.channelSet = cpu_to_le16(v); | ||
| 4913 | set_bit (FLAG_COMMIT, &ai->flags); | ||
| 4914 | } | ||
| 4915 | } else if (!strncmp(line, "XmitPower: ", 11)) { | ||
| 4916 | int v, i = 0; | ||
| 4917 | line += 11; | ||
| 4918 | v = get_dec_u16(line, &i, i+3); | ||
| 4919 | if (v != -1) { | ||
| 4920 | ai->config.txPower = cpu_to_le16(v); | ||
| 4921 | set_bit (FLAG_COMMIT, &ai->flags); | ||
| 4922 | } | ||
| 4923 | } else if (!strncmp(line, "WEP: ", 5)) { | ||
| 4924 | line += 5; | ||
| 4925 | switch(line[0]) { | ||
| 4926 | case 's': | ||
| 4927 | set_auth_type(ai, AUTH_SHAREDKEY); | ||
| 4928 | break; | ||
| 4929 | case 'e': | ||
| 4930 | set_auth_type(ai, AUTH_ENCRYPT); | ||
| 4931 | break; | ||
| 4932 | default: | ||
| 4933 | set_auth_type(ai, AUTH_OPEN); | ||
| 4934 | break; | ||
| 4935 | } | ||
| 4936 | set_bit (FLAG_COMMIT, &ai->flags); | ||
| 4937 | } else if (!strncmp(line, "LongRetryLimit: ", 16)) { | ||
| 4938 | int v, i = 0; | ||
| 4939 | |||
| 4940 | line += 16; | ||
| 4941 | v = get_dec_u16(line, &i, 3); | ||
| 4942 | v = (v<0) ? 0 : ((v>255) ? 255 : v); | ||
| 4943 | ai->config.longRetryLimit = cpu_to_le16(v); | ||
| 4944 | set_bit (FLAG_COMMIT, &ai->flags); | ||
| 4945 | } else if (!strncmp(line, "ShortRetryLimit: ", 17)) { | ||
| 4946 | int v, i = 0; | ||
| 4947 | |||
| 4948 | line += 17; | ||
| 4949 | v = get_dec_u16(line, &i, 3); | ||
| 4950 | v = (v<0) ? 0 : ((v>255) ? 255 : v); | ||
| 4951 | ai->config.shortRetryLimit = cpu_to_le16(v); | ||
| 4952 | set_bit (FLAG_COMMIT, &ai->flags); | ||
| 4953 | } else if (!strncmp(line, "RTSThreshold: ", 14)) { | ||
| 4954 | int v, i = 0; | ||
| 4955 | |||
| 4956 | line += 14; | ||
| 4957 | v = get_dec_u16(line, &i, 4); | ||
| 4958 | v = (v<0) ? 0 : ((v>AIRO_DEF_MTU) ? AIRO_DEF_MTU : v); | ||
| 4959 | ai->config.rtsThres = cpu_to_le16(v); | ||
| 4960 | set_bit (FLAG_COMMIT, &ai->flags); | ||
| 4961 | } else if (!strncmp(line, "TXMSDULifetime: ", 16)) { | ||
| 4962 | int v, i = 0; | ||
| 4963 | |||
| 4964 | line += 16; | ||
| 4965 | v = get_dec_u16(line, &i, 5); | ||
| 4966 | v = (v<0) ? 0 : v; | ||
| 4967 | ai->config.txLifetime = cpu_to_le16(v); | ||
| 4968 | set_bit (FLAG_COMMIT, &ai->flags); | ||
| 4969 | } else if (!strncmp(line, "RXMSDULifetime: ", 16)) { | ||
| 4970 | int v, i = 0; | ||
| 4971 | |||
| 4972 | line += 16; | ||
| 4973 | v = get_dec_u16(line, &i, 5); | ||
| 4974 | v = (v<0) ? 0 : v; | ||
| 4975 | ai->config.rxLifetime = cpu_to_le16(v); | ||
| 4976 | set_bit (FLAG_COMMIT, &ai->flags); | ||
| 4977 | } else if (!strncmp(line, "TXDiversity: ", 13)) { | ||
| 4978 | ai->config.txDiversity = | ||
| 4979 | (line[13]=='l') ? 1 : | ||
| 4980 | ((line[13]=='r')? 2: 3); | ||
| 4981 | set_bit (FLAG_COMMIT, &ai->flags); | ||
| 4982 | } else if (!strncmp(line, "RXDiversity: ", 13)) { | ||
| 4983 | ai->config.rxDiversity = | ||
| 4984 | (line[13]=='l') ? 1 : | ||
| 4985 | ((line[13]=='r')? 2: 3); | ||
| 4986 | set_bit (FLAG_COMMIT, &ai->flags); | ||
| 4987 | } else if (!strncmp(line, "FragThreshold: ", 15)) { | ||
| 4988 | int v, i = 0; | ||
| 4989 | |||
| 4990 | line += 15; | ||
| 4991 | v = get_dec_u16(line, &i, 4); | ||
| 4992 | v = (v<256) ? 256 : ((v>AIRO_DEF_MTU) ? AIRO_DEF_MTU : v); | ||
| 4993 | v = v & 0xfffe; /* Make sure its even */ | ||
| 4994 | ai->config.fragThresh = cpu_to_le16(v); | ||
| 4995 | set_bit (FLAG_COMMIT, &ai->flags); | ||
| 4996 | } else if (!strncmp(line, "Modulation: ", 12)) { | ||
| 4997 | line += 12; | ||
| 4998 | switch(*line) { | ||
| 4999 | case 'd': ai->config.modulation = MOD_DEFAULT; set_bit(FLAG_COMMIT, &ai->flags); break; | ||
3289 more lines aren’t shown here.
| @@ -1,10 +0,0 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 2 | #ifndef _AIRO_H_ | ||
| 3 | #define _AIRO_H_ | ||
| 4 | |||
| 5 | struct net_device *init_airo_card(unsigned short irq, int port, int is_pcmcia, | ||
| 6 | struct device *dmdev); | ||
| 7 | int reset_airo_card(struct net_device *dev); | ||
| 8 | void stop_airo_card(struct net_device *dev, int freeres); | ||
| 9 | |||
| 10 | #endif /* _AIRO_H_ */ | ||
| @@ -1,218 +0,0 @@ | |||
| 1 | /*====================================================================== | ||
| 2 | |||
| 3 | Aironet driver for 4500 and 4800 series cards | ||
| 4 | |||
| 5 | This code is released under both the GPL version 2 and BSD licenses. | ||
| 6 | Either license may be used. The respective licenses are found at | ||
| 7 | the end of this file. | ||
| 8 | |||
| 9 | This code was developed by Benjamin Reed <breed@users.sourceforge.net> | ||
| 10 | including portions of which come from the Aironet PC4500 | ||
| 11 | Developer's Reference Manual and used with permission. Copyright | ||
| 12 | (C) 1999 Benjamin Reed. All Rights Reserved. Permission to use | ||
| 13 | code in the Developer's manual was granted for this driver by | ||
| 14 | Aironet. | ||
| 15 | |||
| 16 | In addition this module was derived from dummy_cs. | ||
| 17 | The initial developer of dummy_cs is David A. Hinds | ||
| 18 | <dahinds@users.sourceforge.net>. Portions created by David A. Hinds | ||
| 19 | are Copyright (C) 1999 David A. Hinds. All Rights Reserved. | ||
| 20 | |||
| 21 | ======================================================================*/ | ||
| 22 | |||
| 23 | #ifdef __IN_PCMCIA_PACKAGE__ | ||
| 24 | #include <pcmcia/k_compat.h> | ||
| 25 | #endif | ||
| 26 | #include <linux/kernel.h> | ||
| 27 | #include <linux/module.h> | ||
| 28 | #include <linux/ptrace.h> | ||
| 29 | #include <linux/slab.h> | ||
| 30 | #include <linux/string.h> | ||
| 31 | #include <linux/timer.h> | ||
| 32 | #include <linux/netdevice.h> | ||
| 33 | |||
| 34 | #include <pcmcia/cistpl.h> | ||
| 35 | #include <pcmcia/cisreg.h> | ||
| 36 | #include <pcmcia/ds.h> | ||
| 37 | |||
| 38 | #include <linux/io.h> | ||
| 39 | |||
| 40 | #include "airo.h" | ||
| 41 | |||
| 42 | |||
| 43 | /*====================================================================*/ | ||
| 44 | |||
| 45 | MODULE_AUTHOR("Benjamin Reed"); | ||
| 46 | MODULE_DESCRIPTION("Support for Cisco/Aironet 802.11 wireless ethernet " | ||
| 47 | "cards. This is the module that links the PCMCIA card " | ||
| 48 | "with the airo module."); | ||
| 49 | MODULE_LICENSE("Dual BSD/GPL"); | ||
| 50 | |||
| 51 | /*====================================================================*/ | ||
| 52 | |||
| 53 | static int airo_config(struct pcmcia_device *link); | ||
| 54 | static void airo_release(struct pcmcia_device *link); | ||
| 55 | |||
| 56 | static void airo_detach(struct pcmcia_device *p_dev); | ||
| 57 | |||
| 58 | struct local_info { | ||
| 59 | struct net_device *eth_dev; | ||
| 60 | }; | ||
| 61 | |||
| 62 | static int airo_probe(struct pcmcia_device *p_dev) | ||
| 63 | { | ||
| 64 | struct local_info *local; | ||
| 65 | |||
| 66 | dev_dbg(&p_dev->dev, "airo_attach()\n"); | ||
| 67 | |||
| 68 | /* Allocate space for private device-specific data */ | ||
| 69 | local = kzalloc(sizeof(*local), GFP_KERNEL); | ||
| 70 | if (!local) | ||
| 71 | return -ENOMEM; | ||
| 72 | |||
| 73 | p_dev->priv = local; | ||
| 74 | |||
| 75 | return airo_config(p_dev); | ||
| 76 | } /* airo_attach */ | ||
| 77 | |||
| 78 | static void airo_detach(struct pcmcia_device *link) | ||
| 79 | { | ||
| 80 | dev_dbg(&link->dev, "airo_detach\n"); | ||
| 81 | |||
| 82 | airo_release(link); | ||
| 83 | |||
| 84 | if (((struct local_info *)link->priv)->eth_dev) { | ||
| 85 | stop_airo_card(((struct local_info *)link->priv)->eth_dev, | ||
| 86 | 0); | ||
| 87 | } | ||
| 88 | ((struct local_info *)link->priv)->eth_dev = NULL; | ||
| 89 | |||
| 90 | kfree(link->priv); | ||
| 91 | } /* airo_detach */ | ||
| 92 | |||
| 93 | static int airo_cs_config_check(struct pcmcia_device *p_dev, void *priv_data) | ||
| 94 | { | ||
| 95 | if (p_dev->config_index == 0) | ||
| 96 | return -EINVAL; | ||
| 97 | |||
| 98 | return pcmcia_request_io(p_dev); | ||
| 99 | } | ||
| 100 | |||
| 101 | |||
| 102 | static int airo_config(struct pcmcia_device *link) | ||
| 103 | { | ||
| 104 | int ret; | ||
| 105 | |||
| 106 | dev_dbg(&link->dev, "airo_config\n"); | ||
| 107 | |||
| 108 | link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_VPP | | ||
| 109 | CONF_AUTO_AUDIO | CONF_AUTO_SET_IO; | ||
| 110 | |||
| 111 | ret = pcmcia_loop_config(link, airo_cs_config_check, NULL); | ||
| 112 | if (ret) | ||
| 113 | goto failed; | ||
| 114 | |||
| 115 | if (!link->irq) | ||
| 116 | goto failed; | ||
| 117 | |||
| 118 | ret = pcmcia_enable_device(link); | ||
| 119 | if (ret) | ||
| 120 | goto failed; | ||
| 121 | ((struct local_info *)link->priv)->eth_dev = | ||
| 122 | init_airo_card(link->irq, | ||
| 123 | link->resource[0]->start, 1, &link->dev); | ||
| 124 | if (!((struct local_info *)link->priv)->eth_dev) | ||
| 125 | goto failed; | ||
| 126 | |||
| 127 | return 0; | ||
| 128 | |||
| 129 | failed: | ||
| 130 | airo_release(link); | ||
| 131 | return -ENODEV; | ||
| 132 | } /* airo_config */ | ||
| 133 | |||
| 134 | static void airo_release(struct pcmcia_device *link) | ||
| 135 | { | ||
| 136 | dev_dbg(&link->dev, "airo_release\n"); | ||
| 137 | pcmcia_disable_device(link); | ||
| 138 | } | ||
| 139 | |||
| 140 | static int airo_suspend(struct pcmcia_device *link) | ||
| 141 | { | ||
| 142 | struct local_info *local = link->priv; | ||
| 143 | |||
| 144 | netif_device_detach(local->eth_dev); | ||
| 145 | |||
| 146 | return 0; | ||
| 147 | } | ||
| 148 | |||
| 149 | static int airo_resume(struct pcmcia_device *link) | ||
| 150 | { | ||
| 151 | struct local_info *local = link->priv; | ||
| 152 | |||
| 153 | if (link->open) { | ||
| 154 | reset_airo_card(local->eth_dev); | ||
| 155 | netif_device_attach(local->eth_dev); | ||
| 156 | } | ||
| 157 | |||
| 158 | return 0; | ||
| 159 | } | ||
| 160 | |||
| 161 | static const struct pcmcia_device_id airo_ids[] = { | ||
| 162 | PCMCIA_DEVICE_MANF_CARD(0x015f, 0x000a), | ||
| 163 | PCMCIA_DEVICE_MANF_CARD(0x015f, 0x0005), | ||
| 164 | PCMCIA_DEVICE_MANF_CARD(0x015f, 0x0007), | ||
| 165 | PCMCIA_DEVICE_MANF_CARD(0x0105, 0x0007), | ||
| 166 | PCMCIA_DEVICE_NULL, | ||
| 167 | }; | ||
| 168 | MODULE_DEVICE_TABLE(pcmcia, airo_ids); | ||
| 169 | |||
| 170 | static struct pcmcia_driver airo_driver = { | ||
| 171 | .owner = THIS_MODULE, | ||
| 172 | .name = "airo_cs", | ||
| 173 | .probe = airo_probe, | ||
| 174 | .remove = airo_detach, | ||
| 175 | .id_table = airo_ids, | ||
| 176 | .suspend = airo_suspend, | ||
| 177 | .resume = airo_resume, | ||
| 178 | }; | ||
| 179 | module_pcmcia_driver(airo_driver); | ||
| 180 | |||
| 181 | /* | ||
| 182 | This program is free software; you can redistribute it and/or | ||
| 183 | modify it under the terms of the GNU General Public License | ||
| 184 | as published by the Free Software Foundation; either version 2 | ||
| 185 | of the License, or (at your option) any later version. | ||
| 186 | |||
| 187 | This program is distributed in the hope that it will be useful, | ||
| 188 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 189 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 190 | GNU General Public License for more details. | ||
| 191 | |||
| 192 | In addition: | ||
| 193 | |||
| 194 | Redistribution and use in source and binary forms, with or without | ||
| 195 | modification, are permitted provided that the following conditions | ||
| 196 | are met: | ||
| 197 | |||
| 198 | 1. Redistributions of source code must retain the above copyright | ||
| 199 | notice, this list of conditions and the following disclaimer. | ||
| 200 | 2. Redistributions in binary form must reproduce the above copyright | ||
| 201 | notice, this list of conditions and the following disclaimer in the | ||
| 202 | documentation and/or other materials provided with the distribution. | ||
| 203 | 3. The name of the author may not be used to endorse or promote | ||
| 204 | products derived from this software without specific prior written | ||
| 205 | permission. | ||
| 206 | |||
| 207 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | ||
| 208 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
| 209 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
| 210 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, | ||
| 211 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
| 212 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||
| 213 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
| 214 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | ||
| 215 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING | ||
| 216 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| 217 | POSSIBILITY OF SUCH DAMAGE. | ||
| 218 | */ | ||
| @@ -4231,8 +4231,6 @@ il4965_rx_handle(struct il_priv *il) · 4230 unmodified lines | |||
| 4231 | fill_rx = 1; | 4231 | fill_rx = 1; |
| 4232 | 4232 | ||
| 4233 | while (i != r) { | 4233 | while (i != r) { |
| 4234 | int len; | ||
| 4235 | |||
| 4236 | rxb = rxq->queue[i]; | 4234 | rxb = rxq->queue[i]; |
| 4237 | 4235 | ||
| 4238 | /* If an RXB doesn't have a Rx queue slot associated with it, | 4236 | /* If an RXB doesn't have a Rx queue slot associated with it, |
| @@ -4246,10 +4244,6 @@ il4965_rx_handle(struct il_priv *il) · 7 unmodified lines | |||
| 4246 | PAGE_SIZE << il->hw_params.rx_page_order, | 4244 | PAGE_SIZE << il->hw_params.rx_page_order, |
| 4247 | DMA_FROM_DEVICE); | 4245 | DMA_FROM_DEVICE); |
| 4248 | pkt = rxb_addr(rxb); | 4246 | pkt = rxb_addr(rxb); |
| 4249 | |||
| 4250 | len = le32_to_cpu(pkt->len_n_flags) & IL_RX_FRAME_SIZE_MSK; | ||
| 4251 | len += sizeof(u32); /* account for status word */ | ||
| 4252 | |||
| 4253 | reclaim = il_need_reclaim(il, pkt); | 4247 | reclaim = il_need_reclaim(il, pkt); |
| 4254 | 4248 | ||
| 4255 | /* Based on type of command response or notification, | 4249 | /* Based on type of command response or notification, |
| @@ -12,8 +12,6 @@ config WLAN_VENDOR_INTERSIL · 11 unmodified lines | |||
| 12 | 12 | ||
| 13 | if WLAN_VENDOR_INTERSIL | 13 | if WLAN_VENDOR_INTERSIL |
| 14 | 14 | ||
| 15 | source "drivers/net/wireless/intersil/hostap/Kconfig" | ||
| 16 | source "drivers/net/wireless/intersil/orinoco/Kconfig" | ||
| 17 | source "drivers/net/wireless/intersil/p54/Kconfig" | 15 | source "drivers/net/wireless/intersil/p54/Kconfig" |
| 18 | 16 | ||
| 19 | endif # WLAN_VENDOR_INTERSIL | 17 | endif # WLAN_VENDOR_INTERSIL |
| @@ -1,4 +1,2 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0-only | 1 | # SPDX-License-Identifier: GPL-2.0-only |
| 2 | obj-$(CONFIG_HOSTAP) += hostap/ | ||
| 3 | obj-$(CONFIG_HERMES) += orinoco/ | ||
| 4 | obj-$(CONFIG_P54_COMMON) += p54/ | 2 | obj-$(CONFIG_P54_COMMON) += p54/ |
| @@ -1,95 +0,0 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0-only | ||
| 2 | config HOSTAP | ||
| 3 | tristate "IEEE 802.11 for Host AP (Prism2/2.5/3 and WEP/TKIP/CCMP)" | ||
| 4 | select WIRELESS_EXT | ||
| 5 | select WEXT_SPY | ||
| 6 | select WEXT_PRIV | ||
| 7 | select CRYPTO | ||
| 8 | select CRYPTO_MICHAEL_MIC | ||
| 9 | select CRC32 | ||
| 10 | select LIB80211 | ||
| 11 | select LIB80211_CRYPT_WEP | ||
| 12 | select LIB80211_CRYPT_TKIP | ||
| 13 | select LIB80211_CRYPT_CCMP | ||
| 14 | help | ||
| 15 | Shared driver code for IEEE 802.11b wireless cards based on | ||
| 16 | Intersil Prism2/2.5/3 chipset. This driver supports so called | ||
| 17 | Host AP mode that allows the card to act as an IEEE 802.11 | ||
| 18 | access point. | ||
| 19 | |||
| 20 | See <http://hostap.epitest.fi/> for more information about the | ||
| 21 | Host AP driver configuration and tools. This site includes | ||
| 22 | information and tools (hostapd and wpa_supplicant) for WPA/WPA2 | ||
| 23 | support. | ||
| 24 | |||
| 25 | This option includes the base Host AP driver code that is shared by | ||
| 26 | different hardware models. You will also need to enable support for | ||
| 27 | PLX/PCI/CS version of the driver to actually use the driver. | ||
| 28 | |||
| 29 | The driver can be compiled as a module and it will be called | ||
| 30 | hostap. | ||
| 31 | |||
| 32 | config HOSTAP_FIRMWARE | ||
| 33 | bool "Support downloading firmware images with Host AP driver" | ||
| 34 | depends on HOSTAP | ||
| 35 | help | ||
| 36 | Configure Host AP driver to include support for firmware image | ||
| 37 | download. This option by itself only enables downloading to the | ||
| 38 | volatile memory, i.e. the card RAM. This option is required to | ||
| 39 | support cards that don't have firmware in flash, such as D-Link | ||
| 40 | DWL-520 rev E and D-Link DWL-650 rev P. | ||
| 41 | |||
| 42 | Firmware image downloading needs a user space tool, prism2_srec. | ||
| 43 | It is available from http://hostap.epitest.fi/. | ||
| 44 | |||
| 45 | config HOSTAP_FIRMWARE_NVRAM | ||
| 46 | bool "Support for non-volatile firmware download" | ||
| 47 | depends on HOSTAP_FIRMWARE | ||
| 48 | help | ||
| 49 | Allow Host AP driver to write firmware images to the non-volatile | ||
| 50 | card memory, i.e. flash memory that survives power cycling. | ||
| 51 | Enable this option if you want to be able to change card firmware | ||
| 52 | permanently. | ||
| 53 | |||
| 54 | Firmware image downloading needs a user space tool, prism2_srec. | ||
| 55 | It is available from http://hostap.epitest.fi/. | ||
| 56 | |||
| 57 | config HOSTAP_PLX | ||
| 58 | tristate "Host AP driver for Prism2/2.5/3 in PLX9052 PCI adaptors" | ||
| 59 | depends on PCI && HOSTAP && HAS_IOPORT | ||
| 60 | help | ||
| 61 | Host AP driver's version for Prism2/2.5/3 PC Cards in PLX9052 based | ||
| 62 | PCI adaptors. | ||
| 63 | |||
| 64 | "Host AP support for Prism2/2.5/3 IEEE 802.11b" is required for this | ||
| 65 | driver and its help text includes more information about the Host AP | ||
| 66 | driver. | ||
| 67 | |||
| 68 | The driver can be compiled as a module and will be named | ||
| 69 | hostap_plx. | ||
| 70 | |||
| 71 | config HOSTAP_PCI | ||
| 72 | tristate "Host AP driver for Prism2.5 PCI adaptors" | ||
| 73 | depends on PCI && HOSTAP | ||
| 74 | help | ||
| 75 | Host AP driver's version for Prism2.5 PCI adaptors. | ||
| 76 | |||
| 77 | "Host AP support for Prism2/2.5/3 IEEE 802.11b" is required for this | ||
| 78 | driver and its help text includes more information about the Host AP | ||
| 79 | driver. | ||
| 80 | |||
| 81 | The driver can be compiled as a module and will be named | ||
| 82 | hostap_pci. | ||
| 83 | |||
| 84 | config HOSTAP_CS | ||
| 85 | tristate "Host AP driver for Prism2/2.5/3 PC Cards" | ||
| 86 | depends on PCMCIA && HOSTAP | ||
| 87 | help | ||
| 88 | Host AP driver's version for Prism2/2.5/3 PC Cards. | ||
| 89 | |||
| 90 | "Host AP support for Prism2/2.5/3 IEEE 802.11b" is required for this | ||
| 91 | driver and its help text includes more information about the Host AP | ||
| 92 | driver. | ||
| 93 | |||
| 94 | The driver can be compiled as a module and will be named | ||
| 95 | hostap_cs. | ||
| @@ -1,8 +0,0 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0 | ||
| 2 | hostap-y := hostap_80211_rx.o hostap_80211_tx.o hostap_ap.o hostap_info.o \ | ||
| 3 | hostap_ioctl.o hostap_main.o hostap_proc.o | ||
| 4 | obj-$(CONFIG_HOSTAP) += hostap.o | ||
| 5 | |||
| 6 | obj-$(CONFIG_HOSTAP_CS) += hostap_cs.o | ||
| 7 | obj-$(CONFIG_HOSTAP_PLX) += hostap_plx.o | ||
| 8 | obj-$(CONFIG_HOSTAP_PCI) += hostap_pci.o | ||
| @@ -1,98 +0,0 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 2 | #ifndef HOSTAP_H | ||
| 3 | #define HOSTAP_H | ||
| 4 | |||
| 5 | #include <linux/ethtool.h> | ||
| 6 | #include <linux/kernel.h> | ||
| 7 | |||
| 8 | #include "hostap_wlan.h" | ||
| 9 | #include "hostap_ap.h" | ||
| 10 | |||
| 11 | static const long __maybe_unused freq_list[] = { | ||
| 12 | 2412, 2417, 2422, 2427, 2432, 2437, 2442, | ||
| 13 | 2447, 2452, 2457, 2462, 2467, 2472, 2484 | ||
| 14 | }; | ||
| 15 | #define FREQ_COUNT ARRAY_SIZE(freq_list) | ||
| 16 | |||
| 17 | /* hostap.c */ | ||
| 18 | |||
| 19 | extern struct proc_dir_entry *hostap_proc; | ||
| 20 | |||
| 21 | u16 hostap_tx_callback_register(local_info_t *local, | ||
| 22 | void (*func)(struct sk_buff *, int ok, void *), | ||
| 23 | void *data); | ||
| 24 | int hostap_tx_callback_unregister(local_info_t *local, u16 idx); | ||
| 25 | int hostap_set_word(struct net_device *dev, int rid, u16 val); | ||
| 26 | int hostap_set_string(struct net_device *dev, int rid, const char *val); | ||
| 27 | u16 hostap_get_porttype(local_info_t *local); | ||
| 28 | int hostap_set_encryption(local_info_t *local); | ||
| 29 | int hostap_set_antsel(local_info_t *local); | ||
| 30 | int hostap_set_roaming(local_info_t *local); | ||
| 31 | int hostap_set_auth_algs(local_info_t *local); | ||
| 32 | void hostap_dump_rx_header(const char *name, | ||
| 33 | const struct hfa384x_rx_frame *rx); | ||
| 34 | void hostap_dump_tx_header(const char *name, | ||
| 35 | const struct hfa384x_tx_frame *tx); | ||
| 36 | extern const struct header_ops hostap_80211_ops; | ||
| 37 | int hostap_80211_get_hdrlen(__le16 fc); | ||
| 38 | struct net_device_stats *hostap_get_stats(struct net_device *dev); | ||
| 39 | void hostap_setup_dev(struct net_device *dev, local_info_t *local, | ||
| 40 | int type); | ||
| 41 | void hostap_set_multicast_list_queue(struct work_struct *work); | ||
| 42 | int hostap_set_hostapd(local_info_t *local, int val, int rtnl_locked); | ||
| 43 | int hostap_set_hostapd_sta(local_info_t *local, int val, int rtnl_locked); | ||
| 44 | void hostap_cleanup(local_info_t *local); | ||
| 45 | void hostap_cleanup_handler(void *data); | ||
| 46 | struct net_device * hostap_add_interface(struct local_info *local, | ||
| 47 | int type, int rtnl_locked, | ||
| 48 | const char *prefix, const char *name); | ||
| 49 | void hostap_remove_interface(struct net_device *dev, int rtnl_locked, | ||
| 50 | int remove_from_list); | ||
| 51 | int prism2_update_comms_qual(struct net_device *dev); | ||
| 52 | int prism2_sta_send_mgmt(local_info_t *local, u8 *dst, u16 stype, | ||
| 53 | u8 *body, size_t bodylen); | ||
| 54 | int prism2_sta_deauth(local_info_t *local, u16 reason); | ||
| 55 | int prism2_wds_add(local_info_t *local, u8 *remote_addr, | ||
| 56 | int rtnl_locked); | ||
| 57 | int prism2_wds_del(local_info_t *local, u8 *remote_addr, | ||
| 58 | int rtnl_locked, int do_not_remove); | ||
| 59 | |||
| 60 | |||
| 61 | /* hostap_ap.c */ | ||
| 62 | |||
| 63 | int ap_control_add_mac(struct mac_restrictions *mac_restrictions, u8 *mac); | ||
| 64 | int ap_control_del_mac(struct mac_restrictions *mac_restrictions, u8 *mac); | ||
| 65 | void ap_control_flush_macs(struct mac_restrictions *mac_restrictions); | ||
| 66 | int ap_control_kick_mac(struct ap_data *ap, struct net_device *dev, u8 *mac); | ||
| 67 | void ap_control_kickall(struct ap_data *ap); | ||
| 68 | void * ap_crypt_get_ptrs(struct ap_data *ap, u8 *addr, int permanent, | ||
| 69 | struct lib80211_crypt_data ***crypt); | ||
| 70 | int prism2_ap_get_sta_qual(local_info_t *local, struct sockaddr addr[], | ||
| 71 | struct iw_quality qual[], int buf_size, | ||
| 72 | int aplist); | ||
| 73 | int prism2_ap_translate_scan(struct net_device *dev, | ||
| 74 | struct iw_request_info *info, char *buffer); | ||
| 75 | int prism2_hostapd(struct ap_data *ap, struct prism2_hostapd_param *param); | ||
| 76 | |||
| 77 | |||
| 78 | /* hostap_proc.c */ | ||
| 79 | |||
| 80 | void hostap_init_proc(local_info_t *local); | ||
| 81 | void hostap_remove_proc(local_info_t *local); | ||
| 82 | |||
| 83 | |||
| 84 | /* hostap_info.c */ | ||
| 85 | |||
| 86 | void hostap_info_init(local_info_t *local); | ||
| 87 | void hostap_info_process(local_info_t *local, struct sk_buff *skb); | ||
| 88 | |||
| 89 | |||
| 90 | /* hostap_ioctl.c */ | ||
| 91 | |||
| 92 | extern const struct iw_handler_def hostap_iw_handler_def; | ||
| 93 | extern const struct ethtool_ops prism2_ethtool_ops; | ||
| 94 | |||
| 95 | int hostap_siocdevprivate(struct net_device *dev, struct ifreq *ifr, | ||
| 96 | void __user *data, int cmd); | ||
| 97 | |||
| 98 | #endif /* HOSTAP_H */ | ||
| @@ -1,97 +0,0 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 2 | #ifndef HOSTAP_80211_H | ||
| 3 | #define HOSTAP_80211_H | ||
| 4 | |||
| 5 | #include <linux/types.h> | ||
| 6 | #include <linux/skbuff.h> | ||
| 7 | #include <linux/netdevice.h> | ||
| 8 | |||
| 9 | struct hostap_ieee80211_mgmt { | ||
| 10 | __le16 frame_control; | ||
| 11 | __le16 duration; | ||
| 12 | u8 da[6]; | ||
| 13 | u8 sa[6]; | ||
| 14 | u8 bssid[6]; | ||
| 15 | __le16 seq_ctrl; | ||
| 16 | union { | ||
| 17 | struct { | ||
| 18 | __le16 auth_alg; | ||
| 19 | __le16 auth_transaction; | ||
| 20 | __le16 status_code; | ||
| 21 | /* possibly followed by Challenge text */ | ||
| 22 | u8 variable[0]; | ||
| 23 | } __packed auth; | ||
| 24 | struct { | ||
| 25 | __le16 reason_code; | ||
| 26 | } __packed deauth; | ||
| 27 | struct { | ||
| 28 | __le16 capab_info; | ||
| 29 | __le16 listen_interval; | ||
| 30 | /* followed by SSID and Supported rates */ | ||
| 31 | u8 variable[0]; | ||
| 32 | } __packed assoc_req; | ||
| 33 | struct { | ||
| 34 | __le16 capab_info; | ||
| 35 | __le16 status_code; | ||
| 36 | __le16 aid; | ||
| 37 | /* followed by Supported rates */ | ||
| 38 | u8 variable[0]; | ||
| 39 | } __packed assoc_resp, reassoc_resp; | ||
| 40 | struct { | ||
| 41 | __le16 capab_info; | ||
| 42 | __le16 listen_interval; | ||
| 43 | u8 current_ap[6]; | ||
| 44 | /* followed by SSID and Supported rates */ | ||
| 45 | u8 variable[0]; | ||
| 46 | } __packed reassoc_req; | ||
| 47 | struct { | ||
| 48 | __le16 reason_code; | ||
| 49 | } __packed disassoc; | ||
| 50 | struct { | ||
| 51 | } __packed probe_req; | ||
| 52 | struct { | ||
| 53 | u8 timestamp[8]; | ||
| 54 | __le16 beacon_int; | ||
| 55 | __le16 capab_info; | ||
| 56 | /* followed by some of SSID, Supported rates, | ||
| 57 | * FH Params, DS Params, CF Params, IBSS Params, TIM */ | ||
| 58 | u8 variable[0]; | ||
| 59 | } __packed beacon, probe_resp; | ||
| 60 | } u; | ||
| 61 | } __packed; | ||
| 62 | |||
| 63 | |||
| 64 | #define IEEE80211_MGMT_HDR_LEN 24 | ||
| 65 | #define IEEE80211_DATA_HDR3_LEN 24 | ||
| 66 | #define IEEE80211_DATA_HDR4_LEN 30 | ||
| 67 | |||
| 68 | |||
| 69 | struct hostap_80211_rx_status { | ||
| 70 | u32 mac_time; | ||
| 71 | u8 signal; | ||
| 72 | u8 noise; | ||
| 73 | u16 rate; /* in 100 kbps */ | ||
| 74 | }; | ||
| 75 | |||
| 76 | /* prism2_rx_80211 'type' argument */ | ||
| 77 | enum { | ||
| 78 | PRISM2_RX_MONITOR, PRISM2_RX_MGMT, PRISM2_RX_NON_ASSOC, | ||
| 79 | PRISM2_RX_NULLFUNC_ACK | ||
| 80 | }; | ||
| 81 | |||
| 82 | int prism2_rx_80211(struct net_device *dev, struct sk_buff *skb, | ||
| 83 | struct hostap_80211_rx_status *rx_stats, int type); | ||
| 84 | void hostap_80211_rx(struct net_device *dev, struct sk_buff *skb, | ||
| 85 | struct hostap_80211_rx_status *rx_stats); | ||
| 86 | void hostap_dump_rx_80211(const char *name, struct sk_buff *skb, | ||
| 87 | struct hostap_80211_rx_status *rx_stats); | ||
| 88 | |||
| 89 | void hostap_dump_tx_80211(const char *name, struct sk_buff *skb); | ||
| 90 | netdev_tx_t hostap_data_start_xmit(struct sk_buff *skb, | ||
| 91 | struct net_device *dev); | ||
| 92 | netdev_tx_t hostap_mgmt_start_xmit(struct sk_buff *skb, | ||
| 93 | struct net_device *dev); | ||
| 94 | netdev_tx_t hostap_master_start_xmit(struct sk_buff *skb, | ||
| 95 | struct net_device *dev); | ||
| 96 | |||
| 97 | #endif /* HOSTAP_80211_H */ | ||
| @@ -1,1116 +0,0 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | #include <linux/etherdevice.h> | ||
| 3 | #include <linux/slab.h> | ||
| 4 | #include <linux/export.h> | ||
| 5 | #include <net/lib80211.h> | ||
| 6 | #include <linux/if_arp.h> | ||
| 7 | |||
| 8 | #include "hostap_80211.h" | ||
| 9 | #include "hostap.h" | ||
| 10 | #include "hostap_ap.h" | ||
| 11 | |||
| 12 | /* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */ | ||
| 13 | /* Ethernet-II snap header (RFC1042 for most EtherTypes) */ | ||
| 14 | static unsigned char rfc1042_header[] = | ||
| 15 | { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; | ||
| 16 | /* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */ | ||
| 17 | static unsigned char bridge_tunnel_header[] = | ||
| 18 | { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 }; | ||
| 19 | /* No encapsulation header if EtherType < 0x600 (=length) */ | ||
| 20 | |||
| 21 | void hostap_dump_rx_80211(const char *name, struct sk_buff *skb, | ||
| 22 | struct hostap_80211_rx_status *rx_stats) | ||
| 23 | { | ||
| 24 | struct ieee80211_hdr *hdr; | ||
| 25 | u16 fc; | ||
| 26 | |||
| 27 | hdr = (struct ieee80211_hdr *) skb->data; | ||
| 28 | |||
| 29 | printk(KERN_DEBUG "%s: RX signal=%d noise=%d rate=%d len=%d " | ||
| 30 | "jiffies=%ld\n", | ||
| 31 | name, rx_stats->signal, rx_stats->noise, rx_stats->rate, | ||
| 32 | skb->len, jiffies); | ||
| 33 | |||
| 34 | if (skb->len < 2) | ||
| 35 | return; | ||
| 36 | |||
| 37 | fc = le16_to_cpu(hdr->frame_control); | ||
| 38 | printk(KERN_DEBUG " FC=0x%04x (type=%d:%d)%s%s", | ||
| 39 | fc, (fc & IEEE80211_FCTL_FTYPE) >> 2, | ||
| 40 | (fc & IEEE80211_FCTL_STYPE) >> 4, | ||
| 41 | fc & IEEE80211_FCTL_TODS ? " [ToDS]" : "", | ||
| 42 | fc & IEEE80211_FCTL_FROMDS ? " [FromDS]" : ""); | ||
| 43 | |||
| 44 | if (skb->len < IEEE80211_DATA_HDR3_LEN) { | ||
| 45 | printk("\n"); | ||
| 46 | return; | ||
| 47 | } | ||
| 48 | |||
| 49 | printk(" dur=0x%04x seq=0x%04x\n", le16_to_cpu(hdr->duration_id), | ||
| 50 | le16_to_cpu(hdr->seq_ctrl)); | ||
| 51 | |||
| 52 | printk(KERN_DEBUG " A1=%pM", hdr->addr1); | ||
| 53 | printk(" A2=%pM", hdr->addr2); | ||
| 54 | printk(" A3=%pM", hdr->addr3); | ||
| 55 | if (skb->len >= 30) | ||
| 56 | printk(" A4=%pM", hdr->addr4); | ||
| 57 | printk("\n"); | ||
| 58 | } | ||
| 59 | |||
| 60 | |||
| 61 | /* Send RX frame to netif with 802.11 (and possible prism) header. | ||
| 62 | * Called from hardware or software IRQ context. */ | ||
| 63 | int prism2_rx_80211(struct net_device *dev, struct sk_buff *skb, | ||
| 64 | struct hostap_80211_rx_status *rx_stats, int type) | ||
| 65 | { | ||
| 66 | struct hostap_interface *iface; | ||
| 67 | local_info_t *local; | ||
| 68 | int hdrlen, phdrlen, head_need, tail_need; | ||
| 69 | u16 fc; | ||
| 70 | int prism_header, ret; | ||
| 71 | struct ieee80211_hdr *fhdr; | ||
| 72 | |||
| 73 | iface = netdev_priv(dev); | ||
| 74 | local = iface->local; | ||
| 75 | |||
| 76 | if (dev->type == ARPHRD_IEEE80211_PRISM) { | ||
| 77 | if (local->monitor_type == PRISM2_MONITOR_PRISM) { | ||
| 78 | prism_header = 1; | ||
| 79 | phdrlen = sizeof(struct linux_wlan_ng_prism_hdr); | ||
| 80 | } else { /* local->monitor_type == PRISM2_MONITOR_CAPHDR */ | ||
| 81 | prism_header = 2; | ||
| 82 | phdrlen = sizeof(struct linux_wlan_ng_cap_hdr); | ||
| 83 | } | ||
| 84 | } else if (dev->type == ARPHRD_IEEE80211_RADIOTAP) { | ||
| 85 | prism_header = 3; | ||
| 86 | phdrlen = sizeof(struct hostap_radiotap_rx); | ||
| 87 | } else { | ||
| 88 | prism_header = 0; | ||
| 89 | phdrlen = 0; | ||
| 90 | } | ||
| 91 | |||
| 92 | fhdr = (struct ieee80211_hdr *) skb->data; | ||
| 93 | fc = le16_to_cpu(fhdr->frame_control); | ||
| 94 | |||
| 95 | if (type == PRISM2_RX_MGMT && (fc & IEEE80211_FCTL_VERS)) { | ||
| 96 | printk(KERN_DEBUG "%s: dropped management frame with header " | ||
| 97 | "version %d\n", dev->name, fc & IEEE80211_FCTL_VERS); | ||
| 98 | dev_kfree_skb_any(skb); | ||
| 99 | return 0; | ||
| 100 | } | ||
| 101 | |||
| 102 | hdrlen = hostap_80211_get_hdrlen(fhdr->frame_control); | ||
| 103 | |||
| 104 | /* check if there is enough room for extra data; if not, expand skb | ||
| 105 | * buffer to be large enough for the changes */ | ||
| 106 | head_need = phdrlen; | ||
| 107 | tail_need = 0; | ||
| 108 | #ifdef PRISM2_ADD_BOGUS_CRC | ||
| 109 | tail_need += 4; | ||
| 110 | #endif /* PRISM2_ADD_BOGUS_CRC */ | ||
| 111 | |||
| 112 | head_need -= skb_headroom(skb); | ||
| 113 | tail_need -= skb_tailroom(skb); | ||
| 114 | |||
| 115 | if (head_need > 0 || tail_need > 0) { | ||
| 116 | if (pskb_expand_head(skb, head_need > 0 ? head_need : 0, | ||
| 117 | tail_need > 0 ? tail_need : 0, | ||
| 118 | GFP_ATOMIC)) { | ||
| 119 | printk(KERN_DEBUG "%s: prism2_rx_80211 failed to " | ||
| 120 | "reallocate skb buffer\n", dev->name); | ||
| 121 | dev_kfree_skb_any(skb); | ||
| 122 | return 0; | ||
| 123 | } | ||
| 124 | } | ||
| 125 | |||
| 126 | /* We now have an skb with enough head and tail room, so just insert | ||
| 127 | * the extra data */ | ||
| 128 | |||
| 129 | #ifdef PRISM2_ADD_BOGUS_CRC | ||
| 130 | memset(skb_put(skb, 4), 0xff, 4); /* Prism2 strips CRC */ | ||
| 131 | #endif /* PRISM2_ADD_BOGUS_CRC */ | ||
| 132 | |||
| 133 | if (prism_header == 1) { | ||
| 134 | struct linux_wlan_ng_prism_hdr *hdr; | ||
| 135 | hdr = skb_push(skb, phdrlen); | ||
| 136 | memset(hdr, 0, phdrlen); | ||
| 137 | hdr->msgcode = LWNG_CAP_DID_BASE; | ||
| 138 | hdr->msglen = sizeof(*hdr); | ||
| 139 | memcpy(hdr->devname, dev->name, sizeof(hdr->devname)); | ||
| 140 | #define LWNG_SETVAL(f,i,s,l,d) \ | ||
| 141 | hdr->f.did = LWNG_CAP_DID_BASE | (i << 12); \ | ||
| 142 | hdr->f.status = s; hdr->f.len = l; hdr->f.data = d | ||
| 143 | LWNG_SETVAL(hosttime, 1, 0, 4, jiffies); | ||
| 144 | LWNG_SETVAL(mactime, 2, 0, 4, rx_stats->mac_time); | ||
| 145 | LWNG_SETVAL(channel, 3, 1 /* no value */, 4, 0); | ||
| 146 | LWNG_SETVAL(rssi, 4, 1 /* no value */, 4, 0); | ||
| 147 | LWNG_SETVAL(sq, 5, 1 /* no value */, 4, 0); | ||
| 148 | LWNG_SETVAL(signal, 6, 0, 4, rx_stats->signal); | ||
| 149 | LWNG_SETVAL(noise, 7, 0, 4, rx_stats->noise); | ||
| 150 | LWNG_SETVAL(rate, 8, 0, 4, rx_stats->rate / 5); | ||
| 151 | LWNG_SETVAL(istx, 9, 0, 4, 0); | ||
| 152 | LWNG_SETVAL(frmlen, 10, 0, 4, skb->len - phdrlen); | ||
| 153 | #undef LWNG_SETVAL | ||
| 154 | } else if (prism_header == 2) { | ||
| 155 | struct linux_wlan_ng_cap_hdr *hdr; | ||
| 156 | hdr = skb_push(skb, phdrlen); | ||
| 157 | memset(hdr, 0, phdrlen); | ||
| 158 | hdr->version = htonl(LWNG_CAPHDR_VERSION); | ||
| 159 | hdr->length = htonl(phdrlen); | ||
| 160 | hdr->mactime = __cpu_to_be64(rx_stats->mac_time); | ||
| 161 | hdr->hosttime = __cpu_to_be64(jiffies); | ||
| 162 | hdr->phytype = htonl(4); /* dss_dot11_b */ | ||
| 163 | hdr->channel = htonl(local->channel); | ||
| 164 | hdr->datarate = htonl(rx_stats->rate); | ||
| 165 | hdr->antenna = htonl(0); /* unknown */ | ||
| 166 | hdr->priority = htonl(0); /* unknown */ | ||
| 167 | hdr->ssi_type = htonl(3); /* raw */ | ||
| 168 | hdr->ssi_signal = htonl(rx_stats->signal); | ||
| 169 | hdr->ssi_noise = htonl(rx_stats->noise); | ||
| 170 | hdr->preamble = htonl(0); /* unknown */ | ||
| 171 | hdr->encoding = htonl(1); /* cck */ | ||
| 172 | } else if (prism_header == 3) { | ||
| 173 | struct hostap_radiotap_rx *hdr; | ||
| 174 | hdr = skb_push(skb, phdrlen); | ||
| 175 | memset(hdr, 0, phdrlen); | ||
| 176 | hdr->hdr.it_len = cpu_to_le16(phdrlen); | ||
| 177 | hdr->hdr.it_present = | ||
| 178 | cpu_to_le32((1 << IEEE80211_RADIOTAP_TSFT) | | ||
| 179 | (1 << IEEE80211_RADIOTAP_CHANNEL) | | ||
| 180 | (1 << IEEE80211_RADIOTAP_RATE) | | ||
| 181 | (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | | ||
| 182 | (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE)); | ||
| 183 | hdr->tsft = cpu_to_le64(rx_stats->mac_time); | ||
| 184 | hdr->chan_freq = cpu_to_le16(freq_list[local->channel - 1]); | ||
| 185 | hdr->chan_flags = cpu_to_le16(IEEE80211_CHAN_CCK | | ||
| 186 | IEEE80211_CHAN_2GHZ); | ||
| 187 | hdr->rate = rx_stats->rate / 5; | ||
| 188 | hdr->dbm_antsignal = rx_stats->signal; | ||
| 189 | hdr->dbm_antnoise = rx_stats->noise; | ||
| 190 | } | ||
| 191 | |||
| 192 | ret = skb->len - phdrlen; | ||
| 193 | skb->dev = dev; | ||
| 194 | skb_reset_mac_header(skb); | ||
| 195 | skb_pull(skb, hdrlen); | ||
| 196 | if (prism_header) | ||
| 197 | skb_pull(skb, phdrlen); | ||
| 198 | skb->pkt_type = PACKET_OTHERHOST; | ||
| 199 | skb->protocol = cpu_to_be16(ETH_P_802_2); | ||
| 200 | memset(skb->cb, 0, sizeof(skb->cb)); | ||
| 201 | netif_rx(skb); | ||
| 202 | |||
| 203 | return ret; | ||
| 204 | } | ||
| 205 | |||
| 206 | |||
| 207 | /* Called only as a tasklet (software IRQ) */ | ||
| 208 | static void monitor_rx(struct net_device *dev, struct sk_buff *skb, | ||
| 209 | struct hostap_80211_rx_status *rx_stats) | ||
| 210 | { | ||
| 211 | int len; | ||
| 212 | |||
| 213 | len = prism2_rx_80211(dev, skb, rx_stats, PRISM2_RX_MONITOR); | ||
| 214 | dev->stats.rx_packets++; | ||
| 215 | dev->stats.rx_bytes += len; | ||
| 216 | } | ||
| 217 | |||
| 218 | |||
| 219 | /* Called only as a tasklet (software IRQ) */ | ||
| 220 | static struct prism2_frag_entry * | ||
| 221 | prism2_frag_cache_find(local_info_t *local, unsigned int seq, | ||
| 222 | unsigned int frag, u8 *src, u8 *dst) | ||
| 223 | { | ||
| 224 | struct prism2_frag_entry *entry; | ||
| 225 | int i; | ||
| 226 | |||
| 227 | for (i = 0; i < PRISM2_FRAG_CACHE_LEN; i++) { | ||
| 228 | entry = &local->frag_cache[i]; | ||
| 229 | if (entry->skb != NULL && | ||
| 230 | time_after(jiffies, entry->first_frag_time + 2 * HZ)) { | ||
| 231 | printk(KERN_DEBUG "%s: expiring fragment cache entry " | ||
| 232 | "seq=%u last_frag=%u\n", | ||
| 233 | local->dev->name, entry->seq, entry->last_frag); | ||
| 234 | dev_kfree_skb(entry->skb); | ||
| 235 | entry->skb = NULL; | ||
| 236 | } | ||
| 237 | |||
| 238 | if (entry->skb != NULL && entry->seq == seq && | ||
| 239 | (entry->last_frag + 1 == frag || frag == -1) && | ||
| 240 | memcmp(entry->src_addr, src, ETH_ALEN) == 0 && | ||
| 241 | memcmp(entry->dst_addr, dst, ETH_ALEN) == 0) | ||
| 242 | return entry; | ||
| 243 | } | ||
| 244 | |||
| 245 | return NULL; | ||
| 246 | } | ||
| 247 | |||
| 248 | |||
| 249 | /* Called only as a tasklet (software IRQ) */ | ||
| 250 | static struct sk_buff * | ||
| 251 | prism2_frag_cache_get(local_info_t *local, struct ieee80211_hdr *hdr) | ||
| 252 | { | ||
| 253 | struct sk_buff *skb = NULL; | ||
| 254 | u16 sc; | ||
| 255 | unsigned int frag, seq; | ||
| 256 | struct prism2_frag_entry *entry; | ||
| 257 | |||
| 258 | sc = le16_to_cpu(hdr->seq_ctrl); | ||
| 259 | frag = sc & IEEE80211_SCTL_FRAG; | ||
| 260 | seq = (sc & IEEE80211_SCTL_SEQ) >> 4; | ||
| 261 | |||
| 262 | if (frag == 0) { | ||
| 263 | /* Reserve enough space to fit maximum frame length */ | ||
| 264 | skb = dev_alloc_skb(local->dev->mtu + | ||
| 265 | sizeof(struct ieee80211_hdr) + | ||
| 266 | 8 /* LLC */ + | ||
| 267 | 2 /* alignment */ + | ||
| 268 | 8 /* WEP */ + ETH_ALEN /* WDS */); | ||
| 269 | if (skb == NULL) | ||
| 270 | return NULL; | ||
| 271 | |||
| 272 | entry = &local->frag_cache[local->frag_next_idx]; | ||
| 273 | local->frag_next_idx++; | ||
| 274 | if (local->frag_next_idx >= PRISM2_FRAG_CACHE_LEN) | ||
| 275 | local->frag_next_idx = 0; | ||
| 276 | |||
| 277 | if (entry->skb != NULL) | ||
| 278 | dev_kfree_skb(entry->skb); | ||
| 279 | |||
| 280 | entry->first_frag_time = jiffies; | ||
| 281 | entry->seq = seq; | ||
| 282 | entry->last_frag = frag; | ||
| 283 | entry->skb = skb; | ||
| 284 | memcpy(entry->src_addr, hdr->addr2, ETH_ALEN); | ||
| 285 | memcpy(entry->dst_addr, hdr->addr1, ETH_ALEN); | ||
| 286 | } else { | ||
| 287 | /* received a fragment of a frame for which the head fragment | ||
| 288 | * should have already been received */ | ||
| 289 | entry = prism2_frag_cache_find(local, seq, frag, hdr->addr2, | ||
| 290 | hdr->addr1); | ||
| 291 | if (entry != NULL) { | ||
| 292 | entry->last_frag = frag; | ||
| 293 | skb = entry->skb; | ||
| 294 | } | ||
| 295 | } | ||
| 296 | |||
| 297 | return skb; | ||
| 298 | } | ||
| 299 | |||
| 300 | |||
| 301 | /* Called only as a tasklet (software IRQ) */ | ||
| 302 | static int prism2_frag_cache_invalidate(local_info_t *local, | ||
| 303 | struct ieee80211_hdr *hdr) | ||
| 304 | { | ||
| 305 | u16 sc; | ||
| 306 | unsigned int seq; | ||
| 307 | struct prism2_frag_entry *entry; | ||
| 308 | |||
| 309 | sc = le16_to_cpu(hdr->seq_ctrl); | ||
| 310 | seq = (sc & IEEE80211_SCTL_SEQ) >> 4; | ||
| 311 | |||
| 312 | entry = prism2_frag_cache_find(local, seq, -1, hdr->addr2, hdr->addr1); | ||
| 313 | |||
| 314 | if (entry == NULL) { | ||
| 315 | printk(KERN_DEBUG "%s: could not invalidate fragment cache " | ||
| 316 | "entry (seq=%u)\n", | ||
| 317 | local->dev->name, seq); | ||
| 318 | return -1; | ||
| 319 | } | ||
| 320 | |||
| 321 | entry->skb = NULL; | ||
| 322 | return 0; | ||
| 323 | } | ||
| 324 | |||
| 325 | |||
| 326 | static struct hostap_bss_info *__hostap_get_bss(local_info_t *local, u8 *bssid, | ||
| 327 | u8 *ssid, size_t ssid_len) | ||
| 328 | { | ||
| 329 | struct list_head *ptr; | ||
| 330 | struct hostap_bss_info *bss; | ||
| 331 | |||
| 332 | list_for_each(ptr, &local->bss_list) { | ||
| 333 | bss = list_entry(ptr, struct hostap_bss_info, list); | ||
| 334 | if (memcmp(bss->bssid, bssid, ETH_ALEN) == 0 && | ||
| 335 | (ssid == NULL || | ||
| 336 | (ssid_len == bss->ssid_len && | ||
| 337 | memcmp(ssid, bss->ssid, ssid_len) == 0))) { | ||
| 338 | list_move(&bss->list, &local->bss_list); | ||
| 339 | return bss; | ||
| 340 | } | ||
| 341 | } | ||
| 342 | |||
| 343 | return NULL; | ||
| 344 | } | ||
| 345 | |||
| 346 | |||
| 347 | static struct hostap_bss_info *__hostap_add_bss(local_info_t *local, u8 *bssid, | ||
| 348 | u8 *ssid, size_t ssid_len) | ||
| 349 | { | ||
| 350 | struct hostap_bss_info *bss; | ||
| 351 | |||
| 352 | if (local->num_bss_info >= HOSTAP_MAX_BSS_COUNT) { | ||
| 353 | bss = list_entry(local->bss_list.prev, | ||
| 354 | struct hostap_bss_info, list); | ||
| 355 | list_del(&bss->list); | ||
| 356 | local->num_bss_info--; | ||
| 357 | } else { | ||
| 358 | bss = kmalloc(sizeof(*bss), GFP_ATOMIC); | ||
| 359 | if (bss == NULL) | ||
| 360 | return NULL; | ||
| 361 | } | ||
| 362 | |||
| 363 | memset(bss, 0, sizeof(*bss)); | ||
| 364 | memcpy(bss->bssid, bssid, ETH_ALEN); | ||
| 365 | memcpy(bss->ssid, ssid, ssid_len); | ||
| 366 | bss->ssid_len = ssid_len; | ||
| 367 | local->num_bss_info++; | ||
| 368 | list_add(&bss->list, &local->bss_list); | ||
| 369 | return bss; | ||
| 370 | } | ||
| 371 | |||
| 372 | |||
| 373 | static void __hostap_expire_bss(local_info_t *local) | ||
| 374 | { | ||
| 375 | struct hostap_bss_info *bss; | ||
| 376 | |||
| 377 | while (local->num_bss_info > 0) { | ||
| 378 | bss = list_entry(local->bss_list.prev, | ||
| 379 | struct hostap_bss_info, list); | ||
| 380 | if (!time_after(jiffies, bss->last_update + 60 * HZ)) | ||
| 381 | break; | ||
| 382 | |||
| 383 | list_del(&bss->list); | ||
| 384 | local->num_bss_info--; | ||
| 385 | kfree(bss); | ||
| 386 | } | ||
| 387 | } | ||
| 388 | |||
| 389 | |||
| 390 | /* Both IEEE 802.11 Beacon and Probe Response frames have similar structure, so | ||
| 391 | * the same routine can be used to parse both of them. */ | ||
| 392 | static void hostap_rx_sta_beacon(local_info_t *local, struct sk_buff *skb, | ||
| 393 | int stype) | ||
| 394 | { | ||
| 395 | struct hostap_ieee80211_mgmt *mgmt; | ||
| 396 | int left, chan = 0; | ||
| 397 | u8 *pos; | ||
| 398 | u8 *ssid = NULL, *wpa = NULL, *rsn = NULL; | ||
| 399 | size_t ssid_len = 0, wpa_len = 0, rsn_len = 0; | ||
| 400 | struct hostap_bss_info *bss; | ||
| 401 | |||
| 402 | if (skb->len < IEEE80211_MGMT_HDR_LEN + sizeof(mgmt->u.beacon)) | ||
| 403 | return; | ||
| 404 | |||
| 405 | mgmt = (struct hostap_ieee80211_mgmt *) skb->data; | ||
| 406 | pos = mgmt->u.beacon.variable; | ||
| 407 | left = skb->len - (pos - skb->data); | ||
| 408 | |||
| 409 | while (left >= 2) { | ||
| 410 | if (2 + pos[1] > left) | ||
| 411 | return; /* parse failed */ | ||
| 412 | switch (*pos) { | ||
| 413 | case WLAN_EID_SSID: | ||
| 414 | ssid = pos + 2; | ||
| 415 | ssid_len = pos[1]; | ||
| 416 | break; | ||
| 417 | case WLAN_EID_VENDOR_SPECIFIC: | ||
| 418 | if (pos[1] >= 4 && | ||
| 419 | pos[2] == 0x00 && pos[3] == 0x50 && | ||
| 420 | pos[4] == 0xf2 && pos[5] == 1) { | ||
| 421 | wpa = pos; | ||
| 422 | wpa_len = pos[1] + 2; | ||
| 423 | } | ||
| 424 | break; | ||
| 425 | case WLAN_EID_RSN: | ||
| 426 | rsn = pos; | ||
| 427 | rsn_len = pos[1] + 2; | ||
| 428 | break; | ||
| 429 | case WLAN_EID_DS_PARAMS: | ||
| 430 | if (pos[1] >= 1) | ||
| 431 | chan = pos[2]; | ||
| 432 | break; | ||
| 433 | } | ||
| 434 | left -= 2 + pos[1]; | ||
| 435 | pos += 2 + pos[1]; | ||
| 436 | } | ||
| 437 | |||
| 438 | if (wpa_len > MAX_WPA_IE_LEN) | ||
| 439 | wpa_len = MAX_WPA_IE_LEN; | ||
| 440 | if (rsn_len > MAX_WPA_IE_LEN) | ||
| 441 | rsn_len = MAX_WPA_IE_LEN; | ||
| 442 | if (ssid_len > sizeof(bss->ssid)) | ||
| 443 | ssid_len = sizeof(bss->ssid); | ||
| 444 | |||
| 445 | spin_lock(&local->lock); | ||
| 446 | bss = __hostap_get_bss(local, mgmt->bssid, ssid, ssid_len); | ||
| 447 | if (bss == NULL) | ||
| 448 | bss = __hostap_add_bss(local, mgmt->bssid, ssid, ssid_len); | ||
| 449 | if (bss) { | ||
| 450 | bss->last_update = jiffies; | ||
| 451 | bss->count++; | ||
| 452 | bss->capab_info = le16_to_cpu(mgmt->u.beacon.capab_info); | ||
| 453 | if (wpa) { | ||
| 454 | memcpy(bss->wpa_ie, wpa, wpa_len); | ||
| 455 | bss->wpa_ie_len = wpa_len; | ||
| 456 | } else | ||
| 457 | bss->wpa_ie_len = 0; | ||
| 458 | if (rsn) { | ||
| 459 | memcpy(bss->rsn_ie, rsn, rsn_len); | ||
| 460 | bss->rsn_ie_len = rsn_len; | ||
| 461 | } else | ||
| 462 | bss->rsn_ie_len = 0; | ||
| 463 | bss->chan = chan; | ||
| 464 | } | ||
| 465 | __hostap_expire_bss(local); | ||
| 466 | spin_unlock(&local->lock); | ||
| 467 | } | ||
| 468 | |||
| 469 | |||
| 470 | static int | ||
| 471 | hostap_rx_frame_mgmt(local_info_t *local, struct sk_buff *skb, | ||
| 472 | struct hostap_80211_rx_status *rx_stats, u16 type, | ||
| 473 | u16 stype) | ||
| 474 | { | ||
| 475 | if (local->iw_mode == IW_MODE_MASTER) | ||
| 476 | hostap_update_sta_ps(local, (struct ieee80211_hdr *) skb->data); | ||
| 477 | |||
| 478 | if (local->hostapd && type == IEEE80211_FTYPE_MGMT) { | ||
| 479 | if (stype == IEEE80211_STYPE_BEACON && | ||
| 480 | local->iw_mode == IW_MODE_MASTER) { | ||
| 481 | struct sk_buff *skb2; | ||
| 482 | /* Process beacon frames also in kernel driver to | ||
| 483 | * update STA(AP) table statistics */ | ||
| 484 | skb2 = skb_clone(skb, GFP_ATOMIC); | ||
| 485 | if (skb2) | ||
| 486 | hostap_rx(skb2->dev, skb2, rx_stats); | ||
| 487 | } | ||
| 488 | |||
| 489 | /* send management frames to the user space daemon for | ||
| 490 | * processing */ | ||
| 491 | local->apdevstats.rx_packets++; | ||
| 492 | local->apdevstats.rx_bytes += skb->len; | ||
| 493 | if (local->apdev == NULL) | ||
| 494 | return -1; | ||
| 495 | prism2_rx_80211(local->apdev, skb, rx_stats, PRISM2_RX_MGMT); | ||
| 496 | return 0; | ||
| 497 | } | ||
| 498 | |||
| 499 | if (local->iw_mode == IW_MODE_MASTER) { | ||
| 500 | if (type != IEEE80211_FTYPE_MGMT && | ||
| 501 | type != IEEE80211_FTYPE_CTL) { | ||
| 502 | printk(KERN_DEBUG "%s: unknown management frame " | ||
| 503 | "(type=0x%02x, stype=0x%02x) dropped\n", | ||
| 504 | skb->dev->name, type >> 2, stype >> 4); | ||
| 505 | return -1; | ||
| 506 | } | ||
| 507 | |||
| 508 | hostap_rx(skb->dev, skb, rx_stats); | ||
| 509 | return 0; | ||
| 510 | } else if (type == IEEE80211_FTYPE_MGMT && | ||
| 511 | (stype == IEEE80211_STYPE_BEACON || | ||
| 512 | stype == IEEE80211_STYPE_PROBE_RESP)) { | ||
| 513 | hostap_rx_sta_beacon(local, skb, stype); | ||
| 514 | return -1; | ||
| 515 | } else if (type == IEEE80211_FTYPE_MGMT && | ||
| 516 | (stype == IEEE80211_STYPE_ASSOC_RESP || | ||
| 517 | stype == IEEE80211_STYPE_REASSOC_RESP)) { | ||
| 518 | /* Ignore (Re)AssocResp silently since these are not currently | ||
| 519 | * needed but are still received when WPA/RSN mode is enabled. | ||
| 520 | */ | ||
| 521 | return -1; | ||
| 522 | } else { | ||
| 523 | printk(KERN_DEBUG "%s: hostap_rx_frame_mgmt: dropped unhandled" | ||
| 524 | " management frame in non-Host AP mode (type=%d:%d)\n", | ||
| 525 | skb->dev->name, type >> 2, stype >> 4); | ||
| 526 | return -1; | ||
| 527 | } | ||
| 528 | } | ||
| 529 | |||
| 530 | |||
| 531 | /* Called only as a tasklet (software IRQ) */ | ||
| 532 | static struct net_device *prism2_rx_get_wds(local_info_t *local, | ||
| 533 | u8 *addr) | ||
| 534 | { | ||
| 535 | struct hostap_interface *iface = NULL; | ||
| 536 | struct list_head *ptr; | ||
| 537 | |||
| 538 | read_lock_bh(&local->iface_lock); | ||
| 539 | list_for_each(ptr, &local->hostap_interfaces) { | ||
| 540 | iface = list_entry(ptr, struct hostap_interface, list); | ||
| 541 | if (iface->type == HOSTAP_INTERFACE_WDS && | ||
| 542 | memcmp(iface->u.wds.remote_addr, addr, ETH_ALEN) == 0) | ||
| 543 | break; | ||
| 544 | iface = NULL; | ||
| 545 | } | ||
| 546 | read_unlock_bh(&local->iface_lock); | ||
| 547 | |||
| 548 | return iface ? iface->dev : NULL; | ||
| 549 | } | ||
| 550 | |||
| 551 | |||
| 552 | static int | ||
| 553 | hostap_rx_frame_wds(local_info_t *local, struct ieee80211_hdr *hdr, u16 fc, | ||
| 554 | struct net_device **wds) | ||
| 555 | { | ||
| 556 | /* FIX: is this really supposed to accept WDS frames only in Master | ||
| 557 | * mode? What about Repeater or Managed with WDS frames? */ | ||
| 558 | if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) != | ||
| 559 | (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS) && | ||
| 560 | (local->iw_mode != IW_MODE_MASTER || !(fc & IEEE80211_FCTL_TODS))) | ||
| 561 | return 0; /* not a WDS frame */ | ||
| 562 | |||
| 563 | /* Possible WDS frame: either IEEE 802.11 compliant (if FromDS) | ||
| 564 | * or own non-standard frame with 4th address after payload */ | ||
| 565 | if (!ether_addr_equal(hdr->addr1, local->dev->dev_addr) && | ||
| 566 | (hdr->addr1[0] != 0xff || hdr->addr1[1] != 0xff || | ||
| 567 | hdr->addr1[2] != 0xff || hdr->addr1[3] != 0xff || | ||
| 568 | hdr->addr1[4] != 0xff || hdr->addr1[5] != 0xff)) { | ||
| 569 | /* RA (or BSSID) is not ours - drop */ | ||
| 570 | PDEBUG(DEBUG_EXTRA2, "%s: received WDS frame with " | ||
| 571 | "not own or broadcast %s=%pM\n", | ||
| 572 | local->dev->name, | ||
| 573 | fc & IEEE80211_FCTL_FROMDS ? "RA" : "BSSID", | ||
| 574 | hdr->addr1); | ||
| 575 | return -1; | ||
| 576 | } | ||
| 577 | |||
| 578 | /* check if the frame came from a registered WDS connection */ | ||
| 579 | *wds = prism2_rx_get_wds(local, hdr->addr2); | ||
| 580 | if (*wds == NULL && fc & IEEE80211_FCTL_FROMDS && | ||
| 581 | (local->iw_mode != IW_MODE_INFRA || | ||
| 582 | !(local->wds_type & HOSTAP_WDS_AP_CLIENT) || | ||
| 583 | memcmp(hdr->addr2, local->bssid, ETH_ALEN) != 0)) { | ||
| 584 | /* require that WDS link has been registered with TA or the | ||
| 585 | * frame is from current AP when using 'AP client mode' */ | ||
| 586 | PDEBUG(DEBUG_EXTRA, "%s: received WDS[4 addr] frame " | ||
| 587 | "from unknown TA=%pM\n", | ||
| 588 | local->dev->name, hdr->addr2); | ||
| 589 | if (local->ap && local->ap->autom_ap_wds) | ||
| 590 | hostap_wds_link_oper(local, hdr->addr2, WDS_ADD); | ||
| 591 | return -1; | ||
| 592 | } | ||
| 593 | |||
| 594 | if (*wds && !(fc & IEEE80211_FCTL_FROMDS) && local->ap && | ||
| 595 | hostap_is_sta_assoc(local->ap, hdr->addr2)) { | ||
| 596 | /* STA is actually associated with us even though it has a | ||
| 597 | * registered WDS link. Assume it is in 'AP client' mode. | ||
| 598 | * Since this is a 3-addr frame, assume it is not (bogus) WDS | ||
| 599 | * frame and process it like any normal ToDS frame from | ||
| 600 | * associated STA. */ | ||
| 601 | *wds = NULL; | ||
| 602 | } | ||
| 603 | |||
| 604 | return 0; | ||
| 605 | } | ||
| 606 | |||
| 607 | |||
| 608 | static int hostap_is_eapol_frame(local_info_t *local, struct sk_buff *skb) | ||
| 609 | { | ||
| 610 | struct net_device *dev = local->dev; | ||
| 611 | u16 fc, ethertype; | ||
| 612 | struct ieee80211_hdr *hdr; | ||
| 613 | u8 *pos; | ||
| 614 | |||
| 615 | if (skb->len < 24) | ||
| 616 | return 0; | ||
| 617 | |||
| 618 | hdr = (struct ieee80211_hdr *) skb->data; | ||
| 619 | fc = le16_to_cpu(hdr->frame_control); | ||
| 620 | |||
| 621 | /* check that the frame is unicast frame to us */ | ||
| 622 | if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == | ||
| 623 | IEEE80211_FCTL_TODS && | ||
| 624 | ether_addr_equal(hdr->addr1, dev->dev_addr) && | ||
| 625 | ether_addr_equal(hdr->addr3, dev->dev_addr)) { | ||
| 626 | /* ToDS frame with own addr BSSID and DA */ | ||
| 627 | } else if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == | ||
| 628 | IEEE80211_FCTL_FROMDS && | ||
| 629 | ether_addr_equal(hdr->addr1, dev->dev_addr)) { | ||
| 630 | /* FromDS frame with own addr as DA */ | ||
| 631 | } else | ||
| 632 | return 0; | ||
| 633 | |||
| 634 | if (skb->len < 24 + 8) | ||
| 635 | return 0; | ||
| 636 | |||
| 637 | /* check for port access entity Ethernet type */ | ||
| 638 | pos = skb->data + 24; | ||
| 639 | ethertype = (pos[6] << 8) | pos[7]; | ||
| 640 | if (ethertype == ETH_P_PAE) | ||
| 641 | return 1; | ||
| 642 | |||
| 643 | return 0; | ||
| 644 | } | ||
| 645 | |||
| 646 | |||
| 647 | /* Called only as a tasklet (software IRQ) */ | ||
| 648 | static int | ||
| 649 | hostap_rx_frame_decrypt(local_info_t *local, struct sk_buff *skb, | ||
| 650 | struct lib80211_crypt_data *crypt) | ||
| 651 | { | ||
| 652 | struct ieee80211_hdr *hdr; | ||
| 653 | int res, hdrlen; | ||
| 654 | |||
| 655 | if (crypt == NULL || crypt->ops->decrypt_mpdu == NULL) | ||
| 656 | return 0; | ||
| 657 | |||
| 658 | hdr = (struct ieee80211_hdr *) skb->data; | ||
| 659 | hdrlen = hostap_80211_get_hdrlen(hdr->frame_control); | ||
| 660 | |||
| 661 | if (local->tkip_countermeasures && | ||
| 662 | strcmp(crypt->ops->name, "TKIP") == 0) { | ||
| 663 | if (net_ratelimit()) { | ||
| 664 | printk(KERN_DEBUG "%s: TKIP countermeasures: dropped " | ||
| 665 | "received packet from %pM\n", | ||
| 666 | local->dev->name, hdr->addr2); | ||
| 667 | } | ||
| 668 | return -1; | ||
| 669 | } | ||
| 670 | |||
| 671 | atomic_inc(&crypt->refcnt); | ||
| 672 | res = crypt->ops->decrypt_mpdu(skb, hdrlen, crypt->priv); | ||
| 673 | atomic_dec(&crypt->refcnt); | ||
| 674 | if (res < 0) { | ||
| 675 | printk(KERN_DEBUG "%s: decryption failed (SA=%pM) res=%d\n", | ||
| 676 | local->dev->name, hdr->addr2, res); | ||
| 677 | local->comm_tallies.rx_discards_wep_undecryptable++; | ||
| 678 | return -1; | ||
| 679 | } | ||
| 680 | |||
| 681 | return res; | ||
| 682 | } | ||
| 683 | |||
| 684 | |||
| 685 | /* Called only as a tasklet (software IRQ) */ | ||
| 686 | static int | ||
| 687 | hostap_rx_frame_decrypt_msdu(local_info_t *local, struct sk_buff *skb, | ||
| 688 | int keyidx, struct lib80211_crypt_data *crypt) | ||
| 689 | { | ||
| 690 | struct ieee80211_hdr *hdr; | ||
| 691 | int res, hdrlen; | ||
| 692 | |||
| 693 | if (crypt == NULL || crypt->ops->decrypt_msdu == NULL) | ||
| 694 | return 0; | ||
| 695 | |||
| 696 | hdr = (struct ieee80211_hdr *) skb->data; | ||
| 697 | hdrlen = hostap_80211_get_hdrlen(hdr->frame_control); | ||
| 698 | |||
| 699 | atomic_inc(&crypt->refcnt); | ||
| 700 | res = crypt->ops->decrypt_msdu(skb, keyidx, hdrlen, crypt->priv); | ||
| 701 | atomic_dec(&crypt->refcnt); | ||
| 702 | if (res < 0) { | ||
| 703 | printk(KERN_DEBUG "%s: MSDU decryption/MIC verification failed" | ||
| 704 | " (SA=%pM keyidx=%d)\n", | ||
| 705 | local->dev->name, hdr->addr2, keyidx); | ||
| 706 | return -1; | ||
| 707 | } | ||
| 708 | |||
| 709 | return 0; | ||
| 710 | } | ||
| 711 | |||
| 712 | |||
| 713 | /* All received frames are sent to this function. @skb contains the frame in | ||
| 714 | * IEEE 802.11 format, i.e., in the format it was sent over air. | ||
| 715 | * This function is called only as a tasklet (software IRQ). */ | ||
| 716 | void hostap_80211_rx(struct net_device *dev, struct sk_buff *skb, | ||
| 717 | struct hostap_80211_rx_status *rx_stats) | ||
| 718 | { | ||
| 719 | struct hostap_interface *iface; | ||
| 720 | local_info_t *local; | ||
| 721 | struct ieee80211_hdr *hdr; | ||
| 722 | size_t hdrlen; | ||
| 723 | u16 fc, type, stype, sc; | ||
| 724 | struct net_device *wds = NULL; | ||
| 725 | unsigned int frag; | ||
| 726 | u8 *payload; | ||
| 727 | struct sk_buff *skb2 = NULL; | ||
| 728 | u16 ethertype; | ||
| 729 | int frame_authorized = 0; | ||
| 730 | int from_assoc_ap = 0; | ||
| 731 | u8 dst[ETH_ALEN]; | ||
| 732 | u8 src[ETH_ALEN]; | ||
| 733 | struct lib80211_crypt_data *crypt = NULL; | ||
| 734 | void *sta = NULL; | ||
| 735 | int keyidx = 0; | ||
| 736 | |||
| 737 | iface = netdev_priv(dev); | ||
| 738 | local = iface->local; | ||
| 739 | iface->stats.rx_packets++; | ||
| 740 | iface->stats.rx_bytes += skb->len; | ||
| 741 | |||
| 742 | /* dev is the master radio device; change this to be the default | ||
| 743 | * virtual interface (this may be changed to WDS device below) */ | ||
| 744 | dev = local->ddev; | ||
| 745 | iface = netdev_priv(dev); | ||
| 746 | |||
| 747 | hdr = (struct ieee80211_hdr *) skb->data; | ||
| 748 | |||
| 749 | if (skb->len < 10) | ||
| 750 | goto rx_dropped; | ||
| 751 | |||
| 752 | fc = le16_to_cpu(hdr->frame_control); | ||
| 753 | type = fc & IEEE80211_FCTL_FTYPE; | ||
| 754 | stype = fc & IEEE80211_FCTL_STYPE; | ||
| 755 | sc = le16_to_cpu(hdr->seq_ctrl); | ||
| 756 | frag = sc & IEEE80211_SCTL_FRAG; | ||
| 757 | hdrlen = hostap_80211_get_hdrlen(hdr->frame_control); | ||
| 758 | |||
| 759 | /* Put this code here so that we avoid duplicating it in all | ||
| 760 | * Rx paths. - Jean II */ | ||
| 761 | #ifdef IW_WIRELESS_SPY /* defined in iw_handler.h */ | ||
| 762 | /* If spy monitoring on */ | ||
| 763 | if (iface->spy_data.spy_number > 0) { | ||
| 764 | struct iw_quality wstats; | ||
| 765 | wstats.level = rx_stats->signal; | ||
| 766 | wstats.noise = rx_stats->noise; | ||
| 767 | wstats.updated = IW_QUAL_LEVEL_UPDATED | IW_QUAL_NOISE_UPDATED | ||
| 768 | | IW_QUAL_QUAL_INVALID | IW_QUAL_DBM; | ||
| 769 | /* Update spy records */ | ||
| 770 | wireless_spy_update(dev, hdr->addr2, &wstats); | ||
| 771 | } | ||
| 772 | #endif /* IW_WIRELESS_SPY */ | ||
| 773 | hostap_update_rx_stats(local->ap, hdr, rx_stats); | ||
| 774 | |||
| 775 | if (local->iw_mode == IW_MODE_MONITOR) { | ||
| 776 | monitor_rx(dev, skb, rx_stats); | ||
| 777 | return; | ||
| 778 | } | ||
| 779 | |||
| 780 | if (local->host_decrypt) { | ||
| 781 | int idx = 0; | ||
| 782 | if (skb->len >= hdrlen + 3) | ||
| 783 | idx = skb->data[hdrlen + 3] >> 6; | ||
| 784 | crypt = local->crypt_info.crypt[idx]; | ||
| 785 | sta = NULL; | ||
| 786 | |||
| 787 | /* Use station specific key to override default keys if the | ||
| 788 | * receiver address is a unicast address ("individual RA"). If | ||
| 789 | * bcrx_sta_key parameter is set, station specific key is used | ||
| 790 | * even with broad/multicast targets (this is against IEEE | ||
| 791 | * 802.11, but makes it easier to use different keys with | ||
| 792 | * stations that do not support WEP key mapping). */ | ||
| 793 | |||
| 794 | if (!(hdr->addr1[0] & 0x01) || local->bcrx_sta_key) | ||
| 795 | (void) hostap_handle_sta_crypto(local, hdr, &crypt, | ||
| 796 | &sta); | ||
| 797 | |||
| 798 | /* allow NULL decrypt to indicate an station specific override | ||
| 799 | * for default encryption */ | ||
| 800 | if (crypt && (crypt->ops == NULL || | ||
| 801 | crypt->ops->decrypt_mpdu == NULL)) | ||
| 802 | crypt = NULL; | ||
| 803 | |||
| 804 | if (!crypt && (fc & IEEE80211_FCTL_PROTECTED)) { | ||
| 805 | #if 0 | ||
| 806 | /* This seems to be triggered by some (multicast?) | ||
| 807 | * frames from other than current BSS, so just drop the | ||
| 808 | * frames silently instead of filling system log with | ||
| 809 | * these reports. */ | ||
| 810 | printk(KERN_DEBUG "%s: WEP decryption failed (not set)" | ||
| 811 | " (SA=%pM)\n", | ||
| 812 | local->dev->name, hdr->addr2); | ||
| 813 | #endif | ||
| 814 | local->comm_tallies.rx_discards_wep_undecryptable++; | ||
| 815 | goto rx_dropped; | ||
| 816 | } | ||
| 817 | } | ||
| 818 | |||
| 819 | if (type != IEEE80211_FTYPE_DATA) { | ||
| 820 | if (type == IEEE80211_FTYPE_MGMT && | ||
| 821 | stype == IEEE80211_STYPE_AUTH && | ||
| 822 | fc & IEEE80211_FCTL_PROTECTED && local->host_decrypt && | ||
| 823 | (keyidx = hostap_rx_frame_decrypt(local, skb, crypt)) < 0) | ||
| 824 | { | ||
| 825 | printk(KERN_DEBUG "%s: failed to decrypt mgmt::auth " | ||
| 826 | "from %pM\n", dev->name, hdr->addr2); | ||
| 827 | /* TODO: could inform hostapd about this so that it | ||
| 828 | * could send auth failure report */ | ||
| 829 | goto rx_dropped; | ||
| 830 | } | ||
| 831 | |||
| 832 | if (hostap_rx_frame_mgmt(local, skb, rx_stats, type, stype)) | ||
| 833 | goto rx_dropped; | ||
| 834 | else | ||
| 835 | goto rx_exit; | ||
| 836 | } | ||
| 837 | |||
| 838 | /* Data frame - extract src/dst addresses */ | ||
| 839 | if (skb->len < IEEE80211_DATA_HDR3_LEN) | ||
| 840 | goto rx_dropped; | ||
| 841 | |||
| 842 | switch (fc & (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) { | ||
| 843 | case IEEE80211_FCTL_FROMDS: | ||
| 844 | memcpy(dst, hdr->addr1, ETH_ALEN); | ||
| 845 | memcpy(src, hdr->addr3, ETH_ALEN); | ||
| 846 | break; | ||
| 847 | case IEEE80211_FCTL_TODS: | ||
| 848 | memcpy(dst, hdr->addr3, ETH_ALEN); | ||
| 849 | memcpy(src, hdr->addr2, ETH_ALEN); | ||
| 850 | break; | ||
| 851 | case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS: | ||
| 852 | if (skb->len < IEEE80211_DATA_HDR4_LEN) | ||
| 853 | goto rx_dropped; | ||
| 854 | memcpy(dst, hdr->addr3, ETH_ALEN); | ||
| 855 | memcpy(src, hdr->addr4, ETH_ALEN); | ||
| 856 | break; | ||
| 857 | default: | ||
| 858 | memcpy(dst, hdr->addr1, ETH_ALEN); | ||
| 859 | memcpy(src, hdr->addr2, ETH_ALEN); | ||
| 860 | break; | ||
| 861 | } | ||
| 862 | |||
| 863 | if (hostap_rx_frame_wds(local, hdr, fc, &wds)) | ||
| 864 | goto rx_dropped; | ||
| 865 | if (wds) | ||
| 866 | skb->dev = dev = wds; | ||
| 867 | |||
| 868 | if (local->iw_mode == IW_MODE_MASTER && !wds && | ||
| 869 | (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == | ||
| 870 | IEEE80211_FCTL_FROMDS && | ||
| 871 | local->stadev && | ||
| 872 | memcmp(hdr->addr2, local->assoc_ap_addr, ETH_ALEN) == 0) { | ||
| 873 | /* Frame from BSSID of the AP for which we are a client */ | ||
| 874 | skb->dev = dev = local->stadev; | ||
| 875 | from_assoc_ap = 1; | ||
| 876 | } | ||
| 877 | |||
| 878 | if ((local->iw_mode == IW_MODE_MASTER || | ||
| 879 | local->iw_mode == IW_MODE_REPEAT) && | ||
| 880 | !from_assoc_ap) { | ||
| 881 | switch (hostap_handle_sta_rx(local, dev, skb, rx_stats, | ||
| 882 | wds != NULL)) { | ||
| 883 | case AP_RX_CONTINUE_NOT_AUTHORIZED: | ||
| 884 | frame_authorized = 0; | ||
| 885 | break; | ||
| 886 | case AP_RX_CONTINUE: | ||
| 887 | frame_authorized = 1; | ||
| 888 | break; | ||
| 889 | case AP_RX_DROP: | ||
| 890 | goto rx_dropped; | ||
| 891 | case AP_RX_EXIT: | ||
| 892 | goto rx_exit; | ||
| 893 | } | ||
| 894 | } | ||
| 895 | |||
| 896 | /* Nullfunc frames may have PS-bit set, so they must be passed to | ||
| 897 | * hostap_handle_sta_rx() before being dropped here. */ | ||
| 898 | if (stype != IEEE80211_STYPE_DATA && | ||
| 899 | stype != IEEE80211_STYPE_DATA_CFACK && | ||
| 900 | stype != IEEE80211_STYPE_DATA_CFPOLL && | ||
| 901 | stype != IEEE80211_STYPE_DATA_CFACKPOLL) { | ||
| 902 | if (stype != IEEE80211_STYPE_NULLFUNC) | ||
| 903 | printk(KERN_DEBUG "%s: RX: dropped data frame " | ||
| 904 | "with no data (type=0x%02x, subtype=0x%02x)\n", | ||
| 905 | dev->name, type >> 2, stype >> 4); | ||
| 906 | goto rx_dropped; | ||
| 907 | } | ||
| 908 | |||
| 909 | /* skb: hdr + (possibly fragmented, possibly encrypted) payload */ | ||
| 910 | |||
| 911 | if (local->host_decrypt && (fc & IEEE80211_FCTL_PROTECTED) && | ||
| 912 | (keyidx = hostap_rx_frame_decrypt(local, skb, crypt)) < 0) | ||
| 913 | goto rx_dropped; | ||
| 914 | hdr = (struct ieee80211_hdr *) skb->data; | ||
| 915 | |||
| 916 | /* skb: hdr + (possibly fragmented) plaintext payload */ | ||
| 917 | |||
| 918 | if (local->host_decrypt && (fc & IEEE80211_FCTL_PROTECTED) && | ||
| 919 | (frag != 0 || (fc & IEEE80211_FCTL_MOREFRAGS))) { | ||
| 920 | int flen; | ||
| 921 | struct sk_buff *frag_skb = | ||
| 922 | prism2_frag_cache_get(local, hdr); | ||
| 923 | if (!frag_skb) { | ||
| 924 | printk(KERN_DEBUG "%s: Rx cannot get skb from " | ||
| 925 | "fragment cache (morefrag=%d seq=%u frag=%u)\n", | ||
| 926 | dev->name, (fc & IEEE80211_FCTL_MOREFRAGS) != 0, | ||
| 927 | (sc & IEEE80211_SCTL_SEQ) >> 4, frag); | ||
| 928 | goto rx_dropped; | ||
| 929 | } | ||
| 930 | |||
| 931 | flen = skb->len; | ||
| 932 | if (frag != 0) | ||
| 933 | flen -= hdrlen; | ||
| 934 | |||
| 935 | if (frag_skb->tail + flen > frag_skb->end) { | ||
| 936 | printk(KERN_WARNING "%s: host decrypted and " | ||
| 937 | "reassembled frame did not fit skb\n", | ||
| 938 | dev->name); | ||
| 939 | prism2_frag_cache_invalidate(local, hdr); | ||
| 940 | goto rx_dropped; | ||
| 941 | } | ||
| 942 | |||
| 943 | if (frag == 0) { | ||
| 944 | /* copy first fragment (including full headers) into | ||
| 945 | * beginning of the fragment cache skb */ | ||
| 946 | skb_copy_from_linear_data(skb, skb_put(frag_skb, flen), | ||
| 947 | flen); | ||
| 948 | } else { | ||
| 949 | /* append frame payload to the end of the fragment | ||
| 950 | * cache skb */ | ||
| 951 | skb_copy_from_linear_data_offset(skb, hdrlen, | ||
| 952 | skb_put(frag_skb, | ||
| 953 | flen), flen); | ||
| 954 | } | ||
| 955 | dev_kfree_skb(skb); | ||
| 956 | skb = NULL; | ||
| 957 | |||
| 958 | if (fc & IEEE80211_FCTL_MOREFRAGS) { | ||
| 959 | /* more fragments expected - leave the skb in fragment | ||
| 960 | * cache for now; it will be delivered to upper layers | ||
| 961 | * after all fragments have been received */ | ||
| 962 | goto rx_exit; | ||
| 963 | } | ||
| 964 | |||
| 965 | /* this was the last fragment and the frame will be | ||
| 966 | * delivered, so remove skb from fragment cache */ | ||
| 967 | skb = frag_skb; | ||
| 968 | hdr = (struct ieee80211_hdr *) skb->data; | ||
| 969 | prism2_frag_cache_invalidate(local, hdr); | ||
| 970 | } | ||
| 971 | |||
| 972 | /* skb: hdr + (possible reassembled) full MSDU payload; possibly still | ||
| 973 | * encrypted/authenticated */ | ||
| 974 | |||
| 975 | if (local->host_decrypt && (fc & IEEE80211_FCTL_PROTECTED) && | ||
| 976 | hostap_rx_frame_decrypt_msdu(local, skb, keyidx, crypt)) | ||
| 977 | goto rx_dropped; | ||
| 978 | |||
| 979 | hdr = (struct ieee80211_hdr *) skb->data; | ||
| 980 | if (crypt && !(fc & IEEE80211_FCTL_PROTECTED) && !local->open_wep) { | ||
| 981 | if (local->ieee_802_1x && | ||
| 982 | hostap_is_eapol_frame(local, skb)) { | ||
| 983 | /* pass unencrypted EAPOL frames even if encryption is | ||
| 984 | * configured */ | ||
| 985 | PDEBUG(DEBUG_EXTRA2, "%s: RX: IEEE 802.1X - passing " | ||
| 986 | "unencrypted EAPOL frame\n", local->dev->name); | ||
| 987 | } else { | ||
| 988 | printk(KERN_DEBUG "%s: encryption configured, but RX " | ||
| 989 | "frame not encrypted (SA=%pM)\n", | ||
| 990 | local->dev->name, hdr->addr2); | ||
| 991 | goto rx_dropped; | ||
| 992 | } | ||
| 993 | } | ||
| 994 | |||
| 995 | if (local->drop_unencrypted && !(fc & IEEE80211_FCTL_PROTECTED) && | ||
| 996 | !hostap_is_eapol_frame(local, skb)) { | ||
| 997 | if (net_ratelimit()) { | ||
| 998 | printk(KERN_DEBUG "%s: dropped unencrypted RX data " | ||
| 999 | "frame from %pM (drop_unencrypted=1)\n", | ||
| 1000 | dev->name, hdr->addr2); | ||
| 1001 | } | ||
| 1002 | goto rx_dropped; | ||
| 1003 | } | ||
| 1004 | |||
| 1005 | /* skb: hdr + (possible reassembled) full plaintext payload */ | ||
| 1006 | |||
| 1007 | payload = skb->data + hdrlen; | ||
| 1008 | ethertype = (payload[6] << 8) | payload[7]; | ||
| 1009 | |||
| 1010 | /* If IEEE 802.1X is used, check whether the port is authorized to send | ||
| 1011 | * the received frame. */ | ||
| 1012 | if (local->ieee_802_1x && local->iw_mode == IW_MODE_MASTER) { | ||
| 1013 | if (ethertype == ETH_P_PAE) { | ||
| 1014 | PDEBUG(DEBUG_EXTRA2, "%s: RX: IEEE 802.1X frame\n", | ||
| 1015 | dev->name); | ||
| 1016 | if (local->hostapd && local->apdev) { | ||
| 1017 | /* Send IEEE 802.1X frames to the user | ||
| 1018 | * space daemon for processing */ | ||
| 1019 | prism2_rx_80211(local->apdev, skb, rx_stats, | ||
| 1020 | PRISM2_RX_MGMT); | ||
| 1021 | local->apdevstats.rx_packets++; | ||
| 1022 | local->apdevstats.rx_bytes += skb->len; | ||
| 1023 | goto rx_exit; | ||
| 1024 | } | ||
| 1025 | } else if (!frame_authorized) { | ||
| 1026 | printk(KERN_DEBUG "%s: dropped frame from " | ||
| 1027 | "unauthorized port (IEEE 802.1X): " | ||
| 1028 | "ethertype=0x%04x\n", | ||
| 1029 | dev->name, ethertype); | ||
| 1030 | goto rx_dropped; | ||
| 1031 | } | ||
| 1032 | } | ||
| 1033 | |||
| 1034 | /* convert hdr + possible LLC headers into Ethernet header */ | ||
| 1035 | if (skb->len - hdrlen >= 8 && | ||
| 1036 | ((memcmp(payload, rfc1042_header, 6) == 0 && | ||
| 1037 | ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) || | ||
| 1038 | memcmp(payload, bridge_tunnel_header, 6) == 0)) { | ||
| 1039 | /* remove RFC1042 or Bridge-Tunnel encapsulation and | ||
| 1040 | * replace EtherType */ | ||
| 1041 | skb_pull(skb, hdrlen + 6); | ||
| 1042 | memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN); | ||
| 1043 | memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN); | ||
| 1044 | } else { | ||
| 1045 | __be16 len; | ||
| 1046 | /* Leave Ethernet header part of hdr and full payload */ | ||
| 1047 | skb_pull(skb, hdrlen); | ||
| 1048 | len = htons(skb->len); | ||
| 1049 | memcpy(skb_push(skb, 2), &len, 2); | ||
| 1050 | memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN); | ||
| 1051 | memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN); | ||
| 1052 | } | ||
| 1053 | |||
| 1054 | if (wds && ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == | ||
| 1055 | IEEE80211_FCTL_TODS) && | ||
| 1056 | skb->len >= ETH_HLEN + ETH_ALEN) { | ||
| 1057 | /* Non-standard frame: get addr4 from its bogus location after | ||
| 1058 | * the payload */ | ||
| 1059 | skb_copy_from_linear_data_offset(skb, skb->len - ETH_ALEN, | ||
| 1060 | skb->data + ETH_ALEN, | ||
| 1061 | ETH_ALEN); | ||
| 1062 | skb_trim(skb, skb->len - ETH_ALEN); | ||
| 1063 | } | ||
| 1064 | |||
| 1065 | dev->stats.rx_packets++; | ||
| 1066 | dev->stats.rx_bytes += skb->len; | ||
| 1067 | |||
| 1068 | if (local->iw_mode == IW_MODE_MASTER && !wds && | ||
| 1069 | local->ap->bridge_packets) { | ||
| 1070 | if (dst[0] & 0x01) { | ||
| 1071 | /* copy multicast frame both to the higher layers and | ||
| 1072 | * to the wireless media */ | ||
| 1073 | local->ap->bridged_multicast++; | ||
| 1074 | skb2 = skb_clone(skb, GFP_ATOMIC); | ||
| 1075 | if (skb2 == NULL) | ||
| 1076 | printk(KERN_DEBUG "%s: skb_clone failed for " | ||
| 1077 | "multicast frame\n", dev->name); | ||
| 1078 | } else if (hostap_is_sta_authorized(local->ap, dst)) { | ||
| 1079 | /* send frame directly to the associated STA using | ||
| 1080 | * wireless media and not passing to higher layers */ | ||
| 1081 | local->ap->bridged_unicast++; | ||
| 1082 | skb2 = skb; | ||
| 1083 | skb = NULL; | ||
| 1084 | } | ||
| 1085 | } | ||
| 1086 | |||
| 1087 | if (skb2 != NULL) { | ||
| 1088 | /* send to wireless media */ | ||
| 1089 | skb2->dev = dev; | ||
| 1090 | skb2->protocol = cpu_to_be16(ETH_P_802_3); | ||
| 1091 | skb_reset_mac_header(skb2); | ||
| 1092 | skb_reset_network_header(skb2); | ||
| 1093 | /* skb2->network_header += ETH_HLEN; */ | ||
| 1094 | dev_queue_xmit(skb2); | ||
| 1095 | } | ||
| 1096 | |||
| 1097 | if (skb) { | ||
| 1098 | skb->protocol = eth_type_trans(skb, dev); | ||
| 1099 | memset(skb->cb, 0, sizeof(skb->cb)); | ||
| 1100 | netif_rx(skb); | ||
| 1101 | } | ||
| 1102 | |||
| 1103 | rx_exit: | ||
| 1104 | if (sta) | ||
| 1105 | hostap_handle_sta_release(sta); | ||
| 1106 | return; | ||
| 1107 | |||
| 1108 | rx_dropped: | ||
| 1109 | dev_kfree_skb(skb); | ||
| 1110 | |||
| 1111 | dev->stats.rx_dropped++; | ||
| 1112 | goto rx_exit; | ||
| 1113 | } | ||
| 1114 | |||
| 1115 | |||
| 1116 | EXPORT_SYMBOL(hostap_80211_rx); | ||
| @@ -1,554 +0,0 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | #include <linux/slab.h> | ||
| 3 | #include <linux/export.h> | ||
| 4 | #include <linux/etherdevice.h> | ||
| 5 | |||
| 6 | #include "hostap_80211.h" | ||
| 7 | #include "hostap_common.h" | ||
| 8 | #include "hostap_wlan.h" | ||
| 9 | #include "hostap.h" | ||
| 10 | #include "hostap_ap.h" | ||
| 11 | |||
| 12 | /* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */ | ||
| 13 | /* Ethernet-II snap header (RFC1042 for most EtherTypes) */ | ||
| 14 | static unsigned char rfc1042_header[] = | ||
| 15 | { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; | ||
| 16 | /* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */ | ||
| 17 | static unsigned char bridge_tunnel_header[] = | ||
| 18 | { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 }; | ||
| 19 | /* No encapsulation header if EtherType < 0x600 (=length) */ | ||
| 20 | |||
| 21 | void hostap_dump_tx_80211(const char *name, struct sk_buff *skb) | ||
| 22 | { | ||
| 23 | struct ieee80211_hdr *hdr; | ||
| 24 | u16 fc; | ||
| 25 | |||
| 26 | hdr = (struct ieee80211_hdr *) skb->data; | ||
| 27 | |||
| 28 | printk(KERN_DEBUG "%s: TX len=%d jiffies=%ld\n", | ||
| 29 | name, skb->len, jiffies); | ||
| 30 | |||
| 31 | if (skb->len < 2) | ||
| 32 | return; | ||
| 33 | |||
| 34 | fc = le16_to_cpu(hdr->frame_control); | ||
| 35 | printk(KERN_DEBUG " FC=0x%04x (type=%d:%d)%s%s", | ||
| 36 | fc, (fc & IEEE80211_FCTL_FTYPE) >> 2, | ||
| 37 | (fc & IEEE80211_FCTL_STYPE) >> 4, | ||
| 38 | fc & IEEE80211_FCTL_TODS ? " [ToDS]" : "", | ||
| 39 | fc & IEEE80211_FCTL_FROMDS ? " [FromDS]" : ""); | ||
| 40 | |||
| 41 | if (skb->len < IEEE80211_DATA_HDR3_LEN) { | ||
| 42 | printk("\n"); | ||
| 43 | return; | ||
| 44 | } | ||
| 45 | |||
| 46 | printk(" dur=0x%04x seq=0x%04x\n", le16_to_cpu(hdr->duration_id), | ||
| 47 | le16_to_cpu(hdr->seq_ctrl)); | ||
| 48 | |||
| 49 | printk(KERN_DEBUG " A1=%pM", hdr->addr1); | ||
| 50 | printk(" A2=%pM", hdr->addr2); | ||
| 51 | printk(" A3=%pM", hdr->addr3); | ||
| 52 | if (skb->len >= 30) | ||
| 53 | printk(" A4=%pM", hdr->addr4); | ||
| 54 | printk("\n"); | ||
| 55 | } | ||
| 56 | |||
| 57 | |||
| 58 | /* hard_start_xmit function for data interfaces (wlan#, wlan#wds#, wlan#sta) | ||
| 59 | * Convert Ethernet header into a suitable IEEE 802.11 header depending on | ||
| 60 | * device configuration. */ | ||
| 61 | netdev_tx_t hostap_data_start_xmit(struct sk_buff *skb, | ||
| 62 | struct net_device *dev) | ||
| 63 | { | ||
| 64 | struct hostap_interface *iface; | ||
| 65 | local_info_t *local; | ||
| 66 | int need_headroom, need_tailroom = 0; | ||
| 67 | struct ieee80211_hdr hdr; | ||
| 68 | u16 fc, ethertype = 0; | ||
| 69 | enum { | ||
| 70 | WDS_NO = 0, WDS_OWN_FRAME, WDS_COMPLIANT_FRAME | ||
| 71 | } use_wds = WDS_NO; | ||
| 72 | u8 *encaps_data; | ||
| 73 | int hdr_len, encaps_len, skip_header_bytes; | ||
| 74 | int to_assoc_ap = 0; | ||
| 75 | struct hostap_skb_tx_data *meta; | ||
| 76 | |||
| 77 | iface = netdev_priv(dev); | ||
| 78 | local = iface->local; | ||
| 79 | |||
| 80 | if (skb->len < ETH_HLEN) { | ||
| 81 | printk(KERN_DEBUG "%s: hostap_data_start_xmit: short skb " | ||
| 82 | "(len=%d)\n", dev->name, skb->len); | ||
| 83 | kfree_skb(skb); | ||
| 84 | return NETDEV_TX_OK; | ||
| 85 | } | ||
| 86 | |||
| 87 | if (local->ddev != dev) { | ||
| 88 | use_wds = (local->iw_mode == IW_MODE_MASTER && | ||
| 89 | !(local->wds_type & HOSTAP_WDS_STANDARD_FRAME)) ? | ||
| 90 | WDS_OWN_FRAME : WDS_COMPLIANT_FRAME; | ||
| 91 | if (dev == local->stadev) { | ||
| 92 | to_assoc_ap = 1; | ||
| 93 | use_wds = WDS_NO; | ||
| 94 | } else if (dev == local->apdev) { | ||
| 95 | printk(KERN_DEBUG "%s: prism2_tx: trying to use " | ||
| 96 | "AP device with Ethernet net dev\n", dev->name); | ||
| 97 | kfree_skb(skb); | ||
| 98 | return NETDEV_TX_OK; | ||
| 99 | } | ||
| 100 | } else { | ||
| 101 | if (local->iw_mode == IW_MODE_REPEAT) { | ||
| 102 | printk(KERN_DEBUG "%s: prism2_tx: trying to use " | ||
| 103 | "non-WDS link in Repeater mode\n", dev->name); | ||
| 104 | kfree_skb(skb); | ||
| 105 | return NETDEV_TX_OK; | ||
| 106 | } else if (local->iw_mode == IW_MODE_INFRA && | ||
| 107 | (local->wds_type & HOSTAP_WDS_AP_CLIENT) && | ||
| 108 | !ether_addr_equal(skb->data + ETH_ALEN, dev->dev_addr)) { | ||
| 109 | /* AP client mode: send frames with foreign src addr | ||
| 110 | * using 4-addr WDS frames */ | ||
| 111 | use_wds = WDS_COMPLIANT_FRAME; | ||
| 112 | } | ||
| 113 | } | ||
| 114 | |||
| 115 | /* Incoming skb->data: dst_addr[6], src_addr[6], proto[2], payload | ||
| 116 | * ==> | ||
| 117 | * Prism2 TX frame with 802.11 header: | ||
| 118 | * txdesc (address order depending on used mode; includes dst_addr and | ||
| 119 | * src_addr), possible encapsulation (RFC1042/Bridge-Tunnel; | ||
| 120 | * proto[2], payload {, possible addr4[6]} */ | ||
| 121 | |||
| 122 | ethertype = (skb->data[12] << 8) | skb->data[13]; | ||
| 123 | |||
| 124 | memset(&hdr, 0, sizeof(hdr)); | ||
| 125 | |||
| 126 | /* Length of data after IEEE 802.11 header */ | ||
| 127 | encaps_data = NULL; | ||
| 128 | encaps_len = 0; | ||
| 129 | skip_header_bytes = ETH_HLEN; | ||
| 130 | if (ethertype == ETH_P_AARP || ethertype == ETH_P_IPX) { | ||
| 131 | encaps_data = bridge_tunnel_header; | ||
| 132 | encaps_len = sizeof(bridge_tunnel_header); | ||
| 133 | skip_header_bytes -= 2; | ||
| 134 | } else if (ethertype >= 0x600) { | ||
| 135 | encaps_data = rfc1042_header; | ||
| 136 | encaps_len = sizeof(rfc1042_header); | ||
| 137 | skip_header_bytes -= 2; | ||
| 138 | } | ||
| 139 | |||
| 140 | fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA; | ||
| 141 | hdr_len = IEEE80211_DATA_HDR3_LEN; | ||
| 142 | |||
| 143 | if (use_wds != WDS_NO) { | ||
| 144 | /* Note! Prism2 station firmware has problems with sending real | ||
| 145 | * 802.11 frames with four addresses; until these problems can | ||
| 146 | * be fixed or worked around, 4-addr frames needed for WDS are | ||
| 147 | * using incompatible format: FromDS flag is not set and the | ||
| 148 | * fourth address is added after the frame payload; it is | ||
| 149 | * assumed, that the receiving station knows how to handle this | ||
| 150 | * frame format */ | ||
| 151 | |||
| 152 | if (use_wds == WDS_COMPLIANT_FRAME) { | ||
| 153 | fc |= IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS; | ||
| 154 | /* From&To DS: Addr1 = RA, Addr2 = TA, Addr3 = DA, | ||
| 155 | * Addr4 = SA */ | ||
| 156 | skb_copy_from_linear_data_offset(skb, ETH_ALEN, | ||
| 157 | &hdr.addr4, ETH_ALEN); | ||
| 158 | hdr_len += ETH_ALEN; | ||
| 159 | } else { | ||
| 160 | /* bogus 4-addr format to workaround Prism2 station | ||
| 161 | * f/w bug */ | ||
| 162 | fc |= IEEE80211_FCTL_TODS; | ||
| 163 | /* From DS: Addr1 = DA (used as RA), | ||
| 164 | * Addr2 = BSSID (used as TA), Addr3 = SA (used as DA), | ||
| 165 | */ | ||
| 166 | |||
| 167 | /* SA from skb->data + ETH_ALEN will be added after | ||
| 168 | * frame payload; use hdr.addr4 as a temporary buffer | ||
| 169 | */ | ||
| 170 | skb_copy_from_linear_data_offset(skb, ETH_ALEN, | ||
| 171 | &hdr.addr4, ETH_ALEN); | ||
| 172 | need_tailroom += ETH_ALEN; | ||
| 173 | } | ||
| 174 | |||
| 175 | /* send broadcast and multicast frames to broadcast RA, if | ||
| 176 | * configured; otherwise, use unicast RA of the WDS link */ | ||
| 177 | if ((local->wds_type & HOSTAP_WDS_BROADCAST_RA) && | ||
| 178 | is_multicast_ether_addr(skb->data)) | ||
| 179 | eth_broadcast_addr(hdr.addr1); | ||
| 180 | else if (iface->type == HOSTAP_INTERFACE_WDS) | ||
| 181 | memcpy(&hdr.addr1, iface->u.wds.remote_addr, | ||
| 182 | ETH_ALEN); | ||
| 183 | else | ||
| 184 | memcpy(&hdr.addr1, local->bssid, ETH_ALEN); | ||
| 185 | memcpy(&hdr.addr2, dev->dev_addr, ETH_ALEN); | ||
| 186 | skb_copy_from_linear_data(skb, &hdr.addr3, ETH_ALEN); | ||
| 187 | } else if (local->iw_mode == IW_MODE_MASTER && !to_assoc_ap) { | ||
| 188 | fc |= IEEE80211_FCTL_FROMDS; | ||
| 189 | /* From DS: Addr1 = DA, Addr2 = BSSID, Addr3 = SA */ | ||
| 190 | skb_copy_from_linear_data(skb, &hdr.addr1, ETH_ALEN); | ||
| 191 | memcpy(&hdr.addr2, dev->dev_addr, ETH_ALEN); | ||
| 192 | skb_copy_from_linear_data_offset(skb, ETH_ALEN, &hdr.addr3, | ||
| 193 | ETH_ALEN); | ||
| 194 | } else if (local->iw_mode == IW_MODE_INFRA || to_assoc_ap) { | ||
| 195 | fc |= IEEE80211_FCTL_TODS; | ||
| 196 | /* To DS: Addr1 = BSSID, Addr2 = SA, Addr3 = DA */ | ||
| 197 | memcpy(&hdr.addr1, to_assoc_ap ? | ||
| 198 | local->assoc_ap_addr : local->bssid, ETH_ALEN); | ||
| 199 | skb_copy_from_linear_data_offset(skb, ETH_ALEN, &hdr.addr2, | ||
| 200 | ETH_ALEN); | ||
| 201 | skb_copy_from_linear_data(skb, &hdr.addr3, ETH_ALEN); | ||
| 202 | } else if (local->iw_mode == IW_MODE_ADHOC) { | ||
| 203 | /* not From/To DS: Addr1 = DA, Addr2 = SA, Addr3 = BSSID */ | ||
| 204 | skb_copy_from_linear_data(skb, &hdr.addr1, ETH_ALEN); | ||
| 205 | skb_copy_from_linear_data_offset(skb, ETH_ALEN, &hdr.addr2, | ||
| 206 | ETH_ALEN); | ||
| 207 | memcpy(&hdr.addr3, local->bssid, ETH_ALEN); | ||
| 208 | } | ||
| 209 | |||
| 210 | hdr.frame_control = cpu_to_le16(fc); | ||
| 211 | |||
| 212 | skb_pull(skb, skip_header_bytes); | ||
| 213 | need_headroom = local->func->need_tx_headroom + hdr_len + encaps_len; | ||
| 214 | if (skb_tailroom(skb) < need_tailroom) { | ||
| 215 | skb = skb_unshare(skb, GFP_ATOMIC); | ||
| 216 | if (skb == NULL) { | ||
| 217 | iface->stats.tx_dropped++; | ||
| 218 | return NETDEV_TX_OK; | ||
| 219 | } | ||
| 220 | if (pskb_expand_head(skb, need_headroom, need_tailroom, | ||
| 221 | GFP_ATOMIC)) { | ||
| 222 | kfree_skb(skb); | ||
| 223 | iface->stats.tx_dropped++; | ||
| 224 | return NETDEV_TX_OK; | ||
| 225 | } | ||
| 226 | } else if (skb_headroom(skb) < need_headroom) { | ||
| 227 | struct sk_buff *tmp = skb; | ||
| 228 | skb = skb_realloc_headroom(skb, need_headroom); | ||
| 229 | kfree_skb(tmp); | ||
| 230 | if (skb == NULL) { | ||
| 231 | iface->stats.tx_dropped++; | ||
| 232 | return NETDEV_TX_OK; | ||
| 233 | } | ||
| 234 | } else { | ||
| 235 | skb = skb_unshare(skb, GFP_ATOMIC); | ||
| 236 | if (skb == NULL) { | ||
| 237 | iface->stats.tx_dropped++; | ||
| 238 | return NETDEV_TX_OK; | ||
| 239 | } | ||
| 240 | } | ||
| 241 | |||
| 242 | if (encaps_data) | ||
| 243 | memcpy(skb_push(skb, encaps_len), encaps_data, encaps_len); | ||
| 244 | memcpy(skb_push(skb, hdr_len), &hdr, hdr_len); | ||
| 245 | if (use_wds == WDS_OWN_FRAME) { | ||
| 246 | skb_put_data(skb, &hdr.addr4, ETH_ALEN); | ||
| 247 | } | ||
| 248 | |||
| 249 | iface->stats.tx_packets++; | ||
| 250 | iface->stats.tx_bytes += skb->len; | ||
| 251 | |||
| 252 | skb_reset_mac_header(skb); | ||
| 253 | meta = (struct hostap_skb_tx_data *) skb->cb; | ||
| 254 | memset(meta, 0, sizeof(*meta)); | ||
| 255 | meta->magic = HOSTAP_SKB_TX_DATA_MAGIC; | ||
| 256 | if (use_wds) | ||
| 257 | meta->flags |= HOSTAP_TX_FLAGS_WDS; | ||
| 258 | meta->ethertype = ethertype; | ||
| 259 | meta->iface = iface; | ||
| 260 | |||
| 261 | /* Send IEEE 802.11 encapsulated frame using the master radio device */ | ||
| 262 | skb->dev = local->dev; | ||
| 263 | dev_queue_xmit(skb); | ||
| 264 | return NETDEV_TX_OK; | ||
| 265 | } | ||
| 266 | |||
| 267 | |||
| 268 | /* hard_start_xmit function for hostapd wlan#ap interfaces */ | ||
| 269 | netdev_tx_t hostap_mgmt_start_xmit(struct sk_buff *skb, | ||
| 270 | struct net_device *dev) | ||
| 271 | { | ||
| 272 | struct hostap_interface *iface; | ||
| 273 | local_info_t *local; | ||
| 274 | struct hostap_skb_tx_data *meta; | ||
| 275 | struct ieee80211_hdr *hdr; | ||
| 276 | u16 fc; | ||
| 277 | |||
| 278 | iface = netdev_priv(dev); | ||
| 279 | local = iface->local; | ||
| 280 | |||
| 281 | if (skb->len < 10) { | ||
| 282 | printk(KERN_DEBUG "%s: hostap_mgmt_start_xmit: short skb " | ||
| 283 | "(len=%d)\n", dev->name, skb->len); | ||
| 284 | kfree_skb(skb); | ||
| 285 | return NETDEV_TX_OK; | ||
| 286 | } | ||
| 287 | |||
| 288 | iface->stats.tx_packets++; | ||
| 289 | iface->stats.tx_bytes += skb->len; | ||
| 290 | |||
| 291 | meta = (struct hostap_skb_tx_data *) skb->cb; | ||
| 292 | memset(meta, 0, sizeof(*meta)); | ||
| 293 | meta->magic = HOSTAP_SKB_TX_DATA_MAGIC; | ||
| 294 | meta->iface = iface; | ||
| 295 | |||
| 296 | if (skb->len >= IEEE80211_DATA_HDR3_LEN + sizeof(rfc1042_header) + 2) { | ||
| 297 | hdr = (struct ieee80211_hdr *) skb->data; | ||
| 298 | fc = le16_to_cpu(hdr->frame_control); | ||
| 299 | if (ieee80211_is_data(hdr->frame_control) && | ||
| 300 | (fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_DATA) { | ||
| 301 | u8 *pos = &skb->data[IEEE80211_DATA_HDR3_LEN + | ||
| 302 | sizeof(rfc1042_header)]; | ||
| 303 | meta->ethertype = (pos[0] << 8) | pos[1]; | ||
| 304 | } | ||
| 305 | } | ||
| 306 | |||
| 307 | /* Send IEEE 802.11 encapsulated frame using the master radio device */ | ||
| 308 | skb->dev = local->dev; | ||
| 309 | dev_queue_xmit(skb); | ||
| 310 | return NETDEV_TX_OK; | ||
| 311 | } | ||
| 312 | |||
| 313 | |||
| 314 | /* Called only from software IRQ */ | ||
| 315 | static struct sk_buff * hostap_tx_encrypt(struct sk_buff *skb, | ||
| 316 | struct lib80211_crypt_data *crypt) | ||
| 317 | { | ||
| 318 | struct hostap_interface *iface; | ||
| 319 | local_info_t *local; | ||
| 320 | struct ieee80211_hdr *hdr; | ||
| 321 | int prefix_len, postfix_len, hdr_len, res; | ||
| 322 | |||
| 323 | iface = netdev_priv(skb->dev); | ||
| 324 | local = iface->local; | ||
| 325 | |||
| 326 | if (skb->len < IEEE80211_DATA_HDR3_LEN) { | ||
| 327 | kfree_skb(skb); | ||
| 328 | return NULL; | ||
| 329 | } | ||
| 330 | |||
| 331 | if (local->tkip_countermeasures && | ||
| 332 | strcmp(crypt->ops->name, "TKIP") == 0) { | ||
| 333 | hdr = (struct ieee80211_hdr *) skb->data; | ||
| 334 | if (net_ratelimit()) { | ||
| 335 | printk(KERN_DEBUG "%s: TKIP countermeasures: dropped " | ||
| 336 | "TX packet to %pM\n", | ||
| 337 | local->dev->name, hdr->addr1); | ||
| 338 | } | ||
| 339 | kfree_skb(skb); | ||
| 340 | return NULL; | ||
| 341 | } | ||
| 342 | |||
| 343 | skb = skb_unshare(skb, GFP_ATOMIC); | ||
| 344 | if (skb == NULL) | ||
| 345 | return NULL; | ||
| 346 | |||
| 347 | prefix_len = crypt->ops->extra_mpdu_prefix_len + | ||
| 348 | crypt->ops->extra_msdu_prefix_len; | ||
| 349 | postfix_len = crypt->ops->extra_mpdu_postfix_len + | ||
| 350 | crypt->ops->extra_msdu_postfix_len; | ||
| 351 | if ((skb_headroom(skb) < prefix_len || | ||
| 352 | skb_tailroom(skb) < postfix_len) && | ||
| 353 | pskb_expand_head(skb, prefix_len, postfix_len, GFP_ATOMIC)) { | ||
| 354 | kfree_skb(skb); | ||
| 355 | return NULL; | ||
| 356 | } | ||
| 357 | |||
| 358 | hdr = (struct ieee80211_hdr *) skb->data; | ||
| 359 | hdr_len = hostap_80211_get_hdrlen(hdr->frame_control); | ||
| 360 | |||
| 361 | /* Host-based IEEE 802.11 fragmentation for TX is not yet supported, so | ||
| 362 | * call both MSDU and MPDU encryption functions from here. */ | ||
| 363 | atomic_inc(&crypt->refcnt); | ||
| 364 | res = 0; | ||
| 365 | if (crypt->ops->encrypt_msdu) | ||
| 366 | res = crypt->ops->encrypt_msdu(skb, hdr_len, crypt->priv); | ||
| 367 | if (res == 0 && crypt->ops->encrypt_mpdu) | ||
| 368 | res = crypt->ops->encrypt_mpdu(skb, hdr_len, crypt->priv); | ||
| 369 | atomic_dec(&crypt->refcnt); | ||
| 370 | if (res < 0) { | ||
| 371 | kfree_skb(skb); | ||
| 372 | return NULL; | ||
| 373 | } | ||
| 374 | |||
| 375 | return skb; | ||
| 376 | } | ||
| 377 | |||
| 378 | |||
| 379 | /* hard_start_xmit function for master radio interface wifi#. | ||
| 380 | * AP processing (TX rate control, power save buffering, etc.). | ||
| 381 | * Use hardware TX function to send the frame. */ | ||
| 382 | netdev_tx_t hostap_master_start_xmit(struct sk_buff *skb, | ||
| 383 | struct net_device *dev) | ||
| 384 | { | ||
| 385 | struct hostap_interface *iface; | ||
| 386 | local_info_t *local; | ||
| 387 | netdev_tx_t ret = NETDEV_TX_BUSY; | ||
| 388 | u16 fc; | ||
| 389 | struct hostap_tx_data tx; | ||
| 390 | ap_tx_ret tx_ret; | ||
| 391 | struct hostap_skb_tx_data *meta; | ||
| 392 | int no_encrypt = 0; | ||
| 393 | struct ieee80211_hdr *hdr; | ||
| 394 | |||
| 395 | iface = netdev_priv(dev); | ||
| 396 | local = iface->local; | ||
| 397 | |||
| 398 | tx.skb = skb; | ||
| 399 | tx.sta_ptr = NULL; | ||
| 400 | |||
| 401 | meta = (struct hostap_skb_tx_data *) skb->cb; | ||
| 402 | if (meta->magic != HOSTAP_SKB_TX_DATA_MAGIC) { | ||
| 403 | printk(KERN_DEBUG "%s: invalid skb->cb magic (0x%08x, " | ||
| 404 | "expected 0x%08x)\n", | ||
| 405 | dev->name, meta->magic, HOSTAP_SKB_TX_DATA_MAGIC); | ||
| 406 | ret = NETDEV_TX_OK; | ||
| 407 | iface->stats.tx_dropped++; | ||
| 408 | goto fail; | ||
| 409 | } | ||
| 410 | |||
| 411 | if (local->host_encrypt) { | ||
| 412 | /* Set crypt to default algorithm and key; will be replaced in | ||
| 413 | * AP code if STA has own alg/key */ | ||
| 414 | tx.crypt = local->crypt_info.crypt[local->crypt_info.tx_keyidx]; | ||
| 415 | tx.host_encrypt = 1; | ||
| 416 | } else { | ||
| 417 | tx.crypt = NULL; | ||
| 418 | tx.host_encrypt = 0; | ||
| 419 | } | ||
| 420 | |||
| 421 | if (skb->len < 24) { | ||
| 422 | printk(KERN_DEBUG "%s: hostap_master_start_xmit: short skb " | ||
| 423 | "(len=%d)\n", dev->name, skb->len); | ||
| 424 | ret = NETDEV_TX_OK; | ||
| 425 | iface->stats.tx_dropped++; | ||
| 426 | goto fail; | ||
| 427 | } | ||
| 428 | |||
| 429 | /* FIX (?): | ||
| 430 | * Wi-Fi 802.11b test plan suggests that AP should ignore power save | ||
| 431 | * bit in authentication and (re)association frames and assume tha | ||
| 432 | * STA remains awake for the response. */ | ||
| 433 | tx_ret = hostap_handle_sta_tx(local, &tx); | ||
| 434 | skb = tx.skb; | ||
| 435 | meta = (struct hostap_skb_tx_data *) skb->cb; | ||
| 436 | hdr = (struct ieee80211_hdr *) skb->data; | ||
| 437 | fc = le16_to_cpu(hdr->frame_control); | ||
| 438 | switch (tx_ret) { | ||
| 439 | case AP_TX_CONTINUE: | ||
| 440 | break; | ||
| 441 | case AP_TX_CONTINUE_NOT_AUTHORIZED: | ||
| 442 | if (local->ieee_802_1x && | ||
| 443 | ieee80211_is_data(hdr->frame_control) && | ||
| 444 | meta->ethertype != ETH_P_PAE && | ||
| 445 | !(meta->flags & HOSTAP_TX_FLAGS_WDS)) { | ||
| 446 | printk(KERN_DEBUG "%s: dropped frame to unauthorized " | ||
| 447 | "port (IEEE 802.1X): ethertype=0x%04x\n", | ||
| 448 | dev->name, meta->ethertype); | ||
| 449 | hostap_dump_tx_80211(dev->name, skb); | ||
| 450 | |||
| 451 | ret = NETDEV_TX_OK; /* drop packet */ | ||
| 452 | iface->stats.tx_dropped++; | ||
| 453 | goto fail; | ||
| 454 | } | ||
| 455 | break; | ||
| 456 | case AP_TX_DROP: | ||
| 457 | ret = NETDEV_TX_OK; /* drop packet */ | ||
| 458 | iface->stats.tx_dropped++; | ||
| 459 | goto fail; | ||
| 460 | case AP_TX_RETRY: | ||
| 461 | goto fail; | ||
| 462 | case AP_TX_BUFFERED: | ||
| 463 | /* do not free skb here, it will be freed when the | ||
| 464 | * buffered frame is sent/timed out */ | ||
| 465 | ret = NETDEV_TX_OK; | ||
| 466 | goto tx_exit; | ||
| 467 | } | ||
| 468 | |||
| 469 | /* Request TX callback if protocol version is 2 in 802.11 header; | ||
| 470 | * this version 2 is a special case used between hostapd and kernel | ||
| 471 | * driver */ | ||
| 472 | if (((fc & IEEE80211_FCTL_VERS) == BIT(1)) && | ||
| 473 | local->ap && local->ap->tx_callback_idx && meta->tx_cb_idx == 0) { | ||
| 474 | meta->tx_cb_idx = local->ap->tx_callback_idx; | ||
| 475 | |||
| 476 | /* remove special version from the frame header */ | ||
| 477 | fc &= ~IEEE80211_FCTL_VERS; | ||
| 478 | hdr->frame_control = cpu_to_le16(fc); | ||
| 479 | } | ||
| 480 | |||
| 481 | if (!ieee80211_is_data(hdr->frame_control)) { | ||
| 482 | no_encrypt = 1; | ||
| 483 | tx.crypt = NULL; | ||
| 484 | } | ||
| 485 | |||
| 486 | if (local->ieee_802_1x && meta->ethertype == ETH_P_PAE && tx.crypt && | ||
| 487 | !(fc & IEEE80211_FCTL_PROTECTED)) { | ||
| 488 | no_encrypt = 1; | ||
| 489 | PDEBUG(DEBUG_EXTRA2, "%s: TX: IEEE 802.1X - passing " | ||
| 490 | "unencrypted EAPOL frame\n", dev->name); | ||
| 491 | tx.crypt = NULL; /* no encryption for IEEE 802.1X frames */ | ||
| 492 | } | ||
| 493 | |||
| 494 | if (tx.crypt && (!tx.crypt->ops || !tx.crypt->ops->encrypt_mpdu)) | ||
| 495 | tx.crypt = NULL; | ||
| 496 | else if ((tx.crypt || | ||
| 497 | local->crypt_info.crypt[local->crypt_info.tx_keyidx]) && | ||
| 498 | !no_encrypt) { | ||
| 499 | /* Add ISWEP flag both for firmware and host based encryption | ||
| 500 | */ | ||
| 501 | fc |= IEEE80211_FCTL_PROTECTED; | ||
| 502 | hdr->frame_control = cpu_to_le16(fc); | ||
| 503 | } else if (local->drop_unencrypted && | ||
| 504 | ieee80211_is_data(hdr->frame_control) && | ||
| 505 | meta->ethertype != ETH_P_PAE) { | ||
| 506 | if (net_ratelimit()) { | ||
| 507 | printk(KERN_DEBUG "%s: dropped unencrypted TX data " | ||
| 508 | "frame (drop_unencrypted=1)\n", dev->name); | ||
| 509 | } | ||
| 510 | iface->stats.tx_dropped++; | ||
| 511 | ret = NETDEV_TX_OK; | ||
| 512 | goto fail; | ||
| 513 | } | ||
| 514 | |||
| 515 | if (tx.crypt) { | ||
| 516 | skb = hostap_tx_encrypt(skb, tx.crypt); | ||
| 517 | if (skb == NULL) { | ||
| 518 | printk(KERN_DEBUG "%s: TX - encryption failed\n", | ||
| 519 | dev->name); | ||
| 520 | ret = NETDEV_TX_OK; | ||
| 521 | goto fail; | ||
| 522 | } | ||
| 523 | meta = (struct hostap_skb_tx_data *) skb->cb; | ||
| 524 | if (meta->magic != HOSTAP_SKB_TX_DATA_MAGIC) { | ||
| 525 | printk(KERN_DEBUG "%s: invalid skb->cb magic (0x%08x, " | ||
| 526 | "expected 0x%08x) after hostap_tx_encrypt\n", | ||
| 527 | dev->name, meta->magic, | ||
| 528 | HOSTAP_SKB_TX_DATA_MAGIC); | ||
| 529 | ret = NETDEV_TX_OK; | ||
| 530 | iface->stats.tx_dropped++; | ||
| 531 | goto fail; | ||
| 532 | } | ||
| 533 | } | ||
| 534 | |||
| 535 | if (local->func->tx == NULL || local->func->tx(skb, dev)) { | ||
| 536 | ret = NETDEV_TX_OK; | ||
| 537 | iface->stats.tx_dropped++; | ||
| 538 | } else { | ||
| 539 | ret = NETDEV_TX_OK; | ||
| 540 | iface->stats.tx_packets++; | ||
| 541 | iface->stats.tx_bytes += skb->len; | ||
| 542 | } | ||
| 543 | |||
| 544 | fail: | ||
| 545 | if (ret == NETDEV_TX_OK && skb) | ||
| 546 | dev_kfree_skb(skb); | ||
| 547 | tx_exit: | ||
| 548 | if (tx.sta_ptr) | ||
| 549 | hostap_handle_sta_release(tx.sta_ptr); | ||
| 550 | return ret; | ||
| 551 | } | ||
| 552 | |||
| 553 | |||
| 554 | EXPORT_SYMBOL(hostap_master_start_xmit); | ||
| @@ -1,3277 +0,0 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * Intersil Prism2 driver with Host AP (software access point) support | ||
| 4 | * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen | ||
| 5 | * <j@w1.fi> | ||
| 6 | * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi> | ||
| 7 | * | ||
| 8 | * This file is to be included into hostap.c when S/W AP functionality is | ||
| 9 | * compiled. | ||
| 10 | * | ||
| 11 | * AP: FIX: | ||
| 12 | * - if unicast Class 2 (assoc,reassoc,disassoc) frame received from | ||
| 13 | * unauthenticated STA, send deauth. frame (8802.11: 5.5) | ||
| 14 | * - if unicast Class 3 (data with to/from DS,deauth,pspoll) frame received | ||
| 15 | * from authenticated, but unassoc STA, send disassoc frame (8802.11: 5.5) | ||
| 16 | * - if unicast Class 3 received from unauthenticated STA, send deauth. frame | ||
| 17 | * (8802.11: 5.5) | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include <linux/proc_fs.h> | ||
| 21 | #include <linux/seq_file.h> | ||
| 22 | #include <linux/delay.h> | ||
| 23 | #include <linux/random.h> | ||
| 24 | #include <linux/if_arp.h> | ||
| 25 | #include <linux/slab.h> | ||
| 26 | #include <linux/export.h> | ||
| 27 | #include <linux/moduleparam.h> | ||
| 28 | #include <linux/etherdevice.h> | ||
| 29 | |||
| 30 | #include "hostap_wlan.h" | ||
| 31 | #include "hostap.h" | ||
| 32 | #include "hostap_ap.h" | ||
| 33 | |||
| 34 | static int other_ap_policy[MAX_PARM_DEVICES] = { AP_OTHER_AP_SKIP_ALL, | ||
| 35 | DEF_INTS }; | ||
| 36 | module_param_array(other_ap_policy, int, NULL, 0444); | ||
| 37 | MODULE_PARM_DESC(other_ap_policy, "Other AP beacon monitoring policy (0-3)"); | ||
| 38 | |||
| 39 | static int ap_max_inactivity[MAX_PARM_DEVICES] = { AP_MAX_INACTIVITY_SEC, | ||
| 40 | DEF_INTS }; | ||
| 41 | module_param_array(ap_max_inactivity, int, NULL, 0444); | ||
| 42 | MODULE_PARM_DESC(ap_max_inactivity, "AP timeout (in seconds) for station " | ||
| 43 | "inactivity"); | ||
| 44 | |||
| 45 | static int ap_bridge_packets[MAX_PARM_DEVICES] = { 1, DEF_INTS }; | ||
| 46 | module_param_array(ap_bridge_packets, int, NULL, 0444); | ||
| 47 | MODULE_PARM_DESC(ap_bridge_packets, "Bridge packets directly between " | ||
| 48 | "stations"); | ||
| 49 | |||
| 50 | static int autom_ap_wds[MAX_PARM_DEVICES] = { 0, DEF_INTS }; | ||
| 51 | module_param_array(autom_ap_wds, int, NULL, 0444); | ||
| 52 | MODULE_PARM_DESC(autom_ap_wds, "Add WDS connections to other APs " | ||
| 53 | "automatically"); | ||
| 54 | |||
| 55 | |||
| 56 | static struct sta_info* ap_get_sta(struct ap_data *ap, u8 *sta); | ||
| 57 | static void hostap_event_expired_sta(struct net_device *dev, | ||
| 58 | struct sta_info *sta); | ||
| 59 | static void handle_add_proc_queue(struct work_struct *work); | ||
| 60 | |||
| 61 | #ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT | ||
| 62 | static void handle_wds_oper_queue(struct work_struct *work); | ||
| 63 | static void prism2_send_mgmt(struct net_device *dev, | ||
| 64 | u16 type_subtype, char *body, | ||
| 65 | int body_len, u8 *addr, u16 tx_cb_idx); | ||
| 66 | #endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ | ||
| 67 | |||
| 68 | |||
| 69 | #if !defined(PRISM2_NO_PROCFS_DEBUG) && defined(CONFIG_PROC_FS) | ||
| 70 | static int ap_debug_proc_show(struct seq_file *m, void *v) | ||
| 71 | { | ||
| 72 | struct ap_data *ap = pde_data(file_inode(m->file)); | ||
| 73 | |||
| 74 | seq_printf(m, "BridgedUnicastFrames=%u\n", ap->bridged_unicast); | ||
| 75 | seq_printf(m, "BridgedMulticastFrames=%u\n", ap->bridged_multicast); | ||
| 76 | seq_printf(m, "max_inactivity=%u\n", ap->max_inactivity / HZ); | ||
| 77 | seq_printf(m, "bridge_packets=%u\n", ap->bridge_packets); | ||
| 78 | seq_printf(m, "nullfunc_ack=%u\n", ap->nullfunc_ack); | ||
| 79 | seq_printf(m, "autom_ap_wds=%u\n", ap->autom_ap_wds); | ||
| 80 | seq_printf(m, "auth_algs=%u\n", ap->local->auth_algs); | ||
| 81 | seq_printf(m, "tx_drop_nonassoc=%u\n", ap->tx_drop_nonassoc); | ||
| 82 | return 0; | ||
| 83 | } | ||
| 84 | #endif | ||
| 85 | |||
| 86 | static void ap_sta_hash_add(struct ap_data *ap, struct sta_info *sta) | ||
| 87 | { | ||
| 88 | sta->hnext = ap->sta_hash[STA_HASH(sta->addr)]; | ||
| 89 | ap->sta_hash[STA_HASH(sta->addr)] = sta; | ||
| 90 | } | ||
| 91 | |||
| 92 | static void ap_sta_hash_del(struct ap_data *ap, struct sta_info *sta) | ||
| 93 | { | ||
| 94 | struct sta_info *s; | ||
| 95 | |||
| 96 | s = ap->sta_hash[STA_HASH(sta->addr)]; | ||
| 97 | if (s == NULL) return; | ||
| 98 | if (ether_addr_equal(s->addr, sta->addr)) { | ||
| 99 | ap->sta_hash[STA_HASH(sta->addr)] = s->hnext; | ||
| 100 | return; | ||
| 101 | } | ||
| 102 | |||
| 103 | while (s->hnext != NULL && !ether_addr_equal(s->hnext->addr, sta->addr)) | ||
| 104 | s = s->hnext; | ||
| 105 | if (s->hnext != NULL) | ||
| 106 | s->hnext = s->hnext->hnext; | ||
| 107 | else | ||
| 108 | printk("AP: could not remove STA %pM from hash table\n", | ||
| 109 | sta->addr); | ||
| 110 | } | ||
| 111 | |||
| 112 | static void ap_free_sta(struct ap_data *ap, struct sta_info *sta) | ||
| 113 | { | ||
| 114 | if (sta->ap && sta->local) | ||
| 115 | hostap_event_expired_sta(sta->local->dev, sta); | ||
| 116 | |||
| 117 | if (ap->proc != NULL) { | ||
| 118 | char name[20]; | ||
| 119 | sprintf(name, "%pM", sta->addr); | ||
| 120 | remove_proc_entry(name, ap->proc); | ||
| 121 | } | ||
| 122 | |||
| 123 | if (sta->crypt) { | ||
| 124 | sta->crypt->ops->deinit(sta->crypt->priv); | ||
| 125 | kfree(sta->crypt); | ||
| 126 | sta->crypt = NULL; | ||
| 127 | } | ||
| 128 | |||
| 129 | skb_queue_purge(&sta->tx_buf); | ||
| 130 | |||
| 131 | ap->num_sta--; | ||
| 132 | #ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT | ||
| 133 | if (sta->aid > 0) | ||
| 134 | ap->sta_aid[sta->aid - 1] = NULL; | ||
| 135 | |||
| 136 | if (!sta->ap) | ||
| 137 | kfree(sta->u.sta.challenge); | ||
| 138 | timer_shutdown_sync(&sta->timer); | ||
| 139 | #endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ | ||
| 140 | |||
| 141 | kfree(sta); | ||
| 142 | } | ||
| 143 | |||
| 144 | |||
| 145 | static void hostap_set_tim(local_info_t *local, int aid, int set) | ||
| 146 | { | ||
| 147 | if (local->func->set_tim) | ||
| 148 | local->func->set_tim(local->dev, aid, set); | ||
| 149 | } | ||
| 150 | |||
| 151 | |||
| 152 | static void hostap_event_new_sta(struct net_device *dev, struct sta_info *sta) | ||
| 153 | { | ||
| 154 | union iwreq_data wrqu; | ||
| 155 | memset(&wrqu, 0, sizeof(wrqu)); | ||
| 156 | memcpy(wrqu.addr.sa_data, sta->addr, ETH_ALEN); | ||
| 157 | wrqu.addr.sa_family = ARPHRD_ETHER; | ||
| 158 | wireless_send_event(dev, IWEVREGISTERED, &wrqu, NULL); | ||
| 159 | } | ||
| 160 | |||
| 161 | |||
| 162 | static void hostap_event_expired_sta(struct net_device *dev, | ||
| 163 | struct sta_info *sta) | ||
| 164 | { | ||
| 165 | union iwreq_data wrqu; | ||
| 166 | memset(&wrqu, 0, sizeof(wrqu)); | ||
| 167 | memcpy(wrqu.addr.sa_data, sta->addr, ETH_ALEN); | ||
| 168 | wrqu.addr.sa_family = ARPHRD_ETHER; | ||
| 169 | wireless_send_event(dev, IWEVEXPIRED, &wrqu, NULL); | ||
| 170 | } | ||
| 171 | |||
| 172 | |||
| 173 | #ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT | ||
| 174 | |||
| 175 | static void ap_handle_timer(struct timer_list *t) | ||
| 176 | { | ||
| 177 | struct sta_info *sta = from_timer(sta, t, timer); | ||
| 178 | local_info_t *local; | ||
| 179 | struct ap_data *ap; | ||
| 180 | unsigned long next_time = 0; | ||
| 181 | int was_assoc; | ||
| 182 | |||
| 183 | if (sta == NULL || sta->local == NULL || sta->local->ap == NULL) { | ||
| 184 | PDEBUG(DEBUG_AP, "ap_handle_timer() called with NULL data\n"); | ||
| 185 | return; | ||
| 186 | } | ||
| 187 | |||
| 188 | local = sta->local; | ||
| 189 | ap = local->ap; | ||
| 190 | was_assoc = sta->flags & WLAN_STA_ASSOC; | ||
| 191 | |||
| 192 | if (atomic_read(&sta->users) != 0) | ||
| 193 | next_time = jiffies + HZ; | ||
| 194 | else if ((sta->flags & WLAN_STA_PERM) && !(sta->flags & WLAN_STA_AUTH)) | ||
| 195 | next_time = jiffies + ap->max_inactivity; | ||
| 196 | |||
| 197 | if (time_before(jiffies, sta->last_rx + ap->max_inactivity)) { | ||
| 198 | /* station activity detected; reset timeout state */ | ||
| 199 | sta->timeout_next = STA_NULLFUNC; | ||
| 200 | next_time = sta->last_rx + ap->max_inactivity; | ||
| 201 | } else if (sta->timeout_next == STA_DISASSOC && | ||
| 202 | !(sta->flags & WLAN_STA_PENDING_POLL)) { | ||
| 203 | /* STA ACKed data nullfunc frame poll */ | ||
| 204 | sta->timeout_next = STA_NULLFUNC; | ||
| 205 | next_time = jiffies + ap->max_inactivity; | ||
| 206 | } | ||
| 207 | |||
| 208 | if (next_time) { | ||
| 209 | sta->timer.expires = next_time; | ||
| 210 | add_timer(&sta->timer); | ||
| 211 | return; | ||
| 212 | } | ||
| 213 | |||
| 214 | if (sta->ap) | ||
| 215 | sta->timeout_next = STA_DEAUTH; | ||
| 216 | |||
| 217 | if (sta->timeout_next == STA_DEAUTH && !(sta->flags & WLAN_STA_PERM)) { | ||
| 218 | spin_lock(&ap->sta_table_lock); | ||
| 219 | ap_sta_hash_del(ap, sta); | ||
| 220 | list_del(&sta->list); | ||
| 221 | spin_unlock(&ap->sta_table_lock); | ||
| 222 | sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); | ||
| 223 | } else if (sta->timeout_next == STA_DISASSOC) | ||
| 224 | sta->flags &= ~WLAN_STA_ASSOC; | ||
| 225 | |||
| 226 | if (was_assoc && !(sta->flags & WLAN_STA_ASSOC) && !sta->ap) | ||
| 227 | hostap_event_expired_sta(local->dev, sta); | ||
| 228 | |||
| 229 | if (sta->timeout_next == STA_DEAUTH && sta->aid > 0 && | ||
| 230 | !skb_queue_empty(&sta->tx_buf)) { | ||
| 231 | hostap_set_tim(local, sta->aid, 0); | ||
| 232 | sta->flags &= ~WLAN_STA_TIM; | ||
| 233 | } | ||
| 234 | |||
| 235 | if (sta->ap) { | ||
| 236 | if (ap->autom_ap_wds) { | ||
| 237 | PDEBUG(DEBUG_AP, "%s: removing automatic WDS " | ||
| 238 | "connection to AP %pM\n", | ||
| 239 | local->dev->name, sta->addr); | ||
| 240 | hostap_wds_link_oper(local, sta->addr, WDS_DEL); | ||
| 241 | } | ||
| 242 | } else if (sta->timeout_next == STA_NULLFUNC) { | ||
| 243 | /* send data frame to poll STA and check whether this frame | ||
| 244 | * is ACKed */ | ||
| 245 | /* FIX: IEEE80211_STYPE_NULLFUNC would be more appropriate, but | ||
| 246 | * it is apparently not retried so TX Exc events are not | ||
| 247 | * received for it */ | ||
| 248 | sta->flags |= WLAN_STA_PENDING_POLL; | ||
| 249 | prism2_send_mgmt(local->dev, IEEE80211_FTYPE_DATA | | ||
| 250 | IEEE80211_STYPE_DATA, NULL, 0, | ||
| 251 | sta->addr, ap->tx_callback_poll); | ||
| 252 | } else { | ||
| 253 | int deauth = sta->timeout_next == STA_DEAUTH; | ||
| 254 | __le16 resp; | ||
| 255 | PDEBUG(DEBUG_AP, "%s: sending %s info to STA %pM" | ||
| 256 | "(last=%lu, jiffies=%lu)\n", | ||
| 257 | local->dev->name, | ||
| 258 | deauth ? "deauthentication" : "disassociation", | ||
| 259 | sta->addr, sta->last_rx, jiffies); | ||
| 260 | |||
| 261 | resp = cpu_to_le16(deauth ? WLAN_REASON_PREV_AUTH_NOT_VALID : | ||
| 262 | WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY); | ||
| 263 | prism2_send_mgmt(local->dev, IEEE80211_FTYPE_MGMT | | ||
| 264 | (deauth ? IEEE80211_STYPE_DEAUTH : | ||
| 265 | IEEE80211_STYPE_DISASSOC), | ||
| 266 | (char *) &resp, 2, sta->addr, 0); | ||
| 267 | } | ||
| 268 | |||
| 269 | if (sta->timeout_next == STA_DEAUTH) { | ||
| 270 | if (sta->flags & WLAN_STA_PERM) { | ||
| 271 | PDEBUG(DEBUG_AP, "%s: STA %pM" | ||
| 272 | " would have been removed, " | ||
| 273 | "but it has 'perm' flag\n", | ||
| 274 | local->dev->name, sta->addr); | ||
| 275 | } else | ||
| 276 | ap_free_sta(ap, sta); | ||
| 277 | return; | ||
| 278 | } | ||
| 279 | |||
| 280 | if (sta->timeout_next == STA_NULLFUNC) { | ||
| 281 | sta->timeout_next = STA_DISASSOC; | ||
| 282 | sta->timer.expires = jiffies + AP_DISASSOC_DELAY; | ||
| 283 | } else { | ||
| 284 | sta->timeout_next = STA_DEAUTH; | ||
| 285 | sta->timer.expires = jiffies + AP_DEAUTH_DELAY; | ||
| 286 | } | ||
| 287 | |||
| 288 | add_timer(&sta->timer); | ||
| 289 | } | ||
| 290 | |||
| 291 | |||
| 292 | void hostap_deauth_all_stas(struct net_device *dev, struct ap_data *ap, | ||
| 293 | int resend) | ||
| 294 | { | ||
| 295 | u8 addr[ETH_ALEN]; | ||
| 296 | __le16 resp; | ||
| 297 | int i; | ||
| 298 | |||
| 299 | PDEBUG(DEBUG_AP, "%s: Deauthenticate all stations\n", dev->name); | ||
| 300 | eth_broadcast_addr(addr); | ||
| 301 | |||
| 302 | resp = cpu_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); | ||
| 303 | |||
| 304 | /* deauth message sent; try to resend it few times; the message is | ||
| 305 | * broadcast, so it may be delayed until next DTIM; there is not much | ||
| 306 | * else we can do at this point since the driver is going to be shut | ||
| 307 | * down */ | ||
| 308 | for (i = 0; i < 5; i++) { | ||
| 309 | prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT | | ||
| 310 | IEEE80211_STYPE_DEAUTH, | ||
| 311 | (char *) &resp, 2, addr, 0); | ||
| 312 | |||
| 313 | if (!resend || ap->num_sta <= 0) | ||
| 314 | return; | ||
| 315 | |||
| 316 | mdelay(50); | ||
| 317 | } | ||
| 318 | } | ||
| 319 | |||
| 320 | |||
| 321 | static int ap_control_proc_show(struct seq_file *m, void *v) | ||
| 322 | { | ||
| 323 | struct ap_data *ap = pde_data(file_inode(m->file)); | ||
| 324 | char *policy_txt; | ||
| 325 | struct mac_entry *entry; | ||
| 326 | |||
| 327 | if (v == SEQ_START_TOKEN) { | ||
| 328 | switch (ap->mac_restrictions.policy) { | ||
| 329 | case MAC_POLICY_OPEN: | ||
| 330 | policy_txt = "open"; | ||
| 331 | break; | ||
| 332 | case MAC_POLICY_ALLOW: | ||
| 333 | policy_txt = "allow"; | ||
| 334 | break; | ||
| 335 | case MAC_POLICY_DENY: | ||
| 336 | policy_txt = "deny"; | ||
| 337 | break; | ||
| 338 | default: | ||
| 339 | policy_txt = "unknown"; | ||
| 340 | break; | ||
| 341 | } | ||
| 342 | seq_printf(m, "MAC policy: %s\n", policy_txt); | ||
| 343 | seq_printf(m, "MAC entries: %u\n", ap->mac_restrictions.entries); | ||
| 344 | seq_puts(m, "MAC list:\n"); | ||
| 345 | return 0; | ||
| 346 | } | ||
| 347 | |||
| 348 | entry = v; | ||
| 349 | seq_printf(m, "%pM\n", entry->addr); | ||
| 350 | return 0; | ||
| 351 | } | ||
| 352 | |||
| 353 | static void *ap_control_proc_start(struct seq_file *m, loff_t *_pos) | ||
| 354 | { | ||
| 355 | struct ap_data *ap = pde_data(file_inode(m->file)); | ||
| 356 | spin_lock_bh(&ap->mac_restrictions.lock); | ||
| 357 | return seq_list_start_head(&ap->mac_restrictions.mac_list, *_pos); | ||
| 358 | } | ||
| 359 | |||
| 360 | static void *ap_control_proc_next(struct seq_file *m, void *v, loff_t *_pos) | ||
| 361 | { | ||
| 362 | struct ap_data *ap = pde_data(file_inode(m->file)); | ||
| 363 | return seq_list_next(v, &ap->mac_restrictions.mac_list, _pos); | ||
| 364 | } | ||
| 365 | |||
| 366 | static void ap_control_proc_stop(struct seq_file *m, void *v) | ||
| 367 | { | ||
| 368 | struct ap_data *ap = pde_data(file_inode(m->file)); | ||
| 369 | spin_unlock_bh(&ap->mac_restrictions.lock); | ||
| 370 | } | ||
| 371 | |||
| 372 | static const struct seq_operations ap_control_proc_seqops = { | ||
| 373 | .start = ap_control_proc_start, | ||
| 374 | .next = ap_control_proc_next, | ||
| 375 | .stop = ap_control_proc_stop, | ||
| 376 | .show = ap_control_proc_show, | ||
| 377 | }; | ||
| 378 | |||
| 379 | int ap_control_add_mac(struct mac_restrictions *mac_restrictions, u8 *mac) | ||
| 380 | { | ||
| 381 | struct mac_entry *entry; | ||
| 382 | |||
| 383 | entry = kmalloc(sizeof(struct mac_entry), GFP_KERNEL); | ||
| 384 | if (entry == NULL) | ||
| 385 | return -ENOMEM; | ||
| 386 | |||
| 387 | memcpy(entry->addr, mac, ETH_ALEN); | ||
| 388 | |||
| 389 | spin_lock_bh(&mac_restrictions->lock); | ||
| 390 | list_add_tail(&entry->list, &mac_restrictions->mac_list); | ||
| 391 | mac_restrictions->entries++; | ||
| 392 | spin_unlock_bh(&mac_restrictions->lock); | ||
| 393 | |||
| 394 | return 0; | ||
| 395 | } | ||
| 396 | |||
| 397 | |||
| 398 | int ap_control_del_mac(struct mac_restrictions *mac_restrictions, u8 *mac) | ||
| 399 | { | ||
| 400 | struct list_head *ptr; | ||
| 401 | struct mac_entry *entry; | ||
| 402 | |||
| 403 | spin_lock_bh(&mac_restrictions->lock); | ||
| 404 | for (ptr = mac_restrictions->mac_list.next; | ||
| 405 | ptr != &mac_restrictions->mac_list; ptr = ptr->next) { | ||
| 406 | entry = list_entry(ptr, struct mac_entry, list); | ||
| 407 | |||
| 408 | if (ether_addr_equal(entry->addr, mac)) { | ||
| 409 | list_del(ptr); | ||
| 410 | kfree(entry); | ||
| 411 | mac_restrictions->entries--; | ||
| 412 | spin_unlock_bh(&mac_restrictions->lock); | ||
| 413 | return 0; | ||
| 414 | } | ||
| 415 | } | ||
| 416 | spin_unlock_bh(&mac_restrictions->lock); | ||
| 417 | return -1; | ||
| 418 | } | ||
| 419 | |||
| 420 | |||
| 421 | static int ap_control_mac_deny(struct mac_restrictions *mac_restrictions, | ||
| 422 | u8 *mac) | ||
| 423 | { | ||
| 424 | struct mac_entry *entry; | ||
| 425 | int found = 0; | ||
| 426 | |||
| 427 | if (mac_restrictions->policy == MAC_POLICY_OPEN) | ||
| 428 | return 0; | ||
| 429 | |||
| 430 | spin_lock_bh(&mac_restrictions->lock); | ||
| 431 | list_for_each_entry(entry, &mac_restrictions->mac_list, list) { | ||
| 432 | if (ether_addr_equal(entry->addr, mac)) { | ||
| 433 | found = 1; | ||
| 434 | break; | ||
| 435 | } | ||
| 436 | } | ||
| 437 | spin_unlock_bh(&mac_restrictions->lock); | ||
| 438 | |||
| 439 | if (mac_restrictions->policy == MAC_POLICY_ALLOW) | ||
| 440 | return !found; | ||
| 441 | else | ||
| 442 | return found; | ||
| 443 | } | ||
| 444 | |||
| 445 | |||
| 446 | void ap_control_flush_macs(struct mac_restrictions *mac_restrictions) | ||
| 447 | { | ||
| 448 | struct list_head *ptr, *n; | ||
| 449 | struct mac_entry *entry; | ||
| 450 | |||
| 451 | if (mac_restrictions->entries == 0) | ||
| 452 | return; | ||
| 453 | |||
| 454 | spin_lock_bh(&mac_restrictions->lock); | ||
| 455 | for (ptr = mac_restrictions->mac_list.next, n = ptr->next; | ||
| 456 | ptr != &mac_restrictions->mac_list; | ||
| 457 | ptr = n, n = ptr->next) { | ||
| 458 | entry = list_entry(ptr, struct mac_entry, list); | ||
| 459 | list_del(ptr); | ||
| 460 | kfree(entry); | ||
| 461 | } | ||
| 462 | mac_restrictions->entries = 0; | ||
| 463 | spin_unlock_bh(&mac_restrictions->lock); | ||
| 464 | } | ||
| 465 | |||
| 466 | |||
| 467 | int ap_control_kick_mac(struct ap_data *ap, struct net_device *dev, u8 *mac) | ||
| 468 | { | ||
| 469 | struct sta_info *sta; | ||
| 470 | __le16 resp; | ||
| 471 | |||
| 472 | spin_lock_bh(&ap->sta_table_lock); | ||
| 473 | sta = ap_get_sta(ap, mac); | ||
| 474 | if (sta) { | ||
| 475 | ap_sta_hash_del(ap, sta); | ||
| 476 | list_del(&sta->list); | ||
| 477 | } | ||
| 478 | spin_unlock_bh(&ap->sta_table_lock); | ||
| 479 | |||
| 480 | if (!sta) | ||
| 481 | return -EINVAL; | ||
| 482 | |||
| 483 | resp = cpu_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); | ||
| 484 | prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DEAUTH, | ||
| 485 | (char *) &resp, 2, sta->addr, 0); | ||
| 486 | |||
| 487 | if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap) | ||
| 488 | hostap_event_expired_sta(dev, sta); | ||
| 489 | |||
| 490 | ap_free_sta(ap, sta); | ||
| 491 | |||
| 492 | return 0; | ||
| 493 | } | ||
| 494 | |||
| 495 | #endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ | ||
| 496 | |||
| 497 | |||
| 498 | void ap_control_kickall(struct ap_data *ap) | ||
| 499 | { | ||
| 500 | struct list_head *ptr, *n; | ||
| 501 | struct sta_info *sta; | ||
| 502 | |||
| 503 | spin_lock_bh(&ap->sta_table_lock); | ||
| 504 | for (ptr = ap->sta_list.next, n = ptr->next; ptr != &ap->sta_list; | ||
| 505 | ptr = n, n = ptr->next) { | ||
| 506 | sta = list_entry(ptr, struct sta_info, list); | ||
| 507 | ap_sta_hash_del(ap, sta); | ||
| 508 | list_del(&sta->list); | ||
| 509 | if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local) | ||
| 510 | hostap_event_expired_sta(sta->local->dev, sta); | ||
| 511 | ap_free_sta(ap, sta); | ||
| 512 | } | ||
| 513 | spin_unlock_bh(&ap->sta_table_lock); | ||
| 514 | } | ||
| 515 | |||
| 516 | |||
| 517 | #ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT | ||
| 518 | |||
| 519 | static int prism2_ap_proc_show(struct seq_file *m, void *v) | ||
| 520 | { | ||
| 521 | struct sta_info *sta = v; | ||
| 522 | int i; | ||
| 523 | |||
| 524 | if (v == SEQ_START_TOKEN) { | ||
| 525 | seq_printf(m, "# BSSID CHAN SIGNAL NOISE RATE SSID FLAGS\n"); | ||
| 526 | return 0; | ||
| 527 | } | ||
| 528 | |||
| 529 | if (!sta->ap) | ||
| 530 | return 0; | ||
| 531 | |||
| 532 | seq_printf(m, "%pM %d %d %d %d '", | ||
| 533 | sta->addr, | ||
| 534 | sta->u.ap.channel, sta->last_rx_signal, | ||
| 535 | sta->last_rx_silence, sta->last_rx_rate); | ||
| 536 | |||
| 537 | for (i = 0; i < sta->u.ap.ssid_len; i++) { | ||
| 538 | if (sta->u.ap.ssid[i] >= 32 && sta->u.ap.ssid[i] < 127) | ||
| 539 | seq_putc(m, sta->u.ap.ssid[i]); | ||
| 540 | else | ||
| 541 | seq_printf(m, "<%02x>", sta->u.ap.ssid[i]); | ||
| 542 | } | ||
| 543 | |||
| 544 | seq_putc(m, '\''); | ||
| 545 | if (sta->capability & WLAN_CAPABILITY_ESS) | ||
| 546 | seq_puts(m, " [ESS]"); | ||
| 547 | if (sta->capability & WLAN_CAPABILITY_IBSS) | ||
| 548 | seq_puts(m, " [IBSS]"); | ||
| 549 | if (sta->capability & WLAN_CAPABILITY_PRIVACY) | ||
| 550 | seq_puts(m, " [WEP]"); | ||
| 551 | seq_putc(m, '\n'); | ||
| 552 | return 0; | ||
| 553 | } | ||
| 554 | |||
| 555 | static void *prism2_ap_proc_start(struct seq_file *m, loff_t *_pos) | ||
| 556 | { | ||
| 557 | struct ap_data *ap = pde_data(file_inode(m->file)); | ||
| 558 | spin_lock_bh(&ap->sta_table_lock); | ||
| 559 | return seq_list_start_head(&ap->sta_list, *_pos); | ||
| 560 | } | ||
| 561 | |||
| 562 | static void *prism2_ap_proc_next(struct seq_file *m, void *v, loff_t *_pos) | ||
| 563 | { | ||
| 564 | struct ap_data *ap = pde_data(file_inode(m->file)); | ||
| 565 | return seq_list_next(v, &ap->sta_list, _pos); | ||
| 566 | } | ||
| 567 | |||
| 568 | static void prism2_ap_proc_stop(struct seq_file *m, void *v) | ||
| 569 | { | ||
| 570 | struct ap_data *ap = pde_data(file_inode(m->file)); | ||
| 571 | spin_unlock_bh(&ap->sta_table_lock); | ||
| 572 | } | ||
| 573 | |||
| 574 | static const struct seq_operations prism2_ap_proc_seqops = { | ||
| 575 | .start = prism2_ap_proc_start, | ||
| 576 | .next = prism2_ap_proc_next, | ||
| 577 | .stop = prism2_ap_proc_stop, | ||
| 578 | .show = prism2_ap_proc_show, | ||
| 579 | }; | ||
| 580 | #endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ | ||
| 581 | |||
| 582 | |||
| 583 | void hostap_check_sta_fw_version(struct ap_data *ap, int sta_fw_ver) | ||
| 584 | { | ||
| 585 | if (!ap) | ||
| 586 | return; | ||
| 587 | |||
| 588 | if (sta_fw_ver == PRISM2_FW_VER(0,8,0)) { | ||
| 589 | PDEBUG(DEBUG_AP, "Using data::nullfunc ACK workaround - " | ||
| 590 | "firmware upgrade recommended\n"); | ||
| 591 | ap->nullfunc_ack = 1; | ||
| 592 | } else | ||
| 593 | ap->nullfunc_ack = 0; | ||
| 594 | |||
| 595 | if (sta_fw_ver == PRISM2_FW_VER(1,4,2)) { | ||
| 596 | printk(KERN_WARNING "%s: Warning: secondary station firmware " | ||
| 597 | "version 1.4.2 does not seem to work in Host AP mode\n", | ||
| 598 | ap->local->dev->name); | ||
| 599 | } | ||
| 600 | } | ||
| 601 | |||
| 602 | |||
| 603 | /* Called only as a tasklet (software IRQ) */ | ||
| 604 | static void hostap_ap_tx_cb(struct sk_buff *skb, int ok, void *data) | ||
| 605 | { | ||
| 606 | struct ap_data *ap = data; | ||
| 607 | struct ieee80211_hdr *hdr; | ||
| 608 | |||
| 609 | if (!ap->local->hostapd || !ap->local->apdev) { | ||
| 610 | dev_kfree_skb(skb); | ||
| 611 | return; | ||
| 612 | } | ||
| 613 | |||
| 614 | /* Pass the TX callback frame to the hostapd; use 802.11 header version | ||
| 615 | * 1 to indicate failure (no ACK) and 2 success (frame ACKed) */ | ||
| 616 | |||
| 617 | hdr = (struct ieee80211_hdr *) skb->data; | ||
| 618 | hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_VERS); | ||
| 619 | hdr->frame_control |= cpu_to_le16(ok ? BIT(1) : BIT(0)); | ||
| 620 | |||
| 621 | skb->dev = ap->local->apdev; | ||
| 622 | skb_pull(skb, hostap_80211_get_hdrlen(hdr->frame_control)); | ||
| 623 | skb->pkt_type = PACKET_OTHERHOST; | ||
| 624 | skb->protocol = cpu_to_be16(ETH_P_802_2); | ||
| 625 | memset(skb->cb, 0, sizeof(skb->cb)); | ||
| 626 | netif_rx(skb); | ||
| 627 | } | ||
| 628 | |||
| 629 | |||
| 630 | #ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT | ||
| 631 | /* Called only as a tasklet (software IRQ) */ | ||
| 632 | static void hostap_ap_tx_cb_auth(struct sk_buff *skb, int ok, void *data) | ||
| 633 | { | ||
| 634 | struct ap_data *ap = data; | ||
| 635 | struct net_device *dev = ap->local->dev; | ||
| 636 | struct ieee80211_hdr *hdr; | ||
| 637 | u16 auth_alg, auth_transaction, status; | ||
| 638 | __le16 *pos; | ||
| 639 | struct sta_info *sta = NULL; | ||
| 640 | char *txt = NULL; | ||
| 641 | |||
| 642 | if (ap->local->hostapd) { | ||
| 643 | dev_kfree_skb(skb); | ||
| 644 | return; | ||
| 645 | } | ||
| 646 | |||
| 647 | hdr = (struct ieee80211_hdr *) skb->data; | ||
| 648 | if (!ieee80211_is_auth(hdr->frame_control) || | ||
| 649 | skb->len < IEEE80211_MGMT_HDR_LEN + 6) { | ||
| 650 | printk(KERN_DEBUG "%s: hostap_ap_tx_cb_auth received invalid " | ||
| 651 | "frame\n", dev->name); | ||
| 652 | dev_kfree_skb(skb); | ||
| 653 | return; | ||
| 654 | } | ||
| 655 | |||
| 656 | pos = (__le16 *) (skb->data + IEEE80211_MGMT_HDR_LEN); | ||
| 657 | auth_alg = le16_to_cpu(*pos++); | ||
| 658 | auth_transaction = le16_to_cpu(*pos++); | ||
| 659 | status = le16_to_cpu(*pos++); | ||
| 660 | |||
| 661 | if (!ok) { | ||
| 662 | txt = "frame was not ACKed"; | ||
| 663 | goto done; | ||
| 664 | } | ||
| 665 | |||
| 666 | spin_lock(&ap->sta_table_lock); | ||
| 667 | sta = ap_get_sta(ap, hdr->addr1); | ||
| 668 | if (sta) | ||
| 669 | atomic_inc(&sta->users); | ||
| 670 | spin_unlock(&ap->sta_table_lock); | ||
| 671 | |||
| 672 | if (!sta) { | ||
| 673 | txt = "STA not found"; | ||
| 674 | goto done; | ||
| 675 | } | ||
| 676 | |||
| 677 | if (status == WLAN_STATUS_SUCCESS && | ||
| 678 | ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 2) || | ||
| 679 | (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 4))) { | ||
| 680 | txt = "STA authenticated"; | ||
| 681 | sta->flags |= WLAN_STA_AUTH; | ||
| 682 | sta->last_auth = jiffies; | ||
| 683 | } else if (status != WLAN_STATUS_SUCCESS) | ||
| 684 | txt = "authentication failed"; | ||
| 685 | |||
| 686 | done: | ||
| 687 | if (sta) | ||
| 688 | atomic_dec(&sta->users); | ||
| 689 | if (txt) { | ||
| 690 | PDEBUG(DEBUG_AP, "%s: %pM auth_cb - alg=%d " | ||
| 691 | "trans#=%d status=%d - %s\n", | ||
| 692 | dev->name, hdr->addr1, | ||
| 693 | auth_alg, auth_transaction, status, txt); | ||
| 694 | } | ||
| 695 | dev_kfree_skb(skb); | ||
| 696 | } | ||
| 697 | |||
| 698 | |||
| 699 | /* Called only as a tasklet (software IRQ) */ | ||
| 700 | static void hostap_ap_tx_cb_assoc(struct sk_buff *skb, int ok, void *data) | ||
| 701 | { | ||
| 702 | struct ap_data *ap = data; | ||
| 703 | struct net_device *dev = ap->local->dev; | ||
| 704 | struct ieee80211_hdr *hdr; | ||
| 705 | u16 status; | ||
| 706 | __le16 *pos; | ||
| 707 | struct sta_info *sta = NULL; | ||
| 708 | char *txt = NULL; | ||
| 709 | |||
| 710 | if (ap->local->hostapd) { | ||
| 711 | dev_kfree_skb(skb); | ||
| 712 | return; | ||
| 713 | } | ||
| 714 | |||
| 715 | hdr = (struct ieee80211_hdr *) skb->data; | ||
| 716 | if ((!ieee80211_is_assoc_resp(hdr->frame_control) && | ||
| 717 | !ieee80211_is_reassoc_resp(hdr->frame_control)) || | ||
| 718 | skb->len < IEEE80211_MGMT_HDR_LEN + 4) { | ||
| 719 | printk(KERN_DEBUG "%s: hostap_ap_tx_cb_assoc received invalid " | ||
| 720 | "frame\n", dev->name); | ||
| 721 | dev_kfree_skb(skb); | ||
| 722 | return; | ||
| 723 | } | ||
| 724 | |||
| 725 | if (!ok) { | ||
| 726 | txt = "frame was not ACKed"; | ||
| 727 | goto done; | ||
| 728 | } | ||
| 729 | |||
| 730 | spin_lock(&ap->sta_table_lock); | ||
| 731 | sta = ap_get_sta(ap, hdr->addr1); | ||
| 732 | if (sta) | ||
| 733 | atomic_inc(&sta->users); | ||
| 734 | spin_unlock(&ap->sta_table_lock); | ||
| 735 | |||
| 736 | if (!sta) { | ||
| 737 | txt = "STA not found"; | ||
| 738 | goto done; | ||
| 739 | } | ||
| 740 | |||
| 741 | pos = (__le16 *) (skb->data + IEEE80211_MGMT_HDR_LEN); | ||
| 742 | pos++; | ||
| 743 | status = le16_to_cpu(*pos++); | ||
| 744 | if (status == WLAN_STATUS_SUCCESS) { | ||
| 745 | if (!(sta->flags & WLAN_STA_ASSOC)) | ||
| 746 | hostap_event_new_sta(dev, sta); | ||
| 747 | txt = "STA associated"; | ||
| 748 | sta->flags |= WLAN_STA_ASSOC; | ||
| 749 | sta->last_assoc = jiffies; | ||
| 750 | } else | ||
| 751 | txt = "association failed"; | ||
| 752 | |||
| 753 | done: | ||
| 754 | if (sta) | ||
| 755 | atomic_dec(&sta->users); | ||
| 756 | if (txt) { | ||
| 757 | PDEBUG(DEBUG_AP, "%s: %pM assoc_cb - %s\n", | ||
| 758 | dev->name, hdr->addr1, txt); | ||
| 759 | } | ||
| 760 | dev_kfree_skb(skb); | ||
| 761 | } | ||
| 762 | |||
| 763 | /* Called only as a tasklet (software IRQ); TX callback for poll frames used | ||
| 764 | * in verifying whether the STA is still present. */ | ||
| 765 | static void hostap_ap_tx_cb_poll(struct sk_buff *skb, int ok, void *data) | ||
| 766 | { | ||
| 767 | struct ap_data *ap = data; | ||
| 768 | struct ieee80211_hdr *hdr; | ||
| 769 | struct sta_info *sta; | ||
| 770 | |||
| 771 | if (skb->len < 24) | ||
| 772 | goto fail; | ||
| 773 | hdr = (struct ieee80211_hdr *) skb->data; | ||
| 774 | if (ok) { | ||
| 775 | spin_lock(&ap->sta_table_lock); | ||
| 776 | sta = ap_get_sta(ap, hdr->addr1); | ||
| 777 | if (sta) | ||
| 778 | sta->flags &= ~WLAN_STA_PENDING_POLL; | ||
| 779 | spin_unlock(&ap->sta_table_lock); | ||
| 780 | } else { | ||
| 781 | PDEBUG(DEBUG_AP, | ||
| 782 | "%s: STA %pM did not ACK activity poll frame\n", | ||
| 783 | ap->local->dev->name, hdr->addr1); | ||
| 784 | } | ||
| 785 | |||
| 786 | fail: | ||
| 787 | dev_kfree_skb(skb); | ||
| 788 | } | ||
| 789 | #endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ | ||
| 790 | |||
| 791 | |||
| 792 | void hostap_init_data(local_info_t *local) | ||
| 793 | { | ||
| 794 | struct ap_data *ap = local->ap; | ||
| 795 | |||
| 796 | if (ap == NULL) { | ||
| 797 | printk(KERN_WARNING "hostap_init_data: ap == NULL\n"); | ||
| 798 | return; | ||
| 799 | } | ||
| 800 | memset(ap, 0, sizeof(struct ap_data)); | ||
| 801 | ap->local = local; | ||
| 802 | |||
| 803 | ap->ap_policy = GET_INT_PARM(other_ap_policy, local->card_idx); | ||
| 804 | ap->bridge_packets = GET_INT_PARM(ap_bridge_packets, local->card_idx); | ||
| 805 | ap->max_inactivity = | ||
| 806 | GET_INT_PARM(ap_max_inactivity, local->card_idx) * HZ; | ||
| 807 | ap->autom_ap_wds = GET_INT_PARM(autom_ap_wds, local->card_idx); | ||
| 808 | |||
| 809 | spin_lock_init(&ap->sta_table_lock); | ||
| 810 | INIT_LIST_HEAD(&ap->sta_list); | ||
| 811 | |||
| 812 | /* Initialize task queue structure for AP management */ | ||
| 813 | INIT_WORK(&local->ap->add_sta_proc_queue, handle_add_proc_queue); | ||
| 814 | |||
| 815 | ap->tx_callback_idx = | ||
| 816 | hostap_tx_callback_register(local, hostap_ap_tx_cb, ap); | ||
| 817 | if (ap->tx_callback_idx == 0) | ||
| 818 | printk(KERN_WARNING "%s: failed to register TX callback for " | ||
| 819 | "AP\n", local->dev->name); | ||
| 820 | #ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT | ||
| 821 | INIT_WORK(&local->ap->wds_oper_queue, handle_wds_oper_queue); | ||
| 822 | |||
| 823 | ap->tx_callback_auth = | ||
| 824 | hostap_tx_callback_register(local, hostap_ap_tx_cb_auth, ap); | ||
| 825 | ap->tx_callback_assoc = | ||
| 826 | hostap_tx_callback_register(local, hostap_ap_tx_cb_assoc, ap); | ||
| 827 | ap->tx_callback_poll = | ||
| 828 | hostap_tx_callback_register(local, hostap_ap_tx_cb_poll, ap); | ||
| 829 | if (ap->tx_callback_auth == 0 || ap->tx_callback_assoc == 0 || | ||
| 830 | ap->tx_callback_poll == 0) | ||
| 831 | printk(KERN_WARNING "%s: failed to register TX callback for " | ||
| 832 | "AP\n", local->dev->name); | ||
| 833 | |||
| 834 | spin_lock_init(&ap->mac_restrictions.lock); | ||
| 835 | INIT_LIST_HEAD(&ap->mac_restrictions.mac_list); | ||
| 836 | #endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ | ||
| 837 | |||
| 838 | ap->initialized = 1; | ||
| 839 | } | ||
| 840 | |||
| 841 | |||
| 842 | void hostap_init_ap_proc(local_info_t *local) | ||
| 843 | { | ||
| 844 | struct ap_data *ap = local->ap; | ||
| 845 | |||
| 846 | ap->proc = local->proc; | ||
| 847 | if (ap->proc == NULL) | ||
| 848 | return; | ||
| 849 | |||
| 850 | #ifndef PRISM2_NO_PROCFS_DEBUG | ||
| 851 | proc_create_single_data("ap_debug", 0, ap->proc, ap_debug_proc_show, ap); | ||
| 852 | #endif /* PRISM2_NO_PROCFS_DEBUG */ | ||
| 853 | |||
| 854 | #ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT | ||
| 855 | proc_create_seq_data("ap_control", 0, ap->proc, &ap_control_proc_seqops, | ||
| 856 | ap); | ||
| 857 | proc_create_seq_data("ap", 0, ap->proc, &prism2_ap_proc_seqops, ap); | ||
| 858 | #endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ | ||
| 859 | |||
| 860 | } | ||
| 861 | |||
| 862 | |||
| 863 | void hostap_free_data(struct ap_data *ap) | ||
| 864 | { | ||
| 865 | struct sta_info *n, *sta; | ||
| 866 | |||
| 867 | if (ap == NULL || !ap->initialized) { | ||
| 868 | printk(KERN_DEBUG "hostap_free_data: ap has not yet been " | ||
| 869 | "initialized - skip resource freeing\n"); | ||
| 870 | return; | ||
| 871 | } | ||
| 872 | |||
| 873 | flush_work(&ap->add_sta_proc_queue); | ||
| 874 | |||
| 875 | #ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT | ||
| 876 | flush_work(&ap->wds_oper_queue); | ||
| 877 | if (ap->crypt) | ||
| 878 | ap->crypt->deinit(ap->crypt_priv); | ||
| 879 | ap->crypt = ap->crypt_priv = NULL; | ||
| 880 | #endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ | ||
| 881 | |||
| 882 | list_for_each_entry_safe(sta, n, &ap->sta_list, list) { | ||
| 883 | ap_sta_hash_del(ap, sta); | ||
| 884 | list_del(&sta->list); | ||
| 885 | if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local) | ||
| 886 | hostap_event_expired_sta(sta->local->dev, sta); | ||
| 887 | ap_free_sta(ap, sta); | ||
| 888 | } | ||
| 889 | |||
| 890 | #ifndef PRISM2_NO_PROCFS_DEBUG | ||
| 891 | if (ap->proc != NULL) { | ||
| 892 | remove_proc_entry("ap_debug", ap->proc); | ||
| 893 | } | ||
| 894 | #endif /* PRISM2_NO_PROCFS_DEBUG */ | ||
| 895 | |||
| 896 | #ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT | ||
| 897 | if (ap->proc != NULL) { | ||
| 898 | remove_proc_entry("ap", ap->proc); | ||
| 899 | remove_proc_entry("ap_control", ap->proc); | ||
| 900 | } | ||
| 901 | ap_control_flush_macs(&ap->mac_restrictions); | ||
| 902 | #endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ | ||
| 903 | |||
| 904 | ap->initialized = 0; | ||
| 905 | } | ||
| 906 | |||
| 907 | |||
| 908 | /* caller should have mutex for AP STA list handling */ | ||
| 909 | static struct sta_info* ap_get_sta(struct ap_data *ap, u8 *sta) | ||
| 910 | { | ||
| 911 | struct sta_info *s; | ||
| 912 | |||
| 913 | s = ap->sta_hash[STA_HASH(sta)]; | ||
| 914 | while (s != NULL && !ether_addr_equal(s->addr, sta)) | ||
| 915 | s = s->hnext; | ||
| 916 | return s; | ||
| 917 | } | ||
| 918 | |||
| 919 | |||
| 920 | #ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT | ||
| 921 | |||
| 922 | /* Called from timer handler and from scheduled AP queue handlers */ | ||
| 923 | static void prism2_send_mgmt(struct net_device *dev, | ||
| 924 | u16 type_subtype, char *body, | ||
| 925 | int body_len, u8 *addr, u16 tx_cb_idx) | ||
| 926 | { | ||
| 927 | struct hostap_interface *iface; | ||
| 928 | local_info_t *local; | ||
| 929 | struct ieee80211_hdr *hdr; | ||
| 930 | u16 fc; | ||
| 931 | struct sk_buff *skb; | ||
| 932 | struct hostap_skb_tx_data *meta; | ||
| 933 | int hdrlen; | ||
| 934 | |||
| 935 | iface = netdev_priv(dev); | ||
| 936 | local = iface->local; | ||
| 937 | dev = local->dev; /* always use master radio device */ | ||
| 938 | iface = netdev_priv(dev); | ||
| 939 | |||
| 940 | if (!(dev->flags & IFF_UP)) { | ||
| 941 | PDEBUG(DEBUG_AP, "%s: prism2_send_mgmt - device is not UP - " | ||
| 942 | "cannot send frame\n", dev->name); | ||
| 943 | return; | ||
| 944 | } | ||
| 945 | |||
| 946 | skb = dev_alloc_skb(sizeof(*hdr) + body_len); | ||
| 947 | if (skb == NULL) { | ||
| 948 | PDEBUG(DEBUG_AP, "%s: prism2_send_mgmt failed to allocate " | ||
| 949 | "skb\n", dev->name); | ||
| 950 | return; | ||
| 951 | } | ||
| 952 | |||
| 953 | fc = type_subtype; | ||
| 954 | hdrlen = hostap_80211_get_hdrlen(cpu_to_le16(type_subtype)); | ||
| 955 | hdr = skb_put_zero(skb, hdrlen); | ||
| 956 | if (body) | ||
| 957 | skb_put_data(skb, body, body_len); | ||
| 958 | |||
| 959 | /* FIX: ctrl::ack sending used special HFA384X_TX_CTRL_802_11 | ||
| 960 | * tx_control instead of using local->tx_control */ | ||
| 961 | |||
| 962 | |||
| 963 | memcpy(hdr->addr1, addr, ETH_ALEN); /* DA / RA */ | ||
| 964 | if (ieee80211_is_data(hdr->frame_control)) { | ||
| 965 | fc |= IEEE80211_FCTL_FROMDS; | ||
| 966 | memcpy(hdr->addr2, dev->dev_addr, ETH_ALEN); /* BSSID */ | ||
| 967 | memcpy(hdr->addr3, dev->dev_addr, ETH_ALEN); /* SA */ | ||
| 968 | } else if (ieee80211_is_ctl(hdr->frame_control)) { | ||
| 969 | /* control:ACK does not have addr2 or addr3 */ | ||
| 970 | eth_zero_addr(hdr->addr2); | ||
| 971 | eth_zero_addr(hdr->addr3); | ||
| 972 | } else { | ||
| 973 | memcpy(hdr->addr2, dev->dev_addr, ETH_ALEN); /* SA */ | ||
| 974 | memcpy(hdr->addr3, dev->dev_addr, ETH_ALEN); /* BSSID */ | ||
| 975 | } | ||
| 976 | |||
| 977 | hdr->frame_control = cpu_to_le16(fc); | ||
| 978 | |||
| 979 | meta = (struct hostap_skb_tx_data *) skb->cb; | ||
| 980 | memset(meta, 0, sizeof(*meta)); | ||
| 981 | meta->magic = HOSTAP_SKB_TX_DATA_MAGIC; | ||
| 982 | meta->iface = iface; | ||
| 983 | meta->tx_cb_idx = tx_cb_idx; | ||
| 984 | |||
| 985 | skb->dev = dev; | ||
| 986 | skb_reset_mac_header(skb); | ||
| 987 | skb_reset_network_header(skb); | ||
| 988 | dev_queue_xmit(skb); | ||
| 989 | } | ||
| 990 | #endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ | ||
| 991 | |||
| 992 | #ifdef CONFIG_PROC_FS | ||
| 993 | static int prism2_sta_proc_show(struct seq_file *m, void *v) | ||
| 994 | { | ||
| 995 | struct sta_info *sta = m->private; | ||
| 996 | int i; | ||
| 997 | |||
| 998 | /* FIX: possible race condition.. the STA data could have just expired, | ||
| 999 | * but proc entry was still here so that the read could have started; | ||
| 1000 | * some locking should be done here.. */ | ||
| 1001 | |||
| 1002 | seq_printf(m, | ||
| 1003 | "%s=%pM\nusers=%d\naid=%d\n" | ||
| 1004 | "flags=0x%04x%s%s%s%s%s%s%s\n" | ||
| 1005 | "capability=0x%02x\nlisten_interval=%d\nsupported_rates=", | ||
| 1006 | sta->ap ? "AP" : "STA", | ||
| 1007 | sta->addr, atomic_read(&sta->users), sta->aid, | ||
| 1008 | sta->flags, | ||
| 1009 | sta->flags & WLAN_STA_AUTH ? " AUTH" : "", | ||
| 1010 | sta->flags & WLAN_STA_ASSOC ? " ASSOC" : "", | ||
| 1011 | sta->flags & WLAN_STA_PS ? " PS" : "", | ||
| 1012 | sta->flags & WLAN_STA_TIM ? " TIM" : "", | ||
| 1013 | sta->flags & WLAN_STA_PERM ? " PERM" : "", | ||
| 1014 | sta->flags & WLAN_STA_AUTHORIZED ? " AUTHORIZED" : "", | ||
| 1015 | sta->flags & WLAN_STA_PENDING_POLL ? " POLL" : "", | ||
| 1016 | sta->capability, sta->listen_interval); | ||
| 1017 | /* supported_rates: 500 kbit/s units with msb ignored */ | ||
| 1018 | for (i = 0; i < sizeof(sta->supported_rates); i++) | ||
| 1019 | if (sta->supported_rates[i] != 0) | ||
| 1020 | seq_printf(m, "%d%sMbps ", | ||
| 1021 | (sta->supported_rates[i] & 0x7f) / 2, | ||
| 1022 | sta->supported_rates[i] & 1 ? ".5" : ""); | ||
| 1023 | seq_printf(m, | ||
| 1024 | "\njiffies=%lu\nlast_auth=%lu\nlast_assoc=%lu\n" | ||
| 1025 | "last_rx=%lu\nlast_tx=%lu\nrx_packets=%lu\n" | ||
| 1026 | "tx_packets=%lu\n" | ||
| 1027 | "rx_bytes=%lu\ntx_bytes=%lu\nbuffer_count=%d\n" | ||
| 1028 | "last_rx: silence=%d dBm signal=%d dBm rate=%d%s Mbps\n" | ||
| 1029 | "tx_rate=%d\ntx[1M]=%d\ntx[2M]=%d\ntx[5.5M]=%d\n" | ||
| 1030 | "tx[11M]=%d\n" | ||
| 1031 | "rx[1M]=%d\nrx[2M]=%d\nrx[5.5M]=%d\nrx[11M]=%d\n", | ||
| 1032 | jiffies, sta->last_auth, sta->last_assoc, sta->last_rx, | ||
| 1033 | sta->last_tx, | ||
| 1034 | sta->rx_packets, sta->tx_packets, sta->rx_bytes, | ||
| 1035 | sta->tx_bytes, skb_queue_len(&sta->tx_buf), | ||
| 1036 | sta->last_rx_silence, | ||
| 1037 | sta->last_rx_signal, sta->last_rx_rate / 10, | ||
| 1038 | sta->last_rx_rate % 10 ? ".5" : "", | ||
| 1039 | sta->tx_rate, sta->tx_count[0], sta->tx_count[1], | ||
| 1040 | sta->tx_count[2], sta->tx_count[3], sta->rx_count[0], | ||
| 1041 | sta->rx_count[1], sta->rx_count[2], sta->rx_count[3]); | ||
| 1042 | if (sta->crypt && sta->crypt->ops && sta->crypt->ops->print_stats) | ||
| 1043 | sta->crypt->ops->print_stats(m, sta->crypt->priv); | ||
| 1044 | #ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT | ||
| 1045 | if (sta->ap) { | ||
| 1046 | if (sta->u.ap.channel >= 0) | ||
| 1047 | seq_printf(m, "channel=%d\n", sta->u.ap.channel); | ||
| 1048 | seq_puts(m, "ssid="); | ||
| 1049 | for (i = 0; i < sta->u.ap.ssid_len; i++) { | ||
| 1050 | if (sta->u.ap.ssid[i] >= 32 && sta->u.ap.ssid[i] < 127) | ||
| 1051 | seq_putc(m, sta->u.ap.ssid[i]); | ||
| 1052 | else | ||
| 1053 | seq_printf(m, "<%02x>", sta->u.ap.ssid[i]); | ||
| 1054 | } | ||
| 1055 | seq_putc(m, '\n'); | ||
| 1056 | } | ||
| 1057 | #endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ | ||
| 1058 | |||
| 1059 | return 0; | ||
| 1060 | } | ||
| 1061 | #endif | ||
| 1062 | |||
| 1063 | static void handle_add_proc_queue(struct work_struct *work) | ||
| 1064 | { | ||
| 1065 | struct ap_data *ap = container_of(work, struct ap_data, | ||
| 1066 | add_sta_proc_queue); | ||
| 1067 | struct sta_info *sta; | ||
| 1068 | char name[20]; | ||
| 1069 | struct add_sta_proc_data *entry, *prev; | ||
| 1070 | |||
| 1071 | entry = ap->add_sta_proc_entries; | ||
| 1072 | ap->add_sta_proc_entries = NULL; | ||
| 1073 | |||
| 1074 | while (entry) { | ||
| 1075 | spin_lock_bh(&ap->sta_table_lock); | ||
| 1076 | sta = ap_get_sta(ap, entry->addr); | ||
| 1077 | if (sta) | ||
| 1078 | atomic_inc(&sta->users); | ||
| 1079 | spin_unlock_bh(&ap->sta_table_lock); | ||
| 1080 | |||
| 1081 | if (sta) { | ||
| 1082 | sprintf(name, "%pM", sta->addr); | ||
| 1083 | sta->proc = proc_create_single_data( | ||
| 1084 | name, 0, ap->proc, | ||
| 1085 | prism2_sta_proc_show, sta); | ||
| 1086 | |||
| 1087 | atomic_dec(&sta->users); | ||
| 1088 | } | ||
| 1089 | |||
| 1090 | prev = entry; | ||
| 1091 | entry = entry->next; | ||
| 1092 | kfree(prev); | ||
| 1093 | } | ||
| 1094 | } | ||
| 1095 | |||
| 1096 | |||
| 1097 | static struct sta_info * ap_add_sta(struct ap_data *ap, u8 *addr) | ||
| 1098 | { | ||
| 1099 | struct sta_info *sta; | ||
| 1100 | |||
| 1101 | sta = kzalloc(sizeof(struct sta_info), GFP_ATOMIC); | ||
| 1102 | if (sta == NULL) { | ||
| 1103 | PDEBUG(DEBUG_AP, "AP: kmalloc failed\n"); | ||
| 1104 | return NULL; | ||
| 1105 | } | ||
| 1106 | |||
| 1107 | /* initialize STA info data */ | ||
| 1108 | sta->local = ap->local; | ||
| 1109 | skb_queue_head_init(&sta->tx_buf); | ||
| 1110 | memcpy(sta->addr, addr, ETH_ALEN); | ||
| 1111 | |||
| 1112 | atomic_inc(&sta->users); | ||
| 1113 | spin_lock_bh(&ap->sta_table_lock); | ||
| 1114 | list_add(&sta->list, &ap->sta_list); | ||
| 1115 | ap->num_sta++; | ||
| 1116 | ap_sta_hash_add(ap, sta); | ||
| 1117 | spin_unlock_bh(&ap->sta_table_lock); | ||
| 1118 | |||
| 1119 | if (ap->proc) { | ||
| 1120 | struct add_sta_proc_data *entry; | ||
| 1121 | /* schedule a non-interrupt context process to add a procfs | ||
| 1122 | * entry for the STA since procfs code use GFP_KERNEL */ | ||
| 1123 | entry = kmalloc(sizeof(*entry), GFP_ATOMIC); | ||
| 1124 | if (entry) { | ||
| 1125 | memcpy(entry->addr, sta->addr, ETH_ALEN); | ||
| 1126 | entry->next = ap->add_sta_proc_entries; | ||
| 1127 | ap->add_sta_proc_entries = entry; | ||
| 1128 | schedule_work(&ap->add_sta_proc_queue); | ||
| 1129 | } else | ||
| 1130 | printk(KERN_DEBUG "Failed to add STA proc data\n"); | ||
| 1131 | } | ||
| 1132 | |||
| 1133 | #ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT | ||
| 1134 | timer_setup(&sta->timer, ap_handle_timer, 0); | ||
| 1135 | sta->timer.expires = jiffies + ap->max_inactivity; | ||
| 1136 | if (!ap->local->hostapd) | ||
| 1137 | add_timer(&sta->timer); | ||
| 1138 | #endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ | ||
| 1139 | |||
| 1140 | return sta; | ||
| 1141 | } | ||
| 1142 | |||
| 1143 | |||
| 1144 | static int ap_tx_rate_ok(int rateidx, struct sta_info *sta, | ||
| 1145 | local_info_t *local) | ||
| 1146 | { | ||
| 1147 | if (rateidx > sta->tx_max_rate || | ||
| 1148 | !(sta->tx_supp_rates & (1 << rateidx))) | ||
| 1149 | return 0; | ||
| 1150 | |||
| 1151 | if (local->tx_rate_control != 0 && | ||
| 1152 | !(local->tx_rate_control & (1 << rateidx))) | ||
| 1153 | return 0; | ||
| 1154 | |||
| 1155 | return 1; | ||
| 1156 | } | ||
| 1157 | |||
| 1158 | |||
| 1159 | static void prism2_check_tx_rates(struct sta_info *sta) | ||
| 1160 | { | ||
| 1161 | int i; | ||
| 1162 | |||
| 1163 | sta->tx_supp_rates = 0; | ||
| 1164 | for (i = 0; i < sizeof(sta->supported_rates); i++) { | ||
| 1165 | if ((sta->supported_rates[i] & 0x7f) == 2) | ||
| 1166 | sta->tx_supp_rates |= WLAN_RATE_1M; | ||
| 1167 | if ((sta->supported_rates[i] & 0x7f) == 4) | ||
| 1168 | sta->tx_supp_rates |= WLAN_RATE_2M; | ||
| 1169 | if ((sta->supported_rates[i] & 0x7f) == 11) | ||
| 1170 | sta->tx_supp_rates |= WLAN_RATE_5M5; | ||
| 1171 | if ((sta->supported_rates[i] & 0x7f) == 22) | ||
| 1172 | sta->tx_supp_rates |= WLAN_RATE_11M; | ||
| 1173 | } | ||
| 1174 | sta->tx_max_rate = sta->tx_rate = sta->tx_rate_idx = 0; | ||
| 1175 | if (sta->tx_supp_rates & WLAN_RATE_1M) { | ||
| 1176 | sta->tx_max_rate = 0; | ||
| 1177 | if (ap_tx_rate_ok(0, sta, sta->local)) { | ||
| 1178 | sta->tx_rate = 10; | ||
| 1179 | sta->tx_rate_idx = 0; | ||
| 1180 | } | ||
| 1181 | } | ||
| 1182 | if (sta->tx_supp_rates & WLAN_RATE_2M) { | ||
| 1183 | sta->tx_max_rate = 1; | ||
| 1184 | if (ap_tx_rate_ok(1, sta, sta->local)) { | ||
| 1185 | sta->tx_rate = 20; | ||
| 1186 | sta->tx_rate_idx = 1; | ||
| 1187 | } | ||
| 1188 | } | ||
| 1189 | if (sta->tx_supp_rates & WLAN_RATE_5M5) { | ||
| 1190 | sta->tx_max_rate = 2; | ||
| 1191 | if (ap_tx_rate_ok(2, sta, sta->local)) { | ||
| 1192 | sta->tx_rate = 55; | ||
| 1193 | sta->tx_rate_idx = 2; | ||
| 1194 | } | ||
| 1195 | } | ||
| 1196 | if (sta->tx_supp_rates & WLAN_RATE_11M) { | ||
| 1197 | sta->tx_max_rate = 3; | ||
| 1198 | if (ap_tx_rate_ok(3, sta, sta->local)) { | ||
| 1199 | sta->tx_rate = 110; | ||
| 1200 | sta->tx_rate_idx = 3; | ||
| 1201 | } | ||
| 1202 | } | ||
| 1203 | } | ||
| 1204 | |||
| 1205 | |||
| 1206 | #ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT | ||
| 1207 | |||
| 1208 | static void ap_crypt_init(struct ap_data *ap) | ||
| 1209 | { | ||
| 1210 | ap->crypt = lib80211_get_crypto_ops("WEP"); | ||
| 1211 | |||
| 1212 | if (ap->crypt) { | ||
| 1213 | if (ap->crypt->init) { | ||
| 1214 | ap->crypt_priv = ap->crypt->init(0); | ||
| 1215 | if (ap->crypt_priv == NULL) | ||
| 1216 | ap->crypt = NULL; | ||
| 1217 | else { | ||
| 1218 | u8 key[WEP_KEY_LEN]; | ||
| 1219 | get_random_bytes(key, WEP_KEY_LEN); | ||
| 1220 | ap->crypt->set_key(key, WEP_KEY_LEN, NULL, | ||
| 1221 | ap->crypt_priv); | ||
| 1222 | } | ||
| 1223 | } | ||
| 1224 | } | ||
| 1225 | |||
| 1226 | if (ap->crypt == NULL) { | ||
| 1227 | printk(KERN_WARNING "AP could not initialize WEP: load module " | ||
| 1228 | "lib80211_crypt_wep.ko\n"); | ||
| 1229 | } | ||
| 1230 | } | ||
| 1231 | |||
| 1232 | |||
| 1233 | /* Generate challenge data for shared key authentication. IEEE 802.11 specifies | ||
| 1234 | * that WEP algorithm is used for generating challenge. This should be unique, | ||
| 1235 | * but otherwise there is not really need for randomness etc. Initialize WEP | ||
| 1236 | * with pseudo random key and then use increasing IV to get unique challenge | ||
| 1237 | * streams. | ||
| 1238 | * | ||
| 1239 | * Called only as a scheduled task for pending AP frames. | ||
| 1240 | */ | ||
| 1241 | static char * ap_auth_make_challenge(struct ap_data *ap) | ||
| 1242 | { | ||
| 1243 | char *tmpbuf; | ||
| 1244 | struct sk_buff *skb; | ||
| 1245 | |||
| 1246 | if (ap->crypt == NULL) { | ||
| 1247 | ap_crypt_init(ap); | ||
| 1248 | if (ap->crypt == NULL) | ||
| 1249 | return NULL; | ||
| 1250 | } | ||
| 1251 | |||
| 1252 | tmpbuf = kmalloc(WLAN_AUTH_CHALLENGE_LEN, GFP_ATOMIC); | ||
| 1253 | if (tmpbuf == NULL) { | ||
| 1254 | PDEBUG(DEBUG_AP, "AP: kmalloc failed for challenge\n"); | ||
| 1255 | return NULL; | ||
| 1256 | } | ||
| 1257 | |||
| 1258 | skb = dev_alloc_skb(WLAN_AUTH_CHALLENGE_LEN + | ||
| 1259 | ap->crypt->extra_mpdu_prefix_len + | ||
| 1260 | ap->crypt->extra_mpdu_postfix_len); | ||
| 1261 | if (skb == NULL) { | ||
| 1262 | kfree(tmpbuf); | ||
| 1263 | return NULL; | ||
| 1264 | } | ||
| 1265 | |||
| 1266 | skb_reserve(skb, ap->crypt->extra_mpdu_prefix_len); | ||
| 1267 | skb_put_zero(skb, WLAN_AUTH_CHALLENGE_LEN); | ||
| 1268 | if (ap->crypt->encrypt_mpdu(skb, 0, ap->crypt_priv)) { | ||
| 1269 | dev_kfree_skb(skb); | ||
| 1270 | kfree(tmpbuf); | ||
| 1271 | return NULL; | ||
| 1272 | } | ||
| 1273 | |||
| 1274 | skb_copy_from_linear_data_offset(skb, ap->crypt->extra_mpdu_prefix_len, | ||
| 1275 | tmpbuf, WLAN_AUTH_CHALLENGE_LEN); | ||
| 1276 | dev_kfree_skb(skb); | ||
| 1277 | |||
| 1278 | return tmpbuf; | ||
| 1279 | } | ||
| 1280 | |||
| 1281 | |||
| 1282 | /* Called only as a scheduled task for pending AP frames. */ | ||
| 1283 | static void handle_authen(local_info_t *local, struct sk_buff *skb, | ||
| 1284 | struct hostap_80211_rx_status *rx_stats) | ||
| 1285 | { | ||
| 1286 | struct net_device *dev = local->dev; | ||
| 1287 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; | ||
| 1288 | size_t hdrlen; | ||
| 1289 | struct ap_data *ap = local->ap; | ||
| 1290 | char body[8 + WLAN_AUTH_CHALLENGE_LEN], *challenge = NULL; | ||
| 1291 | int len, olen; | ||
| 1292 | u16 auth_alg, auth_transaction, status_code; | ||
| 1293 | __le16 *pos; | ||
| 1294 | u16 resp = WLAN_STATUS_SUCCESS; | ||
| 1295 | struct sta_info *sta = NULL; | ||
| 1296 | struct lib80211_crypt_data *crypt; | ||
| 1297 | char *txt = ""; | ||
| 1298 | |||
| 1299 | len = skb->len - IEEE80211_MGMT_HDR_LEN; | ||
| 1300 | |||
| 1301 | hdrlen = hostap_80211_get_hdrlen(hdr->frame_control); | ||
| 1302 | |||
| 1303 | if (len < 6) { | ||
| 1304 | PDEBUG(DEBUG_AP, "%s: handle_authen - too short payload " | ||
| 1305 | "(len=%d) from %pM\n", dev->name, len, hdr->addr2); | ||
| 1306 | return; | ||
| 1307 | } | ||
| 1308 | |||
| 1309 | spin_lock_bh(&local->ap->sta_table_lock); | ||
| 1310 | sta = ap_get_sta(local->ap, hdr->addr2); | ||
| 1311 | if (sta) | ||
| 1312 | atomic_inc(&sta->users); | ||
| 1313 | spin_unlock_bh(&local->ap->sta_table_lock); | ||
| 1314 | |||
| 1315 | if (sta && sta->crypt) | ||
| 1316 | crypt = sta->crypt; | ||
| 1317 | else { | ||
| 1318 | int idx = 0; | ||
| 1319 | if (skb->len >= hdrlen + 3) | ||
| 1320 | idx = skb->data[hdrlen + 3] >> 6; | ||
| 1321 | crypt = local->crypt_info.crypt[idx]; | ||
| 1322 | } | ||
| 1323 | |||
| 1324 | pos = (__le16 *) (skb->data + IEEE80211_MGMT_HDR_LEN); | ||
| 1325 | auth_alg = __le16_to_cpu(*pos); | ||
| 1326 | pos++; | ||
| 1327 | auth_transaction = __le16_to_cpu(*pos); | ||
| 1328 | pos++; | ||
| 1329 | status_code = __le16_to_cpu(*pos); | ||
| 1330 | pos++; | ||
| 1331 | |||
| 1332 | if (ether_addr_equal(dev->dev_addr, hdr->addr2) || | ||
| 1333 | ap_control_mac_deny(&ap->mac_restrictions, hdr->addr2)) { | ||
| 1334 | txt = "authentication denied"; | ||
| 1335 | resp = WLAN_STATUS_UNSPECIFIED_FAILURE; | ||
| 1336 | goto fail; | ||
| 1337 | } | ||
| 1338 | |||
| 1339 | if (((local->auth_algs & PRISM2_AUTH_OPEN) && | ||
| 1340 | auth_alg == WLAN_AUTH_OPEN) || | ||
| 1341 | ((local->auth_algs & PRISM2_AUTH_SHARED_KEY) && | ||
| 1342 | crypt && auth_alg == WLAN_AUTH_SHARED_KEY)) { | ||
| 1343 | } else { | ||
| 1344 | txt = "unsupported algorithm"; | ||
| 1345 | resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG; | ||
| 1346 | goto fail; | ||
| 1347 | } | ||
| 1348 | |||
| 1349 | if (len >= 8) { | ||
| 1350 | u8 *u = (u8 *) pos; | ||
| 1351 | if (*u == WLAN_EID_CHALLENGE) { | ||
| 1352 | if (*(u + 1) != WLAN_AUTH_CHALLENGE_LEN) { | ||
| 1353 | txt = "invalid challenge len"; | ||
| 1354 | resp = WLAN_STATUS_CHALLENGE_FAIL; | ||
| 1355 | goto fail; | ||
| 1356 | } | ||
| 1357 | if (len - 8 < WLAN_AUTH_CHALLENGE_LEN) { | ||
| 1358 | txt = "challenge underflow"; | ||
| 1359 | resp = WLAN_STATUS_CHALLENGE_FAIL; | ||
| 1360 | goto fail; | ||
| 1361 | } | ||
| 1362 | challenge = (char *) (u + 2); | ||
| 1363 | } | ||
| 1364 | } | ||
| 1365 | |||
| 1366 | if (sta && sta->ap) { | ||
| 1367 | if (time_after(jiffies, sta->u.ap.last_beacon + | ||
| 1368 | (10 * sta->listen_interval * HZ) / 1024)) { | ||
| 1369 | PDEBUG(DEBUG_AP, "%s: no beacons received for a while," | ||
| 1370 | " assuming AP %pM is now STA\n", | ||
| 1371 | dev->name, sta->addr); | ||
| 1372 | sta->ap = 0; | ||
| 1373 | sta->flags = 0; | ||
| 1374 | sta->u.sta.challenge = NULL; | ||
| 1375 | } else { | ||
| 1376 | txt = "AP trying to authenticate?"; | ||
| 1377 | resp = WLAN_STATUS_UNSPECIFIED_FAILURE; | ||
| 1378 | goto fail; | ||
| 1379 | } | ||
| 1380 | } | ||
| 1381 | |||
| 1382 | if ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 1) || | ||
| 1383 | (auth_alg == WLAN_AUTH_SHARED_KEY && | ||
| 1384 | (auth_transaction == 1 || | ||
| 1385 | (auth_transaction == 3 && sta != NULL && | ||
| 1386 | sta->u.sta.challenge != NULL)))) { | ||
| 1387 | } else { | ||
| 1388 | txt = "unknown authentication transaction number"; | ||
| 1389 | resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; | ||
| 1390 | goto fail; | ||
| 1391 | } | ||
| 1392 | |||
| 1393 | if (sta == NULL) { | ||
| 1394 | txt = "new STA"; | ||
| 1395 | |||
| 1396 | if (local->ap->num_sta >= MAX_STA_COUNT) { | ||
| 1397 | /* FIX: might try to remove some old STAs first? */ | ||
| 1398 | txt = "no more room for new STAs"; | ||
| 1399 | resp = WLAN_STATUS_UNSPECIFIED_FAILURE; | ||
| 1400 | goto fail; | ||
| 1401 | } | ||
| 1402 | |||
| 1403 | sta = ap_add_sta(local->ap, hdr->addr2); | ||
| 1404 | if (sta == NULL) { | ||
| 1405 | txt = "ap_add_sta failed"; | ||
| 1406 | resp = WLAN_STATUS_UNSPECIFIED_FAILURE; | ||
| 1407 | goto fail; | ||
| 1408 | } | ||
| 1409 | } | ||
| 1410 | |||
| 1411 | switch (auth_alg) { | ||
| 1412 | case WLAN_AUTH_OPEN: | ||
| 1413 | txt = "authOK"; | ||
| 1414 | /* IEEE 802.11 standard is not completely clear about | ||
| 1415 | * whether STA is considered authenticated after | ||
| 1416 | * authentication OK frame has been send or after it | ||
| 1417 | * has been ACKed. In order to reduce interoperability | ||
| 1418 | * issues, mark the STA authenticated before ACK. */ | ||
| 1419 | sta->flags |= WLAN_STA_AUTH; | ||
| 1420 | break; | ||
| 1421 | |||
| 1422 | case WLAN_AUTH_SHARED_KEY: | ||
| 1423 | if (auth_transaction == 1) { | ||
| 1424 | if (sta->u.sta.challenge == NULL) { | ||
| 1425 | sta->u.sta.challenge = | ||
| 1426 | ap_auth_make_challenge(local->ap); | ||
| 1427 | if (sta->u.sta.challenge == NULL) { | ||
| 1428 | resp = WLAN_STATUS_UNSPECIFIED_FAILURE; | ||
| 1429 | goto fail; | ||
| 1430 | } | ||
| 1431 | } | ||
| 1432 | } else { | ||
| 1433 | if (sta->u.sta.challenge == NULL || | ||
| 1434 | challenge == NULL || | ||
| 1435 | memcmp(sta->u.sta.challenge, challenge, | ||
| 1436 | WLAN_AUTH_CHALLENGE_LEN) != 0 || | ||
| 1437 | !ieee80211_has_protected(hdr->frame_control)) { | ||
| 1438 | txt = "challenge response incorrect"; | ||
| 1439 | resp = WLAN_STATUS_CHALLENGE_FAIL; | ||
| 1440 | goto fail; | ||
| 1441 | } | ||
| 1442 | |||
| 1443 | txt = "challenge OK - authOK"; | ||
| 1444 | /* IEEE 802.11 standard is not completely clear about | ||
| 1445 | * whether STA is considered authenticated after | ||
| 1446 | * authentication OK frame has been send or after it | ||
| 1447 | * has been ACKed. In order to reduce interoperability | ||
| 1448 | * issues, mark the STA authenticated before ACK. */ | ||
| 1449 | sta->flags |= WLAN_STA_AUTH; | ||
| 1450 | kfree(sta->u.sta.challenge); | ||
| 1451 | sta->u.sta.challenge = NULL; | ||
| 1452 | } | ||
| 1453 | break; | ||
| 1454 | } | ||
| 1455 | |||
| 1456 | fail: | ||
| 1457 | pos = (__le16 *) body; | ||
| 1458 | *pos = cpu_to_le16(auth_alg); | ||
| 1459 | pos++; | ||
| 1460 | *pos = cpu_to_le16(auth_transaction + 1); | ||
| 1461 | pos++; | ||
| 1462 | *pos = cpu_to_le16(resp); /* status_code */ | ||
| 1463 | pos++; | ||
| 1464 | olen = 6; | ||
| 1465 | |||
| 1466 | if (resp == WLAN_STATUS_SUCCESS && sta != NULL && | ||
| 1467 | sta->u.sta.challenge != NULL && | ||
| 1468 | auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 1) { | ||
| 1469 | u8 *tmp = (u8 *) pos; | ||
| 1470 | *tmp++ = WLAN_EID_CHALLENGE; | ||
| 1471 | *tmp++ = WLAN_AUTH_CHALLENGE_LEN; | ||
| 1472 | pos++; | ||
| 1473 | memcpy(pos, sta->u.sta.challenge, WLAN_AUTH_CHALLENGE_LEN); | ||
| 1474 | olen += 2 + WLAN_AUTH_CHALLENGE_LEN; | ||
| 1475 | } | ||
| 1476 | |||
| 1477 | prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH, | ||
| 1478 | body, olen, hdr->addr2, ap->tx_callback_auth); | ||
| 1479 | |||
| 1480 | if (sta) { | ||
| 1481 | sta->last_rx = jiffies; | ||
| 1482 | atomic_dec(&sta->users); | ||
| 1483 | } | ||
| 1484 | |||
| 1485 | if (resp) { | ||
| 1486 | PDEBUG(DEBUG_AP, "%s: %pM auth (alg=%d " | ||
| 1487 | "trans#=%d stat=%d len=%d fc=%04x) ==> %d (%s)\n", | ||
| 1488 | dev->name, hdr->addr2, | ||
| 1489 | auth_alg, auth_transaction, status_code, len, | ||
| 1490 | le16_to_cpu(hdr->frame_control), resp, txt); | ||
| 1491 | } | ||
| 1492 | } | ||
| 1493 | |||
| 1494 | |||
| 1495 | /* Called only as a scheduled task for pending AP frames. */ | ||
| 1496 | static void handle_assoc(local_info_t *local, struct sk_buff *skb, | ||
| 1497 | struct hostap_80211_rx_status *rx_stats, int reassoc) | ||
| 1498 | { | ||
| 1499 | struct net_device *dev = local->dev; | ||
| 1500 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; | ||
| 1501 | char body[12], *p, *lpos; | ||
| 1502 | int len, left; | ||
| 1503 | __le16 *pos; | ||
| 1504 | u16 resp = WLAN_STATUS_SUCCESS; | ||
| 1505 | struct sta_info *sta = NULL; | ||
| 1506 | int send_deauth = 0; | ||
| 1507 | char __always_unused *txt = ""; | ||
| 1508 | u8 prev_ap[ETH_ALEN]; | ||
| 1509 | |||
| 1510 | left = len = skb->len - IEEE80211_MGMT_HDR_LEN; | ||
| 1511 | |||
| 1512 | if (len < (reassoc ? 10 : 4)) { | ||
| 1513 | PDEBUG(DEBUG_AP, "%s: handle_assoc - too short payload " | ||
| 1514 | "(len=%d, reassoc=%d) from %pM\n", | ||
| 1515 | dev->name, len, reassoc, hdr->addr2); | ||
| 1516 | return; | ||
| 1517 | } | ||
| 1518 | |||
| 1519 | spin_lock_bh(&local->ap->sta_table_lock); | ||
| 1520 | sta = ap_get_sta(local->ap, hdr->addr2); | ||
| 1521 | if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) { | ||
| 1522 | spin_unlock_bh(&local->ap->sta_table_lock); | ||
| 1523 | txt = "trying to associate before authentication"; | ||
| 1524 | send_deauth = 1; | ||
| 1525 | resp = WLAN_STATUS_UNSPECIFIED_FAILURE; | ||
| 1526 | sta = NULL; /* do not decrement sta->users */ | ||
| 1527 | goto fail; | ||
| 1528 | } | ||
| 1529 | atomic_inc(&sta->users); | ||
| 1530 | spin_unlock_bh(&local->ap->sta_table_lock); | ||
| 1531 | |||
| 1532 | pos = (__le16 *) (skb->data + IEEE80211_MGMT_HDR_LEN); | ||
| 1533 | sta->capability = __le16_to_cpu(*pos); | ||
| 1534 | pos++; left -= 2; | ||
| 1535 | sta->listen_interval = __le16_to_cpu(*pos); | ||
| 1536 | pos++; left -= 2; | ||
| 1537 | |||
| 1538 | if (reassoc) { | ||
| 1539 | memcpy(prev_ap, pos, ETH_ALEN); | ||
| 1540 | pos++; pos++; pos++; left -= 6; | ||
| 1541 | } else | ||
| 1542 | eth_zero_addr(prev_ap); | ||
| 1543 | |||
| 1544 | if (left >= 2) { | ||
| 1545 | unsigned int ileft; | ||
| 1546 | unsigned char *u = (unsigned char *) pos; | ||
| 1547 | |||
| 1548 | if (*u == WLAN_EID_SSID) { | ||
| 1549 | u++; left--; | ||
| 1550 | ileft = *u; | ||
| 1551 | u++; left--; | ||
| 1552 | |||
| 1553 | if (ileft > left || ileft > MAX_SSID_LEN) { | ||
| 1554 | txt = "SSID overflow"; | ||
| 1555 | resp = WLAN_STATUS_UNSPECIFIED_FAILURE; | ||
| 1556 | goto fail; | ||
| 1557 | } | ||
| 1558 | |||
| 1559 | if (ileft != strlen(local->essid) || | ||
| 1560 | memcmp(local->essid, u, ileft) != 0) { | ||
| 1561 | txt = "not our SSID"; | ||
| 1562 | resp = WLAN_STATUS_ASSOC_DENIED_UNSPEC; | ||
| 1563 | goto fail; | ||
| 1564 | } | ||
| 1565 | |||
| 1566 | u += ileft; | ||
| 1567 | left -= ileft; | ||
| 1568 | } | ||
| 1569 | |||
| 1570 | if (left >= 2 && *u == WLAN_EID_SUPP_RATES) { | ||
| 1571 | u++; left--; | ||
| 1572 | ileft = *u; | ||
| 1573 | u++; left--; | ||
| 1574 | |||
| 1575 | if (ileft > left || ileft == 0 || | ||
| 1576 | ileft > WLAN_SUPP_RATES_MAX) { | ||
| 1577 | txt = "SUPP_RATES len error"; | ||
| 1578 | resp = WLAN_STATUS_UNSPECIFIED_FAILURE; | ||
| 1579 | goto fail; | ||
| 1580 | } | ||
| 1581 | |||
| 1582 | memset(sta->supported_rates, 0, | ||
| 1583 | sizeof(sta->supported_rates)); | ||
| 1584 | memcpy(sta->supported_rates, u, ileft); | ||
| 1585 | prism2_check_tx_rates(sta); | ||
| 1586 | |||
| 1587 | u += ileft; | ||
| 1588 | left -= ileft; | ||
| 1589 | } | ||
| 1590 | |||
| 1591 | if (left > 0) { | ||
| 1592 | PDEBUG(DEBUG_AP, "%s: assoc from %pM" | ||
| 1593 | " with extra data (%d bytes) [", | ||
| 1594 | dev->name, hdr->addr2, left); | ||
| 1595 | while (left > 0) { | ||
| 1596 | PDEBUG2(DEBUG_AP, "<%02x>", *u); | ||
| 1597 | u++; left--; | ||
| 1598 | } | ||
| 1599 | PDEBUG2(DEBUG_AP, "]\n"); | ||
| 1600 | } | ||
| 1601 | } else { | ||
| 1602 | txt = "frame underflow"; | ||
| 1603 | resp = WLAN_STATUS_UNSPECIFIED_FAILURE; | ||
| 1604 | goto fail; | ||
| 1605 | } | ||
| 1606 | |||
| 1607 | /* get a unique AID */ | ||
| 1608 | if (sta->aid > 0) | ||
| 1609 | txt = "OK, old AID"; | ||
| 1610 | else { | ||
| 1611 | spin_lock_bh(&local->ap->sta_table_lock); | ||
| 1612 | for (sta->aid = 1; sta->aid <= MAX_AID_TABLE_SIZE; sta->aid++) | ||
| 1613 | if (local->ap->sta_aid[sta->aid - 1] == NULL) | ||
| 1614 | break; | ||
| 1615 | if (sta->aid > MAX_AID_TABLE_SIZE) { | ||
| 1616 | sta->aid = 0; | ||
| 1617 | spin_unlock_bh(&local->ap->sta_table_lock); | ||
| 1618 | resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; | ||
| 1619 | txt = "no room for more AIDs"; | ||
| 1620 | } else { | ||
| 1621 | local->ap->sta_aid[sta->aid - 1] = sta; | ||
| 1622 | spin_unlock_bh(&local->ap->sta_table_lock); | ||
| 1623 | txt = "OK, new AID"; | ||
| 1624 | } | ||
| 1625 | } | ||
| 1626 | |||
| 1627 | fail: | ||
| 1628 | pos = (__le16 *) body; | ||
| 1629 | |||
| 1630 | if (send_deauth) { | ||
| 1631 | *pos = cpu_to_le16(WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH); | ||
| 1632 | pos++; | ||
| 1633 | } else { | ||
| 1634 | /* FIX: CF-Pollable and CF-PollReq should be set to match the | ||
| 1635 | * values in beacons/probe responses */ | ||
| 1636 | /* FIX: how about privacy and WEP? */ | ||
| 1637 | /* capability */ | ||
| 1638 | *pos = cpu_to_le16(WLAN_CAPABILITY_ESS); | ||
| 1639 | pos++; | ||
| 1640 | |||
| 1641 | /* status_code */ | ||
| 1642 | *pos = cpu_to_le16(resp); | ||
| 1643 | pos++; | ||
| 1644 | |||
| 1645 | *pos = cpu_to_le16((sta && sta->aid > 0 ? sta->aid : 0) | | ||
| 1646 | BIT(14) | BIT(15)); /* AID */ | ||
| 1647 | pos++; | ||
| 1648 | |||
| 1649 | /* Supported rates (Information element) */ | ||
| 1650 | p = (char *) pos; | ||
| 1651 | *p++ = WLAN_EID_SUPP_RATES; | ||
| 1652 | lpos = p; | ||
| 1653 | *p++ = 0; /* len */ | ||
| 1654 | if (local->tx_rate_control & WLAN_RATE_1M) { | ||
| 1655 | *p++ = local->basic_rates & WLAN_RATE_1M ? 0x82 : 0x02; | ||
| 1656 | (*lpos)++; | ||
| 1657 | } | ||
| 1658 | if (local->tx_rate_control & WLAN_RATE_2M) { | ||
| 1659 | *p++ = local->basic_rates & WLAN_RATE_2M ? 0x84 : 0x04; | ||
| 1660 | (*lpos)++; | ||
| 1661 | } | ||
| 1662 | if (local->tx_rate_control & WLAN_RATE_5M5) { | ||
| 1663 | *p++ = local->basic_rates & WLAN_RATE_5M5 ? | ||
| 1664 | 0x8b : 0x0b; | ||
| 1665 | (*lpos)++; | ||
| 1666 | } | ||
| 1667 | if (local->tx_rate_control & WLAN_RATE_11M) { | ||
| 1668 | *p++ = local->basic_rates & WLAN_RATE_11M ? | ||
| 1669 | 0x96 : 0x16; | ||
| 1670 | (*lpos)++; | ||
| 1671 | } | ||
| 1672 | pos = (__le16 *) p; | ||
| 1673 | } | ||
| 1674 | |||
| 1675 | prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT | | ||
| 1676 | (send_deauth ? IEEE80211_STYPE_DEAUTH : | ||
| 1677 | (reassoc ? IEEE80211_STYPE_REASSOC_RESP : | ||
| 1678 | IEEE80211_STYPE_ASSOC_RESP)), | ||
| 1679 | body, (u8 *) pos - (u8 *) body, | ||
| 1680 | hdr->addr2, | ||
| 1681 | send_deauth ? 0 : local->ap->tx_callback_assoc); | ||
| 1682 | |||
| 1683 | if (sta) { | ||
| 1684 | if (resp == WLAN_STATUS_SUCCESS) { | ||
| 1685 | sta->last_rx = jiffies; | ||
| 1686 | /* STA will be marked associated from TX callback, if | ||
| 1687 | * AssocResp is ACKed */ | ||
| 1688 | } | ||
| 1689 | atomic_dec(&sta->users); | ||
| 1690 | } | ||
| 1691 | |||
| 1692 | #if 0 | ||
| 1693 | PDEBUG(DEBUG_AP, "%s: %pM %sassoc (len=%d " | ||
| 1694 | "prev_ap=%pM) => %d(%d) (%s)\n", | ||
| 1695 | dev->name, | ||
| 1696 | hdr->addr2, | ||
| 1697 | reassoc ? "re" : "", len, | ||
| 1698 | prev_ap, | ||
| 1699 | resp, send_deauth, txt); | ||
| 1700 | #endif | ||
| 1701 | } | ||
| 1702 | |||
| 1703 | |||
| 1704 | /* Called only as a scheduled task for pending AP frames. */ | ||
| 1705 | static void handle_deauth(local_info_t *local, struct sk_buff *skb, | ||
| 1706 | struct hostap_80211_rx_status *rx_stats) | ||
| 1707 | { | ||
| 1708 | struct net_device *dev = local->dev; | ||
| 1709 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; | ||
| 1710 | char *body = (char *) (skb->data + IEEE80211_MGMT_HDR_LEN); | ||
| 1711 | int len; | ||
| 1712 | u16 reason_code; | ||
| 1713 | __le16 *pos; | ||
| 1714 | struct sta_info *sta = NULL; | ||
| 1715 | |||
| 1716 | len = skb->len - IEEE80211_MGMT_HDR_LEN; | ||
| 1717 | |||
| 1718 | if (len < 2) { | ||
| 1719 | printk("handle_deauth - too short payload (len=%d)\n", len); | ||
| 1720 | return; | ||
| 1721 | } | ||
| 1722 | |||
| 1723 | pos = (__le16 *) body; | ||
| 1724 | reason_code = le16_to_cpu(*pos); | ||
| 1725 | |||
| 1726 | PDEBUG(DEBUG_AP, "%s: deauthentication: %pM len=%d, " | ||
| 1727 | "reason_code=%d\n", dev->name, hdr->addr2, | ||
| 1728 | len, reason_code); | ||
| 1729 | |||
| 1730 | spin_lock_bh(&local->ap->sta_table_lock); | ||
| 1731 | sta = ap_get_sta(local->ap, hdr->addr2); | ||
| 1732 | if (sta != NULL) { | ||
| 1733 | if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap) | ||
| 1734 | hostap_event_expired_sta(local->dev, sta); | ||
| 1735 | sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); | ||
| 1736 | } | ||
| 1737 | spin_unlock_bh(&local->ap->sta_table_lock); | ||
| 1738 | if (sta == NULL) { | ||
| 1739 | printk("%s: deauthentication from %pM, " | ||
| 1740 | "reason_code=%d, but STA not authenticated\n", dev->name, | ||
| 1741 | hdr->addr2, reason_code); | ||
| 1742 | } | ||
| 1743 | } | ||
| 1744 | |||
| 1745 | |||
| 1746 | /* Called only as a scheduled task for pending AP frames. */ | ||
| 1747 | static void handle_disassoc(local_info_t *local, struct sk_buff *skb, | ||
| 1748 | struct hostap_80211_rx_status *rx_stats) | ||
| 1749 | { | ||
| 1750 | struct net_device *dev = local->dev; | ||
| 1751 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; | ||
| 1752 | char *body = skb->data + IEEE80211_MGMT_HDR_LEN; | ||
| 1753 | int len; | ||
| 1754 | u16 reason_code; | ||
| 1755 | __le16 *pos; | ||
| 1756 | struct sta_info *sta = NULL; | ||
| 1757 | |||
| 1758 | len = skb->len - IEEE80211_MGMT_HDR_LEN; | ||
| 1759 | |||
| 1760 | if (len < 2) { | ||
| 1761 | printk("handle_disassoc - too short payload (len=%d)\n", len); | ||
| 1762 | return; | ||
| 1763 | } | ||
| 1764 | |||
| 1765 | pos = (__le16 *) body; | ||
| 1766 | reason_code = le16_to_cpu(*pos); | ||
| 1767 | |||
| 1768 | PDEBUG(DEBUG_AP, "%s: disassociation: %pM len=%d, " | ||
| 1769 | "reason_code=%d\n", dev->name, hdr->addr2, | ||
| 1770 | len, reason_code); | ||
| 1771 | |||
| 1772 | spin_lock_bh(&local->ap->sta_table_lock); | ||
| 1773 | sta = ap_get_sta(local->ap, hdr->addr2); | ||
| 1774 | if (sta != NULL) { | ||
| 1775 | if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap) | ||
| 1776 | hostap_event_expired_sta(local->dev, sta); | ||
| 1777 | sta->flags &= ~WLAN_STA_ASSOC; | ||
| 1778 | } | ||
| 1779 | spin_unlock_bh(&local->ap->sta_table_lock); | ||
| 1780 | if (sta == NULL) { | ||
| 1781 | printk("%s: disassociation from %pM, " | ||
| 1782 | "reason_code=%d, but STA not authenticated\n", | ||
| 1783 | dev->name, hdr->addr2, reason_code); | ||
| 1784 | } | ||
| 1785 | } | ||
| 1786 | |||
| 1787 | |||
| 1788 | /* Called only as a scheduled task for pending AP frames. */ | ||
| 1789 | static void ap_handle_data_nullfunc(local_info_t *local, | ||
| 1790 | struct ieee80211_hdr *hdr) | ||
| 1791 | { | ||
| 1792 | struct net_device *dev = local->dev; | ||
| 1793 | |||
| 1794 | /* some STA f/w's seem to require control::ACK frame for | ||
| 1795 | * data::nullfunc, but at least Prism2 station f/w version 0.8.0 does | ||
| 1796 | * not send this.. | ||
| 1797 | * send control::ACK for the data::nullfunc */ | ||
| 1798 | |||
| 1799 | printk(KERN_DEBUG "Sending control::ACK for data::nullfunc\n"); | ||
| 1800 | prism2_send_mgmt(dev, IEEE80211_FTYPE_CTL | IEEE80211_STYPE_ACK, | ||
| 1801 | NULL, 0, hdr->addr2, 0); | ||
| 1802 | } | ||
| 1803 | |||
| 1804 | |||
| 1805 | /* Called only as a scheduled task for pending AP frames. */ | ||
| 1806 | static void ap_handle_dropped_data(local_info_t *local, | ||
| 1807 | struct ieee80211_hdr *hdr) | ||
| 1808 | { | ||
| 1809 | struct net_device *dev = local->dev; | ||
| 1810 | struct sta_info *sta; | ||
| 1811 | __le16 reason; | ||
| 1812 | |||
| 1813 | spin_lock_bh(&local->ap->sta_table_lock); | ||
| 1814 | sta = ap_get_sta(local->ap, hdr->addr2); | ||
| 1815 | if (sta) | ||
| 1816 | atomic_inc(&sta->users); | ||
| 1817 | spin_unlock_bh(&local->ap->sta_table_lock); | ||
| 1818 | |||
| 1819 | if (sta != NULL && (sta->flags & WLAN_STA_ASSOC)) { | ||
| 1820 | PDEBUG(DEBUG_AP, "ap_handle_dropped_data: STA is now okay?\n"); | ||
| 1821 | atomic_dec(&sta->users); | ||
| 1822 | return; | ||
| 1823 | } | ||
| 1824 | |||
| 1825 | reason = cpu_to_le16(WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA); | ||
| 1826 | prism2_send_mgmt(dev, IEEE80211_FTYPE_MGMT | | ||
| 1827 | ((sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) ? | ||
| 1828 | IEEE80211_STYPE_DEAUTH : IEEE80211_STYPE_DISASSOC), | ||
| 1829 | (char *) &reason, sizeof(reason), hdr->addr2, 0); | ||
| 1830 | |||
| 1831 | if (sta) | ||
| 1832 | atomic_dec(&sta->users); | ||
| 1833 | } | ||
| 1834 | |||
| 1835 | #endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ | ||
| 1836 | |||
| 1837 | |||
| 1838 | /* Called only as a scheduled task for pending AP frames. */ | ||
| 1839 | static void pspoll_send_buffered(local_info_t *local, struct sta_info *sta, | ||
| 1840 | struct sk_buff *skb) | ||
| 1841 | { | ||
| 1842 | struct hostap_skb_tx_data *meta; | ||
| 1843 | |||
| 1844 | if (!(sta->flags & WLAN_STA_PS)) { | ||
| 1845 | /* Station has moved to non-PS mode, so send all buffered | ||
| 1846 | * frames using normal device queue. */ | ||
| 1847 | dev_queue_xmit(skb); | ||
| 1848 | return; | ||
| 1849 | } | ||
| 1850 | |||
| 1851 | /* add a flag for hostap_handle_sta_tx() to know that this skb should | ||
| 1852 | * be passed through even though STA is using PS */ | ||
| 1853 | meta = (struct hostap_skb_tx_data *) skb->cb; | ||
| 1854 | meta->flags |= HOSTAP_TX_FLAGS_BUFFERED_FRAME; | ||
| 1855 | if (!skb_queue_empty(&sta->tx_buf)) { | ||
| 1856 | /* indicate to STA that more frames follow */ | ||
| 1857 | meta->flags |= HOSTAP_TX_FLAGS_ADD_MOREDATA; | ||
| 1858 | } | ||
| 1859 | dev_queue_xmit(skb); | ||
| 1860 | } | ||
| 1861 | |||
| 1862 | |||
| 1863 | /* Called only as a scheduled task for pending AP frames. */ | ||
| 1864 | static void handle_pspoll(local_info_t *local, | ||
| 1865 | struct ieee80211_hdr *hdr, | ||
| 1866 | struct hostap_80211_rx_status *rx_stats) | ||
| 1867 | { | ||
| 1868 | struct net_device *dev = local->dev; | ||
| 1869 | struct sta_info *sta; | ||
| 1870 | u16 aid; | ||
| 1871 | struct sk_buff *skb; | ||
| 1872 | |||
| 1873 | PDEBUG(DEBUG_PS2, "handle_pspoll: BSSID=%pM, TA=%pM PWRMGT=%d\n", | ||
| 1874 | hdr->addr1, hdr->addr2, !!ieee80211_has_pm(hdr->frame_control)); | ||
| 1875 | |||
| 1876 | if (!ether_addr_equal(hdr->addr1, dev->dev_addr)) { | ||
| 1877 | PDEBUG(DEBUG_AP, | ||
| 1878 | "handle_pspoll - addr1(BSSID)=%pM not own MAC\n", | ||
| 1879 | hdr->addr1); | ||
| 1880 | return; | ||
| 1881 | } | ||
| 1882 | |||
| 1883 | aid = le16_to_cpu(hdr->duration_id); | ||
| 1884 | if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14))) { | ||
| 1885 | PDEBUG(DEBUG_PS, " PSPOLL and AID[15:14] not set\n"); | ||
| 1886 | return; | ||
| 1887 | } | ||
| 1888 | aid &= ~(BIT(15) | BIT(14)); | ||
| 1889 | if (aid == 0 || aid > MAX_AID_TABLE_SIZE) { | ||
| 1890 | PDEBUG(DEBUG_PS, " invalid aid=%d\n", aid); | ||
| 1891 | return; | ||
| 1892 | } | ||
| 1893 | PDEBUG(DEBUG_PS2, " aid=%d\n", aid); | ||
| 1894 | |||
| 1895 | spin_lock_bh(&local->ap->sta_table_lock); | ||
| 1896 | sta = ap_get_sta(local->ap, hdr->addr2); | ||
| 1897 | if (sta) | ||
| 1898 | atomic_inc(&sta->users); | ||
| 1899 | spin_unlock_bh(&local->ap->sta_table_lock); | ||
| 1900 | |||
| 1901 | if (sta == NULL) { | ||
| 1902 | PDEBUG(DEBUG_PS, " STA not found\n"); | ||
| 1903 | return; | ||
| 1904 | } | ||
| 1905 | if (sta->aid != aid) { | ||
| 1906 | PDEBUG(DEBUG_PS, " received aid=%i does not match with " | ||
| 1907 | "assoc.aid=%d\n", aid, sta->aid); | ||
| 1908 | return; | ||
| 1909 | } | ||
| 1910 | |||
| 1911 | /* FIX: todo: | ||
| 1912 | * - add timeout for buffering (clear aid in TIM vector if buffer timed | ||
| 1913 | * out (expiry time must be longer than ListenInterval for | ||
| 1914 | * the corresponding STA; "8802-11: 11.2.1.9 AP aging function" | ||
| 1915 | * - what to do, if buffered, pspolled, and sent frame is not ACKed by | ||
| 1916 | * sta; store buffer for later use and leave TIM aid bit set? use | ||
| 1917 | * TX event to check whether frame was ACKed? | ||
| 1918 | */ | ||
| 1919 | |||
| 1920 | while ((skb = skb_dequeue(&sta->tx_buf)) != NULL) { | ||
| 1921 | /* send buffered frame .. */ | ||
| 1922 | PDEBUG(DEBUG_PS2, "Sending buffered frame to STA after PS POLL" | ||
| 1923 | " (buffer_count=%d)\n", skb_queue_len(&sta->tx_buf)); | ||
| 1924 | |||
| 1925 | pspoll_send_buffered(local, sta, skb); | ||
| 1926 | |||
| 1927 | if (sta->flags & WLAN_STA_PS) { | ||
| 1928 | /* send only one buffered packet per PS Poll */ | ||
| 1929 | /* FIX: should ignore further PS Polls until the | ||
| 1930 | * buffered packet that was just sent is acknowledged | ||
| 1931 | * (Tx or TxExc event) */ | ||
| 1932 | break; | ||
| 1933 | } | ||
| 1934 | } | ||
| 1935 | |||
| 1936 | if (skb_queue_empty(&sta->tx_buf)) { | ||
| 1937 | /* try to clear aid from TIM */ | ||
| 1938 | if (!(sta->flags & WLAN_STA_TIM)) | ||
| 1939 | PDEBUG(DEBUG_PS2, "Re-unsetting TIM for aid %d\n", | ||
| 1940 | aid); | ||
| 1941 | hostap_set_tim(local, aid, 0); | ||
| 1942 | sta->flags &= ~WLAN_STA_TIM; | ||
| 1943 | } | ||
| 1944 | |||
| 1945 | atomic_dec(&sta->users); | ||
| 1946 | } | ||
| 1947 | |||
| 1948 | |||
| 1949 | #ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT | ||
| 1950 | |||
| 1951 | static void handle_wds_oper_queue(struct work_struct *work) | ||
| 1952 | { | ||
| 1953 | struct ap_data *ap = container_of(work, struct ap_data, | ||
| 1954 | wds_oper_queue); | ||
| 1955 | local_info_t *local = ap->local; | ||
| 1956 | struct wds_oper_data *entry, *prev; | ||
| 1957 | |||
| 1958 | spin_lock_bh(&local->lock); | ||
| 1959 | entry = local->ap->wds_oper_entries; | ||
| 1960 | local->ap->wds_oper_entries = NULL; | ||
| 1961 | spin_unlock_bh(&local->lock); | ||
| 1962 | |||
| 1963 | while (entry) { | ||
| 1964 | PDEBUG(DEBUG_AP, "%s: %s automatic WDS connection " | ||
| 1965 | "to AP %pM\n", | ||
| 1966 | local->dev->name, | ||
| 1967 | entry->type == WDS_ADD ? "adding" : "removing", | ||
| 1968 | entry->addr); | ||
| 1969 | if (entry->type == WDS_ADD) | ||
| 1970 | prism2_wds_add(local, entry->addr, 0); | ||
| 1971 | else if (entry->type == WDS_DEL) | ||
| 1972 | prism2_wds_del(local, entry->addr, 0, 1); | ||
| 1973 | |||
| 1974 | prev = entry; | ||
| 1975 | entry = entry->next; | ||
| 1976 | kfree(prev); | ||
| 1977 | } | ||
| 1978 | } | ||
| 1979 | |||
| 1980 | |||
| 1981 | /* Called only as a scheduled task for pending AP frames. */ | ||
| 1982 | static void handle_beacon(local_info_t *local, struct sk_buff *skb, | ||
| 1983 | struct hostap_80211_rx_status *rx_stats) | ||
| 1984 | { | ||
| 1985 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; | ||
| 1986 | char *body = skb->data + IEEE80211_MGMT_HDR_LEN; | ||
| 1987 | int len, left; | ||
| 1988 | u16 beacon_int, capability; | ||
| 1989 | __le16 *pos; | ||
| 1990 | char *ssid = NULL; | ||
| 1991 | unsigned char *supp_rates = NULL; | ||
| 1992 | int ssid_len = 0, supp_rates_len = 0; | ||
| 1993 | struct sta_info *sta = NULL; | ||
| 1994 | int new_sta = 0, channel = -1; | ||
| 1995 | |||
| 1996 | len = skb->len - IEEE80211_MGMT_HDR_LEN; | ||
| 1997 | |||
| 1998 | if (len < 8 + 2 + 2) { | ||
| 1999 | printk(KERN_DEBUG "handle_beacon - too short payload " | ||
| 2000 | "(len=%d)\n", len); | ||
| 2001 | return; | ||
| 2002 | } | ||
| 2003 | |||
| 2004 | pos = (__le16 *) body; | ||
| 2005 | left = len; | ||
| 2006 | |||
| 2007 | /* Timestamp (8 octets) */ | ||
| 2008 | pos += 4; left -= 8; | ||
| 2009 | /* Beacon interval (2 octets) */ | ||
| 2010 | beacon_int = le16_to_cpu(*pos); | ||
| 2011 | pos++; left -= 2; | ||
| 2012 | /* Capability information (2 octets) */ | ||
| 2013 | capability = le16_to_cpu(*pos); | ||
| 2014 | pos++; left -= 2; | ||
| 2015 | |||
| 2016 | if (local->ap->ap_policy != AP_OTHER_AP_EVEN_IBSS && | ||
| 2017 | capability & WLAN_CAPABILITY_IBSS) | ||
| 2018 | return; | ||
| 2019 | |||
| 2020 | if (left >= 2) { | ||
| 2021 | unsigned int ileft; | ||
| 2022 | unsigned char *u = (unsigned char *) pos; | ||
| 2023 | |||
| 2024 | if (*u == WLAN_EID_SSID) { | ||
| 2025 | u++; left--; | ||
| 2026 | ileft = *u; | ||
| 2027 | u++; left--; | ||
| 2028 | |||
| 2029 | if (ileft > left || ileft > MAX_SSID_LEN) { | ||
| 2030 | PDEBUG(DEBUG_AP, "SSID: overflow\n"); | ||
| 2031 | return; | ||
| 2032 | } | ||
| 2033 | |||
| 2034 | if (local->ap->ap_policy == AP_OTHER_AP_SAME_SSID && | ||
| 2035 | (ileft != strlen(local->essid) || | ||
| 2036 | memcmp(local->essid, u, ileft) != 0)) { | ||
| 2037 | /* not our SSID */ | ||
| 2038 | return; | ||
| 2039 | } | ||
| 2040 | |||
| 2041 | ssid = u; | ||
| 2042 | ssid_len = ileft; | ||
| 2043 | |||
| 2044 | u += ileft; | ||
| 2045 | left -= ileft; | ||
| 2046 | } | ||
| 2047 | |||
| 2048 | if (*u == WLAN_EID_SUPP_RATES) { | ||
| 2049 | u++; left--; | ||
| 2050 | ileft = *u; | ||
| 2051 | u++; left--; | ||
| 2052 | |||
| 2053 | if (ileft > left || ileft == 0 || ileft > 8) { | ||
| 2054 | PDEBUG(DEBUG_AP, " - SUPP_RATES len error\n"); | ||
| 2055 | return; | ||
| 2056 | } | ||
| 2057 | |||
| 2058 | supp_rates = u; | ||
| 2059 | supp_rates_len = ileft; | ||
| 2060 | |||
| 2061 | u += ileft; | ||
| 2062 | left -= ileft; | ||
| 2063 | } | ||
| 2064 | |||
| 2065 | if (*u == WLAN_EID_DS_PARAMS) { | ||
| 2066 | u++; left--; | ||
| 2067 | ileft = *u; | ||
| 2068 | u++; left--; | ||
| 2069 | |||
| 2070 | if (ileft > left || ileft != 1) { | ||
| 2071 | PDEBUG(DEBUG_AP, " - DS_PARAMS len error\n"); | ||
| 2072 | return; | ||
| 2073 | } | ||
| 2074 | |||
| 2075 | channel = *u; | ||
| 2076 | |||
| 2077 | u += ileft; | ||
| 2078 | left -= ileft; | ||
| 2079 | } | ||
| 2080 | } | ||
| 2081 | |||
| 2082 | spin_lock_bh(&local->ap->sta_table_lock); | ||
| 2083 | sta = ap_get_sta(local->ap, hdr->addr2); | ||
| 2084 | if (sta != NULL) | ||
| 2085 | atomic_inc(&sta->users); | ||
| 2086 | spin_unlock_bh(&local->ap->sta_table_lock); | ||
| 2087 | |||
| 2088 | if (sta == NULL) { | ||
| 2089 | /* add new AP */ | ||
| 2090 | new_sta = 1; | ||
| 2091 | sta = ap_add_sta(local->ap, hdr->addr2); | ||
| 2092 | if (sta == NULL) { | ||
| 2093 | printk(KERN_INFO "prism2: kmalloc failed for AP " | ||
| 2094 | "data structure\n"); | ||
| 2095 | return; | ||
| 2096 | } | ||
| 2097 | hostap_event_new_sta(local->dev, sta); | ||
| 2098 | |||
| 2099 | /* mark APs authentication and associated for pseudo ad-hoc | ||
| 2100 | * style communication */ | ||
| 2101 | sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC; | ||
| 2102 | |||
| 2103 | if (local->ap->autom_ap_wds) { | ||
| 2104 | hostap_wds_link_oper(local, sta->addr, WDS_ADD); | ||
| 2105 | } | ||
| 2106 | } | ||
| 2107 | |||
| 2108 | sta->ap = 1; | ||
| 2109 | if (ssid) { | ||
| 2110 | sta->u.ap.ssid_len = ssid_len; | ||
| 2111 | memcpy(sta->u.ap.ssid, ssid, ssid_len); | ||
| 2112 | sta->u.ap.ssid[ssid_len] = '\0'; | ||
| 2113 | } else { | ||
| 2114 | sta->u.ap.ssid_len = 0; | ||
| 2115 | sta->u.ap.ssid[0] = '\0'; | ||
| 2116 | } | ||
| 2117 | sta->u.ap.channel = channel; | ||
| 2118 | sta->rx_packets++; | ||
| 2119 | sta->rx_bytes += len; | ||
| 2120 | sta->u.ap.last_beacon = sta->last_rx = jiffies; | ||
| 2121 | sta->capability = capability; | ||
| 2122 | sta->listen_interval = beacon_int; | ||
| 2123 | |||
| 2124 | atomic_dec(&sta->users); | ||
| 2125 | |||
| 2126 | if (new_sta) { | ||
| 2127 | memset(sta->supported_rates, 0, sizeof(sta->supported_rates)); | ||
| 2128 | memcpy(sta->supported_rates, supp_rates, supp_rates_len); | ||
| 2129 | prism2_check_tx_rates(sta); | ||
| 2130 | } | ||
| 2131 | } | ||
| 2132 | |||
| 2133 | #endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ | ||
| 2134 | |||
| 2135 | |||
| 2136 | /* Called only as a tasklet. */ | ||
| 2137 | static void handle_ap_item(local_info_t *local, struct sk_buff *skb, | ||
| 2138 | struct hostap_80211_rx_status *rx_stats) | ||
| 2139 | { | ||
| 2140 | #ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT | ||
| 2141 | struct net_device *dev = local->dev; | ||
| 2142 | #endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ | ||
| 2143 | u16 fc, type, stype; | ||
| 2144 | struct ieee80211_hdr *hdr; | ||
| 2145 | |||
| 2146 | /* FIX: should give skb->len to handler functions and check that the | ||
| 2147 | * buffer is long enough */ | ||
| 2148 | hdr = (struct ieee80211_hdr *) skb->data; | ||
| 2149 | fc = le16_to_cpu(hdr->frame_control); | ||
| 2150 | type = fc & IEEE80211_FCTL_FTYPE; | ||
| 2151 | stype = fc & IEEE80211_FCTL_STYPE; | ||
| 2152 | |||
| 2153 | #ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT | ||
| 2154 | if (!local->hostapd && type == IEEE80211_FTYPE_DATA) { | ||
| 2155 | PDEBUG(DEBUG_AP, "handle_ap_item - data frame\n"); | ||
| 2156 | |||
| 2157 | if (!(fc & IEEE80211_FCTL_TODS) || | ||
| 2158 | (fc & IEEE80211_FCTL_FROMDS)) { | ||
| 2159 | if (stype == IEEE80211_STYPE_NULLFUNC) { | ||
| 2160 | /* no ToDS nullfunc seems to be used to check | ||
| 2161 | * AP association; so send reject message to | ||
| 2162 | * speed up re-association */ | ||
| 2163 | ap_handle_dropped_data(local, hdr); | ||
| 2164 | goto done; | ||
| 2165 | } | ||
| 2166 | PDEBUG(DEBUG_AP, " not ToDS frame (fc=0x%04x)\n", | ||
| 2167 | fc); | ||
| 2168 | goto done; | ||
| 2169 | } | ||
| 2170 | |||
| 2171 | if (!ether_addr_equal(hdr->addr1, dev->dev_addr)) { | ||
| 2172 | PDEBUG(DEBUG_AP, "handle_ap_item - addr1(BSSID)=%pM" | ||
| 2173 | " not own MAC\n", hdr->addr1); | ||
| 2174 | goto done; | ||
| 2175 | } | ||
| 2176 | |||
| 2177 | if (local->ap->nullfunc_ack && | ||
| 2178 | stype == IEEE80211_STYPE_NULLFUNC) | ||
| 2179 | ap_handle_data_nullfunc(local, hdr); | ||
| 2180 | else | ||
| 2181 | ap_handle_dropped_data(local, hdr); | ||
| 2182 | goto done; | ||
| 2183 | } | ||
| 2184 | |||
| 2185 | if (type == IEEE80211_FTYPE_MGMT && stype == IEEE80211_STYPE_BEACON) { | ||
| 2186 | handle_beacon(local, skb, rx_stats); | ||
| 2187 | goto done; | ||
| 2188 | } | ||
| 2189 | #endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ | ||
| 2190 | |||
| 2191 | if (type == IEEE80211_FTYPE_CTL && stype == IEEE80211_STYPE_PSPOLL) { | ||
| 2192 | handle_pspoll(local, hdr, rx_stats); | ||
| 2193 | goto done; | ||
| 2194 | } | ||
| 2195 | |||
| 2196 | if (local->hostapd) { | ||
| 2197 | PDEBUG(DEBUG_AP, "Unknown frame in AP queue: type=0x%02x " | ||
| 2198 | "subtype=0x%02x\n", type, stype); | ||
| 2199 | goto done; | ||
| 2200 | } | ||
| 2201 | |||
| 2202 | #ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT | ||
| 2203 | if (type != IEEE80211_FTYPE_MGMT) { | ||
| 2204 | PDEBUG(DEBUG_AP, "handle_ap_item - not a management frame?\n"); | ||
| 2205 | goto done; | ||
| 2206 | } | ||
| 2207 | |||
| 2208 | if (!ether_addr_equal(hdr->addr1, dev->dev_addr)) { | ||
| 2209 | PDEBUG(DEBUG_AP, "handle_ap_item - addr1(DA)=%pM" | ||
| 2210 | " not own MAC\n", hdr->addr1); | ||
| 2211 | goto done; | ||
| 2212 | } | ||
| 2213 | |||
| 2214 | if (!ether_addr_equal(hdr->addr3, dev->dev_addr)) { | ||
| 2215 | PDEBUG(DEBUG_AP, "handle_ap_item - addr3(BSSID)=%pM" | ||
| 2216 | " not own MAC\n", hdr->addr3); | ||
| 2217 | goto done; | ||
| 2218 | } | ||
| 2219 | |||
| 2220 | switch (stype) { | ||
| 2221 | case IEEE80211_STYPE_ASSOC_REQ: | ||
| 2222 | handle_assoc(local, skb, rx_stats, 0); | ||
| 2223 | break; | ||
| 2224 | case IEEE80211_STYPE_ASSOC_RESP: | ||
| 2225 | PDEBUG(DEBUG_AP, "==> ASSOC RESP (ignored)\n"); | ||
| 2226 | break; | ||
| 2227 | case IEEE80211_STYPE_REASSOC_REQ: | ||
| 2228 | handle_assoc(local, skb, rx_stats, 1); | ||
| 2229 | break; | ||
| 2230 | case IEEE80211_STYPE_REASSOC_RESP: | ||
| 2231 | PDEBUG(DEBUG_AP, "==> REASSOC RESP (ignored)\n"); | ||
| 2232 | break; | ||
| 2233 | case IEEE80211_STYPE_ATIM: | ||
| 2234 | PDEBUG(DEBUG_AP, "==> ATIM (ignored)\n"); | ||
| 2235 | break; | ||
| 2236 | case IEEE80211_STYPE_DISASSOC: | ||
| 2237 | handle_disassoc(local, skb, rx_stats); | ||
| 2238 | break; | ||
| 2239 | case IEEE80211_STYPE_AUTH: | ||
| 2240 | handle_authen(local, skb, rx_stats); | ||
| 2241 | break; | ||
| 2242 | case IEEE80211_STYPE_DEAUTH: | ||
| 2243 | handle_deauth(local, skb, rx_stats); | ||
| 2244 | break; | ||
| 2245 | default: | ||
| 2246 | PDEBUG(DEBUG_AP, "Unknown mgmt frame subtype 0x%02x\n", | ||
| 2247 | stype >> 4); | ||
| 2248 | break; | ||
| 2249 | } | ||
| 2250 | #endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ | ||
| 2251 | |||
| 2252 | done: | ||
| 2253 | dev_kfree_skb(skb); | ||
| 2254 | } | ||
| 2255 | |||
| 2256 | |||
| 2257 | /* Called only as a tasklet (software IRQ) */ | ||
| 2258 | void hostap_rx(struct net_device *dev, struct sk_buff *skb, | ||
| 2259 | struct hostap_80211_rx_status *rx_stats) | ||
| 2260 | { | ||
| 2261 | struct hostap_interface *iface; | ||
| 2262 | local_info_t *local; | ||
| 2263 | struct ieee80211_hdr *hdr; | ||
| 2264 | |||
| 2265 | iface = netdev_priv(dev); | ||
| 2266 | local = iface->local; | ||
| 2267 | |||
| 2268 | if (skb->len < 16) | ||
| 2269 | goto drop; | ||
| 2270 | |||
| 2271 | dev->stats.rx_packets++; | ||
| 2272 | |||
| 2273 | hdr = (struct ieee80211_hdr *) skb->data; | ||
| 2274 | |||
| 2275 | if (local->ap->ap_policy == AP_OTHER_AP_SKIP_ALL && | ||
| 2276 | ieee80211_is_beacon(hdr->frame_control)) | ||
| 2277 | goto drop; | ||
| 2278 | |||
| 2279 | skb->protocol = cpu_to_be16(ETH_P_HOSTAP); | ||
| 2280 | handle_ap_item(local, skb, rx_stats); | ||
| 2281 | return; | ||
| 2282 | |||
| 2283 | drop: | ||
| 2284 | dev_kfree_skb(skb); | ||
| 2285 | } | ||
| 2286 | |||
| 2287 | |||
| 2288 | /* Called only as a tasklet (software IRQ) */ | ||
| 2289 | static void schedule_packet_send(local_info_t *local, struct sta_info *sta) | ||
| 2290 | { | ||
| 2291 | struct sk_buff *skb; | ||
| 2292 | struct ieee80211_hdr *hdr; | ||
| 2293 | struct hostap_80211_rx_status rx_stats; | ||
| 2294 | |||
| 2295 | if (skb_queue_empty(&sta->tx_buf)) | ||
| 2296 | return; | ||
| 2297 | |||
| 2298 | skb = dev_alloc_skb(16); | ||
| 2299 | if (skb == NULL) { | ||
| 2300 | printk(KERN_DEBUG "%s: schedule_packet_send: skb alloc " | ||
| 2301 | "failed\n", local->dev->name); | ||
| 2302 | return; | ||
| 2303 | } | ||
| 2304 | |||
| 2305 | hdr = skb_put(skb, 16); | ||
| 2306 | |||
| 2307 | /* Generate a fake pspoll frame to start packet delivery */ | ||
| 2308 | hdr->frame_control = cpu_to_le16( | ||
| 2309 | IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL); | ||
| 2310 | memcpy(hdr->addr1, local->dev->dev_addr, ETH_ALEN); | ||
| 2311 | memcpy(hdr->addr2, sta->addr, ETH_ALEN); | ||
| 2312 | hdr->duration_id = cpu_to_le16(sta->aid | BIT(15) | BIT(14)); | ||
| 2313 | |||
| 2314 | PDEBUG(DEBUG_PS2, | ||
| 2315 | "%s: Scheduling buffered packet delivery for STA %pM\n", | ||
| 2316 | local->dev->name, sta->addr); | ||
| 2317 | |||
| 2318 | skb->dev = local->dev; | ||
| 2319 | |||
| 2320 | memset(&rx_stats, 0, sizeof(rx_stats)); | ||
| 2321 | hostap_rx(local->dev, skb, &rx_stats); | ||
| 2322 | } | ||
| 2323 | |||
| 2324 | |||
| 2325 | int prism2_ap_get_sta_qual(local_info_t *local, struct sockaddr addr[], | ||
| 2326 | struct iw_quality qual[], int buf_size, | ||
| 2327 | int aplist) | ||
| 2328 | { | ||
| 2329 | struct ap_data *ap = local->ap; | ||
| 2330 | struct list_head *ptr; | ||
| 2331 | int count = 0; | ||
| 2332 | |||
| 2333 | spin_lock_bh(&ap->sta_table_lock); | ||
| 2334 | |||
| 2335 | for (ptr = ap->sta_list.next; ptr != NULL && ptr != &ap->sta_list; | ||
| 2336 | ptr = ptr->next) { | ||
| 2337 | struct sta_info *sta = (struct sta_info *) ptr; | ||
| 2338 | |||
| 2339 | if (aplist && !sta->ap) | ||
| 2340 | continue; | ||
| 2341 | addr[count].sa_family = ARPHRD_ETHER; | ||
| 2342 | memcpy(addr[count].sa_data, sta->addr, ETH_ALEN); | ||
| 2343 | if (sta->last_rx_silence == 0) | ||
| 2344 | qual[count].qual = sta->last_rx_signal < 27 ? | ||
| 2345 | 0 : (sta->last_rx_signal - 27) * 92 / 127; | ||
| 2346 | else | ||
| 2347 | qual[count].qual = sta->last_rx_signal - | ||
| 2348 | sta->last_rx_silence - 35; | ||
| 2349 | qual[count].level = HFA384X_LEVEL_TO_dBm(sta->last_rx_signal); | ||
| 2350 | qual[count].noise = HFA384X_LEVEL_TO_dBm(sta->last_rx_silence); | ||
| 2351 | qual[count].updated = sta->last_rx_updated; | ||
| 2352 | |||
| 2353 | sta->last_rx_updated = IW_QUAL_DBM; | ||
| 2354 | |||
| 2355 | count++; | ||
| 2356 | if (count >= buf_size) | ||
| 2357 | break; | ||
| 2358 | } | ||
| 2359 | spin_unlock_bh(&ap->sta_table_lock); | ||
| 2360 | |||
| 2361 | return count; | ||
| 2362 | } | ||
| 2363 | |||
| 2364 | |||
| 2365 | /* Translate our list of Access Points & Stations to a card independent | ||
| 2366 | * format that the Wireless Tools will understand - Jean II */ | ||
| 2367 | int prism2_ap_translate_scan(struct net_device *dev, | ||
| 2368 | struct iw_request_info *info, char *buffer) | ||
| 2369 | { | ||
| 2370 | struct hostap_interface *iface; | ||
| 2371 | local_info_t *local; | ||
| 2372 | struct ap_data *ap; | ||
| 2373 | struct list_head *ptr; | ||
| 2374 | struct iw_event iwe; | ||
| 2375 | char *current_ev = buffer; | ||
| 2376 | char *end_buf = buffer + IW_SCAN_MAX_DATA; | ||
| 2377 | #if !defined(PRISM2_NO_KERNEL_IEEE80211_MGMT) | ||
| 2378 | char buf[64]; | ||
| 2379 | #endif | ||
| 2380 | |||
| 2381 | iface = netdev_priv(dev); | ||
| 2382 | local = iface->local; | ||
| 2383 | ap = local->ap; | ||
| 2384 | |||
| 2385 | spin_lock_bh(&ap->sta_table_lock); | ||
| 2386 | |||
| 2387 | for (ptr = ap->sta_list.next; ptr != NULL && ptr != &ap->sta_list; | ||
| 2388 | ptr = ptr->next) { | ||
| 2389 | struct sta_info *sta = (struct sta_info *) ptr; | ||
| 2390 | |||
| 2391 | /* First entry *MUST* be the AP MAC address */ | ||
| 2392 | memset(&iwe, 0, sizeof(iwe)); | ||
| 2393 | iwe.cmd = SIOCGIWAP; | ||
| 2394 | iwe.u.ap_addr.sa_family = ARPHRD_ETHER; | ||
| 2395 | memcpy(iwe.u.ap_addr.sa_data, sta->addr, ETH_ALEN); | ||
| 2396 | iwe.len = IW_EV_ADDR_LEN; | ||
| 2397 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, | ||
| 2398 | &iwe, IW_EV_ADDR_LEN); | ||
| 2399 | |||
| 2400 | /* Use the mode to indicate if it's a station or | ||
| 2401 | * an Access Point */ | ||
| 2402 | memset(&iwe, 0, sizeof(iwe)); | ||
| 2403 | iwe.cmd = SIOCGIWMODE; | ||
| 2404 | if (sta->ap) | ||
| 2405 | iwe.u.mode = IW_MODE_MASTER; | ||
| 2406 | else | ||
| 2407 | iwe.u.mode = IW_MODE_INFRA; | ||
| 2408 | iwe.len = IW_EV_UINT_LEN; | ||
| 2409 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, | ||
| 2410 | &iwe, IW_EV_UINT_LEN); | ||
| 2411 | |||
| 2412 | /* Some quality */ | ||
| 2413 | memset(&iwe, 0, sizeof(iwe)); | ||
| 2414 | iwe.cmd = IWEVQUAL; | ||
| 2415 | if (sta->last_rx_silence == 0) | ||
| 2416 | iwe.u.qual.qual = sta->last_rx_signal < 27 ? | ||
| 2417 | 0 : (sta->last_rx_signal - 27) * 92 / 127; | ||
| 2418 | else | ||
| 2419 | iwe.u.qual.qual = sta->last_rx_signal - | ||
| 2420 | sta->last_rx_silence - 35; | ||
| 2421 | iwe.u.qual.level = HFA384X_LEVEL_TO_dBm(sta->last_rx_signal); | ||
| 2422 | iwe.u.qual.noise = HFA384X_LEVEL_TO_dBm(sta->last_rx_silence); | ||
| 2423 | iwe.u.qual.updated = sta->last_rx_updated; | ||
| 2424 | iwe.len = IW_EV_QUAL_LEN; | ||
| 2425 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, | ||
| 2426 | &iwe, IW_EV_QUAL_LEN); | ||
| 2427 | |||
| 2428 | #ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT | ||
| 2429 | if (sta->ap) { | ||
| 2430 | memset(&iwe, 0, sizeof(iwe)); | ||
| 2431 | iwe.cmd = SIOCGIWESSID; | ||
| 2432 | iwe.u.data.length = sta->u.ap.ssid_len; | ||
| 2433 | iwe.u.data.flags = 1; | ||
| 2434 | current_ev = iwe_stream_add_point(info, current_ev, | ||
| 2435 | end_buf, &iwe, | ||
| 2436 | sta->u.ap.ssid); | ||
| 2437 | |||
| 2438 | memset(&iwe, 0, sizeof(iwe)); | ||
| 2439 | iwe.cmd = SIOCGIWENCODE; | ||
| 2440 | if (sta->capability & WLAN_CAPABILITY_PRIVACY) | ||
| 2441 | iwe.u.data.flags = | ||
| 2442 | IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; | ||
| 2443 | else | ||
| 2444 | iwe.u.data.flags = IW_ENCODE_DISABLED; | ||
| 2445 | current_ev = iwe_stream_add_point(info, current_ev, | ||
| 2446 | end_buf, &iwe, | ||
| 2447 | sta->u.ap.ssid); | ||
| 2448 | |||
| 2449 | if (sta->u.ap.channel > 0 && | ||
| 2450 | sta->u.ap.channel <= FREQ_COUNT) { | ||
| 2451 | memset(&iwe, 0, sizeof(iwe)); | ||
| 2452 | iwe.cmd = SIOCGIWFREQ; | ||
| 2453 | iwe.u.freq.m = freq_list[sta->u.ap.channel - 1] | ||
| 2454 | * 100000; | ||
| 2455 | iwe.u.freq.e = 1; | ||
| 2456 | current_ev = iwe_stream_add_event( | ||
| 2457 | info, current_ev, end_buf, &iwe, | ||
| 2458 | IW_EV_FREQ_LEN); | ||
| 2459 | } | ||
| 2460 | |||
| 2461 | memset(&iwe, 0, sizeof(iwe)); | ||
| 2462 | iwe.cmd = IWEVCUSTOM; | ||
| 2463 | sprintf(buf, "beacon_interval=%d", | ||
| 2464 | sta->listen_interval); | ||
| 2465 | iwe.u.data.length = strlen(buf); | ||
| 2466 | current_ev = iwe_stream_add_point(info, current_ev, | ||
| 2467 | end_buf, &iwe, buf); | ||
| 2468 | } | ||
| 2469 | #endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ | ||
| 2470 | |||
| 2471 | sta->last_rx_updated = IW_QUAL_DBM; | ||
| 2472 | |||
| 2473 | /* To be continued, we should make good use of IWEVCUSTOM */ | ||
| 2474 | } | ||
| 2475 | |||
| 2476 | spin_unlock_bh(&ap->sta_table_lock); | ||
| 2477 | |||
| 2478 | return current_ev - buffer; | ||
| 2479 | } | ||
| 2480 | |||
| 2481 | |||
| 2482 | static int prism2_hostapd_add_sta(struct ap_data *ap, | ||
| 2483 | struct prism2_hostapd_param *param) | ||
| 2484 | { | ||
| 2485 | struct sta_info *sta; | ||
| 2486 | |||
| 2487 | spin_lock_bh(&ap->sta_table_lock); | ||
| 2488 | sta = ap_get_sta(ap, param->sta_addr); | ||
| 2489 | if (sta) | ||
| 2490 | atomic_inc(&sta->users); | ||
| 2491 | spin_unlock_bh(&ap->sta_table_lock); | ||
| 2492 | |||
| 2493 | if (sta == NULL) { | ||
| 2494 | sta = ap_add_sta(ap, param->sta_addr); | ||
| 2495 | if (sta == NULL) | ||
| 2496 | return -1; | ||
| 2497 | } | ||
| 2498 | |||
| 2499 | if (!(sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local) | ||
| 2500 | hostap_event_new_sta(sta->local->dev, sta); | ||
| 2501 | |||
| 2502 | sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC; | ||
| 2503 | sta->last_rx = jiffies; | ||
| 2504 | sta->aid = param->u.add_sta.aid; | ||
| 2505 | sta->capability = param->u.add_sta.capability; | ||
| 2506 | sta->tx_supp_rates = param->u.add_sta.tx_supp_rates; | ||
| 2507 | if (sta->tx_supp_rates & WLAN_RATE_1M) | ||
| 2508 | sta->supported_rates[0] = 2; | ||
| 2509 | if (sta->tx_supp_rates & WLAN_RATE_2M) | ||
| 2510 | sta->supported_rates[1] = 4; | ||
| 2511 | if (sta->tx_supp_rates & WLAN_RATE_5M5) | ||
| 2512 | sta->supported_rates[2] = 11; | ||
| 2513 | if (sta->tx_supp_rates & WLAN_RATE_11M) | ||
| 2514 | sta->supported_rates[3] = 22; | ||
| 2515 | prism2_check_tx_rates(sta); | ||
| 2516 | atomic_dec(&sta->users); | ||
| 2517 | return 0; | ||
| 2518 | } | ||
| 2519 | |||
| 2520 | |||
| 2521 | static int prism2_hostapd_remove_sta(struct ap_data *ap, | ||
| 2522 | struct prism2_hostapd_param *param) | ||
| 2523 | { | ||
| 2524 | struct sta_info *sta; | ||
| 2525 | |||
| 2526 | spin_lock_bh(&ap->sta_table_lock); | ||
| 2527 | sta = ap_get_sta(ap, param->sta_addr); | ||
| 2528 | if (sta) { | ||
| 2529 | ap_sta_hash_del(ap, sta); | ||
| 2530 | list_del(&sta->list); | ||
| 2531 | } | ||
| 2532 | spin_unlock_bh(&ap->sta_table_lock); | ||
| 2533 | |||
| 2534 | if (!sta) | ||
| 2535 | return -ENOENT; | ||
| 2536 | |||
| 2537 | if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local) | ||
| 2538 | hostap_event_expired_sta(sta->local->dev, sta); | ||
| 2539 | ap_free_sta(ap, sta); | ||
| 2540 | |||
| 2541 | return 0; | ||
| 2542 | } | ||
| 2543 | |||
| 2544 | |||
| 2545 | static int prism2_hostapd_get_info_sta(struct ap_data *ap, | ||
| 2546 | struct prism2_hostapd_param *param) | ||
| 2547 | { | ||
| 2548 | struct sta_info *sta; | ||
| 2549 | |||
| 2550 | spin_lock_bh(&ap->sta_table_lock); | ||
| 2551 | sta = ap_get_sta(ap, param->sta_addr); | ||
| 2552 | if (sta) | ||
| 2553 | atomic_inc(&sta->users); | ||
| 2554 | spin_unlock_bh(&ap->sta_table_lock); | ||
| 2555 | |||
| 2556 | if (!sta) | ||
| 2557 | return -ENOENT; | ||
| 2558 | |||
| 2559 | param->u.get_info_sta.inactive_sec = (jiffies - sta->last_rx) / HZ; | ||
| 2560 | |||
| 2561 | atomic_dec(&sta->users); | ||
| 2562 | |||
| 2563 | return 1; | ||
| 2564 | } | ||
| 2565 | |||
| 2566 | |||
| 2567 | static int prism2_hostapd_set_flags_sta(struct ap_data *ap, | ||
| 2568 | struct prism2_hostapd_param *param) | ||
| 2569 | { | ||
| 2570 | struct sta_info *sta; | ||
| 2571 | |||
| 2572 | spin_lock_bh(&ap->sta_table_lock); | ||
| 2573 | sta = ap_get_sta(ap, param->sta_addr); | ||
| 2574 | if (sta) { | ||
| 2575 | sta->flags |= param->u.set_flags_sta.flags_or; | ||
| 2576 | sta->flags &= param->u.set_flags_sta.flags_and; | ||
| 2577 | } | ||
| 2578 | spin_unlock_bh(&ap->sta_table_lock); | ||
| 2579 | |||
| 2580 | if (!sta) | ||
| 2581 | return -ENOENT; | ||
| 2582 | |||
| 2583 | return 0; | ||
| 2584 | } | ||
| 2585 | |||
| 2586 | |||
| 2587 | static int prism2_hostapd_sta_clear_stats(struct ap_data *ap, | ||
| 2588 | struct prism2_hostapd_param *param) | ||
| 2589 | { | ||
| 2590 | struct sta_info *sta; | ||
| 2591 | int rate; | ||
| 2592 | |||
| 2593 | spin_lock_bh(&ap->sta_table_lock); | ||
| 2594 | sta = ap_get_sta(ap, param->sta_addr); | ||
| 2595 | if (sta) { | ||
| 2596 | sta->rx_packets = sta->tx_packets = 0; | ||
| 2597 | sta->rx_bytes = sta->tx_bytes = 0; | ||
| 2598 | for (rate = 0; rate < WLAN_RATE_COUNT; rate++) { | ||
| 2599 | sta->tx_count[rate] = 0; | ||
| 2600 | sta->rx_count[rate] = 0; | ||
| 2601 | } | ||
| 2602 | } | ||
| 2603 | spin_unlock_bh(&ap->sta_table_lock); | ||
| 2604 | |||
| 2605 | if (!sta) | ||
| 2606 | return -ENOENT; | ||
| 2607 | |||
| 2608 | return 0; | ||
| 2609 | } | ||
| 2610 | |||
| 2611 | |||
| 2612 | int prism2_hostapd(struct ap_data *ap, struct prism2_hostapd_param *param) | ||
| 2613 | { | ||
| 2614 | switch (param->cmd) { | ||
| 2615 | case PRISM2_HOSTAPD_FLUSH: | ||
| 2616 | ap_control_kickall(ap); | ||
| 2617 | return 0; | ||
| 2618 | case PRISM2_HOSTAPD_ADD_STA: | ||
| 2619 | return prism2_hostapd_add_sta(ap, param); | ||
| 2620 | case PRISM2_HOSTAPD_REMOVE_STA: | ||
| 2621 | return prism2_hostapd_remove_sta(ap, param); | ||
| 2622 | case PRISM2_HOSTAPD_GET_INFO_STA: | ||
| 2623 | return prism2_hostapd_get_info_sta(ap, param); | ||
| 2624 | case PRISM2_HOSTAPD_SET_FLAGS_STA: | ||
| 2625 | return prism2_hostapd_set_flags_sta(ap, param); | ||
| 2626 | case PRISM2_HOSTAPD_STA_CLEAR_STATS: | ||
| 2627 | return prism2_hostapd_sta_clear_stats(ap, param); | ||
| 2628 | default: | ||
| 2629 | printk(KERN_WARNING "prism2_hostapd: unknown cmd=%d\n", | ||
| 2630 | param->cmd); | ||
| 2631 | return -EOPNOTSUPP; | ||
| 2632 | } | ||
| 2633 | } | ||
| 2634 | |||
| 2635 | |||
| 2636 | /* Update station info for host-based TX rate control and return current | ||
| 2637 | * TX rate */ | ||
| 2638 | static int ap_update_sta_tx_rate(struct sta_info *sta, struct net_device *dev) | ||
| 2639 | { | ||
| 2640 | int ret = sta->tx_rate; | ||
| 2641 | struct hostap_interface *iface; | ||
| 2642 | local_info_t *local; | ||
| 2643 | |||
| 2644 | iface = netdev_priv(dev); | ||
| 2645 | local = iface->local; | ||
| 2646 | |||
| 2647 | sta->tx_count[sta->tx_rate_idx]++; | ||
| 2648 | sta->tx_since_last_failure++; | ||
| 2649 | sta->tx_consecutive_exc = 0; | ||
| 2650 | if (sta->tx_since_last_failure >= WLAN_RATE_UPDATE_COUNT && | ||
| 2651 | sta->tx_rate_idx < sta->tx_max_rate) { | ||
| 2652 | /* use next higher rate */ | ||
| 2653 | int old_rate, new_rate; | ||
| 2654 | old_rate = new_rate = sta->tx_rate_idx; | ||
| 2655 | while (new_rate < sta->tx_max_rate) { | ||
| 2656 | new_rate++; | ||
| 2657 | if (ap_tx_rate_ok(new_rate, sta, local)) { | ||
| 2658 | sta->tx_rate_idx = new_rate; | ||
| 2659 | break; | ||
| 2660 | } | ||
| 2661 | } | ||
| 2662 | if (old_rate != sta->tx_rate_idx) { | ||
| 2663 | switch (sta->tx_rate_idx) { | ||
| 2664 | case 0: sta->tx_rate = 10; break; | ||
| 2665 | case 1: sta->tx_rate = 20; break; | ||
| 2666 | case 2: sta->tx_rate = 55; break; | ||
| 2667 | case 3: sta->tx_rate = 110; break; | ||
| 2668 | default: sta->tx_rate = 0; break; | ||
| 2669 | } | ||
| 2670 | PDEBUG(DEBUG_AP, "%s: STA %pM TX rate raised to %d\n", | ||
| 2671 | dev->name, sta->addr, sta->tx_rate); | ||
| 2672 | } | ||
| 2673 | sta->tx_since_last_failure = 0; | ||
| 2674 | } | ||
| 2675 | |||
| 2676 | return ret; | ||
| 2677 | } | ||
| 2678 | |||
| 2679 | |||
| 2680 | /* Called only from software IRQ. Called for each TX frame prior possible | ||
| 2681 | * encryption and transmit. */ | ||
| 2682 | ap_tx_ret hostap_handle_sta_tx(local_info_t *local, struct hostap_tx_data *tx) | ||
| 2683 | { | ||
| 2684 | struct sta_info *sta = NULL; | ||
| 2685 | struct sk_buff *skb = tx->skb; | ||
| 2686 | int set_tim, ret; | ||
| 2687 | struct ieee80211_hdr *hdr; | ||
| 2688 | struct hostap_skb_tx_data *meta; | ||
| 2689 | |||
| 2690 | meta = (struct hostap_skb_tx_data *) skb->cb; | ||
| 2691 | ret = AP_TX_CONTINUE; | ||
| 2692 | if (local->ap == NULL || skb->len < 10 || | ||
| 2693 | meta->iface->type == HOSTAP_INTERFACE_STA) | ||
| 2694 | goto out; | ||
| 2695 | |||
| 2696 | hdr = (struct ieee80211_hdr *) skb->data; | ||
| 2697 | |||
| 2698 | if (hdr->addr1[0] & 0x01) { | ||
| 2699 | /* broadcast/multicast frame - no AP related processing */ | ||
| 2700 | if (local->ap->num_sta <= 0) | ||
| 2701 | ret = AP_TX_DROP; | ||
| 2702 | goto out; | ||
| 2703 | } | ||
| 2704 | |||
| 2705 | /* unicast packet - check whether destination STA is associated */ | ||
| 2706 | spin_lock(&local->ap->sta_table_lock); | ||
| 2707 | sta = ap_get_sta(local->ap, hdr->addr1); | ||
| 2708 | if (sta) | ||
| 2709 | atomic_inc(&sta->users); | ||
| 2710 | spin_unlock(&local->ap->sta_table_lock); | ||
| 2711 | |||
| 2712 | if (local->iw_mode == IW_MODE_MASTER && sta == NULL && | ||
| 2713 | !(meta->flags & HOSTAP_TX_FLAGS_WDS) && | ||
| 2714 | meta->iface->type != HOSTAP_INTERFACE_MASTER && | ||
| 2715 | meta->iface->type != HOSTAP_INTERFACE_AP) { | ||
| 2716 | #if 0 | ||
| 2717 | /* This can happen, e.g., when wlan0 is added to a bridge and | ||
| 2718 | * bridging code does not know which port is the correct target | ||
| 2719 | * for a unicast frame. In this case, the packet is send to all | ||
| 2720 | * ports of the bridge. Since this is a valid scenario, do not | ||
| 2721 | * print out any errors here. */ | ||
| 2722 | if (net_ratelimit()) { | ||
| 2723 | printk(KERN_DEBUG "AP: drop packet to non-associated " | ||
| 2724 | "STA %pM\n", hdr->addr1); | ||
| 2725 | } | ||
| 2726 | #endif | ||
| 2727 | local->ap->tx_drop_nonassoc++; | ||
| 2728 | ret = AP_TX_DROP; | ||
| 2729 | goto out; | ||
| 2730 | } | ||
| 2731 | |||
| 2732 | if (sta == NULL) | ||
| 2733 | goto out; | ||
| 2734 | |||
| 2735 | if (!(sta->flags & WLAN_STA_AUTHORIZED)) | ||
| 2736 | ret = AP_TX_CONTINUE_NOT_AUTHORIZED; | ||
| 2737 | |||
| 2738 | /* Set tx_rate if using host-based TX rate control */ | ||
| 2739 | if (!local->fw_tx_rate_control) | ||
| 2740 | local->ap->last_tx_rate = meta->rate = | ||
| 2741 | ap_update_sta_tx_rate(sta, local->dev); | ||
| 2742 | |||
| 2743 | if (local->iw_mode != IW_MODE_MASTER) | ||
| 2744 | goto out; | ||
| 2745 | |||
| 2746 | if (!(sta->flags & WLAN_STA_PS)) | ||
| 2747 | goto out; | ||
| 2748 | |||
| 2749 | if (meta->flags & HOSTAP_TX_FLAGS_ADD_MOREDATA) { | ||
| 2750 | /* indicate to STA that more frames follow */ | ||
| 2751 | hdr->frame_control |= | ||
| 2752 | cpu_to_le16(IEEE80211_FCTL_MOREDATA); | ||
| 2753 | } | ||
| 2754 | |||
| 2755 | if (meta->flags & HOSTAP_TX_FLAGS_BUFFERED_FRAME) { | ||
| 2756 | /* packet was already buffered and now send due to | ||
| 2757 | * PS poll, so do not rebuffer it */ | ||
| 2758 | goto out; | ||
| 2759 | } | ||
| 2760 | |||
| 2761 | if (skb_queue_len(&sta->tx_buf) >= STA_MAX_TX_BUFFER) { | ||
| 2762 | PDEBUG(DEBUG_PS, "%s: No more space in STA (%pM)'s" | ||
| 2763 | "PS mode buffer\n", | ||
| 2764 | local->dev->name, sta->addr); | ||
| 2765 | /* Make sure that TIM is set for the station (it might not be | ||
| 2766 | * after AP wlan hw reset). */ | ||
| 2767 | /* FIX: should fix hw reset to restore bits based on STA | ||
| 2768 | * buffer state.. */ | ||
| 2769 | hostap_set_tim(local, sta->aid, 1); | ||
| 2770 | sta->flags |= WLAN_STA_TIM; | ||
| 2771 | ret = AP_TX_DROP; | ||
| 2772 | goto out; | ||
| 2773 | } | ||
| 2774 | |||
| 2775 | /* STA in PS mode, buffer frame for later delivery */ | ||
| 2776 | set_tim = skb_queue_empty(&sta->tx_buf); | ||
| 2777 | skb_queue_tail(&sta->tx_buf, skb); | ||
| 2778 | /* FIX: could save RX time to skb and expire buffered frames after | ||
| 2779 | * some time if STA does not poll for them */ | ||
| 2780 | |||
| 2781 | if (set_tim) { | ||
| 2782 | if (sta->flags & WLAN_STA_TIM) | ||
| 2783 | PDEBUG(DEBUG_PS2, "Re-setting TIM for aid %d\n", | ||
| 2784 | sta->aid); | ||
| 2785 | hostap_set_tim(local, sta->aid, 1); | ||
| 2786 | sta->flags |= WLAN_STA_TIM; | ||
| 2787 | } | ||
| 2788 | |||
| 2789 | ret = AP_TX_BUFFERED; | ||
| 2790 | |||
| 2791 | out: | ||
| 2792 | if (sta != NULL) { | ||
| 2793 | if (ret == AP_TX_CONTINUE || | ||
| 2794 | ret == AP_TX_CONTINUE_NOT_AUTHORIZED) { | ||
| 2795 | sta->tx_packets++; | ||
| 2796 | sta->tx_bytes += skb->len; | ||
| 2797 | sta->last_tx = jiffies; | ||
| 2798 | } | ||
| 2799 | |||
| 2800 | if ((ret == AP_TX_CONTINUE || | ||
| 2801 | ret == AP_TX_CONTINUE_NOT_AUTHORIZED) && | ||
| 2802 | sta->crypt && tx->host_encrypt) { | ||
| 2803 | tx->crypt = sta->crypt; | ||
| 2804 | tx->sta_ptr = sta; /* hostap_handle_sta_release() will | ||
| 2805 | * be called to release sta info | ||
| 2806 | * later */ | ||
| 2807 | } else | ||
| 2808 | atomic_dec(&sta->users); | ||
| 2809 | } | ||
| 2810 | |||
| 2811 | return ret; | ||
| 2812 | } | ||
| 2813 | |||
| 2814 | |||
| 2815 | void hostap_handle_sta_release(void *ptr) | ||
| 2816 | { | ||
| 2817 | struct sta_info *sta = ptr; | ||
| 2818 | atomic_dec(&sta->users); | ||
| 2819 | } | ||
| 2820 | |||
| 2821 | |||
| 2822 | /* Called only as a tasklet (software IRQ) */ | ||
| 2823 | void hostap_handle_sta_tx_exc(local_info_t *local, struct sk_buff *skb) | ||
| 2824 | { | ||
| 2825 | struct sta_info *sta; | ||
| 2826 | struct ieee80211_hdr *hdr; | ||
| 2827 | struct hostap_skb_tx_data *meta; | ||
| 2828 | |||
| 2829 | hdr = (struct ieee80211_hdr *) skb->data; | ||
| 2830 | meta = (struct hostap_skb_tx_data *) skb->cb; | ||
| 2831 | |||
| 2832 | spin_lock(&local->ap->sta_table_lock); | ||
| 2833 | sta = ap_get_sta(local->ap, hdr->addr1); | ||
| 2834 | if (!sta) { | ||
| 2835 | spin_unlock(&local->ap->sta_table_lock); | ||
| 2836 | PDEBUG(DEBUG_AP, "%s: Could not find STA %pM" | ||
| 2837 | " for this TX error (@%lu)\n", | ||
| 2838 | local->dev->name, hdr->addr1, jiffies); | ||
| 2839 | return; | ||
| 2840 | } | ||
| 2841 | |||
| 2842 | sta->tx_since_last_failure = 0; | ||
| 2843 | sta->tx_consecutive_exc++; | ||
| 2844 | |||
| 2845 | if (sta->tx_consecutive_exc >= WLAN_RATE_DECREASE_THRESHOLD && | ||
| 2846 | sta->tx_rate_idx > 0 && meta->rate <= sta->tx_rate) { | ||
| 2847 | /* use next lower rate */ | ||
| 2848 | int old, rate; | ||
| 2849 | old = rate = sta->tx_rate_idx; | ||
| 2850 | while (rate > 0) { | ||
| 2851 | rate--; | ||
| 2852 | if (ap_tx_rate_ok(rate, sta, local)) { | ||
| 2853 | sta->tx_rate_idx = rate; | ||
| 2854 | break; | ||
| 2855 | } | ||
| 2856 | } | ||
| 2857 | if (old != sta->tx_rate_idx) { | ||
| 2858 | switch (sta->tx_rate_idx) { | ||
| 2859 | case 0: sta->tx_rate = 10; break; | ||
| 2860 | case 1: sta->tx_rate = 20; break; | ||
| 2861 | case 2: sta->tx_rate = 55; break; | ||
| 2862 | case 3: sta->tx_rate = 110; break; | ||
| 2863 | default: sta->tx_rate = 0; break; | ||
| 2864 | } | ||
| 2865 | PDEBUG(DEBUG_AP, | ||
| 2866 | "%s: STA %pM TX rate lowered to %d\n", | ||
| 2867 | local->dev->name, sta->addr, sta->tx_rate); | ||
| 2868 | } | ||
| 2869 | sta->tx_consecutive_exc = 0; | ||
| 2870 | } | ||
| 2871 | spin_unlock(&local->ap->sta_table_lock); | ||
| 2872 | } | ||
| 2873 | |||
| 2874 | |||
| 2875 | static void hostap_update_sta_ps2(local_info_t *local, struct sta_info *sta, | ||
| 2876 | int pwrmgt, int type, int stype) | ||
| 2877 | { | ||
| 2878 | if (pwrmgt && !(sta->flags & WLAN_STA_PS)) { | ||
| 2879 | sta->flags |= WLAN_STA_PS; | ||
| 2880 | PDEBUG(DEBUG_PS2, "STA %pM changed to use PS " | ||
| 2881 | "mode (type=0x%02X, stype=0x%02X)\n", | ||
| 2882 | sta->addr, type >> 2, stype >> 4); | ||
| 2883 | } else if (!pwrmgt && (sta->flags & WLAN_STA_PS)) { | ||
| 2884 | sta->flags &= ~WLAN_STA_PS; | ||
| 2885 | PDEBUG(DEBUG_PS2, "STA %pM changed to not use " | ||
| 2886 | "PS mode (type=0x%02X, stype=0x%02X)\n", | ||
| 2887 | sta->addr, type >> 2, stype >> 4); | ||
| 2888 | if (type != IEEE80211_FTYPE_CTL || | ||
| 2889 | stype != IEEE80211_STYPE_PSPOLL) | ||
| 2890 | schedule_packet_send(local, sta); | ||
| 2891 | } | ||
| 2892 | } | ||
| 2893 | |||
| 2894 | |||
| 2895 | /* Called only as a tasklet (software IRQ). Called for each RX frame to update | ||
| 2896 | * STA power saving state. pwrmgt is a flag from 802.11 frame_control field. */ | ||
| 2897 | int hostap_update_sta_ps(local_info_t *local, struct ieee80211_hdr *hdr) | ||
| 2898 | { | ||
| 2899 | struct sta_info *sta; | ||
| 2900 | u16 fc; | ||
| 2901 | |||
| 2902 | spin_lock(&local->ap->sta_table_lock); | ||
| 2903 | sta = ap_get_sta(local->ap, hdr->addr2); | ||
| 2904 | if (sta) | ||
| 2905 | atomic_inc(&sta->users); | ||
| 2906 | spin_unlock(&local->ap->sta_table_lock); | ||
| 2907 | |||
| 2908 | if (!sta) | ||
| 2909 | return -1; | ||
| 2910 | |||
| 2911 | fc = le16_to_cpu(hdr->frame_control); | ||
| 2912 | hostap_update_sta_ps2(local, sta, fc & IEEE80211_FCTL_PM, | ||
| 2913 | fc & IEEE80211_FCTL_FTYPE, | ||
| 2914 | fc & IEEE80211_FCTL_STYPE); | ||
| 2915 | |||
| 2916 | atomic_dec(&sta->users); | ||
| 2917 | return 0; | ||
| 2918 | } | ||
| 2919 | |||
| 2920 | |||
| 2921 | /* Called only as a tasklet (software IRQ). Called for each RX frame after | ||
| 2922 | * getting RX header and payload from hardware. */ | ||
| 2923 | ap_rx_ret hostap_handle_sta_rx(local_info_t *local, struct net_device *dev, | ||
| 2924 | struct sk_buff *skb, | ||
| 2925 | struct hostap_80211_rx_status *rx_stats, | ||
| 2926 | int wds) | ||
| 2927 | { | ||
| 2928 | int ret; | ||
| 2929 | struct sta_info *sta; | ||
| 2930 | u16 fc, type, stype; | ||
| 2931 | struct ieee80211_hdr *hdr; | ||
| 2932 | |||
| 2933 | if (local->ap == NULL) | ||
| 2934 | return AP_RX_CONTINUE; | ||
| 2935 | |||
| 2936 | hdr = (struct ieee80211_hdr *) skb->data; | ||
| 2937 | |||
| 2938 | fc = le16_to_cpu(hdr->frame_control); | ||
| 2939 | type = fc & IEEE80211_FCTL_FTYPE; | ||
| 2940 | stype = fc & IEEE80211_FCTL_STYPE; | ||
| 2941 | |||
| 2942 | spin_lock(&local->ap->sta_table_lock); | ||
| 2943 | sta = ap_get_sta(local->ap, hdr->addr2); | ||
| 2944 | if (sta) | ||
| 2945 | atomic_inc(&sta->users); | ||
| 2946 | spin_unlock(&local->ap->sta_table_lock); | ||
| 2947 | |||
| 2948 | if (sta && !(sta->flags & WLAN_STA_AUTHORIZED)) | ||
| 2949 | ret = AP_RX_CONTINUE_NOT_AUTHORIZED; | ||
| 2950 | else | ||
| 2951 | ret = AP_RX_CONTINUE; | ||
| 2952 | |||
| 2953 | |||
| 2954 | if (fc & IEEE80211_FCTL_TODS) { | ||
| 2955 | if (!wds && (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))) { | ||
| 2956 | if (local->hostapd) { | ||
| 2957 | prism2_rx_80211(local->apdev, skb, rx_stats, | ||
| 2958 | PRISM2_RX_NON_ASSOC); | ||
| 2959 | #ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT | ||
| 2960 | } else { | ||
| 2961 | printk(KERN_DEBUG "%s: dropped received packet" | ||
| 2962 | " from non-associated STA %pM" | ||
| 2963 | " (type=0x%02x, subtype=0x%02x)\n", | ||
| 2964 | dev->name, hdr->addr2, | ||
| 2965 | type >> 2, stype >> 4); | ||
| 2966 | hostap_rx(dev, skb, rx_stats); | ||
| 2967 | #endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ | ||
| 2968 | } | ||
| 2969 | ret = AP_RX_EXIT; | ||
| 2970 | goto out; | ||
| 2971 | } | ||
| 2972 | } else if (fc & IEEE80211_FCTL_FROMDS) { | ||
| 2973 | if (!wds) { | ||
| 2974 | /* FromDS frame - not for us; probably | ||
| 2975 | * broadcast/multicast in another BSS - drop */ | ||
| 2976 | if (ether_addr_equal(hdr->addr1, dev->dev_addr)) { | ||
| 2977 | printk(KERN_DEBUG "Odd.. FromDS packet " | ||
| 2978 | "received with own BSSID\n"); | ||
| 2979 | hostap_dump_rx_80211(dev->name, skb, rx_stats); | ||
| 2980 | } | ||
| 2981 | ret = AP_RX_DROP; | ||
| 2982 | goto out; | ||
| 2983 | } | ||
| 2984 | } else if (stype == IEEE80211_STYPE_NULLFUNC && sta == NULL && | ||
| 2985 | ether_addr_equal(hdr->addr1, dev->dev_addr)) { | ||
| 2986 | |||
| 2987 | if (local->hostapd) { | ||
| 2988 | prism2_rx_80211(local->apdev, skb, rx_stats, | ||
| 2989 | PRISM2_RX_NON_ASSOC); | ||
| 2990 | #ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT | ||
| 2991 | } else { | ||
| 2992 | /* At least Lucent f/w seems to send data::nullfunc | ||
| 2993 | * frames with no ToDS flag when the current AP returns | ||
| 2994 | * after being unavailable for some time. Speed up | ||
| 2995 | * re-association by informing the station about it not | ||
| 2996 | * being associated. */ | ||
| 2997 | printk(KERN_DEBUG "%s: rejected received nullfunc frame" | ||
| 2998 | " without ToDS from not associated STA %pM\n", | ||
| 2999 | dev->name, hdr->addr2); | ||
| 3000 | hostap_rx(dev, skb, rx_stats); | ||
| 3001 | #endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ | ||
| 3002 | } | ||
| 3003 | ret = AP_RX_EXIT; | ||
| 3004 | goto out; | ||
| 3005 | } else if (stype == IEEE80211_STYPE_NULLFUNC) { | ||
| 3006 | /* At least Lucent cards seem to send periodic nullfunc | ||
| 3007 | * frames with ToDS. Let these through to update SQ | ||
| 3008 | * stats and PS state. Nullfunc frames do not contain | ||
| 3009 | * any data and they will be dropped below. */ | ||
| 3010 | } else { | ||
| 3011 | /* If BSSID (Addr3) is foreign, this frame is a normal | ||
| 3012 | * broadcast frame from an IBSS network. Drop it silently. | ||
| 3013 | * If BSSID is own, report the dropping of this frame. */ | ||
| 3014 | if (ether_addr_equal(hdr->addr3, dev->dev_addr)) { | ||
| 3015 | printk(KERN_DEBUG "%s: dropped received packet from %pM" | ||
| 3016 | " with no ToDS flag " | ||
| 3017 | "(type=0x%02x, subtype=0x%02x)\n", dev->name, | ||
| 3018 | hdr->addr2, type >> 2, stype >> 4); | ||
| 3019 | hostap_dump_rx_80211(dev->name, skb, rx_stats); | ||
| 3020 | } | ||
| 3021 | ret = AP_RX_DROP; | ||
| 3022 | goto out; | ||
| 3023 | } | ||
| 3024 | |||
| 3025 | if (sta) { | ||
| 3026 | hostap_update_sta_ps2(local, sta, fc & IEEE80211_FCTL_PM, | ||
| 3027 | type, stype); | ||
| 3028 | |||
| 3029 | sta->rx_packets++; | ||
| 3030 | sta->rx_bytes += skb->len; | ||
| 3031 | sta->last_rx = jiffies; | ||
| 3032 | } | ||
| 3033 | |||
| 3034 | if (local->ap->nullfunc_ack && stype == IEEE80211_STYPE_NULLFUNC && | ||
| 3035 | fc & IEEE80211_FCTL_TODS) { | ||
| 3036 | if (local->hostapd) { | ||
| 3037 | prism2_rx_80211(local->apdev, skb, rx_stats, | ||
| 3038 | PRISM2_RX_NULLFUNC_ACK); | ||
| 3039 | #ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT | ||
| 3040 | } else { | ||
| 3041 | /* some STA f/w's seem to require control::ACK frame | ||
| 3042 | * for data::nullfunc, but Prism2 f/w 0.8.0 (at least | ||
| 3043 | * from Compaq) does not send this.. Try to generate | ||
| 3044 | * ACK for these frames from the host driver to make | ||
| 3045 | * power saving work with, e.g., Lucent WaveLAN f/w */ | ||
| 3046 | hostap_rx(dev, skb, rx_stats); | ||
| 3047 | #endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ | ||
| 3048 | } | ||
| 3049 | ret = AP_RX_EXIT; | ||
| 3050 | goto out; | ||
| 3051 | } | ||
| 3052 | |||
| 3053 | out: | ||
| 3054 | if (sta) | ||
| 3055 | atomic_dec(&sta->users); | ||
| 3056 | |||
| 3057 | return ret; | ||
| 3058 | } | ||
| 3059 | |||
| 3060 | |||
| 3061 | /* Called only as a tasklet (software IRQ) */ | ||
| 3062 | int hostap_handle_sta_crypto(local_info_t *local, | ||
| 3063 | struct ieee80211_hdr *hdr, | ||
| 3064 | struct lib80211_crypt_data **crypt, | ||
| 3065 | void **sta_ptr) | ||
| 3066 | { | ||
| 3067 | struct sta_info *sta; | ||
| 3068 | |||
| 3069 | spin_lock(&local->ap->sta_table_lock); | ||
| 3070 | sta = ap_get_sta(local->ap, hdr->addr2); | ||
| 3071 | if (sta) | ||
| 3072 | atomic_inc(&sta->users); | ||
| 3073 | spin_unlock(&local->ap->sta_table_lock); | ||
| 3074 | |||
| 3075 | if (!sta) | ||
| 3076 | return -1; | ||
| 3077 | |||
| 3078 | if (sta->crypt) { | ||
| 3079 | *crypt = sta->crypt; | ||
| 3080 | *sta_ptr = sta; | ||
| 3081 | /* hostap_handle_sta_release() will be called to release STA | ||
| 3082 | * info */ | ||
| 3083 | } else | ||
| 3084 | atomic_dec(&sta->users); | ||
| 3085 | |||
| 3086 | return 0; | ||
| 3087 | } | ||
| 3088 | |||
| 3089 | |||
| 3090 | /* Called only as a tasklet (software IRQ) */ | ||
| 3091 | int hostap_is_sta_assoc(struct ap_data *ap, u8 *sta_addr) | ||
| 3092 | { | ||
| 3093 | struct sta_info *sta; | ||
| 3094 | int ret = 0; | ||
| 3095 | |||
| 3096 | spin_lock(&ap->sta_table_lock); | ||
| 3097 | sta = ap_get_sta(ap, sta_addr); | ||
| 3098 | if (sta != NULL && (sta->flags & WLAN_STA_ASSOC) && !sta->ap) | ||
| 3099 | ret = 1; | ||
| 3100 | spin_unlock(&ap->sta_table_lock); | ||
| 3101 | |||
| 3102 | return ret; | ||
| 3103 | } | ||
| 3104 | |||
| 3105 | |||
| 3106 | /* Called only as a tasklet (software IRQ) */ | ||
| 3107 | int hostap_is_sta_authorized(struct ap_data *ap, u8 *sta_addr) | ||
| 3108 | { | ||
| 3109 | struct sta_info *sta; | ||
| 3110 | int ret = 0; | ||
| 3111 | |||
| 3112 | spin_lock(&ap->sta_table_lock); | ||
| 3113 | sta = ap_get_sta(ap, sta_addr); | ||
| 3114 | if (sta != NULL && (sta->flags & WLAN_STA_ASSOC) && !sta->ap && | ||
| 3115 | ((sta->flags & WLAN_STA_AUTHORIZED) || | ||
| 3116 | ap->local->ieee_802_1x == 0)) | ||
| 3117 | ret = 1; | ||
| 3118 | spin_unlock(&ap->sta_table_lock); | ||
| 3119 | |||
| 3120 | return ret; | ||
| 3121 | } | ||
| 3122 | |||
| 3123 | |||
| 3124 | /* Called only as a tasklet (software IRQ) */ | ||
| 3125 | int hostap_add_sta(struct ap_data *ap, u8 *sta_addr) | ||
| 3126 | { | ||
| 3127 | struct sta_info *sta; | ||
| 3128 | int ret = 1; | ||
| 3129 | |||
| 3130 | if (!ap) | ||
| 3131 | return -1; | ||
| 3132 | |||
| 3133 | spin_lock(&ap->sta_table_lock); | ||
| 3134 | sta = ap_get_sta(ap, sta_addr); | ||
| 3135 | if (sta) | ||
| 3136 | ret = 0; | ||
| 3137 | spin_unlock(&ap->sta_table_lock); | ||
| 3138 | |||
| 3139 | if (ret == 1) { | ||
| 3140 | sta = ap_add_sta(ap, sta_addr); | ||
| 3141 | if (!sta) | ||
| 3142 | return -1; | ||
| 3143 | sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC; | ||
| 3144 | sta->ap = 1; | ||
| 3145 | memset(sta->supported_rates, 0, sizeof(sta->supported_rates)); | ||
| 3146 | /* No way of knowing which rates are supported since we did not | ||
| 3147 | * get supported rates element from beacon/assoc req. Assume | ||
| 3148 | * that remote end supports all 802.11b rates. */ | ||
| 3149 | sta->supported_rates[0] = 0x82; | ||
| 3150 | sta->supported_rates[1] = 0x84; | ||
| 3151 | sta->supported_rates[2] = 0x0b; | ||
| 3152 | sta->supported_rates[3] = 0x16; | ||
| 3153 | sta->tx_supp_rates = WLAN_RATE_1M | WLAN_RATE_2M | | ||
| 3154 | WLAN_RATE_5M5 | WLAN_RATE_11M; | ||
| 3155 | sta->tx_rate = 110; | ||
| 3156 | sta->tx_max_rate = sta->tx_rate_idx = 3; | ||
| 3157 | } | ||
| 3158 | |||
| 3159 | return ret; | ||
| 3160 | } | ||
| 3161 | |||
| 3162 | |||
| 3163 | /* Called only as a tasklet (software IRQ) */ | ||
| 3164 | int hostap_update_rx_stats(struct ap_data *ap, | ||
| 3165 | struct ieee80211_hdr *hdr, | ||
| 3166 | struct hostap_80211_rx_status *rx_stats) | ||
| 3167 | { | ||
| 3168 | struct sta_info *sta; | ||
| 3169 | |||
| 3170 | if (!ap) | ||
| 3171 | return -1; | ||
| 3172 | |||
| 3173 | spin_lock(&ap->sta_table_lock); | ||
| 3174 | sta = ap_get_sta(ap, hdr->addr2); | ||
| 3175 | if (sta) { | ||
| 3176 | sta->last_rx_silence = rx_stats->noise; | ||
| 3177 | sta->last_rx_signal = rx_stats->signal; | ||
| 3178 | sta->last_rx_rate = rx_stats->rate; | ||
| 3179 | sta->last_rx_updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; | ||
| 3180 | if (rx_stats->rate == 10) | ||
| 3181 | sta->rx_count[0]++; | ||
| 3182 | else if (rx_stats->rate == 20) | ||
| 3183 | sta->rx_count[1]++; | ||
| 3184 | else if (rx_stats->rate == 55) | ||
| 3185 | sta->rx_count[2]++; | ||
| 3186 | else if (rx_stats->rate == 110) | ||
| 3187 | sta->rx_count[3]++; | ||
| 3188 | } | ||
| 3189 | spin_unlock(&ap->sta_table_lock); | ||
| 3190 | |||
| 3191 | return sta ? 0 : -1; | ||
| 3192 | } | ||
| 3193 | |||
| 3194 | |||
| 3195 | void hostap_update_rates(local_info_t *local) | ||
| 3196 | { | ||
| 3197 | struct sta_info *sta; | ||
| 3198 | struct ap_data *ap = local->ap; | ||
| 3199 | |||
| 3200 | if (!ap) | ||
| 3201 | return; | ||
| 3202 | |||
| 3203 | spin_lock_bh(&ap->sta_table_lock); | ||
| 3204 | list_for_each_entry(sta, &ap->sta_list, list) { | ||
| 3205 | prism2_check_tx_rates(sta); | ||
| 3206 | } | ||
| 3207 | spin_unlock_bh(&ap->sta_table_lock); | ||
| 3208 | } | ||
| 3209 | |||
| 3210 | |||
| 3211 | void * ap_crypt_get_ptrs(struct ap_data *ap, u8 *addr, int permanent, | ||
| 3212 | struct lib80211_crypt_data ***crypt) | ||
| 3213 | { | ||
| 3214 | struct sta_info *sta; | ||
| 3215 | |||
| 3216 | spin_lock_bh(&ap->sta_table_lock); | ||
| 3217 | sta = ap_get_sta(ap, addr); | ||
| 3218 | if (sta) | ||
| 3219 | atomic_inc(&sta->users); | ||
| 3220 | spin_unlock_bh(&ap->sta_table_lock); | ||
| 3221 | |||
| 3222 | if (!sta && permanent) | ||
| 3223 | sta = ap_add_sta(ap, addr); | ||
| 3224 | |||
| 3225 | if (!sta) | ||
| 3226 | return NULL; | ||
| 3227 | |||
| 3228 | if (permanent) | ||
| 3229 | sta->flags |= WLAN_STA_PERM; | ||
| 3230 | |||
| 3231 | *crypt = &sta->crypt; | ||
| 3232 | |||
| 3233 | return sta; | ||
| 3234 | } | ||
| 3235 | |||
| 3236 | |||
| 3237 | void hostap_add_wds_links(local_info_t *local) | ||
| 3238 | { | ||
| 3239 | struct ap_data *ap = local->ap; | ||
| 3240 | struct sta_info *sta; | ||
| 3241 | |||
| 3242 | spin_lock_bh(&ap->sta_table_lock); | ||
| 3243 | list_for_each_entry(sta, &ap->sta_list, list) { | ||
| 3244 | if (sta->ap) | ||
| 3245 | hostap_wds_link_oper(local, sta->addr, WDS_ADD); | ||
| 3246 | } | ||
| 3247 | spin_unlock_bh(&ap->sta_table_lock); | ||
| 3248 | |||
| 3249 | schedule_work(&local->ap->wds_oper_queue); | ||
| 3250 | } | ||
| 3251 | |||
| 3252 | |||
| 3253 | void hostap_wds_link_oper(local_info_t *local, u8 *addr, wds_oper_type type) | ||
| 3254 | { | ||
| 3255 | struct wds_oper_data *entry; | ||
| 3256 | |||
| 3257 | entry = kmalloc(sizeof(*entry), GFP_ATOMIC); | ||
| 3258 | if (!entry) | ||
| 3259 | return; | ||
| 3260 | memcpy(entry->addr, addr, ETH_ALEN); | ||
| 3261 | entry->type = type; | ||
| 3262 | spin_lock_bh(&local->lock); | ||
| 3263 | entry->next = local->ap->wds_oper_entries; | ||
| 3264 | local->ap->wds_oper_entries = entry; | ||
| 3265 | spin_unlock_bh(&local->lock); | ||
| 3266 | |||
| 3267 | schedule_work(&local->ap->wds_oper_queue); | ||
| 3268 | } | ||
| 3269 | |||
| 3270 | |||
| 3271 | EXPORT_SYMBOL(hostap_init_data); | ||
| 3272 | EXPORT_SYMBOL(hostap_init_ap_proc); | ||
| 3273 | EXPORT_SYMBOL(hostap_free_data); | ||
| 3274 | EXPORT_SYMBOL(hostap_check_sta_fw_version); | ||
| 3275 | EXPORT_SYMBOL(hostap_handle_sta_tx_exc); | ||
| 3276 | #ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT | ||
| 3277 | #endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ | ||
| @@ -1,264 +0,0 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 2 | #ifndef HOSTAP_AP_H | ||
| 3 | #define HOSTAP_AP_H | ||
| 4 | |||
| 5 | #include "hostap_80211.h" | ||
| 6 | |||
| 7 | /* AP data structures for STAs */ | ||
| 8 | |||
| 9 | /* maximum number of frames to buffer per STA */ | ||
| 10 | #define STA_MAX_TX_BUFFER 32 | ||
| 11 | |||
| 12 | /* STA flags */ | ||
| 13 | #define WLAN_STA_AUTH BIT(0) | ||
| 14 | #define WLAN_STA_ASSOC BIT(1) | ||
| 15 | #define WLAN_STA_PS BIT(2) | ||
| 16 | #define WLAN_STA_TIM BIT(3) /* TIM bit is on for PS stations */ | ||
| 17 | #define WLAN_STA_PERM BIT(4) /* permanent; do not remove entry on expiration */ | ||
| 18 | #define WLAN_STA_AUTHORIZED BIT(5) /* If 802.1X is used, this flag is | ||
| 19 | * controlling whether STA is authorized to | ||
| 20 | * send and receive non-IEEE 802.1X frames | ||
| 21 | */ | ||
| 22 | #define WLAN_STA_PENDING_POLL BIT(6) /* pending activity poll not ACKed */ | ||
| 23 | |||
| 24 | #define WLAN_RATE_1M BIT(0) | ||
| 25 | #define WLAN_RATE_2M BIT(1) | ||
| 26 | #define WLAN_RATE_5M5 BIT(2) | ||
| 27 | #define WLAN_RATE_11M BIT(3) | ||
| 28 | #define WLAN_RATE_COUNT 4 | ||
| 29 | |||
| 30 | /* Maximum size of Supported Rates info element. IEEE 802.11 has a limit of 8, | ||
| 31 | * but some pre-standard IEEE 802.11g products use longer elements. */ | ||
| 32 | #define WLAN_SUPP_RATES_MAX 32 | ||
| 33 | |||
| 34 | /* Try to increase TX rate after # successfully sent consecutive packets */ | ||
| 35 | #define WLAN_RATE_UPDATE_COUNT 50 | ||
| 36 | |||
| 37 | /* Decrease TX rate after # consecutive dropped packets */ | ||
| 38 | #define WLAN_RATE_DECREASE_THRESHOLD 2 | ||
| 39 | |||
| 40 | struct sta_info { | ||
| 41 | struct list_head list; | ||
| 42 | struct sta_info *hnext; /* next entry in hash table list */ | ||
| 43 | atomic_t users; /* number of users (do not remove if > 0) */ | ||
| 44 | struct proc_dir_entry *proc; | ||
| 45 | |||
| 46 | u8 addr[6]; | ||
| 47 | u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */ | ||
| 48 | u32 flags; | ||
| 49 | u16 capability; | ||
| 50 | u16 listen_interval; /* or beacon_int for APs */ | ||
| 51 | u8 supported_rates[WLAN_SUPP_RATES_MAX]; | ||
| 52 | |||
| 53 | unsigned long last_auth; | ||
| 54 | unsigned long last_assoc; | ||
| 55 | unsigned long last_rx; | ||
| 56 | unsigned long last_tx; | ||
| 57 | unsigned long rx_packets, tx_packets; | ||
| 58 | unsigned long rx_bytes, tx_bytes; | ||
| 59 | struct sk_buff_head tx_buf; | ||
| 60 | /* FIX: timeout buffers with an expiry time somehow derived from | ||
| 61 | * listen_interval */ | ||
| 62 | |||
| 63 | s8 last_rx_silence; /* Noise in dBm */ | ||
| 64 | s8 last_rx_signal; /* Signal strength in dBm */ | ||
| 65 | u8 last_rx_rate; /* TX rate in 0.1 Mbps */ | ||
| 66 | u8 last_rx_updated; /* IWSPY's struct iw_quality::updated */ | ||
| 67 | |||
| 68 | u8 tx_supp_rates; /* bit field of supported TX rates */ | ||
| 69 | u8 tx_rate; /* current TX rate (in 0.1 Mbps) */ | ||
| 70 | u8 tx_rate_idx; /* current TX rate (WLAN_RATE_*) */ | ||
| 71 | u8 tx_max_rate; /* max TX rate (WLAN_RATE_*) */ | ||
| 72 | u32 tx_count[WLAN_RATE_COUNT]; /* number of frames sent (per rate) */ | ||
| 73 | u32 rx_count[WLAN_RATE_COUNT]; /* number of frames received (per rate) | ||
| 74 | */ | ||
| 75 | u32 tx_since_last_failure; | ||
| 76 | u32 tx_consecutive_exc; | ||
| 77 | |||
| 78 | struct lib80211_crypt_data *crypt; | ||
| 79 | |||
| 80 | int ap; /* whether this station is an AP */ | ||
| 81 | |||
| 82 | local_info_t *local; | ||
| 83 | |||
| 84 | #ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT | ||
| 85 | union { | ||
| 86 | struct { | ||
| 87 | char *challenge; /* shared key authentication | ||
| 88 | * challenge */ | ||
| 89 | } sta; | ||
| 90 | struct { | ||
| 91 | int ssid_len; | ||
| 92 | unsigned char ssid[MAX_SSID_LEN + 1]; /* AP's ssid */ | ||
| 93 | int channel; | ||
| 94 | unsigned long last_beacon; /* last RX beacon time */ | ||
| 95 | } ap; | ||
| 96 | } u; | ||
| 97 | |||
| 98 | struct timer_list timer; | ||
| 99 | enum { STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH } timeout_next; | ||
| 100 | #endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ | ||
| 101 | }; | ||
| 102 | |||
| 103 | |||
| 104 | #define MAX_STA_COUNT 1024 | ||
| 105 | |||
| 106 | /* Maximum number of AIDs to use for STAs; must be 2007 or lower | ||
| 107 | * (8802.11 limitation) */ | ||
| 108 | #define MAX_AID_TABLE_SIZE 128 | ||
| 109 | |||
| 110 | #define STA_HASH_SIZE 256 | ||
| 111 | #define STA_HASH(sta) (sta[5]) | ||
| 112 | |||
| 113 | |||
| 114 | /* Default value for maximum station inactivity. After AP_MAX_INACTIVITY_SEC | ||
| 115 | * has passed since last received frame from the station, a nullfunc data | ||
| 116 | * frame is sent to the station. If this frame is not acknowledged and no other | ||
| 117 | * frames have been received, the station will be disassociated after | ||
| 118 | * AP_DISASSOC_DELAY. Similarly, a the station will be deauthenticated after | ||
| 119 | * AP_DEAUTH_DELAY. AP_TIMEOUT_RESOLUTION is the resolution that is used with | ||
| 120 | * max inactivity timer. */ | ||
| 121 | #define AP_MAX_INACTIVITY_SEC (5 * 60) | ||
| 122 | #define AP_DISASSOC_DELAY (HZ) | ||
| 123 | #define AP_DEAUTH_DELAY (HZ) | ||
| 124 | |||
| 125 | /* ap_policy: whether to accept frames to/from other APs/IBSS */ | ||
| 126 | typedef enum { | ||
| 127 | AP_OTHER_AP_SKIP_ALL = 0, | ||
| 128 | AP_OTHER_AP_SAME_SSID = 1, | ||
| 129 | AP_OTHER_AP_ALL = 2, | ||
| 130 | AP_OTHER_AP_EVEN_IBSS = 3 | ||
| 131 | } ap_policy_enum; | ||
| 132 | |||
| 133 | #define PRISM2_AUTH_OPEN BIT(0) | ||
| 134 | #define PRISM2_AUTH_SHARED_KEY BIT(1) | ||
| 135 | |||
| 136 | |||
| 137 | /* MAC address-based restrictions */ | ||
| 138 | struct mac_entry { | ||
| 139 | struct list_head list; | ||
| 140 | u8 addr[6]; | ||
| 141 | }; | ||
| 142 | |||
| 143 | struct mac_restrictions { | ||
| 144 | enum { MAC_POLICY_OPEN = 0, MAC_POLICY_ALLOW, MAC_POLICY_DENY } policy; | ||
| 145 | unsigned int entries; | ||
| 146 | struct list_head mac_list; | ||
| 147 | spinlock_t lock; | ||
| 148 | }; | ||
| 149 | |||
| 150 | |||
| 151 | struct add_sta_proc_data { | ||
| 152 | u8 addr[ETH_ALEN]; | ||
| 153 | struct add_sta_proc_data *next; | ||
| 154 | }; | ||
| 155 | |||
| 156 | |||
| 157 | typedef enum { WDS_ADD, WDS_DEL } wds_oper_type; | ||
| 158 | struct wds_oper_data { | ||
| 159 | wds_oper_type type; | ||
| 160 | u8 addr[ETH_ALEN]; | ||
| 161 | struct wds_oper_data *next; | ||
| 162 | }; | ||
| 163 | |||
| 164 | |||
| 165 | struct ap_data { | ||
| 166 | int initialized; /* whether ap_data has been initialized */ | ||
| 167 | local_info_t *local; | ||
| 168 | int bridge_packets; /* send packet to associated STAs directly to the | ||
| 169 | * wireless media instead of higher layers in the | ||
| 170 | * kernel */ | ||
| 171 | unsigned int bridged_unicast; /* number of unicast frames bridged on | ||
| 172 | * wireless media */ | ||
| 173 | unsigned int bridged_multicast; /* number of non-unicast frames | ||
| 174 | * bridged on wireless media */ | ||
| 175 | unsigned int tx_drop_nonassoc; /* number of unicast TX packets dropped | ||
| 176 | * because they were to an address that | ||
| 177 | * was not associated */ | ||
| 178 | int nullfunc_ack; /* use workaround for nullfunc frame ACKs */ | ||
| 179 | |||
| 180 | spinlock_t sta_table_lock; | ||
| 181 | int num_sta; /* number of entries in sta_list */ | ||
| 182 | struct list_head sta_list; /* STA info list head */ | ||
| 183 | struct sta_info *sta_hash[STA_HASH_SIZE]; | ||
| 184 | |||
| 185 | struct proc_dir_entry *proc; | ||
| 186 | |||
| 187 | ap_policy_enum ap_policy; | ||
| 188 | unsigned int max_inactivity; | ||
| 189 | int autom_ap_wds; | ||
| 190 | |||
| 191 | struct mac_restrictions mac_restrictions; /* MAC-based auth */ | ||
| 192 | int last_tx_rate; | ||
| 193 | |||
| 194 | struct work_struct add_sta_proc_queue; | ||
| 195 | struct add_sta_proc_data *add_sta_proc_entries; | ||
| 196 | |||
| 197 | struct work_struct wds_oper_queue; | ||
| 198 | struct wds_oper_data *wds_oper_entries; | ||
| 199 | |||
| 200 | u16 tx_callback_idx; | ||
| 201 | |||
| 202 | #ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT | ||
| 203 | /* pointers to STA info; based on allocated AID or NULL if AID free | ||
| 204 | * AID is in the range 1-2007, so sta_aid[0] corresponders to AID 1 | ||
| 205 | * and so on | ||
| 206 | */ | ||
| 207 | struct sta_info *sta_aid[MAX_AID_TABLE_SIZE]; | ||
| 208 | |||
| 209 | u16 tx_callback_auth, tx_callback_assoc, tx_callback_poll; | ||
| 210 | |||
| 211 | /* WEP operations for generating challenges to be used with shared key | ||
| 212 | * authentication */ | ||
| 213 | struct lib80211_crypto_ops *crypt; | ||
| 214 | void *crypt_priv; | ||
| 215 | #endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ | ||
| 216 | }; | ||
| 217 | |||
| 218 | |||
| 219 | void hostap_rx(struct net_device *dev, struct sk_buff *skb, | ||
| 220 | struct hostap_80211_rx_status *rx_stats); | ||
| 221 | void hostap_init_data(local_info_t *local); | ||
| 222 | void hostap_init_ap_proc(local_info_t *local); | ||
| 223 | void hostap_free_data(struct ap_data *ap); | ||
| 224 | void hostap_check_sta_fw_version(struct ap_data *ap, int sta_fw_ver); | ||
| 225 | |||
| 226 | typedef enum { | ||
| 227 | AP_TX_CONTINUE, AP_TX_DROP, AP_TX_RETRY, AP_TX_BUFFERED, | ||
| 228 | AP_TX_CONTINUE_NOT_AUTHORIZED | ||
| 229 | } ap_tx_ret; | ||
| 230 | struct hostap_tx_data { | ||
| 231 | struct sk_buff *skb; | ||
| 232 | int host_encrypt; | ||
| 233 | struct lib80211_crypt_data *crypt; | ||
| 234 | void *sta_ptr; | ||
| 235 | }; | ||
| 236 | ap_tx_ret hostap_handle_sta_tx(local_info_t *local, struct hostap_tx_data *tx); | ||
| 237 | void hostap_handle_sta_release(void *ptr); | ||
| 238 | void hostap_handle_sta_tx_exc(local_info_t *local, struct sk_buff *skb); | ||
| 239 | int hostap_update_sta_ps(local_info_t *local, struct ieee80211_hdr *hdr); | ||
| 240 | typedef enum { | ||
| 241 | AP_RX_CONTINUE, AP_RX_DROP, AP_RX_EXIT, AP_RX_CONTINUE_NOT_AUTHORIZED | ||
| 242 | } ap_rx_ret; | ||
| 243 | ap_rx_ret hostap_handle_sta_rx(local_info_t *local, struct net_device *dev, | ||
| 244 | struct sk_buff *skb, | ||
| 245 | struct hostap_80211_rx_status *rx_stats, | ||
| 246 | int wds); | ||
| 247 | int hostap_handle_sta_crypto(local_info_t *local, struct ieee80211_hdr *hdr, | ||
| 248 | struct lib80211_crypt_data **crypt, | ||
| 249 | void **sta_ptr); | ||
| 250 | int hostap_is_sta_assoc(struct ap_data *ap, u8 *sta_addr); | ||
| 251 | int hostap_is_sta_authorized(struct ap_data *ap, u8 *sta_addr); | ||
| 252 | int hostap_add_sta(struct ap_data *ap, u8 *sta_addr); | ||
| 253 | int hostap_update_rx_stats(struct ap_data *ap, struct ieee80211_hdr *hdr, | ||
| 254 | struct hostap_80211_rx_status *rx_stats); | ||
| 255 | void hostap_update_rates(local_info_t *local); | ||
| 256 | void hostap_add_wds_links(local_info_t *local); | ||
| 257 | void hostap_wds_link_oper(local_info_t *local, u8 *addr, wds_oper_type type); | ||
| 258 | |||
| 259 | #ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT | ||
| 260 | void hostap_deauth_all_stas(struct net_device *dev, struct ap_data *ap, | ||
| 261 | int resend); | ||
| 262 | #endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ | ||
| 263 | |||
| 264 | #endif /* HOSTAP_AP_H */ | ||
| @@ -1,420 +0,0 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 2 | #ifndef HOSTAP_COMMON_H | ||
| 3 | #define HOSTAP_COMMON_H | ||
| 4 | |||
| 5 | #include <linux/types.h> | ||
| 6 | #include <linux/if_ether.h> | ||
| 7 | |||
| 8 | /* IEEE 802.11 defines */ | ||
| 9 | |||
| 10 | /* HFA384X Configuration RIDs */ | ||
| 11 | #define HFA384X_RID_CNFPORTTYPE 0xFC00 | ||
| 12 | #define HFA384X_RID_CNFOWNMACADDR 0xFC01 | ||
| 13 | #define HFA384X_RID_CNFDESIREDSSID 0xFC02 | ||
| 14 | #define HFA384X_RID_CNFOWNCHANNEL 0xFC03 | ||
| 15 | #define HFA384X_RID_CNFOWNSSID 0xFC04 | ||
| 16 | #define HFA384X_RID_CNFOWNATIMWINDOW 0xFC05 | ||
| 17 | #define HFA384X_RID_CNFSYSTEMSCALE 0xFC06 | ||
| 18 | #define HFA384X_RID_CNFMAXDATALEN 0xFC07 | ||
| 19 | #define HFA384X_RID_CNFWDSADDRESS 0xFC08 | ||
| 20 | #define HFA384X_RID_CNFPMENABLED 0xFC09 | ||
| 21 | #define HFA384X_RID_CNFPMEPS 0xFC0A | ||
| 22 | #define HFA384X_RID_CNFMULTICASTRECEIVE 0xFC0B | ||
| 23 | #define HFA384X_RID_CNFMAXSLEEPDURATION 0xFC0C | ||
| 24 | #define HFA384X_RID_CNFPMHOLDOVERDURATION 0xFC0D | ||
| 25 | #define HFA384X_RID_CNFOWNNAME 0xFC0E | ||
| 26 | #define HFA384X_RID_CNFOWNDTIMPERIOD 0xFC10 | ||
| 27 | #define HFA384X_RID_CNFWDSADDRESS1 0xFC11 /* AP f/w only */ | ||
| 28 | #define HFA384X_RID_CNFWDSADDRESS2 0xFC12 /* AP f/w only */ | ||
| 29 | #define HFA384X_RID_CNFWDSADDRESS3 0xFC13 /* AP f/w only */ | ||
| 30 | #define HFA384X_RID_CNFWDSADDRESS4 0xFC14 /* AP f/w only */ | ||
| 31 | #define HFA384X_RID_CNFWDSADDRESS5 0xFC15 /* AP f/w only */ | ||
| 32 | #define HFA384X_RID_CNFWDSADDRESS6 0xFC16 /* AP f/w only */ | ||
| 33 | #define HFA384X_RID_CNFMULTICASTPMBUFFERING 0xFC17 /* AP f/w only */ | ||
| 34 | #define HFA384X_RID_UNKNOWN1 0xFC20 | ||
| 35 | #define HFA384X_RID_UNKNOWN2 0xFC21 | ||
| 36 | #define HFA384X_RID_CNFWEPDEFAULTKEYID 0xFC23 | ||
| 37 | #define HFA384X_RID_CNFDEFAULTKEY0 0xFC24 | ||
| 38 | #define HFA384X_RID_CNFDEFAULTKEY1 0xFC25 | ||
| 39 | #define HFA384X_RID_CNFDEFAULTKEY2 0xFC26 | ||
| 40 | #define HFA384X_RID_CNFDEFAULTKEY3 0xFC27 | ||
| 41 | #define HFA384X_RID_CNFWEPFLAGS 0xFC28 | ||
| 42 | #define HFA384X_RID_CNFWEPKEYMAPPINGTABLE 0xFC29 | ||
| 43 | #define HFA384X_RID_CNFAUTHENTICATION 0xFC2A | ||
| 44 | #define HFA384X_RID_CNFMAXASSOCSTA 0xFC2B /* AP f/w only */ | ||
| 45 | #define HFA384X_RID_CNFTXCONTROL 0xFC2C | ||
| 46 | #define HFA384X_RID_CNFROAMINGMODE 0xFC2D | ||
| 47 | #define HFA384X_RID_CNFHOSTAUTHENTICATION 0xFC2E /* AP f/w only */ | ||
| 48 | #define HFA384X_RID_CNFRCVCRCERROR 0xFC30 | ||
| 49 | #define HFA384X_RID_CNFMMLIFE 0xFC31 | ||
| 50 | #define HFA384X_RID_CNFALTRETRYCOUNT 0xFC32 | ||
| 51 | #define HFA384X_RID_CNFBEACONINT 0xFC33 | ||
| 52 | #define HFA384X_RID_CNFAPPCFINFO 0xFC34 /* AP f/w only */ | ||
| 53 | #define HFA384X_RID_CNFSTAPCFINFO 0xFC35 | ||
| 54 | #define HFA384X_RID_CNFPRIORITYQUSAGE 0xFC37 | ||
| 55 | #define HFA384X_RID_CNFTIMCTRL 0xFC40 | ||
| 56 | #define HFA384X_RID_UNKNOWN3 0xFC41 /* added in STA f/w 0.7.x */ | ||
| 57 | #define HFA384X_RID_CNFTHIRTY2TALLY 0xFC42 /* added in STA f/w 0.8.0 */ | ||
| 58 | #define HFA384X_RID_CNFENHSECURITY 0xFC43 /* AP f/w or STA f/w >= 1.6.3 */ | ||
| 59 | #define HFA384X_RID_CNFDBMADJUST 0xFC46 /* added in STA f/w 1.3.1 */ | ||
| 60 | #define HFA384X_RID_GENERICELEMENT 0xFC48 /* added in STA f/w 1.7.0; | ||
| 61 | * write only */ | ||
| 62 | #define HFA384X_RID_PROPAGATIONDELAY 0xFC49 /* added in STA f/w 1.7.6 */ | ||
| 63 | #define HFA384X_RID_GROUPADDRESSES 0xFC80 | ||
| 64 | #define HFA384X_RID_CREATEIBSS 0xFC81 | ||
| 65 | #define HFA384X_RID_FRAGMENTATIONTHRESHOLD 0xFC82 | ||
| 66 | #define HFA384X_RID_RTSTHRESHOLD 0xFC83 | ||
| 67 | #define HFA384X_RID_TXRATECONTROL 0xFC84 | ||
| 68 | #define HFA384X_RID_PROMISCUOUSMODE 0xFC85 | ||
| 69 | #define HFA384X_RID_FRAGMENTATIONTHRESHOLD0 0xFC90 /* AP f/w only */ | ||
| 70 | #define HFA384X_RID_FRAGMENTATIONTHRESHOLD1 0xFC91 /* AP f/w only */ | ||
| 71 | #define HFA384X_RID_FRAGMENTATIONTHRESHOLD2 0xFC92 /* AP f/w only */ | ||
| 72 | #define HFA384X_RID_FRAGMENTATIONTHRESHOLD3 0xFC93 /* AP f/w only */ | ||
| 73 | #define HFA384X_RID_FRAGMENTATIONTHRESHOLD4 0xFC94 /* AP f/w only */ | ||
| 74 | #define HFA384X_RID_FRAGMENTATIONTHRESHOLD5 0xFC95 /* AP f/w only */ | ||
| 75 | #define HFA384X_RID_FRAGMENTATIONTHRESHOLD6 0xFC96 /* AP f/w only */ | ||
| 76 | #define HFA384X_RID_RTSTHRESHOLD0 0xFC97 /* AP f/w only */ | ||
| 77 | #define HFA384X_RID_RTSTHRESHOLD1 0xFC98 /* AP f/w only */ | ||
| 78 | #define HFA384X_RID_RTSTHRESHOLD2 0xFC99 /* AP f/w only */ | ||
| 79 | #define HFA384X_RID_RTSTHRESHOLD3 0xFC9A /* AP f/w only */ | ||
| 80 | #define HFA384X_RID_RTSTHRESHOLD4 0xFC9B /* AP f/w only */ | ||
| 81 | #define HFA384X_RID_RTSTHRESHOLD5 0xFC9C /* AP f/w only */ | ||
| 82 | #define HFA384X_RID_RTSTHRESHOLD6 0xFC9D /* AP f/w only */ | ||
| 83 | #define HFA384X_RID_TXRATECONTROL0 0xFC9E /* AP f/w only */ | ||
| 84 | #define HFA384X_RID_TXRATECONTROL1 0xFC9F /* AP f/w only */ | ||
| 85 | #define HFA384X_RID_TXRATECONTROL2 0xFCA0 /* AP f/w only */ | ||
| 86 | #define HFA384X_RID_TXRATECONTROL3 0xFCA1 /* AP f/w only */ | ||
| 87 | #define HFA384X_RID_TXRATECONTROL4 0xFCA2 /* AP f/w only */ | ||
| 88 | #define HFA384X_RID_TXRATECONTROL5 0xFCA3 /* AP f/w only */ | ||
| 89 | #define HFA384X_RID_TXRATECONTROL6 0xFCA4 /* AP f/w only */ | ||
| 90 | #define HFA384X_RID_CNFSHORTPREAMBLE 0xFCB0 | ||
| 91 | #define HFA384X_RID_CNFEXCLUDELONGPREAMBLE 0xFCB1 | ||
| 92 | #define HFA384X_RID_CNFAUTHENTICATIONRSPTO 0xFCB2 | ||
| 93 | #define HFA384X_RID_CNFBASICRATES 0xFCB3 | ||
| 94 | #define HFA384X_RID_CNFSUPPORTEDRATES 0xFCB4 | ||
| 95 | #define HFA384X_RID_CNFFALLBACKCTRL 0xFCB5 /* added in STA f/w 1.3.1 */ | ||
| 96 | #define HFA384X_RID_WEPKEYDISABLE 0xFCB6 /* added in STA f/w 1.3.1 */ | ||
| 97 | #define HFA384X_RID_WEPKEYMAPINDEX 0xFCB7 /* ? */ | ||
| 98 | #define HFA384X_RID_BROADCASTKEYID 0xFCB8 /* ? */ | ||
| 99 | #define HFA384X_RID_ENTSECFLAGEYID 0xFCB9 /* ? */ | ||
| 100 | #define HFA384X_RID_CNFPASSIVESCANCTRL 0xFCBA /* added in STA f/w 1.5.0 */ | ||
| 101 | #define HFA384X_RID_SSNHANDLINGMODE 0xFCBB /* added in STA f/w 1.7.0 */ | ||
| 102 | #define HFA384X_RID_MDCCONTROL 0xFCBC /* added in STA f/w 1.7.0 */ | ||
| 103 | #define HFA384X_RID_MDCCOUNTRY 0xFCBD /* added in STA f/w 1.7.0 */ | ||
| 104 | #define HFA384X_RID_TXPOWERMAX 0xFCBE /* added in STA f/w 1.7.0 */ | ||
| 105 | #define HFA384X_RID_CNFLFOENABLED 0xFCBF /* added in STA f/w 1.6.3 */ | ||
| 106 | #define HFA384X_RID_CAPINFO 0xFCC0 /* added in STA f/w 1.7.0 */ | ||
| 107 | #define HFA384X_RID_LISTENINTERVAL 0xFCC1 /* added in STA f/w 1.7.0 */ | ||
| 108 | #define HFA384X_RID_SW_ANT_DIV 0xFCC2 /* added in STA f/w 1.7.0; Prism3 */ | ||
| 109 | #define HFA384X_RID_LED_CTRL 0xFCC4 /* added in STA f/w 1.7.6 */ | ||
| 110 | #define HFA384X_RID_HFODELAY 0xFCC5 /* added in STA f/w 1.7.6 */ | ||
| 111 | #define HFA384X_RID_DISALLOWEDBSSID 0xFCC6 /* added in STA f/w 1.8.0 */ | ||
| 112 | #define HFA384X_RID_TICKTIME 0xFCE0 | ||
| 113 | #define HFA384X_RID_SCANREQUEST 0xFCE1 | ||
| 114 | #define HFA384X_RID_JOINREQUEST 0xFCE2 | ||
| 115 | #define HFA384X_RID_AUTHENTICATESTATION 0xFCE3 /* AP f/w only */ | ||
| 116 | #define HFA384X_RID_CHANNELINFOREQUEST 0xFCE4 /* AP f/w only */ | ||
| 117 | #define HFA384X_RID_HOSTSCAN 0xFCE5 /* added in STA f/w 1.3.1 */ | ||
| 118 | |||
| 119 | /* HFA384X Information RIDs */ | ||
| 120 | #define HFA384X_RID_MAXLOADTIME 0xFD00 | ||
| 121 | #define HFA384X_RID_DOWNLOADBUFFER 0xFD01 | ||
| 122 | #define HFA384X_RID_PRIID 0xFD02 | ||
| 123 | #define HFA384X_RID_PRISUPRANGE 0xFD03 | ||
| 124 | #define HFA384X_RID_CFIACTRANGES 0xFD04 | ||
| 125 | #define HFA384X_RID_NICSERNUM 0xFD0A | ||
| 126 | #define HFA384X_RID_NICID 0xFD0B | ||
| 127 | #define HFA384X_RID_MFISUPRANGE 0xFD0C | ||
| 128 | #define HFA384X_RID_CFISUPRANGE 0xFD0D | ||
| 129 | #define HFA384X_RID_CHANNELLIST 0xFD10 | ||
| 130 | #define HFA384X_RID_REGULATORYDOMAINS 0xFD11 | ||
| 131 | #define HFA384X_RID_TEMPTYPE 0xFD12 | ||
| 132 | #define HFA384X_RID_CIS 0xFD13 | ||
| 133 | #define HFA384X_RID_STAID 0xFD20 | ||
| 134 | #define HFA384X_RID_STASUPRANGE 0xFD21 | ||
| 135 | #define HFA384X_RID_MFIACTRANGES 0xFD22 | ||
| 136 | #define HFA384X_RID_CFIACTRANGES2 0xFD23 | ||
| 137 | #define HFA384X_RID_PRODUCTNAME 0xFD24 /* added in STA f/w 1.3.1; | ||
| 138 | * only Prism2.5(?) */ | ||
| 139 | #define HFA384X_RID_PORTSTATUS 0xFD40 | ||
| 140 | #define HFA384X_RID_CURRENTSSID 0xFD41 | ||
| 141 | #define HFA384X_RID_CURRENTBSSID 0xFD42 | ||
| 142 | #define HFA384X_RID_COMMSQUALITY 0xFD43 | ||
| 143 | #define HFA384X_RID_CURRENTTXRATE 0xFD44 | ||
| 144 | #define HFA384X_RID_CURRENTBEACONINTERVAL 0xFD45 | ||
| 145 | #define HFA384X_RID_CURRENTSCALETHRESHOLDS 0xFD46 | ||
| 146 | #define HFA384X_RID_PROTOCOLRSPTIME 0xFD47 | ||
| 147 | #define HFA384X_RID_SHORTRETRYLIMIT 0xFD48 | ||
| 148 | #define HFA384X_RID_LONGRETRYLIMIT 0xFD49 | ||
| 149 | #define HFA384X_RID_MAXTRANSMITLIFETIME 0xFD4A | ||
| 150 | #define HFA384X_RID_MAXRECEIVELIFETIME 0xFD4B | ||
| 151 | #define HFA384X_RID_CFPOLLABLE 0xFD4C | ||
| 152 | #define HFA384X_RID_AUTHENTICATIONALGORITHMS 0xFD4D | ||
| 153 | #define HFA384X_RID_PRIVACYOPTIONIMPLEMENTED 0xFD4F | ||
| 154 | #define HFA384X_RID_DBMCOMMSQUALITY 0xFD51 /* added in STA f/w 1.3.1 */ | ||
| 155 | #define HFA384X_RID_CURRENTTXRATE1 0xFD80 /* AP f/w only */ | ||
| 156 | #define HFA384X_RID_CURRENTTXRATE2 0xFD81 /* AP f/w only */ | ||
| 157 | #define HFA384X_RID_CURRENTTXRATE3 0xFD82 /* AP f/w only */ | ||
| 158 | #define HFA384X_RID_CURRENTTXRATE4 0xFD83 /* AP f/w only */ | ||
| 159 | #define HFA384X_RID_CURRENTTXRATE5 0xFD84 /* AP f/w only */ | ||
| 160 | #define HFA384X_RID_CURRENTTXRATE6 0xFD85 /* AP f/w only */ | ||
| 161 | #define HFA384X_RID_OWNMACADDR 0xFD86 /* AP f/w only */ | ||
| 162 | #define HFA384X_RID_SCANRESULTSTABLE 0xFD88 /* added in STA f/w 0.8.3 */ | ||
| 163 | #define HFA384X_RID_HOSTSCANRESULTS 0xFD89 /* added in STA f/w 1.3.1 */ | ||
| 164 | #define HFA384X_RID_AUTHENTICATIONUSED 0xFD8A /* added in STA f/w 1.3.4 */ | ||
| 165 | #define HFA384X_RID_CNFFAASWITCHCTRL 0xFD8B /* added in STA f/w 1.6.3 */ | ||
| 166 | #define HFA384X_RID_ASSOCIATIONFAILURE 0xFD8D /* added in STA f/w 1.8.0 */ | ||
| 167 | #define HFA384X_RID_PHYTYPE 0xFDC0 | ||
| 168 | #define HFA384X_RID_CURRENTCHANNEL 0xFDC1 | ||
| 169 | #define HFA384X_RID_CURRENTPOWERSTATE 0xFDC2 | ||
| 170 | #define HFA384X_RID_CCAMODE 0xFDC3 | ||
| 171 | #define HFA384X_RID_SUPPORTEDDATARATES 0xFDC6 | ||
| 172 | #define HFA384X_RID_LFO_VOLT_REG_TEST_RES 0xFDC7 /* added in STA f/w 1.7.1 */ | ||
| 173 | #define HFA384X_RID_BUILDSEQ 0xFFFE | ||
| 174 | #define HFA384X_RID_FWID 0xFFFF | ||
| 175 | |||
| 176 | |||
| 177 | struct hfa384x_comp_ident | ||
| 178 | { | ||
| 179 | __le16 id; | ||
| 180 | __le16 variant; | ||
| 181 | __le16 major; | ||
| 182 | __le16 minor; | ||
| 183 | } __packed; | ||
| 184 | |||
| 185 | #define HFA384X_COMP_ID_PRI 0x15 | ||
| 186 | #define HFA384X_COMP_ID_STA 0x1f | ||
| 187 | #define HFA384X_COMP_ID_FW_AP 0x14b | ||
| 188 | |||
| 189 | struct hfa384x_sup_range | ||
| 190 | { | ||
| 191 | __le16 role; | ||
| 192 | __le16 id; | ||
| 193 | __le16 variant; | ||
| 194 | __le16 bottom; | ||
| 195 | __le16 top; | ||
| 196 | } __packed; | ||
| 197 | |||
| 198 | |||
| 199 | struct hfa384x_build_id | ||
| 200 | { | ||
| 201 | __le16 pri_seq; | ||
| 202 | __le16 sec_seq; | ||
| 203 | } __packed; | ||
| 204 | |||
| 205 | /* FD01 - Download Buffer */ | ||
| 206 | struct hfa384x_rid_download_buffer | ||
| 207 | { | ||
| 208 | __le16 page; | ||
| 209 | __le16 offset; | ||
| 210 | __le16 length; | ||
| 211 | } __packed; | ||
| 212 | |||
| 213 | /* BSS connection quality (RID FD43 range, RID FD51 dBm-normalized) */ | ||
| 214 | struct hfa384x_comms_quality { | ||
| 215 | __le16 comm_qual; /* 0 .. 92 */ | ||
| 216 | __le16 signal_level; /* 27 .. 154 */ | ||
| 217 | __le16 noise_level; /* 27 .. 154 */ | ||
| 218 | } __packed; | ||
| 219 | |||
| 220 | |||
| 221 | /* netdevice private ioctls (used, e.g., with iwpriv from user space) */ | ||
| 222 | |||
| 223 | /* New wireless extensions API - SET/GET convention (even ioctl numbers are | ||
| 224 | * root only) | ||
| 225 | */ | ||
| 226 | #define PRISM2_IOCTL_PRISM2_PARAM (SIOCIWFIRSTPRIV + 0) | ||
| 227 | #define PRISM2_IOCTL_GET_PRISM2_PARAM (SIOCIWFIRSTPRIV + 1) | ||
| 228 | #define PRISM2_IOCTL_WRITEMIF (SIOCIWFIRSTPRIV + 2) | ||
| 229 | #define PRISM2_IOCTL_READMIF (SIOCIWFIRSTPRIV + 3) | ||
| 230 | #define PRISM2_IOCTL_MONITOR (SIOCIWFIRSTPRIV + 4) | ||
| 231 | #define PRISM2_IOCTL_RESET (SIOCIWFIRSTPRIV + 6) | ||
| 232 | #define PRISM2_IOCTL_INQUIRE (SIOCIWFIRSTPRIV + 8) | ||
| 233 | #define PRISM2_IOCTL_WDS_ADD (SIOCIWFIRSTPRIV + 10) | ||
| 234 | #define PRISM2_IOCTL_WDS_DEL (SIOCIWFIRSTPRIV + 12) | ||
| 235 | #define PRISM2_IOCTL_SET_RID_WORD (SIOCIWFIRSTPRIV + 14) | ||
| 236 | #define PRISM2_IOCTL_MACCMD (SIOCIWFIRSTPRIV + 16) | ||
| 237 | #define PRISM2_IOCTL_ADDMAC (SIOCIWFIRSTPRIV + 18) | ||
| 238 | #define PRISM2_IOCTL_DELMAC (SIOCIWFIRSTPRIV + 20) | ||
| 239 | #define PRISM2_IOCTL_KICKMAC (SIOCIWFIRSTPRIV + 22) | ||
| 240 | |||
| 241 | /* following are not in SIOCGIWPRIV list; check permission in the driver code | ||
| 242 | */ | ||
| 243 | #define PRISM2_IOCTL_DOWNLOAD (SIOCDEVPRIVATE + 13) | ||
| 244 | #define PRISM2_IOCTL_HOSTAPD (SIOCDEVPRIVATE + 14) | ||
| 245 | |||
| 246 | |||
| 247 | /* PRISM2_IOCTL_PRISM2_PARAM ioctl() subtypes: */ | ||
| 248 | enum { | ||
| 249 | /* PRISM2_PARAM_PTYPE = 1, */ /* REMOVED 2003-10-22 */ | ||
| 250 | PRISM2_PARAM_TXRATECTRL = 2, | ||
| 251 | PRISM2_PARAM_BEACON_INT = 3, | ||
| 252 | PRISM2_PARAM_PSEUDO_IBSS = 4, | ||
| 253 | PRISM2_PARAM_ALC = 5, | ||
| 254 | /* PRISM2_PARAM_TXPOWER = 6, */ /* REMOVED 2003-10-22 */ | ||
| 255 | PRISM2_PARAM_DUMP = 7, | ||
| 256 | PRISM2_PARAM_OTHER_AP_POLICY = 8, | ||
| 257 | PRISM2_PARAM_AP_MAX_INACTIVITY = 9, | ||
| 258 | PRISM2_PARAM_AP_BRIDGE_PACKETS = 10, | ||
| 259 | PRISM2_PARAM_DTIM_PERIOD = 11, | ||
| 260 | PRISM2_PARAM_AP_NULLFUNC_ACK = 12, | ||
| 261 | PRISM2_PARAM_MAX_WDS = 13, | ||
| 262 | PRISM2_PARAM_AP_AUTOM_AP_WDS = 14, | ||
| 263 | PRISM2_PARAM_AP_AUTH_ALGS = 15, | ||
| 264 | PRISM2_PARAM_MONITOR_ALLOW_FCSERR = 16, | ||
| 265 | PRISM2_PARAM_HOST_ENCRYPT = 17, | ||
| 266 | PRISM2_PARAM_HOST_DECRYPT = 18, | ||
| 267 | /* PRISM2_PARAM_BUS_MASTER_THRESHOLD_RX = 19, REMOVED 2005-08-14 */ | ||
| 268 | /* PRISM2_PARAM_BUS_MASTER_THRESHOLD_TX = 20, REMOVED 2005-08-14 */ | ||
| 269 | PRISM2_PARAM_HOST_ROAMING = 21, | ||
| 270 | PRISM2_PARAM_BCRX_STA_KEY = 22, | ||
| 271 | PRISM2_PARAM_IEEE_802_1X = 23, | ||
| 272 | PRISM2_PARAM_ANTSEL_TX = 24, | ||
| 273 | PRISM2_PARAM_ANTSEL_RX = 25, | ||
| 274 | PRISM2_PARAM_MONITOR_TYPE = 26, | ||
| 275 | PRISM2_PARAM_WDS_TYPE = 27, | ||
| 276 | PRISM2_PARAM_HOSTSCAN = 28, | ||
| 277 | PRISM2_PARAM_AP_SCAN = 29, | ||
| 278 | PRISM2_PARAM_ENH_SEC = 30, | ||
| 279 | PRISM2_PARAM_IO_DEBUG = 31, | ||
| 280 | PRISM2_PARAM_BASIC_RATES = 32, | ||
| 281 | PRISM2_PARAM_OPER_RATES = 33, | ||
| 282 | PRISM2_PARAM_HOSTAPD = 34, | ||
| 283 | PRISM2_PARAM_HOSTAPD_STA = 35, | ||
| 284 | PRISM2_PARAM_WPA = 36, | ||
| 285 | PRISM2_PARAM_PRIVACY_INVOKED = 37, | ||
| 286 | PRISM2_PARAM_TKIP_COUNTERMEASURES = 38, | ||
| 287 | PRISM2_PARAM_DROP_UNENCRYPTED = 39, | ||
| 288 | PRISM2_PARAM_SCAN_CHANNEL_MASK = 40, | ||
| 289 | }; | ||
| 290 | |||
| 291 | enum { HOSTAP_ANTSEL_DO_NOT_TOUCH = 0, HOSTAP_ANTSEL_DIVERSITY = 1, | ||
| 292 | HOSTAP_ANTSEL_LOW = 2, HOSTAP_ANTSEL_HIGH = 3 }; | ||
| 293 | |||
| 294 | |||
| 295 | /* PRISM2_IOCTL_MACCMD ioctl() subcommands: */ | ||
| 296 | enum { AP_MAC_CMD_POLICY_OPEN = 0, AP_MAC_CMD_POLICY_ALLOW = 1, | ||
| 297 | AP_MAC_CMD_POLICY_DENY = 2, AP_MAC_CMD_FLUSH = 3, | ||
| 298 | AP_MAC_CMD_KICKALL = 4 }; | ||
| 299 | |||
| 300 | |||
| 301 | /* PRISM2_IOCTL_DOWNLOAD ioctl() dl_cmd: */ | ||
| 302 | enum { | ||
| 303 | PRISM2_DOWNLOAD_VOLATILE = 1 /* RAM */, | ||
| 304 | /* Note! Old versions of prism2_srec have a fatal error in CRC-16 | ||
| 305 | * calculation, which will corrupt all non-volatile downloads. | ||
| 306 | * PRISM2_DOWNLOAD_NON_VOLATILE used to be 2, but it is now 3 to | ||
| 307 | * prevent use of old versions of prism2_srec for non-volatile | ||
| 308 | * download. */ | ||
| 309 | PRISM2_DOWNLOAD_NON_VOLATILE = 3 /* FLASH */, | ||
| 310 | PRISM2_DOWNLOAD_VOLATILE_GENESIS = 4 /* RAM in Genesis mode */, | ||
| 311 | /* Persistent versions of volatile download commands (keep firmware | ||
| 312 | * data in memory and automatically re-download after hw_reset */ | ||
| 313 | PRISM2_DOWNLOAD_VOLATILE_PERSISTENT = 5, | ||
| 314 | PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT = 6, | ||
| 315 | }; | ||
| 316 | |||
| 317 | struct prism2_download_param { | ||
| 318 | u32 dl_cmd; | ||
| 319 | u32 start_addr; | ||
| 320 | u32 num_areas; | ||
| 321 | struct prism2_download_area { | ||
| 322 | u32 addr; /* wlan card address */ | ||
| 323 | u32 len; | ||
| 324 | void __user *ptr; /* pointer to data in user space */ | ||
| 325 | } data[]; | ||
| 326 | }; | ||
| 327 | |||
| 328 | #define PRISM2_MAX_DOWNLOAD_AREA_LEN 131072 | ||
| 329 | #define PRISM2_MAX_DOWNLOAD_LEN 262144 | ||
| 330 | |||
| 331 | |||
| 332 | /* PRISM2_IOCTL_HOSTAPD ioctl() cmd: */ | ||
| 333 | enum { | ||
| 334 | PRISM2_HOSTAPD_FLUSH = 1, | ||
| 335 | PRISM2_HOSTAPD_ADD_STA = 2, | ||
| 336 | PRISM2_HOSTAPD_REMOVE_STA = 3, | ||
| 337 | PRISM2_HOSTAPD_GET_INFO_STA = 4, | ||
| 338 | /* REMOVED: PRISM2_HOSTAPD_RESET_TXEXC_STA = 5, */ | ||
| 339 | PRISM2_SET_ENCRYPTION = 6, | ||
| 340 | PRISM2_GET_ENCRYPTION = 7, | ||
| 341 | PRISM2_HOSTAPD_SET_FLAGS_STA = 8, | ||
| 342 | PRISM2_HOSTAPD_GET_RID = 9, | ||
| 343 | PRISM2_HOSTAPD_SET_RID = 10, | ||
| 344 | PRISM2_HOSTAPD_SET_ASSOC_AP_ADDR = 11, | ||
| 345 | PRISM2_HOSTAPD_SET_GENERIC_ELEMENT = 12, | ||
| 346 | PRISM2_HOSTAPD_MLME = 13, | ||
| 347 | PRISM2_HOSTAPD_SCAN_REQ = 14, | ||
| 348 | PRISM2_HOSTAPD_STA_CLEAR_STATS = 15, | ||
| 349 | }; | ||
| 350 | |||
| 351 | #define PRISM2_HOSTAPD_MAX_BUF_SIZE 1024 | ||
| 352 | #define PRISM2_HOSTAPD_RID_HDR_LEN \ | ||
| 353 | offsetof(struct prism2_hostapd_param, u.rid.data) | ||
| 354 | #define PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN \ | ||
| 355 | offsetof(struct prism2_hostapd_param, u.generic_elem.data) | ||
| 356 | |||
| 357 | /* Maximum length for algorithm names (-1 for nul termination) used in ioctl() | ||
| 358 | */ | ||
| 359 | #define HOSTAP_CRYPT_ALG_NAME_LEN 16 | ||
| 360 | |||
| 361 | |||
| 362 | struct prism2_hostapd_param { | ||
| 363 | u32 cmd; | ||
| 364 | u8 sta_addr[ETH_ALEN]; | ||
| 365 | union { | ||
| 366 | struct { | ||
| 367 | u16 aid; | ||
| 368 | u16 capability; | ||
| 369 | u8 tx_supp_rates; | ||
| 370 | } add_sta; | ||
| 371 | struct { | ||
| 372 | u32 inactive_sec; | ||
| 373 | } get_info_sta; | ||
| 374 | struct { | ||
| 375 | u8 alg[HOSTAP_CRYPT_ALG_NAME_LEN]; | ||
| 376 | u32 flags; | ||
| 377 | u32 err; | ||
| 378 | u8 idx; | ||
| 379 | u8 seq[8]; /* sequence counter (set: RX, get: TX) */ | ||
| 380 | u16 key_len; | ||
| 381 | u8 key[0]; | ||
| 382 | } crypt; | ||
| 383 | struct { | ||
| 384 | u32 flags_and; | ||
| 385 | u32 flags_or; | ||
| 386 | } set_flags_sta; | ||
| 387 | struct { | ||
| 388 | u16 rid; | ||
| 389 | u16 len; | ||
| 390 | u8 data[0]; | ||
| 391 | } rid; | ||
| 392 | struct { | ||
| 393 | u8 len; | ||
| 394 | u8 data[0]; | ||
| 395 | } generic_elem; | ||
| 396 | struct { | ||
| 397 | #define MLME_STA_DEAUTH 0 | ||
| 398 | #define MLME_STA_DISASSOC 1 | ||
| 399 | u16 cmd; | ||
| 400 | u16 reason_code; | ||
| 401 | } mlme; | ||
| 402 | struct { | ||
| 403 | u8 ssid_len; | ||
| 404 | u8 ssid[32]; | ||
| 405 | } scan_req; | ||
| 406 | } u; | ||
| 407 | }; | ||
| 408 | |||
| 409 | #define HOSTAP_CRYPT_FLAG_SET_TX_KEY BIT(0) | ||
| 410 | #define HOSTAP_CRYPT_FLAG_PERMANENT BIT(1) | ||
| 411 | |||
| 412 | #define HOSTAP_CRYPT_ERR_UNKNOWN_ALG 2 | ||
| 413 | #define HOSTAP_CRYPT_ERR_UNKNOWN_ADDR 3 | ||
| 414 | #define HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED 4 | ||
| 415 | #define HOSTAP_CRYPT_ERR_KEY_SET_FAILED 5 | ||
| 416 | #define HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED 6 | ||
| 417 | #define HOSTAP_CRYPT_ERR_CARD_CONF_FAILED 7 | ||
| 418 | |||
| 419 | |||
| 420 | #endif /* HOSTAP_COMMON_H */ | ||
| @@ -1,49 +0,0 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 2 | #ifndef HOSTAP_CONFIG_H | ||
| 3 | #define HOSTAP_CONFIG_H | ||
| 4 | |||
| 5 | /* In the previous versions of Host AP driver, support for user space version | ||
| 6 | * of IEEE 802.11 management (hostapd) used to be disabled in the default | ||
| 7 | * configuration. From now on, support for hostapd is always included and it is | ||
| 8 | * possible to disable kernel driver version of IEEE 802.11 management with a | ||
| 9 | * separate define, PRISM2_NO_KERNEL_IEEE80211_MGMT. */ | ||
| 10 | /* #define PRISM2_NO_KERNEL_IEEE80211_MGMT */ | ||
| 11 | |||
| 12 | /* Maximum number of events handler per one interrupt */ | ||
| 13 | #define PRISM2_MAX_INTERRUPT_EVENTS 20 | ||
| 14 | |||
| 15 | /* Include code for downloading firmware images into volatile RAM. */ | ||
| 16 | #define PRISM2_DOWNLOAD_SUPPORT | ||
| 17 | |||
| 18 | /* Allow kernel configuration to enable download support. */ | ||
| 19 | #if !defined(PRISM2_DOWNLOAD_SUPPORT) && defined(CONFIG_HOSTAP_FIRMWARE) | ||
| 20 | #define PRISM2_DOWNLOAD_SUPPORT | ||
| 21 | #endif | ||
| 22 | |||
| 23 | /* Allow kernel configuration to enable non-volatile download support. */ | ||
| 24 | #ifdef CONFIG_HOSTAP_FIRMWARE_NVRAM | ||
| 25 | #define PRISM2_NON_VOLATILE_DOWNLOAD | ||
| 26 | #endif | ||
| 27 | |||
| 28 | /* Save low-level I/O for debugging. This should not be enabled in normal use. | ||
| 29 | */ | ||
| 30 | /* #define PRISM2_IO_DEBUG */ | ||
| 31 | |||
| 32 | /* Following defines can be used to remove unneeded parts of the driver, e.g., | ||
| 33 | * to limit the size of the kernel module. Definitions can be added here in | ||
| 34 | * hostap_config.h or they can be added to make command with ccflags-y, | ||
| 35 | * e.g., | ||
| 36 | * 'make pccard ccflags-y="-DPRISM2_NO_DEBUG -DPRISM2_NO_PROCFS_DEBUG"' | ||
| 37 | */ | ||
| 38 | |||
| 39 | /* Do not include debug messages into the driver */ | ||
| 40 | /* #define PRISM2_NO_DEBUG */ | ||
| 41 | |||
| 42 | /* Do not include /proc/net/prism2/wlan#/{registers,debug} */ | ||
| 43 | /* #define PRISM2_NO_PROCFS_DEBUG */ | ||
| 44 | |||
| 45 | /* Do not include station functionality (i.e., allow only Master (Host AP) mode | ||
| 46 | */ | ||
| 47 | /* #define PRISM2_NO_STATION_MODES */ | ||
| 48 | |||
| 49 | #endif /* HOSTAP_CONFIG_H */ | ||
| @@ -1,710 +0,0 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0-only | ||
| 2 | #define PRISM2_PCCARD | ||
| 3 | |||
| 4 | #include <linux/module.h> | ||
| 5 | #include <linux/if.h> | ||
| 6 | #include <linux/slab.h> | ||
| 7 | #include <linux/wait.h> | ||
| 8 | #include <linux/timer.h> | ||
| 9 | #include <linux/skbuff.h> | ||
| 10 | #include <linux/netdevice.h> | ||
| 11 | #include <linux/workqueue.h> | ||
| 12 | #include <linux/wireless.h> | ||
| 13 | #include <net/iw_handler.h> | ||
| 14 | |||
| 15 | #include <pcmcia/cistpl.h> | ||
| 16 | #include <pcmcia/cisreg.h> | ||
| 17 | #include <pcmcia/ds.h> | ||
| 18 | |||
| 19 | #include <asm/io.h> | ||
| 20 | |||
| 21 | #include "hostap_wlan.h" | ||
| 22 | |||
| 23 | |||
| 24 | static char *dev_info = "hostap_cs"; | ||
| 25 | |||
| 26 | MODULE_AUTHOR("Jouni Malinen"); | ||
| 27 | MODULE_DESCRIPTION("Support for Intersil Prism2-based 802.11 wireless LAN " | ||
| 28 | "cards (PC Card)."); | ||
| 29 | MODULE_LICENSE("GPL"); | ||
| 30 | |||
| 31 | |||
| 32 | static int ignore_cis_vcc; | ||
| 33 | module_param(ignore_cis_vcc, int, 0444); | ||
| 34 | MODULE_PARM_DESC(ignore_cis_vcc, "Ignore broken CIS VCC entry"); | ||
| 35 | |||
| 36 | |||
| 37 | /* struct local_info::hw_priv */ | ||
| 38 | struct hostap_cs_priv { | ||
| 39 | struct pcmcia_device *link; | ||
| 40 | int sandisk_connectplus; | ||
| 41 | }; | ||
| 42 | |||
| 43 | |||
| 44 | #ifdef PRISM2_IO_DEBUG | ||
| 45 | |||
| 46 | static inline void hfa384x_outb_debug(struct net_device *dev, int a, u8 v) | ||
| 47 | { | ||
| 48 | struct hostap_interface *iface; | ||
| 49 | local_info_t *local; | ||
| 50 | unsigned long flags; | ||
| 51 | |||
| 52 | iface = netdev_priv(dev); | ||
| 53 | local = iface->local; | ||
| 54 | spin_lock_irqsave(&local->lock, flags); | ||
| 55 | prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTB, a, v); | ||
| 56 | outb(v, dev->base_addr + a); | ||
| 57 | spin_unlock_irqrestore(&local->lock, flags); | ||
| 58 | } | ||
| 59 | |||
| 60 | static inline u8 hfa384x_inb_debug(struct net_device *dev, int a) | ||
| 61 | { | ||
| 62 | struct hostap_interface *iface; | ||
| 63 | local_info_t *local; | ||
| 64 | unsigned long flags; | ||
| 65 | u8 v; | ||
| 66 | |||
| 67 | iface = netdev_priv(dev); | ||
| 68 | local = iface->local; | ||
| 69 | spin_lock_irqsave(&local->lock, flags); | ||
| 70 | v = inb(dev->base_addr + a); | ||
| 71 | prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INB, a, v); | ||
| 72 | spin_unlock_irqrestore(&local->lock, flags); | ||
| 73 | return v; | ||
| 74 | } | ||
| 75 | |||
| 76 | static inline void hfa384x_outw_debug(struct net_device *dev, int a, u16 v) | ||
| 77 | { | ||
| 78 | struct hostap_interface *iface; | ||
| 79 | local_info_t *local; | ||
| 80 | unsigned long flags; | ||
| 81 | |||
| 82 | iface = netdev_priv(dev); | ||
| 83 | local = iface->local; | ||
| 84 | spin_lock_irqsave(&local->lock, flags); | ||
| 85 | prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTW, a, v); | ||
| 86 | outw(v, dev->base_addr + a); | ||
| 87 | spin_unlock_irqrestore(&local->lock, flags); | ||
| 88 | } | ||
| 89 | |||
| 90 | static inline u16 hfa384x_inw_debug(struct net_device *dev, int a) | ||
| 91 | { | ||
| 92 | struct hostap_interface *iface; | ||
| 93 | local_info_t *local; | ||
| 94 | unsigned long flags; | ||
| 95 | u16 v; | ||
| 96 | |||
| 97 | iface = netdev_priv(dev); | ||
| 98 | local = iface->local; | ||
| 99 | spin_lock_irqsave(&local->lock, flags); | ||
| 100 | v = inw(dev->base_addr + a); | ||
| 101 | prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INW, a, v); | ||
| 102 | spin_unlock_irqrestore(&local->lock, flags); | ||
| 103 | return v; | ||
| 104 | } | ||
| 105 | |||
| 106 | static inline void hfa384x_outsw_debug(struct net_device *dev, int a, | ||
| 107 | u8 *buf, int wc) | ||
| 108 | { | ||
| 109 | struct hostap_interface *iface; | ||
| 110 | local_info_t *local; | ||
| 111 | unsigned long flags; | ||
| 112 | |||
| 113 | iface = netdev_priv(dev); | ||
| 114 | local = iface->local; | ||
| 115 | spin_lock_irqsave(&local->lock, flags); | ||
| 116 | prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTSW, a, wc); | ||
| 117 | outsw(dev->base_addr + a, buf, wc); | ||
| 118 | spin_unlock_irqrestore(&local->lock, flags); | ||
| 119 | } | ||
| 120 | |||
| 121 | static inline void hfa384x_insw_debug(struct net_device *dev, int a, | ||
| 122 | u8 *buf, int wc) | ||
| 123 | { | ||
| 124 | struct hostap_interface *iface; | ||
| 125 | local_info_t *local; | ||
| 126 | unsigned long flags; | ||
| 127 | |||
| 128 | iface = netdev_priv(dev); | ||
| 129 | local = iface->local; | ||
| 130 | spin_lock_irqsave(&local->lock, flags); | ||
| 131 | prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INSW, a, wc); | ||
| 132 | insw(dev->base_addr + a, buf, wc); | ||
| 133 | spin_unlock_irqrestore(&local->lock, flags); | ||
| 134 | } | ||
| 135 | |||
| 136 | #define HFA384X_OUTB(v,a) hfa384x_outb_debug(dev, (a), (v)) | ||
| 137 | #define HFA384X_INB(a) hfa384x_inb_debug(dev, (a)) | ||
| 138 | #define HFA384X_OUTW(v,a) hfa384x_outw_debug(dev, (a), (v)) | ||
| 139 | #define HFA384X_INW(a) hfa384x_inw_debug(dev, (a)) | ||
| 140 | #define HFA384X_OUTSW(a, buf, wc) hfa384x_outsw_debug(dev, (a), (buf), (wc)) | ||
| 141 | #define HFA384X_INSW(a, buf, wc) hfa384x_insw_debug(dev, (a), (buf), (wc)) | ||
| 142 | |||
| 143 | #else /* PRISM2_IO_DEBUG */ | ||
| 144 | |||
| 145 | #define HFA384X_OUTB(v,a) outb((v), dev->base_addr + (a)) | ||
| 146 | #define HFA384X_INB(a) inb(dev->base_addr + (a)) | ||
| 147 | #define HFA384X_OUTW(v,a) outw((v), dev->base_addr + (a)) | ||
| 148 | #define HFA384X_INW(a) inw(dev->base_addr + (a)) | ||
| 149 | #define HFA384X_INSW(a, buf, wc) insw(dev->base_addr + (a), buf, wc) | ||
| 150 | #define HFA384X_OUTSW(a, buf, wc) outsw(dev->base_addr + (a), buf, wc) | ||
| 151 | |||
| 152 | #endif /* PRISM2_IO_DEBUG */ | ||
| 153 | |||
| 154 | |||
| 155 | static int hfa384x_from_bap(struct net_device *dev, u16 bap, void *buf, | ||
| 156 | int len) | ||
| 157 | { | ||
| 158 | u16 d_off; | ||
| 159 | u16 *pos; | ||
| 160 | |||
| 161 | d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF; | ||
| 162 | pos = (u16 *) buf; | ||
| 163 | |||
| 164 | if (len / 2) | ||
| 165 | HFA384X_INSW(d_off, buf, len / 2); | ||
| 166 | pos += len / 2; | ||
| 167 | |||
| 168 | if (len & 1) | ||
| 169 | *((char *) pos) = HFA384X_INB(d_off); | ||
| 170 | |||
| 171 | return 0; | ||
| 172 | } | ||
| 173 | |||
| 174 | |||
| 175 | static int hfa384x_to_bap(struct net_device *dev, u16 bap, void *buf, int len) | ||
| 176 | { | ||
| 177 | u16 d_off; | ||
| 178 | u16 *pos; | ||
| 179 | |||
| 180 | d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF; | ||
| 181 | pos = (u16 *) buf; | ||
| 182 | |||
| 183 | if (len / 2) | ||
| 184 | HFA384X_OUTSW(d_off, buf, len / 2); | ||
| 185 | pos += len / 2; | ||
| 186 | |||
| 187 | if (len & 1) | ||
| 188 | HFA384X_OUTB(*((char *) pos), d_off); | ||
| 189 | |||
| 190 | return 0; | ||
| 191 | } | ||
| 192 | |||
| 193 | |||
| 194 | /* FIX: This might change at some point.. */ | ||
| 195 | #include "hostap_hw.c" | ||
| 196 | |||
| 197 | |||
| 198 | |||
| 199 | static void prism2_detach(struct pcmcia_device *p_dev); | ||
| 200 | static void prism2_release(u_long arg); | ||
| 201 | static int prism2_config(struct pcmcia_device *link); | ||
| 202 | |||
| 203 | |||
| 204 | static int prism2_pccard_card_present(local_info_t *local) | ||
| 205 | { | ||
| 206 | struct hostap_cs_priv *hw_priv = local->hw_priv; | ||
| 207 | if (hw_priv != NULL && hw_priv->link != NULL && pcmcia_dev_present(hw_priv->link)) | ||
| 208 | return 1; | ||
| 209 | return 0; | ||
| 210 | } | ||
| 211 | |||
| 212 | |||
| 213 | /* | ||
| 214 | * SanDisk CompactFlash WLAN Flashcard - Product Manual v1.0 | ||
| 215 | * Document No. 20-10-00058, January 2004 | ||
| 216 | * http://www.sandisk.com/pdf/industrial/ProdManualCFWLANv1.0.pdf | ||
| 217 | */ | ||
| 218 | #define SANDISK_WLAN_ACTIVATION_OFF 0x40 | ||
| 219 | #define SANDISK_HCR_OFF 0x42 | ||
| 220 | |||
| 221 | |||
| 222 | static void sandisk_set_iobase(local_info_t *local) | ||
| 223 | { | ||
| 224 | int res; | ||
| 225 | struct hostap_cs_priv *hw_priv = local->hw_priv; | ||
| 226 | |||
| 227 | res = pcmcia_write_config_byte(hw_priv->link, 0x10, | ||
| 228 | hw_priv->link->resource[0]->start & 0x00ff); | ||
| 229 | if (res != 0) { | ||
| 230 | printk(KERN_DEBUG "Prism3 SanDisk - failed to set I/O base 0 -" | ||
| 231 | " res=%d\n", res); | ||
| 232 | } | ||
| 233 | udelay(10); | ||
| 234 | |||
| 235 | res = pcmcia_write_config_byte(hw_priv->link, 0x12, | ||
| 236 | (hw_priv->link->resource[0]->start >> 8) & 0x00ff); | ||
| 237 | if (res != 0) { | ||
| 238 | printk(KERN_DEBUG "Prism3 SanDisk - failed to set I/O base 1 -" | ||
| 239 | " res=%d\n", res); | ||
| 240 | } | ||
| 241 | } | ||
| 242 | |||
| 243 | |||
| 244 | static void sandisk_write_hcr(local_info_t *local, int hcr) | ||
| 245 | { | ||
| 246 | struct net_device *dev = local->dev; | ||
| 247 | int i; | ||
| 248 | |||
| 249 | HFA384X_OUTB(0x80, SANDISK_WLAN_ACTIVATION_OFF); | ||
| 250 | udelay(50); | ||
| 251 | for (i = 0; i < 10; i++) { | ||
| 252 | HFA384X_OUTB(hcr, SANDISK_HCR_OFF); | ||
| 253 | } | ||
| 254 | udelay(55); | ||
| 255 | HFA384X_OUTB(0x45, SANDISK_WLAN_ACTIVATION_OFF); | ||
| 256 | } | ||
| 257 | |||
| 258 | |||
| 259 | static int sandisk_enable_wireless(struct net_device *dev) | ||
| 260 | { | ||
| 261 | int res, ret = 0; | ||
| 262 | struct hostap_interface *iface = netdev_priv(dev); | ||
| 263 | local_info_t *local = iface->local; | ||
| 264 | struct hostap_cs_priv *hw_priv = local->hw_priv; | ||
| 265 | |||
| 266 | if (resource_size(hw_priv->link->resource[0]) < 0x42) { | ||
| 267 | /* Not enough ports to be SanDisk multi-function card */ | ||
| 268 | ret = -ENODEV; | ||
| 269 | goto done; | ||
| 270 | } | ||
| 271 | |||
| 272 | if (hw_priv->link->manf_id != 0xd601 || hw_priv->link->card_id != 0x0101) { | ||
| 273 | /* No SanDisk manfid found */ | ||
| 274 | ret = -ENODEV; | ||
| 275 | goto done; | ||
| 276 | } | ||
| 277 | |||
| 278 | if (hw_priv->link->socket->functions < 2) { | ||
| 279 | /* No multi-function links found */ | ||
| 280 | ret = -ENODEV; | ||
| 281 | goto done; | ||
| 282 | } | ||
| 283 | |||
| 284 | printk(KERN_DEBUG "%s: Multi-function SanDisk ConnectPlus detected" | ||
| 285 | " - using vendor-specific initialization\n", dev->name); | ||
| 286 | hw_priv->sandisk_connectplus = 1; | ||
| 287 | |||
| 288 | res = pcmcia_write_config_byte(hw_priv->link, CISREG_COR, | ||
| 289 | COR_SOFT_RESET); | ||
| 290 | if (res != 0) { | ||
| 291 | printk(KERN_DEBUG "%s: SanDisk - COR sreset failed (%d)\n", | ||
| 292 | dev->name, res); | ||
| 293 | goto done; | ||
| 294 | } | ||
| 295 | mdelay(5); | ||
| 296 | |||
| 297 | /* | ||
| 298 | * Do not enable interrupts here to avoid some bogus events. Interrupts | ||
| 299 | * will be enabled during the first cor_sreset call. | ||
| 300 | */ | ||
| 301 | res = pcmcia_write_config_byte(hw_priv->link, CISREG_COR, | ||
| 302 | (COR_LEVEL_REQ | 0x8 | COR_ADDR_DECODE | | ||
| 303 | COR_FUNC_ENA)); | ||
| 304 | if (res != 0) { | ||
| 305 | printk(KERN_DEBUG "%s: SanDisk - COR sreset failed (%d)\n", | ||
| 306 | dev->name, res); | ||
| 307 | goto done; | ||
| 308 | } | ||
| 309 | mdelay(5); | ||
| 310 | |||
| 311 | sandisk_set_iobase(local); | ||
| 312 | |||
| 313 | HFA384X_OUTB(0xc5, SANDISK_WLAN_ACTIVATION_OFF); | ||
| 314 | udelay(10); | ||
| 315 | HFA384X_OUTB(0x4b, SANDISK_WLAN_ACTIVATION_OFF); | ||
| 316 | udelay(10); | ||
| 317 | |||
| 318 | done: | ||
| 319 | return ret; | ||
| 320 | } | ||
| 321 | |||
| 322 | |||
| 323 | static void prism2_pccard_cor_sreset(local_info_t *local) | ||
| 324 | { | ||
| 325 | int res; | ||
| 326 | u8 val; | ||
| 327 | struct hostap_cs_priv *hw_priv = local->hw_priv; | ||
| 328 | |||
| 329 | if (!prism2_pccard_card_present(local)) | ||
| 330 | return; | ||
| 331 | |||
| 332 | res = pcmcia_read_config_byte(hw_priv->link, CISREG_COR, &val); | ||
| 333 | if (res != 0) { | ||
| 334 | printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 1 (%d)\n", | ||
| 335 | res); | ||
| 336 | return; | ||
| 337 | } | ||
| 338 | printk(KERN_DEBUG "prism2_pccard_cor_sreset: original COR %02x\n", | ||
| 339 | val); | ||
| 340 | |||
| 341 | val |= COR_SOFT_RESET; | ||
| 342 | res = pcmcia_write_config_byte(hw_priv->link, CISREG_COR, val); | ||
| 343 | if (res != 0) { | ||
| 344 | printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 2 (%d)\n", | ||
| 345 | res); | ||
| 346 | return; | ||
| 347 | } | ||
| 348 | |||
| 349 | mdelay(hw_priv->sandisk_connectplus ? 5 : 2); | ||
| 350 | |||
| 351 | val &= ~COR_SOFT_RESET; | ||
| 352 | if (hw_priv->sandisk_connectplus) | ||
| 353 | val |= COR_IREQ_ENA; | ||
| 354 | res = pcmcia_write_config_byte(hw_priv->link, CISREG_COR, val); | ||
| 355 | if (res != 0) { | ||
| 356 | printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 3 (%d)\n", | ||
| 357 | res); | ||
| 358 | return; | ||
| 359 | } | ||
| 360 | |||
| 361 | mdelay(hw_priv->sandisk_connectplus ? 5 : 2); | ||
| 362 | |||
| 363 | if (hw_priv->sandisk_connectplus) | ||
| 364 | sandisk_set_iobase(local); | ||
| 365 | } | ||
| 366 | |||
| 367 | |||
| 368 | static void prism2_pccard_genesis_reset(local_info_t *local, int hcr) | ||
| 369 | { | ||
| 370 | int res; | ||
| 371 | u8 old_cor; | ||
| 372 | struct hostap_cs_priv *hw_priv = local->hw_priv; | ||
| 373 | |||
| 374 | if (!prism2_pccard_card_present(local)) | ||
| 375 | return; | ||
| 376 | |||
| 377 | if (hw_priv->sandisk_connectplus) { | ||
| 378 | sandisk_write_hcr(local, hcr); | ||
| 379 | return; | ||
| 380 | } | ||
| 381 | |||
| 382 | res = pcmcia_read_config_byte(hw_priv->link, CISREG_COR, &old_cor); | ||
| 383 | if (res != 0) { | ||
| 384 | printk(KERN_DEBUG "%s failed 1 (%d)\n", __func__, res); | ||
| 385 | return; | ||
| 386 | } | ||
| 387 | printk(KERN_DEBUG "%s: original COR %02x\n", __func__, old_cor); | ||
| 388 | |||
| 389 | res = pcmcia_write_config_byte(hw_priv->link, CISREG_COR, | ||
| 390 | old_cor | COR_SOFT_RESET); | ||
| 391 | if (res != 0) { | ||
| 392 | printk(KERN_DEBUG "%s failed 2 (%d)\n", __func__, res); | ||
| 393 | return; | ||
| 394 | } | ||
| 395 | |||
| 396 | mdelay(10); | ||
| 397 | |||
| 398 | /* Setup Genesis mode */ | ||
| 399 | res = pcmcia_write_config_byte(hw_priv->link, CISREG_CCSR, hcr); | ||
| 400 | if (res != 0) { | ||
| 401 | printk(KERN_DEBUG "%s failed 3 (%d)\n", __func__, res); | ||
| 402 | return; | ||
| 403 | } | ||
| 404 | mdelay(10); | ||
| 405 | |||
| 406 | res = pcmcia_write_config_byte(hw_priv->link, CISREG_COR, | ||
| 407 | old_cor & ~COR_SOFT_RESET); | ||
| 408 | if (res != 0) { | ||
| 409 | printk(KERN_DEBUG "%s failed 4 (%d)\n", __func__, res); | ||
| 410 | return; | ||
| 411 | } | ||
| 412 | |||
| 413 | mdelay(10); | ||
| 414 | } | ||
| 415 | |||
| 416 | |||
| 417 | static struct prism2_helper_functions prism2_pccard_funcs = | ||
| 418 | { | ||
| 419 | .card_present = prism2_pccard_card_present, | ||
| 420 | .cor_sreset = prism2_pccard_cor_sreset, | ||
| 421 | .genesis_reset = prism2_pccard_genesis_reset, | ||
| 422 | .hw_type = HOSTAP_HW_PCCARD, | ||
| 423 | }; | ||
| 424 | |||
| 425 | |||
| 426 | /* allocate local data and register with CardServices | ||
| 427 | * initialize dev_link structure, but do not configure the card yet */ | ||
| 428 | static int hostap_cs_probe(struct pcmcia_device *p_dev) | ||
| 429 | { | ||
| 430 | int ret; | ||
| 431 | |||
| 432 | PDEBUG(DEBUG_HW, "%s: setting Vcc=33 (constant)\n", dev_info); | ||
| 433 | |||
| 434 | ret = prism2_config(p_dev); | ||
| 435 | if (ret) { | ||
| 436 | PDEBUG(DEBUG_EXTRA, "prism2_config() failed\n"); | ||
| 437 | } | ||
| 438 | |||
| 439 | return ret; | ||
| 440 | } | ||
| 441 | |||
| 442 | |||
| 443 | static void prism2_detach(struct pcmcia_device *link) | ||
| 444 | { | ||
| 445 | PDEBUG(DEBUG_FLOW, "prism2_detach\n"); | ||
| 446 | |||
| 447 | prism2_release((u_long)link); | ||
| 448 | |||
| 449 | /* release net devices */ | ||
| 450 | if (link->priv) { | ||
| 451 | struct hostap_cs_priv *hw_priv; | ||
| 452 | struct net_device *dev; | ||
| 453 | struct hostap_interface *iface; | ||
| 454 | dev = link->priv; | ||
| 455 | iface = netdev_priv(dev); | ||
| 456 | hw_priv = iface->local->hw_priv; | ||
| 457 | prism2_free_local_data(dev); | ||
| 458 | kfree(hw_priv); | ||
| 459 | } | ||
| 460 | } | ||
| 461 | |||
| 462 | |||
| 463 | static int prism2_config_check(struct pcmcia_device *p_dev, void *priv_data) | ||
| 464 | { | ||
| 465 | if (p_dev->config_index == 0) | ||
| 466 | return -EINVAL; | ||
| 467 | |||
| 468 | return pcmcia_request_io(p_dev); | ||
| 469 | } | ||
| 470 | |||
| 471 | static int prism2_config(struct pcmcia_device *link) | ||
| 472 | { | ||
| 473 | struct net_device *dev; | ||
| 474 | struct hostap_interface *iface; | ||
| 475 | local_info_t *local; | ||
| 476 | int ret; | ||
| 477 | struct hostap_cs_priv *hw_priv; | ||
| 478 | unsigned long flags; | ||
| 479 | |||
| 480 | PDEBUG(DEBUG_FLOW, "prism2_config()\n"); | ||
| 481 | |||
| 482 | hw_priv = kzalloc(sizeof(*hw_priv), GFP_KERNEL); | ||
| 483 | if (hw_priv == NULL) { | ||
| 484 | ret = -ENOMEM; | ||
| 485 | goto failed; | ||
| 486 | } | ||
| 487 | |||
| 488 | /* Look for an appropriate configuration table entry in the CIS */ | ||
| 489 | link->config_flags |= CONF_AUTO_SET_VPP | CONF_AUTO_AUDIO | | ||
| 490 | CONF_AUTO_CHECK_VCC | CONF_AUTO_SET_IO | CONF_ENABLE_IRQ; | ||
| 491 | if (ignore_cis_vcc) | ||
| 492 | link->config_flags &= ~CONF_AUTO_CHECK_VCC; | ||
| 493 | ret = pcmcia_loop_config(link, prism2_config_check, NULL); | ||
| 494 | if (ret) { | ||
| 495 | if (!ignore_cis_vcc) | ||
| 496 | printk(KERN_ERR "GetNextTuple(): No matching " | ||
| 497 | "CIS configuration. Maybe you need the " | ||
| 498 | "ignore_cis_vcc=1 parameter.\n"); | ||
| 499 | goto failed; | ||
| 500 | } | ||
| 501 | |||
| 502 | /* Need to allocate net_device before requesting IRQ handler */ | ||
| 503 | dev = prism2_init_local_data(&prism2_pccard_funcs, 0, | ||
| 504 | &link->dev); | ||
| 505 | if (!dev) { | ||
| 506 | ret = -ENOMEM; | ||
| 507 | goto failed; | ||
| 508 | } | ||
| 509 | link->priv = dev; | ||
| 510 | |||
| 511 | iface = netdev_priv(dev); | ||
| 512 | local = iface->local; | ||
| 513 | local->hw_priv = hw_priv; | ||
| 514 | hw_priv->link = link; | ||
| 515 | |||
| 516 | /* | ||
| 517 | * We enable IRQ here, but IRQ handler will not proceed | ||
| 518 | * until dev->base_addr is set below. This protect us from | ||
| 519 | * receive interrupts when driver is not initialized. | ||
| 520 | */ | ||
| 521 | ret = pcmcia_request_irq(link, prism2_interrupt); | ||
| 522 | if (ret) | ||
| 523 | goto failed; | ||
| 524 | |||
| 525 | ret = pcmcia_enable_device(link); | ||
| 526 | if (ret) | ||
| 527 | goto failed; | ||
| 528 | |||
| 529 | spin_lock_irqsave(&local->irq_init_lock, flags); | ||
| 530 | dev->irq = link->irq; | ||
| 531 | dev->base_addr = link->resource[0]->start; | ||
| 532 | spin_unlock_irqrestore(&local->irq_init_lock, flags); | ||
| 533 | |||
| 534 | local->shutdown = 0; | ||
| 535 | |||
| 536 | sandisk_enable_wireless(dev); | ||
| 537 | |||
| 538 | ret = prism2_hw_config(dev, 1); | ||
| 539 | if (!ret) | ||
| 540 | ret = hostap_hw_ready(dev); | ||
| 541 | |||
| 542 | return ret; | ||
| 543 | |||
| 544 | failed: | ||
| 545 | kfree(hw_priv); | ||
| 546 | prism2_release((u_long)link); | ||
| 547 | return ret; | ||
| 548 | } | ||
| 549 | |||
| 550 | |||
| 551 | static void prism2_release(u_long arg) | ||
| 552 | { | ||
| 553 | struct pcmcia_device *link = (struct pcmcia_device *)arg; | ||
| 554 | |||
| 555 | PDEBUG(DEBUG_FLOW, "prism2_release\n"); | ||
| 556 | |||
| 557 | if (link->priv) { | ||
| 558 | struct net_device *dev = link->priv; | ||
| 559 | struct hostap_interface *iface; | ||
| 560 | |||
| 561 | iface = netdev_priv(dev); | ||
| 562 | prism2_hw_shutdown(dev, 0); | ||
| 563 | iface->local->shutdown = 1; | ||
| 564 | } | ||
| 565 | |||
| 566 | pcmcia_disable_device(link); | ||
| 567 | PDEBUG(DEBUG_FLOW, "release - done\n"); | ||
| 568 | } | ||
| 569 | |||
| 570 | static int hostap_cs_suspend(struct pcmcia_device *link) | ||
| 571 | { | ||
| 572 | struct net_device *dev = (struct net_device *) link->priv; | ||
| 573 | int dev_open = 0; | ||
| 574 | struct hostap_interface *iface = NULL; | ||
| 575 | |||
| 576 | if (!dev) | ||
| 577 | return -ENODEV; | ||
| 578 | |||
| 579 | iface = netdev_priv(dev); | ||
| 580 | |||
| 581 | PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_PM_SUSPEND\n", dev_info); | ||
| 582 | if (iface && iface->local) | ||
| 583 | dev_open = iface->local->num_dev_open > 0; | ||
| 584 | if (dev_open) { | ||
| 585 | netif_stop_queue(dev); | ||
| 586 | netif_device_detach(dev); | ||
| 587 | } | ||
| 588 | prism2_suspend(dev); | ||
| 589 | |||
| 590 | return 0; | ||
| 591 | } | ||
| 592 | |||
| 593 | static int hostap_cs_resume(struct pcmcia_device *link) | ||
| 594 | { | ||
| 595 | struct net_device *dev = (struct net_device *) link->priv; | ||
| 596 | int dev_open = 0; | ||
| 597 | struct hostap_interface *iface = NULL; | ||
| 598 | |||
| 599 | if (!dev) | ||
| 600 | return -ENODEV; | ||
| 601 | |||
| 602 | iface = netdev_priv(dev); | ||
| 603 | |||
| 604 | PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_PM_RESUME\n", dev_info); | ||
| 605 | |||
| 606 | if (iface && iface->local) | ||
| 607 | dev_open = iface->local->num_dev_open > 0; | ||
| 608 | |||
| 609 | prism2_hw_shutdown(dev, 1); | ||
| 610 | prism2_hw_config(dev, dev_open ? 0 : 1); | ||
| 611 | if (dev_open) { | ||
| 612 | netif_device_attach(dev); | ||
| 613 | netif_start_queue(dev); | ||
| 614 | } | ||
| 615 | |||
| 616 | return 0; | ||
| 617 | } | ||
| 618 | |||
| 619 | static const struct pcmcia_device_id hostap_cs_ids[] = { | ||
| 620 | PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7100), | ||
| 621 | PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7300), | ||
| 622 | PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0777), | ||
| 623 | PCMCIA_DEVICE_MANF_CARD(0x0126, 0x8000), | ||
| 624 | PCMCIA_DEVICE_MANF_CARD(0x0138, 0x0002), | ||
| 625 | PCMCIA_DEVICE_MANF_CARD(0x01bf, 0x3301), | ||
| 626 | PCMCIA_DEVICE_MANF_CARD(0x0250, 0x0002), | ||
| 627 | PCMCIA_DEVICE_MANF_CARD(0x026f, 0x030b), | ||
| 628 | PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1612), | ||
| 629 | PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1613), | ||
| 630 | PCMCIA_DEVICE_MANF_CARD(0x028a, 0x0002), | ||
| 631 | PCMCIA_DEVICE_MANF_CARD(0x02aa, 0x0002), | ||
| 632 | PCMCIA_DEVICE_MANF_CARD(0x02d2, 0x0001), | ||
| 633 | PCMCIA_DEVICE_MANF_CARD(0x50c2, 0x0001), | ||
| 634 | PCMCIA_DEVICE_MANF_CARD(0x50c2, 0x7300), | ||
| 635 | /* PCMCIA_DEVICE_MANF_CARD(0xc00f, 0x0000), conflict with pcnet_cs */ | ||
| 636 | PCMCIA_DEVICE_MANF_CARD(0xc250, 0x0002), | ||
| 637 | PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0002), | ||
| 638 | PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0005), | ||
| 639 | PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0010), | ||
| 640 | PCMCIA_DEVICE_MANF_CARD(0x0126, 0x0002), | ||
| 641 | PCMCIA_DEVICE_MANF_CARD_PROD_ID1(0xd601, 0x0005, "ADLINK 345 CF", | ||
| 642 | 0x2d858104), | ||
| 643 | PCMCIA_DEVICE_MANF_CARD_PROD_ID1(0x0156, 0x0002, "INTERSIL", | ||
| 644 | 0x74c5e40d), | ||
| 645 | PCMCIA_DEVICE_MANF_CARD_PROD_ID1(0x0156, 0x0002, "Intersil", | ||
| 646 | 0x4b801a17), | ||
| 647 | PCMCIA_DEVICE_MANF_CARD_PROD_ID3(0x0156, 0x0002, "Version 01.02", | ||
| 648 | 0x4b74baa0), | ||
| 649 | PCMCIA_MFC_DEVICE_PROD_ID12(0, "SanDisk", "ConnectPlus", | ||
| 650 | 0x7a954bd9, 0x74be00c6), | ||
| 651 | PCMCIA_DEVICE_PROD_ID123( | ||
| 652 | "Addtron", "AWP-100 Wireless PCMCIA", "Version 01.02", | ||
| 653 | 0xe6ec52ce, 0x08649af2, 0x4b74baa0), | ||
| 654 | PCMCIA_DEVICE_PROD_ID123( | ||
| 655 | "Canon", "Wireless LAN CF Card K30225", "Version 01.00", | ||
| 656 | 0x96ef6fe2, 0x263fcbab, 0xa57adb8c), | ||
| 657 | PCMCIA_DEVICE_PROD_ID123( | ||
| 658 | "D", "Link DWL-650 11Mbps WLAN Card", "Version 01.02", | ||
| 659 | 0x71b18589, 0xb6f1b0ab, 0x4b74baa0), | ||
| 660 | PCMCIA_DEVICE_PROD_ID123( | ||
| 661 | "Instant Wireless ", " Network PC CARD", "Version 01.02", | ||
| 662 | 0x11d901af, 0x6e9bd926, 0x4b74baa0), | ||
| 663 | PCMCIA_DEVICE_PROD_ID123( | ||
| 664 | "SMC", "SMC2632W", "Version 01.02", | ||
| 665 | 0xc4f8b18b, 0x474a1f2a, 0x4b74baa0), | ||
| 666 | PCMCIA_DEVICE_PROD_ID12("BUFFALO", "WLI-CF-S11G", | ||
| 667 | 0x2decece3, 0x82067c18), | ||
| 668 | PCMCIA_DEVICE_PROD_ID12("Compaq", "WL200_11Mbps_Wireless_PCI_Card", | ||
| 669 | 0x54f7c49c, 0x15a75e5b), | ||
| 670 | PCMCIA_DEVICE_PROD_ID12("INTERSIL", "HFA384x/IEEE", | ||
| 671 | 0x74c5e40d, 0xdb472a18), | ||
| 672 | PCMCIA_DEVICE_PROD_ID12("Linksys", "Wireless CompactFlash Card", | ||
| 673 | 0x0733cc81, 0x0c52f395), | ||
| 674 | PCMCIA_DEVICE_PROD_ID12( | ||
| 675 | "ZoomAir 11Mbps High", "Rate wireless Networking", | ||
| 676 | 0x273fe3db, 0x32a1eaee), | ||
| 677 | PCMCIA_DEVICE_PROD_ID12("NETGEAR MA401 Wireless PC", "Card", | ||
| 678 | 0xa37434e9, 0x9762e8f1), | ||
| 679 | PCMCIA_DEVICE_PROD_ID123( | ||
| 680 | "Pretec", "CompactWLAN Card 802.11b", "2.5", | ||
| 681 | 0x1cadd3e5, 0xe697636c, 0x7a5bfcf1), | ||
| 682 | PCMCIA_DEVICE_PROD_ID123( | ||
| 683 | "U.S. Robotics", "IEEE 802.11b PC-CARD", "Version 01.02", | ||
| 684 | 0xc7b8df9d, 0x1700d087, 0x4b74baa0), | ||
| 685 | PCMCIA_DEVICE_PROD_ID123( | ||
| 686 | "Allied Telesyn", "AT-WCL452 Wireless PCMCIA Radio", | ||
| 687 | "Ver. 1.00", | ||
| 688 | 0x5cd01705, 0x4271660f, 0x9d08ee12), | ||
| 689 | PCMCIA_DEVICE_PROD_ID123( | ||
| 690 | "Wireless LAN" , "11Mbps PC Card", "Version 01.02", | ||
| 691 | 0x4b8870ff, 0x70e946d1, 0x4b74baa0), | ||
| 692 | PCMCIA_DEVICE_PROD_ID3("HFA3863", 0x355cb092), | ||
| 693 | PCMCIA_DEVICE_PROD_ID3("ISL37100P", 0x630d52b2), | ||
| 694 | PCMCIA_DEVICE_PROD_ID3("ISL37101P-10", 0xdd97a26b), | ||
| 695 | PCMCIA_DEVICE_PROD_ID3("ISL37300P", 0xc9049a39), | ||
| 696 | PCMCIA_DEVICE_NULL | ||
| 697 | }; | ||
| 698 | MODULE_DEVICE_TABLE(pcmcia, hostap_cs_ids); | ||
| 699 | |||
| 700 | |||
| 701 | static struct pcmcia_driver hostap_driver = { | ||
| 702 | .name = "hostap_cs", | ||
| 703 | .probe = hostap_cs_probe, | ||
| 704 | .remove = prism2_detach, | ||
| 705 | .owner = THIS_MODULE, | ||
| 706 | .id_table = hostap_cs_ids, | ||
| 707 | .suspend = hostap_cs_suspend, | ||
| 708 | .resume = hostap_cs_resume, | ||
| 709 | }; | ||
| 710 | module_pcmcia_driver(hostap_driver); | ||
| @@ -1,810 +0,0 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | static int prism2_enable_aux_port(struct net_device *dev, int enable) | ||
| 3 | { | ||
| 4 | u16 val, reg; | ||
| 5 | int i, tries; | ||
| 6 | unsigned long flags; | ||
| 7 | struct hostap_interface *iface; | ||
| 8 | local_info_t *local; | ||
| 9 | |||
| 10 | iface = netdev_priv(dev); | ||
| 11 | local = iface->local; | ||
| 12 | |||
| 13 | if (local->no_pri) { | ||
| 14 | if (enable) { | ||
| 15 | PDEBUG(DEBUG_EXTRA2, "%s: no PRI f/w - assuming Aux " | ||
| 16 | "port is already enabled\n", dev->name); | ||
| 17 | } | ||
| 18 | return 0; | ||
| 19 | } | ||
| 20 | |||
| 21 | spin_lock_irqsave(&local->cmdlock, flags); | ||
| 22 | |||
| 23 | /* wait until busy bit is clear */ | ||
| 24 | tries = HFA384X_CMD_BUSY_TIMEOUT; | ||
| 25 | while (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY && tries > 0) { | ||
| 26 | tries--; | ||
| 27 | udelay(1); | ||
| 28 | } | ||
| 29 | if (tries == 0) { | ||
| 30 | reg = HFA384X_INW(HFA384X_CMD_OFF); | ||
| 31 | spin_unlock_irqrestore(&local->cmdlock, flags); | ||
| 32 | printk("%s: prism2_enable_aux_port - timeout - reg=0x%04x\n", | ||
| 33 | dev->name, reg); | ||
| 34 | return -ETIMEDOUT; | ||
| 35 | } | ||
| 36 | |||
| 37 | val = HFA384X_INW(HFA384X_CONTROL_OFF); | ||
| 38 | |||
| 39 | if (enable) { | ||
| 40 | HFA384X_OUTW(HFA384X_AUX_MAGIC0, HFA384X_PARAM0_OFF); | ||
| 41 | HFA384X_OUTW(HFA384X_AUX_MAGIC1, HFA384X_PARAM1_OFF); | ||
| 42 | HFA384X_OUTW(HFA384X_AUX_MAGIC2, HFA384X_PARAM2_OFF); | ||
| 43 | |||
| 44 | if ((val & HFA384X_AUX_PORT_MASK) != HFA384X_AUX_PORT_DISABLED) | ||
| 45 | printk("prism2_enable_aux_port: was not disabled!?\n"); | ||
| 46 | val &= ~HFA384X_AUX_PORT_MASK; | ||
| 47 | val |= HFA384X_AUX_PORT_ENABLE; | ||
| 48 | } else { | ||
| 49 | HFA384X_OUTW(0, HFA384X_PARAM0_OFF); | ||
| 50 | HFA384X_OUTW(0, HFA384X_PARAM1_OFF); | ||
| 51 | HFA384X_OUTW(0, HFA384X_PARAM2_OFF); | ||
| 52 | |||
| 53 | if ((val & HFA384X_AUX_PORT_MASK) != HFA384X_AUX_PORT_ENABLED) | ||
| 54 | printk("prism2_enable_aux_port: was not enabled!?\n"); | ||
| 55 | val &= ~HFA384X_AUX_PORT_MASK; | ||
| 56 | val |= HFA384X_AUX_PORT_DISABLE; | ||
| 57 | } | ||
| 58 | HFA384X_OUTW(val, HFA384X_CONTROL_OFF); | ||
| 59 | |||
| 60 | udelay(5); | ||
| 61 | |||
| 62 | i = 10000; | ||
| 63 | while (i > 0) { | ||
| 64 | val = HFA384X_INW(HFA384X_CONTROL_OFF); | ||
| 65 | val &= HFA384X_AUX_PORT_MASK; | ||
| 66 | |||
| 67 | if ((enable && val == HFA384X_AUX_PORT_ENABLED) || | ||
| 68 | (!enable && val == HFA384X_AUX_PORT_DISABLED)) | ||
| 69 | break; | ||
| 70 | |||
| 71 | udelay(10); | ||
| 72 | i--; | ||
| 73 | } | ||
| 74 | |||
| 75 | spin_unlock_irqrestore(&local->cmdlock, flags); | ||
| 76 | |||
| 77 | if (i == 0) { | ||
| 78 | printk("prism2_enable_aux_port(%d) timed out\n", | ||
| 79 | enable); | ||
| 80 | return -ETIMEDOUT; | ||
| 81 | } | ||
| 82 | |||
| 83 | return 0; | ||
| 84 | } | ||
| 85 | |||
| 86 | |||
| 87 | static int hfa384x_from_aux(struct net_device *dev, unsigned int addr, int len, | ||
| 88 | void *buf) | ||
| 89 | { | ||
| 90 | u16 page, offset; | ||
| 91 | if (addr & 1 || len & 1) | ||
| 92 | return -1; | ||
| 93 | |||
| 94 | page = addr >> 7; | ||
| 95 | offset = addr & 0x7f; | ||
| 96 | |||
| 97 | HFA384X_OUTW(page, HFA384X_AUXPAGE_OFF); | ||
| 98 | HFA384X_OUTW(offset, HFA384X_AUXOFFSET_OFF); | ||
| 99 | |||
| 100 | udelay(5); | ||
| 101 | |||
| 102 | #ifdef PRISM2_PCI | ||
| 103 | { | ||
| 104 | __le16 *pos = (__le16 *) buf; | ||
| 105 | while (len > 0) { | ||
| 106 | *pos++ = HFA384X_INW_DATA(HFA384X_AUXDATA_OFF); | ||
| 107 | len -= 2; | ||
| 108 | } | ||
| 109 | } | ||
| 110 | #else /* PRISM2_PCI */ | ||
| 111 | HFA384X_INSW(HFA384X_AUXDATA_OFF, buf, len / 2); | ||
| 112 | #endif /* PRISM2_PCI */ | ||
| 113 | |||
| 114 | return 0; | ||
| 115 | } | ||
| 116 | |||
| 117 | |||
| 118 | static int hfa384x_to_aux(struct net_device *dev, unsigned int addr, int len, | ||
| 119 | void *buf) | ||
| 120 | { | ||
| 121 | u16 page, offset; | ||
| 122 | if (addr & 1 || len & 1) | ||
| 123 | return -1; | ||
| 124 | |||
| 125 | page = addr >> 7; | ||
| 126 | offset = addr & 0x7f; | ||
| 127 | |||
| 128 | HFA384X_OUTW(page, HFA384X_AUXPAGE_OFF); | ||
| 129 | HFA384X_OUTW(offset, HFA384X_AUXOFFSET_OFF); | ||
| 130 | |||
| 131 | udelay(5); | ||
| 132 | |||
| 133 | #ifdef PRISM2_PCI | ||
| 134 | { | ||
| 135 | __le16 *pos = (__le16 *) buf; | ||
| 136 | while (len > 0) { | ||
| 137 | HFA384X_OUTW_DATA(*pos++, HFA384X_AUXDATA_OFF); | ||
| 138 | len -= 2; | ||
| 139 | } | ||
| 140 | } | ||
| 141 | #else /* PRISM2_PCI */ | ||
| 142 | HFA384X_OUTSW(HFA384X_AUXDATA_OFF, buf, len / 2); | ||
| 143 | #endif /* PRISM2_PCI */ | ||
| 144 | |||
| 145 | return 0; | ||
| 146 | } | ||
| 147 | |||
| 148 | |||
| 149 | static int prism2_pda_ok(u8 *buf) | ||
| 150 | { | ||
| 151 | __le16 *pda = (__le16 *) buf; | ||
| 152 | int pos; | ||
| 153 | u16 len, pdr; | ||
| 154 | |||
| 155 | if (buf[0] == 0xff && buf[1] == 0x00 && buf[2] == 0xff && | ||
| 156 | buf[3] == 0x00) | ||
| 157 | return 0; | ||
| 158 | |||
| 159 | pos = 0; | ||
| 160 | while (pos + 1 < PRISM2_PDA_SIZE / 2) { | ||
| 161 | len = le16_to_cpu(pda[pos]); | ||
| 162 | pdr = le16_to_cpu(pda[pos + 1]); | ||
| 163 | if (len == 0 || pos + len > PRISM2_PDA_SIZE / 2) | ||
| 164 | return 0; | ||
| 165 | |||
| 166 | if (pdr == 0x0000 && len == 2) { | ||
| 167 | /* PDA end found */ | ||
| 168 | return 1; | ||
| 169 | } | ||
| 170 | |||
| 171 | pos += len + 1; | ||
| 172 | } | ||
| 173 | |||
| 174 | return 0; | ||
| 175 | } | ||
| 176 | |||
| 177 | |||
| 178 | #define prism2_download_aux_dump_npages 65536 | ||
| 179 | |||
| 180 | struct prism2_download_aux_dump { | ||
| 181 | local_info_t *local; | ||
| 182 | u16 page[0x80]; | ||
| 183 | }; | ||
| 184 | |||
| 185 | static int prism2_download_aux_dump_proc_show(struct seq_file *m, void *v) | ||
| 186 | { | ||
| 187 | struct prism2_download_aux_dump *ctx = m->private; | ||
| 188 | |||
| 189 | hfa384x_from_aux(ctx->local->dev, (unsigned long)v - 1, 0x80, ctx->page); | ||
| 190 | seq_write(m, ctx->page, 0x80); | ||
| 191 | return 0; | ||
| 192 | } | ||
| 193 | |||
| 194 | static void *prism2_download_aux_dump_proc_start(struct seq_file *m, loff_t *_pos) | ||
| 195 | { | ||
| 196 | struct prism2_download_aux_dump *ctx = m->private; | ||
| 197 | prism2_enable_aux_port(ctx->local->dev, 1); | ||
| 198 | if (*_pos >= prism2_download_aux_dump_npages) | ||
| 199 | return NULL; | ||
| 200 | return (void *)((unsigned long)*_pos + 1); | ||
| 201 | } | ||
| 202 | |||
| 203 | static void *prism2_download_aux_dump_proc_next(struct seq_file *m, void *v, loff_t *_pos) | ||
| 204 | { | ||
| 205 | ++*_pos; | ||
| 206 | if (*_pos >= prism2_download_aux_dump_npages) | ||
| 207 | return NULL; | ||
| 208 | return (void *)((unsigned long)*_pos + 1); | ||
| 209 | } | ||
| 210 | |||
| 211 | static void prism2_download_aux_dump_proc_stop(struct seq_file *m, void *v) | ||
| 212 | { | ||
| 213 | struct prism2_download_aux_dump *ctx = m->private; | ||
| 214 | prism2_enable_aux_port(ctx->local->dev, 0); | ||
| 215 | } | ||
| 216 | |||
| 217 | static const struct seq_operations prism2_download_aux_dump_proc_seqops = { | ||
| 218 | .start = prism2_download_aux_dump_proc_start, | ||
| 219 | .next = prism2_download_aux_dump_proc_next, | ||
| 220 | .stop = prism2_download_aux_dump_proc_stop, | ||
| 221 | .show = prism2_download_aux_dump_proc_show, | ||
| 222 | }; | ||
| 223 | |||
| 224 | static int prism2_download_aux_dump_proc_open(struct inode *inode, struct file *file) | ||
| 225 | { | ||
| 226 | int ret = seq_open_private(file, &prism2_download_aux_dump_proc_seqops, | ||
| 227 | sizeof(struct prism2_download_aux_dump)); | ||
| 228 | if (ret == 0) { | ||
| 229 | struct seq_file *m = file->private_data; | ||
| 230 | m->private = pde_data(inode); | ||
| 231 | } | ||
| 232 | return ret; | ||
| 233 | } | ||
| 234 | |||
| 235 | static const struct proc_ops prism2_download_aux_dump_proc_ops = { | ||
| 236 | .proc_open = prism2_download_aux_dump_proc_open, | ||
| 237 | .proc_read = seq_read, | ||
| 238 | .proc_lseek = seq_lseek, | ||
| 239 | .proc_release = seq_release_private, | ||
| 240 | }; | ||
| 241 | |||
| 242 | |||
| 243 | static u8 * prism2_read_pda(struct net_device *dev) | ||
| 244 | { | ||
| 245 | u8 *buf; | ||
| 246 | int res, i, found = 0; | ||
| 247 | #define NUM_PDA_ADDRS 4 | ||
| 248 | unsigned int pda_addr[NUM_PDA_ADDRS] = { | ||
| 249 | 0x7f0000 /* others than HFA3841 */, | ||
| 250 | 0x3f0000 /* HFA3841 */, | ||
| 251 | 0x390000 /* apparently used in older cards */, | ||
| 252 | 0x7f0002 /* Intel PRO/Wireless 2011B (PCI) */, | ||
| 253 | }; | ||
| 254 | |||
| 255 | buf = kmalloc(PRISM2_PDA_SIZE, GFP_KERNEL); | ||
| 256 | if (buf == NULL) | ||
| 257 | return NULL; | ||
| 258 | |||
| 259 | /* Note: wlan card should be in initial state (just after init cmd) | ||
| 260 | * and no other operations should be performed concurrently. */ | ||
| 261 | |||
| 262 | prism2_enable_aux_port(dev, 1); | ||
| 263 | |||
| 264 | for (i = 0; i < NUM_PDA_ADDRS; i++) { | ||
| 265 | PDEBUG(DEBUG_EXTRA2, "%s: trying to read PDA from 0x%08x", | ||
| 266 | dev->name, pda_addr[i]); | ||
| 267 | res = hfa384x_from_aux(dev, pda_addr[i], PRISM2_PDA_SIZE, buf); | ||
| 268 | if (res) | ||
| 269 | continue; | ||
| 270 | if (res == 0 && prism2_pda_ok(buf)) { | ||
| 271 | PDEBUG2(DEBUG_EXTRA2, ": OK\n"); | ||
| 272 | found = 1; | ||
| 273 | break; | ||
| 274 | } else { | ||
| 275 | PDEBUG2(DEBUG_EXTRA2, ": failed\n"); | ||
| 276 | } | ||
| 277 | } | ||
| 278 | |||
| 279 | prism2_enable_aux_port(dev, 0); | ||
| 280 | |||
| 281 | if (!found) { | ||
| 282 | printk(KERN_DEBUG "%s: valid PDA not found\n", dev->name); | ||
| 283 | kfree(buf); | ||
| 284 | buf = NULL; | ||
| 285 | } | ||
| 286 | |||
| 287 | return buf; | ||
| 288 | } | ||
| 289 | |||
| 290 | |||
| 291 | static int prism2_download_volatile(local_info_t *local, | ||
| 292 | struct prism2_download_data *param) | ||
| 293 | { | ||
| 294 | struct net_device *dev = local->dev; | ||
| 295 | int ret = 0, i; | ||
| 296 | u16 param0, param1; | ||
| 297 | |||
| 298 | if (local->hw_downloading) { | ||
| 299 | printk(KERN_WARNING "%s: Already downloading - aborting new " | ||
| 300 | "request\n", dev->name); | ||
| 301 | return -1; | ||
| 302 | } | ||
| 303 | |||
| 304 | local->hw_downloading = 1; | ||
| 305 | if (local->pri_only) { | ||
| 306 | hfa384x_disable_interrupts(dev); | ||
| 307 | } else { | ||
| 308 | prism2_hw_shutdown(dev, 0); | ||
| 309 | |||
| 310 | if (prism2_hw_init(dev, 0)) { | ||
| 311 | printk(KERN_WARNING "%s: Could not initialize card for" | ||
| 312 | " download\n", dev->name); | ||
| 313 | ret = -1; | ||
| 314 | goto out; | ||
| 315 | } | ||
| 316 | } | ||
| 317 | |||
| 318 | if (prism2_enable_aux_port(dev, 1)) { | ||
| 319 | printk(KERN_WARNING "%s: Could not enable AUX port\n", | ||
| 320 | dev->name); | ||
| 321 | ret = -1; | ||
| 322 | goto out; | ||
| 323 | } | ||
| 324 | |||
| 325 | param0 = param->start_addr & 0xffff; | ||
| 326 | param1 = param->start_addr >> 16; | ||
| 327 | |||
| 328 | HFA384X_OUTW(0, HFA384X_PARAM2_OFF); | ||
| 329 | HFA384X_OUTW(param1, HFA384X_PARAM1_OFF); | ||
| 330 | if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD | | ||
| 331 | (HFA384X_PROGMODE_ENABLE_VOLATILE << 8), | ||
| 332 | param0)) { | ||
| 333 | printk(KERN_WARNING "%s: Download command execution failed\n", | ||
| 334 | dev->name); | ||
| 335 | ret = -1; | ||
| 336 | goto out; | ||
| 337 | } | ||
| 338 | |||
| 339 | for (i = 0; i < param->num_areas; i++) { | ||
| 340 | PDEBUG(DEBUG_EXTRA2, "%s: Writing %d bytes at 0x%08x\n", | ||
| 341 | dev->name, param->data[i].len, param->data[i].addr); | ||
| 342 | if (hfa384x_to_aux(dev, param->data[i].addr, | ||
| 343 | param->data[i].len, param->data[i].data)) { | ||
| 344 | printk(KERN_WARNING "%s: RAM download at 0x%08x " | ||
| 345 | "(len=%d) failed\n", dev->name, | ||
| 346 | param->data[i].addr, param->data[i].len); | ||
| 347 | ret = -1; | ||
| 348 | goto out; | ||
| 349 | } | ||
| 350 | } | ||
| 351 | |||
| 352 | HFA384X_OUTW(param1, HFA384X_PARAM1_OFF); | ||
| 353 | HFA384X_OUTW(0, HFA384X_PARAM2_OFF); | ||
| 354 | if (hfa384x_cmd_no_wait(dev, HFA384X_CMDCODE_DOWNLOAD | | ||
| 355 | (HFA384X_PROGMODE_DISABLE << 8), param0)) { | ||
| 356 | printk(KERN_WARNING "%s: Download command execution failed\n", | ||
| 357 | dev->name); | ||
| 358 | ret = -1; | ||
| 359 | goto out; | ||
| 360 | } | ||
| 361 | /* ProgMode disable causes the hardware to restart itself from the | ||
| 362 | * given starting address. Give hw some time and ACK command just in | ||
| 363 | * case restart did not happen. */ | ||
| 364 | mdelay(5); | ||
| 365 | HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF); | ||
| 366 | |||
| 367 | if (prism2_enable_aux_port(dev, 0)) { | ||
| 368 | printk(KERN_DEBUG "%s: Disabling AUX port failed\n", | ||
| 369 | dev->name); | ||
| 370 | /* continue anyway.. restart should have taken care of this */ | ||
| 371 | } | ||
| 372 | |||
| 373 | mdelay(5); | ||
| 374 | local->hw_downloading = 0; | ||
| 375 | if (prism2_hw_config(dev, 2)) { | ||
| 376 | printk(KERN_WARNING "%s: Card configuration after RAM " | ||
| 377 | "download failed\n", dev->name); | ||
| 378 | ret = -1; | ||
| 379 | goto out; | ||
| 380 | } | ||
| 381 | |||
| 382 | out: | ||
| 383 | local->hw_downloading = 0; | ||
| 384 | return ret; | ||
| 385 | } | ||
| 386 | |||
| 387 | |||
| 388 | static int prism2_enable_genesis(local_info_t *local, int hcr) | ||
| 389 | { | ||
| 390 | struct net_device *dev = local->dev; | ||
| 391 | u8 initseq[4] = { 0x00, 0xe1, 0xa1, 0xff }; | ||
| 392 | u8 readbuf[4]; | ||
| 393 | |||
| 394 | printk(KERN_DEBUG "%s: test Genesis mode with HCR 0x%02x\n", | ||
| 395 | dev->name, hcr); | ||
| 396 | local->func->cor_sreset(local); | ||
| 397 | hfa384x_to_aux(dev, 0x7e0038, sizeof(initseq), initseq); | ||
| 398 | local->func->genesis_reset(local, hcr); | ||
| 399 | |||
| 400 | /* Readback test */ | ||
| 401 | hfa384x_from_aux(dev, 0x7e0038, sizeof(readbuf), readbuf); | ||
| 402 | hfa384x_to_aux(dev, 0x7e0038, sizeof(initseq), initseq); | ||
| 403 | hfa384x_from_aux(dev, 0x7e0038, sizeof(readbuf), readbuf); | ||
| 404 | |||
| 405 | if (memcmp(initseq, readbuf, sizeof(initseq)) == 0) { | ||
| 406 | printk(KERN_DEBUG "Readback test succeeded, HCR 0x%02x\n", | ||
| 407 | hcr); | ||
| 408 | return 0; | ||
| 409 | } else { | ||
| 410 | printk(KERN_DEBUG "Readback test failed, HCR 0x%02x write %4ph read %4ph\n", | ||
| 411 | hcr, initseq, readbuf); | ||
| 412 | return 1; | ||
| 413 | } | ||
| 414 | } | ||
| 415 | |||
| 416 | |||
| 417 | static int prism2_get_ram_size(local_info_t *local) | ||
| 418 | { | ||
| 419 | int ret; | ||
| 420 | |||
| 421 | /* Try to enable genesis mode; 0x1F for x8 SRAM or 0x0F for x16 SRAM */ | ||
| 422 | if (prism2_enable_genesis(local, 0x1f) == 0) | ||
| 423 | ret = 8; | ||
| 424 | else if (prism2_enable_genesis(local, 0x0f) == 0) | ||
| 425 | ret = 16; | ||
| 426 | else | ||
| 427 | ret = -1; | ||
| 428 | |||
| 429 | /* Disable genesis mode */ | ||
| 430 | local->func->genesis_reset(local, ret == 16 ? 0x07 : 0x17); | ||
| 431 | |||
| 432 | return ret; | ||
| 433 | } | ||
| 434 | |||
| 435 | |||
| 436 | static int prism2_download_genesis(local_info_t *local, | ||
| 437 | struct prism2_download_data *param) | ||
| 438 | { | ||
| 439 | struct net_device *dev = local->dev; | ||
| 440 | int ram16 = 0, i; | ||
| 441 | int ret = 0; | ||
| 442 | |||
| 443 | if (local->hw_downloading) { | ||
| 444 | printk(KERN_WARNING "%s: Already downloading - aborting new " | ||
| 445 | "request\n", dev->name); | ||
| 446 | return -EBUSY; | ||
| 447 | } | ||
| 448 | |||
| 449 | if (!local->func->genesis_reset || !local->func->cor_sreset) { | ||
| 450 | printk(KERN_INFO "%s: Genesis mode downloading not supported " | ||
| 451 | "with this hwmodel\n", dev->name); | ||
| 452 | return -EOPNOTSUPP; | ||
| 453 | } | ||
| 454 | |||
| 455 | local->hw_downloading = 1; | ||
| 456 | |||
| 457 | if (prism2_enable_aux_port(dev, 1)) { | ||
| 458 | printk(KERN_DEBUG "%s: failed to enable AUX port\n", | ||
| 459 | dev->name); | ||
| 460 | ret = -EIO; | ||
| 461 | goto out; | ||
| 462 | } | ||
| 463 | |||
| 464 | if (local->sram_type == -1) { | ||
| 465 | /* 0x1F for x8 SRAM or 0x0F for x16 SRAM */ | ||
| 466 | if (prism2_enable_genesis(local, 0x1f) == 0) { | ||
| 467 | ram16 = 0; | ||
| 468 | PDEBUG(DEBUG_EXTRA2, "%s: Genesis mode OK using x8 " | ||
| 469 | "SRAM\n", dev->name); | ||
| 470 | } else if (prism2_enable_genesis(local, 0x0f) == 0) { | ||
| 471 | ram16 = 1; | ||
| 472 | PDEBUG(DEBUG_EXTRA2, "%s: Genesis mode OK using x16 " | ||
| 473 | "SRAM\n", dev->name); | ||
| 474 | } else { | ||
| 475 | printk(KERN_DEBUG "%s: Could not initiate genesis " | ||
| 476 | "mode\n", dev->name); | ||
| 477 | ret = -EIO; | ||
| 478 | goto out; | ||
| 479 | } | ||
| 480 | } else { | ||
| 481 | if (prism2_enable_genesis(local, local->sram_type == 8 ? | ||
| 482 | 0x1f : 0x0f)) { | ||
| 483 | printk(KERN_DEBUG "%s: Failed to set Genesis " | ||
| 484 | "mode (sram_type=%d)\n", dev->name, | ||
| 485 | local->sram_type); | ||
| 486 | ret = -EIO; | ||
| 487 | goto out; | ||
| 488 | } | ||
| 489 | ram16 = local->sram_type != 8; | ||
| 490 | } | ||
| 491 | |||
| 492 | for (i = 0; i < param->num_areas; i++) { | ||
| 493 | PDEBUG(DEBUG_EXTRA2, "%s: Writing %d bytes at 0x%08x\n", | ||
| 494 | dev->name, param->data[i].len, param->data[i].addr); | ||
| 495 | if (hfa384x_to_aux(dev, param->data[i].addr, | ||
| 496 | param->data[i].len, param->data[i].data)) { | ||
| 497 | printk(KERN_WARNING "%s: RAM download at 0x%08x " | ||
| 498 | "(len=%d) failed\n", dev->name, | ||
| 499 | param->data[i].addr, param->data[i].len); | ||
| 500 | ret = -EIO; | ||
| 501 | goto out; | ||
| 502 | } | ||
| 503 | } | ||
| 504 | |||
| 505 | PDEBUG(DEBUG_EXTRA2, "Disable genesis mode\n"); | ||
| 506 | local->func->genesis_reset(local, ram16 ? 0x07 : 0x17); | ||
| 507 | if (prism2_enable_aux_port(dev, 0)) { | ||
| 508 | printk(KERN_DEBUG "%s: Failed to disable AUX port\n", | ||
| 509 | dev->name); | ||
| 510 | } | ||
| 511 | |||
| 512 | mdelay(5); | ||
| 513 | local->hw_downloading = 0; | ||
| 514 | |||
| 515 | PDEBUG(DEBUG_EXTRA2, "Trying to initialize card\n"); | ||
| 516 | /* | ||
| 517 | * Make sure the INIT command does not generate a command completion | ||
| 518 | * event by disabling interrupts. | ||
| 519 | */ | ||
| 520 | hfa384x_disable_interrupts(dev); | ||
| 521 | if (prism2_hw_init(dev, 1)) { | ||
| 522 | printk(KERN_DEBUG "%s: Initialization after genesis mode " | ||
| 523 | "download failed\n", dev->name); | ||
| 524 | ret = -EIO; | ||
| 525 | goto out; | ||
| 526 | } | ||
| 527 | |||
| 528 | PDEBUG(DEBUG_EXTRA2, "Card initialized - running PRI only\n"); | ||
| 529 | if (prism2_hw_init2(dev, 1)) { | ||
| 530 | printk(KERN_DEBUG "%s: Initialization(2) after genesis mode " | ||
| 531 | "download failed\n", dev->name); | ||
| 532 | ret = -EIO; | ||
| 533 | goto out; | ||
| 534 | } | ||
| 535 | |||
| 536 | out: | ||
| 537 | local->hw_downloading = 0; | ||
| 538 | return ret; | ||
| 539 | } | ||
| 540 | |||
| 541 | |||
| 542 | #ifdef PRISM2_NON_VOLATILE_DOWNLOAD | ||
| 543 | /* Note! Non-volatile downloading functionality has not yet been tested | ||
| 544 | * thoroughly and it may corrupt flash image and effectively kill the card that | ||
| 545 | * is being updated. You have been warned. */ | ||
| 546 | |||
| 547 | static inline int prism2_download_block(struct net_device *dev, | ||
| 548 | u32 addr, u8 *data, | ||
| 549 | u32 bufaddr, int rest_len) | ||
| 550 | { | ||
| 551 | u16 param0, param1; | ||
| 552 | int block_len; | ||
| 553 | |||
| 554 | block_len = rest_len < 4096 ? rest_len : 4096; | ||
| 555 | |||
| 556 | param0 = addr & 0xffff; | ||
| 557 | param1 = addr >> 16; | ||
| 558 | |||
| 559 | HFA384X_OUTW(block_len, HFA384X_PARAM2_OFF); | ||
| 560 | HFA384X_OUTW(param1, HFA384X_PARAM1_OFF); | ||
| 561 | |||
| 562 | if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD | | ||
| 563 | (HFA384X_PROGMODE_ENABLE_NON_VOLATILE << 8), | ||
| 564 | param0)) { | ||
| 565 | printk(KERN_WARNING "%s: Flash download command execution " | ||
| 566 | "failed\n", dev->name); | ||
| 567 | return -1; | ||
| 568 | } | ||
| 569 | |||
| 570 | if (hfa384x_to_aux(dev, bufaddr, block_len, data)) { | ||
| 571 | printk(KERN_WARNING "%s: flash download at 0x%08x " | ||
| 572 | "(len=%d) failed\n", dev->name, addr, block_len); | ||
| 573 | return -1; | ||
| 574 | } | ||
| 575 | |||
| 576 | HFA384X_OUTW(0, HFA384X_PARAM2_OFF); | ||
| 577 | HFA384X_OUTW(0, HFA384X_PARAM1_OFF); | ||
| 578 | if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD | | ||
| 579 | (HFA384X_PROGMODE_PROGRAM_NON_VOLATILE << 8), | ||
| 580 | 0)) { | ||
| 581 | printk(KERN_WARNING "%s: Flash write command execution " | ||
| 582 | "failed\n", dev->name); | ||
| 583 | return -1; | ||
| 584 | } | ||
| 585 | |||
| 586 | return block_len; | ||
| 587 | } | ||
| 588 | |||
| 589 | |||
| 590 | static int prism2_download_nonvolatile(local_info_t *local, | ||
| 591 | struct prism2_download_data *dl) | ||
| 592 | { | ||
| 593 | struct net_device *dev = local->dev; | ||
| 594 | int ret = 0, i; | ||
| 595 | struct { | ||
| 596 | __le16 page; | ||
| 597 | __le16 offset; | ||
| 598 | __le16 len; | ||
| 599 | } dlbuffer; | ||
| 600 | u32 bufaddr; | ||
| 601 | |||
| 602 | if (local->hw_downloading) { | ||
| 603 | printk(KERN_WARNING "%s: Already downloading - aborting new " | ||
| 604 | "request\n", dev->name); | ||
| 605 | return -1; | ||
| 606 | } | ||
| 607 | |||
| 608 | ret = local->func->get_rid(dev, HFA384X_RID_DOWNLOADBUFFER, | ||
| 609 | &dlbuffer, 6, 0); | ||
| 610 | |||
| 611 | if (ret < 0) { | ||
| 612 | printk(KERN_WARNING "%s: Could not read download buffer " | ||
| 613 | "parameters\n", dev->name); | ||
| 614 | goto out; | ||
| 615 | } | ||
| 616 | |||
| 617 | printk(KERN_DEBUG "Download buffer: %d bytes at 0x%04x:0x%04x\n", | ||
| 618 | le16_to_cpu(dlbuffer.len), | ||
| 619 | le16_to_cpu(dlbuffer.page), | ||
| 620 | le16_to_cpu(dlbuffer.offset)); | ||
| 621 | |||
| 622 | bufaddr = (le16_to_cpu(dlbuffer.page) << 7) + le16_to_cpu(dlbuffer.offset); | ||
| 623 | |||
| 624 | local->hw_downloading = 1; | ||
| 625 | |||
| 626 | if (!local->pri_only) { | ||
| 627 | prism2_hw_shutdown(dev, 0); | ||
| 628 | |||
| 629 | if (prism2_hw_init(dev, 0)) { | ||
| 630 | printk(KERN_WARNING "%s: Could not initialize card for" | ||
| 631 | " download\n", dev->name); | ||
| 632 | ret = -1; | ||
| 633 | goto out; | ||
| 634 | } | ||
| 635 | } | ||
| 636 | |||
| 637 | hfa384x_disable_interrupts(dev); | ||
| 638 | |||
| 639 | if (prism2_enable_aux_port(dev, 1)) { | ||
| 640 | printk(KERN_WARNING "%s: Could not enable AUX port\n", | ||
| 641 | dev->name); | ||
| 642 | ret = -1; | ||
| 643 | goto out; | ||
| 644 | } | ||
| 645 | |||
| 646 | printk(KERN_DEBUG "%s: starting flash download\n", dev->name); | ||
| 647 | for (i = 0; i < dl->num_areas; i++) { | ||
| 648 | int rest_len = dl->data[i].len; | ||
| 649 | int data_off = 0; | ||
| 650 | |||
| 651 | while (rest_len > 0) { | ||
| 652 | int block_len; | ||
| 653 | |||
| 654 | block_len = prism2_download_block( | ||
| 655 | dev, dl->data[i].addr + data_off, | ||
| 656 | dl->data[i].data + data_off, bufaddr, | ||
| 657 | rest_len); | ||
| 658 | |||
| 659 | if (block_len < 0) { | ||
| 660 | ret = -1; | ||
| 661 | goto out; | ||
| 662 | } | ||
| 663 | |||
| 664 | rest_len -= block_len; | ||
| 665 | data_off += block_len; | ||
| 666 | } | ||
| 667 | } | ||
| 668 | |||
| 669 | HFA384X_OUTW(0, HFA384X_PARAM1_OFF); | ||
| 670 | HFA384X_OUTW(0, HFA384X_PARAM2_OFF); | ||
| 671 | if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD | | ||
| 672 | (HFA384X_PROGMODE_DISABLE << 8), 0)) { | ||
| 673 | printk(KERN_WARNING "%s: Download command execution failed\n", | ||
| 674 | dev->name); | ||
| 675 | ret = -1; | ||
| 676 | goto out; | ||
| 677 | } | ||
| 678 | |||
| 679 | if (prism2_enable_aux_port(dev, 0)) { | ||
| 680 | printk(KERN_DEBUG "%s: Disabling AUX port failed\n", | ||
| 681 | dev->name); | ||
| 682 | /* continue anyway.. restart should have taken care of this */ | ||
| 683 | } | ||
| 684 | |||
| 685 | mdelay(5); | ||
| 686 | |||
| 687 | local->func->hw_reset(dev); | ||
| 688 | local->hw_downloading = 0; | ||
| 689 | if (prism2_hw_config(dev, 2)) { | ||
| 690 | printk(KERN_WARNING "%s: Card configuration after flash " | ||
| 691 | "download failed\n", dev->name); | ||
| 692 | ret = -1; | ||
| 693 | } else { | ||
| 694 | printk(KERN_INFO "%s: Card initialized successfully after " | ||
| 695 | "flash download\n", dev->name); | ||
| 696 | } | ||
| 697 | |||
| 698 | out: | ||
| 699 | local->hw_downloading = 0; | ||
| 700 | return ret; | ||
| 701 | } | ||
| 702 | #endif /* PRISM2_NON_VOLATILE_DOWNLOAD */ | ||
| 703 | |||
| 704 | |||
| 705 | static void prism2_download_free_data(struct prism2_download_data *dl) | ||
| 706 | { | ||
| 707 | int i; | ||
| 708 | |||
| 709 | if (dl == NULL) | ||
| 710 | return; | ||
| 711 | |||
| 712 | for (i = 0; i < dl->num_areas; i++) | ||
| 713 | kfree(dl->data[i].data); | ||
| 714 | kfree(dl); | ||
| 715 | } | ||
| 716 | |||
| 717 | |||
| 718 | static int prism2_download(local_info_t *local, | ||
| 719 | struct prism2_download_param *param) | ||
| 720 | { | ||
| 721 | int ret = 0; | ||
| 722 | int i; | ||
| 723 | u32 total_len = 0; | ||
| 724 | struct prism2_download_data *dl = NULL; | ||
| 725 | |||
| 726 | printk(KERN_DEBUG "prism2_download: dl_cmd=%d start_addr=0x%08x " | ||
| 727 | "num_areas=%d\n", | ||
| 728 | param->dl_cmd, param->start_addr, param->num_areas); | ||
| 729 | |||
| 730 | if (param->num_areas > 100) { | ||
| 731 | ret = -EINVAL; | ||
| 732 | goto out; | ||
| 733 | } | ||
| 734 | |||
| 735 | dl = kzalloc(struct_size(dl, data, param->num_areas), GFP_KERNEL); | ||
| 736 | if (dl == NULL) { | ||
| 737 | ret = -ENOMEM; | ||
| 738 | goto out; | ||
| 739 | } | ||
| 740 | dl->dl_cmd = param->dl_cmd; | ||
| 741 | dl->start_addr = param->start_addr; | ||
| 742 | dl->num_areas = param->num_areas; | ||
| 743 | for (i = 0; i < param->num_areas; i++) { | ||
| 744 | PDEBUG(DEBUG_EXTRA2, | ||
| 745 | " area %d: addr=0x%08x len=%d ptr=0x%p\n", | ||
| 746 | i, param->data[i].addr, param->data[i].len, | ||
| 747 | param->data[i].ptr); | ||
| 748 | |||
| 749 | dl->data[i].addr = param->data[i].addr; | ||
| 750 | dl->data[i].len = param->data[i].len; | ||
| 751 | |||
| 752 | total_len += param->data[i].len; | ||
| 753 | if (param->data[i].len > PRISM2_MAX_DOWNLOAD_AREA_LEN || | ||
| 754 | total_len > PRISM2_MAX_DOWNLOAD_LEN) { | ||
| 755 | ret = -E2BIG; | ||
| 756 | goto out; | ||
| 757 | } | ||
| 758 | |||
| 759 | dl->data[i].data = kmalloc(dl->data[i].len, GFP_KERNEL); | ||
| 760 | if (dl->data[i].data == NULL) { | ||
| 761 | ret = -ENOMEM; | ||
| 762 | goto out; | ||
| 763 | } | ||
| 764 | |||
| 765 | if (copy_from_user(dl->data[i].data, param->data[i].ptr, | ||
| 766 | param->data[i].len)) { | ||
| 767 | ret = -EFAULT; | ||
| 768 | goto out; | ||
| 769 | } | ||
| 770 | } | ||
| 771 | |||
| 772 | switch (param->dl_cmd) { | ||
| 773 | case PRISM2_DOWNLOAD_VOLATILE: | ||
| 774 | case PRISM2_DOWNLOAD_VOLATILE_PERSISTENT: | ||
| 775 | ret = prism2_download_volatile(local, dl); | ||
| 776 | break; | ||
| 777 | case PRISM2_DOWNLOAD_VOLATILE_GENESIS: | ||
| 778 | case PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT: | ||
| 779 | ret = prism2_download_genesis(local, dl); | ||
| 780 | break; | ||
| 781 | case PRISM2_DOWNLOAD_NON_VOLATILE: | ||
| 782 | #ifdef PRISM2_NON_VOLATILE_DOWNLOAD | ||
| 783 | ret = prism2_download_nonvolatile(local, dl); | ||
| 784 | #else /* PRISM2_NON_VOLATILE_DOWNLOAD */ | ||
| 785 | printk(KERN_INFO "%s: non-volatile downloading not enabled\n", | ||
| 786 | local->dev->name); | ||
| 787 | ret = -EOPNOTSUPP; | ||
| 788 | #endif /* PRISM2_NON_VOLATILE_DOWNLOAD */ | ||
| 789 | break; | ||
| 790 | default: | ||
| 791 | printk(KERN_DEBUG "%s: unsupported download command %d\n", | ||
| 792 | local->dev->name, param->dl_cmd); | ||
| 793 | ret = -EINVAL; | ||
| 794 | break; | ||
| 795 | } | ||
| 796 | |||
| 797 | out: | ||
| 798 | if (ret == 0 && dl && | ||
| 799 | param->dl_cmd == PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT) { | ||
| 800 | prism2_download_free_data(local->dl_pri); | ||
| 801 | local->dl_pri = dl; | ||
| 802 | } else if (ret == 0 && dl && | ||
| 803 | param->dl_cmd == PRISM2_DOWNLOAD_VOLATILE_PERSISTENT) { | ||
| 804 | prism2_download_free_data(local->dl_sec); | ||
| 805 | local->dl_sec = dl; | ||
| 806 | } else | ||
| 807 | prism2_download_free_data(dl); | ||
| 808 | |||
| 809 | return ret; | ||
| 810 | } | ||
| @@ -1,3387 +0,0 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0-only | ||
| 2 | /* | ||
| 3 | * Host AP (software wireless LAN access point) driver for | ||
| 4 | * Intersil Prism2/2.5/3. | ||
| 5 | * | ||
| 6 | * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen | ||
| 7 | * <j@w1.fi> | ||
| 8 | * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi> | ||
| 9 | * | ||
| 10 | * FIX: | ||
| 11 | * - there is currently no way of associating TX packets to correct wds device | ||
| 12 | * when TX Exc/OK event occurs, so all tx_packets and some | ||
| 13 | * tx_errors/tx_dropped are added to the main netdevice; using sw_support | ||
| 14 | * field in txdesc might be used to fix this (using Alloc event to increment | ||
| 15 | * tx_packets would need some further info in txfid table) | ||
| 16 | * | ||
| 17 | * Buffer Access Path (BAP) usage: | ||
| 18 | * Prism2 cards have two separate BAPs for accessing the card memory. These | ||
| 19 | * should allow concurrent access to two different frames and the driver | ||
| 20 | * previously used BAP0 for sending data and BAP1 for receiving data. | ||
| 21 | * However, there seems to be number of issues with concurrent access and at | ||
| 22 | * least one know hardware bug in using BAP0 and BAP1 concurrently with PCI | ||
| 23 | * Prism2.5. Therefore, the driver now only uses BAP0 for moving data between | ||
| 24 | * host and card memories. BAP0 accesses are protected with local->baplock | ||
| 25 | * (spin_lock_bh) to prevent concurrent use. | ||
| 26 | */ | ||
| 27 | |||
| 28 | |||
| 29 | |||
| 30 | #include <asm/delay.h> | ||
| 31 | #include <linux/uaccess.h> | ||
| 32 | |||
| 33 | #include <linux/slab.h> | ||
| 34 | #include <linux/netdevice.h> | ||
| 35 | #include <linux/etherdevice.h> | ||
| 36 | #include <linux/proc_fs.h> | ||
| 37 | #include <linux/seq_file.h> | ||
| 38 | #include <linux/if_arp.h> | ||
| 39 | #include <linux/delay.h> | ||
| 40 | #include <linux/random.h> | ||
| 41 | #include <linux/wait.h> | ||
| 42 | #include <linux/sched/signal.h> | ||
| 43 | #include <linux/rtnetlink.h> | ||
| 44 | #include <linux/wireless.h> | ||
| 45 | #include <net/iw_handler.h> | ||
| 46 | #include <net/lib80211.h> | ||
| 47 | #include <asm/irq.h> | ||
| 48 | |||
| 49 | #include "hostap_80211.h" | ||
| 50 | #include "hostap.h" | ||
| 51 | #include "hostap_ap.h" | ||
| 52 | |||
| 53 | |||
| 54 | /* #define final_version */ | ||
| 55 | |||
| 56 | static int mtu = 1500; | ||
| 57 | module_param(mtu, int, 0444); | ||
| 58 | MODULE_PARM_DESC(mtu, "Maximum transfer unit"); | ||
| 59 | |||
| 60 | static int channel[MAX_PARM_DEVICES] = { 3, DEF_INTS }; | ||
| 61 | module_param_array(channel, int, NULL, 0444); | ||
| 62 | MODULE_PARM_DESC(channel, "Initial channel"); | ||
| 63 | |||
| 64 | static char essid[33] = "test"; | ||
| 65 | module_param_string(essid, essid, sizeof(essid), 0444); | ||
| 66 | MODULE_PARM_DESC(essid, "Host AP's ESSID"); | ||
| 67 | |||
| 68 | static int iw_mode[MAX_PARM_DEVICES] = { IW_MODE_MASTER, DEF_INTS }; | ||
| 69 | module_param_array(iw_mode, int, NULL, 0444); | ||
| 70 | MODULE_PARM_DESC(iw_mode, "Initial operation mode"); | ||
| 71 | |||
| 72 | static int beacon_int[MAX_PARM_DEVICES] = { 100, DEF_INTS }; | ||
| 73 | module_param_array(beacon_int, int, NULL, 0444); | ||
| 74 | MODULE_PARM_DESC(beacon_int, "Beacon interval (1 = 1024 usec)"); | ||
| 75 | |||
| 76 | static int dtim_period[MAX_PARM_DEVICES] = { 1, DEF_INTS }; | ||
| 77 | module_param_array(dtim_period, int, NULL, 0444); | ||
| 78 | MODULE_PARM_DESC(dtim_period, "DTIM period"); | ||
| 79 | |||
| 80 | static char dev_template[16] = "wlan%d"; | ||
| 81 | module_param_string(dev_template, dev_template, sizeof(dev_template), 0444); | ||
| 82 | MODULE_PARM_DESC(dev_template, "Prefix for network device name (default: " | ||
| 83 | "wlan%d)"); | ||
| 84 | |||
| 85 | #ifdef final_version | ||
| 86 | #define EXTRA_EVENTS_WTERR 0 | ||
| 87 | #else | ||
| 88 | /* check WTERR events (Wait Time-out) in development versions */ | ||
| 89 | #define EXTRA_EVENTS_WTERR HFA384X_EV_WTERR | ||
| 90 | #endif | ||
| 91 | |||
| 92 | /* Events that will be using BAP0 */ | ||
| 93 | #define HFA384X_BAP0_EVENTS \ | ||
| 94 | (HFA384X_EV_TXEXC | HFA384X_EV_RX | HFA384X_EV_INFO | HFA384X_EV_TX) | ||
| 95 | |||
| 96 | /* event mask, i.e., events that will result in an interrupt */ | ||
| 97 | #define HFA384X_EVENT_MASK \ | ||
| 98 | (HFA384X_BAP0_EVENTS | HFA384X_EV_ALLOC | HFA384X_EV_INFDROP | \ | ||
| 99 | HFA384X_EV_CMD | HFA384X_EV_TICK | \ | ||
| 100 | EXTRA_EVENTS_WTERR) | ||
| 101 | |||
| 102 | /* Default TX control flags: use 802.11 headers and request interrupt for | ||
| 103 | * failed transmits. Frames that request ACK callback, will add | ||
| 104 | * _TX_OK flag and _ALT_RTRY flag may be used to select different retry policy. | ||
| 105 | */ | ||
| 106 | #define HFA384X_TX_CTRL_FLAGS \ | ||
| 107 | (HFA384X_TX_CTRL_802_11 | HFA384X_TX_CTRL_TX_EX) | ||
| 108 | |||
| 109 | |||
| 110 | /* ca. 1 usec */ | ||
| 111 | #define HFA384X_CMD_BUSY_TIMEOUT 5000 | ||
| 112 | #define HFA384X_BAP_BUSY_TIMEOUT 50000 | ||
| 113 | |||
| 114 | /* ca. 10 usec */ | ||
| 115 | #define HFA384X_CMD_COMPL_TIMEOUT 20000 | ||
| 116 | #define HFA384X_DL_COMPL_TIMEOUT 1000000 | ||
| 117 | |||
| 118 | /* Wait times for initialization; yield to other processes to avoid busy | ||
| 119 | * waiting for long time. */ | ||
| 120 | #define HFA384X_INIT_TIMEOUT (HZ / 2) /* 500 ms */ | ||
| 121 | #define HFA384X_ALLOC_COMPL_TIMEOUT (HZ / 20) /* 50 ms */ | ||
| 122 | |||
| 123 | |||
| 124 | static void prism2_hw_reset(struct net_device *dev); | ||
| 125 | static void prism2_check_sta_fw_version(local_info_t *local); | ||
| 126 | |||
| 127 | #ifdef PRISM2_DOWNLOAD_SUPPORT | ||
| 128 | /* hostap_download.c */ | ||
| 129 | static const struct proc_ops prism2_download_aux_dump_proc_ops; | ||
| 130 | static u8 * prism2_read_pda(struct net_device *dev); | ||
| 131 | static int prism2_download(local_info_t *local, | ||
| 132 | struct prism2_download_param *param); | ||
| 133 | static void prism2_download_free_data(struct prism2_download_data *dl); | ||
| 134 | static int prism2_download_volatile(local_info_t *local, | ||
| 135 | struct prism2_download_data *param); | ||
| 136 | static int prism2_download_genesis(local_info_t *local, | ||
| 137 | struct prism2_download_data *param); | ||
| 138 | static int prism2_get_ram_size(local_info_t *local); | ||
| 139 | #endif /* PRISM2_DOWNLOAD_SUPPORT */ | ||
| 140 | |||
| 141 | |||
| 142 | |||
| 143 | |||
| 144 | #ifndef final_version | ||
| 145 | /* magic value written to SWSUPPORT0 reg. for detecting whether card is still | ||
| 146 | * present */ | ||
| 147 | #define HFA384X_MAGIC 0x8A32 | ||
| 148 | #endif | ||
| 149 | |||
| 150 | static void hfa384x_read_regs(struct net_device *dev, | ||
| 151 | struct hfa384x_regs *regs) | ||
| 152 | { | ||
| 153 | regs->cmd = HFA384X_INW(HFA384X_CMD_OFF); | ||
| 154 | regs->evstat = HFA384X_INW(HFA384X_EVSTAT_OFF); | ||
| 155 | regs->offset0 = HFA384X_INW(HFA384X_OFFSET0_OFF); | ||
| 156 | regs->offset1 = HFA384X_INW(HFA384X_OFFSET1_OFF); | ||
| 157 | regs->swsupport0 = HFA384X_INW(HFA384X_SWSUPPORT0_OFF); | ||
| 158 | } | ||
| 159 | |||
| 160 | |||
| 161 | /** | ||
| 162 | * __hostap_cmd_queue_free - Free Prism2 command queue entry (private) | ||
| 163 | * @local: pointer to private Host AP driver data | ||
| 164 | * @entry: Prism2 command queue entry to be freed | ||
| 165 | * @del_req: request the entry to be removed | ||
| 166 | * | ||
| 167 | * Internal helper function for freeing Prism2 command queue entries. | ||
| 168 | * Caller must have acquired local->cmdlock before calling this function. | ||
| 169 | */ | ||
| 170 | static inline void __hostap_cmd_queue_free(local_info_t *local, | ||
| 171 | struct hostap_cmd_queue *entry, | ||
| 172 | int del_req) | ||
| 173 | { | ||
| 174 | if (del_req) { | ||
| 175 | entry->del_req = 1; | ||
| 176 | if (!list_empty(&entry->list)) { | ||
| 177 | list_del_init(&entry->list); | ||
| 178 | local->cmd_queue_len--; | ||
| 179 | } | ||
| 180 | } | ||
| 181 | |||
| 182 | if (refcount_dec_and_test(&entry->usecnt) && entry->del_req) | ||
| 183 | kfree(entry); | ||
| 184 | } | ||
| 185 | |||
| 186 | |||
| 187 | /** | ||
| 188 | * hostap_cmd_queue_free - Free Prism2 command queue entry | ||
| 189 | * @local: pointer to private Host AP driver data | ||
| 190 | * @entry: Prism2 command queue entry to be freed | ||
| 191 | * @del_req: request the entry to be removed | ||
| 192 | * | ||
| 193 | * Free a Prism2 command queue entry. | ||
| 194 | */ | ||
| 195 | static inline void hostap_cmd_queue_free(local_info_t *local, | ||
| 196 | struct hostap_cmd_queue *entry, | ||
| 197 | int del_req) | ||
| 198 | { | ||
| 199 | unsigned long flags; | ||
| 200 | |||
| 201 | spin_lock_irqsave(&local->cmdlock, flags); | ||
| 202 | __hostap_cmd_queue_free(local, entry, del_req); | ||
| 203 | spin_unlock_irqrestore(&local->cmdlock, flags); | ||
| 204 | } | ||
| 205 | |||
| 206 | |||
| 207 | /** | ||
| 208 | * prism2_clear_cmd_queue - Free all pending Prism2 command queue entries | ||
| 209 | * @local: pointer to private Host AP driver data | ||
| 210 | */ | ||
| 211 | static void prism2_clear_cmd_queue(local_info_t *local) | ||
| 212 | { | ||
| 213 | struct list_head *ptr, *n; | ||
| 214 | unsigned long flags; | ||
| 215 | struct hostap_cmd_queue *entry; | ||
| 216 | |||
| 217 | spin_lock_irqsave(&local->cmdlock, flags); | ||
| 218 | list_for_each_safe(ptr, n, &local->cmd_queue) { | ||
| 219 | entry = list_entry(ptr, struct hostap_cmd_queue, list); | ||
| 220 | refcount_inc(&entry->usecnt); | ||
| 221 | printk(KERN_DEBUG "%s: removed pending cmd_queue entry " | ||
| 222 | "(type=%d, cmd=0x%04x, param0=0x%04x)\n", | ||
| 223 | local->dev->name, entry->type, entry->cmd, | ||
| 224 | entry->param0); | ||
| 225 | __hostap_cmd_queue_free(local, entry, 1); | ||
| 226 | } | ||
| 227 | if (local->cmd_queue_len) { | ||
| 228 | /* This should not happen; print debug message and clear | ||
| 229 | * queue length. */ | ||
| 230 | printk(KERN_DEBUG "%s: cmd_queue_len (%d) not zero after " | ||
| 231 | "flush\n", local->dev->name, local->cmd_queue_len); | ||
| 232 | local->cmd_queue_len = 0; | ||
| 233 | } | ||
| 234 | spin_unlock_irqrestore(&local->cmdlock, flags); | ||
| 235 | } | ||
| 236 | |||
| 237 | |||
| 238 | /** | ||
| 239 | * hfa384x_cmd_issue - Issue a Prism2 command to the hardware | ||
| 240 | * @dev: pointer to net_device | ||
| 241 | * @entry: Prism2 command queue entry to be issued | ||
| 242 | */ | ||
| 243 | static int hfa384x_cmd_issue(struct net_device *dev, | ||
| 244 | struct hostap_cmd_queue *entry) | ||
| 245 | { | ||
| 246 | struct hostap_interface *iface; | ||
| 247 | local_info_t *local; | ||
| 248 | int tries; | ||
| 249 | u16 reg; | ||
| 250 | unsigned long flags; | ||
| 251 | |||
| 252 | iface = netdev_priv(dev); | ||
| 253 | local = iface->local; | ||
| 254 | |||
| 255 | if (local->func->card_present && !local->func->card_present(local)) | ||
| 256 | return -ENODEV; | ||
| 257 | |||
| 258 | if (entry->issued) { | ||
| 259 | printk(KERN_DEBUG "%s: driver bug - re-issuing command @%p\n", | ||
| 260 | dev->name, entry); | ||
| 261 | } | ||
| 262 | |||
| 263 | /* wait until busy bit is clear; this should always be clear since the | ||
| 264 | * commands are serialized */ | ||
| 265 | tries = HFA384X_CMD_BUSY_TIMEOUT; | ||
| 266 | while (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY && tries > 0) { | ||
| 267 | tries--; | ||
| 268 | udelay(1); | ||
| 269 | } | ||
| 270 | #ifndef final_version | ||
| 271 | if (tries != HFA384X_CMD_BUSY_TIMEOUT) { | ||
| 272 | prism2_io_debug_error(dev, 1); | ||
| 273 | printk(KERN_DEBUG "%s: hfa384x_cmd_issue: cmd reg was busy " | ||
| 274 | "for %d usec\n", dev->name, | ||
| 275 | HFA384X_CMD_BUSY_TIMEOUT - tries); | ||
| 276 | } | ||
| 277 | #endif | ||
| 278 | if (tries == 0) { | ||
| 279 | reg = HFA384X_INW(HFA384X_CMD_OFF); | ||
| 280 | prism2_io_debug_error(dev, 2); | ||
| 281 | printk(KERN_DEBUG "%s: hfa384x_cmd_issue - timeout - " | ||
| 282 | "reg=0x%04x\n", dev->name, reg); | ||
| 283 | return -ETIMEDOUT; | ||
| 284 | } | ||
| 285 | |||
| 286 | /* write command */ | ||
| 287 | spin_lock_irqsave(&local->cmdlock, flags); | ||
| 288 | HFA384X_OUTW(entry->param0, HFA384X_PARAM0_OFF); | ||
| 289 | HFA384X_OUTW(entry->param1, HFA384X_PARAM1_OFF); | ||
| 290 | HFA384X_OUTW(entry->cmd, HFA384X_CMD_OFF); | ||
| 291 | entry->issued = 1; | ||
| 292 | spin_unlock_irqrestore(&local->cmdlock, flags); | ||
| 293 | |||
| 294 | return 0; | ||
| 295 | } | ||
| 296 | |||
| 297 | |||
| 298 | /** | ||
| 299 | * hfa384x_cmd - Issue a Prism2 command and wait (sleep) for completion | ||
| 300 | * @dev: pointer to net_device | ||
| 301 | * @cmd: Prism2 command code (HFA384X_CMD_CODE_*) | ||
| 302 | * @param0: value for Param0 register | ||
| 303 | * @param1: value for Param1 register (pointer; %NULL if not used) | ||
| 304 | * @resp0: pointer for Resp0 data or %NULL if Resp0 is not needed | ||
| 305 | * | ||
| 306 | * Issue given command (possibly after waiting in command queue) and sleep | ||
| 307 | * until the command is completed (or timed out or interrupted). This can be | ||
| 308 | * called only from user process context. | ||
| 309 | */ | ||
| 310 | static int hfa384x_cmd(struct net_device *dev, u16 cmd, u16 param0, | ||
| 311 | u16 *param1, u16 *resp0) | ||
| 312 | { | ||
| 313 | struct hostap_interface *iface; | ||
| 314 | local_info_t *local; | ||
| 315 | int err, res, issue, issued = 0; | ||
| 316 | unsigned long flags; | ||
| 317 | struct hostap_cmd_queue *entry; | ||
| 318 | DECLARE_WAITQUEUE(wait, current); | ||
| 319 | |||
| 320 | iface = netdev_priv(dev); | ||
| 321 | local = iface->local; | ||
| 322 | |||
| 323 | if (local->cmd_queue_len >= HOSTAP_CMD_QUEUE_MAX_LEN) { | ||
| 324 | printk(KERN_DEBUG "%s: hfa384x_cmd: cmd_queue full\n", | ||
| 325 | dev->name); | ||
| 326 | return -1; | ||
| 327 | } | ||
| 328 | |||
| 329 | if (signal_pending(current)) | ||
| 330 | return -EINTR; | ||
| 331 | |||
| 332 | entry = kzalloc(sizeof(*entry), GFP_ATOMIC); | ||
| 333 | if (entry == NULL) | ||
| 334 | return -ENOMEM; | ||
| 335 | |||
| 336 | refcount_set(&entry->usecnt, 1); | ||
| 337 | entry->type = CMD_SLEEP; | ||
| 338 | entry->cmd = cmd; | ||
| 339 | entry->param0 = param0; | ||
| 340 | if (param1) | ||
| 341 | entry->param1 = *param1; | ||
| 342 | init_waitqueue_head(&entry->compl); | ||
| 343 | |||
| 344 | /* prepare to wait for command completion event, but do not sleep yet | ||
| 345 | */ | ||
| 346 | add_wait_queue(&entry->compl, &wait); | ||
| 347 | set_current_state(TASK_INTERRUPTIBLE); | ||
| 348 | |||
| 349 | spin_lock_irqsave(&local->cmdlock, flags); | ||
| 350 | issue = list_empty(&local->cmd_queue); | ||
| 351 | if (issue) | ||
| 352 | entry->issuing = 1; | ||
| 353 | list_add_tail(&entry->list, &local->cmd_queue); | ||
| 354 | local->cmd_queue_len++; | ||
| 355 | spin_unlock_irqrestore(&local->cmdlock, flags); | ||
| 356 | |||
| 357 | err = 0; | ||
| 358 | if (!issue) | ||
| 359 | goto wait_completion; | ||
| 360 | |||
| 361 | if (signal_pending(current)) | ||
| 362 | err = -EINTR; | ||
| 363 | |||
| 364 | if (!err) { | ||
| 365 | if (hfa384x_cmd_issue(dev, entry)) | ||
| 366 | err = -ETIMEDOUT; | ||
| 367 | else | ||
| 368 | issued = 1; | ||
| 369 | } | ||
| 370 | |||
| 371 | wait_completion: | ||
| 372 | if (!err && entry->type != CMD_COMPLETED) { | ||
| 373 | /* sleep until command is completed or timed out */ | ||
| 374 | res = schedule_timeout(2 * HZ); | ||
| 375 | } else | ||
| 376 | res = -1; | ||
| 377 | |||
| 378 | if (!err && signal_pending(current)) | ||
| 379 | err = -EINTR; | ||
| 380 | |||
| 381 | if (err && issued) { | ||
| 382 | /* the command was issued, so a CmdCompl event should occur | ||
| 383 | * soon; however, there's a pending signal and | ||
| 384 | * schedule_timeout() would be interrupted; wait a short period | ||
| 385 | * of time to avoid removing entry from the list before | ||
| 386 | * CmdCompl event */ | ||
| 387 | udelay(300); | ||
| 388 | } | ||
| 389 | |||
| 390 | set_current_state(TASK_RUNNING); | ||
| 391 | remove_wait_queue(&entry->compl, &wait); | ||
| 392 | |||
| 393 | /* If entry->list is still in the list, it must be removed | ||
| 394 | * first and in this case prism2_cmd_ev() does not yet have | ||
| 395 | * local reference to it, and the data can be kfree()'d | ||
| 396 | * here. If the command completion event is still generated, | ||
| 397 | * it will be assigned to next (possibly) pending command, but | ||
| 398 | * the driver will reset the card anyway due to timeout | ||
| 399 | * | ||
| 400 | * If the entry is not in the list prism2_cmd_ev() has a local | ||
| 401 | * reference to it, but keeps cmdlock as long as the data is | ||
| 402 | * needed, so the data can be kfree()'d here. */ | ||
| 403 | |||
| 404 | /* FIX: if the entry->list is in the list, it has not been completed | ||
| 405 | * yet, so removing it here is somewhat wrong.. this could cause | ||
| 406 | * references to freed memory and next list_del() causing NULL pointer | ||
| 407 | * dereference.. it would probably be better to leave the entry in the | ||
| 408 | * list and the list should be emptied during hw reset */ | ||
| 409 | |||
| 410 | spin_lock_irqsave(&local->cmdlock, flags); | ||
| 411 | if (!list_empty(&entry->list)) { | ||
| 412 | printk(KERN_DEBUG "%s: hfa384x_cmd: entry still in list? " | ||
| 413 | "(entry=%p, type=%d, res=%d)\n", dev->name, entry, | ||
| 414 | entry->type, res); | ||
| 415 | list_del_init(&entry->list); | ||
| 416 | local->cmd_queue_len--; | ||
| 417 | } | ||
| 418 | spin_unlock_irqrestore(&local->cmdlock, flags); | ||
| 419 | |||
| 420 | if (err) { | ||
| 421 | printk(KERN_DEBUG "%s: hfa384x_cmd: interrupted; err=%d\n", | ||
| 422 | dev->name, err); | ||
| 423 | res = err; | ||
| 424 | goto done; | ||
| 425 | } | ||
| 426 | |||
| 427 | if (entry->type != CMD_COMPLETED) { | ||
| 428 | u16 reg = HFA384X_INW(HFA384X_EVSTAT_OFF); | ||
| 429 | printk(KERN_DEBUG "%s: hfa384x_cmd: command was not " | ||
| 430 | "completed (res=%d, entry=%p, type=%d, cmd=0x%04x, " | ||
| 431 | "param0=0x%04x, EVSTAT=%04x INTEN=%04x)\n", dev->name, | ||
| 432 | res, entry, entry->type, entry->cmd, entry->param0, reg, | ||
| 433 | HFA384X_INW(HFA384X_INTEN_OFF)); | ||
| 434 | if (reg & HFA384X_EV_CMD) { | ||
| 435 | /* Command completion event is pending, but the | ||
| 436 | * interrupt was not delivered - probably an issue | ||
| 437 | * with pcmcia-cs configuration. */ | ||
| 438 | printk(KERN_WARNING "%s: interrupt delivery does not " | ||
| 439 | "seem to work\n", dev->name); | ||
| 440 | } | ||
| 441 | prism2_io_debug_error(dev, 3); | ||
| 442 | res = -ETIMEDOUT; | ||
| 443 | goto done; | ||
| 444 | } | ||
| 445 | |||
| 446 | if (resp0 != NULL) | ||
| 447 | *resp0 = entry->resp0; | ||
| 448 | #ifndef final_version | ||
| 449 | if (entry->res) { | ||
| 450 | printk(KERN_DEBUG "%s: CMD=0x%04x => res=0x%02x, " | ||
| 451 | "resp0=0x%04x\n", | ||
| 452 | dev->name, cmd, entry->res, entry->resp0); | ||
| 453 | } | ||
| 454 | #endif /* final_version */ | ||
| 455 | |||
| 456 | res = entry->res; | ||
| 457 | done: | ||
| 458 | hostap_cmd_queue_free(local, entry, 1); | ||
| 459 | return res; | ||
| 460 | } | ||
| 461 | |||
| 462 | |||
| 463 | /** | ||
| 464 | * hfa384x_cmd_callback - Issue a Prism2 command; callback when completed | ||
| 465 | * @dev: pointer to net_device | ||
| 466 | * @cmd: Prism2 command code (HFA384X_CMD_CODE_*) | ||
| 467 | * @param0: value for Param0 register | ||
| 468 | * @callback: command completion callback function (%NULL = no callback) | ||
| 469 | * @context: context data to be given to the callback function | ||
| 470 | * | ||
| 471 | * Issue given command (possibly after waiting in command queue) and use | ||
| 472 | * callback function to indicate command completion. This can be called both | ||
| 473 | * from user and interrupt context. The callback function will be called in | ||
| 474 | * hardware IRQ context. It can be %NULL, when no function is called when | ||
| 475 | * command is completed. | ||
| 476 | */ | ||
| 477 | static int hfa384x_cmd_callback(struct net_device *dev, u16 cmd, u16 param0, | ||
| 478 | void (*callback)(struct net_device *dev, | ||
| 479 | long context, u16 resp0, | ||
| 480 | u16 status), | ||
| 481 | long context) | ||
| 482 | { | ||
| 483 | struct hostap_interface *iface; | ||
| 484 | local_info_t *local; | ||
| 485 | int issue, ret; | ||
| 486 | unsigned long flags; | ||
| 487 | struct hostap_cmd_queue *entry; | ||
| 488 | |||
| 489 | iface = netdev_priv(dev); | ||
| 490 | local = iface->local; | ||
| 491 | |||
| 492 | if (local->cmd_queue_len >= HOSTAP_CMD_QUEUE_MAX_LEN + 2) { | ||
| 493 | printk(KERN_DEBUG "%s: hfa384x_cmd: cmd_queue full\n", | ||
| 494 | dev->name); | ||
| 495 | return -1; | ||
| 496 | } | ||
| 497 | |||
| 498 | entry = kzalloc(sizeof(*entry), GFP_ATOMIC); | ||
| 499 | if (entry == NULL) | ||
| 500 | return -ENOMEM; | ||
| 501 | |||
| 502 | refcount_set(&entry->usecnt, 1); | ||
| 503 | entry->type = CMD_CALLBACK; | ||
| 504 | entry->cmd = cmd; | ||
| 505 | entry->param0 = param0; | ||
| 506 | entry->callback = callback; | ||
| 507 | entry->context = context; | ||
| 508 | |||
| 509 | spin_lock_irqsave(&local->cmdlock, flags); | ||
| 510 | issue = list_empty(&local->cmd_queue); | ||
| 511 | if (issue) | ||
| 512 | entry->issuing = 1; | ||
| 513 | list_add_tail(&entry->list, &local->cmd_queue); | ||
| 514 | local->cmd_queue_len++; | ||
| 515 | spin_unlock_irqrestore(&local->cmdlock, flags); | ||
| 516 | |||
| 517 | if (issue && hfa384x_cmd_issue(dev, entry)) | ||
| 518 | ret = -ETIMEDOUT; | ||
| 519 | else | ||
| 520 | ret = 0; | ||
| 521 | |||
| 522 | hostap_cmd_queue_free(local, entry, ret); | ||
| 523 | |||
| 524 | return ret; | ||
| 525 | } | ||
| 526 | |||
| 527 | |||
| 528 | /** | ||
| 529 | * __hfa384x_cmd_no_wait - Issue a Prism2 command (private) | ||
| 530 | * @dev: pointer to net_device | ||
| 531 | * @cmd: Prism2 command code (HFA384X_CMD_CODE_*) | ||
| 532 | * @param0: value for Param0 register | ||
| 533 | * @io_debug_num: I/O debug error number | ||
| 534 | * | ||
| 535 | * Shared helper function for hfa384x_cmd_wait() and hfa384x_cmd_no_wait(). | ||
| 536 | */ | ||
| 537 | static int __hfa384x_cmd_no_wait(struct net_device *dev, u16 cmd, u16 param0, | ||
| 538 | int io_debug_num) | ||
| 539 | { | ||
| 540 | int tries; | ||
| 541 | u16 reg; | ||
| 542 | |||
| 543 | /* wait until busy bit is clear; this should always be clear since the | ||
| 544 | * commands are serialized */ | ||
| 545 | tries = HFA384X_CMD_BUSY_TIMEOUT; | ||
| 546 | while (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY && tries > 0) { | ||
| 547 | tries--; | ||
| 548 | udelay(1); | ||
| 549 | } | ||
| 550 | if (tries == 0) { | ||
| 551 | reg = HFA384X_INW(HFA384X_CMD_OFF); | ||
| 552 | prism2_io_debug_error(dev, io_debug_num); | ||
| 553 | printk(KERN_DEBUG "%s: __hfa384x_cmd_no_wait(%d) - timeout - " | ||
| 554 | "reg=0x%04x\n", dev->name, io_debug_num, reg); | ||
| 555 | return -ETIMEDOUT; | ||
| 556 | } | ||
| 557 | |||
| 558 | /* write command */ | ||
| 559 | HFA384X_OUTW(param0, HFA384X_PARAM0_OFF); | ||
| 560 | HFA384X_OUTW(cmd, HFA384X_CMD_OFF); | ||
| 561 | |||
| 562 | return 0; | ||
| 563 | } | ||
| 564 | |||
| 565 | |||
| 566 | /** | ||
| 567 | * hfa384x_cmd_wait - Issue a Prism2 command and busy wait for completion | ||
| 568 | * @dev: pointer to net_device | ||
| 569 | * @cmd: Prism2 command code (HFA384X_CMD_CODE_*) | ||
| 570 | * @param0: value for Param0 register | ||
| 571 | */ | ||
| 572 | static int hfa384x_cmd_wait(struct net_device *dev, u16 cmd, u16 param0) | ||
| 573 | { | ||
| 574 | int res, tries; | ||
| 575 | u16 reg; | ||
| 576 | |||
| 577 | res = __hfa384x_cmd_no_wait(dev, cmd, param0, 4); | ||
| 578 | if (res) | ||
| 579 | return res; | ||
| 580 | |||
| 581 | /* wait for command completion */ | ||
| 582 | if ((cmd & HFA384X_CMDCODE_MASK) == HFA384X_CMDCODE_DOWNLOAD) | ||
| 583 | tries = HFA384X_DL_COMPL_TIMEOUT; | ||
| 584 | else | ||
| 585 | tries = HFA384X_CMD_COMPL_TIMEOUT; | ||
| 586 | |||
| 587 | while (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_CMD) && | ||
| 588 | tries > 0) { | ||
| 589 | tries--; | ||
| 590 | udelay(10); | ||
| 591 | } | ||
| 592 | if (tries == 0) { | ||
| 593 | reg = HFA384X_INW(HFA384X_EVSTAT_OFF); | ||
| 594 | prism2_io_debug_error(dev, 5); | ||
| 595 | printk(KERN_DEBUG "%s: hfa384x_cmd_wait - timeout2 - " | ||
| 596 | "reg=0x%04x\n", dev->name, reg); | ||
| 597 | return -ETIMEDOUT; | ||
| 598 | } | ||
| 599 | |||
| 600 | res = (HFA384X_INW(HFA384X_STATUS_OFF) & | ||
| 601 | (BIT(14) | BIT(13) | BIT(12) | BIT(11) | BIT(10) | BIT(9) | | ||
| 602 | BIT(8))) >> 8; | ||
| 603 | #ifndef final_version | ||
| 604 | if (res) { | ||
| 605 | printk(KERN_DEBUG "%s: CMD=0x%04x => res=0x%02x\n", | ||
| 606 | dev->name, cmd, res); | ||
| 607 | } | ||
| 608 | #endif | ||
| 609 | |||
| 610 | HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF); | ||
| 611 | |||
| 612 | return res; | ||
| 613 | } | ||
| 614 | |||
| 615 | |||
| 616 | /** | ||
| 617 | * hfa384x_cmd_no_wait - Issue a Prism2 command; do not wait for completion | ||
| 618 | * @dev: pointer to net_device | ||
| 619 | * @cmd: Prism2 command code (HFA384X_CMD_CODE_*) | ||
| 620 | * @param0: value for Param0 register | ||
| 621 | */ | ||
| 622 | static inline int hfa384x_cmd_no_wait(struct net_device *dev, u16 cmd, | ||
| 623 | u16 param0) | ||
| 624 | { | ||
| 625 | return __hfa384x_cmd_no_wait(dev, cmd, param0, 6); | ||
| 626 | } | ||
| 627 | |||
| 628 | |||
| 629 | /** | ||
| 630 | * prism2_cmd_ev - Prism2 command completion event handler | ||
| 631 | * @dev: pointer to net_device | ||
| 632 | * | ||
| 633 | * Interrupt handler for command completion events. Called by the main | ||
| 634 | * interrupt handler in hardware IRQ context. Read Resp0 and status registers | ||
| 635 | * from the hardware and ACK the event. Depending on the issued command type | ||
| 636 | * either wake up the sleeping process that is waiting for command completion | ||
| 637 | * or call the callback function. Issue the next command, if one is pending. | ||
| 638 | */ | ||
| 639 | static void prism2_cmd_ev(struct net_device *dev) | ||
| 640 | { | ||
| 641 | struct hostap_interface *iface; | ||
| 642 | local_info_t *local; | ||
| 643 | struct hostap_cmd_queue *entry = NULL; | ||
| 644 | |||
| 645 | iface = netdev_priv(dev); | ||
| 646 | local = iface->local; | ||
| 647 | |||
| 648 | spin_lock(&local->cmdlock); | ||
| 649 | if (!list_empty(&local->cmd_queue)) { | ||
| 650 | entry = list_entry(local->cmd_queue.next, | ||
| 651 | struct hostap_cmd_queue, list); | ||
| 652 | refcount_inc(&entry->usecnt); | ||
| 653 | list_del_init(&entry->list); | ||
| 654 | local->cmd_queue_len--; | ||
| 655 | |||
| 656 | if (!entry->issued) { | ||
| 657 | printk(KERN_DEBUG "%s: Command completion event, but " | ||
| 658 | "cmd not issued\n", dev->name); | ||
| 659 | __hostap_cmd_queue_free(local, entry, 1); | ||
| 660 | entry = NULL; | ||
| 661 | } | ||
| 662 | } | ||
| 663 | spin_unlock(&local->cmdlock); | ||
| 664 | |||
| 665 | if (!entry) { | ||
| 666 | HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF); | ||
| 667 | printk(KERN_DEBUG "%s: Command completion event, but no " | ||
| 668 | "pending commands\n", dev->name); | ||
| 669 | return; | ||
| 670 | } | ||
| 671 | |||
| 672 | entry->resp0 = HFA384X_INW(HFA384X_RESP0_OFF); | ||
| 673 | entry->res = (HFA384X_INW(HFA384X_STATUS_OFF) & | ||
| 674 | (BIT(14) | BIT(13) | BIT(12) | BIT(11) | BIT(10) | | ||
| 675 | BIT(9) | BIT(8))) >> 8; | ||
| 676 | HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF); | ||
| 677 | |||
| 678 | /* TODO: rest of the CmdEv handling could be moved to tasklet */ | ||
| 679 | if (entry->type == CMD_SLEEP) { | ||
| 680 | entry->type = CMD_COMPLETED; | ||
| 681 | wake_up_interruptible(&entry->compl); | ||
| 682 | } else if (entry->type == CMD_CALLBACK) { | ||
| 683 | if (entry->callback) | ||
| 684 | entry->callback(dev, entry->context, entry->resp0, | ||
| 685 | entry->res); | ||
| 686 | } else { | ||
| 687 | printk(KERN_DEBUG "%s: Invalid command completion type %d\n", | ||
| 688 | dev->name, entry->type); | ||
| 689 | } | ||
| 690 | hostap_cmd_queue_free(local, entry, 1); | ||
| 691 | |||
| 692 | /* issue next command, if pending */ | ||
| 693 | entry = NULL; | ||
| 694 | spin_lock(&local->cmdlock); | ||
| 695 | if (!list_empty(&local->cmd_queue)) { | ||
| 696 | entry = list_entry(local->cmd_queue.next, | ||
| 697 | struct hostap_cmd_queue, list); | ||
| 698 | if (entry->issuing) { | ||
| 699 | /* hfa384x_cmd() has already started issuing this | ||
| 700 | * command, so do not start here */ | ||
| 701 | entry = NULL; | ||
| 702 | } | ||
| 703 | if (entry) | ||
| 704 | refcount_inc(&entry->usecnt); | ||
| 705 | } | ||
| 706 | spin_unlock(&local->cmdlock); | ||
| 707 | |||
| 708 | if (entry) { | ||
| 709 | /* issue next command; if command issuing fails, remove the | ||
| 710 | * entry from cmd_queue */ | ||
| 711 | int res = hfa384x_cmd_issue(dev, entry); | ||
| 712 | spin_lock(&local->cmdlock); | ||
| 713 | __hostap_cmd_queue_free(local, entry, res); | ||
| 714 | spin_unlock(&local->cmdlock); | ||
| 715 | } | ||
| 716 | } | ||
| 717 | |||
| 718 | |||
| 719 | static int hfa384x_wait_offset(struct net_device *dev, u16 o_off) | ||
| 720 | { | ||
| 721 | int tries = HFA384X_BAP_BUSY_TIMEOUT; | ||
| 722 | int res = HFA384X_INW(o_off) & HFA384X_OFFSET_BUSY; | ||
| 723 | |||
| 724 | while (res && tries > 0) { | ||
| 725 | tries--; | ||
| 726 | udelay(1); | ||
| 727 | res = HFA384X_INW(o_off) & HFA384X_OFFSET_BUSY; | ||
| 728 | } | ||
| 729 | return res; | ||
| 730 | } | ||
| 731 | |||
| 732 | |||
| 733 | /* Offset must be even */ | ||
| 734 | static int hfa384x_setup_bap(struct net_device *dev, u16 bap, u16 id, | ||
| 735 | int offset) | ||
| 736 | { | ||
| 737 | u16 o_off, s_off; | ||
| 738 | int ret = 0; | ||
| 739 | |||
| 740 | if (offset % 2 || bap > 1) | ||
| 741 | return -EINVAL; | ||
| 742 | |||
| 743 | if (bap == BAP1) { | ||
| 744 | o_off = HFA384X_OFFSET1_OFF; | ||
| 745 | s_off = HFA384X_SELECT1_OFF; | ||
| 746 | } else { | ||
| 747 | o_off = HFA384X_OFFSET0_OFF; | ||
| 748 | s_off = HFA384X_SELECT0_OFF; | ||
| 749 | } | ||
| 750 | |||
| 751 | if (hfa384x_wait_offset(dev, o_off)) { | ||
| 752 | prism2_io_debug_error(dev, 7); | ||
| 753 | printk(KERN_DEBUG "%s: hfa384x_setup_bap - timeout before\n", | ||
| 754 | dev->name); | ||
| 755 | ret = -ETIMEDOUT; | ||
| 756 | goto out; | ||
| 757 | } | ||
| 758 | |||
| 759 | HFA384X_OUTW(id, s_off); | ||
| 760 | HFA384X_OUTW(offset, o_off); | ||
| 761 | |||
| 762 | if (hfa384x_wait_offset(dev, o_off)) { | ||
| 763 | prism2_io_debug_error(dev, 8); | ||
| 764 | printk(KERN_DEBUG "%s: hfa384x_setup_bap - timeout after\n", | ||
| 765 | dev->name); | ||
| 766 | ret = -ETIMEDOUT; | ||
| 767 | goto out; | ||
| 768 | } | ||
| 769 | #ifndef final_version | ||
| 770 | if (HFA384X_INW(o_off) & HFA384X_OFFSET_ERR) { | ||
| 771 | prism2_io_debug_error(dev, 9); | ||
| 772 | printk(KERN_DEBUG "%s: hfa384x_setup_bap - offset error " | ||
| 773 | "(%d,0x04%x,%d); reg=0x%04x\n", | ||
| 774 | dev->name, bap, id, offset, HFA384X_INW(o_off)); | ||
| 775 | ret = -EINVAL; | ||
| 776 | } | ||
| 777 | #endif | ||
| 778 | |||
| 779 | out: | ||
| 780 | return ret; | ||
| 781 | } | ||
| 782 | |||
| 783 | |||
| 784 | static int hfa384x_get_rid(struct net_device *dev, u16 rid, void *buf, int len, | ||
| 785 | int exact_len) | ||
| 786 | { | ||
| 787 | struct hostap_interface *iface; | ||
| 788 | local_info_t *local; | ||
| 789 | int res, rlen = 0; | ||
| 790 | struct hfa384x_rid_hdr rec; | ||
| 791 | |||
| 792 | iface = netdev_priv(dev); | ||
| 793 | local = iface->local; | ||
| 794 | |||
| 795 | if (local->no_pri) { | ||
| 796 | printk(KERN_DEBUG "%s: cannot get RID %04x (len=%d) - no PRI " | ||
| 797 | "f/w\n", dev->name, rid, len); | ||
| 798 | return -ENOTTY; /* Well.. not really correct, but return | ||
| 799 | * something unique enough.. */ | ||
| 800 | } | ||
| 801 | |||
| 802 | if ((local->func->card_present && !local->func->card_present(local)) || | ||
| 803 | local->hw_downloading) | ||
| 804 | return -ENODEV; | ||
| 805 | |||
| 806 | res = mutex_lock_interruptible(&local->rid_bap_mtx); | ||
| 807 | if (res) | ||
| 808 | return res; | ||
| 809 | |||
| 810 | res = hfa384x_cmd(dev, HFA384X_CMDCODE_ACCESS, rid, NULL, NULL); | ||
| 811 | if (res) { | ||
| 812 | printk(KERN_DEBUG "%s: hfa384x_get_rid: CMDCODE_ACCESS failed " | ||
| 813 | "(res=%d, rid=%04x, len=%d)\n", | ||
| 814 | dev->name, res, rid, len); | ||
| 815 | mutex_unlock(&local->rid_bap_mtx); | ||
| 816 | return res; | ||
| 817 | } | ||
| 818 | |||
| 819 | spin_lock_bh(&local->baplock); | ||
| 820 | |||
| 821 | res = hfa384x_setup_bap(dev, BAP0, rid, 0); | ||
| 822 | if (res) | ||
| 823 | goto unlock; | ||
| 824 | |||
| 825 | res = hfa384x_from_bap(dev, BAP0, &rec, sizeof(rec)); | ||
| 826 | if (res) | ||
| 827 | goto unlock; | ||
| 828 | |||
| 829 | if (le16_to_cpu(rec.len) == 0) { | ||
| 830 | /* RID not available */ | ||
| 831 | res = -ENODATA; | ||
| 832 | goto unlock; | ||
| 833 | } | ||
| 834 | |||
| 835 | rlen = (le16_to_cpu(rec.len) - 1) * 2; | ||
| 836 | if (exact_len && rlen != len) { | ||
| 837 | printk(KERN_DEBUG "%s: hfa384x_get_rid - RID len mismatch: " | ||
| 838 | "rid=0x%04x, len=%d (expected %d)\n", | ||
| 839 | dev->name, rid, rlen, len); | ||
| 840 | res = -ENODATA; | ||
| 841 | } | ||
| 842 | |||
| 843 | res = hfa384x_from_bap(dev, BAP0, buf, len); | ||
| 844 | |||
| 845 | unlock: | ||
| 846 | spin_unlock_bh(&local->baplock); | ||
| 847 | mutex_unlock(&local->rid_bap_mtx); | ||
| 848 | |||
| 849 | if (res) { | ||
| 850 | if (res != -ENODATA) | ||
| 851 | printk(KERN_DEBUG "%s: hfa384x_get_rid (rid=%04x, " | ||
| 852 | "len=%d) - failed - res=%d\n", dev->name, rid, | ||
| 853 | len, res); | ||
| 854 | if (res == -ETIMEDOUT) | ||
| 855 | prism2_hw_reset(dev); | ||
| 856 | return res; | ||
| 857 | } | ||
| 858 | |||
| 859 | return rlen; | ||
| 860 | } | ||
| 861 | |||
| 862 | |||
| 863 | static int hfa384x_set_rid(struct net_device *dev, u16 rid, void *buf, int len) | ||
| 864 | { | ||
| 865 | struct hostap_interface *iface; | ||
| 866 | local_info_t *local; | ||
| 867 | struct hfa384x_rid_hdr rec; | ||
| 868 | int res; | ||
| 869 | |||
| 870 | iface = netdev_priv(dev); | ||
| 871 | local = iface->local; | ||
| 872 | |||
| 873 | if (local->no_pri) { | ||
| 874 | printk(KERN_DEBUG "%s: cannot set RID %04x (len=%d) - no PRI " | ||
| 875 | "f/w\n", dev->name, rid, len); | ||
| 876 | return -ENOTTY; /* Well.. not really correct, but return | ||
| 877 | * something unique enough.. */ | ||
| 878 | } | ||
| 879 | |||
| 880 | if ((local->func->card_present && !local->func->card_present(local)) || | ||
| 881 | local->hw_downloading) | ||
| 882 | return -ENODEV; | ||
| 883 | |||
| 884 | rec.rid = cpu_to_le16(rid); | ||
| 885 | /* RID len in words and +1 for rec.rid */ | ||
| 886 | rec.len = cpu_to_le16(len / 2 + len % 2 + 1); | ||
| 887 | |||
| 888 | res = mutex_lock_interruptible(&local->rid_bap_mtx); | ||
| 889 | if (res) | ||
| 890 | return res; | ||
| 891 | |||
| 892 | spin_lock_bh(&local->baplock); | ||
| 893 | res = hfa384x_setup_bap(dev, BAP0, rid, 0); | ||
| 894 | if (!res) | ||
| 895 | res = hfa384x_to_bap(dev, BAP0, &rec, sizeof(rec)); | ||
| 896 | if (!res) | ||
| 897 | res = hfa384x_to_bap(dev, BAP0, buf, len); | ||
| 898 | spin_unlock_bh(&local->baplock); | ||
| 899 | |||
| 900 | if (res) { | ||
| 901 | printk(KERN_DEBUG "%s: hfa384x_set_rid (rid=%04x, len=%d) - " | ||
| 902 | "failed - res=%d\n", dev->name, rid, len, res); | ||
| 903 | mutex_unlock(&local->rid_bap_mtx); | ||
| 904 | return res; | ||
| 905 | } | ||
| 906 | |||
| 907 | res = hfa384x_cmd(dev, HFA384X_CMDCODE_ACCESS_WRITE, rid, NULL, NULL); | ||
| 908 | mutex_unlock(&local->rid_bap_mtx); | ||
| 909 | |||
| 910 | if (res) { | ||
| 911 | printk(KERN_DEBUG "%s: hfa384x_set_rid: CMDCODE_ACCESS_WRITE " | ||
| 912 | "failed (res=%d, rid=%04x, len=%d)\n", | ||
| 913 | dev->name, res, rid, len); | ||
| 914 | |||
| 915 | if (res == -ETIMEDOUT) | ||
| 916 | prism2_hw_reset(dev); | ||
| 917 | } | ||
| 918 | |||
| 919 | return res; | ||
| 920 | } | ||
| 921 | |||
| 922 | |||
| 923 | static void hfa384x_disable_interrupts(struct net_device *dev) | ||
| 924 | { | ||
| 925 | /* disable interrupts and clear event status */ | ||
| 926 | HFA384X_OUTW(0, HFA384X_INTEN_OFF); | ||
| 927 | HFA384X_OUTW(0xffff, HFA384X_EVACK_OFF); | ||
| 928 | } | ||
| 929 | |||
| 930 | |||
| 931 | static void hfa384x_enable_interrupts(struct net_device *dev) | ||
| 932 | { | ||
| 933 | /* ack pending events and enable interrupts from selected events */ | ||
| 934 | HFA384X_OUTW(0xffff, HFA384X_EVACK_OFF); | ||
| 935 | HFA384X_OUTW(HFA384X_EVENT_MASK, HFA384X_INTEN_OFF); | ||
| 936 | } | ||
| 937 | |||
| 938 | |||
| 939 | static void hfa384x_events_no_bap0(struct net_device *dev) | ||
| 940 | { | ||
| 941 | HFA384X_OUTW(HFA384X_EVENT_MASK & ~HFA384X_BAP0_EVENTS, | ||
| 942 | HFA384X_INTEN_OFF); | ||
| 943 | } | ||
| 944 | |||
| 945 | |||
| 946 | static void hfa384x_events_all(struct net_device *dev) | ||
| 947 | { | ||
| 948 | HFA384X_OUTW(HFA384X_EVENT_MASK, HFA384X_INTEN_OFF); | ||
| 949 | } | ||
| 950 | |||
| 951 | |||
| 952 | static void hfa384x_events_only_cmd(struct net_device *dev) | ||
| 953 | { | ||
| 954 | HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_INTEN_OFF); | ||
| 955 | } | ||
| 956 | |||
| 957 | |||
| 958 | static u16 hfa384x_allocate_fid(struct net_device *dev, int len) | ||
| 959 | { | ||
| 960 | u16 fid; | ||
| 961 | unsigned long delay; | ||
| 962 | |||
| 963 | /* FIX: this could be replace with hfa384x_cmd() if the Alloc event | ||
| 964 | * below would be handled like CmdCompl event (sleep here, wake up from | ||
| 965 | * interrupt handler */ | ||
| 966 | if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_ALLOC, len)) { | ||
| 967 | printk(KERN_DEBUG "%s: cannot allocate fid, len=%d\n", | ||
| 968 | dev->name, len); | ||
| 969 | return 0xffff; | ||
| 970 | } | ||
| 971 | |||
| 972 | delay = jiffies + HFA384X_ALLOC_COMPL_TIMEOUT; | ||
| 973 | while (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_ALLOC) && | ||
| 974 | time_before(jiffies, delay)) | ||
| 975 | yield(); | ||
| 976 | if (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_ALLOC)) { | ||
| 977 | printk("%s: fid allocate, len=%d - timeout\n", dev->name, len); | ||
| 978 | return 0xffff; | ||
| 979 | } | ||
| 980 | |||
| 981 | fid = HFA384X_INW(HFA384X_ALLOCFID_OFF); | ||
| 982 | HFA384X_OUTW(HFA384X_EV_ALLOC, HFA384X_EVACK_OFF); | ||
| 983 | |||
| 984 | return fid; | ||
| 985 | } | ||
| 986 | |||
| 987 | |||
| 988 | static int prism2_reset_port(struct net_device *dev) | ||
| 989 | { | ||
| 990 | struct hostap_interface *iface; | ||
| 991 | local_info_t *local; | ||
| 992 | int res; | ||
| 993 | |||
| 994 | iface = netdev_priv(dev); | ||
| 995 | local = iface->local; | ||
| 996 | |||
| 997 | if (!local->dev_enabled) | ||
| 998 | return 0; | ||
| 999 | |||
| 1000 | res = hfa384x_cmd(dev, HFA384X_CMDCODE_DISABLE, 0, | ||
| 1001 | NULL, NULL); | ||
| 1002 | if (res) | ||
| 1003 | printk(KERN_DEBUG "%s: reset port failed to disable port\n", | ||
| 1004 | dev->name); | ||
| 1005 | else { | ||
| 1006 | res = hfa384x_cmd(dev, HFA384X_CMDCODE_ENABLE, 0, | ||
| 1007 | NULL, NULL); | ||
| 1008 | if (res) | ||
| 1009 | printk(KERN_DEBUG "%s: reset port failed to enable " | ||
| 1010 | "port\n", dev->name); | ||
| 1011 | } | ||
| 1012 | |||
| 1013 | /* It looks like at least some STA firmware versions reset | ||
| 1014 | * fragmentation threshold back to 2346 after enable command. Restore | ||
| 1015 | * the configured value, if it differs from this default. */ | ||
| 1016 | if (local->fragm_threshold != 2346 && | ||
| 1017 | hostap_set_word(dev, HFA384X_RID_FRAGMENTATIONTHRESHOLD, | ||
| 1018 | local->fragm_threshold)) { | ||
| 1019 | printk(KERN_DEBUG "%s: failed to restore fragmentation " | ||
| 1020 | "threshold (%d) after Port0 enable\n", | ||
| 1021 | dev->name, local->fragm_threshold); | ||
| 1022 | } | ||
| 1023 | |||
| 1024 | /* Some firmwares lose antenna selection settings on reset */ | ||
| 1025 | (void) hostap_set_antsel(local); | ||
| 1026 | |||
| 1027 | return res; | ||
| 1028 | } | ||
| 1029 | |||
| 1030 | |||
| 1031 | static int prism2_get_version_info(struct net_device *dev, u16 rid, | ||
| 1032 | const char *txt) | ||
| 1033 | { | ||
| 1034 | struct hfa384x_comp_ident comp; | ||
| 1035 | struct hostap_interface *iface; | ||
| 1036 | local_info_t *local; | ||
| 1037 | |||
| 1038 | iface = netdev_priv(dev); | ||
| 1039 | local = iface->local; | ||
| 1040 | |||
| 1041 | if (local->no_pri) { | ||
| 1042 | /* PRI f/w not yet available - cannot read RIDs */ | ||
| 1043 | return -1; | ||
| 1044 | } | ||
| 1045 | if (hfa384x_get_rid(dev, rid, &comp, sizeof(comp), 1) < 0) { | ||
| 1046 | printk(KERN_DEBUG "Could not get RID for component %s\n", txt); | ||
| 1047 | return -1; | ||
| 1048 | } | ||
| 1049 | |||
| 1050 | printk(KERN_INFO "%s: %s: id=0x%02x v%d.%d.%d\n", dev->name, txt, | ||
| 1051 | __le16_to_cpu(comp.id), __le16_to_cpu(comp.major), | ||
| 1052 | __le16_to_cpu(comp.minor), __le16_to_cpu(comp.variant)); | ||
| 1053 | return 0; | ||
| 1054 | } | ||
| 1055 | |||
| 1056 | |||
| 1057 | static int prism2_setup_rids(struct net_device *dev) | ||
| 1058 | { | ||
| 1059 | struct hostap_interface *iface; | ||
| 1060 | local_info_t *local; | ||
| 1061 | __le16 tmp; | ||
| 1062 | int ret = 0; | ||
| 1063 | |||
| 1064 | iface = netdev_priv(dev); | ||
| 1065 | local = iface->local; | ||
| 1066 | |||
| 1067 | hostap_set_word(dev, HFA384X_RID_TICKTIME, 2000); | ||
| 1068 | |||
| 1069 | if (!local->fw_ap) { | ||
| 1070 | u16 tmp1 = hostap_get_porttype(local); | ||
| 1071 | ret = hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE, tmp1); | ||
| 1072 | if (ret) { | ||
| 1073 | printk("%s: Port type setting to %d failed\n", | ||
| 1074 | dev->name, tmp1); | ||
| 1075 | goto fail; | ||
| 1076 | } | ||
| 1077 | } | ||
| 1078 | |||
| 1079 | /* Setting SSID to empty string seems to kill the card in Host AP mode | ||
| 1080 | */ | ||
| 1081 | if (local->iw_mode != IW_MODE_MASTER || local->essid[0] != '\0') { | ||
| 1082 | ret = hostap_set_string(dev, HFA384X_RID_CNFOWNSSID, | ||
| 1083 | local->essid); | ||
| 1084 | if (ret) { | ||
| 1085 | printk("%s: AP own SSID setting failed\n", dev->name); | ||
| 1086 | goto fail; | ||
| 1087 | } | ||
| 1088 | } | ||
| 1089 | |||
| 1090 | ret = hostap_set_word(dev, HFA384X_RID_CNFMAXDATALEN, | ||
| 1091 | PRISM2_DATA_MAXLEN); | ||
| 1092 | if (ret) { | ||
| 1093 | printk("%s: MAC data length setting to %d failed\n", | ||
| 1094 | dev->name, PRISM2_DATA_MAXLEN); | ||
| 1095 | goto fail; | ||
| 1096 | } | ||
| 1097 | |||
| 1098 | if (hfa384x_get_rid(dev, HFA384X_RID_CHANNELLIST, &tmp, 2, 1) < 0) { | ||
| 1099 | printk("%s: Channel list read failed\n", dev->name); | ||
| 1100 | ret = -EINVAL; | ||
| 1101 | goto fail; | ||
| 1102 | } | ||
| 1103 | local->channel_mask = le16_to_cpu(tmp); | ||
| 1104 | |||
| 1105 | if (local->channel < 1 || local->channel > 14 || | ||
| 1106 | !(local->channel_mask & (1 << (local->channel - 1)))) { | ||
| 1107 | printk(KERN_WARNING "%s: Channel setting out of range " | ||
| 1108 | "(%d)!\n", dev->name, local->channel); | ||
| 1109 | ret = -EBUSY; | ||
| 1110 | goto fail; | ||
| 1111 | } | ||
| 1112 | |||
| 1113 | ret = hostap_set_word(dev, HFA384X_RID_CNFOWNCHANNEL, local->channel); | ||
| 1114 | if (ret) { | ||
| 1115 | printk("%s: Channel setting to %d failed\n", | ||
| 1116 | dev->name, local->channel); | ||
| 1117 | goto fail; | ||
| 1118 | } | ||
| 1119 | |||
| 1120 | ret = hostap_set_word(dev, HFA384X_RID_CNFBEACONINT, | ||
| 1121 | local->beacon_int); | ||
| 1122 | if (ret) { | ||
| 1123 | printk("%s: Beacon interval setting to %d failed\n", | ||
| 1124 | dev->name, local->beacon_int); | ||
| 1125 | /* this may fail with Symbol/Lucent firmware */ | ||
| 1126 | if (ret == -ETIMEDOUT) | ||
| 1127 | goto fail; | ||
| 1128 | } | ||
| 1129 | |||
| 1130 | ret = hostap_set_word(dev, HFA384X_RID_CNFOWNDTIMPERIOD, | ||
| 1131 | local->dtim_period); | ||
| 1132 | if (ret) { | ||
| 1133 | printk("%s: DTIM period setting to %d failed\n", | ||
| 1134 | dev->name, local->dtim_period); | ||
| 1135 | /* this may fail with Symbol/Lucent firmware */ | ||
| 1136 | if (ret == -ETIMEDOUT) | ||
| 1137 | goto fail; | ||
| 1138 | } | ||
| 1139 | |||
| 1140 | ret = hostap_set_word(dev, HFA384X_RID_PROMISCUOUSMODE, | ||
| 1141 | local->is_promisc); | ||
| 1142 | if (ret) | ||
| 1143 | printk(KERN_INFO "%s: Setting promiscuous mode (%d) failed\n", | ||
| 1144 | dev->name, local->is_promisc); | ||
| 1145 | |||
| 1146 | if (!local->fw_ap) { | ||
| 1147 | ret = hostap_set_string(dev, HFA384X_RID_CNFDESIREDSSID, | ||
| 1148 | local->essid); | ||
| 1149 | if (ret) { | ||
| 1150 | printk("%s: Desired SSID setting failed\n", dev->name); | ||
| 1151 | goto fail; | ||
| 1152 | } | ||
| 1153 | } | ||
| 1154 | |||
| 1155 | /* Setup TXRateControl, defaults to allow use of 1, 2, 5.5, and | ||
| 1156 | * 11 Mbps in automatic TX rate fallback and 1 and 2 Mbps as basic | ||
| 1157 | * rates */ | ||
| 1158 | if (local->tx_rate_control == 0) { | ||
| 1159 | local->tx_rate_control = | ||
| 1160 | HFA384X_RATES_1MBPS | | ||
| 1161 | HFA384X_RATES_2MBPS | | ||
| 1162 | HFA384X_RATES_5MBPS | | ||
| 1163 | HFA384X_RATES_11MBPS; | ||
| 1164 | } | ||
| 1165 | if (local->basic_rates == 0) | ||
| 1166 | local->basic_rates = HFA384X_RATES_1MBPS | HFA384X_RATES_2MBPS; | ||
| 1167 | |||
| 1168 | if (!local->fw_ap) { | ||
| 1169 | ret = hostap_set_word(dev, HFA384X_RID_TXRATECONTROL, | ||
| 1170 | local->tx_rate_control); | ||
| 1171 | if (ret) { | ||
| 1172 | printk("%s: TXRateControl setting to %d failed\n", | ||
| 1173 | dev->name, local->tx_rate_control); | ||
| 1174 | goto fail; | ||
| 1175 | } | ||
| 1176 | |||
| 1177 | ret = hostap_set_word(dev, HFA384X_RID_CNFSUPPORTEDRATES, | ||
| 1178 | local->tx_rate_control); | ||
| 1179 | if (ret) { | ||
| 1180 | printk("%s: cnfSupportedRates setting to %d failed\n", | ||
| 1181 | dev->name, local->tx_rate_control); | ||
| 1182 | } | ||
| 1183 | |||
| 1184 | ret = hostap_set_word(dev, HFA384X_RID_CNFBASICRATES, | ||
| 1185 | local->basic_rates); | ||
| 1186 | if (ret) { | ||
| 1187 | printk("%s: cnfBasicRates setting to %d failed\n", | ||
| 1188 | dev->name, local->basic_rates); | ||
| 1189 | } | ||
| 1190 | |||
| 1191 | ret = hostap_set_word(dev, HFA384X_RID_CREATEIBSS, 1); | ||
| 1192 | if (ret) { | ||
| 1193 | printk("%s: Create IBSS setting to 1 failed\n", | ||
| 1194 | dev->name); | ||
| 1195 | } | ||
| 1196 | } | ||
| 1197 | |||
| 1198 | if (local->name_set) | ||
| 1199 | (void) hostap_set_string(dev, HFA384X_RID_CNFOWNNAME, | ||
| 1200 | local->name); | ||
| 1201 | |||
| 1202 | if (hostap_set_encryption(local)) { | ||
| 1203 | printk(KERN_INFO "%s: could not configure encryption\n", | ||
| 1204 | dev->name); | ||
| 1205 | } | ||
| 1206 | |||
| 1207 | (void) hostap_set_antsel(local); | ||
| 1208 | |||
| 1209 | if (hostap_set_roaming(local)) { | ||
| 1210 | printk(KERN_INFO "%s: could not set host roaming\n", | ||
| 1211 | dev->name); | ||
| 1212 | } | ||
| 1213 | |||
| 1214 | if (local->sta_fw_ver >= PRISM2_FW_VER(1,6,3) && | ||
| 1215 | hostap_set_word(dev, HFA384X_RID_CNFENHSECURITY, local->enh_sec)) | ||
| 1216 | printk(KERN_INFO "%s: cnfEnhSecurity setting to 0x%x failed\n", | ||
| 1217 | dev->name, local->enh_sec); | ||
| 1218 | |||
| 1219 | /* 32-bit tallies were added in STA f/w 0.8.0, but they were apparently | ||
| 1220 | * not working correctly (last seven counters report bogus values). | ||
| 1221 | * This has been fixed in 0.8.2, so enable 32-bit tallies only | ||
| 1222 | * beginning with that firmware version. Another bug fix for 32-bit | ||
| 1223 | * tallies in 1.4.0; should 16-bit tallies be used for some other | ||
| 1224 | * versions, too? */ | ||
| 1225 | if (local->sta_fw_ver >= PRISM2_FW_VER(0,8,2)) { | ||
| 1226 | if (hostap_set_word(dev, HFA384X_RID_CNFTHIRTY2TALLY, 1)) { | ||
| 1227 | printk(KERN_INFO "%s: cnfThirty2Tally setting " | ||
| 1228 | "failed\n", dev->name); | ||
| 1229 | local->tallies32 = 0; | ||
| 1230 | } else | ||
| 1231 | local->tallies32 = 1; | ||
| 1232 | } else | ||
| 1233 | local->tallies32 = 0; | ||
| 1234 | |||
| 1235 | hostap_set_auth_algs(local); | ||
| 1236 | |||
| 1237 | if (hostap_set_word(dev, HFA384X_RID_FRAGMENTATIONTHRESHOLD, | ||
| 1238 | local->fragm_threshold)) { | ||
| 1239 | printk(KERN_INFO "%s: setting FragmentationThreshold to %d " | ||
| 1240 | "failed\n", dev->name, local->fragm_threshold); | ||
| 1241 | } | ||
| 1242 | |||
| 1243 | if (hostap_set_word(dev, HFA384X_RID_RTSTHRESHOLD, | ||
| 1244 | local->rts_threshold)) { | ||
| 1245 | printk(KERN_INFO "%s: setting RTSThreshold to %d failed\n", | ||
| 1246 | dev->name, local->rts_threshold); | ||
| 1247 | } | ||
| 1248 | |||
| 1249 | if (local->manual_retry_count >= 0 && | ||
| 1250 | hostap_set_word(dev, HFA384X_RID_CNFALTRETRYCOUNT, | ||
| 1251 | local->manual_retry_count)) { | ||
| 1252 | printk(KERN_INFO "%s: setting cnfAltRetryCount to %d failed\n", | ||
| 1253 | dev->name, local->manual_retry_count); | ||
| 1254 | } | ||
| 1255 | |||
| 1256 | if (local->sta_fw_ver >= PRISM2_FW_VER(1,3,1) && | ||
| 1257 | hfa384x_get_rid(dev, HFA384X_RID_CNFDBMADJUST, &tmp, 2, 1) == 2) { | ||
| 1258 | local->rssi_to_dBm = le16_to_cpu(tmp); | ||
| 1259 | } | ||
| 1260 | |||
| 1261 | if (local->sta_fw_ver >= PRISM2_FW_VER(1,7,0) && local->wpa && | ||
| 1262 | hostap_set_word(dev, HFA384X_RID_SSNHANDLINGMODE, 1)) { | ||
| 1263 | printk(KERN_INFO "%s: setting ssnHandlingMode to 1 failed\n", | ||
| 1264 | dev->name); | ||
| 1265 | } | ||
| 1266 | |||
| 1267 | if (local->sta_fw_ver >= PRISM2_FW_VER(1,7,0) && local->generic_elem && | ||
| 1268 | hfa384x_set_rid(dev, HFA384X_RID_GENERICELEMENT, | ||
| 1269 | local->generic_elem, local->generic_elem_len)) { | ||
| 1270 | printk(KERN_INFO "%s: setting genericElement failed\n", | ||
| 1271 | dev->name); | ||
| 1272 | } | ||
| 1273 | |||
| 1274 | fail: | ||
| 1275 | return ret; | ||
| 1276 | } | ||
| 1277 | |||
| 1278 | |||
| 1279 | static int prism2_hw_init(struct net_device *dev, int initial) | ||
| 1280 | { | ||
| 1281 | struct hostap_interface *iface; | ||
| 1282 | local_info_t *local; | ||
| 1283 | int ret, first = 1; | ||
| 1284 | unsigned long start, delay; | ||
| 1285 | |||
| 1286 | PDEBUG(DEBUG_FLOW, "prism2_hw_init()\n"); | ||
| 1287 | |||
| 1288 | iface = netdev_priv(dev); | ||
| 1289 | local = iface->local; | ||
| 1290 | |||
| 1291 | clear_bit(HOSTAP_BITS_TRANSMIT, &local->bits); | ||
| 1292 | |||
| 1293 | init: | ||
| 1294 | /* initialize HFA 384x */ | ||
| 1295 | ret = hfa384x_cmd_no_wait(dev, HFA384X_CMDCODE_INIT, 0); | ||
| 1296 | if (ret) { | ||
| 1297 | printk(KERN_INFO "%s: first command failed - assuming card " | ||
| 1298 | "does not have primary firmware\n", dev_info); | ||
| 1299 | } | ||
| 1300 | |||
| 1301 | if (first && (HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_CMD)) { | ||
| 1302 | /* EvStat has Cmd bit set in some cases, so retry once if no | ||
| 1303 | * wait was needed */ | ||
| 1304 | HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF); | ||
| 1305 | printk(KERN_DEBUG "%s: init command completed too quickly - " | ||
| 1306 | "retrying\n", dev->name); | ||
| 1307 | first = 0; | ||
| 1308 | goto init; | ||
| 1309 | } | ||
| 1310 | |||
| 1311 | start = jiffies; | ||
| 1312 | delay = jiffies + HFA384X_INIT_TIMEOUT; | ||
| 1313 | while (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_CMD) && | ||
| 1314 | time_before(jiffies, delay)) | ||
| 1315 | yield(); | ||
| 1316 | if (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_CMD)) { | ||
| 1317 | printk(KERN_DEBUG "%s: assuming no Primary image in " | ||
| 1318 | "flash - card initialization not completed\n", | ||
| 1319 | dev_info); | ||
| 1320 | local->no_pri = 1; | ||
| 1321 | #ifdef PRISM2_DOWNLOAD_SUPPORT | ||
| 1322 | if (local->sram_type == -1) | ||
| 1323 | local->sram_type = prism2_get_ram_size(local); | ||
| 1324 | #endif /* PRISM2_DOWNLOAD_SUPPORT */ | ||
| 1325 | return 1; | ||
| 1326 | } | ||
| 1327 | local->no_pri = 0; | ||
| 1328 | printk(KERN_DEBUG "prism2_hw_init: initialized in %lu ms\n", | ||
| 1329 | (jiffies - start) * 1000 / HZ); | ||
| 1330 | HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF); | ||
| 1331 | return 0; | ||
| 1332 | } | ||
| 1333 | |||
| 1334 | |||
| 1335 | static int prism2_hw_init2(struct net_device *dev, int initial) | ||
| 1336 | { | ||
| 1337 | struct hostap_interface *iface; | ||
| 1338 | local_info_t *local; | ||
| 1339 | int i; | ||
| 1340 | |||
| 1341 | iface = netdev_priv(dev); | ||
| 1342 | local = iface->local; | ||
| 1343 | |||
| 1344 | #ifdef PRISM2_DOWNLOAD_SUPPORT | ||
| 1345 | kfree(local->pda); | ||
| 1346 | if (local->no_pri) | ||
| 1347 | local->pda = NULL; | ||
| 1348 | else | ||
| 1349 | local->pda = prism2_read_pda(dev); | ||
| 1350 | #endif /* PRISM2_DOWNLOAD_SUPPORT */ | ||
| 1351 | |||
| 1352 | hfa384x_disable_interrupts(dev); | ||
| 1353 | |||
| 1354 | #ifndef final_version | ||
| 1355 | HFA384X_OUTW(HFA384X_MAGIC, HFA384X_SWSUPPORT0_OFF); | ||
| 1356 | if (HFA384X_INW(HFA384X_SWSUPPORT0_OFF) != HFA384X_MAGIC) { | ||
| 1357 | printk("SWSUPPORT0 write/read failed: %04X != %04X\n", | ||
| 1358 | HFA384X_INW(HFA384X_SWSUPPORT0_OFF), HFA384X_MAGIC); | ||
| 1359 | goto failed; | ||
| 1360 | } | ||
| 1361 | #endif | ||
| 1362 | |||
| 1363 | if (initial || local->pri_only) { | ||
| 1364 | hfa384x_events_only_cmd(dev); | ||
| 1365 | /* get card version information */ | ||
| 1366 | if (prism2_get_version_info(dev, HFA384X_RID_NICID, "NIC") || | ||
| 1367 | prism2_get_version_info(dev, HFA384X_RID_PRIID, "PRI")) { | ||
| 1368 | hfa384x_disable_interrupts(dev); | ||
| 1369 | goto failed; | ||
| 1370 | } | ||
| 1371 | |||
| 1372 | if (prism2_get_version_info(dev, HFA384X_RID_STAID, "STA")) { | ||
| 1373 | printk(KERN_DEBUG "%s: Failed to read STA f/w version " | ||
| 1374 | "- only Primary f/w present\n", dev->name); | ||
| 1375 | local->pri_only = 1; | ||
| 1376 | return 0; | ||
| 1377 | } | ||
| 1378 | local->pri_only = 0; | ||
| 1379 | hfa384x_disable_interrupts(dev); | ||
| 1380 | } | ||
| 1381 | |||
| 1382 | /* FIX: could convert allocate_fid to use sleeping CmdCompl wait and | ||
| 1383 | * enable interrupts before this. This would also require some sort of | ||
| 1384 | * sleeping AllocEv waiting */ | ||
| 1385 | |||
| 1386 | /* allocate TX FIDs */ | ||
| 1387 | local->txfid_len = PRISM2_TXFID_LEN; | ||
| 1388 | for (i = 0; i < PRISM2_TXFID_COUNT; i++) { | ||
| 1389 | local->txfid[i] = hfa384x_allocate_fid(dev, local->txfid_len); | ||
| 1390 | if (local->txfid[i] == 0xffff && local->txfid_len > 1600) { | ||
| 1391 | local->txfid[i] = hfa384x_allocate_fid(dev, 1600); | ||
| 1392 | if (local->txfid[i] != 0xffff) { | ||
| 1393 | printk(KERN_DEBUG "%s: Using shorter TX FID " | ||
| 1394 | "(1600 bytes)\n", dev->name); | ||
| 1395 | local->txfid_len = 1600; | ||
| 1396 | } | ||
| 1397 | } | ||
| 1398 | if (local->txfid[i] == 0xffff) | ||
| 1399 | goto failed; | ||
| 1400 | local->intransmitfid[i] = PRISM2_TXFID_EMPTY; | ||
| 1401 | } | ||
| 1402 | |||
| 1403 | hfa384x_events_only_cmd(dev); | ||
| 1404 | |||
| 1405 | if (initial) { | ||
| 1406 | u8 addr[ETH_ALEN] = {}; | ||
| 1407 | struct list_head *ptr; | ||
| 1408 | |||
| 1409 | prism2_check_sta_fw_version(local); | ||
| 1410 | |||
| 1411 | if (hfa384x_get_rid(dev, HFA384X_RID_CNFOWNMACADDR, | ||
| 1412 | addr, ETH_ALEN, 1) < 0) { | ||
| 1413 | printk("%s: could not get own MAC address\n", | ||
| 1414 | dev->name); | ||
| 1415 | } | ||
| 1416 | eth_hw_addr_set(dev, addr); | ||
| 1417 | list_for_each(ptr, &local->hostap_interfaces) { | ||
| 1418 | iface = list_entry(ptr, struct hostap_interface, list); | ||
| 1419 | eth_hw_addr_inherit(iface->dev, dev); | ||
| 1420 | } | ||
| 1421 | } else if (local->fw_ap) | ||
| 1422 | prism2_check_sta_fw_version(local); | ||
| 1423 | |||
| 1424 | prism2_setup_rids(dev); | ||
| 1425 | |||
| 1426 | /* MAC is now configured, but port 0 is not yet enabled */ | ||
| 1427 | return 0; | ||
| 1428 | |||
| 1429 | failed: | ||
| 1430 | if (!local->no_pri) | ||
| 1431 | printk(KERN_WARNING "%s: Initialization failed\n", dev_info); | ||
| 1432 | return 1; | ||
| 1433 | } | ||
| 1434 | |||
| 1435 | |||
| 1436 | static int prism2_hw_enable(struct net_device *dev, int initial) | ||
| 1437 | { | ||
| 1438 | struct hostap_interface *iface; | ||
| 1439 | local_info_t *local; | ||
| 1440 | int was_resetting; | ||
| 1441 | |||
| 1442 | iface = netdev_priv(dev); | ||
| 1443 | local = iface->local; | ||
| 1444 | was_resetting = local->hw_resetting; | ||
| 1445 | |||
| 1446 | if (hfa384x_cmd(dev, HFA384X_CMDCODE_ENABLE, 0, NULL, NULL)) { | ||
| 1447 | printk("%s: MAC port 0 enabling failed\n", dev->name); | ||
| 1448 | return 1; | ||
| 1449 | } | ||
| 1450 | |||
| 1451 | local->hw_ready = 1; | ||
| 1452 | local->hw_reset_tries = 0; | ||
| 1453 | local->hw_resetting = 0; | ||
| 1454 | hfa384x_enable_interrupts(dev); | ||
| 1455 | |||
| 1456 | /* at least D-Link DWL-650 seems to require additional port reset | ||
| 1457 | * before it starts acting as an AP, so reset port automatically | ||
| 1458 | * here just in case */ | ||
| 1459 | if (initial && prism2_reset_port(dev)) { | ||
| 1460 | printk("%s: MAC port 0 resetting failed\n", dev->name); | ||
| 1461 | return 1; | ||
| 1462 | } | ||
| 1463 | |||
| 1464 | if (was_resetting && netif_queue_stopped(dev)) { | ||
| 1465 | /* If hw_reset() was called during pending transmit, netif | ||
| 1466 | * queue was stopped. Wake it up now since the wlan card has | ||
| 1467 | * been resetted. */ | ||
| 1468 | netif_wake_queue(dev); | ||
| 1469 | } | ||
| 1470 | |||
| 1471 | return 0; | ||
| 1472 | } | ||
| 1473 | |||
| 1474 | |||
| 1475 | static int prism2_hw_config(struct net_device *dev, int initial) | ||
| 1476 | { | ||
| 1477 | struct hostap_interface *iface; | ||
| 1478 | local_info_t *local; | ||
| 1479 | |||
| 1480 | iface = netdev_priv(dev); | ||
| 1481 | local = iface->local; | ||
| 1482 | |||
| 1483 | if (local->hw_downloading) | ||
| 1484 | return 1; | ||
| 1485 | |||
| 1486 | if (prism2_hw_init(dev, initial)) { | ||
| 1487 | return local->no_pri ? 0 : 1; | ||
| 1488 | } | ||
| 1489 | |||
| 1490 | if (prism2_hw_init2(dev, initial)) | ||
| 1491 | return 1; | ||
| 1492 | |||
| 1493 | /* Enable firmware if secondary image is loaded and at least one of the | ||
| 1494 | * netdevices is up. */ | ||
| 1495 | if (!local->pri_only && | ||
| 1496 | (initial == 0 || (initial == 2 && local->num_dev_open > 0))) { | ||
| 1497 | if (!local->dev_enabled) | ||
| 1498 | prism2_callback(local, PRISM2_CALLBACK_ENABLE); | ||
| 1499 | local->dev_enabled = 1; | ||
| 1500 | return prism2_hw_enable(dev, initial); | ||
| 1501 | } | ||
| 1502 | |||
| 1503 | return 0; | ||
| 1504 | } | ||
| 1505 | |||
| 1506 | |||
| 1507 | static void prism2_hw_shutdown(struct net_device *dev, int no_disable) | ||
| 1508 | { | ||
| 1509 | struct hostap_interface *iface; | ||
| 1510 | local_info_t *local; | ||
| 1511 | |||
| 1512 | iface = netdev_priv(dev); | ||
| 1513 | local = iface->local; | ||
| 1514 | |||
| 1515 | /* Allow only command completion events during disable */ | ||
| 1516 | hfa384x_events_only_cmd(dev); | ||
| 1517 | |||
| 1518 | local->hw_ready = 0; | ||
| 1519 | if (local->dev_enabled) | ||
| 1520 | prism2_callback(local, PRISM2_CALLBACK_DISABLE); | ||
| 1521 | local->dev_enabled = 0; | ||
| 1522 | |||
| 1523 | if (local->func->card_present && !local->func->card_present(local)) { | ||
| 1524 | printk(KERN_DEBUG "%s: card already removed or not configured " | ||
| 1525 | "during shutdown\n", dev->name); | ||
| 1526 | return; | ||
| 1527 | } | ||
| 1528 | |||
| 1529 | if ((no_disable & HOSTAP_HW_NO_DISABLE) == 0 && | ||
| 1530 | hfa384x_cmd(dev, HFA384X_CMDCODE_DISABLE, 0, NULL, NULL)) | ||
| 1531 | printk(KERN_WARNING "%s: Shutdown failed\n", dev_info); | ||
| 1532 | |||
| 1533 | hfa384x_disable_interrupts(dev); | ||
| 1534 | |||
| 1535 | if (no_disable & HOSTAP_HW_ENABLE_CMDCOMPL) | ||
| 1536 | hfa384x_events_only_cmd(dev); | ||
| 1537 | else | ||
| 1538 | prism2_clear_cmd_queue(local); | ||
| 1539 | } | ||
| 1540 | |||
| 1541 | |||
| 1542 | static void prism2_hw_reset(struct net_device *dev) | ||
| 1543 | { | ||
| 1544 | struct hostap_interface *iface; | ||
| 1545 | local_info_t *local; | ||
| 1546 | |||
| 1547 | #if 0 | ||
| 1548 | static long last_reset = 0; | ||
| 1549 | |||
| 1550 | /* do not reset card more than once per second to avoid ending up in a | ||
| 1551 | * busy loop resetting the card */ | ||
| 1552 | if (time_before_eq(jiffies, last_reset + HZ)) | ||
| 1553 | return; | ||
| 1554 | last_reset = jiffies; | ||
| 1555 | #endif | ||
| 1556 | |||
| 1557 | iface = netdev_priv(dev); | ||
| 1558 | local = iface->local; | ||
| 1559 | |||
| 1560 | if (local->hw_downloading) | ||
| 1561 | return; | ||
| 1562 | |||
| 1563 | if (local->hw_resetting) { | ||
| 1564 | printk(KERN_WARNING "%s: %s: already resetting card - " | ||
| 1565 | "ignoring reset request\n", dev_info, dev->name); | ||
| 1566 | return; | ||
| 1567 | } | ||
| 1568 | |||
| 1569 | local->hw_reset_tries++; | ||
| 1570 | if (local->hw_reset_tries > 10) { | ||
| 1571 | printk(KERN_WARNING "%s: too many reset tries, skipping\n", | ||
| 1572 | dev->name); | ||
| 1573 | return; | ||
| 1574 | } | ||
| 1575 | |||
| 1576 | printk(KERN_WARNING "%s: %s: resetting card\n", dev_info, dev->name); | ||
| 1577 | hfa384x_disable_interrupts(dev); | ||
| 1578 | local->hw_resetting = 1; | ||
| 1579 | if (local->func->cor_sreset) { | ||
| 1580 | /* Host system seems to hang in some cases with high traffic | ||
| 1581 | * load or shared interrupts during COR sreset. Disable shared | ||
| 1582 | * interrupts during reset to avoid these crashes. COS sreset | ||
| 1583 | * takes quite a long time, so it is unfortunate that this | ||
| 1584 | * seems to be needed. Anyway, I do not know of any better way | ||
| 1585 | * of avoiding the crash. */ | ||
| 1586 | disable_irq(dev->irq); | ||
| 1587 | local->func->cor_sreset(local); | ||
| 1588 | enable_irq(dev->irq); | ||
| 1589 | } | ||
| 1590 | prism2_hw_shutdown(dev, 1); | ||
| 1591 | prism2_hw_config(dev, 0); | ||
| 1592 | local->hw_resetting = 0; | ||
| 1593 | |||
| 1594 | #ifdef PRISM2_DOWNLOAD_SUPPORT | ||
| 1595 | if (local->dl_pri) { | ||
| 1596 | printk(KERN_DEBUG "%s: persistent download of primary " | ||
| 1597 | "firmware\n", dev->name); | ||
| 1598 | if (prism2_download_genesis(local, local->dl_pri) < 0) | ||
| 1599 | printk(KERN_WARNING "%s: download (PRI) failed\n", | ||
| 1600 | dev->name); | ||
| 1601 | } | ||
| 1602 | |||
| 1603 | if (local->dl_sec) { | ||
| 1604 | printk(KERN_DEBUG "%s: persistent download of secondary " | ||
| 1605 | "firmware\n", dev->name); | ||
| 1606 | if (prism2_download_volatile(local, local->dl_sec) < 0) | ||
| 1607 | printk(KERN_WARNING "%s: download (SEC) failed\n", | ||
| 1608 | dev->name); | ||
| 1609 | } | ||
| 1610 | #endif /* PRISM2_DOWNLOAD_SUPPORT */ | ||
| 1611 | |||
| 1612 | /* TODO: restore beacon TIM bits for STAs that have buffered frames */ | ||
| 1613 | } | ||
| 1614 | |||
| 1615 | |||
| 1616 | static void prism2_schedule_reset(local_info_t *local) | ||
| 1617 | { | ||
| 1618 | schedule_work(&local->reset_queue); | ||
| 1619 | } | ||
| 1620 | |||
| 1621 | |||
| 1622 | /* Called only as scheduled task after noticing card timeout in interrupt | ||
| 1623 | * context */ | ||
| 1624 | static void handle_reset_queue(struct work_struct *work) | ||
| 1625 | { | ||
| 1626 | local_info_t *local = container_of(work, local_info_t, reset_queue); | ||
| 1627 | |||
| 1628 | printk(KERN_DEBUG "%s: scheduled card reset\n", local->dev->name); | ||
| 1629 | prism2_hw_reset(local->dev); | ||
| 1630 | |||
| 1631 | if (netif_queue_stopped(local->dev)) { | ||
| 1632 | int i; | ||
| 1633 | |||
| 1634 | for (i = 0; i < PRISM2_TXFID_COUNT; i++) | ||
| 1635 | if (local->intransmitfid[i] == PRISM2_TXFID_EMPTY) { | ||
| 1636 | PDEBUG(DEBUG_EXTRA, "prism2_tx_timeout: " | ||
| 1637 | "wake up queue\n"); | ||
| 1638 | netif_wake_queue(local->dev); | ||
| 1639 | break; | ||
| 1640 | } | ||
| 1641 | } | ||
| 1642 | } | ||
| 1643 | |||
| 1644 | |||
| 1645 | static int prism2_get_txfid_idx(local_info_t *local) | ||
| 1646 | { | ||
| 1647 | int idx, end; | ||
| 1648 | unsigned long flags; | ||
| 1649 | |||
| 1650 | spin_lock_irqsave(&local->txfidlock, flags); | ||
| 1651 | end = idx = local->next_txfid; | ||
| 1652 | do { | ||
| 1653 | if (local->intransmitfid[idx] == PRISM2_TXFID_EMPTY) { | ||
| 1654 | local->intransmitfid[idx] = PRISM2_TXFID_RESERVED; | ||
| 1655 | spin_unlock_irqrestore(&local->txfidlock, flags); | ||
| 1656 | return idx; | ||
| 1657 | } | ||
| 1658 | idx++; | ||
| 1659 | if (idx >= PRISM2_TXFID_COUNT) | ||
| 1660 | idx = 0; | ||
| 1661 | } while (idx != end); | ||
| 1662 | spin_unlock_irqrestore(&local->txfidlock, flags); | ||
| 1663 | |||
| 1664 | PDEBUG(DEBUG_EXTRA2, "prism2_get_txfid_idx: no room in txfid buf: " | ||
| 1665 | "packet dropped\n"); | ||
| 1666 | local->dev->stats.tx_dropped++; | ||
| 1667 | |||
| 1668 | return -1; | ||
| 1669 | } | ||
| 1670 | |||
| 1671 | |||
| 1672 | /* Called only from hardware IRQ */ | ||
| 1673 | static void prism2_transmit_cb(struct net_device *dev, long context, | ||
| 1674 | u16 resp0, u16 res) | ||
| 1675 | { | ||
| 1676 | struct hostap_interface *iface; | ||
| 1677 | local_info_t *local; | ||
| 1678 | int idx = (int) context; | ||
| 1679 | |||
| 1680 | iface = netdev_priv(dev); | ||
| 1681 | local = iface->local; | ||
| 1682 | |||
| 1683 | if (res) { | ||
| 1684 | printk(KERN_DEBUG "%s: prism2_transmit_cb - res=0x%02x\n", | ||
| 1685 | dev->name, res); | ||
| 1686 | return; | ||
| 1687 | } | ||
| 1688 | |||
| 1689 | if (idx < 0 || idx >= PRISM2_TXFID_COUNT) { | ||
| 1690 | printk(KERN_DEBUG "%s: prism2_transmit_cb called with invalid " | ||
| 1691 | "idx=%d\n", dev->name, idx); | ||
| 1692 | return; | ||
| 1693 | } | ||
| 1694 | |||
| 1695 | if (!test_and_clear_bit(HOSTAP_BITS_TRANSMIT, &local->bits)) { | ||
| 1696 | printk(KERN_DEBUG "%s: driver bug: prism2_transmit_cb called " | ||
| 1697 | "with no pending transmit\n", dev->name); | ||
| 1698 | } | ||
| 1699 | |||
| 1700 | if (netif_queue_stopped(dev)) { | ||
| 1701 | /* ready for next TX, so wake up queue that was stopped in | ||
| 1702 | * prism2_transmit() */ | ||
| 1703 | netif_wake_queue(dev); | ||
| 1704 | } | ||
| 1705 | |||
| 1706 | spin_lock(&local->txfidlock); | ||
| 1707 | |||
| 1708 | /* With reclaim, Resp0 contains new txfid for transmit; the old txfid | ||
| 1709 | * will be automatically allocated for the next TX frame */ | ||
| 1710 | local->intransmitfid[idx] = resp0; | ||
| 1711 | |||
| 1712 | PDEBUG(DEBUG_FID, "%s: prism2_transmit_cb: txfid[%d]=0x%04x, " | ||
| 1713 | "resp0=0x%04x, transmit_txfid=0x%04x\n", | ||
| 1714 | dev->name, idx, local->txfid[idx], | ||
| 1715 | resp0, local->intransmitfid[local->next_txfid]); | ||
| 1716 | |||
| 1717 | idx++; | ||
| 1718 | if (idx >= PRISM2_TXFID_COUNT) | ||
| 1719 | idx = 0; | ||
| 1720 | local->next_txfid = idx; | ||
| 1721 | |||
| 1722 | /* check if all TX buffers are occupied */ | ||
| 1723 | do { | ||
| 1724 | if (local->intransmitfid[idx] == PRISM2_TXFID_EMPTY) { | ||
| 1725 | spin_unlock(&local->txfidlock); | ||
| 1726 | return; | ||
| 1727 | } | ||
| 1728 | idx++; | ||
| 1729 | if (idx >= PRISM2_TXFID_COUNT) | ||
| 1730 | idx = 0; | ||
| 1731 | } while (idx != local->next_txfid); | ||
| 1732 | spin_unlock(&local->txfidlock); | ||
| 1733 | |||
| 1734 | /* no empty TX buffers, stop queue */ | ||
| 1735 | netif_stop_queue(dev); | ||
| 1736 | } | ||
| 1737 | |||
| 1738 | |||
| 1739 | /* Called only from software IRQ if PCI bus master is not used (with bus master | ||
| 1740 | * this can be called both from software and hardware IRQ) */ | ||
| 1741 | static int prism2_transmit(struct net_device *dev, int idx) | ||
| 1742 | { | ||
| 1743 | struct hostap_interface *iface; | ||
| 1744 | local_info_t *local; | ||
| 1745 | int res; | ||
| 1746 | |||
| 1747 | iface = netdev_priv(dev); | ||
| 1748 | local = iface->local; | ||
| 1749 | |||
| 1750 | /* The driver tries to stop netif queue so that there would not be | ||
| 1751 | * more than one attempt to transmit frames going on; check that this | ||
| 1752 | * is really the case */ | ||
| 1753 | |||
| 1754 | if (test_and_set_bit(HOSTAP_BITS_TRANSMIT, &local->bits)) { | ||
| 1755 | printk(KERN_DEBUG "%s: driver bug - prism2_transmit() called " | ||
| 1756 | "when previous TX was pending\n", dev->name); | ||
| 1757 | return -1; | ||
| 1758 | } | ||
| 1759 | |||
| 1760 | /* stop the queue for the time that transmit is pending */ | ||
| 1761 | netif_stop_queue(dev); | ||
| 1762 | |||
| 1763 | /* transmit packet */ | ||
| 1764 | res = hfa384x_cmd_callback( | ||
| 1765 | dev, | ||
| 1766 | HFA384X_CMDCODE_TRANSMIT | HFA384X_CMD_TX_RECLAIM, | ||
| 1767 | local->txfid[idx], | ||
| 1768 | prism2_transmit_cb, (long) idx); | ||
| 1769 | |||
| 1770 | if (res) { | ||
| 1771 | printk(KERN_DEBUG "%s: prism2_transmit: CMDCODE_TRANSMIT " | ||
| 1772 | "failed (res=%d)\n", dev->name, res); | ||
| 1773 | dev->stats.tx_dropped++; | ||
| 1774 | netif_wake_queue(dev); | ||
| 1775 | return -1; | ||
| 1776 | } | ||
| 1777 | netif_trans_update(dev); | ||
| 1778 | |||
| 1779 | /* Since we did not wait for command completion, the card continues | ||
| 1780 | * to process on the background and we will finish handling when | ||
| 1781 | * command completion event is handled (prism2_cmd_ev() function) */ | ||
| 1782 | |||
| 1783 | return 0; | ||
| 1784 | } | ||
| 1785 | |||
| 1786 | |||
| 1787 | /* Send IEEE 802.11 frame (convert the header into Prism2 TX descriptor and | ||
| 1788 | * send the payload with this descriptor) */ | ||
| 1789 | /* Called only from software IRQ */ | ||
| 1790 | static int prism2_tx_80211(struct sk_buff *skb, struct net_device *dev) | ||
| 1791 | { | ||
| 1792 | struct hostap_interface *iface; | ||
| 1793 | local_info_t *local; | ||
| 1794 | struct hfa384x_tx_frame txdesc; | ||
| 1795 | struct hostap_skb_tx_data *meta; | ||
| 1796 | int hdr_len, data_len, idx, res, ret = -1; | ||
| 1797 | u16 tx_control; | ||
| 1798 | |||
| 1799 | iface = netdev_priv(dev); | ||
| 1800 | local = iface->local; | ||
| 1801 | |||
| 1802 | meta = (struct hostap_skb_tx_data *) skb->cb; | ||
| 1803 | |||
| 1804 | prism2_callback(local, PRISM2_CALLBACK_TX_START); | ||
| 1805 | |||
| 1806 | if ((local->func->card_present && !local->func->card_present(local)) || | ||
| 1807 | !local->hw_ready || local->hw_downloading || local->pri_only) { | ||
| 1808 | if (net_ratelimit()) { | ||
| 1809 | printk(KERN_DEBUG "%s: prism2_tx_80211: hw not ready -" | ||
| 1810 | " skipping\n", dev->name); | ||
| 1811 | } | ||
| 1812 | goto fail; | ||
| 1813 | } | ||
| 1814 | |||
| 1815 | memset(&txdesc, 0, sizeof(txdesc)); | ||
| 1816 | |||
| 1817 | /* skb->data starts with txdesc->frame_control */ | ||
| 1818 | hdr_len = sizeof(txdesc.header); | ||
| 1819 | BUILD_BUG_ON(hdr_len != 24); | ||
| 1820 | skb_copy_from_linear_data(skb, &txdesc.header, hdr_len); | ||
| 1821 | if (ieee80211_is_data(txdesc.frame_control) && | ||
| 1822 | ieee80211_has_a4(txdesc.frame_control) && | ||
| 1823 | skb->len >= 30) { | ||
| 1824 | /* Addr4 */ | ||
| 1825 | skb_copy_from_linear_data_offset(skb, hdr_len, txdesc.addr4, | ||
| 1826 | ETH_ALEN); | ||
| 1827 | hdr_len += ETH_ALEN; | ||
| 1828 | } | ||
| 1829 | |||
| 1830 | tx_control = local->tx_control; | ||
| 1831 | if (meta->tx_cb_idx) { | ||
| 1832 | tx_control |= HFA384X_TX_CTRL_TX_OK; | ||
| 1833 | txdesc.sw_support = cpu_to_le32(meta->tx_cb_idx); | ||
| 1834 | } | ||
| 1835 | txdesc.tx_control = cpu_to_le16(tx_control); | ||
| 1836 | txdesc.tx_rate = meta->rate; | ||
| 1837 | |||
| 1838 | data_len = skb->len - hdr_len; | ||
| 1839 | txdesc.data_len = cpu_to_le16(data_len); | ||
| 1840 | txdesc.len = cpu_to_be16(data_len); | ||
| 1841 | |||
| 1842 | idx = prism2_get_txfid_idx(local); | ||
| 1843 | if (idx < 0) | ||
| 1844 | goto fail; | ||
| 1845 | |||
| 1846 | if (local->frame_dump & PRISM2_DUMP_TX_HDR) | ||
| 1847 | hostap_dump_tx_header(dev->name, &txdesc); | ||
| 1848 | |||
| 1849 | spin_lock(&local->baplock); | ||
| 1850 | res = hfa384x_setup_bap(dev, BAP0, local->txfid[idx], 0); | ||
| 1851 | |||
| 1852 | if (!res) | ||
| 1853 | res = hfa384x_to_bap(dev, BAP0, &txdesc, sizeof(txdesc)); | ||
| 1854 | if (!res) | ||
| 1855 | res = hfa384x_to_bap(dev, BAP0, skb->data + hdr_len, | ||
| 1856 | skb->len - hdr_len); | ||
| 1857 | spin_unlock(&local->baplock); | ||
| 1858 | |||
| 1859 | if (!res) | ||
| 1860 | res = prism2_transmit(dev, idx); | ||
| 1861 | if (res) { | ||
| 1862 | printk(KERN_DEBUG "%s: prism2_tx_80211 - to BAP0 failed\n", | ||
| 1863 | dev->name); | ||
| 1864 | local->intransmitfid[idx] = PRISM2_TXFID_EMPTY; | ||
| 1865 | schedule_work(&local->reset_queue); | ||
| 1866 | goto fail; | ||
| 1867 | } | ||
| 1868 | |||
| 1869 | ret = 0; | ||
| 1870 | |||
| 1871 | fail: | ||
| 1872 | prism2_callback(local, PRISM2_CALLBACK_TX_END); | ||
| 1873 | return ret; | ||
| 1874 | } | ||
| 1875 | |||
| 1876 | |||
| 1877 | /* Some SMP systems have reported number of odd errors with hostap_pci. fid | ||
| 1878 | * register has changed values between consecutive reads for an unknown reason. | ||
| 1879 | * This should really not happen, so more debugging is needed. This test | ||
| 1880 | * version is a bit slower, but it will detect most of such register changes | ||
| 1881 | * and will try to get the correct fid eventually. */ | ||
| 1882 | #define EXTRA_FID_READ_TESTS | ||
| 1883 | |||
| 1884 | static u16 prism2_read_fid_reg(struct net_device *dev, u16 reg) | ||
| 1885 | { | ||
| 1886 | #ifdef EXTRA_FID_READ_TESTS | ||
| 1887 | u16 val, val2, val3; | ||
| 1888 | int i; | ||
| 1889 | |||
| 1890 | for (i = 0; i < 10; i++) { | ||
| 1891 | val = HFA384X_INW(reg); | ||
| 1892 | val2 = HFA384X_INW(reg); | ||
| 1893 | val3 = HFA384X_INW(reg); | ||
| 1894 | |||
| 1895 | if (val == val2 && val == val3) | ||
| 1896 | return val; | ||
| 1897 | |||
1490 more lines aren’t shown here.
This diff is large: its 510 lines aren’t shown here.
This diff is large: its 3848 lines aren’t shown here.
This diff is large: its 1124 lines aren’t shown here.
This diff is large: its 446 lines aren’t shown here.
This diff is large: its 618 lines aren’t shown here.
This diff is large: its 412 lines aren’t shown here.
This diff is large: its 1052 lines aren’t shown here.
This diff is large: its 144 lines aren’t shown here.
This diff is large: its 16 lines aren’t shown here.
This diff is large: its 269 lines aren’t shown here.
This diff is large: its 292 lines aren’t shown here.
This diff is large: its 16 lines aren’t shown here.
This diff is large: its 388 lines aren’t shown here.
This diff is large: its 22 lines aren’t shown here.
This diff is large: its 779 lines aren’t shown here.
This diff is large: its 535 lines aren’t shown here.
This diff is large: its 478 lines aren’t shown here.
This diff is large: its 53 lines aren’t shown here.
This diff is large: its 166 lines aren’t shown here.
This diff is large: its 1363 lines aren’t shown here.
This diff is large: its 61 lines aren’t shown here.
This diff is large: its 2415 lines aren’t shown here.
This diff is large: its 51 lines aren’t shown here.
This diff is large: its 90 lines aren’t shown here.
This diff is large: its 24 lines aren’t shown here.
This diff is large: its 252 lines aren’t shown here.
This diff is large: its 351 lines aren’t shown here.
This diff is large: its 315 lines aren’t shown here.
This diff is large: its 258 lines aren’t shown here.
This diff is large: its 55 lines aren’t shown here.
This diff is large: its 363 lines aren’t shown here.
This diff is large: its 238 lines aren’t shown here.
This diff is large: its 1788 lines aren’t shown here.
This diff is large: its 260 lines aren’t shown here.
This diff is large: its 22 lines aren’t shown here.
This diff is large: its 329 lines aren’t shown here.
This diff is large: its 1429 lines aren’t shown here.
This diff is large: its 14 lines aren’t shown here.
This diff is large: its 56 lines aren’t shown here.
This diff is large: its 7 lines aren’t shown here.
This diff is large: its 2825 lines aren’t shown here.
This diff is large: its 75 lines aren’t shown here.
This diff is large: its 735 lines aren’t shown here.
This diff is large: its 3761 lines aren’t shown here.
This diff is large: its 616 lines aren’t shown here.
This diff is large: its 2037 lines aren’t shown here.
This diff is large: its 24 lines aren’t shown here.
This diff is large: its 7 lines aren’t shown here.
This diff is large: its 958 lines aren’t shown here.
This diff is large: its 22 lines aren’t shown here.
This diff is large: its 70 lines aren’t shown here.
This diff is large: its 157 lines aren’t shown here.
This diff is large: its 97 lines aren’t shown here.
This diff is large: its 23 lines aren’t shown here.
This diff is large: its 14 lines aren’t shown here.
This diff is large: its 9 lines aren’t shown here.
This diff is large: its 145 lines aren’t shown here.
This diff is large: its 10 lines aren’t shown here.
This diff is large: its 24 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 29 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 10 lines aren’t shown here.
This diff is large: its 13 lines aren’t shown here.
This diff is large: its 10 lines aren’t shown here.
This diff is large: its 10 lines aren’t shown here.
This diff is large: its 65 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 10 lines aren’t shown here.
This diff is large: its 10 lines aren’t shown here.
This diff is large: its 10 lines aren’t shown here.
This diff is large: its 10 lines aren’t shown here.
This diff is large: its 10 lines aren’t shown here.
This diff is large: its 84 lines aren’t shown here.
This diff is large: its 12 lines aren’t shown here.
This diff is large: its 11 lines aren’t shown here.
This diff is large: its 107 lines aren’t shown here.
This diff is large: its 39 lines aren’t shown here.
This diff is large: its 30 lines aren’t shown here.
This diff is large: its 16 lines aren’t shown here.
This diff is large: its 181 lines aren’t shown here.
This diff is large: its 123 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.
This diff is large: its 39 lines aren’t shown here.
This diff is large: its 24 lines aren’t shown here.
This diff is large: its 421 lines aren’t shown here.
This diff is large: its 48 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 665 lines aren’t shown here.
This diff is large: its 631 lines aren’t shown here.
This diff is large: its 510 lines aren’t shown here.
This diff is large: its 23 lines aren’t shown here.
This diff is large: its 507 lines aren’t shown here.
This diff is large: its 270 lines aren’t shown here.
This diff is large: its 25 lines aren’t shown here.
This diff is large: its 24 lines aren’t shown here.
This diff is large: its 25 lines aren’t shown here.
This diff is large: its 32 lines aren’t shown here.
This diff is large: its 25 lines aren’t shown here.
This diff is large: its 32 lines aren’t shown here.
This diff is large: its 53 lines aren’t shown here.
This diff is large: its 28 lines aren’t shown here.
This diff is large: its 32 lines aren’t shown here.
This diff is large: its 364 lines aren’t shown here.
This diff is large: its 74 lines aren’t shown here.
This diff is large: its 89 lines aren’t shown here.
This diff is large: its 20 lines aren’t shown here.
This diff is large: its 20 lines aren’t shown here.
This diff is large: its 20 lines aren’t shown here.
This diff is large: its 26 lines aren’t shown here.
This diff is large: its 5 lines aren’t shown here.
This diff is large: its 1910 lines aren’t shown here.
This diff is large: its 145 lines aren’t shown here.
This diff is large: its 14 lines aren’t shown here.
This diff is large: its 17 lines aren’t shown here.
This diff is large: its 36 lines aren’t shown here.
This diff is large: its 69 lines aren’t shown here.
This diff is large: its 14 lines aren’t shown here.
This diff is large: its 8 lines aren’t shown here.