diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/Kconfig linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/Kconfig --- linux-2.6.11-rc4-mm1/drivers/net/wireless/Kconfig 2005-02-23 10:19:26.114821080 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/Kconfig 2005-02-24 13:06:17.665076520 -0800 @@ -357,6 +357,8 @@ source "drivers/net/wireless/hostap/Kconfig" +source "drivers/net/wireless/ath/Kconfig" + # yes, this works even when no drivers are selected config NET_WIRELESS bool diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/Kconfig.orig linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/Kconfig.orig --- linux-2.6.11-rc4-mm1/drivers/net/wireless/Kconfig.orig 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/Kconfig.orig 2005-02-23 10:19:26.000000000 -0800 @@ -0,0 +1,367 @@ +# +# Wireless LAN device configuration +# + +menu "Wireless LAN (non-hamradio)" + depends on NETDEVICES + +config NET_RADIO + bool "Wireless LAN drivers (non-hamradio) & Wireless Extensions" + ---help--- + Support for wireless LANs and everything having to do with radio, + but not with amateur radio or FM broadcasting. + + Saying Y here also enables the Wireless Extensions (creates + /proc/net/wireless and enables iwconfig access). The Wireless + Extension is a generic API allowing a driver to expose to the user + space configuration and statistics specific to common Wireless LANs. + The beauty of it is that a single set of tool can support all the + variations of Wireless LANs, regardless of their type (as long as + the driver supports Wireless Extension). Another advantage is that + these parameters may be changed on the fly without restarting the + driver (or Linux). If you wish to use Wireless Extensions with + wireless PCMCIA (PC-) cards, you need to say Y here; you can fetch + the tools from + . + + Some user-level drivers for scarab devices which don't require + special kernel support are available from + . + +# Note : the cards are obsolete (can't buy them anymore), but the drivers +# are not, as people are still using them... +comment "Obsolete Wireless cards support (pre-802.11)" + depends on NET_RADIO && (INET || ISA || PCMCIA) + +config STRIP + tristate "STRIP (Metricom starmode radio IP)" + depends on NET_RADIO && INET + ---help--- + Say Y if you have a Metricom radio and intend to use Starmode Radio + IP. STRIP is a radio protocol developed for the MosquitoNet project + (on the WWW at ) to send Internet + traffic using Metricom radios. Metricom radios are small, battery + powered, 100kbit/sec packet radio transceivers, about the size and + weight of a cellular telephone. (You may also have heard them called + "Metricom modems" but we avoid the term "modem" because it misleads + many people into thinking that you can plug a Metricom modem into a + phone line and use it as a modem.) + + You can use STRIP on any Linux machine with a serial port, although + it is obviously most useful for people with laptop computers. If you + think you might get a Metricom radio in the future, there is no harm + in saying Y to STRIP now, except that it makes the kernel a bit + bigger. + + To compile this as a module, choose M here: the module will be + called strip. + +config ARLAN + tristate "Aironet Arlan 655 & IC2200 DS support" + depends on NET_RADIO && ISA && !64BIT + ---help--- + Aironet makes Arlan, a class of wireless LAN adapters. These use the + www.Telxon.com chip, which is also used on several similar cards. + This driver is tested on the 655 and IC2200 series cards. Look at + for the latest information. + + The driver is built as two modules, arlan and arlan-proc. The latter + is the /proc interface and is not needed most of time. + + On some computers the card ends up in non-valid state after some + time. Use a ping-reset script to clear it. + +config WAVELAN + tristate "AT&T/Lucent old WaveLAN & DEC RoamAbout DS ISA support" + depends on NET_RADIO && ISA + ---help--- + The Lucent WaveLAN (formerly NCR and AT&T; or DEC RoamAbout DS) is + a Radio LAN (wireless Ethernet-like Local Area Network) using the + radio frequencies 900 MHz and 2.4 GHz. + + This driver support the ISA version of the WaveLAN card. A separate + driver for the PCMCIA (PC-card) hardware is available in David + Hinds' pcmcia-cs package (see the file + for location). + + If you want to use an ISA WaveLAN card under Linux, say Y and read + the Ethernet-HOWTO, available from + . Some more specific + information is contained in + and in the source code + . + + You will also need the wireless tools package available from + . + Please read the man pages contained therein. + + To compile this driver as a module, choose M here: the module will be + called wavelan. + +config PCMCIA_WAVELAN + tristate "AT&T/Lucent old WaveLAN Pcmcia wireless support" + depends on NET_RADIO && PCMCIA + help + Say Y here if you intend to attach an AT&T/Lucent Wavelan PCMCIA + (PC-card) wireless Ethernet networking card to your computer. This + driver is for the non-IEEE-802.11 Wavelan cards. + + To compile this driver as a module, choose M here: the module will be + called wavelan_cs. If unsure, say N. + +config PCMCIA_NETWAVE + tristate "Xircom Netwave AirSurfer Pcmcia wireless support" + depends on NET_RADIO && PCMCIA + help + Say Y here if you intend to attach this type of PCMCIA (PC-card) + wireless Ethernet networking card to your computer. + + To compile this driver as a module, choose M here: the module will be + called netwave_cs. If unsure, say N. + +comment "Wireless 802.11 Frequency Hopping cards support" + depends on NET_RADIO && PCMCIA + +config PCMCIA_RAYCS + tristate "Aviator/Raytheon 2.4MHz wireless support" + depends on NET_RADIO && PCMCIA + ---help--- + Say Y here if you intend to attach an Aviator/Raytheon PCMCIA + (PC-card) wireless Ethernet networking card to your computer. + Please read the file for + details. + + To compile this driver as a module, choose M here: the module will be + called ray_cs. If unsure, say N. + +comment "Wireless 802.11b ISA/PCI cards support" + depends on NET_RADIO && (ISA || PCI || PPC_PMAC || PCMCIA) + +config AIRO + tristate "Cisco/Aironet 34X/35X/4500/4800 ISA and PCI cards" + depends on NET_RADIO && ISA && (PCI || BROKEN) + ---help--- + This is the standard Linux driver to support Cisco/Aironet ISA and + PCI 802.11 wireless cards. + It supports the new 802.11b cards from Cisco (Cisco 34X, Cisco 35X + - with or without encryption) as well as card before the Cisco + aquisition (Aironet 4500, Aironet 4800, Aironet 4800B). + + This driver support both the standard Linux Wireless Extensions + and Cisco proprietary API, so both the Linux Wireless Tools and the + Cisco Linux utilities can be used to configure the card. + + The driver can be compiled as a module and will be named "airo". + +config HERMES + tristate "Hermes chipset 802.11b support (Orinoco/Prism2/Symbol)" + depends on NET_RADIO && (PPC_PMAC || PCI || PCMCIA) + ---help--- + A driver for 802.11b wireless cards based based on the "Hermes" or + Intersil HFA384x (Prism 2) MAC controller. This includes the vast + majority of the PCMCIA 802.11b cards (which are nearly all rebadges) + - except for the Cisco/Aironet cards. Cards supported include the + Apple Airport (not a PCMCIA card), WavelanIEEE/Orinoco, + Cabletron/EnteraSys Roamabout, ELSA AirLancer, MELCO Buffalo, Avaya, + IBM High Rate Wireless, Farralon Syyline, Samsung MagicLAN, Netgear + MA401, LinkSys WPC-11, D-Link DWL-650, 3Com AirConnect, Intel + PRO/Wireless, and Symbol Spectrum24 High Rate amongst others. + + This option includes the guts of the driver, but in order to + actually use a card you will also need to enable support for PCMCIA + Hermes cards, PLX9052 based PCI adaptors or the Apple Airport below. + + You will also very likely also need the Wireless Tools in order to + configure your card and that /etc/pcmcia/wireless.opts works : + + +config APPLE_AIRPORT + tristate "Apple Airport support (built-in)" + depends on PPC_PMAC && HERMES + help + Say Y here to support the Airport 802.11b wireless Ethernet hardware + built into the Macintosh iBook and other recent PowerPC-based + Macintosh machines. This is essentially a Lucent Orinoco card with + a non-standard interface + +config PLX_HERMES + tristate "Hermes in PLX9052 based PCI adaptor support (Netgear MA301 etc.) (EXPERIMENTAL)" + depends on PCI && HERMES && EXPERIMENTAL + help + Enable support for PCMCIA cards supported by the "Hermes" (aka + orinoco) driver when used in PLX9052 based PCI adaptors. These + adaptors are not a full PCMCIA controller but act as a more limited + PCI <-> PCMCIA bridge. Several vendors sell such adaptors so that + 802.11b PCMCIA cards can be used in desktop machines. The Netgear + MA301 is such an adaptor. + + Support for these adaptors is so far still incomplete and buggy. + You have been warned. + +config TMD_HERMES + tristate "Hermes in TMD7160 based PCI adaptor support (EXPERIMENTAL)" + depends on PCI && HERMES && EXPERIMENTAL + help + Enable support for PCMCIA cards supported by the "Hermes" (aka + orinoco) driver when used in TMD7160 based PCI adaptors. These + adaptors are not a full PCMCIA controller but act as a more limited + PCI <-> PCMCIA bridge. Several vendors sell such adaptors so that + 802.11b PCMCIA cards can be used in desktop machines. + + Support for these adaptors is so far still incomplete and buggy. + You have been warned. + +config PCI_HERMES + tristate "Prism 2.5 PCI 802.11b adaptor support (EXPERIMENTAL)" + depends on PCI && HERMES && EXPERIMENTAL + help + Enable support for PCI and mini-PCI 802.11b wireless NICs based on + the Prism 2.5 chipset. These are true PCI cards, not the 802.11b + PCMCIA cards bundled with PCI<->PCMCIA adaptors which are also + common. Some of the built-in wireless adaptors in laptops are of + this variety. + +config ATMEL + tristate "Atmel at76c50x chipset 802.11b support" + depends on NET_RADIO && EXPERIMENTAL + select FW_LOADER + select CRC32 + ---help--- + A driver 802.11b wireless cards based on the Atmel fast-vnet + chips. This driver supports standard Linux wireless extensions. + + Many cards based on this chipset do not have flash memory + and need their firmware loaded at start-up. If yours is + one of these, you will need to provide a firmware image + to be loaded into the card by the driver. The Atmel + firmware package can be downloaded from + + +config PCI_ATMEL + tristate "Atmel at76c506 PCI cards" + depends on ATMEL && PCI + ---help--- + Enable support for PCI and mini-PCI cards containing the + Atmel at76c506 chip. + +# If Pcmcia is compiled in, offer Pcmcia cards... +comment "Wireless 802.11b Pcmcia/Cardbus cards support" + depends on NET_RADIO && PCMCIA + +config PCMCIA_HERMES + tristate "Hermes PCMCIA card support" + depends on NET_RADIO && PCMCIA && HERMES + ---help--- + A driver for "Hermes" chipset based PCMCIA wireless adaptors, such + as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/ + EnteraSys RoamAbout 802.11, ELSA Airlancer, Melco Buffalo and + others). It should also be usable on various Prism II based cards + such as the Linksys, D-Link and Farallon Skyline. It should also + work on Symbol cards such as the 3Com AirConnect and Ericsson WLAN. + + To use your PC-cards, you will need supporting software from David + Hinds' pcmcia-cs package (see the file + for location). You also want to check out the PCMCIA-HOWTO, + available from . + + You will also very likely also need the Wireless Tools in order to + configure your card and that /etc/pcmcia/wireless.opts works: + . + +config AIRO_CS + tristate "Cisco/Aironet 34X/35X/4500/4800 PCMCIA cards" + depends on NET_RADIO && PCMCIA + ---help--- + This is the standard Linux driver to support Cisco/Aironet PCMCIA + 802.11 wireless cards. This driver is the same as the Aironet + driver part of the Linux Pcmcia package. + It supports the new 802.11b cards from Cisco (Cisco 34X, Cisco 35X + - with or without encryption) as well as card before the Cisco + aquisition (Aironet 4500, Aironet 4800, Aironet 4800B). It also + supports OEM of Cisco such as the DELL TrueMobile 4800 and Xircom + 802.11b cards. + + This driver support both the standard Linux Wireless Extensions + and Cisco proprietary API, so both the Linux Wireless Tools and the + Cisco Linux utilities can be used to configure the card. + + To use your PC-cards, you will need supporting software from David + Hinds' pcmcia-cs package (see the file + for location). You also want to check out the PCMCIA-HOWTO, + available from . + +config PCMCIA_ATMEL + tristate "Atmel at76c502/at76c504 PCMCIA cards" + depends on NET_RADIO && ATMEL && PCMCIA + select FW_LOADER + select CRC32 + ---help--- + Enable support for PCMCIA cards containing the + Atmel at76c502 and at76c504 chips. + +config PCMCIA_WL3501 + tristate "Planet WL3501 PCMCIA cards" + depends on NET_RADIO && EXPERIMENTAL && PCMCIA + ---help--- + A driver for WL3501 PCMCIA 802.11 wireless cards made by Planet. + It has basic support for Linux wireless extensions and initial + micro support for ethtool. + +comment "Prism GT/Duette 802.11(a/b/g) PCI/Cardbus support" + depends on NET_RADIO && PCI +config PRISM54 + tristate 'Intersil Prism GT/Duette/Indigo PCI/Cardbus' + depends on PCI && NET_RADIO && EXPERIMENTAL + select FW_LOADER + ---help--- + Enable PCI and Cardbus support for the following chipset based cards: + + ISL3880 - Prism GT 802.11 b/g + ISL3877 - Prism Indigo 802.11 a + ISL3890 - Prism Duette 802.11 a/b/g + + For a complete list of supported cards visit . + Here is the latest confirmed list of supported cards: + + 3com OfficeConnect 11g Cardbus Card aka 3CRWE154G72 + Allnet ALL0271 PCI Card + Compex WL54G Cardbus Card + Corega CG-WLCB54GT Cardbus Card + D-Link Air Plus Xtreme G A1 Cardbus Card aka DWL-g650 + I-O Data WN-G54/CB Cardbus Card + Kobishi XG-300 aka Z-Com Cardbus Card + Netgear WG511 Cardbus Card + Ovislink WL-5400PCI PCI Card + Peabird WLG-PCI PCI Card + Sitecom WL-100i Cardbus Card + Sitecom WL-110i PCI Card + SMC2802W - EZ Connect g 2.4GHz 54 Mbps Wireless PCI Card + SMC2835W - EZ Connect g 2.4GHz 54 Mbps Wireless Cardbus Card + SMC2835W-V2 - EZ Connect g 2.4GHz 54 Mbps Wireless Cardbus Card + Z-Com XG-900 PCI Card + Zyxel G-100 Cardbus Card + + If you enable this you will need a firmware file as well. + You will need to copy this to /usr/lib/hotplug/firmware/isl3890. + You can get this non-GPL'd firmware file from the Prism54 project page: + + You will also need the /etc/hotplug/firmware.agent script from + a current hotplug package. + + Note: You need a motherboard with DMA support to use any of these cards + + If you want to compile the driver as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read . The module + will be called prism54.ko. + +source "drivers/net/wireless/hostap/Kconfig" + +# yes, this works even when no drivers are selected +config NET_WIRELESS + bool + depends on NET_RADIO && (ISA || PCI || PPC_PMAC || PCMCIA) + default y + +endmenu + diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/Makefile linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/Makefile --- linux-2.6.11-rc4-mm1/drivers/net/wireless/Makefile 2005-02-23 10:19:26.131818496 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/Makefile 2005-02-24 13:06:17.717068616 -0800 @@ -33,3 +33,8 @@ # 16-bit wireless PCMCIA client drivers obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o obj-$(CONFIG_PCMCIA_WL3501) += wl3501_cs.o + +obj-$(CONFIG_ATHEROS_HAL) += _ath_hal/ +obj-$(CONFIG_ATHEROS_RATE) += _ath_rate/ +obj-$(CONFIG_ATHEROS) += ath/ +obj-$(CONFIG_NET80211) += net80211/ diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/Makefile.orig linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/Makefile.orig --- linux-2.6.11-rc4-mm1/drivers/net/wireless/Makefile.orig 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/Makefile.orig 2005-02-23 10:19:26.000000000 -0800 @@ -0,0 +1,35 @@ +# +# Makefile for the Linux Wireless network device drivers. +# + +obj-$(CONFIG_STRIP) += strip.o +obj-$(CONFIG_ARLAN) += arlan.o + +arlan-objs := arlan-main.o arlan-proc.o + +# Obsolete cards +obj-$(CONFIG_WAVELAN) += wavelan.o +obj-$(CONFIG_PCMCIA_NETWAVE) += netwave_cs.o +obj-$(CONFIG_PCMCIA_WAVELAN) += wavelan_cs.o + +obj-$(CONFIG_HERMES) += orinoco.o hermes.o +obj-$(CONFIG_PCMCIA_HERMES) += orinoco_cs.o +obj-$(CONFIG_APPLE_AIRPORT) += airport.o +obj-$(CONFIG_PLX_HERMES) += orinoco_plx.o +obj-$(CONFIG_PCI_HERMES) += orinoco_pci.o +obj-$(CONFIG_TMD_HERMES) += orinoco_tmd.o + +obj-$(CONFIG_AIRO) += airo.o +obj-$(CONFIG_AIRO_CS) += airo_cs.o airo.o + +obj-$(CONFIG_ATMEL) += atmel.o +obj-$(CONFIG_PCI_ATMEL) += atmel_pci.o +obj-$(CONFIG_PCMCIA_ATMEL) += atmel_cs.o + +obj-$(CONFIG_PRISM54) += prism54/ + +obj-$(CONFIG_HOSTAP) += hostap/ + +# 16-bit wireless PCMCIA client drivers +obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o +obj-$(CONFIG_PCMCIA_WL3501) += wl3501_cs.o diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/_ath_hal/Kconfig linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/_ath_hal/Kconfig --- linux-2.6.11-rc4-mm1/drivers/net/wireless/_ath_hal/Kconfig 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/_ath_hal/Kconfig 2005-02-24 13:06:17.213145224 -0800 @@ -0,0 +1,2 @@ +config ATHEROS_HAL + tristate diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/_ath_hal/Makefile linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/_ath_hal/Makefile --- linux-2.6.11-rc4-mm1/drivers/net/wireless/_ath_hal/Makefile 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/_ath_hal/Makefile 2005-02-24 13:06:17.216144768 -0800 @@ -0,0 +1,64 @@ +# +# Makefile for the Atheros HAL Module. +# + + +# +# Use ARCH to guess the target platform. +# XXX use .config contents in addition? +# +ifeq ($(strip ${ARCH}),i386) +TARGET= i386-elf +endif +ifeq ($(strip ${ARCH}),mips) +TARGET= mipsisa32-be-elf +endif +ifeq ($(strip ${ARCH}),x86_64) +TARGET= x86_64-elf +endif + +EXTRA_CFLAGS+= -I. -I${HAL}/.. -I${HAL} + +ifndef OS +OS = linux +endif + +ifndef HAL_OBJDIR +# +# Use release version of HAL. +# +# NB: binary HAL's are distributed in a uuencode'd file; on some +# systems uuencode is not installed by default and you must +# specially install it from the sharutils package. +# +UUDECODE?= uudecode + +HAL= $(src)/../hal +OPT_AH_H= ${HAL}/public/${TARGET}.opt_ah.h +else +# +# Use private version of HAL. +# +HAL= $(shell dirname `dirname ${HAL_OBJDIR}`) +OPT_AH_H= ${HAL_OBJDIR}/opt_ah.h +endif + +obj-$(CONFIG_ATHEROS_HAL) += ath_hal.o +ath_hal-objs := ah_osdep.o hal.o + + + + +ifndef HAL_OBJDIR +$(obj)/hal.o: ${HAL}/public/${TARGET}.hal.o.uu + ${UUDECODE} -o $(obj)/hal.o < $< +else +$(obj)/hal.o: ${HAL_OBJDIR}/hal.o + cp ${HAL_OBJDIR}/hal.o $(obj)/hal.o +endif + +# XXX need opt_ah.h to do dependencies so give this bogus dependency +$(obj)/ah_osdep.c: ${HAL}/ah_osdep.c $(obj)/opt_ah.h + cp ${HAL}/${OS}/ah_osdep.c $(obj)/ah_osdep.c +$(obj)/opt_ah.h: ${OPT_AH_H} + cp ${OPT_AH_H} $(obj)/opt_ah.h diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/_ath_rate/Kconfig linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/_ath_rate/Kconfig --- linux-2.6.11-rc4-mm1/drivers/net/wireless/_ath_rate/Kconfig 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/_ath_rate/Kconfig 2005-02-24 13:06:17.234142032 -0800 @@ -0,0 +1,2 @@ +config ATHEROS_RATE + tristate diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/_ath_rate/Makefile linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/_ath_rate/Makefile --- linux-2.6.11-rc4-mm1/drivers/net/wireless/_ath_rate/Makefile 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/_ath_rate/Makefile 2005-02-24 13:06:17.237141576 -0800 @@ -0,0 +1,20 @@ +# +# Makefile for Onoe's rate control algorithm. +# +# $Id: Makefile.kernel,v 1.2 2004/09/29 18:19:39 samleffler Exp $ +# + + +HAL= $(src)/../hal +ATH= $(src)/../ath +WLAN= ${src}/../net80211 +COMPAT= ${WLAN}/compat + +INCS= -include ${COMPAT}/compat.h -I${COMPAT} +EXTRA_CFLAGS+= ${INCS} -I${HAL} -I${HAL}/linux -I${ATH} -I${WLAN} -I${src}/.. + +obj-$(CONFIG_ATHEROS_RATE) += ath_rate_onoe.o +ath_rate_onoe-objs := onoe.o + + + diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/_ath_rate/onoe.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/_ath_rate/onoe.c --- linux-2.6.11-rc4-mm1/drivers/net/wireless/_ath_rate/onoe.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/_ath_rate/onoe.c 2005-02-24 13:06:17.231142488 -0800 @@ -0,0 +1,579 @@ +/*- + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + * + * $Id: onoe.c,v 1.2 2005/02/22 14:17:22 otaku Exp $ + */ + +/* + * Atsushi Onoe's rate control algorithm. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "if_athvar.h" +#include "ah_desc.h" + +#include "onoe.h" + +#define ONOE_DEBUG +#ifdef ONOE_DEBUG +#define DPRINTF(sc, _fmt, ...) do { \ + if (sc->sc_debug & 0x10) \ + printk(_fmt, __VA_ARGS__); \ +} while (0) +#else +#define DPRINTF(sc, _fmt, ...) +#endif + +/* + * Default parameters for the rate control algorithm. These are + * all tunable with sysctls. The rate controller runs periodically + * (each ath_rateinterval ms) analyzing transmit statistics for each + * neighbor/station (when operating in station mode this is only the AP). + * If transmits look to be working well over a sampling period then + * it gives a "raise rate credit". If transmits look to not be working + * well than it deducts a credit. If the credits cross a threshold then + * the transmit rate is raised. Various error conditions force the + * the transmit rate to be dropped. + * + * The decision to issue/deduct a credit is based on the errors and + * retries accumulated over the sampling period. ath_rate_raise defines + * the percent of retransmits for which a credit is issued/deducted. + * ath_rate_raise_threshold defines the threshold on credits at which + * the transmit rate is increased. + * + * XXX this algorithm is flawed. + */ +static int ath_rateinterval = 1000; /* rate ctl interval (ms) */ +static int ath_rate_raise = 10; /* add credit threshold */ +static int ath_rate_raise_threshold = 10; /* rate ctl raise threshold */ + +static void ath_ratectl(unsigned long); +static void ath_rate_update(struct ath_softc *, struct ieee80211_node *, + int rate); +static void ath_rate_ctl_start(struct ath_softc *, struct ieee80211_node *); +static void ath_rate_ctl(void *, struct ieee80211_node *); + +void +ath_rate_node_init(struct ath_softc *sc, struct ath_node *an) +{ + /* NB: assumed to be zero'd by caller */ + ath_rate_update(sc, &an->an_node, 0); +} +EXPORT_SYMBOL(ath_rate_node_init); + +void +ath_rate_node_cleanup(struct ath_softc *sc, struct ath_node *an) +{ +} +EXPORT_SYMBOL(ath_rate_node_cleanup); + +void +ath_rate_node_copy(struct ath_softc *sc, + struct ath_node *dst, const struct ath_node *src) +{ + struct onoe_node *odst = ATH_NODE_ONOE(dst); + const struct onoe_node *osrc = (const struct onoe_node *)&src[1]; + + memcpy(odst, osrc, sizeof(struct onoe_node)); +} +EXPORT_SYMBOL(ath_rate_node_copy); + +void +ath_rate_findrate(struct ath_softc *sc, struct ath_node *an, + HAL_BOOL shortPreamble, size_t frameLen, + u_int8_t *rix, int *try0, u_int8_t *txrate) +{ + struct onoe_node *on = ATH_NODE_ONOE(an); + + *rix = on->on_tx_rix0; + *try0 = on->on_tx_try0; + if (shortPreamble) + *txrate = on->on_tx_rate0sp; + else + *txrate = on->on_tx_rate0; +} +EXPORT_SYMBOL(ath_rate_findrate); + +void +ath_rate_setupxtxdesc(struct ath_softc *sc, struct ath_node *an, + struct ath_desc *ds, HAL_BOOL shortPreamble, u_int8_t rix) +{ + struct onoe_node *on = ATH_NODE_ONOE(an); + + ath_hal_setupxtxdesc(sc->sc_ah, ds + , on->on_tx_rate1sp, 2 /* series 1 */ + , on->on_tx_rate2sp, 2 /* series 2 */ + , on->on_tx_rate3sp, 2 /* series 3 */ + ); +} +EXPORT_SYMBOL(ath_rate_setupxtxdesc); + +void +ath_rate_tx_complete(struct ath_softc *sc, + struct ath_node *an, const struct ath_desc *ds) +{ + struct onoe_node *on = ATH_NODE_ONOE(an); + + if (ds->ds_txstat.ts_status == 0) + on->on_tx_ok++; + else + on->on_tx_err++; + on->on_tx_retr += ds->ds_txstat.ts_shortretry + + ds->ds_txstat.ts_longretry; +} +EXPORT_SYMBOL(ath_rate_tx_complete); + +void +ath_rate_newassoc(struct ath_softc *sc, struct ath_node *an, int isnew) +{ + if (isnew) + ath_rate_ctl_start(sc, &an->an_node); +} +EXPORT_SYMBOL(ath_rate_newassoc); + +static void +ath_rate_update(struct ath_softc *sc, struct ieee80211_node *ni, int rate) +{ + struct ath_node *an = ATH_NODE(ni); + struct onoe_node *on = ATH_NODE_ONOE(an); + const HAL_RATE_TABLE *rt = sc->sc_currates; + u_int8_t rix; + + KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode)); + + DPRINTF(sc, "%s: set xmit rate for %s to %dM\n", + __func__, ether_sprintf(ni->ni_macaddr), + ni->ni_rates.rs_nrates > 0 ? + (ni->ni_rates.rs_rates[rate] & IEEE80211_RATE_VAL) / 2 : 0); + + ni->ni_txrate = rate; + /* XXX management/control frames always go at the lowest speed */ + an->an_tx_mgtrate = rt->info[0].rateCode; + an->an_tx_mgtratesp = an->an_tx_mgtrate | rt->info[0].shortPreamble; + /* + * Before associating a node has no rate set setup + * so we can't calculate any transmit codes to use. + * This is ok since we should never be sending anything + * but management frames and those always go at the + * lowest hardware rate. + */ + if (ni->ni_rates.rs_nrates == 0) + goto done; + on->on_tx_rix0 = sc->sc_rixmap[ + ni->ni_rates.rs_rates[rate] & IEEE80211_RATE_VAL]; + on->on_tx_rate0 = rt->info[on->on_tx_rix0].rateCode; + + on->on_tx_rate0sp = on->on_tx_rate0 | + rt->info[on->on_tx_rix0].shortPreamble; + if (sc->sc_mrretry) { + /* + * Hardware supports multi-rate retry; setup two + * step-down retry rates and make the lowest rate + * be the ``last chance''. We use 4, 2, 2, 2 tries + * respectively (4 is set here, the rest are fixed + * in the xmit routine). + */ + on->on_tx_try0 = 1 + 3; /* 4 tries at rate 0 */ + if (--rate >= 0) { + rix = sc->sc_rixmap[ + ni->ni_rates.rs_rates[rate]&IEEE80211_RATE_VAL]; + on->on_tx_rate1 = rt->info[rix].rateCode; + on->on_tx_rate1sp = on->on_tx_rate1 | + rt->info[rix].shortPreamble; + } else { + on->on_tx_rate1 = on->on_tx_rate1sp = 0; + } + if (--rate >= 0) { + rix = sc->sc_rixmap[ + ni->ni_rates.rs_rates[rate]&IEEE80211_RATE_VAL]; + on->on_tx_rate2 = rt->info[rix].rateCode; + on->on_tx_rate2sp = on->on_tx_rate2 | + rt->info[rix].shortPreamble; + } else { + on->on_tx_rate2 = on->on_tx_rate2sp = 0; + } + if (rate > 0) { + /* NB: only do this if we didn't already do it above */ + on->on_tx_rate3 = rt->info[0].rateCode; + on->on_tx_rate3sp = + an->an_tx_mgtrate | rt->info[0].shortPreamble; + } else { + on->on_tx_rate3 = on->on_tx_rate3sp = 0; + } + } else { + on->on_tx_try0 = ATH_TXMAXTRY; /* max tries at rate 0 */ + on->on_tx_rate1 = on->on_tx_rate1sp = 0; + on->on_tx_rate2 = on->on_tx_rate2sp = 0; + on->on_tx_rate3 = on->on_tx_rate3sp = 0; + } +done: + on->on_tx_ok = on->on_tx_err = on->on_tx_retr = on->on_tx_upper = 0; +} + +/* + * Set the starting transmit rate for a node. + */ +static void +ath_rate_ctl_start(struct ath_softc *sc, struct ieee80211_node *ni) +{ +#define RATE(_ix) (ni->ni_rates.rs_rates[(_ix)] & IEEE80211_RATE_VAL) + struct ieee80211com *ic = &sc->sc_ic; + int srate; + + KASSERT(ni->ni_rates.rs_nrates > 0, ("no rates")); + if (ic->ic_fixed_rate == -1) { + /* + * No fixed rate is requested. For 11b start with + * the highest negotiated rate; otherwise, for 11g + * and 11a, we start "in the middle" at 24Mb or 36Mb. + */ + srate = ni->ni_rates.rs_nrates - 1; + if (sc->sc_curmode != IEEE80211_MODE_11B) { + /* + * Scan the negotiated rate set to find the + * closest rate. + */ + /* NB: the rate set is assumed sorted */ + for (; srate >= 0 && RATE(srate) > 72; srate--) + ; + KASSERT(srate >= 0, ("bogus rate set")); + } + } else { + /* + * A fixed rate is to be used; ic_fixed_rate is an + * index into the supported rate set. Convert this + * to the index into the negotiated rate set for + * the node. We know the rate is there because the + * rate set is checked when the station associates. + */ + const struct ieee80211_rateset *rs = + &ic->ic_sup_rates[ic->ic_curmode]; + int r = rs->rs_rates[ic->ic_fixed_rate] & IEEE80211_RATE_VAL; + /* NB: the rate set is assumed sorted */ + srate = ni->ni_rates.rs_nrates - 1; + for (; srate >= 0 && RATE(srate) != r; srate--) + ; + KASSERT(srate >= 0, + ("fixed rate %d not in rate set", ic->ic_fixed_rate)); + } + ath_rate_update(sc, ni, srate); +#undef RATE +} + +/* + * Reset the rate control state for each 802.11 state transition. + */ +void +ath_rate_newstate(struct ath_softc *sc, enum ieee80211_state state) +{ + struct onoe_softc *osc = (struct onoe_softc *) sc->sc_rc; + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni; + + if (state == IEEE80211_S_INIT) { + del_timer(&osc->timer); + return; + } + if (ic->ic_opmode == IEEE80211_M_STA) { + /* + * Reset local xmit state; this is really only + * meaningful when operating in station mode. + */ + ni = ic->ic_bss; + if (state == IEEE80211_S_RUN) { + ath_rate_ctl_start(sc, ni); + } else { + ath_rate_update(sc, ni, 0); + } + } else { + /* + * When operating as a station the node table holds + * the AP's that were discovered during scanning. + * For any other operating mode we want to reset the + * tx rate state of each node. + */ + TAILQ_FOREACH(ni, &ic->ic_node, ni_list) + ath_rate_update(sc, ni, 0); /* use lowest rate */ + ath_rate_update(sc, ic->ic_bss, 0); + } + if (ic->ic_fixed_rate == -1 && state == IEEE80211_S_RUN) { + int interval; + /* + * Start the background rate control thread if we + * are not configured to use a fixed xmit rate. + */ + interval = ath_rateinterval; + if (ic->ic_opmode == IEEE80211_M_STA) + interval /= 2; + mod_timer(&osc->timer, jiffies + ((HZ * interval) / 1000)); + } +} +EXPORT_SYMBOL(ath_rate_newstate); + +/* + * Examine and potentially adjust the transmit rate. + */ +static void +ath_rate_ctl(void *arg, struct ieee80211_node *ni) +{ + struct ath_softc *sc = arg; + struct onoe_node *on = ATH_NODE_ONOE(ATH_NODE(ni)); + struct ieee80211_rateset *rs = &ni->ni_rates; + int dir = 0, nrate, enough; + + /* + * Rate control + * XXX: very primitive version. + */ + enough = (on->on_tx_ok + on->on_tx_err >= 10); + + /* no packet reached -> down */ + if (on->on_tx_err > 0 && on->on_tx_ok == 0) + dir = -1; + + /* all packets needs retry in average -> down */ + if (enough && on->on_tx_ok < on->on_tx_retr) + dir = -1; + + /* no error and less than rate_raise% of packets need retry -> up */ + if (enough && on->on_tx_err == 0 && + on->on_tx_retr < (on->on_tx_ok * ath_rate_raise) / 100) + dir = 1; + + DPRINTF(sc, "%s: ok %d err %d retr %d upper %d dir %d\n", + ether_sprintf(ni->ni_macaddr), + on->on_tx_ok, on->on_tx_err, on->on_tx_retr, + on->on_tx_upper, dir); + + nrate = ni->ni_txrate; + switch (dir) { + case 0: + if (enough && on->on_tx_upper > 0) + on->on_tx_upper--; + break; + case -1: + if (nrate > 0) { + nrate--; + sc->sc_stats.ast_rate_drop++; + } + on->on_tx_upper = 0; + break; + case 1: + /* raise rate if we hit rate_raise_threshold */ + if (++on->on_tx_upper < ath_rate_raise_threshold) + break; + on->on_tx_upper = 0; + if (nrate + 1 < rs->rs_nrates) { + nrate++; + sc->sc_stats.ast_rate_raise++; + } + break; + } + + if (nrate != ni->ni_txrate) { + DPRINTF(sc, "%s: %dM -> %dM (%d ok, %d err, %d retr)\n", + __func__, + (rs->rs_rates[ni->ni_txrate] & IEEE80211_RATE_VAL) / 2, + (rs->rs_rates[nrate] & IEEE80211_RATE_VAL) / 2, + on->on_tx_ok, on->on_tx_err, on->on_tx_retr); + ath_rate_update(sc, ni, nrate); + } else if (enough) + on->on_tx_ok = on->on_tx_err = on->on_tx_retr = 0; +} + +static void +ath_ratectl(unsigned long data) +{ + struct net_device *dev = (struct net_device *)data; + struct ath_softc *sc = dev->priv; + struct onoe_softc *osc = (struct onoe_softc *) sc->sc_rc; + struct ieee80211com *ic = &sc->sc_ic; + int interval; + + if (dev->flags & IFF_RUNNING) { + sc->sc_stats.ast_rate_calls++; + + if (ic->ic_opmode == IEEE80211_M_STA) + ath_rate_ctl(sc, ic->ic_bss); /* NB: no reference */ + else + ieee80211_iterate_nodes(ic, ath_rate_ctl, sc); + } + interval = ath_rateinterval; + if (ic->ic_opmode == IEEE80211_M_STA) + interval /= 2; + osc->timer.expires = jiffies + ((HZ * interval) / 1000); + add_timer(&osc->timer); +} + +struct ath_ratectrl * +ath_rate_attach(struct ath_softc *sc) +{ + struct onoe_softc *osc; + + osc = kmalloc(sizeof(struct onoe_softc), GFP_ATOMIC); + if (osc == NULL) + return NULL; + osc->arc.arc_space = sizeof(struct onoe_node); + init_timer(&osc->timer); + osc->timer.data = (unsigned long) &sc->sc_dev; + osc->timer.function = ath_ratectl; + + return &osc->arc; +} +EXPORT_SYMBOL(ath_rate_attach); + +void +ath_rate_detach(struct ath_ratectrl *arc) +{ + struct onoe_softc *osc = (struct onoe_softc *) arc; + + del_timer(&osc->timer); + kfree(osc); +} +EXPORT_SYMBOL(ath_rate_detach); + +static int minrateinterval = 500; /* 500ms */ +static int maxpercent = 100; /* 100% */ +static int minpercent = 0; /* 0% */ +static int maxint = 0x7fffffff; /* 32-bit big */ + +#define CTL_AUTO -2 /* cannot be CTL_ANY or CTL_NONE */ + +/* + * Static (i.e. global) sysctls. + */ +enum { + DEV_ATH = 9, /* XXX known by many */ +}; + +static ctl_table ath_rate_static_sysctls[] = { + { .ctl_name = CTL_AUTO, + .procname = "interval", + .mode = 0644, + .data = &ath_rateinterval, + .maxlen = sizeof(ath_rateinterval), + .extra1 = &minrateinterval, + .extra2 = &maxint, + .proc_handler = proc_dointvec_minmax + }, + { .ctl_name = CTL_AUTO, + .procname = "raise", + .mode = 0644, + .data = &ath_rate_raise, + .maxlen = sizeof(ath_rate_raise), + .extra1 = &minpercent, + .extra2 = &maxpercent, + .proc_handler = proc_dointvec_minmax + }, + { .ctl_name = CTL_AUTO, + .procname = "raise_threshold", + .mode = 0644, + .data = &ath_rate_raise_threshold, + .maxlen = sizeof(ath_rate_raise_threshold), + .proc_handler = proc_dointvec + }, + { 0 } +}; +static ctl_table ath_rate_table[] = { + { .ctl_name = CTL_AUTO, + .procname = "rate", + .mode = 0555, + .child = ath_rate_static_sysctls + }, { 0 } +}; +static ctl_table ath_ath_table[] = { + { .ctl_name = DEV_ATH, + .procname = "ath", + .mode = 0555, + .child = ath_rate_table + }, { 0 } +}; +static ctl_table ath_root_table[] = { + { .ctl_name = CTL_DEV, + .procname = "dev", + .mode = 0555, + .child = ath_ath_table + }, { 0 } +}; +static struct ctl_table_header *ath_sysctl_header; + +MODULE_AUTHOR("Errno Consulting, Sam Leffler"); +MODULE_DESCRIPTION("Atsushi Onoe's rate control algorithm for Atheros devices"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("Dual BSD/GPL"); +#endif + +static char *version = "1.0"; +static char *dev_info = "ath_rate_onoe"; + +static int __init +init_ath_rate_onoe(void) +{ + printk(KERN_INFO "%s: %s\n", dev_info, version); + +#ifdef CONFIG_SYSCTL + ath_sysctl_header = register_sysctl_table(ath_root_table, 1); +#endif + return (0); +} +module_init(init_ath_rate_onoe); + +static void __exit +exit_ath_rate_onoe(void) +{ +#ifdef CONFIG_SYSCTL + if (ath_sysctl_header != NULL) + unregister_sysctl_table(ath_sysctl_header); +#endif + + printk(KERN_INFO "%s: unloaded\n", dev_info); +} +module_exit(exit_ath_rate_onoe); diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/_ath_rate/onoe.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/_ath_rate/onoe.h --- linux-2.6.11-rc4-mm1/drivers/net/wireless/_ath_rate/onoe.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/_ath_rate/onoe.h 2005-02-24 13:06:17.231142488 -0800 @@ -0,0 +1,69 @@ +/*- + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + * + * $Id: onoe.h,v 1.2 2005/02/22 14:17:22 otaku Exp $ + */ + +/* + * Defintions for the Atheros Wireless LAN controller driver. + */ +#ifndef _DEV_ATH_RATE_ONOE_H +#define _DEV_ATH_RATE_ONOE_H + +/* per-device state */ +struct onoe_softc { + struct ath_ratectrl arc; /* base state */ + struct timer_list timer; /* periodic timer */ +}; + +/* per-node state */ +struct onoe_node { + u_int on_tx_ok; /* tx ok pkt */ + u_int on_tx_err; /* tx !ok pkt */ + u_int on_tx_retr; /* tx retry count */ + int on_tx_upper; /* tx upper rate req cnt */ + u_int8_t on_tx_rix0; /* series 0 rate index */ + u_int8_t on_tx_try0; /* series 0 try count */ + u_int8_t on_tx_rate0; /* series 0 h/w rate */ + u_int8_t on_tx_rate1; /* series 1 h/w rate */ + u_int8_t on_tx_rate2; /* series 2 h/w rate */ + u_int8_t on_tx_rate3; /* series 3 h/w rate */ + u_int8_t on_tx_rate0sp; /* series 0 short preamble h/w rate */ + u_int8_t on_tx_rate1sp; /* series 1 short preamble h/w rate */ + u_int8_t on_tx_rate2sp; /* series 2 short preamble h/w rate */ + u_int8_t on_tx_rate3sp; /* series 3 short preamble h/w rate */ +}; +#define ATH_NODE_ONOE(an) ((struct onoe_node *)&an[1]) +#endif /* _DEV_ATH_RATE_ONOE_H */ diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/ath/Kconfig linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/ath/Kconfig --- linux-2.6.11-rc4-mm1/drivers/net/wireless/ath/Kconfig 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/ath/Kconfig 2005-02-24 13:06:17.188149024 -0800 @@ -0,0 +1,26 @@ + +comment "Atheros 802.11(a/b/g) PCI/Cardbus support" + +config ATHEROS + tristate "Atheros PCI/Cardbus cards" + depends on PCI && NET_RADIO && EXPERIMENTAL && HOTPLUG + select ATHEROS_HAL + select ATHEROS_RATE + select NET80211 + ---help--- + Say Y here if you intend to attach an Atheros Cardbus or PCI + wireless Ethernet networking card to your computer. This + driver support the standard Linux Wireless Extensions. + + You will also very likely also need the Wireless Tools in order to + configure your card and that /etc/pcmcia/wireless.opts works: + . + + To compile this driver as a module, choose M here: the module will be + called ath_pci. If unsure, say N. + +source "drivers/net/wireless/_ath_hal/Kconfig" + +source "drivers/net/wireless/_ath_rate/Kconfig" + +source "drivers/net/wireless/net80211/Kconfig" diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/ath/Makefile linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/ath/Makefile --- linux-2.6.11-rc4-mm1/drivers/net/wireless/ath/Makefile 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/ath/Makefile 2005-02-24 13:06:17.191148568 -0800 @@ -0,0 +1,30 @@ +# +# Makefile for the Atheros WLAN driver. +# + + +HAL= $(src)/../hal +ATH_HAL=$(src)/../_ath_hal +WLAN= ${src}/../net80211 +COMPAT= ${WLAN}/compat + +# +# Select bus-specific code. Note that this defaults to PCI. +# +ifeq ($(strip ${BUS}),AHB) +BUSNAME=ahb +EXTRA_CFLAGS+= -DATH_AHB +else +BUSNAME=pci +EXTRA_CFLAGS+= -DATH_PCI +endif + +INCS= -include ${COMPAT}/compat.h -I${COMPAT} +EXTRA_CFLAGS+= ${INCS} -I${HAL} -I${HAL}/linux -I${ATH_HAL} -I${WLAN} \ + -I${src}/.. + +obj-$(CONFIG_ATHEROS) += ath_${BUSNAME}.o +ath_${BUSNAME}-objs := if_ath.o if_ath_${BUSNAME}.o + + + diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/ath/if_ath.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/ath/if_ath.c --- linux-2.6.11-rc4-mm1/drivers/net/wireless/ath/if_ath.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/ath/if_ath.c 2005-02-24 13:06:17.174151152 -0800 @@ -0,0 +1,4789 @@ +/*- + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + * + * $Id: if_ath.c,v 1.37 2005/02/16 16:08:38 samleffler Exp $ + */ + +/* + * Driver for the Atheros Wireless LAN controller. + * + * This software is derived from work of Atsushi Onoe; his contribution + * is greatly appreciated. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "if_ethersubr.h" /* for ETHER_IS_MULTICAST */ +#include "if_media.h" +#include "if_llc.h" + +#include + +#define AR_DEBUG +#include "if_athvar.h" +#include "ah_desc.h" +#include "ah_devid.h" /* XXX to identify IBM cards */ +#include "ah.h" + +#ifdef ATH_PCI /* PCI BUS */ +#include "if_ath_pci.h" +#endif /* PCI BUS */ +#ifdef ATH_AHB /* AHB BUS */ +#include "if_ath_ahb.h" +#endif /* AHB BUS */ +extern void bus_read_cachesize(struct ath_softc *sc, u_int8_t *csz); + +/* unalligned little endian access */ +#define LE_READ_2(p) \ + ((u_int16_t) \ + ((((u_int8_t *)(p))[0] ) | (((u_int8_t *)(p))[1] << 8))) +#define LE_READ_4(p) \ + ((u_int32_t) \ + ((((u_int8_t *)(p))[0] ) | (((u_int8_t *)(p))[1] << 8) | \ + (((u_int8_t *)(p))[2] << 16) | (((u_int8_t *)(p))[3] << 24))) + +static int ath_init(struct net_device *); +static int ath_reset(struct net_device *); +static void ath_fatal_tasklet(TQUEUE_ARG); +static void ath_rxorn_tasklet(TQUEUE_ARG); +static void ath_bmiss_tasklet(TQUEUE_ARG); +static int ath_stop_locked(struct net_device *); +static int ath_stop(struct net_device *); +static int ath_media_change(struct net_device *); +static void ath_initkeytable(struct ath_softc *); +static int ath_key_alloc(struct ieee80211com *, + const struct ieee80211_key *); +static int ath_key_delete(struct ieee80211com *, + const struct ieee80211_key *); +static int ath_key_set(struct ieee80211com *, const struct ieee80211_key *, + const u_int8_t mac[IEEE80211_ADDR_LEN]); +static void ath_key_update_begin(struct ieee80211com *); +static void ath_key_update_end(struct ieee80211com *); +static void ath_mode_init(struct net_device *); +static void ath_setslottime(struct ath_softc *); +static void ath_updateslot(struct net_device *); +static int ath_beacon_alloc(struct ath_softc *, struct ieee80211_node *); +static void ath_beacon_tasklet(struct net_device *); +static void ath_beacon_free(struct ath_softc *); +static void ath_beacon_config(struct ath_softc *); +static int ath_desc_alloc(struct ath_softc *); +static void ath_desc_free(struct ath_softc *); +static struct ieee80211_node *ath_node_alloc(struct ieee80211com *); +static void ath_node_cleanup(struct ieee80211com *, + struct ieee80211_node *); +static void ath_node_copy(struct ieee80211com *, + struct ieee80211_node *, const struct ieee80211_node *); +static u_int8_t ath_node_getrssi(struct ieee80211com *, + struct ieee80211_node *); +static int ath_rxbuf_init(struct ath_softc *, struct ath_buf *); +static void ath_recv_mgmt(struct ieee80211com *, struct sk_buff *, + struct ieee80211_node *, + int subtype, int rssi, u_int32_t rstamp); +static void ath_rx_tasklet(TQUEUE_ARG data); +static int ath_hardstart(struct sk_buff *, struct net_device *); +static int ath_mgtstart(struct ieee80211com *ic, struct sk_buff *skb); +static int ath_tx_setup(struct ath_softc *, int ac, int txq); +static int ath_tx_start(struct net_device *, struct ieee80211_node *, + struct ath_buf *, struct sk_buff *); +static void ath_tx_tasklet_q0(TQUEUE_ARG data); +static void ath_tx_tasklet_q0123(TQUEUE_ARG data); +static void ath_tx_tasklet(TQUEUE_ARG data); +static void ath_tx_timeout(struct net_device *); +static int ath_chan_set(struct ath_softc *, struct ieee80211_channel *); +static void ath_draintxq(struct ath_softc *); +static void ath_stoprecv(struct ath_softc *); +static int ath_startrecv(struct ath_softc *); +static void ath_chan_change(struct ath_softc *, struct ieee80211_channel *); +static void ath_next_scan(unsigned long); +static void ath_calibrate(unsigned long); +static int ath_newstate(struct ieee80211com *, enum ieee80211_state, int); +#if IEEE80211_VLAN_TAG_USED +static void ath_vlan_register(struct net_device *, struct vlan_group *); +static void ath_vlan_kill_vid(struct net_device *, unsigned short ); +#endif +static struct net_device_stats *ath_getstats(struct net_device *); +#ifdef CONFIG_NET_WIRELESS +static struct iw_statistics *ath_iw_getstats(struct net_device *); +static struct iw_handler_def ath_iw_handler_def; +#endif +static void ath_newassoc(struct ieee80211com *, + struct ieee80211_node *, int); +static int ath_getchannels(struct net_device *, u_int cc, + HAL_BOOL outdoor, HAL_BOOL xchanmode); +static void ath_update_led(struct ath_softc *); + +static int ath_set_mac_address(struct net_device *, void *); +static int ath_change_mtu(struct net_device *, int); +static int ath_ioctl(struct net_device *, struct ifreq *, int); + +static int ath_rate_setup(struct net_device *, u_int mode); +static void ath_setcurmode(struct ath_softc *, enum ieee80211_phymode); + +#ifdef CONFIG_SYSCTL +static void ath_dynamic_sysctl_register(struct ath_softc *); +static void ath_dynamic_sysctl_unregister(struct ath_softc *); +#endif /* CONFIG_SYSCTL */ +static void ath_announce(struct net_device *); + +static const char *acnames[] = { + "WME_AC_BE", + "WME_AC_BK", + "WME_AC_VI", + "WME_AC_VO", + "WME_UPSD", +}; + +static const char *hal_status_desc[] = { + "No error", + "No hardware present or device not yet supported", + "Memory allocation failed", + "Hardware didn't respond as expected", + "EEPROM magic number invalid", + "EEPROM version invalid", + "EEPROM unreadable", + "EEPROM checksum invalid", + "EEPROM read problem", + "EEPROM mac address invalid", + "EEPROM size not supported", + "Attempt to change write-locked EEPROM", + "Invalid parameter to function", + "Hardware revision not supported", + "Hardware self-test failed", + "Operation incomplete" +}; + +static int ath_dwelltime = 200; /* 5 channels/second */ +static int ath_calinterval = 30; /* calibrate every 30 secs */ +static int ath_countrycode = CTRY_DEFAULT; /* country code */ +static int ath_regdomain = 0; /* regulatory domain */ +static int ath_outdoor = AH_FALSE; /* enable outdoor use */ +static int ath_xchanmode = AH_TRUE; /* enable extended channels */ + +#ifdef AR_DEBUG +int ath_debug = 0; +#define IFF_DUMPPKTS(sc, _m) \ + ((sc->sc_debug & _m) || ieee80211_msg_dumppkts(&sc->sc_ic)) +static void ath_printrxbuf(struct ath_buf *bf, int); +static void ath_printtxbuf(struct ath_buf *bf, int); +enum { + ATH_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ + ATH_DEBUG_XMIT_DESC = 0x00000002, /* xmit descriptors */ + ATH_DEBUG_RECV = 0x00000004, /* basic recv operation */ + ATH_DEBUG_RECV_DESC = 0x00000008, /* recv descriptors */ + ATH_DEBUG_RATE = 0x00000010, /* rate control */ + ATH_DEBUG_RESET = 0x00000020, /* reset processing */ + ATH_DEBUG_MODE = 0x00000040, /* mode init/setup */ + ATH_DEBUG_BEACON = 0x00000080, /* beacon handling */ + ATH_DEBUG_WATCHDOG = 0x00000100, /* watchdog timeout */ + ATH_DEBUG_INTR = 0x00001000, /* ISR */ + ATH_DEBUG_TX_PROC = 0x00002000, /* tx ISR proc */ + ATH_DEBUG_RX_PROC = 0x00004000, /* rx ISR proc */ + ATH_DEBUG_BEACON_PROC = 0x00008000, /* beacon ISR proc */ + ATH_DEBUG_CALIBRATE = 0x00010000, /* periodic calibration */ + ATH_DEBUG_KEYCACHE = 0x00020000, /* key cache management */ + ATH_DEBUG_STATE = 0x00040000, /* 802.11 state transitions */ + ATH_DEBUG_NODE = 0x00080000, /* node management */ + ATH_DEBUG_FATAL = 0x80000000, /* fatal errors */ + ATH_DEBUG_ANY = 0xffffffff +}; +#define DPRINTF(sc, _m, _fmt, ...) do { \ + if (sc->sc_debug & _m) \ + printk(_fmt, __VA_ARGS__); \ +} while (0) +#define KEYPRINTF(sc, ix, hk, mac) do { \ + if (sc->sc_debug & ATH_DEBUG_KEYCACHE) \ + ath_keyprint(__func__, ix, hk, mac); \ +} while (0) +#else +#define IFF_DUMPPKTS(sc, _m) netif_msg_dumppkts(&sc->sc_ic) +#define DPRINTF(sc, _m, _fmt, ...) +#define KEYPRINTF(sc, k, ix, mac) +#endif + +static int countrycode = -1; +MODULE_PARM(countrycode, "i"); +MODULE_PARM_DESC(countrycode, "Override default country code"); +static int outdoor = -1; +MODULE_PARM(outdoor, "i"); +MODULE_PARM_DESC(outdoor, "Enable/disable outdoor use"); +static int xchanmode = -1; +MODULE_PARM(xchanmode, "i"); +MODULE_PARM_DESC(xchanmode, "Enable/disable extended channel mode"); + +int +ath_attach(u_int16_t devid, struct net_device *dev) +{ + struct ath_softc *sc = dev->priv; + struct ieee80211com *ic = &sc->sc_ic; + struct ath_hal *ah; + HAL_STATUS status; + int error = 0, i; + u_int8_t csz; + + DPRINTF(sc, ATH_DEBUG_ANY, "%s: devid 0x%x\n", __func__, devid); + + /* + * Cache line size is used to size and align various + * structures used to communicate with the hardware. + */ + bus_read_cachesize(sc, &csz); + /* XXX assert csz is non-zero */ + sc->sc_cachelsz = csz << 2; /* convert to bytes */ + + ATH_LOCK_INIT(sc); + ATH_TXBUF_LOCK_INIT(sc); + + ATH_INIT_TQUEUE(&sc->sc_rxtq, ath_rx_tasklet, dev); + ATH_INIT_TQUEUE(&sc->sc_txtq, ath_tx_tasklet, dev); + ATH_INIT_TQUEUE(&sc->sc_bmisstq,ath_bmiss_tasklet, dev); + ATH_INIT_TQUEUE(&sc->sc_rxorntq,ath_rxorn_tasklet, dev); + ATH_INIT_TQUEUE(&sc->sc_fataltq,ath_fatal_tasklet, dev); + + /* + * Attach the hal and verify ABI compatibility by checking + * the hal's ABI signature against the one the driver was + * compiled with. A mismatch indicates the driver was + * built with an ah.h that does not correspond to the hal + * module loaded in the kernel. + */ + ah = _ath_hal_attach(devid, sc, 0, (void *) dev->mem_start, &status); + if (ah == NULL) { + printk(KERN_ERR "%s: unable to attach hardware: '%s' (HAL status %u)\n", + dev->name, hal_status_desc[status], status); + error = ENXIO; + goto bad; + } + if (ah->ah_abi != HAL_ABI_VERSION) { + printk(KERN_ERR "%s: HAL ABI mismatch; " + "driver expects 0x%x, HAL reports 0x%x\n", + dev->name, HAL_ABI_VERSION, ah->ah_abi); + error = ENXIO; /* XXX */ + goto bad; + } + sc->sc_ah = ah; + + /* + * Check if the MAC has multi-rate retry support. + * We do this by trying to setup a fake extended + * descriptor. MAC's that don't have support will + * return false w/o doing anything. MAC's that do + * support it will return true w/o doing anything. + */ + sc->sc_mrretry = ath_hal_setupxtxdesc(ah, NULL, 0,0, 0,0, 0,0); + + /* + * Check if the device has hardware counters for PHY + * errors. If so we need to enable the MIB interrupt + * so we can act on stat triggers. + */ + if (ath_hal_hwphycounters(ah)) + sc->sc_needmib = 1; + + /* + * Get the hardware key cache size. + */ + sc->sc_keymax = ath_hal_keycachesize(ah); + if (sc->sc_keymax > sizeof(sc->sc_keymap) * NBBY) { + printk("%s: Warning, using only %u entries in %u key cache\n", + dev->name, sizeof(sc->sc_keymap) * NBBY, sc->sc_keymax); + sc->sc_keymax = sizeof(sc->sc_keymap) * NBBY; + } + /* + * Reset the key cache since some parts do not + * reset the contents on initial power up. + */ + for (i = 0; i < sc->sc_keymax; i++) + ath_hal_keyreset(ah, i); + /* + * Mark key cache slots associated with global keys + * as in use. If we knew TKIP was not to be used we + * could leave the +32, +64, and +32+64 slots free. + * XXX only for splitmic. + */ + for (i = 0; i < IEEE80211_WEP_NKID; i++) { + setbit(sc->sc_keymap, i); + setbit(sc->sc_keymap, i+32); + setbit(sc->sc_keymap, i+64); + setbit(sc->sc_keymap, i+32+64); + } + + /* + * Collect the channel list using the default country + * code and including outdoor channels. The 802.11 layer + * is resposible for filtering this list based on settings + * like the phy mode. + */ + if (countrycode != -1) + ath_countrycode = countrycode; + if (outdoor != -1) + ath_outdoor = outdoor; + if (xchanmode != -1) + ath_xchanmode = xchanmode; + error = ath_getchannels(dev, ath_countrycode, + ath_outdoor, ath_xchanmode); + if (error != 0) + goto bad; + + /* + * Setup rate tables for all potential media types. + */ + ath_rate_setup(dev, IEEE80211_MODE_11A); + ath_rate_setup(dev, IEEE80211_MODE_11B); + ath_rate_setup(dev, IEEE80211_MODE_11G); + ath_rate_setup(dev, IEEE80211_MODE_TURBO); + /* NB: setup here so ath_rate_update is happy */ + ath_setcurmode(sc, IEEE80211_MODE_11A); + + /* + * Allocate tx+rx descriptors and populate the lists. + */ + error = ath_desc_alloc(sc); + if (error != 0) { + printk(KERN_ERR "%s: failed to allocate descriptors: %d\n", + dev->name, error); + goto bad; + } + + /* + * Allocate hardware transmit queues: one queue for + * beacon frames and one data queue for each QoS + * priority. Note that the hal handles reseting + * these queues at the needed time. + * + * XXX PS-Poll + */ + sc->sc_bhalq = ath_hal_setuptxqueue(ah, HAL_TX_QUEUE_BEACON, NULL); + if (sc->sc_bhalq == (u_int) -1) { + printk(KERN_ERR "%s: unable to setup a beacon xmit queue!\n", + dev->name); + error = EIO; + goto bad2; + } + /* NB: insure BK queue is h/w queue 0 */ + if (!ath_tx_setup(sc, WME_AC_BE, HAL_WME_AC_BK) || + !ath_tx_setup(sc, WME_AC_BK, HAL_WME_AC_BE) || + !ath_tx_setup(sc, WME_AC_VI, HAL_WME_AC_VI) || + !ath_tx_setup(sc, WME_AC_VO, HAL_WME_AC_VO)) { + error = EIO; + goto bad2; + } + /* + * Special case certain configurations. + */ + switch (sc->sc_txqsetup) { + case 0x01: + ATH_INIT_TQUEUE(&sc->sc_txtq, ath_tx_tasklet_q0, dev); + break; + case 0x0f: + ATH_INIT_TQUEUE(&sc->sc_txtq, ath_tx_tasklet_q0123, dev); + break; + } + + sc->sc_rc = ath_rate_attach(sc); + if (sc->sc_rc == NULL) { + error = EIO; + goto bad2; + } + + init_timer(&sc->sc_scan_ch); + sc->sc_scan_ch.function = ath_next_scan; + sc->sc_scan_ch.data = (unsigned long) dev; + + init_timer(&sc->sc_cal_ch); + sc->sc_cal_ch.function = ath_calibrate; + sc->sc_cal_ch.data = (unsigned long) dev; + + sc->sc_ledstate = 1; + /* + * Auto-enable soft led processing for IBM cards and for + * 5211 minipci cards. Users can also manually enable/disable + * support with a sysctl. + */ + sc->sc_softled = (devid == AR5212_DEVID_IBM || devid == AR5211_DEVID); + if (sc->sc_softled) { + ath_hal_gpioCfgOutput(ah, sc->sc_ledpin); + ath_hal_gpioset(ah, sc->sc_ledpin, 0); + } + + ether_setup(dev); + dev->open = ath_init; + dev->stop = ath_stop; + dev->hard_start_xmit = ath_hardstart; + dev->tx_timeout = ath_tx_timeout; + dev->watchdog_timeo = 5 * HZ; /* XXX */ + dev->set_multicast_list = ath_mode_init; + dev->do_ioctl = ath_ioctl; + dev->get_stats = ath_getstats; + dev->set_mac_address = ath_set_mac_address; + dev->change_mtu = &ath_change_mtu; + dev->tx_queue_len = ATH_TXBUF-1; /* 1 for mgmt frame */ +#ifdef CONFIG_NET_WIRELESS + dev->get_wireless_stats = ath_iw_getstats; + ieee80211_ioctl_iwsetup(&ath_iw_handler_def); + dev->wireless_handlers = &ath_iw_handler_def; +#endif /* CONFIG_NET_WIRELESS */ +#if IEEE80211_VLAN_TAG_USED + dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; + dev->vlan_rx_register = ath_vlan_register; + dev->vlan_rx_kill_vid = ath_vlan_kill_vid; +#endif /* IEEE80211_VLAN_TAG_USED */ + + ic->ic_dev = dev; + ic->ic_devstats = &sc->sc_devstats; + ic->ic_mgtstart = ath_mgtstart; + ic->ic_init = ath_init; + ic->ic_reset = ath_reset; + ic->ic_newassoc = ath_newassoc; + ic->ic_updateslot = ath_updateslot; + /* XXX not right but it's not used anywhere important */ + ic->ic_phytype = IEEE80211_T_OFDM; + ic->ic_opmode = IEEE80211_M_STA; + ic->ic_caps = + IEEE80211_C_IBSS /* ibss, nee adhoc, mode */ + | IEEE80211_C_HOSTAP /* hostap mode */ + | IEEE80211_C_MONITOR /* monitor mode */ + | IEEE80211_C_SHPREAMBLE /* short preamble supported */ + | IEEE80211_C_SHSLOT /* short slot time supported */ + | IEEE80211_C_TXPMGT /* transmit power control */ + ; + /* + * Query the hal to figure out h/w crypto support. + */ + if (ath_hal_ciphersupported(ah, HAL_CIPHER_WEP)) + ic->ic_caps |= IEEE80211_C_WEP; + if (ath_hal_ciphersupported(ah, HAL_CIPHER_AES_OCB)) + ic->ic_caps |= IEEE80211_C_AES; + if (ath_hal_ciphersupported(ah, HAL_CIPHER_AES_CCM)) + ic->ic_caps |= IEEE80211_C_AES_CCM; + if (ath_hal_ciphersupported(ah, HAL_CIPHER_CKIP)) + ic->ic_caps |= IEEE80211_C_CKIP; + if (ath_hal_ciphersupported(ah, HAL_CIPHER_TKIP)) { + ic->ic_caps |= IEEE80211_C_TKIP; + /* + * Check if h/w does the MIC and/or whether the + * separate key cache entries are required to + * handle both tx+rx MIC keys. + */ + if (ath_hal_ciphersupported(ah, HAL_CIPHER_MIC)) + ic->ic_caps |= IEEE80211_C_TKIPMIC; + if (ath_hal_tkipsplit(ah)) + sc->sc_splitmic = 1; + } + /* + * Indicate we need the 802.11 header padded to a + * 32-bit boundary for 4-address and QoS frames. + */ + ic->ic_flags |= IEEE80211_F_DATAPAD; + + /* + * Query the hal about antenna support. + */ + if (ath_hal_hasdiversity(ah)) { + sc->sc_hasdiversity = 1; + sc->sc_diversity = ath_hal_getdiversity(ah); + } + sc->sc_defant = ath_hal_getdefantenna(ah); + + /* get mac address from hardware */ + ath_hal_getmac(ah, ic->ic_myaddr); + IEEE80211_ADDR_COPY(dev->dev_addr, ic->ic_myaddr); + + /* call MI attach routine. */ + ieee80211_ifattach(ic); + /* override default methods */ + ic->ic_node_alloc = ath_node_alloc; + sc->sc_node_cleanup = ic->ic_node_cleanup; + ic->ic_node_cleanup = ath_node_cleanup; + sc->sc_node_copy = ic->ic_node_copy; + ic->ic_node_copy = ath_node_copy; + ic->ic_node_getrssi = ath_node_getrssi; + sc->sc_recv_mgmt = ic->ic_recv_mgmt; + ic->ic_recv_mgmt = ath_recv_mgmt; + sc->sc_newstate = ic->ic_newstate; + ic->ic_newstate = ath_newstate; + ic->ic_crypto.cs_key_alloc = ath_key_alloc; + ic->ic_crypto.cs_key_delete = ath_key_delete; + ic->ic_crypto.cs_key_set = ath_key_set; + ic->ic_crypto.cs_key_update_begin = ath_key_update_begin; + ic->ic_crypto.cs_key_update_end = ath_key_update_end; + + /* complete initialization */ + ieee80211_media_init(ic, ath_media_change, ieee80211_media_status); + + if (register_netdev(dev)) { + printk(KERN_ERR "%s: unable to register device\n", dev->name); + goto bad3; + } + /* + * Attach dynamic MIB vars and announce support + * now that we have a device name with unit number. + */ +#ifdef CONFIG_SYSCTL + ath_dynamic_sysctl_register(sc); + ieee80211_sysctl_register(ic); +#endif /* CONFIG_SYSCTL */ + ieee80211_announce(ic); + ath_announce(dev); + return 0; +bad3: + ieee80211_ifdetach(ic); + ath_rate_detach(sc->sc_rc); +bad2: + if (sc->sc_txq[WME_AC_BK].axq_qnum != (u_int) -1) + ATH_TXQ_LOCK_DESTROY(&sc->sc_txq[WME_AC_BK]); + if (sc->sc_txq[WME_AC_BE].axq_qnum != (u_int) -1) + ATH_TXQ_LOCK_DESTROY(&sc->sc_txq[WME_AC_BE]); + if (sc->sc_txq[WME_AC_VI].axq_qnum != (u_int) -1) + ATH_TXQ_LOCK_DESTROY(&sc->sc_txq[WME_AC_VI]); + if (sc->sc_txq[WME_AC_VO].axq_qnum != (u_int) -1) + ATH_TXQ_LOCK_DESTROY(&sc->sc_txq[WME_AC_VO]); + ath_desc_free(sc); +bad: + if (ah) + ath_hal_detach(ah); + ATH_TXBUF_LOCK_DESTROY(sc); + ATH_LOCK_DESTROY(sc); + sc->sc_invalid = 1; + return error; +} + +int +ath_detach(struct net_device *dev) +{ + struct ath_softc *sc = dev->priv; + struct ieee80211com *ic = &sc->sc_ic; + int i; + + DPRINTF(sc, ATH_DEBUG_ANY, "%s: flags %x\n", __func__, dev->flags); + ath_stop(dev); + sc->sc_invalid = 1; + /* + * NB: Must do this before detaching the hal to insure + * callbacks into the driver to delete global key + * entries can be handled. + */ + ieee80211_ifdetach(ic); + ath_rate_detach(sc->sc_rc); + ath_desc_free(sc); + ath_hal_detach(sc->sc_ah); + + /* + * NB: can't reclaim these until after ieee80211_ifdetach + * returns because we'll get called back to reclaim node + * state and potentially want to use them. + */ + ATH_TXBUF_LOCK_DESTROY(sc); + for (i = 0; i < HAL_NUM_TX_QUEUES; i++) + if (ATH_TXQ_SETUP(sc, i)) + ATH_TXQ_LOCK_DESTROY(&sc->sc_txq[i]); + +#ifdef CONFIG_SYSCTL + ath_dynamic_sysctl_unregister(sc); +#endif /* CONFIG_SYSCTL */ + ATH_LOCK_DESTROY(sc); + unregister_netdev(dev); + + return 0; +} + +void +ath_suspend(struct net_device *dev) +{ + struct ath_softc *sc = dev->priv; + + DPRINTF(sc, ATH_DEBUG_ANY, "%s: flags %x\n", __func__, dev->flags); + ath_stop(dev); +} + +void +ath_resume(struct net_device *dev) +{ + struct ath_softc *sc = dev->priv; + + DPRINTF(sc, ATH_DEBUG_ANY, "%s: flags %x\n", __func__, dev->flags); + ath_init(dev); +} + +void +ath_shutdown(struct net_device *dev) +{ + struct ath_softc *sc = dev->priv; + + DPRINTF(sc, ATH_DEBUG_ANY, "%s: flags %x\n", __func__, dev->flags); + ath_stop(dev); +} + +/* + * Interrupt handler. Most of the actual processing is deferred. + */ +irqreturn_t +ath_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = dev_id; + struct ath_softc *sc = dev->priv; + struct ath_hal *ah = sc->sc_ah; + HAL_INT status; + int needmark; + + if (sc->sc_invalid) { + /* + * The hardware is not ready/present, don't touch anything. + * Note this can happen early on if the IRQ is shared. + */ + return IRQ_NONE; + } + if (!ath_hal_intrpend(ah)) /* shared irq, not for us */ + return IRQ_NONE; + if ((dev->flags & (IFF_RUNNING|IFF_UP)) != (IFF_RUNNING|IFF_UP)) { + DPRINTF(sc, ATH_DEBUG_INTR, "%s: flags 0x%x\n", + __func__, dev->flags); + ath_hal_getisr(ah, &status); /* clear ISR */ + ath_hal_intrset(ah, 0); /* disable further intr's */ + return IRQ_HANDLED; + } + needmark = 0; + /* + * Figure out the reason(s) for the interrupt. Note + * that the hal returns a pseudo-ISR that may include + * bits we haven't explicitly enabled so we mask the + * value to insure we only process bits we requested. + */ + ath_hal_getisr(ah, &status); /* NB: clears ISR too */ + DPRINTF(sc, ATH_DEBUG_INTR, "%s: status 0x%x\n", __func__, status); + status &= sc->sc_imask; /* discard unasked for bits */ + if (status & HAL_INT_FATAL) { + /* + * Fatal errors are unrecoverable. Typically + * these are caused by DMA errors. Unfortunately + * the exact reason is not (presently) returned + * by the hal. + */ + sc->sc_stats.ast_hardware++; + ath_hal_intrset(ah, 0); /* disable intr's until reset */ + ATH_SCHEDULE_TQUEUE(&sc->sc_fataltq, &needmark); + } else if (status & HAL_INT_RXORN) { + sc->sc_stats.ast_rxorn++; + ath_hal_intrset(ah, 0); /* disable intr's until reset */ + ATH_SCHEDULE_TQUEUE(&sc->sc_rxorntq, &needmark); + } else { + if (status & HAL_INT_RXEOL) { + /* + * NB: the hardware should re-read the link when + * RXE bit is written, but it doesn't work at + * least on older hardware revs. + */ + sc->sc_stats.ast_rxeol++; + sc->sc_rxlink = NULL; + } + if (status & HAL_INT_TXURN) { + sc->sc_stats.ast_txurn++; + /* bump tx trigger level */ + ath_hal_updatetxtriglevel(ah, AH_TRUE); + } + if (status & HAL_INT_RX) + ATH_SCHEDULE_TQUEUE(&sc->sc_rxtq, &needmark); + if (status & HAL_INT_TX) + ATH_SCHEDULE_TQUEUE(&sc->sc_txtq, &needmark); + if (status & HAL_INT_SWBA) { + /* + * Software beacon alert--time to send a beacon. + * Handle beacon transmission directly; deferring + * this is too slow to meet timing constraints + * under load. + */ + ath_beacon_tasklet(dev); + } + if (status & HAL_INT_BMISS) { + sc->sc_stats.ast_bmiss++; + ATH_SCHEDULE_TQUEUE(&sc->sc_bmisstq, &needmark); + } + if (status & HAL_INT_MIB) { + sc->sc_stats.ast_mib++; + /* + * Disable interrupts until we service the MIB + * interrupt; otherwise it will continue to fire. + */ + ath_hal_intrset(ah, 0); + /* + * Let the hal handle the event. We assume it will + * clear whatever condition caused the interrupt. + */ + ath_hal_mibevent(ah, + &ATH_NODE(sc->sc_ic.ic_bss)->an_halstats); + ath_hal_intrset(ah, sc->sc_imask); + } + } + if (needmark) + mark_bh(IMMEDIATE_BH); + return IRQ_HANDLED; +} + +static void +ath_fatal_tasklet(TQUEUE_ARG data) +{ + struct net_device *dev = (struct net_device *)data; + + printk("%s: hardware error; reseting\n", dev->name); + ath_reset(dev); +} + +static void +ath_rxorn_tasklet(TQUEUE_ARG data) +{ + struct net_device *dev = (struct net_device *)data; + + printk("%s: rx FIFO overrun; reseting\n", dev->name); + ath_reset(dev); +} + +static void +ath_bmiss_tasklet(TQUEUE_ARG data) +{ + struct net_device *dev = (struct net_device *)data; + struct ath_softc *sc = dev->priv; + struct ieee80211com *ic = &sc->sc_ic; + + DPRINTF(sc, ATH_DEBUG_ANY, "%s\n", __func__); + KASSERT(ic->ic_opmode == IEEE80211_M_STA, + ("unexpect operating mode %u", ic->ic_opmode)); + + if (ic->ic_state == IEEE80211_S_RUN) { + /* + * Rather than go directly to scan state, try to + * reassociate first. If that fails then the state + * machine will drop us into scanning after timing + * out waiting for a probe response. + */ + ieee80211_new_state(ic, IEEE80211_S_ASSOC, -1); + } +} + +static u_int +ath_chan2flags(struct ieee80211com *ic, struct ieee80211_channel *chan) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + static const u_int modeflags[] = { + 0, /* IEEE80211_MODE_AUTO */ + CHANNEL_A, /* IEEE80211_MODE_11A */ + CHANNEL_B, /* IEEE80211_MODE_11B */ + CHANNEL_PUREG, /* IEEE80211_MODE_11G */ + 0, /* IEEE80211_MODE_FH */ + CHANNEL_T /* IEEE80211_MODE_TURBO */ + }; + enum ieee80211_phymode mode = ieee80211_chan2mode(ic, chan); + + KASSERT(mode < N(modeflags), ("unexpected phy mode %u", mode)); + KASSERT(modeflags[mode] != 0, ("mode %u undefined", mode)); + return modeflags[mode]; +#undef N +} + +static int +ath_init(struct net_device *dev) +{ + struct ath_softc *sc = dev->priv; + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni; + enum ieee80211_phymode mode; + struct ath_hal *ah = sc->sc_ah; + HAL_STATUS status; + int error = 0; + + ATH_LOCK(sc); + + DPRINTF(sc, ATH_DEBUG_RESET, "%s: mode %d\n", __func__, ic->ic_opmode); + + /* + * Stop anything previously setup. This is safe + * whether this is the first time through or not. + */ + ath_stop_locked(dev); + + /* + * Resize receive skb's if changing to or from monitor mode + */ + if ((dev->type == ARPHRD_ETHER && + ic->ic_opmode == IEEE80211_M_MONITOR) || + (dev->type == ARPHRD_IEEE80211_PRISM && + ic->ic_opmode != IEEE80211_M_MONITOR)) { + struct ath_buf *bf; + STAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) + if (bf->bf_skb != NULL) { + bus_unmap_single(sc->sc_bdev, + bf->bf_skbaddr, sc->sc_rxbufsize, + BUS_DMA_FROMDEVICE); + dev_kfree_skb(bf->bf_skb); + bf->bf_skb = NULL; + } + } + /* + * Change our interface type if we are in monitor mode. + */ + dev->type = (ic->ic_opmode == IEEE80211_M_MONITOR) ? + ARPHRD_IEEE80211_PRISM : ARPHRD_ETHER; + + /* + * The basic interface to setting the hardware in a good + * state is ``reset''. On return the hardware is known to + * be powered up and with interrupts disabled. This must + * be followed by initialization of the appropriate bits + * and then setup of the interrupt mask. + */ + sc->sc_curchan.channel = ic->ic_ibss_chan->ic_freq; + sc->sc_curchan.channelFlags = ath_chan2flags(ic, ic->ic_ibss_chan); + if (!ath_hal_reset(ah, ic->ic_opmode, &sc->sc_curchan, AH_FALSE, &status)) { + printk("%s: unable to reset hardware: '%s' (HAL status %u)\n", + dev->name, hal_status_desc[status], status); + error = -EIO; + goto done; + } + + /* + * Setup the hardware after reset: the key cache + * is filled as needed and the receive engine is + * set going. Frame transmit is handled entirely + * in the frame output path; there's nothing to do + * here except setup the interrupt mask. + */ + ath_initkeytable(sc); /* XXX still needed? */ + if (ath_startrecv(sc) != 0) { + printk("%s: unable to start recv logic\n", dev->name); + error = -EIO; + goto done; + } + + /* + * Enable interrupts. + */ + sc->sc_imask = HAL_INT_RX | HAL_INT_TX + | HAL_INT_RXEOL | HAL_INT_RXORN + | HAL_INT_FATAL | HAL_INT_GLOBAL; + /* + * Enable MIB interrupts when there are hardware phy counters. + * Note we only do this (at the moment) for station mode. + */ + if (sc->sc_needmib && ic->ic_opmode == IEEE80211_M_STA) + sc->sc_imask |= HAL_INT_MIB; + ath_hal_intrset(ah, sc->sc_imask); + + dev->flags |= IFF_RUNNING; + ic->ic_state = IEEE80211_S_INIT; + + /* + * The hardware should be ready to go now so it's safe + * to kick the 802.11 state machine as it's likely to + * immediately call back to us to send mgmt frames. + */ + ni = ic->ic_bss; + ni->ni_chan = ic->ic_ibss_chan; + mode = ieee80211_chan2mode(ic, ni->ni_chan); + if (mode != sc->sc_curmode) + ath_setcurmode(sc, mode); + if (ic->ic_opmode != IEEE80211_M_MONITOR) { + if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL) + ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); + } else + ieee80211_new_state(ic, IEEE80211_S_RUN, -1); +done: + ATH_UNLOCK(sc); + return error; +} + +static int +ath_stop_locked(struct net_device *dev) +{ + struct ath_softc *sc = dev->priv; + struct ieee80211com *ic = &sc->sc_ic; + struct ath_hal *ah = sc->sc_ah; + + DPRINTF(sc, ATH_DEBUG_RESET, "%s: invalid %u flags 0x%x\n", + __func__, sc->sc_invalid, dev->flags); + + if (dev->flags & IFF_RUNNING) { + /* + * Shutdown the hardware and driver: + * reset 802.11 state machine (do first so + * station deassoc/deauth frames can be sent) + * stop output from above + * disable interrupts + * turn off timers + * turn off the radio + * clear transmit machinery + * clear receive machinery + * reclaim beacon resources + * power down hardware + * + * Note that some of this work is not possible if the + * hardware is gone (invalid). + */ + ieee80211_new_state(ic, IEEE80211_S_INIT, -1); + netif_stop_queue(dev); + dev->flags &= ~IFF_RUNNING; + if (!sc->sc_invalid) { + if (sc->sc_softled) + ath_hal_gpioset(ah, sc->sc_ledpin, 1); + ath_hal_intrset(ah, 0); + } + ath_draintxq(sc); + if (!sc->sc_invalid) { + ath_stoprecv(sc); + ath_hal_phydisable(ah); + } else + sc->sc_rxlink = NULL; + ath_beacon_free(sc); + } + return 0; +} + +/* + * Stop the device, grabbing the top-level lock to protect + * against concurrent entry through ath_init (which can happen + * if another thread does a system call and the thread doing the + * stop is preempted). + */ +static int +ath_stop(struct net_device *dev) +{ + struct ath_softc *sc = dev->priv; + int error; + + ATH_LOCK(sc); + error = ath_stop_locked(dev); + if (error == 0 && !sc->sc_invalid) { + /* + * Set the chip in full sleep mode. Note that we are + * careful to do this only when bringing the interface + * completely to a stop. When the chip is in this state + * it must be carefully woken up or references to + * registers in the PCI clock domain may freeze the bus + * (and system). This varies by chip and is mostly an + * issue with newer parts that go to sleep more quickly. + */ + ath_hal_setpower(sc->sc_ah, HAL_PM_FULL_SLEEP, 0); + } + ATH_UNLOCK(sc); + return error; +} + +/* + * Reset the hardware w/o losing operational state. This is + * basically a more efficient way of doing ath_stop, ath_init, + * followed by state transitions to the current 802.11 + * operational state. Used to recover from errors rx overrun + * and to reset the hardware when rf gain settings must be reset. + */ +static int +ath_reset(struct net_device *dev) +{ + struct ath_softc *sc = dev->priv; + struct ieee80211com *ic = &sc->sc_ic; + struct ath_hal *ah = sc->sc_ah; + struct ieee80211_channel *c; + HAL_STATUS status; + + /* + * Convert to a HAL channel description with the flags + * constrained to reflect the current operating mode. + */ + c = ic->ic_ibss_chan; + sc->sc_curchan.channel = c->ic_freq; + sc->sc_curchan.channelFlags = ath_chan2flags(ic, c); + + ath_hal_intrset(ah, 0); /* disable interrupts */ + ath_draintxq(sc); /* stop xmit side */ + ath_stoprecv(sc); /* stop recv side */ + /* NB: indicate channel change so we do a full reset */ + if (!ath_hal_reset(ah, ic->ic_opmode, &sc->sc_curchan, AH_TRUE, &status)) + printk("%s: %s: unable to reset hardware: '%s' (HAL status %u)\n", + dev->name, __func__, hal_status_desc[status], status); + if (ath_startrecv(sc) != 0) /* restart recv */ + printk("%s: %s: unable to start recv logic\n", + dev->name, __func__); + /* + * We may be doing a reset in response to an ioctl + * that changes the channel so update any state that + * might change as a result. + */ + ath_chan_change(sc, c); + if (ic->ic_state == IEEE80211_S_RUN) + ath_beacon_config(sc); /* restart beacons */ + ath_hal_intrset(ah, sc->sc_imask); + + if (ic->ic_state == IEEE80211_S_RUN) + netif_wake_queue(dev); /* restart xmit */ + return 0; +} + +/* + * Transmit a data packet. On failure caller is + * assumed to reclaim the resources. + */ +static int +ath_hardstart(struct sk_buff *skb, struct net_device *dev) +{ + struct ath_softc *sc = dev->priv; + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni = NULL; + struct ath_buf *bf = NULL; + int pktlen; + + if ((dev->flags & IFF_RUNNING) == 0 || sc->sc_invalid) { + DPRINTF(sc, ATH_DEBUG_XMIT, + "%s: discard, invalid %d flags %x\n", + __func__, sc->sc_invalid, dev->flags); + sc->sc_stats.ast_tx_invalid++; + return -ENETDOWN; + } + /* + * No data frames go out unless we're associated; this + * should not happen as the 802.11 layer does not enable + * the xmit queue until we enter the RUN state. + */ + if (ic->ic_state != IEEE80211_S_RUN) { + DPRINTF(sc, ATH_DEBUG_XMIT, "%s: discard, state %u\n", + __func__, ic->ic_state); + sc->sc_stats.ast_tx_discard++; + goto bad; + } + + /* + * Grab a TX buffer and associated resources. + */ + ATH_TXBUF_LOCK_BH(sc); + bf = STAILQ_FIRST(&sc->sc_txbuf); + if (bf != NULL) + STAILQ_REMOVE_HEAD(&sc->sc_txbuf, bf_list); + /* XXX use a counter and leave at least one for mgmt frames */ + if (STAILQ_EMPTY(&sc->sc_txbuf)) { + DPRINTF(sc, ATH_DEBUG_XMIT, "%s: stop queue\n", __func__); + sc->sc_stats.ast_tx_qstop++; + netif_stop_queue(dev); + } + ATH_TXBUF_UNLOCK_BH(sc); + if (bf == NULL) { /* NB: should not happen */ + printk("%s: discard, no xmit buf\n", __func__); + sc->sc_stats.ast_tx_nobuf++; + goto bad; + } + + /* + * Encapsulate the packet for transmission. + */ + skb = ieee80211_encap(ic, skb, &ni); + if (skb == NULL) { + DPRINTF(sc, ATH_DEBUG_XMIT, + "%s: discard, encapsulation failure\n", __func__); + sc->sc_stats.ast_tx_encap++; + goto bad; + } + pktlen = skb->len; /* NB: don't reference skb below */ + if (ath_tx_start(dev, ni, bf, skb) == 0) { + sc->sc_devstats.tx_packets++; + sc->sc_devstats.tx_bytes += pktlen; + return 0; + } + /* fall thru... */ +bad: + if (ni && ni != ic->ic_bss) + ieee80211_free_node(ic, ni); + if (bf != NULL) { + ATH_TXBUF_LOCK_BH(sc); + STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list); + ATH_TXBUF_UNLOCK_BH(sc); + } + if (skb) + dev_kfree_skb(skb); + return 0; /* NB: return !0 only in a ``hard error condition'' */ +} + +/* + * Transmit a management frame. On failure we reclaim the skbuff. + * Note that management frames come directly from the 802.11 layer + * and do not honor the send queue flow control. Need to investigate + * using priority queueing so management frames can bypass data. + */ +static int +ath_mgtstart(struct ieee80211com *ic, struct sk_buff *skb) +{ + struct net_device *dev = ic->ic_dev; + struct ath_softc *sc = dev->priv; + struct ath_hal *ah = sc->sc_ah; + struct ieee80211_node *ni = NULL; + struct ath_buf *bf = NULL; + struct ieee80211_frame *wh; + struct ieee80211_cb *cb; + int error; + + if ((dev->flags & IFF_RUNNING) == 0 || sc->sc_invalid) { + DPRINTF(sc, ATH_DEBUG_XMIT, + "%s: discard, invalid %d flags %x\n", + __func__, sc->sc_invalid, dev->flags); + sc->sc_stats.ast_tx_invalid++; + error = -ENETDOWN; + goto bad; + } + /* + * Grab a TX buffer and associated resources. + */ + ATH_TXBUF_LOCK_BH(sc); + bf = STAILQ_FIRST(&sc->sc_txbuf); + if (bf != NULL) + STAILQ_REMOVE_HEAD(&sc->sc_txbuf, bf_list); + if (STAILQ_EMPTY(&sc->sc_txbuf)) { + DPRINTF(sc, ATH_DEBUG_XMIT, "%s: stop queue\n", __func__); + sc->sc_stats.ast_tx_qstop++; + netif_stop_queue(dev); + } + ATH_TXBUF_UNLOCK_BH(sc); + if (bf == NULL) { + printk("ath_mgtstart: discard, no xmit buf\n"); + sc->sc_stats.ast_tx_nobufmgt++; + error = -ENOBUFS; + goto bad; + } + + wh = (struct ieee80211_frame *) skb->data; + if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_PROBE_RESP) { + /* fill time stamp */ + u_int64_t tsf; + u_int32_t *tstamp; + + tsf = ath_hal_gettsf64(ah); + /* XXX: adjust 100us delay to xmit */ + tsf += 100; + tstamp = (u_int32_t *)&wh[1]; + tstamp[0] = cpu_to_le32(tsf & 0xffffffff); + tstamp[1] = cpu_to_le32(tsf >> 32); + } + + /* + * NB: the referenced node pointer is in the + * control block of the sk_buff. This is + * placed there by ieee80211_mgmt_output because + * we need to hold the reference with the frame. + */ + cb = (struct ieee80211_cb *)skb->cb; + ni = cb->ni; + error = ath_tx_start(dev, ni, bf, skb); + if (error == 0) { + sc->sc_stats.ast_tx_mgmt++; + return 0; + } + /* fall thru... */ +bad: + if (ni && ni != ic->ic_bss) + ieee80211_free_node(ic, ni); + if (bf != NULL) { + ATH_TXBUF_LOCK_BH(sc); + STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list); + ATH_TXBUF_UNLOCK_BH(sc); + } + dev_kfree_skb(skb); + return error; +} + +static int +ath_media_change(struct net_device *dev) +{ + int error; + + error = ieee80211_media_change(dev); + if (error == ENETRESET) { + if ((dev->flags & (IFF_RUNNING|IFF_UP)) == (IFF_RUNNING|IFF_UP)) + error = ath_init(dev); + else + error = 0; + } + return error; +} + + +#ifdef AR_DEBUG +static void +ath_keyprint(const char *tag, u_int ix, + const HAL_KEYVAL *hk, const u_int8_t mac[IEEE80211_ADDR_LEN]) +{ + static const char *ciphers[] = { + "WEP", + "AES-OCB", + "AES-CCM", + "CKIP", + "TKIP", + "CLR", + }; + int i, n; + + printk("%s: [%02u] %-7s ", tag, ix, ciphers[hk->kv_type]); + for (i = 0, n = hk->kv_len; i < n; i++) + printk("%02x", hk->kv_val[i]); + printk(" mac %s", ether_sprintf(mac)); + if (hk->kv_type == HAL_CIPHER_TKIP) { + printk(" mic "); + for (i = 0; i < sizeof(hk->kv_mic); i++) + printk("%02x", hk->kv_mic[i]); + } + printk("\n"); +} +#endif + +/* + * Set a TKIP key into the hardware. This handles the + * potential distribution of key state to multiple key + * cache slots for TKIP. + */ +static int +ath_keyset_tkip(struct ath_softc *sc, const struct ieee80211_key *k, + HAL_KEYVAL *hk, const u_int8_t mac[IEEE80211_ADDR_LEN]) +{ +#define IEEE80211_KEY_XR (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV) + static const u_int8_t zerobssid[IEEE80211_ADDR_LEN]; + struct ath_hal *ah = sc->sc_ah; + + KASSERT(k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP, + ("got a non-TKIP key, cipher %u", k->wk_cipher->ic_cipher)); + KASSERT(sc->sc_splitmic, ("key cache !split")); + if ((k->wk_flags & IEEE80211_KEY_XR) == IEEE80211_KEY_XR) { + /* + * TX key goes at first index, RX key at +32. + * The hal handles the MIC keys at index+64. + */ + memcpy(hk->kv_mic, k->wk_txmic, sizeof(hk->kv_mic)); + KEYPRINTF(sc, k->wk_keyix, hk, zerobssid); + if (!ath_hal_keyset(ah, k->wk_keyix, hk, zerobssid)) + return 0; + + memcpy(hk->kv_mic, k->wk_rxmic, sizeof(hk->kv_mic)); + KEYPRINTF(sc, k->wk_keyix+32, hk, mac); + /* XXX delete tx key on failure? */ + return ath_hal_keyset(ah, k->wk_keyix+32, hk, mac); + } else if (k->wk_flags & IEEE80211_KEY_XR) { + /* + * TX/RX key goes at first index. + * The hal handles the MIC keys are index+64. + */ + KASSERT(k->wk_keyix < IEEE80211_WEP_NKID, + ("group key at index %u", k->wk_keyix)); + memcpy(hk->kv_mic, k->wk_flags & IEEE80211_KEY_XMIT ? + k->wk_txmic : k->wk_rxmic, sizeof(hk->kv_mic)); + KEYPRINTF(sc, k->wk_keyix, hk, zerobssid); + return ath_hal_keyset(ah, k->wk_keyix, hk, zerobssid); + } + /* XXX key w/o xmit/recv; need this for compression? */ + return 0; +#undef IEEE80211_KEY_XR +} + +/* + * Set a net80211 key into the hardware. This handles the + * potential distribution of key state to multiple key + * cache slots for TKIP with hardware MIC support. + */ +static int +ath_keyset(struct ath_softc *sc, const struct ieee80211_key *k, + const u_int8_t mac[IEEE80211_ADDR_LEN]) +{ +#define N(a) (sizeof(a)/sizeof(a[0])) + static const u_int8_t ciphermap[] = { + HAL_CIPHER_WEP, /* IEEE80211_CIPHER_WEP */ + HAL_CIPHER_TKIP, /* IEEE80211_CIPHER_TKIP */ + HAL_CIPHER_AES_OCB, /* IEEE80211_CIPHER_AES_OCB */ + HAL_CIPHER_AES_CCM, /* IEEE80211_CIPHER_AES_CCM */ + (u_int8_t) -1, /* 4 is not allocated */ + HAL_CIPHER_CKIP, /* IEEE80211_CIPHER_CKIP */ + HAL_CIPHER_CLR, /* IEEE80211_CIPHER_NONE */ + }; + struct ath_hal *ah = sc->sc_ah; + const struct ieee80211_cipher *cip = k->wk_cipher; + HAL_KEYVAL hk; + + memset(&hk, 0, sizeof(hk)); + /* + * Software crypto uses a "clear key" so non-crypto + * state kept in the key cache are maintained and + * so that rx frames have an entry to match. + */ + if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) == 0) { + KASSERT(cip->ic_cipher < N(ciphermap), + ("invalid cipher type %u", cip->ic_cipher)); + hk.kv_type = ciphermap[cip->ic_cipher]; + hk.kv_len = k->wk_keylen; + memcpy(hk.kv_val, k->wk_key, k->wk_keylen); + } else + hk.kv_type = HAL_CIPHER_CLR; + + if (hk.kv_type == HAL_CIPHER_TKIP && + (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 && + sc->sc_splitmic) { + return ath_keyset_tkip(sc, k, &hk, mac); + } else { + KEYPRINTF(sc, k->wk_keyix, &hk, mac); + return ath_hal_keyset(ah, k->wk_keyix, &hk, mac); + } +#undef N +} + +/* + * Fill the hardware key cache with key entries. + */ +static void +ath_initkeytable(struct ath_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct net_device *dev = ic->ic_dev; + struct ath_hal *ah = sc->sc_ah; + u_int8_t *bssid; + int i; + + /* XXX maybe should reset all keys when !PRIVACY */ + if (ic->ic_state == IEEE80211_S_SCAN) + bssid = dev->broadcast; + else + bssid = ic->ic_bss->ni_bssid; + for (i = 0; i < IEEE80211_WEP_NKID; i++) { + struct ieee80211_key *k = &ic->ic_nw_keys[i]; + + if (k->wk_keylen == 0) { + ath_hal_keyreset(ah, i); + DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: reset key %u\n", + __func__, i); + } else { + ath_keyset(sc, k, bssid); + } + } +} + +/* + * Allocate tx/rx key slots for TKIP. We allocate two slots for + * each key, one for decrypt/encrypt and the other for the MIC. + */ +static u_int16_t +key_alloc_2pair(struct ath_softc *sc) +{ +#define N(a) (sizeof(a)/sizeof(a[0])) + u_int i, keyix; + + KASSERT(sc->sc_splitmic, ("key cache !split")); + /* XXX could optimize */ + for (i = 0; i < N(sc->sc_keymap)/4; i++) { + u_int8_t b = sc->sc_keymap[i]; + if (b != 0xff) { + /* + * One or more slots in this byte are free. + */ + keyix = i*NBBY; + while (b & 1) { + again: + keyix++; + b >>= 1; + } + /* XXX IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV */ + if (isset(sc->sc_keymap, keyix+32) || + isset(sc->sc_keymap, keyix+64) || + isset(sc->sc_keymap, keyix+32+64)) { + /* full pair unavailable */ + /* XXX statistic */ + if (keyix == (i+1)*NBBY) { + /* no slots were appropriate, advance */ + continue; + } + goto again; + } + setbit(sc->sc_keymap, keyix); + setbit(sc->sc_keymap, keyix+64); + setbit(sc->sc_keymap, keyix+32); + setbit(sc->sc_keymap, keyix+32+64); + DPRINTF(sc, ATH_DEBUG_KEYCACHE, + "%s: key pair %u,%u %u,%u\n", + __func__, keyix, keyix+64, + keyix+32, keyix+32+64); + return keyix; + } + } + DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: out of pair space\n", __func__); + return IEEE80211_KEYIX_NONE; +#undef N +} + +/* + * Allocate a single key cache slot. + */ +static u_int16_t +key_alloc_single(struct ath_softc *sc) +{ +#define N(a) (sizeof(a)/sizeof(a[0])) + u_int i, keyix; + + /* XXX try i,i+32,i+64,i+32+64 to minimize key pair conflicts */ + for (i = 0; i < N(sc->sc_keymap); i++) { + u_int8_t b = sc->sc_keymap[i]; + if (b != 0xff) { + /* + * One or more slots are free. + */ + keyix = i*NBBY; + while (b & 1) + keyix++, b >>= 1; + setbit(sc->sc_keymap, keyix); + DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: key %u\n", + __func__, keyix); + return keyix; + } + } + DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: out of space\n", __func__); + return IEEE80211_KEYIX_NONE; +#undef N +} + +/* + * Allocate one or more key cache slots for a uniacst key. The + * key itself is needed only to identify the cipher. For hardware + * TKIP with split cipher+MIC keys we allocate two key cache slot + * pairs so that we can setup separate TX and RX MIC keys. Note + * that the MIC key for a TKIP key at slot i is assumed by the + * hardware to be at slot i+64. This limits TKIP keys to the first + * 64 entries. + */ +static int +ath_key_alloc(struct ieee80211com *ic, const struct ieee80211_key *k) +{ + struct net_device *dev = ic->ic_dev; + struct ath_softc *sc = dev->priv; + + /* + * We allocate two pair for TKIP when using the h/w to do + * the MIC. For everything else, including software crypto, + * we allocate a single entry. Note that s/w crypto requires + * a pass-through slot on the 5211 and 5212. The 5210 does + * not support pass-through cache entries and we map all + * those requests to slot 0. + */ + if (k->wk_flags & IEEE80211_KEY_SWCRYPT) { + return key_alloc_single(sc); + } else if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP && + (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 && sc->sc_splitmic) { + return key_alloc_2pair(sc); + } else { + return key_alloc_single(sc); + } +} + +/* + * Delete an entry in the key cache allocated by ath_key_alloc. + */ +static int +ath_key_delete(struct ieee80211com *ic, const struct ieee80211_key *k) +{ + struct net_device *dev = ic->ic_dev; + struct ath_softc *sc = dev->priv; + struct ath_hal *ah = sc->sc_ah; + const struct ieee80211_cipher *cip = k->wk_cipher; + u_int keyix = k->wk_keyix; + + DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: delete key %u\n", __func__, keyix); + + ath_hal_keyreset(ah, keyix); + /* + * Handle split tx/rx keying required for TKIP with h/w MIC. + */ + if (cip->ic_cipher == IEEE80211_CIPHER_TKIP && + (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 && sc->sc_splitmic) + ath_hal_keyreset(ah, keyix+32); /* RX key */ + if (keyix >= IEEE80211_WEP_NKID) { + /* + * Don't touch keymap entries for global keys so + * they are never considered for dynamic allocation. + */ + clrbit(sc->sc_keymap, keyix); + if (cip->ic_cipher == IEEE80211_CIPHER_TKIP && + (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 && + sc->sc_splitmic) { + clrbit(sc->sc_keymap, keyix+64); /* TX key MIC */ + clrbit(sc->sc_keymap, keyix+32); /* RX key */ + clrbit(sc->sc_keymap, keyix+32+64); /* RX key MIC */ + } + } + return 1; +} + +/* + * Set the key cache contents for the specified key. Key cache + * slot(s) must already have been allocated by ath_key_alloc. + */ +static int +ath_key_set(struct ieee80211com *ic, const struct ieee80211_key *k, + const u_int8_t mac[IEEE80211_ADDR_LEN]) +{ + struct net_device *dev = ic->ic_dev; + struct ath_softc *sc = dev->priv; + + return ath_keyset(sc, k, mac); +} + +/* + * Block/unblock tx+rx processing while a key change is done. + * We assume the caller serializes key management operations + * so we only need to worry about synchronization with other + * uses that originate in the driver. + */ +static void +ath_key_update_begin(struct ieee80211com *ic) +{ + struct net_device *dev = ic->ic_dev; + struct ath_softc *sc = dev->priv; + + DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s:\n", __func__); + /* + * When called from the rx tasklet we cannot use + * tasklet_disable because it will block waiting + * for us to complete execution. + * + * XXX Using in_softirq is not right since we might + * be called from other soft irq contexts than + * ath_rx_tasklet. + */ + if (!in_softirq()) + tasklet_disable(&sc->sc_rxtq); + netif_stop_queue(dev); +} + +static void +ath_key_update_end(struct ieee80211com *ic) +{ + struct net_device *dev = ic->ic_dev; + struct ath_softc *sc = dev->priv; + + DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s:\n", __func__); + netif_start_queue(dev); + if (!in_softirq()) /* NB: see above */ + tasklet_enable(&sc->sc_rxtq); +} + +/* + * Calculate the receive filter according to the + * operating mode and state: + * + * o always accept unicast, broadcast, and multicast traffic + * o maintain current state of phy error reception (the hal + * may enable phy error frames for noise immunity work) + * o probe request frames are accepted only when operating in + * hostap, adhoc, or monitor modes + * o enable promiscuous mode according to the interface state + * o accept beacons: + * - when operating in adhoc mode so the 802.11 layer creates + * node table entries for peers, + * - when operating in station mode for collecting rssi data when + * the station is otherwise quiet, or + * - when scanning + */ +static u_int32_t +ath_calcrxfilter(struct ath_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct net_device *dev = ic->ic_dev; + struct ath_hal *ah = sc->sc_ah; + u_int32_t rfilt; + + rfilt = (ath_hal_getrxfilter(ah) & HAL_RX_FILTER_PHYERR) + | HAL_RX_FILTER_UCAST | HAL_RX_FILTER_BCAST | HAL_RX_FILTER_MCAST; + if (ic->ic_opmode != IEEE80211_M_STA) + rfilt |= HAL_RX_FILTER_PROBEREQ; + if (ic->ic_opmode != IEEE80211_M_HOSTAP && (dev->flags & IFF_PROMISC)) + rfilt |= HAL_RX_FILTER_PROM; + if (ic->ic_opmode == IEEE80211_M_STA || + ic->ic_opmode == IEEE80211_M_IBSS || + ic->ic_state == IEEE80211_S_SCAN) + rfilt |= HAL_RX_FILTER_BEACON; + return rfilt; +} + +static void +ath_mode_init(struct net_device *dev) +{ + struct ath_softc *sc = dev->priv; + struct ath_hal *ah = sc->sc_ah; + u_int32_t rfilt, mfilt[2], val; + u_int8_t pos; + struct dev_mc_list *mc; + + /* configure rx filter */ + rfilt = ath_calcrxfilter(sc); + ath_hal_setrxfilter(ah, rfilt); + + /* configure operational mode */ + ath_hal_setopmode(ah); + + /* calculate and install multicast filter */ + if ((dev->flags & IFF_ALLMULTI) == 0) { + mfilt[0] = mfilt[1] = 0; + for (mc = dev->mc_list; mc; mc = mc->next) { + /* calculate XOR of eight 6bit values */ + val = LE_READ_4(mc->dmi_addr + 0); + pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; + val = LE_READ_4(mc->dmi_addr + 3); + pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; + pos &= 0x3f; + mfilt[pos / 32] |= (1 << (pos % 32)); + } + } else { + mfilt[0] = mfilt[1] = ~0; + } + ath_hal_setmcastfilter(ah, mfilt[0], mfilt[1]); + DPRINTF(sc, ATH_DEBUG_MODE, "%s: RX filter 0x%x, MC filter %08x:%08x\n", + __func__, rfilt, mfilt[0], mfilt[1]); +} + +static struct sk_buff * +ath_alloc_skb(u_int size, u_int align) +{ + struct sk_buff *skb; + u_int off; + + skb = dev_alloc_skb(size + align-1); + if (skb != NULL) { + off = ((unsigned long) skb->data) % align; + if (off != 0) + skb_reserve(skb, align - off); + } + return skb; +} + +/* + * Setup the beacon frame. The frame is mapped for DMA + * and the transmit descriptor is filled in. + */ +static int +ath_beacon_setup(struct ath_softc *sc, struct ath_buf *bf, struct sk_buff *skb) +{ +#define USE_SHPREAMBLE(_ic) \ + (((_ic)->ic_flags & (IEEE80211_F_SHPREAMBLE | IEEE80211_F_USEBARKER))\ + == IEEE80211_F_SHPREAMBLE) +#define MIN(a,b) ((a) < (b) ? (a) : (b)) + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni = bf->bf_node; + struct ath_hal *ah = sc->sc_ah; + struct ath_node *an = ATH_NODE(ni); + struct ath_desc *ds; + u_int8_t rate; + + bf->bf_skbaddr = bus_map_single(sc->sc_bdev, + skb->data, skb->len, BUS_DMA_TODEVICE); + DPRINTF(sc, ATH_DEBUG_BEACON, + "%s: skb %p [data %p len %u] skbaddr %p\n", + __func__, skb, skb->data, skb->len, (caddr_t) bf->bf_skbaddr); + + /* setup descriptors */ + ds = bf->bf_desc; + + ds->ds_link = 0; + ds->ds_data = bf->bf_skbaddr; + /* + * Calculate rate code. + * XXX everything at min xmit rate + */ + if (USE_SHPREAMBLE(ic)) + rate = an->an_tx_mgtratesp; + else + rate = an->an_tx_mgtrate; + ath_hal_setuptxdesc(ah, ds + , skb->len + IEEE80211_CRC_LEN /* frame length */ + , sizeof(struct ieee80211_frame)/* header length */ + , HAL_PKT_TYPE_BEACON /* Atheros packet type */ + , MIN(ni->ni_txpower,60) /* txpower XXX */ + , rate, 1 /* series 0 rate/tries */ + , HAL_TXKEYIX_INVALID /* no encryption */ + , 0 /* antenna mode */ + , HAL_TXDESC_NOACK /* no ack for beacons */ + , 0 /* rts/cts rate */ + , 0 /* rts/cts duration */ + ); + /* NB: beacon's BufLen must be a multiple of 4 bytes */ + ath_hal_filltxdesc(ah, ds + , roundup(skb->len, 4) /* buffer length */ + , AH_TRUE /* first segment */ + , AH_TRUE /* last segment */ + , ds /* first descriptor */ + ); + return 0; +#undef MIN +#undef USE_SHPREAMBLE +} + +/* + * Set the slot time based on the current setting. + */ +static void +ath_setslottime(struct ath_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ath_hal *ah = sc->sc_ah; + + if (ic->ic_flags & IEEE80211_F_SHSLOT) + ath_hal_setslottime(ah, HAL_SLOT_TIME_9); + else + ath_hal_setslottime(ah, HAL_SLOT_TIME_20); + sc->sc_updateslot = OK; +} + +/* + * Callback from the 802.11 layer to update the + * slot time based on the current setting. + */ +static void +ath_updateslot(struct net_device *dev) +{ + struct ath_softc *sc = dev->priv; + struct ieee80211com *ic = &sc->sc_ic; + + /* + * In station mode we change the hardware immediately. + * For other operation we defer the change until beacon + * updates have propagated to the stations. + */ + if (ic->ic_opmode == IEEE80211_M_STA) + ath_setslottime(sc); + else + sc->sc_updateslot = UPDATE; +} + +/* + * Allocate and setup an initial beacon frame. + */ +static int +ath_beacon_alloc(struct ath_softc *sc, struct ieee80211_node *ni) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ath_buf *bf; + struct sk_buff *skb; + int error; + + bf = sc->sc_bcbuf; + if (bf->bf_skb != NULL) { + bus_unmap_single(sc->sc_bdev, + bf->bf_skbaddr, bf->bf_skb->len, BUS_DMA_TODEVICE); + dev_kfree_skb(bf->bf_skb); + bf->bf_skb = NULL; + bf->bf_node = NULL; + } + /* + * NB: the beacon data buffer must be 32-bit aligned; + * we assume the mbuf routines will return us something + * with this alignment (perhaps should assert). + */ + skb = ieee80211_beacon_alloc(ic, ni, &sc->sc_boff); + if (skb == NULL) { + DPRINTF(sc, ATH_DEBUG_BEACON, "%s: cannot get sk_buff\n", + __func__); + sc->sc_stats.ast_be_nobuf++; + return ENOMEM; + } + bf->bf_node = ni; + error = ath_beacon_setup(sc, bf, skb); + if (error == 0) + bf->bf_skb = skb; + else + dev_kfree_skb(skb); + return error; +} + +/* + * Transmit a beacon frame at SWBA. Dynamic updates to the + * frame contents are done as needed and the slot time is + * also adjusted based on current state. + */ +static void +ath_beacon_tasklet(struct net_device *dev) +{ + struct ath_softc *sc = dev->priv; + struct ieee80211com *ic = &sc->sc_ic; + struct ath_buf *bf = sc->sc_bcbuf; + struct ath_hal *ah = sc->sc_ah; + struct sk_buff *skb; + + DPRINTF(sc, ATH_DEBUG_BEACON_PROC, "%s\n", __func__); + if (ic->ic_opmode == IEEE80211_M_STA || + ic->ic_opmode == IEEE80211_M_MONITOR || + bf == NULL || bf->bf_skb == NULL) { + DPRINTF(sc, ATH_DEBUG_ANY, "%s: ic_flags=%x bf=%p bf_m=%p\n", + __func__, ic->ic_flags, bf, bf ? bf->bf_skb : NULL); + return; + } + + /* + * Update dynamic beacon contents. If this returns non-zero + * then we need to update the descriptor state because the + * beacon frame changed size and/or was re-allocated. + */ + skb = bf->bf_skb; + if (ieee80211_beacon_update(ic, bf->bf_node, &sc->sc_boff, &skb)) { + /* NB: the old sk_buff is free'd */ + bus_unmap_single(sc->sc_bdev, + bf->bf_skbaddr, bf->bf_skb->len, BUS_DMA_TODEVICE); + bf->bf_skb = NULL; + if (ath_beacon_setup(sc, bf, skb) != 0) { + dev_kfree_skb(skb); + /* XXX statistic */ + return; /* XXX??? */ + } + bf->bf_skb = skb; + } + + /* + * Handle slot time change when a non-ERP station joins/leaves + * an 11g network. The 802.11 layer notifies us via callback, + * we mark updateslot, then wait one beacon before effecting + * the change. This gives associated stations at least one + * beacon interval to note the state change. + */ + /* XXX locking */ + if (sc->sc_updateslot == UPDATE) + sc->sc_updateslot = COMMIT; /* commit next beacon */ + else if (sc->sc_updateslot == COMMIT) + ath_setslottime(sc); /* commit change to h/w */ + + /* + * Stop any current dma and put the new frame on the queue. + */ + if (!ath_hal_stoptxdma(ah, sc->sc_bhalq)) { + DPRINTF(sc, ATH_DEBUG_ANY, + "%s: beacon queue %u did not stop?\n", + __func__, sc->sc_bhalq); + /* NB: the HAL still stops DMA, so proceed */ + } + bus_dma_sync_single(sc->sc_bdev, + bf->bf_skbaddr, bf->bf_skb->len, BUS_DMA_TODEVICE); + + ath_hal_puttxbuf(ah, sc->sc_bhalq, bf->bf_daddr); + ath_hal_txstart(ah, sc->sc_bhalq); + DPRINTF(sc, ATH_DEBUG_BEACON_PROC, "%s: TXDP%u = %p (%p)\n", __func__, + sc->sc_bhalq, (caddr_t)bf->bf_daddr, bf->bf_desc); +} + +/* + * Reclaim beacon resources. + */ +static void +ath_beacon_free(struct ath_softc *sc) +{ + struct ath_buf *bf = sc->sc_bcbuf; + + if (bf->bf_skb != NULL) { + bus_unmap_single(sc->sc_bdev, + bf->bf_skbaddr, bf->bf_skb->len, BUS_DMA_TODEVICE); + dev_kfree_skb(bf->bf_skb); + bf->bf_skb = NULL; + bf->bf_node = NULL; + } +} + +/* + * Configure the beacon and sleep timers. + * + * When operating as an AP this resets the TSF and sets + * up the hardware to notify us when we need to issue beacons. + * + * When operating in station mode this sets up the beacon + * timers according to the timestamp of the last received + * beacon and the current TSF, configures PCF and DTIM + * handling, programs the sleep registers so the hardware + * will wakeup in time to receive beacons, and configures + * the beacon miss handling so we'll receive a BMISS + * interrupt when we stop seeing beacons from the AP + * we've associated with. + */ +static void +ath_beacon_config(struct ath_softc *sc) +{ + struct ath_hal *ah = sc->sc_ah; + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni = ic->ic_bss; + u_int32_t nexttbtt, intval; + + nexttbtt = (LE_READ_4(ni->ni_tstamp.data + 4) << 22) | + (LE_READ_4(ni->ni_tstamp.data) >> 10); + DPRINTF(sc, ATH_DEBUG_BEACON, "%s: nexttbtt=%u\n", __func__, nexttbtt); + nexttbtt += ni->ni_intval; + intval = ni->ni_intval & HAL_BEACON_PERIOD; + if (ic->ic_opmode == IEEE80211_M_STA) { + HAL_BEACON_STATE bs; + u_int32_t bmisstime; + + /* NB: no PCF support right now */ + memset(&bs, 0, sizeof(bs)); + /* + * Reset our tsf so the hardware will update the + * tsf register to reflect timestamps found in + * received beacons. + */ + bs.bs_intval = intval | HAL_BEACON_RESET_TSF; + bs.bs_nexttbtt = nexttbtt; + bs.bs_dtimperiod = bs.bs_intval; + bs.bs_nextdtim = nexttbtt; + /* + * The 802.11 layer records the offset to the DTIM + * bitmap while receiving beacons; use it here to + * enable h/w detection of our AID being marked in + * the bitmap vector (to indicate frames for us are + * pending at the AP). + */ + bs.bs_timoffset = ni->ni_timoff; + /* + * Calculate the number of consecutive beacons to miss + * before taking a BMISS interrupt. The configuration + * is specified in ms, so we need to convert that to + * TU's and then calculate based on the beacon interval. + * Note that we clamp the result to at most 10 beacons. + */ + bmisstime = (ic->ic_bmisstimeout * 1000) / 1024; + bs.bs_bmissthreshold = howmany(bmisstime,ni->ni_intval); + if (bs.bs_bmissthreshold > 10) + bs.bs_bmissthreshold = 10; + else if (bs.bs_bmissthreshold <= 0) + bs.bs_bmissthreshold = 1; + + /* + * Calculate sleep duration. The configuration is + * given in ms. We insure a multiple of the beacon + * period is used. Also, if the sleep duration is + * greater than the DTIM period then it makes senses + * to make it a multiple of that. + * + * XXX fixed at 100ms + */ + bs.bs_sleepduration = + roundup((100 * 1000) / 1024, bs.bs_intval); + if (bs.bs_sleepduration > bs.bs_dtimperiod) + bs.bs_sleepduration = roundup(bs.bs_sleepduration, bs.bs_dtimperiod); + + DPRINTF(sc, ATH_DEBUG_BEACON, + "%s: intval %u nexttbtt %u dtim %u nextdtim %u bmiss %u sleep %u cfp:period %u maxdur %u next %u timoffset %u\n" + , __func__ + , bs.bs_intval + , bs.bs_nexttbtt + , bs.bs_dtimperiod + , bs.bs_nextdtim + , bs.bs_bmissthreshold + , bs.bs_sleepduration + , bs.bs_cfpperiod + , bs.bs_cfpmaxduration + , bs.bs_cfpnext + , bs.bs_timoffset + ); + ath_hal_intrset(ah, 0); + ath_hal_beacontimers(ah, &bs); + sc->sc_imask |= HAL_INT_BMISS; + ath_hal_intrset(ah, sc->sc_imask); + } else { + DPRINTF(sc, ATH_DEBUG_BEACON, "%s: intval %u nexttbtt %u\n", + __func__, ni->ni_intval, nexttbtt); + ath_hal_intrset(ah, 0); + if (nexttbtt == ni->ni_intval) + intval |= HAL_BEACON_RESET_TSF; + if (ic->ic_opmode != IEEE80211_M_MONITOR) { + intval |= HAL_BEACON_ENA; + sc->sc_imask |= HAL_INT_SWBA; /* beacon prepare */ + } + ath_hal_beaconinit(ah, nexttbtt, intval); + ath_hal_intrset(ah, sc->sc_imask); + } +} + +static int +ath_desc_alloc(struct ath_softc *sc) +{ +#define DS2PHYS(_sc, _ds) \ + ((_sc)->sc_desc_daddr + ((caddr_t)(_ds) - (caddr_t)(_sc)->sc_desc)) + int i, bsize; + struct ath_desc *ds; + struct ath_buf *bf; + + /* allocate descriptors */ + sc->sc_desc_len = sizeof(struct ath_desc) * + (ATH_TXBUF * ATH_TXDESC + ATH_RXBUF + 1); + sc->sc_desc = bus_alloc_consistent(sc->sc_bdev, + sc->sc_desc_len, &sc->sc_desc_daddr); + if (sc->sc_desc == NULL) + return ENOMEM; + ds = sc->sc_desc; + DPRINTF(sc, ATH_DEBUG_ANY, "%s: DMA map: %p (%d) -> %p\n", + __func__, ds, sc->sc_desc_len, (caddr_t) sc->sc_desc_daddr); + + /* allocate buffers */ + bsize = sizeof(struct ath_buf) * (ATH_TXBUF + ATH_RXBUF + 1); + bf = kmalloc(bsize, GFP_KERNEL); + if (bf == NULL) + goto bad; + memset(bf, 0, bsize); + sc->sc_bufptr = bf; + + STAILQ_INIT(&sc->sc_rxbuf); + for (i = 0; i < ATH_RXBUF; i++, bf++, ds++) { + bf->bf_desc = ds; + bf->bf_daddr = DS2PHYS(sc, ds); + STAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list); + } + + STAILQ_INIT(&sc->sc_txbuf); + for (i = 0; i < ATH_TXBUF; i++, bf++, ds += ATH_TXDESC) { + bf->bf_desc = ds; + bf->bf_daddr = DS2PHYS(sc, ds); + STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list); + } + + /* beacon buffer */ + bf->bf_desc = ds; + bf->bf_daddr = DS2PHYS(sc, ds); + sc->sc_bcbuf = bf; + return 0; +bad: + bus_free_consistent(sc->sc_bdev, sc->sc_desc_len, + sc->sc_desc, sc->sc_desc_daddr); + sc->sc_desc = NULL; + return ENOMEM; +#undef DS2PHYS +} + +static void +ath_desc_free(struct ath_softc *sc) +{ + struct ath_buf *bf; + + /* + * NB: TX queues have already been freed in ath_draintxq(), + * which must be called before calling this function. + */ + + /* Free all pre-allocated RX skb */ + STAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) + if (bf->bf_skb != NULL) { + bus_unmap_single(sc->sc_bdev, + bf->bf_skbaddr, sc->sc_rxbufsize, + BUS_DMA_FROMDEVICE); + dev_kfree_skb(bf->bf_skb); + bf->bf_skb = NULL; + } + + /* If beacon skb has not been freed yet, do it now */ + if (sc->sc_bcbuf != NULL) { + bf = sc->sc_bcbuf; + if (bf->bf_skb != NULL) { + bus_unmap_single(sc->sc_bdev, bf->bf_skbaddr, + bf->bf_skb->len, BUS_DMA_TODEVICE); + } + sc->sc_bcbuf = NULL; + } + + /* Free memory associated with all descriptors */ + bus_free_consistent(sc->sc_bdev, sc->sc_desc_len, + sc->sc_desc, sc->sc_desc_daddr); + + STAILQ_INIT(&sc->sc_rxbuf); + STAILQ_INIT(&sc->sc_txbuf); + kfree(sc->sc_bufptr); + sc->sc_bufptr = NULL; +} + +static struct ieee80211_node * +ath_node_alloc(struct ieee80211com *ic) +{ + struct ath_softc *sc = ic->ic_dev->priv; + const size_t space = sizeof(struct ath_node) + sc->sc_rc->arc_space; + struct ath_node *an; + + an = kmalloc(space, GFP_ATOMIC); + if (an == NULL) + return NULL; + memset(an, 0, space); + an->an_avgrssi = ATH_RSSI_DUMMY_MARKER; + an->an_halstats.ns_avgbrssi = ATH_RSSI_DUMMY_MARKER; + an->an_halstats.ns_avgrssi = ATH_RSSI_DUMMY_MARKER; + an->an_halstats.ns_avgtxrssi = ATH_RSSI_DUMMY_MARKER; + ath_rate_node_init(sc, an); + return &an->an_node; +} + +/* + * Clear any references to a node in a transmit queue. + * This happens when the node is removed so we don't + * need to worry about reclaiming reference counts; we + * just null the pointer and the right thing will happen + * when the buffer is cleaned. + */ +static void +ath_tx_cleanq(struct ath_txq *txq, struct ieee80211_node *ni) +{ + struct ath_buf *bf; + + ATH_TXQ_LOCK_BH(txq); + STAILQ_FOREACH(bf, &txq->axq_q, bf_list) { + if (bf->bf_node == ni) + bf->bf_node = NULL; + } + ATH_TXQ_UNLOCK_BH(txq); +} + +static void +ath_node_cleanup(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + struct ath_softc *sc = ic->ic_dev->priv; + int i; + + for (i = 0; i < HAL_NUM_TX_QUEUES; i++) + if (ATH_TXQ_SETUP(sc, i)) + ath_tx_cleanq(&sc->sc_txq[i], ni); + ath_rate_node_cleanup(sc, ATH_NODE(ni)); + sc->sc_node_cleanup(ic, ni); +} + +static void +ath_node_copy(struct ieee80211com *ic, + struct ieee80211_node *dst, const struct ieee80211_node *src) +{ + struct ath_softc *sc = ic->ic_dev->priv; + const struct ath_node *an = (const struct ath_node *)src; + + /* + * NB: Must copy first so the cleanup done by node_copy is + * done before we copy bits around below. + */ + sc->sc_node_copy(ic, dst, src); + memcpy(&dst[1], &src[1], + sizeof(struct ath_node) - sizeof(struct ieee80211_node)); + ath_rate_node_copy(sc, ATH_NODE(dst), an); +} + +static u_int8_t +ath_node_getrssi(struct ieee80211com *ic, struct ieee80211_node *ni) +{ +#define HAL_EP_RND(x, mul) \ + ((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul)) + u_int32_t avgrssi = ATH_NODE_CONST(ni)->an_avgrssi; + int32_t rssi; + + /* + * When only one frame is received there will be no state in + * avgrssi so fallback on the value recorded by the 802.11 layer. + */ + if (avgrssi != ATH_RSSI_DUMMY_MARKER) + rssi = HAL_EP_RND(avgrssi, HAL_RSSI_EP_MULTIPLIER); + else + rssi = ni->ni_rssi; + /* NB: theoretically we shouldn't need this, but be paranoid */ + return rssi < 0 ? 0 : rssi > 127 ? 127 : rssi; +#undef HAL_EP_RND +} + +/* + * For packet capture, define the same physical layer packet header + * structure as used in the wlan-ng driver + */ +enum { + DIDmsg_lnxind_wlansniffrm = 0x00000044, + DIDmsg_lnxind_wlansniffrm_hosttime = 0x00010044, + DIDmsg_lnxind_wlansniffrm_mactime = 0x00020044, + DIDmsg_lnxind_wlansniffrm_channel = 0x00030044, + DIDmsg_lnxind_wlansniffrm_rssi = 0x00040044, + DIDmsg_lnxind_wlansniffrm_sq = 0x00050044, + DIDmsg_lnxind_wlansniffrm_signal = 0x00060044, + DIDmsg_lnxind_wlansniffrm_noise = 0x00070044, + DIDmsg_lnxind_wlansniffrm_rate = 0x00080044, + DIDmsg_lnxind_wlansniffrm_istx = 0x00090044, + DIDmsg_lnxind_wlansniffrm_frmlen = 0x000A0044 +}; +enum { + P80211ENUM_msgitem_status_no_value = 0x00 +}; +enum { + P80211ENUM_truth_false = 0x00 +}; + +typedef struct { + u_int32_t did; + u_int16_t status; + u_int16_t len; + u_int32_t data; +} p80211item_uint32_t; + +typedef struct { + u_int32_t msgcode; + u_int32_t msglen; +#define WLAN_DEVNAMELEN_MAX 16 + u_int8_t devname[WLAN_DEVNAMELEN_MAX]; + p80211item_uint32_t hosttime; + p80211item_uint32_t mactime; + p80211item_uint32_t channel; + p80211item_uint32_t rssi; + p80211item_uint32_t sq; + p80211item_uint32_t signal; + p80211item_uint32_t noise; + p80211item_uint32_t rate; + p80211item_uint32_t istx; + p80211item_uint32_t frmlen; +} wlan_ng_prism2_header; + +static int +ath_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf) +{ + struct ath_hal *ah = sc->sc_ah; + struct sk_buff *skb; + struct ath_desc *ds; + + skb = bf->bf_skb; + if (skb == NULL) { + if (sc->sc_ic.ic_opmode == IEEE80211_M_MONITOR) { + u_int off; + /* + * Allocate buffer for monitor mode with space for the + * wlan-ng style physical layer header at the start. + */ + skb = dev_alloc_skb(sc->sc_rxbufsize + + sizeof(wlan_ng_prism2_header) + + sc->sc_cachelsz - 1); + if (skb == NULL) { + DPRINTF(sc, ATH_DEBUG_ANY, + "%s: skbuff alloc of size %u failed\n", + __func__, + sc->sc_rxbufsize + + sizeof(wlan_ng_prism2_header) + + sc->sc_cachelsz -1); + sc->sc_stats.ast_rx_nobuf++; + return ENOMEM; + } + /* + * Reserve space for the Prism header. + */ + skb_reserve(skb, sizeof(wlan_ng_prism2_header)); + /* + * Align to cache line. + */ + off = ((unsigned long) skb->data) % sc->sc_cachelsz; + if (off != 0) + skb_reserve(skb, sc->sc_cachelsz - off); + } else { + /* + * Cache-line-align. This is important (for the + * 5210 at least) as not doing so causes bogus data + * in rx'd frames. + */ + skb = ath_alloc_skb(sc->sc_rxbufsize, sc->sc_cachelsz); + if (skb == NULL) { + DPRINTF(sc, ATH_DEBUG_ANY, + "%s: skbuff alloc of size %u failed\n", + __func__, sc->sc_rxbufsize); + sc->sc_stats.ast_rx_nobuf++; + return ENOMEM; + } + } + skb->dev = &sc->sc_dev; + bf->bf_skb = skb; + bf->bf_skbaddr = bus_map_single(sc->sc_bdev, + skb->data, sc->sc_rxbufsize, BUS_DMA_FROMDEVICE); + } + + /* + * Setup descriptors. For receive we always terminate + * the descriptor list with a self-linked entry so we'll + * not get overrun under high load (as can happen with a + * 5212 when ANI processing enables PHY error frames). + * + * To insure the last descriptor is self-linked we create + * each descriptor as self-linked and add it to the end. As + * each additional descriptor is added the previous self-linked + * entry is ``fixed'' naturally. This should be safe even + * if DMA is happening. When processing RX interrupts we + * never remove/process the last, self-linked, entry on the + * descriptor list. This insures the hardware always has + * someplace to write a new frame. + */ + ds = bf->bf_desc; + ds->ds_link = bf->bf_daddr; /* link to self */ + ds->ds_data = bf->bf_skbaddr; + ath_hal_setuprxdesc(ah, ds + , skb_tailroom(skb) /* buffer size */ + , 0 + ); + + if (sc->sc_rxlink != NULL) + *sc->sc_rxlink = bf->bf_daddr; + sc->sc_rxlink = &ds->ds_link; + return 0; +} + +/* + * Add a prism2 header to a received frame and + * dispatch it to capture tools like kismet. + */ +static void +ath_rx_capture(struct net_device *dev, struct ath_desc *ds, struct sk_buff *skb) +{ +#define IS_QOS_DATA(wh) \ + ((wh->i_fc[0] & (IEEE80211_FC0_TYPE_MASK|IEEE80211_FC0_SUBTYPE_MASK))==\ + (IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_QOS)) + struct ath_softc *sc = dev->priv; + struct ieee80211com *ic = &sc->sc_ic; + int len = ds->ds_rxstat.rs_datalen; + struct ieee80211_frame *wh; + wlan_ng_prism2_header *ph; + u_int32_t tsf; + + skb->protocol = ETH_P_CONTROL; + skb->pkt_type = PACKET_OTHERHOST; + skb_put(skb, len); + + KASSERT(ic->ic_flags & IEEE80211_F_DATAPAD, + ("data padding not enabled?")); + /* Remove pad bytes */ + wh = (struct ieee80211_frame *) skb->data; + if (IS_QOS_DATA(wh)) { + int headersize = ieee80211_hdrsize(wh); + int padbytes = roundup(headersize,4) - headersize; + + /* + * Copy up 802.11 header and strip h/w padding. + */ + if (padbytes > 0) { + memmove(skb->data + padbytes, skb->data, headersize); + skb_pull(skb, padbytes); + } + } + + ph = (wlan_ng_prism2_header *) + skb_push(skb, sizeof(wlan_ng_prism2_header)); + memset(ph, 0, sizeof(wlan_ng_prism2_header)); + + ph->msgcode = DIDmsg_lnxind_wlansniffrm; + ph->msglen = sizeof(wlan_ng_prism2_header); + strcpy(ph->devname, dev->name); + + ph->hosttime.did = DIDmsg_lnxind_wlansniffrm_hosttime; + ph->hosttime.status = 0; + ph->hosttime.len = 4; + ph->hosttime.data = jiffies; + + /* Pass up tsf clock in mactime */ + ph->mactime.did = DIDmsg_lnxind_wlansniffrm_mactime; + ph->mactime.status = 0; + ph->mactime.len = 4; + /* + * Rx descriptor has the low 15 bits of the tsf at + * the time the frame was received. Use the current + * tsf to extend this to 32 bits. + */ + tsf = ath_hal_gettsf32(sc->sc_ah); + if ((tsf & 0x7fff) < ds->ds_rxstat.rs_tstamp) + tsf -= 0x8000; + ph->mactime.data = ds->ds_rxstat.rs_tstamp | (tsf &~ 0x7fff); + + ph->istx.did = DIDmsg_lnxind_wlansniffrm_istx; + ph->istx.status = 0; + ph->istx.len = 4; + ph->istx.data = P80211ENUM_truth_false; + + ph->frmlen.did = DIDmsg_lnxind_wlansniffrm_frmlen; + ph->frmlen.status = 0; + ph->frmlen.len = 4; + ph->frmlen.data = len; + + ph->channel.did = DIDmsg_lnxind_wlansniffrm_channel; + ph->channel.status = 0; + ph->channel.len = 4; + ph->channel.data = ieee80211_mhz2ieee(ic->ic_ibss_chan->ic_freq,0); + + ph->rssi.did = DIDmsg_lnxind_wlansniffrm_rssi; + ph->rssi.status = P80211ENUM_msgitem_status_no_value; + ph->rssi.len = 4; + ph->rssi.data = 0; + + ph->signal.did = DIDmsg_lnxind_wlansniffrm_signal; + ph->signal.status = 0; + ph->signal.len = 4; + ph->signal.data = ds->ds_rxstat.rs_rssi; + + ph->rate.did = DIDmsg_lnxind_wlansniffrm_rate; + ph->rate.status = 0; + ph->rate.len = 4; + ph->rate.data = sc->sc_hwmap[ds->ds_rxstat.rs_rate]; + + skb->dev = dev; + skb->mac.raw = skb->data; + skb->ip_summed = CHECKSUM_NONE; + skb->pkt_type = PACKET_OTHERHOST; + skb->protocol = __constant_htons(0x0019); /* ETH_P_80211_RAW */ + + netif_rx(skb); +#undef IS_QOS_DATA +} + +/* + * Intercept management frames to collect beacon rssi data + * and to do ibss merges. + */ +static void +ath_recv_mgmt(struct ieee80211com *ic, struct sk_buff *skb, + struct ieee80211_node *ni, + int subtype, int rssi, u_int32_t rstamp) +{ + struct ath_softc *sc = ic->ic_dev->priv; + + /* + * Call up first so subsequent work can use information + * potentially stored in the node (e.g. for ibss merge). + */ + sc->sc_recv_mgmt(ic, skb, ni, subtype, rssi, rstamp); + switch (subtype) { + case IEEE80211_FC0_SUBTYPE_BEACON: + /* update rssi statistics for use by the hal */ + ATH_RSSI_LPF(ATH_NODE(ni)->an_halstats.ns_avgbrssi, rssi); + /* fall thru... */ + case IEEE80211_FC0_SUBTYPE_PROBE_RESP: + break; + } +} + +static void +ath_setdefantenna(struct ath_softc *sc, u_int antenna) +{ + struct ath_hal *ah = sc->sc_ah; + + /* XXX block beacon interrupts */ + ath_hal_setdefantenna(ah, antenna); + if (sc->sc_defant != antenna) + sc->sc_stats.ast_ant_defswitch++; + sc->sc_defant = antenna; + sc->sc_rxotherant = 0; +} + +static void +ath_rx_tasklet(TQUEUE_ARG data) +{ +#define PA2DESC(_sc, _pa) \ + ((struct ath_desc *)((caddr_t)(_sc)->sc_desc + \ + ((_pa) - (_sc)->sc_desc_daddr))) +#define IS_CTL(wh) \ + ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL) + struct net_device *dev = (struct net_device *)data; + struct ath_buf *bf; + struct ath_softc *sc = dev->priv; + struct ieee80211com *ic = &sc->sc_ic; + struct ath_hal *ah = sc->sc_ah; + struct ath_desc *ds; + struct sk_buff *skb; + struct ieee80211_node *ni; + struct ath_node *an; + int len; + u_int phyerr; + HAL_STATUS status; + + DPRINTF(sc, ATH_DEBUG_RX_PROC, "%s\n", __func__); + do { + bf = STAILQ_FIRST(&sc->sc_rxbuf); + if (bf == NULL) { /* XXX ??? can this happen */ + printk("%s: no buffer (%s)\n", dev->name, __func__); + break; + } + ds = bf->bf_desc; + if (ds->ds_link == bf->bf_daddr) { + /* NB: never process the self-linked entry at the end */ + break; + } + skb = bf->bf_skb; + if (skb == NULL) { /* XXX ??? can this happen */ + printk("%s: no skbuff (%s)\n", dev->name, __func__); + continue; + } + /* XXX sync descriptor memory */ + /* + * Must provide the virtual address of the current + * descriptor, the physical address, and the virtual + * address of the next descriptor in the h/w chain. + * This allows the HAL to look ahead to see if the + * hardware is done with a descriptor by checking the + * done bit in the following descriptor and the address + * of the current descriptor the DMA engine is working + * on. All this is necessary because of our use of + * a self-linked list to avoid rx overruns. + */ + status = ath_hal_rxprocdesc(ah, ds, + bf->bf_daddr, PA2DESC(sc, ds->ds_link)); +#ifdef AR_DEBUG + if (sc->sc_debug & ATH_DEBUG_RECV_DESC) + ath_printrxbuf(bf, status == HAL_OK); +#endif + if (status == HAL_EINPROGRESS) + break; + STAILQ_REMOVE_HEAD(&sc->sc_rxbuf, bf_list); + + if (ds->ds_rxstat.rs_more) { + /* + * Frame spans multiple descriptors; this + * cannot happen yet as we don't support + * jumbograms. If not in monitor mode, + * discard the frame. + */ +#ifndef ERROR_FRAMES + /* + * Enable this if you want to see + * error frames in Monitor mode. + */ + if (ic->ic_opmode != IEEE80211_M_MONITOR) { + sc->sc_stats.ast_rx_toobig++; + goto rx_next; + } +#endif + /* fall thru for monitor mode handling... */ + } else if (ds->ds_rxstat.rs_status != 0) { + if (ds->ds_rxstat.rs_status & HAL_RXERR_CRC) + sc->sc_stats.ast_rx_crcerr++; + if (ds->ds_rxstat.rs_status & HAL_RXERR_FIFO) + sc->sc_stats.ast_rx_fifoerr++; + if (ds->ds_rxstat.rs_status & HAL_RXERR_PHY) { + sc->sc_stats.ast_rx_phyerr++; + phyerr = ds->ds_rxstat.rs_phyerr & 0x1f; + sc->sc_stats.ast_rx_phy[phyerr]++; + goto rx_next; + } + if (ds->ds_rxstat.rs_status & HAL_RXERR_DECRYPT) { + /* + * Decrypt error. If the error occurred + * because there was no hardware key, then + * let the frame through so the upper layers + * can process it. This is necessary for 5210 + * parts which have no way to setup a ``clear'' + * key cache entry. + * + * XXX do key cache faulting + */ + if (ds->ds_rxstat.rs_keyix == HAL_RXKEYIX_INVALID) + goto rx_accept; + sc->sc_stats.ast_rx_badcrypt++; + } + if (ds->ds_rxstat.rs_status & HAL_RXERR_MIC) { + sc->sc_stats.ast_rx_badmic++; + /* + * Do minimal work required to hand off + * the 802.11 header for notifcation. + */ + /* XXX frag's and qos frames */ + len = ds->ds_rxstat.rs_datalen; + if (len >= sizeof (struct ieee80211_frame)) { + bus_dma_sync_single(sc->sc_bdev, + bf->bf_skbaddr, len, + BUS_DMA_FROMDEVICE); + ieee80211_notify_michael_failure(ic, + (struct ieee80211_frame *) + skb->data, + ds->ds_rxstat.rs_keyix); + } + } + /* + * Reject error frames, we normally don't want + * to see them in monitor mode (in monitor mode + * allow through packets that have crypto problems). + */ + if ((ds->ds_rxstat.rs_status &~ + (HAL_RXERR_DECRYPT|HAL_RXERR_MIC)) || + sc->sc_ic.ic_opmode != IEEE80211_M_MONITOR) + goto rx_next; + } +rx_accept: + /* + * Sync and unmap the frame. At this point we're + * committed to passing the sk_buff somewhere so + * clear buf_skb; this means a new sk_buff must be + * allocated when the rx descriptor is setup again + * to receive another frame. + */ + len = ds->ds_rxstat.rs_datalen; + bus_dma_sync_single(sc->sc_bdev, + bf->bf_skbaddr, len, BUS_DMA_FROMDEVICE); + bus_unmap_single(sc->sc_bdev, bf->bf_skbaddr, + sc->sc_rxbufsize, BUS_DMA_FROMDEVICE); + bf->bf_skb = NULL; + + if (sc->sc_softled) + ath_update_led(sc); + sc->sc_stats.ast_ant_rx[ds->ds_rxstat.rs_antenna]++; + + if (ic->ic_opmode == IEEE80211_M_MONITOR) { + /* + * Monitor mode: discard anything shorter than + * an ack or cts, clean the skbuff, fabricate + * the Prism header existing tools expect, + * and dispatch. + */ + if (len < IEEE80211_ACK_LEN) { + DPRINTF(sc, ATH_DEBUG_RECV, + "%s: runt packet %d\n", __func__, len); + sc->sc_stats.ast_rx_tooshort++; + dev_kfree_skb(skb); + goto rx_next; + } + ath_rx_capture(dev, ds, skb); + goto rx_next; + } + + /* + * From this point on we assume the frame is at least + * as large as ieee80211_frame_min; verify that. + */ + if (len < IEEE80211_MIN_LEN) { + DPRINTF(sc, ATH_DEBUG_RECV, "%s: short packet %d\n", + __func__, len); + sc->sc_stats.ast_rx_tooshort++; + dev_kfree_skb(skb); + goto rx_next; + } + + /* + * Normal receive. + */ + skb_put(skb, len); + skb->protocol = ETH_P_CONTROL; /* XXX */ + + if (IFF_DUMPPKTS(sc, ATH_DEBUG_RECV)) { + ieee80211_dump_pkt(skb->data, len, + sc->sc_hwmap[ds->ds_rxstat.rs_rate], + ds->ds_rxstat.rs_rssi); + } + + skb_trim(skb, skb->len - IEEE80211_CRC_LEN); + + /* + * Locate the node for sender, track state, and then + * pass the (referenced) node up to the 802.11 layer + * for its use. We are required to pass some node so + * we fall back to ic_bss when this frame is from an + * unknown sender. The 802.11 layer knows this means the + * sender wasn't in the node table and acts accordingly. + * Note also that by convention we do not reference + * count ic_bss, only other nodes (ic_bss is never free'd). + */ + if (ic->ic_opmode != IEEE80211_M_STA) { + struct ieee80211_frame_min *wh = + (struct ieee80211_frame_min *) skb->data; + if (IS_CTL(wh)) + ni = ieee80211_find_node(ic, wh->i_addr1); + else + ni = ieee80211_find_node(ic, wh->i_addr2); + if (ni == NULL) + ni = ic->ic_bss; + } else + ni = ic->ic_bss; + + /* + * Track rx rssi and do any rx antenna management. + */ + an = ATH_NODE(ni); + ATH_RSSI_LPF(an->an_avgrssi, ds->ds_rxstat.rs_rssi); + if (sc->sc_diversity) { + /* + * When using fast diversity, change the default rx + * antenna if diversity chooses the other antenna 3 + * times in a row. + */ + if (sc->sc_defant != ds->ds_rxstat.rs_antenna) { + if (++sc->sc_rxotherant >= 3) + ath_setdefantenna(sc, + ds->ds_rxstat.rs_antenna); + } else + sc->sc_rxotherant = 0; + } + + /* + * Send frame up for processing. + */ + ieee80211_input(ic, skb, ni, + ds->ds_rxstat.rs_rssi, ds->ds_rxstat.rs_tstamp); + + /* + * Reclaim node reference. + */ + if (ni != ic->ic_bss) + ieee80211_free_node(ic, ni); +rx_next: + STAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list); + } while (ath_rxbuf_init(sc, bf) == 0); + + /* rx signal state monitoring */ + ath_hal_rxmonitor(ah, &ATH_NODE(ic->ic_bss)->an_halstats); +#undef IS_CTL +#undef PA2DESC +} + +/* + * Setup a hardware data transmit queue for the specified + * access control. The hal may not support all requested + * queues in which case it will return a reference to a + * previously setup queue. We record the mapping from ac's + * to h/w queues for use by ath_tx_start and also track + * the set of h/w queues being used to optimize work in the + * transmit interrupt handler and related routines. + */ +static int +ath_tx_setup(struct ath_softc *sc, int ac, int haltype) +{ +#define N(a) (sizeof(a)/sizeof(a[0])) + struct ath_hal *ah = sc->sc_ah; + HAL_TXQ_INFO qi; + int qnum; + + if (ac >= N(sc->sc_ac2q)) { + printk("%s: AC %u out of range, max %u!\n", + sc->sc_dev.name, ac, N(sc->sc_ac2q)); + return 0; + } + memset(&qi, 0, sizeof(qi)); + qi.tqi_subtype = haltype; + /* + * Enable interrupts only for EOL and DESC conditions. + * We mark tx descriptors to receive a DESC interrupt + * when a tx queue gets deep; otherwise waiting for the + * EOL to reap descriptors. Note that this is done to + * reduce interrupt load and this only defers reaping + * descriptors, never transmitting frames. Aside from + * reducing interrupts this also permits more concurrency. + * The only potential downside is if the tx queue backs + * up in which case the top half of the kernel may backup + * due to a lack of tx descriptors. + */ + qi.tqi_qflags = TXQ_FLAG_TXEOLINT_ENABLE | TXQ_FLAG_TXDESCINT_ENABLE; + qnum = ath_hal_setuptxqueue(ah, HAL_TX_QUEUE_DATA, &qi); + if (qnum == -1) { + printk("%s: Unable to setup hardware queue for %s traffic!\n", + sc->sc_dev.name, acnames[ac]); + return 0; + } + if (qnum >= N(sc->sc_txq)) { + printk("%s: hal qnum %u out of range, max %u!\n", + sc->sc_dev.name, qnum, N(sc->sc_txq)); + return 0; + } + if (!ATH_TXQ_SETUP(sc, qnum)) { + struct ath_txq *txq = &sc->sc_txq[qnum]; + + txq->axq_qnum = qnum; + txq->axq_depth = 0; + txq->axq_intrcnt = 0; + txq->axq_link = NULL; + STAILQ_INIT(&txq->axq_q); + ATH_TXQ_LOCK_INIT(txq); + sc->sc_txqsetup |= 1<sc_ac2q[ac] = &sc->sc_txq[qnum]; + return 1; +#undef N +} + +static int +ath_tx_start(struct net_device *dev, struct ieee80211_node *ni, struct ath_buf *bf, + struct sk_buff *skb) +{ +#define MIN(a,b) ((a) < (b) ? (a) : (b)) + struct ath_softc *sc = dev->priv; + struct ieee80211com *ic = &sc->sc_ic; + struct ath_hal *ah = sc->sc_ah; + int iswep, ismcast, keyix, hdrlen, pktlen, try0; + u_int8_t rix, txrate, ctsrate; + u_int8_t cix = 0xff; /* NB: silence compiler */ + struct ath_desc *ds; + struct ath_txq *txq; + struct ieee80211_frame *wh; + u_int subtype, flags, ctsduration; + HAL_PKT_TYPE atype; + const HAL_RATE_TABLE *rt; + HAL_BOOL shortPreamble; + struct ath_node *an; + + wh = (struct ieee80211_frame *) skb->data; + iswep = wh->i_fc[1] & IEEE80211_FC1_WEP; + ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); + hdrlen = ieee80211_anyhdrsize(wh); + pktlen = skb->len; + + if (iswep) { + const struct ieee80211_cipher *cip; + struct ieee80211_key *k; + + /* + * Construct the 802.11 header+trailer for an encrypted + * frame. The only reason this can fail is because of an + * unknown or unsupported cipher/key type. + */ + k = ieee80211_crypto_encap(ic, ni, skb); + if (k == NULL) { + /* + * This can happen when the key is yanked after the + * frame was queued. Just discard the frame; the + * 802.11 layer counts failures and provides + * debugging/diagnostics. + */ + return -EIO; + } + /* + * Adjust the packet + header lengths for the crypto + * additions and calculate the h/w key index. When + * a s/w mic is done the frame will have had any mic + * added to it prior to entry so skb->len above will + * account for it. Otherwise we need to add it to the + * packet length. + */ + cip = k->wk_cipher; + hdrlen += cip->ic_header; + pktlen += cip->ic_header + cip->ic_trailer; + if ((k->wk_flags & IEEE80211_KEY_SWMIC) == 0) + pktlen += cip->ic_miclen; + keyix = k->wk_keyix; + + /* packet header may have moved, reset our local pointer */ + wh = (struct ieee80211_frame *) skb->data; + } else + keyix = HAL_TXKEYIX_INVALID; + + pktlen += IEEE80211_CRC_LEN; + + /* + * Load the DMA map so any coalescing is done. This + * also calculates the number of descriptors we need. + */ + bf->bf_skbaddr = bus_map_single(sc->sc_bdev, + skb->data, pktlen, BUS_DMA_TODEVICE); + DPRINTF(sc, ATH_DEBUG_XMIT, "%s: skb %p [data %p len %u] skbaddr %x\n", + __func__, skb, skb->data, skb->len, bf->bf_skbaddr); + bf->bf_skb = skb; + bf->bf_node = ni; + + /* setup descriptors */ + ds = bf->bf_desc; + rt = sc->sc_currates; + KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode)); + + /* + * NB: the 802.11 layer marks whether or not we should + * use short preamble based on the current mode and + * negotiated parameters. + */ + if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && + (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) { + shortPreamble = AH_TRUE; + sc->sc_stats.ast_tx_shortpre++; + } else { + shortPreamble = AH_FALSE; + } + + an = ATH_NODE(ni); + /* + * Calculate Atheros packet type from IEEE80211 packet header, + * setup for rate calculations, and select h/w transmit queue. + */ + switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { + case IEEE80211_FC0_TYPE_MGT: + subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; + if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) + atype = HAL_PKT_TYPE_BEACON; + else if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) + atype = HAL_PKT_TYPE_PROBE_RESP; + else if (subtype == IEEE80211_FC0_SUBTYPE_ATIM) + atype = HAL_PKT_TYPE_ATIM; + else + atype = HAL_PKT_TYPE_NORMAL; /* XXX */ + rix = 0; /* XXX lowest rate */ + try0 = ATH_TXMAXTRY; + if (shortPreamble) + txrate = an->an_tx_mgtratesp; + else + txrate = an->an_tx_mgtrate; + /* NB: force all management frames to highest queue */ + txq = sc->sc_ac2q[WME_AC_VO]; + break; + case IEEE80211_FC0_TYPE_CTL: + atype = HAL_PKT_TYPE_PSPOLL; /* stop setting of duration */ + rix = 0; /* XXX lowest rate */ + try0 = ATH_TXMAXTRY; + if (shortPreamble) + txrate = an->an_tx_mgtratesp; + else + txrate = an->an_tx_mgtrate; + /* NB: force all ctl frames to highest queue */ + txq = sc->sc_ac2q[WME_AC_VO]; + break; + case IEEE80211_FC0_TYPE_DATA: + atype = HAL_PKT_TYPE_NORMAL; /* default */ + /* + * Data frames; consult the rate control module. + */ + ath_rate_findrate(sc, an, shortPreamble, skb->len, + &rix, &try0, &txrate); + /* + * Default all non-QoS traffic to the background queue. + */ + if (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_QOS) { + /* XXX validate skb->priority */ + txq = sc->sc_ac2q[skb->priority]; + } else + txq = sc->sc_ac2q[WME_AC_BK]; + break; + default: + printk("%s: bogus frame type 0x%x (%s)\n", dev->name, + wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK, __func__); + /* XXX statistic */ + return -EIO; + } + + /* + * Calculate miscellaneous flags. + */ + flags = HAL_TXDESC_CLRDMASK; /* XXX needed for crypto errs */ + if (ismcast) { + flags |= HAL_TXDESC_NOACK; /* no ack on broad/multicast */ + sc->sc_stats.ast_tx_noack++; + } else if (pktlen > ic->ic_rtsthreshold) { + flags |= HAL_TXDESC_RTSENA; /* RTS based on frame length */ + cix = rt->info[rix].controlRate; + sc->sc_stats.ast_tx_rts++; + } + + /* + * If 802.11g protection is enabled, determine whether + * to use RTS/CTS or just CTS. Note that this is only + * done for OFDM unicast frames. + */ + if ((ic->ic_flags & IEEE80211_F_USEPROT) && + rt->info[rix].phy == IEEE80211_T_OFDM && + (flags & HAL_TXDESC_NOACK) == 0) { + /* XXX fragments must use CCK rates w/ protection */ + if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) + flags |= HAL_TXDESC_RTSENA; + else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) + flags |= HAL_TXDESC_CTSENA; + cix = rt->info[sc->sc_protrix].controlRate; + sc->sc_stats.ast_tx_protect++; + } + + /* + * Calculate duration. This logically belongs in the 802.11 + * layer but it lacks sufficient information to calculate it. + */ + if ((flags & HAL_TXDESC_NOACK) == 0 && + (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_CTL) { + u_int16_t dur; + /* + * XXX not right with fragmentation. + */ + if (shortPreamble) + dur = rt->info[rix].spAckDuration; + else + dur = rt->info[rix].lpAckDuration; + *(u_int16_t *)wh->i_dur = cpu_to_le16(dur); + } + + /* + * Calculate RTS/CTS rate and duration if needed. + */ + ctsduration = 0; + if (flags & (HAL_TXDESC_RTSENA|HAL_TXDESC_CTSENA)) { + /* + * CTS transmit rate is derived from the transmit rate + * by looking in the h/w rate table. We must also factor + * in whether or not a short preamble is to be used. + */ + /* NB: cix is set above where RTS/CTS is enabled */ + KASSERT(cix != 0xff, ("cix not setup")); + ctsrate = rt->info[cix].rateCode; + /* + * Compute the transmit duration based on the frame + * size and the size of an ACK frame. We call into the + * HAL to do the computation since it depends on the + * characteristics of the actual PHY being used. + * + * NB: CTS is assumed the same size as an ACK so we can + * use the precalculated ACK durations. + */ + if (shortPreamble) { + ctsrate |= rt->info[cix].shortPreamble; + if (flags & HAL_TXDESC_RTSENA) /* SIFS + CTS */ + ctsduration += rt->info[cix].spAckDuration; + ctsduration += ath_hal_computetxtime(ah, + rt, pktlen, rix, AH_TRUE); + if ((flags & HAL_TXDESC_NOACK) == 0) /* SIFS + ACK */ + ctsduration += rt->info[cix].spAckDuration; + } else { + if (flags & HAL_TXDESC_RTSENA) /* SIFS + CTS */ + ctsduration += rt->info[cix].lpAckDuration; + ctsduration += ath_hal_computetxtime(ah, + rt, pktlen, rix, AH_FALSE); + if ((flags & HAL_TXDESC_NOACK) == 0) /* SIFS + ACK */ + ctsduration += rt->info[cix].lpAckDuration; + } + /* + * Must disable multi-rate retry when using RTS/CTS. + */ + try0 = ATH_TXMAXTRY; + } else + ctsrate = 0; + + if (IFF_DUMPPKTS(sc, ATH_DEBUG_XMIT)) + ieee80211_dump_pkt(skb->data, skb->len, + sc->sc_hwmap[txrate], -1); + + /* + * Determine if a tx interrupt should be generated for + * this descriptor. We take a tx interrupt to reap + * descriptors when the h/w hits an EOL condition or + * when the descriptor is specifically marked to generate + * an interrupt. We periodically mark descriptors in this + * way to insure timely replenishing of the supply needed + * for sending frames. Defering interrupts reduces system + * load and potentially allows more concurrent work to be + * done but if done to aggressively can cause senders to + * backup. + * + * NB: use >= to deal with sc_txintrperiod changing + * dynamically through sysctl. + */ + if (++txq->axq_intrcnt >= sc->sc_txintrperiod) { + flags |= HAL_TXDESC_INTREQ; + txq->axq_intrcnt = 0; + } + + /* + * Formulate first tx descriptor with tx controls. + */ + /* XXX check return value? */ + ath_hal_setuptxdesc(ah, ds + , pktlen /* packet length */ + , hdrlen /* header length */ + , atype /* Atheros packet type */ + , MIN(ni->ni_txpower,60)/* txpower */ + , txrate, try0 /* series 0 rate/tries */ + , keyix /* key cache index */ + , sc->sc_txantenna /* antenna mode */ + , flags /* flags */ + , ctsrate /* rts/cts rate */ + , ctsduration /* rts/cts duration */ + ); + /* + * Setup the multi-rate retry state only when we're + * going to use it. This assumes ath_hal_setuptxdesc + * initializes the descriptors (so we don't have to) + * when the hardware supports multi-rate retry and + * we don't use it. + */ + if (try0 != ATH_TXMAXTRY) + ath_rate_setupxtxdesc(sc, an, ds, shortPreamble, rix); + + ds->ds_link = 0; + ds->ds_data = bf->bf_skbaddr; + ath_hal_filltxdesc(ah, ds + , skb->len /* segment length */ + , AH_TRUE /* first segment */ + , AH_TRUE /* last segment */ + , ds /* first descriptor */ + ); + DPRINTF(sc, ATH_DEBUG_XMIT, "%s: Q%d: %08x %08x %08x %08x %08x %08x\n", + __func__, txq->axq_qnum, ds->ds_link, ds->ds_data, + ds->ds_ctl0, ds->ds_ctl1, ds->ds_hw[0], ds->ds_hw[1]); + + /* + * Insert the frame on the outbound list and + * pass it on to the hardware. + */ + ATH_TXQ_LOCK_BH(txq); + ATH_TXQ_INSERT_TAIL(txq, bf, bf_list); + DPRINTF(sc, ATH_DEBUG_TX_PROC, "%s: txq depth = %d\n", + __func__, txq->axq_depth); + if (txq->axq_link == NULL) { + ath_hal_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr); + DPRINTF(sc, ATH_DEBUG_XMIT, "%s: TXDP[%u] = %p (%p)\n", + __func__, + txq->axq_qnum, (caddr_t)bf->bf_daddr, bf->bf_desc); + } else { + *txq->axq_link = bf->bf_daddr; + DPRINTF(sc, ATH_DEBUG_XMIT, "%s: link[%u](%p)=%p (%p)\n", + __func__, + txq->axq_qnum, txq->axq_link, + (caddr_t)bf->bf_daddr, bf->bf_desc); + } + txq->axq_link = &ds->ds_link; + ATH_TXQ_UNLOCK_BH(txq); + + if (sc->sc_softled) + ath_update_led(sc); + + ath_hal_txstart(ah, txq->axq_qnum); + dev->trans_start = jiffies; + return 0; +#undef MIN +} + +/* + * Process completed xmit descriptors from the specified queue. + */ +static void +ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ath_hal *ah = sc->sc_ah; + struct ath_buf *bf; + struct ath_desc *ds; + struct ieee80211_node *ni; + struct ath_node *an; + int sr, lr; + HAL_STATUS status; + + DPRINTF(sc, ATH_DEBUG_TX_PROC, "%s: tx queue %p, link %p\n", __func__, + (caddr_t) ath_hal_gettxbuf(sc->sc_ah, txq->axq_qnum), + txq->axq_link); + for (;;) { + ATH_TXQ_LOCK(txq); + txq->axq_intrcnt = 0; /* reset periodic desc intr count */ + bf = STAILQ_FIRST(&txq->axq_q); + if (bf == NULL) { + txq->axq_link = NULL; + ATH_TXQ_UNLOCK(txq); + break; + } + ds = bf->bf_desc; /* NB: last decriptor */ + status = ath_hal_txprocdesc(ah, ds); +#ifdef AR_DEBUG + if (sc->sc_debug & ATH_DEBUG_XMIT_DESC) + ath_printtxbuf(bf, status == HAL_OK); +#endif + if (status == HAL_EINPROGRESS) { + ATH_TXQ_UNLOCK(txq); + break; + } + ATH_TXQ_REMOVE_HEAD(txq, bf_list); + ATH_TXQ_UNLOCK(txq); + + ni = bf->bf_node; + if (ni != NULL) { + an = ATH_NODE(ni); + if (ds->ds_txstat.ts_status == 0) { + sc->sc_stats.ast_ant_tx[ds->ds_txstat.ts_antenna]++; + if (ds->ds_txstat.ts_rate & HAL_TXSTAT_ALTRATE) + sc->sc_stats.ast_tx_altrate++; + sc->sc_stats.ast_tx_rssi = + ds->ds_txstat.ts_rssi; + ATH_RSSI_LPF(an->an_halstats.ns_avgtxrssi, + ds->ds_txstat.ts_rssi); + } else { + if (ds->ds_txstat.ts_status & HAL_TXERR_XRETRY) + sc->sc_stats.ast_tx_xretries++; + if (ds->ds_txstat.ts_status & HAL_TXERR_FIFO) + sc->sc_stats.ast_tx_fifoerr++; + if (ds->ds_txstat.ts_status & HAL_TXERR_FILT) + sc->sc_stats.ast_tx_filtered++; + } + sr = ds->ds_txstat.ts_shortretry; + lr = ds->ds_txstat.ts_longretry; + sc->sc_stats.ast_tx_shortretry += sr; + sc->sc_stats.ast_tx_longretry += lr; + /* + * Hand the descriptor to the rate control algorithm. + */ + ath_rate_tx_complete(sc, ATH_NODE(ni), ds); + /* + * Reclaim reference to node. + * + * NB: the node may be reclaimed here if, for example + * this is a DEAUTH message that was sent and the + * node was timed out due to inactivity. + */ + if (ni != ic->ic_bss) + ieee80211_free_node(ic, ni); + } + bus_unmap_single(sc->sc_bdev, + bf->bf_skbaddr, bf->bf_skb->len, BUS_DMA_TODEVICE); + dev_kfree_skb(bf->bf_skb); + bf->bf_skb = NULL; + bf->bf_node = NULL; + + ATH_TXBUF_LOCK(sc); + STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list); + ATH_TXBUF_UNLOCK(sc); + } +} + +/* + * Deferred processing of transmit interrupt; special-cased + * for a single hardware transmit queue (e.g. 5210 and 5211). + */ +static void +ath_tx_tasklet_q0(TQUEUE_ARG data) +{ + struct net_device *dev = (struct net_device *)data; + struct ath_softc *sc = dev->priv; + struct ieee80211com *ic = &sc->sc_ic; + + ath_tx_processq(sc, &sc->sc_txq[0]); + /* + * Don't wakeup unless we're associated; this insures we don't + * signal the upper layer it's ok to start sending data frames. + */ + /* XXX use a low watermark to reduce wakeups */ + if (ic->ic_state == IEEE80211_S_RUN) + netif_wake_queue(dev); +} + +/* + * Deferred processing of transmit interrupt; special-cased + * for four hardware queues, 0-3 (e.g. 5212 w/ WME support). + */ +static void +ath_tx_tasklet_q0123(TQUEUE_ARG data) +{ + struct net_device *dev = (struct net_device *)data; + struct ath_softc *sc = dev->priv; + struct ieee80211com *ic = &sc->sc_ic; + + /* + * Process each active queue. + */ + ath_tx_processq(sc, &sc->sc_txq[0]); + ath_tx_processq(sc, &sc->sc_txq[1]); + ath_tx_processq(sc, &sc->sc_txq[2]); + ath_tx_processq(sc, &sc->sc_txq[3]); + /* + * Don't wakeup unless we're associated; this insures we don't + * signal the upper layer it's ok to start sending data frames. + */ + /* XXX use a low watermark to reduce wakeups */ + if (ic->ic_state == IEEE80211_S_RUN) + netif_wake_queue(dev); +} + +/* + * Deferred processing of transmit interrupt. + */ +static void +ath_tx_tasklet(TQUEUE_ARG data) +{ + struct net_device *dev = (struct net_device *)data; + struct ath_softc *sc = dev->priv; + struct ieee80211com *ic = &sc->sc_ic; + int i; + + /* + * Process each active queue. + */ + /* XXX faster to read ISR_S0_S and ISR_S1_S to determine q's? */ + for (i = 0; i < HAL_NUM_TX_QUEUES; i++) + if (ATH_TXQ_SETUP(sc, i)) + ath_tx_processq(sc, &sc->sc_txq[i]); + /* + * Don't wakeup unless we're associated; this insures we don't + * signal the upper layer it's ok to start sending data frames. + */ + /* XXX use a low watermark to reduce wakeups */ + if (ic->ic_state == IEEE80211_S_RUN) + netif_wake_queue(dev); +} + +static void +ath_tx_timeout(struct net_device *dev) +{ + struct ath_softc *sc = dev->priv; + sc->sc_stats.ast_watchdog++; + ath_init(dev); +} + +static void +ath_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq) +{ + struct ath_hal *ah = sc->sc_ah; + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni; + struct ath_buf *bf; + + /* + * NB: this assumes output has been stopped and + * we do not need to block ath_tx_tasklet + */ + for (;;) { + ATH_TXQ_LOCK(txq); + bf = STAILQ_FIRST(&txq->axq_q); + if (bf == NULL) { + txq->axq_link = NULL; + ATH_TXQ_UNLOCK(txq); + break; + } + ATH_TXQ_REMOVE_HEAD(txq, bf_list); + ATH_TXQ_UNLOCK(txq); +#ifdef AR_DEBUG + if (sc->sc_debug & ATH_DEBUG_RESET) + ath_printtxbuf(bf, + ath_hal_txprocdesc(ah, bf->bf_desc) == HAL_OK); +#endif /* AR_DEBUG */ + bus_unmap_single(sc->sc_bdev, + bf->bf_skbaddr, bf->bf_skb->len, BUS_DMA_TODEVICE); + dev_kfree_skb(bf->bf_skb); + bf->bf_skb = NULL; + ni = bf->bf_node; + bf->bf_node = NULL; + if (ni != NULL && ni != ic->ic_bss) { + /* + * Reclaim node reference. + */ + ieee80211_free_node(ic, ni); + } + ATH_TXBUF_LOCK(sc); + STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list); + ATH_TXBUF_UNLOCK(sc); + } +} + +static void +ath_tx_stopdma(struct ath_softc *sc, struct ath_txq *txq) +{ + struct ath_hal *ah = sc->sc_ah; + + (void) ath_hal_stoptxdma(ah, txq->axq_qnum); + DPRINTF(sc, ATH_DEBUG_RESET, "%s: tx queue [%u] %p, link %p\n", + __func__, txq->axq_qnum, + (caddr_t) ath_hal_gettxbuf(ah, txq->axq_qnum), txq->axq_link); +} + +/* + * Drain the transmit queues and reclaim resources. + */ +static void +ath_draintxq(struct ath_softc *sc) +{ + struct ath_hal *ah = sc->sc_ah; + int i; + + /* XXX return value */ + if (!sc->sc_invalid) { + (void) ath_hal_stoptxdma(ah, sc->sc_bhalq); + DPRINTF(sc, ATH_DEBUG_RESET, "%s: beacon queue %p\n", __func__, + (caddr_t) ath_hal_gettxbuf(ah, sc->sc_bhalq)); + for (i = 0; i < HAL_NUM_TX_QUEUES; i++) + if (ATH_TXQ_SETUP(sc, i)) + ath_tx_stopdma(sc, &sc->sc_txq[i]); + } + sc->sc_dev.trans_start = jiffies; + netif_start_queue(&sc->sc_dev); + for (i = 0; i < HAL_NUM_TX_QUEUES; i++) + if (ATH_TXQ_SETUP(sc, i)) + ath_tx_draintxq(sc, &sc->sc_txq[i]); +} + +/* + * Disable the receive h/w in preparation for a reset. + */ +static void +ath_stoprecv(struct ath_softc *sc) +{ +#define PA2DESC(_sc, _pa) \ + ((struct ath_desc *)((caddr_t)(_sc)->sc_desc + \ + ((_pa) - (_sc)->sc_desc_daddr))) + struct ath_hal *ah = sc->sc_ah; + + ath_hal_stoppcurecv(ah); /* disable PCU */ + ath_hal_setrxfilter(ah, 0); /* clear recv filter */ + ath_hal_stopdmarecv(ah); /* disable DMA engine */ + mdelay(3); /* 3ms is long enough for 1 frame */ +#ifdef AR_DEBUG + if (sc->sc_debug & (ATH_DEBUG_RESET | ATH_DEBUG_FATAL)) { + struct ath_buf *bf; + + printk("ath_stoprecv: rx queue %p, link %p\n", + (caddr_t) ath_hal_getrxbuf(ah), sc->sc_rxlink); + STAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) { + struct ath_desc *ds = bf->bf_desc; + HAL_STATUS status = ath_hal_rxprocdesc(ah, ds, + bf->bf_daddr, PA2DESC(sc, ds->ds_link)); + if (status == HAL_OK || (sc->sc_debug & ATH_DEBUG_FATAL)) + ath_printrxbuf(bf, status == HAL_OK); + } + } +#endif + sc->sc_rxlink = NULL; /* just in case */ +#undef PA2DESC +} + +/* + * Enable the receive h/w following a reset. + */ +static int +ath_startrecv(struct ath_softc *sc) +{ + struct ath_hal *ah = sc->sc_ah; + struct ieee80211com *ic = &sc->sc_ic; + struct net_device *dev = ic->ic_dev; + struct ath_buf *bf; + + /* + * Cisco's VPN software requires that drivers be able to + * receive encapsulated frames that are larger than the MTU. + * Since we can't be sure how large a frame we'll get, setup + * to handle the larges on possible. If you're not using the + * VPN driver and want to save memory, define ATH_ENFORCE_MTU + * and you'll allocate only what you really need. + */ +#ifdef ATH_ENFORCE_MTU + if (sc->sc_ic.ic_opmode == IEEE80211_M_MONITOR) { + sc->sc_rxbufsize = roundup(IEEE80211_MAX_LEN, sc->sc_cachelsz); + } else { + sc->sc_rxbufsize = roundup(sizeof(struct ieee80211_frame) + + dev->mtu + IEEE80211_CRC_LEN + + (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + + IEEE80211_WEP_CRCLEN), sc->sc_cachelsz); + } +#else + sc->sc_rxbufsize = roundup(IEEE80211_MAX_LEN, sc->sc_cachelsz); +#endif + DPRINTF(sc, ATH_DEBUG_RESET, "%s: mtu %u cachelsz %u rxbufsize %u\n", + __func__, dev->mtu, sc->sc_cachelsz, sc->sc_rxbufsize); + + sc->sc_rxlink = NULL; + STAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) { + int error = ath_rxbuf_init(sc, bf); + if (error != 0) + return error; + } + + bf = STAILQ_FIRST(&sc->sc_rxbuf); + ath_hal_putrxbuf(ah, bf->bf_daddr); + ath_hal_rxena(ah); /* enable recv descriptors */ + ath_mode_init(&sc->sc_dev); /* set filters, etc. */ + ath_hal_startpcurecv(ah); /* re-enable PCU/DMA engine */ + return 0; +} + +/* + * Update internal state after a channel change. + */ +static void +ath_chan_change(struct ath_softc *sc, struct ieee80211_channel *chan) +{ + struct ieee80211com *ic = &sc->sc_ic; + enum ieee80211_phymode mode; + + /* + * Change channels and update the h/w rate map + * if we're switching; e.g. 11a to 11b/g. + */ + mode = ieee80211_chan2mode(ic, chan); + if (mode != sc->sc_curmode) + ath_setcurmode(sc, mode); +#ifdef notyet + /* + * Update BPF state. + */ + sc->sc_tx_th.wt_chan_freq = sc->sc_rx_th.wr_chan_freq = + htole16(chan->ic_freq); + sc->sc_tx_th.wt_chan_flags = sc->sc_rx_th.wr_chan_flags = + htole16(chan->ic_flags); +#endif +} + +/* + * Set/change channels. If the channel is really being changed, + * it's done by reseting the chip. To accomplish this we must + * first cleanup any pending DMA, then restart stuff after a la + * ath_init. + */ +static int +ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan) +{ + struct ath_hal *ah = sc->sc_ah; + struct ieee80211com *ic = &sc->sc_ic; + struct net_device *dev = ic->ic_dev; + HAL_CHANNEL hchan; + + /* + * Convert to a HAL channel description with + * the flags constrained to reflect the current + * operating mode. + */ + hchan.channel = chan->ic_freq; + hchan.channelFlags = ath_chan2flags(ic, chan); + + DPRINTF(sc, ATH_DEBUG_RESET, "%s: %u (%u MHz) -> %u (%u MHz)\n", + __func__, + ath_hal_mhz2ieee(sc->sc_curchan.channel, + sc->sc_curchan.channelFlags), + sc->sc_curchan.channel, + ath_hal_mhz2ieee(hchan.channel, hchan.channelFlags), hchan.channel); + if (hchan.channel != sc->sc_curchan.channel || + hchan.channelFlags != sc->sc_curchan.channelFlags) { + HAL_STATUS status; + + /* + * To switch channels clear any pending DMA operations; + * wait long enough for the RX fifo to drain, reset the + * hardware at the new frequency, and then re-enable + * the relevant bits of the h/w. + */ + ath_hal_intrset(ah, 0); /* disable interrupts */ + ath_draintxq(sc); /* clear pending tx frames */ + ath_stoprecv(sc); /* turn off frame recv */ + if (!ath_hal_reset(ah, ic->ic_opmode, &hchan, AH_TRUE, &status)) { + printk("%s: %s: unable to reset channel %u (%uMhz): '%s'" + " (HAL status %u)\n", dev->name, __func__, + ieee80211_chan2ieee(ic, chan), chan->ic_freq, + hal_status_desc[status], status); + return EIO; + } + sc->sc_curchan = hchan; + + /* + * Re-enable rx framework. + */ + if (ath_startrecv(sc) != 0) { + printk("%s: %s: unable to restart recv logic\n", + dev->name, __func__); + return EIO; + } + + /* + * Change channels and update the h/w rate map + * if we're switching; e.g. 11a to 11b/g. + */ + ic->ic_ibss_chan = chan; + ath_chan_change(sc, chan); + + /* + * Re-enable interrupts. + */ + ath_hal_intrset(ah, sc->sc_imask); + } + return 0; +} + +static void +ath_next_scan(unsigned long arg) +{ + struct net_device *dev = (struct net_device *) arg; + struct ath_softc *sc = dev->priv; + struct ieee80211com *ic = &sc->sc_ic; + + if (ic->ic_state == IEEE80211_S_SCAN) + ieee80211_next_scan(ic); +} + +/* + * Periodically recalibrate the PHY to account + * for temperature/environment changes. + */ +static void +ath_calibrate(unsigned long arg) +{ + struct net_device *dev = (struct net_device *) arg; + struct ath_softc *sc = dev->priv; + struct ath_hal *ah = sc->sc_ah; + + sc->sc_stats.ast_per_cal++; + + DPRINTF(sc, ATH_DEBUG_CALIBRATE, "%s: channel %u/%x\n", + __func__, sc->sc_curchan.channel, sc->sc_curchan.channelFlags); + + if (ath_hal_getrfgain(ah) == HAL_RFGAIN_NEED_CHANGE) { + /* + * Rfgain is out of bounds, reset the chip + * to load new gain values. + */ + sc->sc_stats.ast_per_rfgain++; + ath_reset(dev); + } + if (!ath_hal_calibrate(ah, &sc->sc_curchan)) { + DPRINTF(sc, ATH_DEBUG_ANY, + "%s: calibration of channel %u failed\n", + __func__, sc->sc_curchan.channel); + sc->sc_stats.ast_per_calfail++; + } + sc->sc_cal_ch.expires = jiffies + (ath_calinterval * HZ); + add_timer(&sc->sc_cal_ch); +} + +static int +ath_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) +{ + struct net_device *dev = ic->ic_dev; + struct ath_softc *sc = dev->priv; + struct ath_hal *ah = sc->sc_ah; + struct ieee80211_node *ni; + int i, error; + u_int8_t *bssid; + u_int32_t rfilt; + static const HAL_LED_STATE leds[] = { + HAL_LED_INIT, /* IEEE80211_S_INIT */ + HAL_LED_SCAN, /* IEEE80211_S_SCAN */ + HAL_LED_AUTH, /* IEEE80211_S_AUTH */ + HAL_LED_ASSOC, /* IEEE80211_S_ASSOC */ + HAL_LED_RUN, /* IEEE80211_S_RUN */ + }; + + DPRINTF(sc, ATH_DEBUG_STATE, "%s: %s -> %s\n", __func__, + ieee80211_state_name[ic->ic_state], + ieee80211_state_name[nstate]); + + del_timer(&sc->sc_scan_ch); /* ap/neighbor scan timer */ + del_timer(&sc->sc_cal_ch); /* periodic calibration timer */ + ath_hal_setledstate(ah, leds[nstate]); /* set LED */ + netif_stop_queue(dev); /* before we do anything else */ + + if (nstate == IEEE80211_S_INIT) { + sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS); + ath_hal_intrset(ah, sc->sc_imask); + goto done; + } + ni = ic->ic_bss; + error = ath_chan_set(sc, ni->ni_chan); + if (error != 0) + goto bad; + rfilt = ath_calcrxfilter(sc); + if (nstate == IEEE80211_S_SCAN) + bssid = dev->broadcast; + else + bssid = ni->ni_bssid; + ath_hal_setrxfilter(ah, rfilt); + DPRINTF(sc, ATH_DEBUG_STATE, "%s: RX filter 0x%x bssid %s\n", + __func__, rfilt, ether_sprintf(bssid)); + + if (nstate == IEEE80211_S_RUN && ic->ic_opmode == IEEE80211_M_STA) + ath_hal_setassocid(ah, bssid, ni->ni_associd); + else + ath_hal_setassocid(ah, bssid, 0); + if (ic->ic_flags & IEEE80211_F_PRIVACY) { + for (i = 0; i < IEEE80211_WEP_NKID; i++) + if (ath_hal_keyisvalid(ah, i)) + ath_hal_keysetmac(ah, i, bssid); + } + + if (ic->ic_opmode == IEEE80211_M_MONITOR) { + /* nothing to do */; + } else if (nstate == IEEE80211_S_RUN) { + DPRINTF(sc, ATH_DEBUG_STATE, + "%s(RUN): ic_flags=0x%08x iv=%d bssid=%s " + "capinfo=0x%04x chan=%d\n" + , __func__ + , ic->ic_flags + , ni->ni_intval + , ether_sprintf(ni->ni_bssid) + , ni->ni_capinfo + , ieee80211_chan2ieee(ic, ni->ni_chan)); + + /* + * Allocate and setup the beacon frame for AP or adhoc mode. + */ + if (ic->ic_opmode == IEEE80211_M_HOSTAP || + ic->ic_opmode == IEEE80211_M_IBSS) { + error = ath_beacon_alloc(sc, ni); + if (error != 0) + goto bad; + } + + /* + * Configure the beacon and sleep timers. + */ + ath_beacon_config(sc); + } else { + ath_hal_intrset(ah, + sc->sc_imask &~ (HAL_INT_SWBA | HAL_INT_BMISS)); + sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS); + } +done: + /* + * Notify the rate control algorithm. + */ + ath_rate_newstate(sc, nstate); + /* + * Invoke the parent method to complete the work. + */ + error = sc->sc_newstate(ic, nstate, arg); + /* + * Finally, start any timers. + */ + if (nstate == IEEE80211_S_RUN) { + /* start periodic recalibration timer */ + mod_timer(&sc->sc_cal_ch, jiffies + (ath_calinterval * HZ)); + } else if (nstate == IEEE80211_S_SCAN) { + /* start ap/neighbor scan timer */ + mod_timer(&sc->sc_scan_ch, + jiffies + ((HZ * ath_dwelltime) / 1000)); + } +bad: + netif_start_queue(dev); + return error; +} + +/* + * Setup driver-specific state for a newly associated node. + * Note that we're called also on a re-associate, the isnew + * param tells us if this is the first time or not. + */ +static void +ath_newassoc(struct ieee80211com *ic, struct ieee80211_node *ni, int isnew) +{ + struct ath_softc *sc = ic->ic_dev->priv; + + ath_rate_newassoc(sc, ATH_NODE(ni), isnew); +} + +static int +ath_getchannels(struct net_device *dev, u_int cc, + HAL_BOOL outdoor, HAL_BOOL xchanmode) +{ + struct ath_softc *sc = dev->priv; + struct ieee80211com *ic = &sc->sc_ic; + struct ath_hal *ah = sc->sc_ah; + HAL_CHANNEL *chans; + int i, ix, nchan; + + chans = kmalloc(IEEE80211_CHAN_MAX * sizeof(HAL_CHANNEL), GFP_KERNEL); + if (chans == NULL) { + printk("%s: unable to allocate channel table\n", dev->name); + return ENOMEM; + } + if (!ath_hal_init_channels(ah, chans, IEEE80211_CHAN_MAX, &nchan, + cc, HAL_MODE_ALL, outdoor, xchanmode)) { + u_int32_t rd; + + ath_hal_getregdomain(ah, &rd); + printk("%s: unable to collect channel list from hal; " + "regdomain likely %u country code %u\n", + dev->name, rd, cc); + kfree(chans); + return EINVAL; + } + + /* + * Convert HAL channels to ieee80211 ones and insert + * them in the table according to their channel number. + */ + for (i = 0; i < nchan; i++) { + HAL_CHANNEL *c = &chans[i]; + ix = ath_hal_mhz2ieee(c->channel, c->channelFlags); + if (ix > IEEE80211_CHAN_MAX) { + printk("%s: bad hal channel %u (%u/%x) ignored\n", + dev->name, ix, c->channel, c->channelFlags); + continue; + } + /* NB: flags are known to be compatible */ + if (ic->ic_channels[ix].ic_freq == 0) { + ic->ic_channels[ix].ic_freq = c->channel; + ic->ic_channels[ix].ic_flags = c->channelFlags; + } else { + /* channels overlap; e.g. 11g and 11b */ + ic->ic_channels[ix].ic_flags |= c->channelFlags; + } + } + kfree(chans); + return 0; +} + +static void +ath_update_led(struct ath_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ath_hal *ah = sc->sc_ah; + u_int32_t threshold; + + /* + * When not associated, flash LED on for 5s, off for 200ms. + * XXX this assumes 100ms beacon interval. + */ + if (ic->ic_state != IEEE80211_S_RUN) { + threshold = 2 + sc->sc_ledstate * 48; + } else { + threshold = 2 + sc->sc_ledstate * 18; + } + if (ic->ic_stats.is_rx_beacon - sc->sc_beacons >= threshold) { + ath_hal_gpioCfgOutput(ah, sc->sc_ledpin); + ath_hal_gpioset(ah, sc->sc_ledpin, sc->sc_ledstate); + sc->sc_ledstate ^= 1; + sc->sc_beacons = ic->ic_stats.is_rx_beacon; + } +} + +static int +ath_rate_setup(struct net_device *dev, u_int mode) +{ + struct ath_softc *sc = dev->priv; + struct ath_hal *ah = sc->sc_ah; + struct ieee80211com *ic = &sc->sc_ic; + const HAL_RATE_TABLE *rt; + struct ieee80211_rateset *rs; + int i, maxrates; + + switch (mode) { + case IEEE80211_MODE_11A: + sc->sc_rates[mode] = ath_hal_getratetable(ah, HAL_MODE_11A); + break; + case IEEE80211_MODE_11B: + sc->sc_rates[mode] = ath_hal_getratetable(ah, HAL_MODE_11B); + break; + case IEEE80211_MODE_11G: + sc->sc_rates[mode] = ath_hal_getratetable(ah, HAL_MODE_11G); + break; + case IEEE80211_MODE_TURBO: + sc->sc_rates[mode] = ath_hal_getratetable(ah, HAL_MODE_TURBO); + break; + default: + DPRINTF(sc, ATH_DEBUG_ANY, "%s: invalid mode %u\n", + __func__, mode); + return 0; + } + rt = sc->sc_rates[mode]; + if (rt == NULL) + return 0; + if (rt->rateCount > IEEE80211_RATE_MAXSIZE) { + DPRINTF(sc, ATH_DEBUG_ANY, + "%s: rate table too small (%u > %u)\n", + __func__, rt->rateCount, IEEE80211_RATE_MAXSIZE); + maxrates = IEEE80211_RATE_MAXSIZE; + } else + maxrates = rt->rateCount; + rs = &ic->ic_sup_rates[mode]; + for (i = 0; i < maxrates; i++) + rs->rs_rates[i] = rt->info[i].dot11Rate; + rs->rs_nrates = maxrates; + return 1; +} + +static void +ath_setcurmode(struct ath_softc *sc, enum ieee80211_phymode mode) +{ + const HAL_RATE_TABLE *rt; + int i; + + memset(sc->sc_rixmap, 0xff, sizeof(sc->sc_rixmap)); + rt = sc->sc_rates[mode]; + KASSERT(rt != NULL, ("no h/w rate set for phy mode %u", mode)); + for (i = 0; i < rt->rateCount; i++) + sc->sc_rixmap[rt->info[i].dot11Rate & IEEE80211_RATE_VAL] = i; + memset(sc->sc_hwmap, 0, sizeof(sc->sc_hwmap)); + for (i = 0; i < 32; i++) { + u_int8_t ix = rt->rateCodeToIndex[i]; + if (ix != 0xff) + sc->sc_hwmap[i] = rt->info[ix].dot11Rate & IEEE80211_RATE_VAL; + } + sc->sc_currates = rt; + sc->sc_curmode = mode; + /* + * All protection frames are transmited at 2Mb/s for + * 11g, otherwise at 1Mb/s. + * XXX select protection rate index from rate table. + */ + sc->sc_protrix = (mode == IEEE80211_MODE_11G ? 1 : 0); + /* NB: caller is responsible for reseting rate control state */ +} + +#if IEEE80211_VLAN_TAG_USED +static void +ath_vlan_register(struct net_device *dev, struct vlan_group *grp) +{ + struct ath_softc *sc = dev->priv; + struct ieee80211com *ic = &sc->sc_ic; + + ieee80211_vlan_register(ic, grp); +} + +static void +ath_vlan_kill_vid(struct net_device *dev, unsigned short vid) +{ + struct ath_softc *sc = dev->priv; + struct ieee80211com *ic = &sc->sc_ic; + + ieee80211_vlan_kill_vid(ic, vid); +} +#endif /* IEEE80211_VLAN_TAG_USED */ + +#ifdef AR_DEBUG +static void +ath_printrxbuf(struct ath_buf *bf, int done) +{ + struct ath_desc *ds = bf->bf_desc; + + printk("R (%p %p) %08x %08x %08x %08x %08x %08x %c\n", + ds, (struct ath_desc *)bf->bf_daddr, + ds->ds_link, ds->ds_data, + ds->ds_ctl0, ds->ds_ctl1, + ds->ds_hw[0], ds->ds_hw[1], + !done ? ' ' : (ds->ds_rxstat.rs_status == 0) ? '*' : '!'); +} + +static void +ath_printtxbuf(struct ath_buf *bf, int done) +{ + struct ath_desc *ds = bf->bf_desc; + + printk("T (%p %p) %08x %08x %08x %08x %08x %08x %08x %08x %c\n", + ds, (struct ath_desc *)bf->bf_daddr, + ds->ds_link, ds->ds_data, + ds->ds_ctl0, ds->ds_ctl1, + ds->ds_hw[0], ds->ds_hw[1], ds->ds_hw[2], ds->ds_hw[3], + !done ? ' ' : (ds->ds_txstat.ts_status == 0) ? '*' : '!'); +} +#endif /* AR_DEBUG */ + +/* + * Return netdevice statistics. + */ +static struct net_device_stats * +ath_getstats(struct net_device *dev) +{ + struct ath_softc *sc = dev->priv; + struct net_device_stats *stats = &sc->sc_devstats; + + /* update according to private statistics */ + stats->tx_errors = sc->sc_stats.ast_tx_encap + + sc->sc_stats.ast_tx_nonode + + sc->sc_stats.ast_tx_xretries + + sc->sc_stats.ast_tx_fifoerr + + sc->sc_stats.ast_tx_filtered + ; + stats->tx_dropped = sc->sc_stats.ast_tx_nobuf + + sc->sc_stats.ast_tx_nobufmgt; + stats->rx_errors = sc->sc_stats.ast_rx_tooshort + + sc->sc_stats.ast_rx_crcerr + + sc->sc_stats.ast_rx_fifoerr + + sc->sc_stats.ast_rx_badcrypt + ; + stats->rx_crc_errors = sc->sc_stats.ast_rx_crcerr; + + return stats; +} + +#ifdef CONFIG_NET_WIRELESS +/* + * Return wireless extensions statistics. + */ +static struct iw_statistics * +ath_iw_getstats(struct net_device *dev) +{ + struct ath_softc *sc = dev->priv; + struct ieee80211com *ic = &sc->sc_ic; + struct iw_statistics *is = &sc->sc_iwstats; + + ieee80211_iw_getstats(ic, is); + /* add in statistics maintained by the driver */ + is->discard.code += sc->sc_stats.ast_rx_badcrypt; + is->discard.retries += sc->sc_stats.ast_tx_xretries; + is->discard.misc += sc->sc_stats.ast_tx_encap + + sc->sc_stats.ast_tx_nonode + + sc->sc_stats.ast_tx_xretries + + sc->sc_stats.ast_tx_fifoerr + + sc->sc_stats.ast_tx_filtered + + sc->sc_stats.ast_tx_nobuf + + sc->sc_stats.ast_tx_nobufmgt; + ; + is->miss.beacon = sc->sc_stats.ast_bmiss; + + return &sc->sc_iwstats; +} + +#include +/* + * Bounce functions to get to the 802.11 code. These are + * necessary for now because wireless extensions operations + * are done on the underlying device and not the 802.11 instance. + * This will change when there can be multiple 802.11 instances + * associated with a device and we must have a net_device for + * each so we can manipulate them individually. + */ +#define ATH_CHAR_BOUNCE(name) \ +static int \ +ath_ioctl_##name(struct net_device *dev, struct iw_request_info *info, \ + char *erq, char *extra) \ +{ \ + struct ath_softc *sc = dev->priv; \ + return ieee80211_ioctl_##name(&sc->sc_ic, info, erq, extra); \ +} +#define ATH_POINT_BOUNCE(name) \ +static int \ +ath_ioctl_##name(struct net_device *dev, struct iw_request_info *info, \ + struct iw_point *erq, char *extra) \ +{ \ + struct ath_softc *sc = dev->priv; \ + return ieee80211_ioctl_##name(&sc->sc_ic, info, erq, extra); \ +} +#define ATH_PARAM_BOUNCE(name) \ +static int \ +ath_ioctl_##name(struct net_device *dev, struct iw_request_info *info, \ + struct iw_param *erq, char *extra) \ +{ \ + struct ath_softc *sc = dev->priv; \ + return ieee80211_ioctl_##name(&sc->sc_ic, info, erq, extra); \ +} +#define ATH_SOCKADDR_BOUNCE(name) \ +static int \ +ath_ioctl_##name(struct net_device *dev, struct iw_request_info *info, \ + struct sockaddr *erq, char *extra) \ +{ \ + struct ath_softc *sc = dev->priv; \ + return ieee80211_ioctl_##name(&sc->sc_ic, info, erq, extra); \ +} +#define ATH_FREQ_BOUNCE(name) \ +static int \ +ath_ioctl_##name(struct net_device *dev, struct iw_request_info *info, \ + struct iw_freq *erq, char *extra) \ +{ \ + struct ath_softc *sc = dev->priv; \ + return ieee80211_ioctl_##name(&sc->sc_ic, info, erq, extra); \ +} +#define ATH_U32_BOUNCE(name) \ +static int \ +ath_ioctl_##name(struct net_device *dev, struct iw_request_info *info, \ + __u32 *erq, char *extra) \ +{ \ + struct ath_softc *sc = dev->priv; \ + return ieee80211_ioctl_##name(&sc->sc_ic, info, erq, extra); \ +} +#define ATH_VOID_BOUNCE(name) \ +static int \ +ath_ioctl_##name(struct net_device *dev, struct iw_request_info *info, \ + void *erq, char *extra) \ +{ \ + struct ath_softc *sc = dev->priv; \ + return ieee80211_ioctl_##name(&sc->sc_ic, info, erq, extra); \ +} + +ATH_CHAR_BOUNCE(giwname) +ATH_POINT_BOUNCE(siwencode) +ATH_POINT_BOUNCE(giwencode) +ATH_PARAM_BOUNCE(siwrate) +ATH_PARAM_BOUNCE(giwrate) +ATH_PARAM_BOUNCE(siwsens) +ATH_PARAM_BOUNCE(giwsens) +ATH_PARAM_BOUNCE(siwrts) +ATH_PARAM_BOUNCE(giwrts) +ATH_PARAM_BOUNCE(siwfrag) +ATH_PARAM_BOUNCE(giwfrag) +ATH_SOCKADDR_BOUNCE(siwap) +ATH_SOCKADDR_BOUNCE(giwap) +ATH_POINT_BOUNCE(siwnickn) +ATH_POINT_BOUNCE(giwnickn) +ATH_FREQ_BOUNCE(siwfreq) +ATH_FREQ_BOUNCE(giwfreq) +ATH_POINT_BOUNCE(siwessid) +ATH_POINT_BOUNCE(giwessid) +ATH_POINT_BOUNCE(giwrange) +ATH_U32_BOUNCE(siwmode) +ATH_U32_BOUNCE(giwmode) +ATH_PARAM_BOUNCE(siwpower) +ATH_PARAM_BOUNCE(giwpower) +ATH_PARAM_BOUNCE(siwretry) +ATH_PARAM_BOUNCE(giwretry) +ATH_PARAM_BOUNCE(siwtxpow) +ATH_PARAM_BOUNCE(giwtxpow) +ATH_POINT_BOUNCE(iwaplist) +#ifdef SIOCGIWSCAN +ATH_POINT_BOUNCE(siwscan) +ATH_POINT_BOUNCE(giwscan) +#endif +ATH_VOID_BOUNCE(setparam) +ATH_VOID_BOUNCE(getparam) +ATH_VOID_BOUNCE(setkey) +ATH_VOID_BOUNCE(delkey) +ATH_VOID_BOUNCE(setmlme) +ATH_VOID_BOUNCE(setoptie) +ATH_VOID_BOUNCE(getoptie) +ATH_VOID_BOUNCE(addmac) +ATH_VOID_BOUNCE(delmac) +ATH_VOID_BOUNCE(chanlist) + +/* Structures to export the Wireless Handlers */ +static const iw_handler ath_handlers[] = { + (iw_handler) NULL, /* SIOCSIWCOMMIT */ + (iw_handler) ath_ioctl_giwname, /* SIOCGIWNAME */ + (iw_handler) NULL, /* SIOCSIWNWID */ + (iw_handler) NULL, /* SIOCGIWNWID */ + (iw_handler) ath_ioctl_siwfreq, /* SIOCSIWFREQ */ + (iw_handler) ath_ioctl_giwfreq, /* SIOCGIWFREQ */ + (iw_handler) ath_ioctl_siwmode, /* SIOCSIWMODE */ + (iw_handler) ath_ioctl_giwmode, /* SIOCGIWMODE */ + (iw_handler) ath_ioctl_siwsens, /* SIOCSIWSENS */ + (iw_handler) ath_ioctl_giwsens, /* SIOCGIWSENS */ + (iw_handler) NULL /* not used */, /* SIOCSIWRANGE */ + (iw_handler) ath_ioctl_giwrange, /* SIOCGIWRANGE */ + (iw_handler) NULL /* not used */, /* SIOCSIWPRIV */ + (iw_handler) NULL /* kernel code */, /* SIOCGIWPRIV */ + (iw_handler) NULL /* not used */, /* SIOCSIWSTATS */ + (iw_handler) NULL /* kernel code */, /* SIOCGIWSTATS */ + (iw_handler) NULL, /* SIOCSIWSPY */ + (iw_handler) NULL, /* SIOCGIWSPY */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) ath_ioctl_siwap, /* SIOCSIWAP */ + (iw_handler) ath_ioctl_giwap, /* SIOCGIWAP */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) ath_ioctl_iwaplist, /* SIOCGIWAPLIST */ +#ifdef SIOCGIWSCAN + (iw_handler) ath_ioctl_siwscan, /* SIOCSIWSCAN */ + (iw_handler) ath_ioctl_giwscan, /* SIOCGIWSCAN */ +#else + (iw_handler) NULL, /* SIOCSIWSCAN */ + (iw_handler) NULL, /* SIOCGIWSCAN */ +#endif /* SIOCGIWSCAN */ + (iw_handler) ath_ioctl_siwessid, /* SIOCSIWESSID */ + (iw_handler) ath_ioctl_giwessid, /* SIOCGIWESSID */ + (iw_handler) ath_ioctl_siwnickn, /* SIOCSIWNICKN */ + (iw_handler) ath_ioctl_giwnickn, /* SIOCGIWNICKN */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) ath_ioctl_siwrate, /* SIOCSIWRATE */ + (iw_handler) ath_ioctl_giwrate, /* SIOCGIWRATE */ + (iw_handler) ath_ioctl_siwrts, /* SIOCSIWRTS */ + (iw_handler) ath_ioctl_giwrts, /* SIOCGIWRTS */ + (iw_handler) ath_ioctl_siwfrag, /* SIOCSIWFRAG */ + (iw_handler) ath_ioctl_giwfrag, /* SIOCGIWFRAG */ + (iw_handler) ath_ioctl_siwtxpow, /* SIOCSIWTXPOW */ + (iw_handler) ath_ioctl_giwtxpow, /* SIOCGIWTXPOW */ + (iw_handler) ath_ioctl_siwretry, /* SIOCSIWRETRY */ + (iw_handler) ath_ioctl_giwretry, /* SIOCGIWRETRY */ + (iw_handler) ath_ioctl_siwencode, /* SIOCSIWENCODE */ + (iw_handler) ath_ioctl_giwencode, /* SIOCGIWENCODE */ + (iw_handler) ath_ioctl_siwpower, /* SIOCSIWPOWER */ + (iw_handler) ath_ioctl_giwpower, /* SIOCGIWPOWER */ +}; +static const iw_handler ath_priv_handlers[] = { + (iw_handler) ath_ioctl_setparam, /* SIOCWFIRSTPRIV+0 */ + (iw_handler) ath_ioctl_getparam, /* SIOCWFIRSTPRIV+1 */ + (iw_handler) ath_ioctl_setkey, /* SIOCWFIRSTPRIV+2 */ + (iw_handler) NULL, /* SIOCWFIRSTPRIV+3 */ + (iw_handler) ath_ioctl_delkey, /* SIOCWFIRSTPRIV+4 */ + (iw_handler) NULL, /* SIOCWFIRSTPRIV+5 */ + (iw_handler) ath_ioctl_setmlme, /* SIOCWFIRSTPRIV+6 */ + (iw_handler) NULL, /* SIOCWFIRSTPRIV+7 */ + (iw_handler) ath_ioctl_setoptie, /* SIOCWFIRSTPRIV+8 */ + (iw_handler) ath_ioctl_getoptie, /* SIOCWFIRSTPRIV+9 */ + (iw_handler) ath_ioctl_addmac, /* SIOCWFIRSTPRIV+10 */ + (iw_handler) NULL, /* SIOCWFIRSTPRIV+11 */ + (iw_handler) ath_ioctl_delmac, /* SIOCWFIRSTPRIV+12 */ + (iw_handler) NULL, /* SIOCWFIRSTPRIV+13 */ + (iw_handler) ath_ioctl_chanlist, /* SIOCWFIRSTPRIV+14 */ +}; + +static struct iw_handler_def ath_iw_handler_def = { +#define N(a) (sizeof (a) / sizeof (a[0])) + .standard = (iw_handler *) ath_handlers, + .num_standard = N(ath_handlers), + .private = (iw_handler *) ath_priv_handlers, + .num_private = N(ath_priv_handlers), +#undef N +}; +#endif /* CONFIG_NET_WIRELESS */ + +static int +ath_set_mac_address(struct net_device *dev, void *addr) +{ + struct ath_softc *sc = dev->priv; + struct ieee80211com *ic = &sc->sc_ic; + struct ath_hal *ah = sc->sc_ah; + struct sockaddr *mac = addr; + int error; + + if (netif_running(dev)) { + DPRINTF(sc, ATH_DEBUG_ANY, + "%s: cannot set address; device running\n", __func__); + return -EBUSY; + } + DPRINTF(sc, ATH_DEBUG_ANY, "%s: %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", + __func__, + mac->sa_data[0], mac->sa_data[1], mac->sa_data[2], + mac->sa_data[3], mac->sa_data[4], mac->sa_data[5]); + + ATH_LOCK(sc); + /* XXX not right for multiple vap's */ + IEEE80211_ADDR_COPY(ic->ic_myaddr, mac->sa_data); + IEEE80211_ADDR_COPY(dev->dev_addr, mac->sa_data); + ath_hal_setmac(ah, dev->dev_addr); + error = -ath_reset(dev); + ATH_UNLOCK(sc); + + return error; +} + +static int +ath_change_mtu(struct net_device *dev, int mtu) +{ + struct ath_softc *sc = dev->priv; + int error; + + if (!(ATH_MIN_MTU < mtu && mtu <= ATH_MAX_MTU)) { + DPRINTF(sc, ATH_DEBUG_ANY, "%s: invalid %d, min %u, max %u\n", + __func__, mtu, ATH_MIN_MTU, ATH_MAX_MTU); + return -EINVAL; + } + DPRINTF(sc, ATH_DEBUG_ANY, "%s: %d\n", __func__, mtu); + + ATH_LOCK(sc); + dev->mtu = mtu; + /* NB: the rx buffers may need to be reallocated */ + tasklet_disable(&sc->sc_rxtq); + error = -ath_reset(dev); + tasklet_enable(&sc->sc_rxtq); + ATH_UNLOCK(sc); + + return error; +} + +/* + * Diagnostic interface to the HAL. This is used by various + * tools to do things like retrieve register contents for + * debugging. The mechanism is intentionally opaque so that + * it can change frequently w/o concern for compatiblity. + */ +static int +ath_ioctl_diag(struct ath_softc *sc, struct ath_diag *ad) +{ + struct ath_hal *ah = sc->sc_ah; + u_int id = ad->ad_id & ATH_DIAG_ID; + void *indata = NULL; + void *outdata = NULL; + u_int32_t insize = ad->ad_in_size; + u_int32_t outsize = ad->ad_out_size; + int error = 0; + + if (ad->ad_id & ATH_DIAG_IN) { + /* + * Copy in data. + */ + indata = kmalloc(insize, GFP_KERNEL); + if (indata == NULL) { + error = -ENOMEM; + goto bad; + } + if (copy_from_user(indata, ad->ad_in_data, insize)) { + error = -EFAULT; + goto bad; + } + } + if (ad->ad_id & ATH_DIAG_DYN) { + /* + * Allocate a buffer for the results (otherwise the HAL + * returns a pointer to a buffer where we can read the + * results). Note that we depend on the HAL leaving this + * pointer for us to use below in reclaiming the buffer; + * may want to be more defensive. + */ + outdata = kmalloc(outsize, GFP_KERNEL); + if (outdata == NULL) { + error = -ENOMEM; + goto bad; + } + } + if (ath_hal_getdiagstate(ah, id, indata, insize, &outdata, &outsize)) { + if (outsize < ad->ad_out_size) + ad->ad_out_size = outsize; + if (outdata && + copy_to_user(ad->ad_out_data, outdata, ad->ad_out_size)) + error = -EFAULT; + } else { + error = -EINVAL; + } +bad: + if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL) + kfree(indata); + if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL) + kfree(outdata); + return error; +} + +extern int ath_ioctl_ethtool(struct ath_softc *sc, int cmd, void *addr); + +static int +ath_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct ath_softc *sc = dev->priv; + struct ieee80211com *ic = &sc->sc_ic; + int error; + + ATH_LOCK(sc); + switch (cmd) { + case SIOCGATHSTATS: + sc->sc_stats.ast_rx_rssi = ieee80211_getrssi(ic); + if (copy_to_user(ifr->ifr_data, &sc->sc_stats, + sizeof (sc->sc_stats))) + error = -EFAULT; + else + error = 0; + break; + case SIOCGATHDIAG: + if (!capable(CAP_NET_ADMIN)) + error = -EPERM; + else + error = ath_ioctl_diag(sc, (struct ath_diag *) ifr); + break; + case SIOCETHTOOL: + if (copy_from_user(&cmd, ifr->ifr_data, sizeof(cmd))) + error = -EFAULT; + else + error = ath_ioctl_ethtool(sc, cmd, ifr->ifr_data); + break; + default: + error = ieee80211_ioctl(ic, ifr, cmd); + break; + } + ATH_UNLOCK(sc); + return error; +} + +#ifdef CONFIG_SYSCTL +/* + * Sysctls are split into ``static'' and ``dynamic'' tables. + * The former are defined at module load time and are used + * control parameters common to all devices. The latter are + * tied to particular device instances and come and go with + * each device. The split is currently a bit tenuous; many of + * the static ones probably should be dynamic but having them + * static (e.g. debug) means they can be set after a module is + * loaded and before bringing up a device. The alternative + * is to add module parameters. + */ + +/* + * Dynamic (i.e. per-device) sysctls. These are automatically + * mirrored in /proc/sys. + */ +enum { + ATH_SLOTTIME = 1, + ATH_ACKTIMEOUT = 2, + ATH_CTSTIMEOUT = 3, + ATH_SOFTLED = 4, + ATH_LEDPIN = 5, + ATH_COUNTRYCODE = 6, + ATH_REGDOMAIN = 7, + ATH_DEBUG = 8, + ATH_TXANTENNA = 9, + ATH_RXANTENNA = 10, + ATH_DIVERSITY = 11, + ATH_TXINTRPERIOD= 12, +}; + +static int +ATH_SYSCTL_DECL(ath_sysctl_halparam, ctl, write, filp, buffer, lenp, ppos) +{ + struct ath_softc *sc = ctl->extra1; + struct ath_hal *ah = sc->sc_ah; + u_int val; + int ret; + + ctl->data = &val; + ctl->maxlen = sizeof(val); + if (write) { + ret = ATH_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer, + lenp, ppos); + if (ret == 0) { + switch (ctl->ctl_name) { + case ATH_SLOTTIME: + if (!ath_hal_setslottime(ah, val)) + ret = -EINVAL; + break; + case ATH_ACKTIMEOUT: + if (!ath_hal_setacktimeout(ah, val)) + ret = -EINVAL; + break; + case ATH_CTSTIMEOUT: + if (!ath_hal_setctstimeout(ah, val)) + ret = -EINVAL; + break; + case ATH_SOFTLED: + if (val != sc->sc_softled) { + if (val) + ath_hal_gpioCfgOutput(ah, + sc->sc_ledpin); + ath_hal_gpioset(ah, sc->sc_ledpin,!val); + sc->sc_softled = val; + } + break; + case ATH_LEDPIN: + sc->sc_ledpin = val; + break; + case ATH_DEBUG: + sc->sc_debug = val; + break; + case ATH_TXANTENNA: + /* XXX validate? */ + sc->sc_txantenna = val; + break; + case ATH_RXANTENNA: + /* XXX validate? */ + ath_setdefantenna(sc, val); + break; + case ATH_DIVERSITY: + /* XXX validate? */ + if (!sc->sc_hasdiversity) + return -EINVAL; + sc->sc_diversity = val; + ath_hal_setdiversity(ah, val); + break; + case ATH_TXINTRPERIOD: + sc->sc_txintrperiod = val; + break; + default: + return -EINVAL; + } + } + } else { + switch (ctl->ctl_name) { + case ATH_SLOTTIME: + val = ath_hal_getslottime(ah); + break; + case ATH_ACKTIMEOUT: + val = ath_hal_getacktimeout(ah); + break; + case ATH_CTSTIMEOUT: + val = ath_hal_getctstimeout(ah); + break; + case ATH_SOFTLED: + val = sc->sc_softled; + break; + case ATH_LEDPIN: + val = sc->sc_ledpin; + break; + case ATH_COUNTRYCODE: + ath_hal_getcountrycode(ah, &val); + break; + case ATH_REGDOMAIN: + ath_hal_getregdomain(ah, &val); + break; + case ATH_DEBUG: + val = sc->sc_debug; + break; + case ATH_TXANTENNA: + val = sc->sc_txantenna; + break; + case ATH_RXANTENNA: + val = ath_hal_getdefantenna(ah); + break; + case ATH_DIVERSITY: + val = sc->sc_diversity; + break; + case ATH_TXINTRPERIOD: + val = sc->sc_txintrperiod; + break; + default: + return -EINVAL; + } + ret = ATH_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer, + lenp, ppos); + } + return ret; +} + +static int mindwelltime = 100; /* 100ms */ +static int mincalibrate = 1; /* once a second */ +static int maxint = 0x7fffffff; /* 32-bit big */ + +#define CTL_AUTO -2 /* cannot be CTL_ANY or CTL_NONE */ + +static const ctl_table ath_sysctl_template[] = { + { .ctl_name = ATH_SLOTTIME, + .procname = "slottime", + .mode = 0644, + .proc_handler = ath_sysctl_halparam + }, + { .ctl_name = ATH_ACKTIMEOUT, + .procname = "acktimeout", + .mode = 0644, + .proc_handler = ath_sysctl_halparam + }, + { .ctl_name = ATH_CTSTIMEOUT, + .procname = "ctstimeout", + .mode = 0644, + .proc_handler = ath_sysctl_halparam + }, + { .ctl_name = ATH_SOFTLED, + .procname = "softled", + .mode = 0644, + .proc_handler = ath_sysctl_halparam + }, + { .ctl_name = ATH_LEDPIN, + .procname = "ledpin", + .mode = 0644, + .proc_handler = ath_sysctl_halparam + }, + { .ctl_name = ATH_COUNTRYCODE, + .procname = "countrycode", + .mode = 0444, + .proc_handler = ath_sysctl_halparam + }, + { .ctl_name = ATH_REGDOMAIN, + .procname = "regdomain", + .mode = 0444, + .proc_handler = ath_sysctl_halparam + }, +#ifdef AR_DEBUG + { .ctl_name = ATH_DEBUG, + .procname = "debug", + .mode = 0644, + .proc_handler = ath_sysctl_halparam + }, +#endif + { .ctl_name = ATH_TXANTENNA, + .procname = "txantenna", + .mode = 0644, + .proc_handler = ath_sysctl_halparam + }, + { .ctl_name = ATH_RXANTENNA, + .procname = "rxantenna", + .mode = 0644, + .proc_handler = ath_sysctl_halparam + }, + { .ctl_name = ATH_DIVERSITY, + .procname = "diversity", + .mode = 0644, + .proc_handler = ath_sysctl_halparam + }, + { .ctl_name = ATH_TXINTRPERIOD, + .procname = "txintrperiod", + .mode = 0644, + .proc_handler = ath_sysctl_halparam + }, + { 0 } +}; + +static void +ath_dynamic_sysctl_register(struct ath_softc *sc) +{ + int i, space; + + space = 5*sizeof(struct ctl_table) + sizeof(ath_sysctl_template); + sc->sc_sysctls = kmalloc(space, GFP_KERNEL); + if (sc->sc_sysctls == NULL) { + printk("%s: no memory for sysctl table!\n", __func__); + return; + } + + /* setup the table */ + memset(sc->sc_sysctls, 0, space); + sc->sc_sysctls[0].ctl_name = CTL_DEV; + sc->sc_sysctls[0].procname = "dev"; + sc->sc_sysctls[0].mode = 0555; + sc->sc_sysctls[0].child = &sc->sc_sysctls[2]; + /* [1] is NULL terminator */ + sc->sc_sysctls[2].ctl_name = CTL_AUTO; + sc->sc_sysctls[2].procname = sc->sc_dev.name; + sc->sc_sysctls[2].mode = 0555; + sc->sc_sysctls[2].child = &sc->sc_sysctls[4]; + /* [3] is NULL terminator */ + /* copy in pre-defined data */ + memcpy(&sc->sc_sysctls[4], ath_sysctl_template, + sizeof(ath_sysctl_template)); + + /* add in dynamic data references */ + for (i = 4; sc->sc_sysctls[i].ctl_name; i++) + if (sc->sc_sysctls[i].extra1 == NULL) + sc->sc_sysctls[i].extra1 = sc; + + /* and register everything */ + sc->sc_sysctl_header = register_sysctl_table(sc->sc_sysctls, 1); + if (!sc->sc_sysctl_header) { + printk("%s: failed to register sysctls!\n", sc->sc_dev.name); + kfree(sc->sc_sysctls); + sc->sc_sysctls = NULL; + } + + /* initialize values */ + sc->sc_debug = ath_debug; + sc->sc_txantenna = 0; /* default to auto-selection */ + sc->sc_txintrperiod = ATH_TXINTR_PERIOD; +} + +static void +ath_dynamic_sysctl_unregister(struct ath_softc *sc) +{ + if (sc->sc_sysctl_header) { + unregister_sysctl_table(sc->sc_sysctl_header); + sc->sc_sysctl_header = NULL; + } + if (sc->sc_sysctls) { + kfree(sc->sc_sysctls); + sc->sc_sysctls = NULL; + } +} + +/* + * Announce various information on device/driver attach. + */ +static void +ath_announce(struct net_device *dev) +{ +#define HAL_MODE_DUALBAND (HAL_MODE_11A|HAL_MODE_11B) + struct ath_softc *sc = dev->priv; + struct ath_hal *ah = sc->sc_ah; + u_int modes, cc; + int i; + + printk("%s: mac %d.%d phy %d.%d", dev->name, + ah->ah_macVersion, ah->ah_macRev, + ah->ah_phyRev >> 4, ah->ah_phyRev & 0xf); + /* + * Print radio revision(s). We check the wireless modes + * to avoid falsely printing revs for inoperable parts. + * Dual-band radio revs are returned in the 5Ghz rev number. + */ + ath_hal_getcountrycode(ah, &cc); + modes = ath_hal_getwirelessmodes(ah, cc); + if ((modes & HAL_MODE_DUALBAND) == HAL_MODE_DUALBAND) { + if (ah->ah_analog5GhzRev && ah->ah_analog2GhzRev) + printk(" 5ghz radio %d.%d 2ghz radio %d.%d", + ah->ah_analog5GhzRev >> 4, + ah->ah_analog5GhzRev & 0xf, + ah->ah_analog2GhzRev >> 4, + ah->ah_analog2GhzRev & 0xf); + else + printk(" radio %d.%d", ah->ah_analog5GhzRev >> 4, + ah->ah_analog5GhzRev & 0xf); + } else + printk(" radio %d.%d", ah->ah_analog5GhzRev >> 4, + ah->ah_analog5GhzRev & 0xf); + printk("\n"); + printk("%s: 802.11 address: %s\n", + dev->name, ether_sprintf(dev->dev_addr)); + for (i = 0; i <= WME_AC_VO; i++) { + struct ath_txq *txq = sc->sc_ac2q[i]; + printk("%s: Use hw queue %u for %s traffic\n", + dev->name, txq->axq_qnum, acnames[i]); + } +#undef HAL_MODE_DUALBAND +} + +/* + * Static (i.e. global) sysctls. Note that the hal sysctls + * are located under ours by sharing the setting for DEV_ATH. + */ +enum { + DEV_ATH = 9, /* XXX known by hal */ +}; + +static ctl_table ath_static_sysctls[] = { +#ifdef AR_DEBUG + { .ctl_name = CTL_AUTO, + .procname = "debug", + .mode = 0644, + .data = &ath_debug, + .maxlen = sizeof(ath_debug), + .proc_handler = proc_dointvec + }, +#endif + { .ctl_name = CTL_AUTO, + .procname = "countrycode", + .mode = 0444, + .data = &ath_countrycode, + .maxlen = sizeof(ath_countrycode), + .proc_handler = proc_dointvec + }, + { .ctl_name = CTL_AUTO, + .procname = "regdomain", + .mode = 0444, + .data = &ath_regdomain, + .maxlen = sizeof(ath_regdomain), + .proc_handler = proc_dointvec + }, + { .ctl_name = CTL_AUTO, + .procname = "outdoor", + .mode = 0444, + .data = &ath_outdoor, + .maxlen = sizeof(ath_outdoor), + .proc_handler = proc_dointvec + }, + { .ctl_name = CTL_AUTO, + .procname = "xchanmode", + .mode = 0444, + .data = &ath_xchanmode, + .maxlen = sizeof(ath_xchanmode), + .proc_handler = proc_dointvec + }, + { .ctl_name = CTL_AUTO, + .procname = "dwelltime", + .mode = 0644, + .data = &ath_dwelltime, + .maxlen = sizeof(ath_dwelltime), + .extra1 = &mindwelltime, + .extra2 = &maxint, + .proc_handler = proc_dointvec_minmax + }, + { .ctl_name = CTL_AUTO, + .procname = "calibrate", + .mode = 0644, + .data = &ath_calinterval, + .maxlen = sizeof(ath_calinterval), + .extra1 = &mincalibrate, + .extra2 = &maxint, + .proc_handler = proc_dointvec_minmax + }, + { 0 } +}; +static ctl_table ath_ath_table[] = { + { .ctl_name = DEV_ATH, + .procname = "ath", + .mode = 0555, + .child = ath_static_sysctls + }, { 0 } +}; +static ctl_table ath_root_table[] = { + { .ctl_name = CTL_DEV, + .procname = "dev", + .mode = 0555, + .child = ath_ath_table + }, { 0 } +}; +static struct ctl_table_header *ath_sysctl_header; + +void +ath_sysctl_register(void) +{ + static int initialized = 0; + + if (!initialized) { + ath_sysctl_header = + register_sysctl_table(ath_root_table, 1); + initialized = 1; + } +} + +void +ath_sysctl_unregister(void) +{ + if (ath_sysctl_header) + unregister_sysctl_table(ath_sysctl_header); +} +#endif /* CONFIG_SYSCTL */ diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/ath/if_ath_pci.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/ath/if_ath_pci.c --- linux-2.6.11-rc4-mm1/drivers/net/wireless/ath/if_ath_pci.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/ath/if_ath_pci.c 2005-02-24 13:06:17.184149632 -0800 @@ -0,0 +1,361 @@ +/*- + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting + * Copyright (c) 2004-2005 Atheros Communications, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + * + * $Id: if_ath_pci.c,v 1.7 2005/02/16 16:08:41 samleffler Exp $ + */ +#include "opt_ah.h" + +#ifndef EXPORT_SYMTAB +#define EXPORT_SYMTAB +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "if_media.h" +#include + +#include "if_athvar.h" +#include "if_ath_pci.h" + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)) +/* + * PCI initialization uses Linux 2.4.x version and + * older kernels do not support this + */ +#error Atheros PCI version requires at least Linux kernel version 2.4.0 +#endif /* kernel < 2.4.0 */ + +struct ath_pci_softc { + struct ath_softc aps_sc; +#ifdef CONFIG_PM + u32 aps_pmstate[16]; +#endif +}; + +/* + * User a static table of PCI id's for now. While this is the + * "new way" to do things, we may want to switch back to having + * the HAL check them by defining a probe method. + */ +static struct pci_device_id ath_pci_id_table[] __devinitdata = { + { 0x168c, 0x0007, PCI_ANY_ID, PCI_ANY_ID }, + { 0x168c, 0x0012, PCI_ANY_ID, PCI_ANY_ID }, + { 0x168c, 0x0013, PCI_ANY_ID, PCI_ANY_ID }, + { 0xa727, 0x0013, PCI_ANY_ID, PCI_ANY_ID }, /* 3com */ + { 0x10b7, 0x0013, PCI_ANY_ID, PCI_ANY_ID }, /* 3com 3CRDAG675 */ + { 0x168c, 0x1014, PCI_ANY_ID, PCI_ANY_ID }, /* IBM minipci 5212 */ + { 0x168c, 0x0015, PCI_ANY_ID, PCI_ANY_ID }, + { 0x168c, 0x0016, PCI_ANY_ID, PCI_ANY_ID }, + { 0x168c, 0x0017, PCI_ANY_ID, PCI_ANY_ID }, + { 0x168c, 0x0018, PCI_ANY_ID, PCI_ANY_ID }, + { 0x168c, 0x0019, PCI_ANY_ID, PCI_ANY_ID }, + { 0x168c, 0x001a, PCI_ANY_ID, PCI_ANY_ID }, + { 0 } +}; + +static int +ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + unsigned long phymem; + unsigned long mem; + struct ath_pci_softc *sc; + struct net_device *dev; + const char *athname; + u_int8_t csz; + u32 val; + + if (pci_enable_device(pdev)) + return (-EIO); + + /* XXX 32-bit addressing only */ + if (pci_set_dma_mask(pdev, 0xffffffff)) { + printk(KERN_ERR "ath_pci: 32-bit DMA not available\n"); + goto bad; + } + + /* + * Cache line size is used to size and align various + * structures used to communicate with the hardware. + */ + pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &csz); + if (csz == 0) { + /* + * Linux 2.4.18 (at least) writes the cache line size + * register as a 16-bit wide register which is wrong. + * We must have this setup properly for rx buffer + * DMA to work so force a reasonable value here if it + * comes up zero. + */ + csz = L1_CACHE_BYTES / sizeof(u_int32_t); + pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, csz); + } + /* + * The default setting of latency timer yields poor results, + * set it to the value used by other systems. It may be worth + * tweaking this setting more. + */ + pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xa8); + + pci_set_master(pdev); + + /* + * Disable the RETRY_TIMEOUT register (0x41) to keep + * PCI Tx retries from interfering with C3 CPU state. + * + * Code taken from ipw2100 driver - jg + */ + pci_read_config_dword(pdev, 0x40, &val); + if ((val & 0x0000ff00) != 0) + pci_write_config_dword(pdev, 0x40, val & 0xffff00ff); + + phymem = pci_resource_start(pdev, 0); + if (!request_mem_region(phymem, pci_resource_len(pdev, 0), "ath")) { + printk(KERN_ERR "ath_pci: cannot reserve PCI memory region\n"); + goto bad; + } + + mem = (unsigned long) ioremap(phymem, pci_resource_len(pdev, 0)); + if (!mem) { + printk(KERN_ERR "ath_pci: cannot remap PCI memory region\n") ; + goto bad1; + } + + sc = kmalloc(sizeof(struct ath_pci_softc), GFP_KERNEL); + if (sc == NULL) { + printk(KERN_ERR "ath_pci: no memory for device state\n"); + goto bad2; + } + memset(sc, 0, sizeof(struct ath_pci_softc)); + + /* + * Mark the device as detached to avoid processing + * interrupts until setup is complete. + */ + sc->aps_sc.sc_invalid = 1; + + dev = &sc->aps_sc.sc_dev; /* XXX blech, violate layering */ + memcpy(dev->name, "ath%d", sizeof("ath%d")); + + dev->irq = pdev->irq; + dev->mem_start = mem; + dev->mem_end = mem + pci_resource_len(pdev, 0); + dev->priv = sc; + + SET_MODULE_OWNER(dev); + SET_NETDEV_DEV(dev, &pdev->dev); + + sc->aps_sc.sc_bdev = (void *) pdev; + + pci_set_drvdata(pdev, dev); + + if (request_irq(dev->irq, ath_intr, SA_SHIRQ, dev->name, dev)) { + printk(KERN_WARNING "%s: request_irq failed\n", dev->name); + goto bad3; + } + + if (ath_attach(id->device, dev) != 0) + goto bad4; + + athname = ath_hal_probe(id->vendor, id->device); + printk(KERN_INFO "%s: %s: mem=0x%lx, irq=%d\n", + dev->name, athname ? athname : "Atheros ???", phymem, dev->irq); + + /* ready to process interrupts */ + sc->aps_sc.sc_invalid = 0; + + return 0; +bad4: + free_irq(dev->irq, dev); +bad3: + kfree(sc); +bad2: + iounmap((void *) mem); +bad1: + release_mem_region(phymem, pci_resource_len(pdev, 0)); +bad: + pci_disable_device(pdev); + return (-ENODEV); +} + +static void +ath_pci_remove(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + + ath_detach(dev); + if (dev->irq) + free_irq(dev->irq, dev); + iounmap((void *) dev->mem_start); + release_mem_region(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + pci_disable_device(pdev); + free_netdev(dev); +} + +#ifdef CONFIG_PM +static int +ath_pci_suspend(struct pci_dev *pdev, u32 state) +{ + struct net_device *dev = pci_get_drvdata(pdev); + + ath_suspend(dev); + PCI_SAVE_STATE(pdev, + ((struct ath_pci_softc *)dev->priv)->aps_pmstate); + pci_disable_device(pdev); + pci_set_power_state(pdev, 3); + + return (0); +} + +static int +ath_pci_resume(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + u32 val; + + pci_enable_device(pdev); + PCI_RESTORE_STATE(pdev, + ((struct ath_pci_softc *)dev->priv)->aps_pmstate); + /* + * Suspend/Resume resets the PCI configuration space, so we have to + * re-disable the RETRY_TIMEOUT register (0x41) to keep + * PCI Tx retries from interfering with C3 CPU state + * + * Code taken from ipw2100 driver - jg + */ + pci_read_config_dword(pdev, 0x40, &val); + if ((val & 0x0000ff00) != 0) + pci_write_config_dword(pdev, 0x40, val & 0xffff00ff); + ath_resume(dev); + + return (0); +} +#endif /* CONFIG_PM */ + +MODULE_DEVICE_TABLE(pci, ath_pci_id_table); + +static struct pci_driver ath_pci_drv_id = { + .name = "ath_pci", + .id_table = ath_pci_id_table, + .probe = ath_pci_probe, + .remove = ath_pci_remove, +#ifdef CONFIG_PM + .suspend = ath_pci_suspend, + .resume = ath_pci_resume, +#endif /* CONFIG_PM */ + /* Linux 2.4.6 has save_state and enable_wake that are not used here */ +}; + +/* + * Module glue. + */ +#include "version.h" +static char *version = ATH_PCI_VERSION " (EXPERIMENTAL)"; +static char *dev_info = "ath_pci"; + +#include + +int +ath_ioctl_ethtool(struct ath_softc *sc, int cmd, void *addr) +{ + struct ethtool_drvinfo info; + + if (cmd != ETHTOOL_GDRVINFO) + return -EOPNOTSUPP; + memset(&info, 0, sizeof(info)); + info.cmd = cmd; + strncpy(info.driver, dev_info, sizeof(info.driver)-1); + strncpy(info.version, version, sizeof(info.version)-1); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,22) + /* include the device name so later versions of kudzu DTRT */ + strncpy(info.bus_info, pci_name((struct pci_dev *)sc->sc_bdev), + sizeof(info.bus_info)-1); +#endif + return copy_to_user(addr, &info, sizeof(info)) ? -EFAULT : 0; +} + +MODULE_AUTHOR("Errno Consulting, Sam Leffler"); +MODULE_DESCRIPTION("Support for Atheros 802.11 wireless LAN cards."); +MODULE_SUPPORTED_DEVICE("Atheros WLAN cards"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("Dual BSD/GPL"); +#endif + +static int __init +init_ath_pci(void) +{ + printk(KERN_INFO "%s: %s\n", dev_info, version); + + if (pci_register_driver(&ath_pci_drv_id) < 0) { + printk("ath_pci: No devices found, driver not installed.\n"); + pci_unregister_driver(&ath_pci_drv_id); + return (-ENODEV); + } +#ifdef CONFIG_SYSCTL + ath_sysctl_register(); +#endif + return (0); +} +module_init(init_ath_pci); + +static void __exit +exit_ath_pci(void) +{ +#ifdef CONFIG_SYSCTL + ath_sysctl_unregister(); +#endif + pci_unregister_driver(&ath_pci_drv_id); + + printk(KERN_INFO "%s: driver unloaded\n", dev_info); +} +module_exit(exit_ath_pci); + +/* return bus cachesize in 4B word units */ +void +bus_read_cachesize(struct ath_softc *sc, u_int8_t *csz) +{ + pci_read_config_byte(sc->sc_bdev, PCI_CACHE_LINE_SIZE, csz); +} diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/ath/if_ath_pci.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/ath/if_ath_pci.h --- linux-2.6.11-rc4-mm1/drivers/net/wireless/ath/if_ath_pci.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/ath/if_ath_pci.h 2005-02-24 13:06:17.184149632 -0800 @@ -0,0 +1,60 @@ +/*- + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting + * Copyright (c) 2004-2005 Atheros Communications, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + * + * $Id: if_ath_pci.h,v 1.3 2005/02/16 16:08:41 samleffler Exp $ + */ + +#ifndef _DEV_ATH_PCI_H_ +#define _DEV_ATH_PCI_H_ + +#include +#define bus_map_single pci_map_single +#define bus_unmap_single pci_unmap_single +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9) +#define bus_dma_sync_single pci_dma_sync_single_for_cpu +#define PCI_SAVE_STATE(a,b) pci_save_state(a) +#define PCI_RESTORE_STATE(a,b) pci_restore_state(a) +#else +#define bus_dma_sync_single pci_dma_sync_single +#define PCI_SAVE_STATE(a,b) pci_save_state(a,b) +#define PCI_RESTORE_STATE(a,b) pci_restore_state(a,b) +#endif +#define bus_alloc_consistent pci_alloc_consistent +#define bus_free_consistent pci_free_consistent +#define BUS_DMA_FROMDEVICE PCI_DMA_FROMDEVICE +#define BUS_DMA_TODEVICE PCI_DMA_TODEVICE + +#endif /* _DEV_ATH_PCI_H_ */ diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/ath/if_athioctl.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/ath/if_athioctl.h --- linux-2.6.11-rc4-mm1/drivers/net/wireless/ath/if_athioctl.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/ath/if_athioctl.h 2005-02-24 13:06:17.184149632 -0800 @@ -0,0 +1,121 @@ +/*- + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + * + * $FreeBSD: src/sys/dev/ath/if_athioctl.h,v 1.5 2003/12/28 07:00:32 sam Exp $ + */ + +/* + * Ioctl-related defintions for the Atheros Wireless LAN controller driver. + */ +#ifndef _DEV_ATH_ATHIOCTL_H +#define _DEV_ATH_ATHIOCTL_H + +struct ath_stats { + u_int32_t ast_watchdog; /* device reset by watchdog */ + u_int32_t ast_hardware; /* fatal hardware error interrupts */ + u_int32_t ast_bmiss; /* beacon miss interrupts */ + u_int32_t ast_rxorn; /* rx overrun interrupts */ + u_int32_t ast_rxeol; /* rx eol interrupts */ + u_int32_t ast_txurn; /* tx underrun interrupts */ + u_int32_t ast_mib; /* mib interrupts */ + u_int32_t ast_tx_mgmt; /* management frames transmitted */ + u_int32_t ast_tx_discard; /* frames discarded prior to assoc */ + u_int32_t ast_tx_invalid; /* frames discarded 'cuz device gone */ + u_int32_t ast_tx_qstop; /* tx queue stopped 'cuz full */ + u_int32_t ast_tx_encap; /* tx encapsulation failed */ + u_int32_t ast_tx_nonode; /* tx failed 'cuz no node */ + u_int32_t ast_tx_nobuf; /* tx failed 'cuz no tx buffer (data) */ + u_int32_t ast_tx_nobufmgt;/* tx failed 'cuz no tx buffer (mgmt)*/ + u_int32_t ast_tx_xretries;/* tx failed 'cuz too many retries */ + u_int32_t ast_tx_fifoerr; /* tx failed 'cuz FIFO underrun */ + u_int32_t ast_tx_filtered;/* tx failed 'cuz xmit filtered */ + u_int32_t ast_tx_shortretry;/* tx on-chip retries (short) */ + u_int32_t ast_tx_longretry;/* tx on-chip retries (long) */ + u_int32_t ast_tx_badrate; /* tx failed 'cuz bogus xmit rate */ + u_int32_t ast_tx_noack; /* tx frames with no ack marked */ + u_int32_t ast_tx_rts; /* tx frames with rts enabled */ + u_int32_t ast_tx_cts; /* tx frames with cts enabled */ + u_int32_t ast_tx_shortpre;/* tx frames with short preamble */ + u_int32_t ast_tx_altrate; /* tx frames with alternate rate */ + u_int32_t ast_tx_protect; /* tx frames with protection */ + u_int32_t ast_rx_orn; /* rx failed 'cuz of desc overrun */ + u_int32_t ast_rx_crcerr; /* rx failed 'cuz of bad CRC */ + u_int32_t ast_rx_fifoerr; /* rx failed 'cuz of FIFO overrun */ + u_int32_t ast_rx_badcrypt;/* rx failed 'cuz decryption */ + u_int32_t ast_rx_badmic; /* rx failed 'cuz MIC failure */ + u_int32_t ast_rx_phyerr; /* rx PHY error summary count */ + u_int32_t ast_rx_phy[32]; /* rx PHY error per-code counts */ + u_int32_t ast_rx_tooshort;/* rx discarded 'cuz frame too short */ + u_int32_t ast_rx_toobig; /* rx discarded 'cuz frame too large */ + u_int32_t ast_rx_nobuf; /* rx setup failed 'cuz no skbuff */ + u_int32_t ast_rx_mgt; /* management frames received */ + u_int32_t ast_rx_ctl; /* control frames received */ + int8_t ast_tx_rssi; /* tx rssi of last ack */ + int8_t ast_rx_rssi; /* rx rssi from histogram */ + u_int32_t ast_be_nobuf; /* no skbuff available for beacon */ + u_int32_t ast_per_cal; /* periodic calibration calls */ + u_int32_t ast_per_calfail;/* periodic calibration failed */ + u_int32_t ast_per_rfgain; /* periodic calibration rfgain reset */ + u_int32_t ast_rate_calls; /* rate control checks */ + u_int32_t ast_rate_raise; /* rate control raised xmit rate */ + u_int32_t ast_rate_drop; /* rate control dropped xmit rate */ + u_int32_t ast_ant_defswitch;/* rx/default antenna switches */ + u_int32_t ast_ant_txswitch;/* tx antenna switches */ + u_int32_t ast_ant_rx[8]; /* rx frames with antenna */ + u_int32_t ast_ant_tx[8]; /* tx frames with antenna */ +}; + +struct ath_diag { + char ad_name[IFNAMSIZ]; /* if name, e.g. "ath0" */ + u_int16_t ad_id; +#define ATH_DIAG_DYN 0x8000 /* allocate buffer in caller */ +#define ATH_DIAG_IN 0x4000 /* copy in parameters */ +#define ATH_DIAG_OUT 0x0000 /* copy out results (always) */ +#define ATH_DIAG_ID 0x0fff + u_int16_t ad_in_size; /* pack to fit, yech */ + caddr_t ad_in_data; + caddr_t ad_out_data; + u_int ad_out_size; + +}; + +#ifdef __linux__ +#define SIOCGATHSTATS (SIOCDEVPRIVATE+0) +#define SIOCGATHDIAG (SIOCDEVPRIVATE+1) +#else +#define SIOCGATHSTATS _IOWR('i', 137, struct ifreq) +#define SIOCGATHDIAG _IOWR('i', 138, struct ath_diag) +#endif +#endif /* _DEV_ATH_ATHIOCTL_H */ diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/ath/if_athrate.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/ath/if_athrate.h --- linux-2.6.11-rc4-mm1/drivers/net/wireless/ath/if_athrate.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/ath/if_athrate.h 2005-02-24 13:06:17.185149480 -0800 @@ -0,0 +1,146 @@ +/*- + * Copyright (c) 2004-2005 Sam Leffler, Errno Consulting + * Copyright (c) 2004-2005 Video54 Technologies, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + * + * $Id: if_athrate.h,v 1.2 2005/02/16 16:08:42 samleffler Exp $ + */ +#ifndef _ATH_RATECTRL_H_ +#define _ATH_RATECTRL_H_ + +/* + * Interface definitions for transmit rate control modules for the + * Atheros driver. + * + * A rate control module is responsible for choosing the transmit rate + * for each data frame. Management+control frames are always sent at + * a fixed rate. + * + * Only one module may be present at a time; the driver references + * rate control interfaces by symbol name. If multiple modules are + * to be supported we'll need to switch to a registration-based scheme + * as is currently done, for example, for authentication modules. + * + * An instance of the rate control module is attached to each device + * at attach time and detached when the device is destroyed. The module + * may associate data with each device and each node (station). Both + * sets of storage are opaque except for the size of the per-node storage + * which must be provided when the module is attached. + * + * The rate control module is notified for each state transition and + * station association/reassociation. Otherwise it is queried for a + * rate for each outgoing frame and provided status from each transmitted + * frame. Any ancillary processing is the responsibility of the module + * (e.g. if periodic processing is required then the module should setup + * it's own timer). + * + * In addition to the transmit rate for each frame the module must also + * indicate the number of attempts to make at the specified rate. If this + * number is != ATH_TXMAXTRY then an additional callback is made to setup + * additional transmit state. The rate control code is assumed to write + * this additional data directly to the transmit descriptor. + */ +struct ath_softc; +struct ath_node; +struct ath_desc; + +struct ath_ratectrl { + size_t arc_space; /* space required for per-node state */ +}; +/* + * Attach/detach a rate control module. + */ +struct ath_ratectrl *ath_rate_attach(struct ath_softc *); +void ath_rate_detach(struct ath_ratectrl *); + + +/* + * State storage handling. + */ +/* + * Initialize per-node state already allocated for the specified + * node; this space can be assumed initialized to zero. + */ +void ath_rate_node_init(struct ath_softc *, struct ath_node *); +/* + * Cleanup any per-node state prior to the node being reclaimed. + */ +void ath_rate_node_cleanup(struct ath_softc *, struct ath_node *); +/* + * Copy per-node state; currently used only to duplicate bss on + * station association. + */ +void ath_rate_node_copy(struct ath_softc *, + struct ath_node *, const struct ath_node *); +/* + * Update rate control state on station associate/reassociate + * (when operating as an ap or for nodes discovered when operating + * in ibss mode). + */ +void ath_rate_newassoc(struct ath_softc *, struct ath_node *, + int isNewAssociation); +/* + * Update/reset rate control state for 802.11 state transitions. + * Important mostly as the analog to ath_rate_newassoc when operating + * in station mode. + */ +void ath_rate_newstate(struct ath_softc *, enum ieee80211_state); + +/* + * Transmit handling. + */ +/* + * Return the transmit info for a data packet. If multi-rate state + * is to be setup then try0 should contain a value other than ATH_TXMATRY + * and ath_rate_setupxtxdesc will be called after deciding if the frame + * can be transmitted with multi-rate retry. + */ +void ath_rate_findrate(struct ath_softc *, struct ath_node *, + HAL_BOOL shortPreamble, size_t frameLen, + u_int8_t *rix, int *try0, u_int8_t *txrate); +/* + * Setup any extended (multi-rate) descriptor state for a data packet. + * The rate index returned by ath_rate_findrate is passed back in. + */ +void ath_rate_setupxtxdesc(struct ath_softc *, struct ath_node *, + struct ath_desc *, HAL_BOOL shortPreamble, u_int8_t rix); +/* + * Update rate control state for a packet associated with the + * supplied transmit descriptor. The routine is invoked both + * for packets that were successfully sent and for those that + * failed (consult the descriptor for details). + */ +void ath_rate_tx_complete(struct ath_softc *, struct ath_node *, + const struct ath_desc *); +#endif /* _ATH_RATECTRL_H_ */ diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/ath/if_athvar.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/ath/if_athvar.h --- linux-2.6.11-rc4-mm1/drivers/net/wireless/ath/if_athvar.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/ath/if_athvar.h 2005-02-24 13:06:17.185149480 -0800 @@ -0,0 +1,478 @@ +/*- + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + * + * $Id: if_athvar.h,v 1.18 2005/02/16 16:08:42 samleffler Exp $ + */ + +/* + * Defintions for the Atheros Wireless LAN controller driver. + */ +#ifndef _DEV_ATH_ATHVAR_H +#define _DEV_ATH_ATHVAR_H + +#include "ah.h" +#include "if_athioctl.h" +#include "if_athrate.h" + +#ifdef CONFIG_NET_WIRELESS +#include +#endif + +/* + * Deduce if tasklets are available. If not then + * fall back to using the immediate work queue. + */ +#include +#ifdef DECLARE_TASKLET /* native tasklets */ +#define tq_struct tasklet_struct +#define ATH_INIT_TQUEUE(a,b,c) tasklet_init((a),(b),(unsigned long)(c)) +#define ATH_SCHEDULE_TQUEUE(a,b) tasklet_schedule((a)) +typedef unsigned long TQUEUE_ARG; +#define mark_bh(a) +#else /* immediate work queue */ +#define ATH_INIT_TQUEUE(a,b,c) INIT_TQUEUE(a,b,c) +#define ATH_SCHEDULE_TQUEUE(a,b) do { \ + *(b) |= queue_task((a), &tq_immediate); \ +} while(0) +typedef void *TQUEUE_ARG; +#define tasklet_disable(t) do { (void) t; local_bh_disable(); } while (0) +#define tasklet_enable(t) do { (void) t; local_bh_enable(); } while (0) +#endif /* !DECLARE_TASKLET */ + +/* + * Guess how the interrupt handler should work. + */ +#if !defined(IRQ_NONE) +typedef void irqreturn_t; +#define IRQ_NONE +#define IRQ_HANDLED +#endif /* !defined(IRQ_NONE) */ + +#ifndef SET_MODULE_OWNER +#define SET_MODULE_OWNER(dev) do { \ + dev->owner = THIS_MODULE; \ +} while (0) +#endif + +#ifndef SET_NETDEV_DEV +#define SET_NETDEV_DEV(ndev, pdev) +#endif + +/* + * Deal with the sysctl handler api changing. + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8) +#define ATH_SYSCTL_DECL(f, ctl, write, filp, buffer, lenp, ppos) \ + f(ctl_table *ctl, int write, struct file *filp, void *buffer, \ + size_t *lenp) +#define ATH_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer, lenp, ppos) \ + proc_dointvec(ctl, write, filp, buffer, lenp) +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8) */ +#define ATH_SYSCTL_DECL(f, ctl, write, filp, buffer, lenp, ppos) \ + f(ctl_table *ctl, int write, struct file *filp, void *buffer,\ + size_t *lenp, loff_t *ppos) +#define ATH_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer, lenp, ppos) \ + proc_dointvec(ctl, write, filp, buffer, lenp, ppos) +#endif + +#define ATH_TIMEOUT 1000 + +/* + * Maximum acceptable MTU + * MAXFRAMEBODY - WEP - QOS - RSN/WPA: + * 2312 - 8 - 2 - 12 = 2290 + */ +#define ATH_MAX_MTU 2290 +#define ATH_MIN_MTU 32 + +#define ATH_RXBUF 40 /* number of RX buffers */ +#define ATH_TXBUF 200 /* number of TX buffers */ +#define ATH_TXDESC 1 /* number of descriptors per buffer */ +#define ATH_TXMAXTRY 11 /* max number of transmit attempts */ +#define ATH_TXINTR_PERIOD 5 /* max number of batched tx descriptors */ + +/* driver-specific node state */ +struct ath_node { + struct ieee80211_node an_node; /* base class */ + u_int8_t an_tx_mgtrate; /* h/w rate for management/ctl frames */ + u_int8_t an_tx_mgtratesp;/* short preamble h/w rate for " " */ + u_int32_t an_avgrssi; /* average rssi over all rx frames */ + HAL_NODE_STATS an_halstats; /* rssi statistics used by hal */ + /* variable-length rate control state follows */ +}; +#define ATH_NODE(_n) ((struct ath_node *)(_n)) +#define ATH_NODE_CONST(_n) ((const struct ath_node *)(_n)) + +#define ATH_RSSI_LPF_LEN 10 +#define ATH_RSSI_DUMMY_MARKER 0x127 +#define ATH_EP_MUL(x, mul) ((x) * (mul)) +#define ATH_RSSI_IN(x) (ATH_EP_MUL((x), HAL_RSSI_EP_MULTIPLIER)) +#define ATH_LPF_RSSI(x, y, len) \ + ((x != ATH_RSSI_DUMMY_MARKER) ? (((x) * ((len) - 1) + (y)) / (len)) : (y)) +#define ATH_RSSI_LPF(x, y) do { \ + if ((y) >= -20) \ + x = ATH_LPF_RSSI((x), ATH_RSSI_IN((y)), ATH_RSSI_LPF_LEN); \ +} while (0) + +struct ath_buf { + STAILQ_ENTRY(ath_buf) bf_list; + struct ath_desc *bf_desc; /* virtual addr of desc */ + dma_addr_t bf_daddr; /* physical addr of desc */ + struct sk_buff *bf_skb; /* skbuff for buf */ + dma_addr_t bf_skbaddr; /* physical addr of skb data */ + struct ieee80211_node *bf_node; /* pointer to the node */ +}; + +struct ath_hal; +struct ath_desc; +struct proc_dir_entry; + +/* + * Data transmit queue state. One of these exists for each + * hardware transmit queue. Packets sent to us from above + * are assigned to queues based on their priority. Not all + * devices support a complete set of hardware transmit queues. + * For those devices the array sc_ac2q will map multiple + * priorities to fewer hardware queues (typically all to one + * hardware queue). + */ +struct ath_txq { + u_int axq_qnum; /* hardware q number */ + u_int axq_depth; /* queue depth (stat only) */ + u_int axq_intrcnt; /* interrupt count */ + u_int32_t *axq_link; /* link ptr in last TX desc */ + STAILQ_HEAD(, ath_buf) axq_q; /* transmit queue */ + spinlock_t axq_lock; /* lock on q and link */ +}; + +#define ATH_TXQ_LOCK_INIT(_tq) spin_lock_init(&(_tq)->axq_lock) +#define ATH_TXQ_LOCK_DESTROY(_tq) +#define ATH_TXQ_LOCK(_tq) spin_lock(&(_tq)->axq_lock) +#define ATH_TXQ_UNLOCK(_tq) spin_unlock(&(_tq)->axq_lock) +#define ATH_TXQ_LOCK_BH(_tq) spin_lock_bh(&(_tq)->axq_lock) +#define ATH_TXQ_UNLOCK_BH(_tq) spin_unlock_bh(&(_tq)->axq_lock) +#define ATH_TXQ_LOCK_ASSERT(_tq) \ + KASSERT(spin_is_locked(&(_tq)->axq_lock), ("txq not locked!")) + +#define ATH_TXQ_INSERT_TAIL(_tq, _elm, _field) do { \ + STAILQ_INSERT_TAIL(&(_tq)->axq_q, (_elm), _field); \ + (_tq)->axq_depth++; \ +} while (0) +#define ATH_TXQ_REMOVE_HEAD(_tq, _field) do { \ + STAILQ_REMOVE_HEAD(&(_tq)->axq_q, _field); \ + (_tq)->axq_depth--; \ +} while (0) + +struct ath_softc { + struct net_device sc_dev; /* NB: must be first */ + struct semaphore sc_lock; /* dev-level lock */ + struct net_device_stats sc_devstats; /* device statistics */ + struct ath_stats sc_stats; /* private statistics */ + struct ieee80211com sc_ic; /* IEEE 802.11 common */ + int sc_debug; + void (*sc_recv_mgmt)(struct ieee80211com *, + struct sk_buff *, + struct ieee80211_node *, + int, int, u_int32_t); + int (*sc_newstate)(struct ieee80211com *, + enum ieee80211_state, int); + void (*sc_node_cleanup)(struct ieee80211com *, + struct ieee80211_node *); + void (*sc_node_copy)(struct ieee80211com *, + struct ieee80211_node *, + const struct ieee80211_node *); + struct ath_hal *sc_ah; /* Atheros HAL */ + struct ath_ratectrl *sc_rc; /* tx rate control support */ + unsigned int sc_invalid : 1, /* being detached */ + sc_mrretry : 1, /* multi-rate retry support */ + sc_softled : 1, /* enable LED gpio status */ + sc_splitmic: 1, /* split TKIP MIC keys */ + sc_needmib : 1, /* enable MIB stats intr */ + sc_hasdiversity : 1,/* rx diversity available */ + sc_diversity : 1;/* enable rx diversity */ + /* rate tables */ + const HAL_RATE_TABLE *sc_rates[IEEE80211_MODE_MAX]; + const HAL_RATE_TABLE *sc_currates; /* current rate table */ + enum ieee80211_phymode sc_curmode; /* current phy mode */ + HAL_CHANNEL sc_curchan; /* current h/w channel */ + u_int8_t sc_rixmap[256]; /* IEEE to h/w rate table ix */ + u_int8_t sc_hwmap[32]; /* h/w rate ix to IEEE table */ + u_int8_t sc_protrix; /* protection rate index */ + u_int8_t sc_txantenna; /* tx antenna (fixed or auto) */ + HAL_INT sc_imask; /* interrupt mask copy */ + u_int sc_keymax; /* size of key cache */ + u_int8_t sc_keymap[16]; /* bit map of key cache use */ + + u_int32_t sc_beacons; + u_int16_t sc_ledstate; + u_int16_t sc_ledpin; /* GPIO pin for LED */ + + void *sc_bdev; /* associated bus device */ + struct ath_desc *sc_desc; /* TX/RX descriptors */ + size_t sc_desc_len; /* size of TX/RX descriptors */ + u_int16_t sc_cachelsz; /* cache line size */ + dma_addr_t sc_desc_daddr; /* DMA (physical) address */ + + struct tq_struct sc_fataltq; /* fatal error intr tasklet */ + + int sc_rxbufsize; /* rx size based on mtu */ + STAILQ_HEAD(, ath_buf) sc_rxbuf; /* receive buffer */ + u_int32_t *sc_rxlink; /* link ptr in last RX desc */ + struct tq_struct sc_rxtq; /* rx intr tasklet */ + struct tq_struct sc_rxorntq; /* rxorn intr tasklet */ + u_int8_t sc_defant; /* current default antenna */ + u_int8_t sc_rxotherant; /* rx's on non-default antenna*/ + + STAILQ_HEAD(, ath_buf) sc_txbuf; /* tx buffer queue */ + spinlock_t sc_txbuflock; /* txbuf lock */ + u_int sc_txqsetup; /* h/w queues setup */ + u_int sc_txintrperiod;/* tx interrupt batching */ + struct ath_txq sc_txq[HAL_NUM_TX_QUEUES]; + struct ath_txq *sc_ac2q[5]; /* WME AC -> h/w qnum */ + struct tq_struct sc_txtq; /* tx intr tasklet */ + + u_int sc_bhalq; /* HAL q for outgoing beacons */ + struct ath_buf *sc_bcbuf; /* beacon buffer */ + struct ath_buf *sc_bufptr; /* allocated buffer ptr */ + struct ieee80211_beacon_offsets sc_boff;/* dynamic update state */ + struct tq_struct sc_bmisstq; /* bmiss intr tasklet */ + enum { + OK, /* no change needed */ + UPDATE, /* update pending */ + COMMIT /* beacon sent, commit change */ + } sc_updateslot; /* slot time update fsm */ + + struct timer_list sc_cal_ch; /* calibration timer */ + struct timer_list sc_scan_ch; /* AP scan timer */ +#ifdef CONFIG_NET_WIRELESS + struct iw_statistics sc_iwstats; /* wireless statistics block */ +#endif +#ifdef CONFIG_SYSCTL + struct ctl_table_header *sc_sysctl_header; + struct ctl_table *sc_sysctls; +#endif +}; + +#define ATH_TXQ_SETUP(sc, i) ((sc)->sc_txqsetup & (1<sc_txbuflock) +#define ATH_TXBUF_LOCK_DESTROY(_sc) +#define ATH_TXBUF_LOCK(_sc) spin_lock(&(_sc)->sc_txbuflock) +#define ATH_TXBUF_UNLOCK(_sc) spin_unlock(&(_sc)->sc_txbuflock) +#define ATH_TXBUF_LOCK_BH(_sc) spin_lock_bh(&(_sc)->sc_txbuflock) +#define ATH_TXBUF_UNLOCK_BH(_sc) spin_unlock_bh(&(_sc)->sc_txbuflock) +#define ATH_TXBUF_LOCK_ASSERT(_sc) \ + KASSERT(spin_is_locked(&(_sc)->sc_txbuflock), ("txbuf not locked!")) + +#define ATH_LOCK_INIT(_sc) init_MUTEX(&(_sc)->sc_lock) +#define ATH_LOCK_DESTROY(_sc) +#define ATH_LOCK(_sc) down(&(_sc)->sc_lock) +#define ATH_UNLOCK(_sc) up(&(_sc)->sc_lock) + +int ath_attach(u_int16_t, struct net_device *); +int ath_detach(struct net_device *); +void ath_resume(struct net_device *); +void ath_suspend(struct net_device *); +void ath_shutdown(struct net_device *); +irqreturn_t ath_intr(int irq, void *dev_id, struct pt_regs *regs); +#ifdef CONFIG_SYSCTL +void ath_sysctl_register(void); +void ath_sysctl_unregister(void); +#endif /* CONFIG_SYSCTL */ + +/* + * HAL definitions to comply with local coding convention. + */ +#define ath_hal_reset(_ah, _opmode, _chan, _outdoor, _pstatus) \ + ((*(_ah)->ah_reset)((_ah), (_opmode), (_chan), (_outdoor), (_pstatus))) +#define ath_hal_getratetable(_ah, _mode) \ + ((*(_ah)->ah_getRateTable)((_ah), (_mode))) +#define ath_hal_getmac(_ah, _mac) \ + ((*(_ah)->ah_getMacAddress)((_ah), (_mac))) +#define ath_hal_setmac(_ah, _mac) \ + ((*(_ah)->ah_setMacAddress)((_ah), (_mac))) +#define ath_hal_intrset(_ah, _mask) \ + ((*(_ah)->ah_setInterrupts)((_ah), (_mask))) +#define ath_hal_intrget(_ah) \ + ((*(_ah)->ah_getInterrupts)((_ah))) +#define ath_hal_intrpend(_ah) \ + ((*(_ah)->ah_isInterruptPending)((_ah))) +#define ath_hal_getisr(_ah, _pmask) \ + ((*(_ah)->ah_getPendingInterrupts)((_ah), (_pmask))) +#define ath_hal_updatetxtriglevel(_ah, _inc) \ + ((*(_ah)->ah_updateTxTrigLevel)((_ah), (_inc))) +#define ath_hal_setpower(_ah, _mode, _sleepduration) \ + ((*(_ah)->ah_setPowerMode)((_ah), (_mode), AH_TRUE, (_sleepduration))) +#define ath_hal_keycachesize(_ah) \ + ((*(_ah)->ah_getKeyCacheSize)((_ah))) +#define ath_hal_keyreset(_ah, _ix) \ + ((*(_ah)->ah_resetKeyCacheEntry)((_ah), (_ix))) +#define ath_hal_keyset(_ah, _ix, _pk, _mac) \ + ((*(_ah)->ah_setKeyCacheEntry)((_ah), (_ix), (_pk), (_mac), AH_FALSE)) +#define ath_hal_keyisvalid(_ah, _ix) \ + (((*(_ah)->ah_isKeyCacheEntryValid)((_ah), (_ix)))) +#define ath_hal_keysetmac(_ah, _ix, _mac) \ + ((*(_ah)->ah_setKeyCacheEntryMac)((_ah), (_ix), (_mac))) +#define ath_hal_getrxfilter(_ah) \ + ((*(_ah)->ah_getRxFilter)((_ah))) +#define ath_hal_setrxfilter(_ah, _filter) \ + ((*(_ah)->ah_setRxFilter)((_ah), (_filter))) +#define ath_hal_setmcastfilter(_ah, _mfilt0, _mfilt1) \ + ((*(_ah)->ah_setMulticastFilter)((_ah), (_mfilt0), (_mfilt1))) +#define ath_hal_waitforbeacon(_ah, _bf) \ + ((*(_ah)->ah_waitForBeaconDone)((_ah), (_bf)->bf_daddr)) +#define ath_hal_putrxbuf(_ah, _bufaddr) \ + ((*(_ah)->ah_setRxDP)((_ah), (_bufaddr))) +#define ath_hal_gettsf32(_ah) \ + ((*(_ah)->ah_getTsf32)((_ah))) +#define ath_hal_gettsf64(_ah) \ + ((*(_ah)->ah_getTsf64)((_ah))) +#define ath_hal_resettsf(_ah) \ + ((*(_ah)->ah_resetTsf)((_ah))) +#define ath_hal_rxena(_ah) \ + ((*(_ah)->ah_enableReceive)((_ah))) +#define ath_hal_puttxbuf(_ah, _q, _bufaddr) \ + ((*(_ah)->ah_setTxDP)((_ah), (_q), (_bufaddr))) +#define ath_hal_gettxbuf(_ah, _q) \ + ((*(_ah)->ah_getTxDP)((_ah), (_q))) +#define ath_hal_getrxbuf(_ah) \ + ((*(_ah)->ah_getRxDP)((_ah))) +#define ath_hal_txstart(_ah, _q) \ + ((*(_ah)->ah_startTxDma)((_ah), (_q))) +#define ath_hal_setchannel(_ah, _chan) \ + ((*(_ah)->ah_setChannel)((_ah), (_chan))) +#define ath_hal_calibrate(_ah, _chan) \ + ((*(_ah)->ah_perCalibration)((_ah), (_chan))) +#define ath_hal_setledstate(_ah, _state) \ + ((*(_ah)->ah_setLedState)((_ah), (_state))) +#define ath_hal_beaconinit(_ah, _nextb, _bperiod) \ + ((*(_ah)->ah_beaconInit)((_ah), (_nextb), (_bperiod))) +#define ath_hal_beaconreset(_ah) \ + ((*(_ah)->ah_resetStationBeaconTimers)((_ah))) +#define ath_hal_beacontimers(_ah, _bs) \ + ((*(_ah)->ah_setStationBeaconTimers)((_ah), (_bs))) +#define ath_hal_setassocid(_ah, _bss, _associd) \ + ((*(_ah)->ah_writeAssocid)((_ah), (_bss), (_associd))) +#define ath_hal_phydisable(_ah) \ + ((*(_ah)->ah_phyDisable)((_ah))) +#define ath_hal_setopmode(_ah) \ + ((*(_ah)->ah_setPCUConfig)((_ah))) +#define ath_hal_stoptxdma(_ah, _qnum) \ + ((*(_ah)->ah_stopTxDma)((_ah), (_qnum))) +#define ath_hal_stoppcurecv(_ah) \ + ((*(_ah)->ah_stopPcuReceive)((_ah))) +#define ath_hal_startpcurecv(_ah) \ + ((*(_ah)->ah_startPcuReceive)((_ah))) +#define ath_hal_stopdmarecv(_ah) \ + ((*(_ah)->ah_stopDmaReceive)((_ah))) +#define ath_hal_getdiagstate(_ah, _id, _indata, _insize, _outdata, _outsize) \ + ((*(_ah)->ah_getDiagState)((_ah), (_id), \ + (_indata), (_insize), (_outdata), (_outsize))) +#define ath_hal_setuptxqueue(_ah, _type, _irq) \ + ((*(_ah)->ah_setupTxQueue)((_ah), (_type), (_irq))) +#define ath_hal_resettxqueue(_ah, _q) \ + ((*(_ah)->ah_resetTxQueue)((_ah), (_q))) +#define ath_hal_releasetxqueue(_ah, _q) \ + ((*(_ah)->ah_releaseTxQueue)((_ah), (_q))) +#define ath_hal_hasveol(_ah) \ + ((*(_ah)->ah_hasVEOL)((_ah))) +#define ath_hal_getrfgain(_ah) \ + ((*(_ah)->ah_getRfGain)((_ah))) +#define ath_hal_getdefantenna(_ah) \ + ((*(_ah)->ah_getDefAntenna)((_ah))) +#define ath_hal_setdefantenna(_ah, _ant) \ + ((*(_ah)->ah_setDefAntenna)((_ah), (_ant))) +#define ath_hal_rxmonitor(_ah, _arg) \ + ((*(_ah)->ah_rxMonitor)((_ah), (_arg))) +#define ath_hal_mibevent(_ah, _stats) \ + ((*(_ah)->ah_procMibEvent)((_ah), (_stats))) +#define ath_hal_setslottime(_ah, _us) \ + ((*(_ah)->ah_setSlotTime)((_ah), (_us))) +#define ath_hal_getslottime(_ah) \ + ((*(_ah)->ah_getSlotTime)((_ah))) +#define ath_hal_setacktimeout(_ah, _us) \ + ((*(_ah)->ah_setAckTimeout)((_ah), (_us))) +#define ath_hal_getacktimeout(_ah) \ + ((*(_ah)->ah_getAckTimeout)((_ah))) +#define ath_hal_setctstimeout(_ah, _us) \ + ((*(_ah)->ah_setCTSTimeout)((_ah), (_us))) +#define ath_hal_getctstimeout(_ah) \ + ((*(_ah)->ah_getCTSTimeout)((_ah))) +#define ath_hal_getcapability(_ah, _cap, _param, _result) \ + ((*(_ah)->ah_getCapability)((_ah), (_cap), (_param), (_result))) +#define ath_hal_setcapability(_ah, _cap, _param, _v, _status) \ + ((*(_ah)->ah_setCapability)((_ah), (_cap), (_param), (_v), (_status))) +#define ath_hal_ciphersupported(_ah, _cipher) \ + (ath_hal_getcapability(_ah, HAL_CAP_CIPHER, _cipher, NULL) == HAL_OK) +#define ath_hal_getregdomain(_ah, _prd) \ + ath_hal_getcapability(_ah, HAL_CAP_REG_DMN, 0, (_prd)) +#define ath_hal_getcountrycode(_ah, _pcc) \ + (*(_pcc) = (_ah)->ah_countryCode) +#define ath_hal_tkipsplit(_ah) \ + (ath_hal_getcapability(_ah, HAL_CAP_TKIP_SPLIT, 0, NULL) == HAL_OK) +#define ath_hal_hwphycounters(_ah) \ + (ath_hal_getcapability(_ah, HAL_CAP_PHYCOUNTERS, 0, NULL) == HAL_OK) +#define ath_hal_hasdiversity(_ah) \ + (ath_hal_getcapability(_ah, HAL_CAP_DIVERSITY, 0, NULL) == HAL_OK) +#define ath_hal_getdiversity(_ah) \ + (ath_hal_getcapability(_ah, HAL_CAP_DIVERSITY, 1, NULL) == HAL_OK) +#define ath_hal_setdiversity(_ah, _v) \ + ath_hal_setcapability(_ah, HAL_CAP_DIVERSITY, 1, _v, NULL) + +#define ath_hal_setuprxdesc(_ah, _ds, _size, _intreq) \ + ((*(_ah)->ah_setupRxDesc)((_ah), (_ds), (_size), (_intreq))) +#define ath_hal_rxprocdesc(_ah, _ds, _dspa, _dsnext) \ + ((*(_ah)->ah_procRxDesc)((_ah), (_ds), (_dspa), (_dsnext))) +#define ath_hal_setuptxdesc(_ah, _ds, _plen, _hlen, _atype, _txpow, \ + _txr0, _txtr0, _keyix, _ant, _flags, \ + _rtsrate, _rtsdura) \ + ((*(_ah)->ah_setupTxDesc)((_ah), (_ds), (_plen), (_hlen), (_atype), \ + (_txpow), (_txr0), (_txtr0), (_keyix), (_ant), \ + (_flags), (_rtsrate), (_rtsdura))) +#define ath_hal_setupxtxdesc(_ah, _ds, \ + _txr1, _txtr1, _txr2, _txtr2, _txr3, _txtr3) \ + ((*(_ah)->ah_setupXTxDesc)((_ah), (_ds), \ + (_txr1), (_txtr1), (_txr2), (_txtr2), (_txr3), (_txtr3))) +#define ath_hal_filltxdesc(_ah, _ds, _l, _first, _last, _ds0) \ + ((*(_ah)->ah_fillTxDesc)((_ah), (_ds), (_l), (_first), (_last), (_ds0))) +#define ath_hal_txprocdesc(_ah, _ds) \ + ((*(_ah)->ah_procTxDesc)((_ah), (_ds))) + +#define ath_hal_gpioCfgOutput(_ah, _gpio) \ + ((*(_ah)->ah_gpioCfgOutput)((_ah), (_gpio))) +#define ath_hal_gpioset(_ah, _gpio, _b) \ + ((*(_ah)->ah_gpioSet)((_ah), (_gpio), (_b))) + +#endif /* _DEV_ATH_ATHVAR_H */ diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/ath/version.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/ath/version.h --- linux-2.6.11-rc4-mm1/drivers/net/wireless/ath/version.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/ath/version.h 2005-02-24 13:06:17.186149328 -0800 @@ -0,0 +1,38 @@ +/*- + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + * + * $Id: version.h,v 1.18 2005/02/16 16:08:42 samleffler Exp $ + */ +#define ATH_PCI_VERSION "0.9.4.12" diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/hal/ah.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/hal/ah.h --- linux-2.6.11-rc4-mm1/drivers/net/wireless/hal/ah.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/hal/ah.h 2005-02-24 13:06:17.244140512 -0800 @@ -0,0 +1,691 @@ +/*- + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting, Atheros + * Communications, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the following conditions are met: + * 1. The materials contained herein are unmodified and are used + * unmodified. + * 2. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following NO + * ''WARRANTY'' disclaimer below (''Disclaimer''), without + * modification. + * 3. Redistributions in binary form must reproduce at minimum a + * disclaimer similar to the Disclaimer below and any redistribution + * must be conditioned upon including a substantially similar + * Disclaimer requirement for further binary redistribution. + * 4. Neither the names of the above-listed copyright holders nor the + * names of any contributors may be used to endorse or promote + * product derived from this software without specific prior written + * permission. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE + * FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGES. + * + * $Id: ah.h,v 1.8 2005/02/22 14:17:22 otaku Exp $ + */ + +#ifndef _ATH_AH_H_ +#define _ATH_AH_H_ +/* + * Atheros Hardware Access Layer + * + * Clients of the HAL call ath_hal_attach to obtain a reference to an ath_hal + * structure for use with the device. Hardware-related operations that + * follow must call back into the HAL through interface, supplying the + * reference as the first parameter. + */ +#include "ah_osdep.h" + +/* + * __ahdecl is analogous to _cdecl; it defines the calling + * convention used within the HAL. For most systems this + * can just default to be empty and the compiler will (should) + * use _cdecl. For systems where _cdecl is not compatible this + * must be defined. See linux/ah_osdep.h for an example. + */ +#ifndef __ahdecl +#define __ahdecl +#endif + +/* + * Status codes that may be returned by the HAL. Note that + * interfaces that return a status code set it only when an + * error occurs--i.e. you cannot check it for success. + */ +typedef enum { + HAL_OK = 0, /* No error */ + HAL_ENXIO = 1, /* No hardware present */ + HAL_ENOMEM = 2, /* Memory allocation failed */ + HAL_EIO = 3, /* Hardware didn't respond as expected */ + HAL_EEMAGIC = 4, /* EEPROM magic number invalid */ + HAL_EEVERSION = 5, /* EEPROM version invalid */ + HAL_EELOCKED = 6, /* EEPROM unreadable */ + HAL_EEBADSUM = 7, /* EEPROM checksum invalid */ + HAL_EEREAD = 8, /* EEPROM read problem */ + HAL_EEBADMAC = 9, /* EEPROM mac address invalid */ + HAL_EESIZE = 10, /* EEPROM size not supported */ + HAL_EEWRITE = 11, /* Attempt to change write-locked EEPROM */ + HAL_EINVAL = 12, /* Invalid parameter to function */ + HAL_ENOTSUPP = 13, /* Hardware revision not supported */ + HAL_ESELFTEST = 14, /* Hardware self-test failed */ + HAL_EINPROGRESS = 15, /* Operation incomplete */ +} HAL_STATUS; + +typedef enum { + AH_FALSE = 0, /* NB: lots of code assumes false is zero */ + AH_TRUE = 1, +} HAL_BOOL; + +typedef enum { + HAL_CAP_REG_DMN = 0, /* current regulatory domain */ + HAL_CAP_CIPHER = 1, /* hardware supports cipher */ + HAL_CAP_TKIP_MIC = 2, /* handle TKIP MIC in hardware */ + HAL_CAP_TKIP_SPLIT = 3, /* hardware TKIP uses split keys */ + HAL_CAP_PHYCOUNTERS = 4, /* hardware PHY error counters */ + HAL_CAP_DIVERSITY = 5, /* hardware supports fast diversity */ + HAL_CAP_KEYCACHE_SIZE = 6, /* number of entries in key cache */ + HAL_CAP_NUM_TXQUEUES = 7, /* number of hardware xmit queues */ + HAL_CAP_VEOL = 9, /* hardware supports virtual EOL */ + HAL_CAP_PSPOLL = 10, /* hardware has working PS-Poll support */ + HAL_CAP_DIAG = 11, /* hardware diagnostic support */ + HAL_CAP_COMPRESSION = 12, /* hardware supports compression */ + HAL_CAP_BURST = 13, /* hardware supports packet bursting */ + HAL_CAP_FASTFRAME = 14, /* hardware supoprts fast frames */ + HAL_CAP_TXPOW = 15, /* global tx power limit */ + HAL_CAP_TPC = 16, /* per-packet tx power control */ +} HAL_CAPABILITY_TYPE; + +/* + * "States" for setting the LED. These correspond to + * the possible 802.11 operational states and there may + * be a many-to-one mapping between these states and the + * actual hardware states for the LED's (i.e. the hardware + * may have fewer states). + */ +typedef enum { + HAL_LED_INIT = 0, + HAL_LED_SCAN = 1, + HAL_LED_AUTH = 2, + HAL_LED_ASSOC = 3, + HAL_LED_RUN = 4 +} HAL_LED_STATE; + +/* + * Transmit queue types/numbers. These are used to tag + * each transmit queue in the hardware and to identify a set + * of transmit queues for operations such as start/stop dma. + */ +typedef enum { + HAL_TX_QUEUE_INACTIVE = 0, /* queue is inactive/unused */ + HAL_TX_QUEUE_DATA = 1, /* data xmit q's */ + HAL_TX_QUEUE_BEACON = 2, /* beacon xmit q */ + HAL_TX_QUEUE_CAB = 3, /* "crap after beacon" xmit q */ + HAL_TX_QUEUE_PSPOLL = 4, /* power-save poll xmit q */ +} HAL_TX_QUEUE; + +#define HAL_NUM_TX_QUEUES 10 /* max possible # of queues */ + +/* + * Transmit queue subtype. These map directly to + * WME Access Categories (except for UPSD). Refer + * to Table 5 of the WME spec. + */ +typedef enum { + HAL_WME_AC_BK = 0, /* background access category */ + HAL_WME_AC_BE = 1, /* best effort access category*/ + HAL_WME_AC_VI = 2, /* video access category */ + HAL_WME_AC_VO = 3, /* voice access category */ + HAL_WME_UPSD = 4, /* uplink power save */ +} HAL_TX_QUEUE_SUBTYPE; + +/* + * Transmit queue flags that control various + * operational parameters. + */ +typedef enum { + TXQ_FLAG_TXOKINT_ENABLE = 0x0001, /* enable TXOK interrupt */ + TXQ_FLAG_TXERRINT_ENABLE = 0x0001, /* enable TXERR interrupt */ + TXQ_FLAG_TXDESCINT_ENABLE = 0x0002, /* enable TXDESC interrupt */ + TXQ_FLAG_TXEOLINT_ENABLE = 0x0004, /* enable TXEOL interrupt */ + TXQ_FLAG_TXURNINT_ENABLE = 0x0008, /* enable TXURN interrupt */ + TXQ_FLAG_BACKOFF_DISABLE = 0x0010, /* disable Post Backoff */ + TXQ_FLAG_COMPRESSION_ENABLE = 0x0020, /* compression enabled */ + TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE = 0x0040, /* enable ready time + expiry policy */ + TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE = 0x0080, /* enable backoff while + sending fragment burst*/ +} HAL_TX_QUEUE_FLAGS; + +typedef struct { + u_int32_t tqi_ver; /* hal TXQ version */ + HAL_TX_QUEUE_SUBTYPE tqi_subtype; /* subtype if applicable */ + HAL_TX_QUEUE_FLAGS tqi_qflags; /* flags (see above) */ + u_int32_t tqi_priority; /* (not used) */ + u_int32_t tqi_aifs; /* aifs */ + u_int32_t tqi_cwmin; /* cwMin */ + u_int32_t tqi_cwmax; /* cwMax */ + u_int16_t tqi_shretry; /* rts retry limit */ + u_int16_t tqi_lgretry; /* long retry limit (not used)*/ + u_int32_t tqi_cbrPeriod; + u_int32_t tqi_cbrOverflowLimit; + u_int32_t tqi_burstTime; + u_int32_t tqi_readyTime; +} HAL_TXQ_INFO; + +/* token to use for aifs, cwmin, cwmax */ +#define HAL_TXQ_USEDEFAULT ((u_int32_t) -1) + +/* + * Transmit packet types. This belongs in ah_desc.h, but + * is here so we can give a proper type to various parameters + * (and not require everyone include the file). + * + * NB: These values are intentionally assigned for + * direct use when setting up h/w descriptors. + */ +typedef enum { + HAL_PKT_TYPE_NORMAL = 0, + HAL_PKT_TYPE_ATIM = 1, + HAL_PKT_TYPE_PSPOLL = 2, + HAL_PKT_TYPE_BEACON = 3, + HAL_PKT_TYPE_PROBE_RESP = 4, +} HAL_PKT_TYPE; + +/* Rx Filter Frame Types */ +typedef enum { + HAL_RX_FILTER_UCAST = 0x00000001, /* Allow unicast frames */ + HAL_RX_FILTER_MCAST = 0x00000002, /* Allow multicast frames */ + HAL_RX_FILTER_BCAST = 0x00000004, /* Allow broadcast frames */ + HAL_RX_FILTER_CONTROL = 0x00000008, /* Allow control frames */ + HAL_RX_FILTER_BEACON = 0x00000010, /* Allow beacon frames */ + HAL_RX_FILTER_PROM = 0x00000020, /* Promiscuous mode */ + HAL_RX_FILTER_PROBEREQ = 0x00000080, /* Allow probe request frames */ + HAL_RX_FILTER_PHYERR = 0x00000100, /* Allow phy errors */ + HAL_RX_FILTER_PHYRADAR = 0x00000200, /* Allow phy radar errors*/ +} HAL_RX_FILTER; + +typedef enum { + HAL_PM_UNDEFINED = 0, + HAL_PM_AUTO = 1, + HAL_PM_AWAKE = 2, + HAL_PM_FULL_SLEEP = 3, + HAL_PM_NETWORK_SLEEP = 4 +} HAL_POWER_MODE; + +/* + * NOTE WELL: + * These are mapped to take advantage of the common locations for many of + * the bits on all of the currently supported MAC chips. This is to make + * the ISR as efficient as possible, while still abstracting HW differences. + * When new hardware breaks this commonality this enumerated type, as well + * as the HAL functions using it, must be modified. All values are directly + * mapped unless commented otherwise. + */ +typedef enum { + HAL_INT_RX = 0x00000001, /* Non-common mapping */ + HAL_INT_RXDESC = 0x00000002, + HAL_INT_RXNOFRM = 0x00000008, + HAL_INT_RXEOL = 0x00000010, + HAL_INT_RXORN = 0x00000020, + HAL_INT_TX = 0x00000040, /* Non-common mapping */ + HAL_INT_TXDESC = 0x00000080, + HAL_INT_TXURN = 0x00000800, + HAL_INT_MIB = 0x00001000, + HAL_INT_RXPHY = 0x00004000, + HAL_INT_RXKCM = 0x00008000, + HAL_INT_SWBA = 0x00010000, + HAL_INT_BMISS = 0x00040000, + HAL_INT_BNR = 0x00100000, /* Non-common mapping */ + HAL_INT_GPIO = 0x01000000, + HAL_INT_FATAL = 0x40000000, /* Non-common mapping */ + HAL_INT_GLOBAL = 0x80000000, /* Set/clear IER */ + + /* Interrupt bits that map directly to ISR/IMR bits */ + HAL_INT_COMMON = HAL_INT_RXNOFRM + | HAL_INT_RXDESC + | HAL_INT_RXEOL + | HAL_INT_RXORN + | HAL_INT_TXURN + | HAL_INT_TXDESC + | HAL_INT_MIB + | HAL_INT_RXPHY + | HAL_INT_RXKCM + | HAL_INT_SWBA + | HAL_INT_BMISS + | HAL_INT_GPIO, + HAL_INT_NOCARD = 0xffffffff /* To signal the card was removed */ +} HAL_INT; + +typedef enum { + HAL_RFGAIN_INACTIVE = 0, + HAL_RFGAIN_READ_REQUESTED = 1, + HAL_RFGAIN_NEED_CHANGE = 2 +} HAL_RFGAIN; + +/* + * Channels are specified by frequency. + */ +typedef struct { + u_int16_t channel; /* setting in Mhz */ + u_int16_t channelFlags; /* see below */ +} HAL_CHANNEL; + +#define CHANNEL_RAD_INT 0x0001 /* Radar interference detected on channel */ +#define CHANNEL_CW_INT 0x0002 /* CW interference detected on channel */ +#define CHANNEL_BUSY 0x0004 /* Busy, occupied or overlap with adjoin chan */ +#define CHANNEL_TURBO 0x0010 /* Turbo Channel */ +#define CHANNEL_CCK 0x0020 /* CCK channel */ +#define CHANNEL_OFDM 0x0040 /* OFDM channel */ +#define CHANNEL_2GHZ 0x0080 /* 2 GHz spectrum channel. */ +#define CHANNEL_5GHZ 0x0100 /* 5 GHz spectrum channel */ +#define CHANNEL_PASSIVE 0x0200 /* Only passive scan allowed in the channel */ +#define CHANNEL_DYN 0x0400 /* dynamic CCK-OFDM channel */ +#define CHANNEL_XR 0x0800 /* XR channel */ +#define CHANNEL_AR 0x8000 /* Software use: radar detected */ + + +#define CHANNEL_A (CHANNEL_5GHZ|CHANNEL_OFDM) +#define CHANNEL_B (CHANNEL_2GHZ|CHANNEL_CCK) +#define CHANNEL_PUREG (CHANNEL_2GHZ|CHANNEL_OFDM) +#ifdef notdef +#define CHANNEL_G (CHANNEL_2GHZ|CHANNEL_DYN) +#else +#define CHANNEL_G (CHANNEL_2GHZ|CHANNEL_OFDM) +#endif +#define CHANNEL_T (CHANNEL_5GHZ|CHANNEL_OFDM|CHANNEL_TURBO) +#define CHANNEL_108G (CHANNEL_2GHZ|CHANNEL_OFDM|CHANNEL_TURBO) +#define CHANNEL_X (CHANNEL_5GHZ|CHANNEL_OFDM|CHANNEL_XR) +#define CHANNEL_ALL \ + (CHANNEL_OFDM|CHANNEL_CCK|CHANNEL_5GHZ|CHANNEL_2GHZ|CHANNEL_TURBO) +#define CHANNEL_ALL_NOTURBO (CHANNEL_ALL &~ CHANNEL_TURBO) + +typedef struct { + u_int32_t ackrcv_bad; + u_int32_t rts_bad; + u_int32_t rts_good; + u_int32_t fcs_bad; + u_int32_t beacons; +} HAL_MIB_STATS; + +typedef u_int16_t HAL_CTRY_CODE; /* country code */ +typedef u_int16_t HAL_REG_DOMAIN; /* regulatory domain code */ + +enum { + CTRY_DEBUG = 0x1ff, /* debug country code */ + CTRY_DEFAULT = 0 /* default country code */ +}; + +enum { + HAL_MODE_11A = 0x001, + HAL_MODE_TURBO = 0x002, + HAL_MODE_11B = 0x004, + HAL_MODE_PUREG = 0x008, +#ifdef notdef + HAL_MODE_11G = 0x010, +#else + HAL_MODE_11G = 0x008, +#endif + HAL_MODE_108G = 0x020, + HAL_MODE_ALL = 0xfff +}; + +typedef struct { + int rateCount; /* NB: for proper padding */ + u_int8_t rateCodeToIndex[32]; /* back mapping */ + struct { + u_int8_t valid; /* valid for rate control use */ + u_int8_t phy; /* CCK/OFDM/XR */ + u_int16_t rateKbps; /* transfer rate in kbs */ + u_int8_t rateCode; /* rate for h/w descriptors */ + u_int8_t shortPreamble; /* mask for enabling short + * preamble in CCK rate code */ + u_int8_t dot11Rate; /* value for supported rates + * info element of MLME */ + u_int8_t controlRate; /* index of next lower basic + * rate; used for dur. calcs */ + u_int16_t lpAckDuration; /* long preamble ACK duration */ + u_int16_t spAckDuration; /* short preamble ACK duration*/ + } info[32]; +} HAL_RATE_TABLE; + +typedef struct { + u_int rs_count; /* number of valid entries */ + u_int8_t rs_rates[32]; /* rates */ +} HAL_RATE_SET; + +typedef enum { + HAL_ANT_VARIABLE = 0, /* variable by programming */ + HAL_ANT_FIXED_A = 1, /* fixed to 11a frequencies */ + HAL_ANT_FIXED_B = 2, /* fixed to 11b frequencies */ +} HAL_ANT_SETTING; + +typedef enum { + HAL_M_STA = 1, /* infrastructure station */ + HAL_M_IBSS = 0, /* IBSS (adhoc) station */ + HAL_M_HOSTAP = 6, /* Software Access Point */ + HAL_M_MONITOR = 8 /* Monitor mode */ +} HAL_OPMODE; + +typedef struct { + u_int8_t kv_type; /* one of HAL_CIPHER */ + u_int8_t kv_pad; + u_int16_t kv_len; /* length in bits */ + u_int8_t kv_val[16]; /* enough for 128-bit keys */ + u_int8_t kv_mic[8]; /* TKIP MIC key */ +} HAL_KEYVAL; + +typedef enum { + HAL_CIPHER_WEP = 0, + HAL_CIPHER_AES_OCB = 1, + HAL_CIPHER_AES_CCM = 2, + HAL_CIPHER_CKIP = 3, + HAL_CIPHER_TKIP = 4, + HAL_CIPHER_CLR = 5, /* no encryption */ + + HAL_CIPHER_MIC = 127 /* TKIP-MIC, not a cipher */ +} HAL_CIPHER; + +enum { + HAL_SLOT_TIME_9 = 9, + HAL_SLOT_TIME_20 = 20, +}; + +/* + * Per-station beacon timer state. Note that the specified + * beacon interval (given in TU's) can also include flags + * to force a TSF reset and to enable the beacon xmit logic. + * If bs_cfpmaxduration is non-zero the hardware is setup to + * coexist with a PCF-capable AP. + */ +typedef struct { + u_int32_t bs_nexttbtt; /* next beacon in TU */ + u_int32_t bs_nextdtim; /* next DTIM in TU */ + u_int32_t bs_intval; /* beacon interval+flags */ +#define HAL_BEACON_PERIOD 0x0000ffff /* beacon interval period */ +#define HAL_BEACON_ENA 0x00800000 /* beacon xmit enable */ +#define HAL_BEACON_RESET_TSF 0x01000000 /* clear TSF */ + u_int32_t bs_dtimperiod; + u_int16_t bs_cfpperiod; /* CFP period in TU */ + u_int16_t bs_cfpmaxduration; /* max CFP duration in TU */ + u_int32_t bs_cfpnext; /* next CFP in TU */ + u_int16_t bs_timoffset; /* byte offset to TIM bitmap */ + u_int16_t bs_bmissthreshold; /* beacon miss threshold */ + u_int32_t bs_sleepduration; /* max sleep duration */ +} HAL_BEACON_STATE; + +/* + * Per-node statistics maintained by the driver for use in + * optimizing signal quality and other operational aspects. + */ +typedef struct { + u_int32_t ns_avgbrssi; /* average beacon rssi */ + u_int32_t ns_avgrssi; /* average data rssi */ + u_int32_t ns_avgtxrssi; /* average tx rssi */ +} HAL_NODE_STATS; + +#define HAL_RSSI_EP_MULTIPLIER (1<<7) /* pow2 to optimize out * and / */ + +struct ath_desc; + +/* + * Hardware Access Layer (HAL) API. + * + * Clients of the HAL call ath_hal_attach to obtain a reference to an + * ath_hal structure for use with the device. Hardware-related operations + * that follow must call back into the HAL through interface, supplying + * the reference as the first parameter. Note that before using the + * reference returned by ath_hal_attach the caller should verify the + * ABI version number. + */ +struct ath_hal { + u_int32_t ah_magic; /* consistency check magic number */ + u_int32_t ah_abi; /* HAL ABI version */ +#define HAL_ABI_VERSION 0x04112900 /* YYMMDDnn */ + u_int16_t ah_devid; /* PCI device ID */ + u_int16_t ah_subvendorid; /* PCI subvendor ID */ + HAL_SOFTC ah_sc; /* back pointer to driver/os state */ + HAL_BUS_TAG ah_st; /* params for register r+w */ + HAL_BUS_HANDLE ah_sh; + HAL_CTRY_CODE ah_countryCode; + + u_int32_t ah_macVersion; /* MAC version id */ + u_int16_t ah_macRev; /* MAC revision */ + u_int16_t ah_phyRev; /* PHY revision */ + /* NB: when only one radio is present the rev is in 5Ghz */ + u_int16_t ah_analog5GhzRev;/* 5GHz radio revision */ + u_int16_t ah_analog2GhzRev;/* 2GHz radio revision */ + + const HAL_RATE_TABLE *__ahdecl(*ah_getRateTable)(struct ath_hal *, + u_int mode); + void __ahdecl(*ah_detach)(struct ath_hal*); + + /* Reset functions */ + HAL_BOOL __ahdecl(*ah_reset)(struct ath_hal *, HAL_OPMODE, + HAL_CHANNEL *, HAL_BOOL bChannelChange, + HAL_STATUS *status); + HAL_BOOL __ahdecl(*ah_phyDisable)(struct ath_hal *); + void __ahdecl(*ah_setPCUConfig)(struct ath_hal *); + HAL_BOOL __ahdecl(*ah_perCalibration)(struct ath_hal*, HAL_CHANNEL *); + HAL_BOOL __ahdecl(*ah_setTxPowerLimit)(struct ath_hal *, u_int32_t); + + /* Transmit functions */ + HAL_BOOL __ahdecl(*ah_updateTxTrigLevel)(struct ath_hal*, + HAL_BOOL incTrigLevel); + int __ahdecl(*ah_setupTxQueue)(struct ath_hal *, HAL_TX_QUEUE, + const HAL_TXQ_INFO *qInfo); + HAL_BOOL __ahdecl(*ah_setTxQueueProps)(struct ath_hal *, int q, + const HAL_TXQ_INFO *qInfo); + HAL_BOOL __ahdecl(*ah_getTxQueueProps)(struct ath_hal *, int q, + HAL_TXQ_INFO *qInfo); + HAL_BOOL __ahdecl(*ah_releaseTxQueue)(struct ath_hal *ah, u_int q); + HAL_BOOL __ahdecl(*ah_resetTxQueue)(struct ath_hal *ah, u_int q); + u_int32_t __ahdecl(*ah_getTxDP)(struct ath_hal*, u_int); + HAL_BOOL __ahdecl(*ah_setTxDP)(struct ath_hal*, u_int, u_int32_t txdp); + u_int32_t __ahdecl(*ah_numTxPending)(struct ath_hal *, u_int q); + HAL_BOOL __ahdecl(*ah_startTxDma)(struct ath_hal*, u_int); + HAL_BOOL __ahdecl(*ah_stopTxDma)(struct ath_hal*, u_int); + HAL_BOOL __ahdecl(*ah_updateCTSForBursting)(struct ath_hal *, + struct ath_desc *, struct ath_desc *, + struct ath_desc *, struct ath_desc *, + u_int32_t, u_int32_t); + HAL_BOOL __ahdecl(*ah_setupTxDesc)(struct ath_hal *, struct ath_desc *, + u_int pktLen, u_int hdrLen, + HAL_PKT_TYPE type, u_int txPower, + u_int txRate0, u_int txTries0, + u_int keyIx, u_int antMode, u_int flags, + u_int rtsctsRate, u_int rtsctsDuration); + HAL_BOOL __ahdecl(*ah_setupXTxDesc)(struct ath_hal *, struct ath_desc*, + u_int txRate1, u_int txTries1, + u_int txRate2, u_int txTries2, + u_int txRate3, u_int txTries3); + HAL_BOOL __ahdecl(*ah_fillTxDesc)(struct ath_hal *, struct ath_desc *, + u_int segLen, HAL_BOOL firstSeg, + HAL_BOOL lastSeg, const struct ath_desc *); + HAL_STATUS __ahdecl(*ah_procTxDesc)(struct ath_hal *, struct ath_desc*); + void __ahdecl(*ah_getTxIntrQueue)(struct ath_hal *, u_int32_t *); + + /* Receive Functions */ + u_int32_t __ahdecl(*ah_getRxDP)(struct ath_hal*); + void __ahdecl(*ah_setRxDP)(struct ath_hal*, u_int32_t rxdp); + void __ahdecl(*ah_enableReceive)(struct ath_hal*); + HAL_BOOL __ahdecl(*ah_stopDmaReceive)(struct ath_hal*); + void __ahdecl(*ah_startPcuReceive)(struct ath_hal*); + void __ahdecl(*ah_stopPcuReceive)(struct ath_hal*); + void __ahdecl(*ah_setMulticastFilter)(struct ath_hal*, + u_int32_t filter0, u_int32_t filter1); + HAL_BOOL __ahdecl(*ah_setMulticastFilterIndex)(struct ath_hal*, + u_int32_t index); + HAL_BOOL __ahdecl(*ah_clrMulticastFilterIndex)(struct ath_hal*, + u_int32_t index); + u_int32_t __ahdecl(*ah_getRxFilter)(struct ath_hal*); + void __ahdecl(*ah_setRxFilter)(struct ath_hal*, u_int32_t); + HAL_BOOL __ahdecl(*ah_setupRxDesc)(struct ath_hal *, struct ath_desc *, + u_int32_t size, u_int flags); + HAL_STATUS __ahdecl(*ah_procRxDesc)(struct ath_hal *, struct ath_desc *, + u_int32_t phyAddr, struct ath_desc *next); + void __ahdecl(*ah_rxMonitor)(struct ath_hal *, + const HAL_NODE_STATS *); + void __ahdecl(*ah_procMibEvent)(struct ath_hal *, + const HAL_NODE_STATS *); + + /* Misc Functions */ + HAL_STATUS __ahdecl(*ah_getCapability)(struct ath_hal *, + HAL_CAPABILITY_TYPE, u_int32_t capability, + u_int32_t *result); + HAL_BOOL __ahdecl(*ah_setCapability)(struct ath_hal *, + HAL_CAPABILITY_TYPE, u_int32_t capability, + u_int32_t setting, HAL_STATUS *); + HAL_BOOL __ahdecl(*ah_getDiagState)(struct ath_hal *, int request, + const void *args, u_int32_t argsize, + void **result, u_int32_t *resultsize); + void __ahdecl(*ah_getMacAddress)(struct ath_hal *, u_int8_t *); + HAL_BOOL __ahdecl(*ah_setMacAddress)(struct ath_hal *, const u_int8_t*); + HAL_BOOL __ahdecl(*ah_setRegulatoryDomain)(struct ath_hal*, + u_int16_t, HAL_STATUS *); + void __ahdecl(*ah_setLedState)(struct ath_hal*, HAL_LED_STATE); + void __ahdecl(*ah_writeAssocid)(struct ath_hal*, + const u_int8_t *bssid, u_int16_t assocId); + HAL_BOOL __ahdecl(*ah_gpioCfgOutput)(struct ath_hal *, u_int32_t gpio); + HAL_BOOL __ahdecl(*ah_gpioCfgInput)(struct ath_hal *, u_int32_t gpio); + u_int32_t __ahdecl(*ah_gpioGet)(struct ath_hal *, u_int32_t gpio); + HAL_BOOL __ahdecl(*ah_gpioSet)(struct ath_hal *, + u_int32_t gpio, u_int32_t val); + void __ahdecl(*ah_gpioSetIntr)(struct ath_hal*, u_int, u_int32_t); + u_int32_t __ahdecl(*ah_getTsf32)(struct ath_hal*); + u_int64_t __ahdecl(*ah_getTsf64)(struct ath_hal*); + void __ahdecl(*ah_resetTsf)(struct ath_hal*); + HAL_BOOL __ahdecl(*ah_detectCardPresent)(struct ath_hal*); + void __ahdecl(*ah_updateMibCounters)(struct ath_hal*, + HAL_MIB_STATS*); + HAL_RFGAIN __ahdecl(*ah_getRfGain)(struct ath_hal*); + u_int __ahdecl(*ah_getDefAntenna)(struct ath_hal*); + void __ahdecl(*ah_setDefAntenna)(struct ath_hal*, u_int); + HAL_BOOL __ahdecl(*ah_setSlotTime)(struct ath_hal*, u_int); + u_int __ahdecl(*ah_getSlotTime)(struct ath_hal*); + HAL_BOOL __ahdecl(*ah_setAckTimeout)(struct ath_hal*, u_int); + u_int __ahdecl(*ah_getAckTimeout)(struct ath_hal*); + HAL_BOOL __ahdecl(*ah_setCTSTimeout)(struct ath_hal*, u_int); + u_int __ahdecl(*ah_getCTSTimeout)(struct ath_hal*); + + /* Key Cache Functions */ + u_int32_t __ahdecl(*ah_getKeyCacheSize)(struct ath_hal*); + HAL_BOOL __ahdecl(*ah_resetKeyCacheEntry)(struct ath_hal*, u_int16_t); + HAL_BOOL __ahdecl(*ah_isKeyCacheEntryValid)(struct ath_hal *, + u_int16_t); + HAL_BOOL __ahdecl(*ah_setKeyCacheEntry)(struct ath_hal*, + u_int16_t, const HAL_KEYVAL *, + const u_int8_t *, int); + HAL_BOOL __ahdecl(*ah_setKeyCacheEntryMac)(struct ath_hal*, + u_int16_t, const u_int8_t *); + + /* Power Management Functions */ + HAL_BOOL __ahdecl(*ah_setPowerMode)(struct ath_hal*, + HAL_POWER_MODE mode, int setChip, + u_int16_t sleepDuration); + HAL_POWER_MODE __ahdecl(*ah_getPowerMode)(struct ath_hal*); + HAL_BOOL __ahdecl(*ah_initPSPoll)(struct ath_hal*); + HAL_BOOL __ahdecl(*ah_enablePSPoll)(struct ath_hal *, + u_int8_t *, u_int16_t); + HAL_BOOL __ahdecl(*ah_disablePSPoll)(struct ath_hal *); + + /* Beacon Management Functions */ + void __ahdecl(*ah_beaconInit)(struct ath_hal *, + u_int32_t nexttbtt, u_int32_t intval); + void __ahdecl(*ah_setStationBeaconTimers)(struct ath_hal*, + const HAL_BEACON_STATE *); + void __ahdecl(*ah_resetStationBeaconTimers)(struct ath_hal*); + HAL_BOOL __ahdecl(*ah_waitForBeaconDone)(struct ath_hal *, + HAL_BUS_ADDR); + + /* Interrupt functions */ + HAL_BOOL __ahdecl(*ah_isInterruptPending)(struct ath_hal*); + HAL_BOOL __ahdecl(*ah_getPendingInterrupts)(struct ath_hal*, HAL_INT*); + HAL_INT __ahdecl(*ah_getInterrupts)(struct ath_hal*); + HAL_INT __ahdecl(*ah_setInterrupts)(struct ath_hal*, HAL_INT); +}; + +/* + * Check the PCI vendor ID and device ID against Atheros' values + * and return a printable description for any Atheros hardware. + * AH_NULL is returned if the ID's do not describe Atheros hardware. + */ +extern const char *__ahdecl ath_hal_probe(u_int16_t vendorid, u_int16_t devid); + +/* + * Attach the HAL for use with the specified device. The device is + * defined by the PCI device ID. The caller provides an opaque pointer + * to an upper-layer data structure (HAL_SOFTC) that is stored in the + * HAL state block for later use. Hardware register accesses are done + * using the specified bus tag and handle. On successful return a + * reference to a state block is returned that must be supplied in all + * subsequent HAL calls. Storage associated with this reference is + * dynamically allocated and must be freed by calling the ah_detach + * method when the client is done. If the attach operation fails a + * null (AH_NULL) reference will be returned and a status code will + * be returned if the status parameter is non-zero. + */ +extern struct ath_hal * __ahdecl ath_hal_attach(u_int16_t devid, HAL_SOFTC, + HAL_BUS_TAG, HAL_BUS_HANDLE, HAL_STATUS* status); + +/* + * Return a list of channels available for use with the hardware. + * The list is based on what the hardware is capable of, the specified + * country code, the modeSelect mask, and whether or not outdoor + * channels are to be permitted. + * + * The channel list is returned in the supplied array. maxchans + * defines the maximum size of this array. nchans contains the actual + * number of channels returned. If a problem occurred or there were + * no channels that met the criteria then AH_FALSE is returned. + */ +extern HAL_BOOL __ahdecl ath_hal_init_channels(struct ath_hal *, + HAL_CHANNEL *chans, u_int maxchans, u_int *nchans, + HAL_CTRY_CODE cc, u_int16_t modeSelect, + HAL_BOOL enableOutdoor, HAL_BOOL enableExtendedChannels); + +/* + * Return bit mask of wireless modes supported by the hardware. + */ +extern u_int __ahdecl ath_hal_getwirelessmodes(struct ath_hal*, HAL_CTRY_CODE); + +/* + * Return rate table for specified mode (11a, 11b, 11g, etc). + */ +extern const HAL_RATE_TABLE * __ahdecl ath_hal_getratetable(struct ath_hal *, + u_int mode); + +/* + * Calculate the transmit duration of a frame. + */ +extern u_int16_t __ahdecl ath_hal_computetxtime(struct ath_hal *, + const HAL_RATE_TABLE *rates, u_int32_t frameLen, + u_int16_t rateix, HAL_BOOL shortPreamble); + +/* + * Convert between IEEE channel number and channel frequency + * using the specified channel flags; e.g. CHANNEL_2GHZ. + */ +extern u_int __ahdecl ath_hal_mhz2ieee(u_int mhz, u_int flags); +extern u_int __ahdecl ath_hal_ieee2mhz(u_int ieee, u_int flags); + +/* + * Return a version string for the HAL release. + */ +extern char ath_hal_version[]; +/* + * Return a NULL-terminated array of build/configuration options. + */ +extern const char* ath_hal_buildopts[]; +#endif /* _ATH_AH_H_ */ diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/hal/ah_desc.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/hal/ah_desc.h --- linux-2.6.11-rc4-mm1/drivers/net/wireless/hal/ah_desc.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/hal/ah_desc.h 2005-02-24 13:06:17.246140208 -0800 @@ -0,0 +1,180 @@ +/*- + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting, Atheros + * Communications, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the following conditions are met: + * 1. The materials contained herein are unmodified and are used + * unmodified. + * 2. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following NO + * ''WARRANTY'' disclaimer below (''Disclaimer''), without + * modification. + * 3. Redistributions in binary form must reproduce at minimum a + * disclaimer similar to the Disclaimer below and any redistribution + * must be conditioned upon including a substantially similar + * Disclaimer requirement for further binary redistribution. + * 4. Neither the names of the above-listed copyright holders nor the + * names of any contributors may be used to endorse or promote + * product derived from this software without specific prior written + * permission. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE + * FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGES. + * + * $Id: ah_desc.h,v 1.4 2005/02/22 14:17:23 otaku Exp $ + */ + +#ifndef _DEV_ATH_DESC_H +#define _DEV_ATH_DESC_H + +/* + * Transmit descriptor status. This structure is filled + * in only after the tx descriptor process method finds a + * ``done'' descriptor; at which point it returns something + * other than HAL_EINPROGRESS. + * + * Note that ts_antenna may not be valid for all h/w. It + * should be used only if non-zero. + */ +struct ath_tx_status { + u_int16_t ts_seqnum; /* h/w assigned sequence number */ + u_int16_t ts_tstamp; /* h/w assigned timestamp */ + u_int8_t ts_status; /* frame status, 0 => xmit ok */ + u_int8_t ts_rate; /* h/w transmit rate index */ +#define HAL_TXSTAT_ALTRATE 0x80 /* alternate xmit rate used */ + int8_t ts_rssi; /* tx ack RSSI */ + u_int8_t ts_shortretry; /* # short retries */ + u_int8_t ts_longretry; /* # long retries */ + u_int8_t ts_virtcol; /* virtual collision count */ + u_int8_t ts_antenna; /* antenna information */ +}; + +#define HAL_TXERR_XRETRY 0x01 /* excessive retries */ +#define HAL_TXERR_FILT 0x02 /* blocked by tx filtering */ +#define HAL_TXERR_FIFO 0x04 /* fifo underrun */ + +/* + * Receive descriptor status. This structure is filled + * in only after the rx descriptor process method finds a + * ``done'' descriptor; at which point it returns something + * other than HAL_EINPROGRESS. + * + * If rx_status is zero, then the frame was received ok; + * otherwise the error information is indicated and rs_phyerr + * contains a phy error code if HAL_RXERR_PHY is set. In general + * the frame contents is undefined when an error occurred thought + * for some errors (e.g. a decryption error), it may be meaningful. + * + * Note that the receive timestamp is expanded using the TSF to + * a full 16 bits (regardless of what the h/w provides directly). + * + * rx_rssi is in units of dbm above the noise floor. This value + * is measured during the preamble and PLCP; i.e. with the initial + * 4us of detection. The noise floor is typically a consistent + * -96dBm absolute power in a 20MHz channel. + */ +struct ath_rx_status { + u_int16_t rs_datalen; /* rx frame length */ + u_int16_t rs_tstamp; /* h/w assigned timestamp */ + u_int8_t rs_status; /* rx status, 0 => recv ok */ + u_int8_t rs_phyerr; /* phy error code */ + int8_t rs_rssi; /* rx frame RSSI */ + u_int8_t rs_keyix; /* key cache index */ + u_int8_t rs_rate; /* h/w receive rate index */ + u_int8_t rs_antenna; /* antenna information */ + u_int8_t rs_more; /* more descriptors follow */ +}; + +#define HAL_RXERR_CRC 0x01 /* CRC error on frame */ +#define HAL_RXERR_PHY 0x02 /* PHY error, rs_phyerr is valid */ +#define HAL_RXERR_FIFO 0x04 /* fifo overrun */ +#define HAL_RXERR_DECRYPT 0x08 /* non-Michael decrypt error */ +#define HAL_RXERR_MIC 0x10 /* Michael MIC decrypt error */ + +enum { + HAL_PHYERR_UNDERRUN = 0, /* Transmit underrun */ + HAL_PHYERR_TIMING = 1, /* Timing error */ + HAL_PHYERR_PARITY = 2, /* Illegal parity */ + HAL_PHYERR_RATE = 3, /* Illegal rate */ + HAL_PHYERR_LENGTH = 4, /* Illegal length */ + HAL_PHYERR_RADAR = 5, /* Radar detect */ + HAL_PHYERR_SERVICE = 6, /* Illegal service */ + HAL_PHYERR_TOR = 7, /* Transmit override receive */ + /* NB: these are specific to the 5212 */ + HAL_PHYERR_OFDM_TIMING = 17, /* */ + HAL_PHYERR_OFDM_SIGNAL_PARITY = 18, /* */ + HAL_PHYERR_OFDM_RATE_ILLEGAL = 19, /* */ + HAL_PHYERR_OFDM_LENGTH_ILLEGAL = 20, /* */ + HAL_PHYERR_OFDM_POWER_DROP = 21, /* */ + HAL_PHYERR_OFDM_SERVICE = 22, /* */ + HAL_PHYERR_OFDM_RESTART = 23, /* */ + HAL_PHYERR_CCK_TIMING = 25, /* */ + HAL_PHYERR_CCK_HEADER_CRC = 26, /* */ + HAL_PHYERR_CCK_RATE_ILLEGAL = 27, /* */ + HAL_PHYERR_CCK_SERVICE = 30, /* */ + HAL_PHYERR_CCK_RESTART = 31, /* */ +}; + +/* value found in rs_keyix to mark invalid entries */ +#define HAL_RXKEYIX_INVALID ((u_int8_t) -1) +/* value used to specify no encryption key for xmit */ +#define HAL_TXKEYIX_INVALID ((u_int) -1) + +/* XXX rs_antenna definitions */ + +/* + * Definitions for the software frame/packet descriptors used by + * the Atheros HAL. This definition obscures hardware-specific + * details from the driver. Drivers are expected to fillin the + * portions of a descriptor that are not opaque then use HAL calls + * to complete the work. Status for completed frames is returned + * in a device-independent format. + */ +struct ath_desc { + /* + * The following definitions are passed directly + * the hardware and managed by the HAL. Drivers + * should not touch those elements marked opaque. + */ + u_int32_t ds_link; /* phys address of next descriptor */ + u_int32_t ds_data; /* phys address of data buffer */ + u_int32_t ds_ctl0; /* opaque DMA control 0 */ + u_int32_t ds_ctl1; /* opaque DMA control 1 */ + u_int32_t ds_hw[4]; /* opaque h/w region */ + /* + * The remaining definitions are managed by software; + * these are valid only after the rx/tx process descriptor + * methods return a non-EINPROGRESS code. + */ + union { + struct ath_tx_status tx;/* xmit status */ + struct ath_rx_status rx;/* recv status */ + } ds_us; +} __packed; + +#define ds_txstat ds_us.tx +#define ds_rxstat ds_us.rx + +/* flags passed to tx descriptor setup methods */ +#define HAL_TXDESC_CLRDMASK 0x0001 /* clear destination filter mask */ +#define HAL_TXDESC_NOACK 0x0002 /* don't wait for ACK */ +#define HAL_TXDESC_RTSENA 0x0004 /* enable RTS */ +#define HAL_TXDESC_CTSENA 0x0008 /* enable CTS */ +#define HAL_TXDESC_INTREQ 0x0010 /* enable per-descriptor interrupt */ +#define HAL_TXDESC_VEOL 0x0020 /* mark virtual EOL */ + +/* flags passed to rx descriptor setup methods */ +#define HAL_RXDESC_INTREQ 0x0020 /* enable per-descriptor interrupt */ +#endif /* _DEV_ATH_AR521XDMA_H */ diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/hal/ah_devid.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/hal/ah_devid.h --- linux-2.6.11-rc4-mm1/drivers/net/wireless/hal/ah_devid.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/hal/ah_devid.h 2005-02-24 13:06:17.249139752 -0800 @@ -0,0 +1,87 @@ +/*- + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting, Atheros + * Communications, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the following conditions are met: + * 1. The materials contained herein are unmodified and are used + * unmodified. + * 2. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following NO + * ''WARRANTY'' disclaimer below (''Disclaimer''), without + * modification. + * 3. Redistributions in binary form must reproduce at minimum a + * disclaimer similar to the Disclaimer below and any redistribution + * must be conditioned upon including a substantially similar + * Disclaimer requirement for further binary redistribution. + * 4. Neither the names of the above-listed copyright holders nor the + * names of any contributors may be used to endorse or promote + * product derived from this software without specific prior written + * permission. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE + * FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGES. + * + * $Id: ah_devid.h,v 1.6 2005/02/22 14:17:23 otaku Exp $ + */ + +#ifndef _DEV_ATH_DEVID_H_ +#define _DEV_ATH_DEVID_H_ + +#define ATHEROS_VENDOR_ID 0x168c /* Atheros PCI vendor ID */ +/* + * NB: all Atheros-based devices should have a PCI vendor ID + * of 0x168c, but some vendors, in their infinite wisdom + * do not follow this so we must handle them specially. + */ +#define ATHEROS_3COM_VENDOR_ID 0xa727 /* 3Com 3CRPAG175 vendor ID */ +#define ATHEROS_3COM2_VENDOR_ID 0x10b7 /* 3Com 3CRDAG675 vendor ID */ + +/* AR5210 (for reference) */ +#define AR5210_DEFAULT 0x1107 /* No eeprom HW default */ +#define AR5210_PROD 0x0007 /* Final device ID */ +#define AR5210_AP 0x0207 /* Early AP11s */ + +/* AR5211 */ +#define AR5211_DEFAULT 0x1112 /* No eeprom HW default */ +#define AR5311_DEVID 0x0011 /* Final ar5311 devid */ +#define AR5211_DEVID 0x0012 /* Final ar5211 devid */ +#define AR5211_LEGACY 0xff12 /* Original emulation board */ +#define AR5211_FPGA11B 0xf11b /* 11b emulation board */ + +/* AR5212 */ +#define AR5212_DEFAULT 0x1113 /* No eeprom HW default */ +#define AR5212_DEVID 0x0013 /* Final ar5212 devid */ +#define AR5212_FPGA 0xf013 /* Emulation board */ +#define AR5212_DEVID_IBM 0x1014 /* IBM minipci ID */ +#define AR5212_AR5312_REV2 0x0052 /* AR5312 WMAC (AP31) */ +#define AR5212_AR5312_REV7 0x0057 /* AR5312 WMAC (AP30-040) */ +#define AR5212_AR2313_REV8 0x0058 /* AR2313 WMAC (AP43-030) */ + +/* AR5212 compatible devid's also attach to 5212 */ +#define AR5212_DEVID_0014 0x0014 +#define AR5212_DEVID_0015 0x0015 +#define AR5212_DEVID_0016 0x0016 +#define AR5212_DEVID_0017 0x0017 +#define AR5212_DEVID_0018 0x0018 +#define AR5212_DEVID_0019 0x0019 +#define AR5212_AR2413 0x001a /* AR2413 aka Griffin-lite */ + +/* AR5213 */ +#define AR5213_SREV_1_0 0x0055 +#define AR5213_SREV_REG 0x4020 + +#define AR_SUBVENDOR_ID_NOG 0x0e11 /* No 11G subvendor ID */ +#define AR_SUBVENDOR_ID_NEW_A 0x7065 /* Update device to new RD */ +#endif /* _DEV_ATH_DEVID_H */ diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/hal/linux/ah_osdep.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/hal/linux/ah_osdep.c --- linux-2.6.11-rc4-mm1/drivers/net/wireless/hal/linux/ah_osdep.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/hal/linux/ah_osdep.c 2005-02-24 13:06:17.258138384 -0800 @@ -0,0 +1,578 @@ +/*- + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting, Atheros + * Communications, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the following conditions are met: + * 1. The materials contained herein are unmodified and are used + * unmodified. + * 2. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following NO + * ''WARRANTY'' disclaimer below (''Disclaimer''), without + * modification. + * 3. Redistributions in binary form must reproduce at minimum a + * disclaimer similar to the Disclaimer below and any redistribution + * must be conditioned upon including a substantially similar + * Disclaimer requirement for further binary redistribution. + * 4. Neither the names of the above-listed copyright holders nor the + * names of any contributors may be used to endorse or promote + * product derived from this software without specific prior written + * permission. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE + * FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGES. + * + * $Id: ah_osdep.c,v 1.7 2005/02/22 14:17:23 otaku Exp $ + */ +#include "opt_ah.h" + +#ifndef EXPORT_SYMTAB +#define EXPORT_SYMTAB +#endif + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + +#include "ah.h" + +#ifndef __MOD_INC_USE_COUNT +#define AH_MOD_INC_USE_COUNT(_m) \ + if (!try_module_get(_m)) { \ + printk(KERN_WARNING "try_module_get failed\n"); \ + return NULL; \ + } +#define AH_MOD_DEC_USE_COUNT(_m) module_put(_m) +#else +#define AH_MOD_INC_USE_COUNT(_m) MOD_INC_USE_COUNT +#define AH_MOD_DEC_USE_COUNT(_m) MOD_DEC_USE_COUNT +#endif + +#ifdef AH_DEBUG +static int ath_hal_debug = 0; +#endif + +int ath_hal_dma_beacon_response_time = 2; /* in TU's */ +int ath_hal_sw_beacon_response_time = 10; /* in TU's */ +int ath_hal_additional_swba_backoff = 0; /* in TU's */ + +struct ath_hal * +_ath_hal_attach(u_int16_t devid, HAL_SOFTC sc, + HAL_BUS_TAG t, HAL_BUS_HANDLE h, void* s) +{ + HAL_STATUS status; + struct ath_hal *ah = ath_hal_attach(devid, sc, t, h, &status); + + *(HAL_STATUS *)s = status; + if (ah) + AH_MOD_INC_USE_COUNT(THIS_MODULE); + return ah; +} + +void +ath_hal_detach(struct ath_hal *ah) +{ + (*ah->ah_detach)(ah); + AH_MOD_DEC_USE_COUNT(THIS_MODULE); +} + +/* + * Print/log message support. + */ + +void __ahdecl +ath_hal_vprintf(struct ath_hal *ah, const char* fmt, va_list ap) +{ + char buf[1024]; /* XXX */ + vsnprintf(buf, sizeof(buf), fmt, ap); + printk("%s", buf); +} + +void __ahdecl +ath_hal_printf(struct ath_hal *ah, const char* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + ath_hal_vprintf(ah, fmt, ap); + va_end(ap); +} +EXPORT_SYMBOL(ath_hal_printf); + +/* + * Format an Ethernet MAC for printing. + */ +const char* __ahdecl +ath_hal_ether_sprintf(const u_int8_t *mac) +{ + static char etherbuf[18]; + snprintf(etherbuf, sizeof(etherbuf), "%02x:%02x:%02x:%02x:%02x:%02x", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + return etherbuf; +} + +#ifdef AH_ASSERT +void __ahdecl +ath_hal_assert_failed(const char* filename, int lineno, const char *msg) +{ + printk("Atheros HAL assertion failure: %s: line %u: %s\n", + filename, lineno, msg); + panic("ath_hal_assert"); +} +#endif /* AH_ASSERT */ + +#ifdef AH_DEBUG_ALQ +/* + * ALQ register tracing support. + * + * Setting hw.ath.hal.alq=1 enables tracing of all register reads and + * writes to the file /tmp/ath_hal.log. The file format is a simple + * fixed-size array of records. When done logging set hw.ath.hal.alq=0 + * and then decode the file with the ardecode program (that is part of the + * HAL). If you start+stop tracing the data will be appended to an + * existing file. + * + * NB: doesn't handle multiple devices properly; only one DEVICE record + * is emitted and the different devices are not identified. + */ +#include "alq/alq.h" +#include "ah_decode.h" + +static struct alq *ath_hal_alq; +static int ath_hal_alq_emitdev; /* need to emit DEVICE record */ +static u_int ath_hal_alq_lost; /* count of lost records */ +static const char *ath_hal_logfile = "/tmp/ath_hal.log"; +static u_int ath_hal_alq_qsize = 8*1024; + +static int +ath_hal_setlogging(int enable) +{ + int error; + + if (enable) { + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + error = alq_open(&ath_hal_alq, ath_hal_logfile, + sizeof (struct athregrec), ath_hal_alq_qsize); + ath_hal_alq_lost = 0; + ath_hal_alq_emitdev = 1; + printk("ath_hal: logging to %s %s\n", ath_hal_logfile, + error == 0 ? "enabled" : "could not be setup"); + } else { + if (ath_hal_alq) + alq_close(ath_hal_alq); + ath_hal_alq = NULL; + printk("ath_hal: logging disabled\n"); + error = 0; + } + return error; +} + +/* + * Deal with the sysctl handler api changing. + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8) +#define AH_SYSCTL_ARGS_DECL \ + ctl_table *ctl, int write, struct file *filp, void *buffer, \ + size_t *lenp +#define AH_SYSCTL_ARGS ctl, write, filp, buffer, lenp +#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,8) */ +#define AH_SYSCTL_ARGS_DECL \ + ctl_table *ctl, int write, struct file *filp, void *buffer,\ + size_t *lenp, loff_t *ppos +#define AH_SYSCTL_ARGS ctl, write, filp, buffer, lenp, ppos +#endif + +static int +sysctl_hw_ath_hal_log(AH_SYSCTL_ARGS_DECL) +{ + int error, enable; + + ctl->data = &enable; + ctl->maxlen = sizeof(enable); + enable = (ath_hal_alq != NULL); + error = proc_dointvec(AH_SYSCTL_ARGS); + if (error || !write) + return error; + else + return ath_hal_setlogging(enable); +} + +static struct ale * +ath_hal_alq_get(struct ath_hal *ah) +{ + struct ale *ale; + + if (ath_hal_alq_emitdev) { + ale = alq_get(ath_hal_alq, ALQ_NOWAIT); + if (ale) { + struct athregrec *r = + (struct athregrec *) ale->ae_data; + r->op = OP_DEVICE; + r->reg = 0; + r->val = ah->ah_devid; + alq_post(ath_hal_alq, ale); + ath_hal_alq_emitdev = 0; + } else + ath_hal_alq_lost++; + } + ale = alq_get(ath_hal_alq, ALQ_NOWAIT); + if (!ale) + ath_hal_alq_lost++; + return ale; +} + +void __ahdecl +ath_hal_reg_write(struct ath_hal *ah, u_int32_t reg, u_int32_t val) +{ + if (ath_hal_alq) { + unsigned long flags; + struct ale *ale; + + local_irq_save(flags); + ale = ath_hal_alq_get(ah); + if (ale) { + struct athregrec *r = (struct athregrec *) ale->ae_data; + r->op = OP_WRITE; + r->reg = reg; + r->val = val; + alq_post(ath_hal_alq, ale); + } + local_irq_restore(flags); + } + _OS_REG_WRITE(ah, reg, val); +} +EXPORT_SYMBOL(ath_hal_reg_write); + +u_int32_t __ahdecl +ath_hal_reg_read(struct ath_hal *ah, u_int32_t reg) +{ + u_int32_t val; + + val = _OS_REG_READ(ah, reg); + if (ath_hal_alq) { + unsigned long flags; + struct ale *ale; + + local_irq_save(flags); + ale = ath_hal_alq_get(ah); + if (ale) { + struct athregrec *r = (struct athregrec *) ale->ae_data; + r->op = OP_READ; + r->reg = reg; + r->val = val; + alq_post(ath_hal_alq, ale); + } + local_irq_restore(flags); + } + return val; +} +EXPORT_SYMBOL(ath_hal_reg_read); + +void __ahdecl +OS_MARK(struct ath_hal *ah, u_int id, u_int32_t v) +{ + if (ath_hal_alq) { + unsigned long flags; + struct ale *ale; + + local_irq_save(flags); + ale = ath_hal_alq_get(ah); + if (ale) { + struct athregrec *r = (struct athregrec *) ale->ae_data; + r->op = OP_MARK; + r->reg = id; + r->val = v; + alq_post(ath_hal_alq, ale); + } + local_irq_restore(flags); + } +} +EXPORT_SYMBOL(OS_MARK); +#elif defined(AH_DEBUG) || defined(AH_REGOPS_FUNC) +/* + * Memory-mapped device register read/write. These are here + * as routines when debugging support is enabled and/or when + * explicitly configured to use function calls. The latter is + * for architectures that might need to do something before + * referencing memory (e.g. remap an i/o window). + * + * NB: see the comments in ah_osdep.h about byte-swapping register + * reads and writes to understand what's going on below. + */ +void __ahdecl +ath_hal_reg_write(struct ath_hal *ah, u_int reg, u_int32_t val) +{ +#ifdef AH_DEBUG + if (ath_hal_debug > 1) + ath_hal_printf(ah, "WRITE 0x%x <= 0x%x\n", reg, val); +#endif + _OS_REG_WRITE(ah, reg, val); +} +EXPORT_SYMBOL(ath_hal_reg_write); + +u_int32_t __ahdecl +ath_hal_reg_read(struct ath_hal *ah, u_int reg) +{ + u_int32_t val; + + val = _OS_REG_READ(ah, reg); +#ifdef AH_DEBUG + if (ath_hal_debug > 1) + ath_hal_printf(ah, "READ 0x%x => 0x%x\n", reg, val); +#endif + return val; +} +EXPORT_SYMBOL(ath_hal_reg_read); +#endif /* AH_DEBUG || AH_REGOPS_FUNC */ + +#ifdef AH_DEBUG +void __ahdecl +HALDEBUG(struct ath_hal *ah, const char* fmt, ...) +{ + if (ath_hal_debug) { + __va_list ap; + va_start(ap, fmt); + ath_hal_vprintf(ah, fmt, ap); + va_end(ap); + } +} + + +void __ahdecl +HALDEBUGn(struct ath_hal *ah, u_int level, const char* fmt, ...) +{ + if (ath_hal_debug >= level) { + __va_list ap; + va_start(ap, fmt); + ath_hal_vprintf(ah, fmt, ap); + va_end(ap); + } +} +#endif /* AH_DEBUG */ + +/* + * Delay n microseconds. + */ +void __ahdecl +ath_hal_delay(int n) +{ + udelay(n); +} + +u_int32_t __ahdecl +ath_hal_getuptime(struct ath_hal *ah) +{ + return ((jiffies / HZ) * 1000) + (jiffies % HZ) * (1000 / HZ); +} +EXPORT_SYMBOL(ath_hal_getuptime); + +/* + * Allocate/free memory. + */ + +void * __ahdecl +ath_hal_malloc(size_t size) +{ + void *p; + p = kmalloc(size, GFP_KERNEL); + if (p) + OS_MEMZERO(p, size); + return p; + +} + +void __ahdecl +ath_hal_free(void* p) +{ + kfree(p); +} + +void __ahdecl +ath_hal_memzero(void *dst, size_t n) +{ + memset(dst, 0, n); +} +EXPORT_SYMBOL(ath_hal_memzero); + +void * __ahdecl +ath_hal_memcpy(void *dst, const void *src, size_t n) +{ + return memcpy(dst, src, n); +} +EXPORT_SYMBOL(ath_hal_memcpy); + +#ifdef CONFIG_SYSCTL +enum { + DEV_ATH = 9, /* XXX must match driver */ +}; + +#define CTL_AUTO -2 /* cannot be CTL_ANY or CTL_NONE */ + +static ctl_table ath_hal_sysctls[] = { +#ifdef AH_DEBUG + { .ctl_name = CTL_AUTO, + .procname = "debug", + .mode = 0644, + .data = &ath_hal_debug, + .maxlen = sizeof(ath_hal_debug), + .proc_handler = proc_dointvec + }, +#endif + { .ctl_name = CTL_AUTO, + .procname = "dma_beacon_response_time", + .data = &ath_hal_dma_beacon_response_time, + .maxlen = sizeof(ath_hal_dma_beacon_response_time), + .mode = 0644, + .proc_handler = proc_dointvec + }, + { .ctl_name = CTL_AUTO, + .procname = "sw_beacon_response_time", + .mode = 0644, + .data = &ath_hal_sw_beacon_response_time, + .maxlen = sizeof(ath_hal_sw_beacon_response_time), + .proc_handler = proc_dointvec + }, + { .ctl_name = CTL_AUTO, + .procname = "swba_backoff", + .mode = 0644, + .data = &ath_hal_additional_swba_backoff, + .maxlen = sizeof(ath_hal_additional_swba_backoff), + .proc_handler = proc_dointvec + }, +#ifdef AH_DEBUG_ALQ + { .ctl_name = CTL_AUTO, + .procname = "alq", + .mode = 0644, + .proc_handler = sysctl_hw_ath_hal_log + }, + { .ctl_name = CTL_AUTO, + .procname = "alq_size", + .mode = 0644, + .data = &ath_hal_alq_qsize, + .maxlen = sizeof(ath_hal_alq_qsize), + .proc_handler = proc_dointvec + }, + { .ctl_name = CTL_AUTO, + .procname = "alq_lost", + .mode = 0644, + .data = &ath_hal_alq_lost, + .maxlen = sizeof(ath_hal_alq_lost), + .proc_handler = proc_dointvec + }, +#endif + { 0 } +}; +static ctl_table ath_hal_table[] = { + { .ctl_name = CTL_AUTO, + .procname = "hal", + .mode = 0555, + .child = ath_hal_sysctls + }, { 0 } +}; +static ctl_table ath_ath_table[] = { + { .ctl_name = DEV_ATH, + .procname = "ath", + .mode = 0555, + .child = ath_hal_table + }, { 0 } +}; +static ctl_table ath_root_table[] = { + { .ctl_name = CTL_DEV, + .procname = "dev", + .mode = 0555, + .child = ath_ath_table + }, { 0 } +}; +static struct ctl_table_header *ath_hal_sysctl_header; + +void +ath_hal_sysctl_register(void) +{ + static int initialized = 0; + + if (!initialized) { + ath_hal_sysctl_header = + register_sysctl_table(ath_root_table, 1); + initialized = 1; + } +} + +void +ath_hal_sysctl_unregister(void) +{ + if (ath_hal_sysctl_header) + unregister_sysctl_table(ath_hal_sysctl_header); +} +#endif /* CONFIG_SYSCTL */ + +/* + * Module glue. + */ +static char *dev_info = "ath_hal"; + +MODULE_AUTHOR("Errno Consulting, Sam Leffler"); +MODULE_DESCRIPTION("Atheros Hardware Access Layer (HAL)"); +MODULE_SUPPORTED_DEVICE("Atheros WLAN devices"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("Proprietary"); +#endif + +EXPORT_SYMBOL(ath_hal_probe); +EXPORT_SYMBOL(_ath_hal_attach); +EXPORT_SYMBOL(ath_hal_detach); +EXPORT_SYMBOL(ath_hal_init_channels); +EXPORT_SYMBOL(ath_hal_getwirelessmodes); +EXPORT_SYMBOL(ath_hal_computetxtime); +EXPORT_SYMBOL(ath_hal_mhz2ieee); +EXPORT_SYMBOL(ath_hal_ieee2mhz); + +static int __init +init_ath_hal(void) +{ + const char *sep; + int i; + + printk(KERN_INFO "%s: %s (", dev_info, ath_hal_version); + sep = ""; + for (i = 0; ath_hal_buildopts[i] != NULL; i++) { + printk("%s%s", sep, ath_hal_buildopts[i]); + sep = ", "; + } + printk(")\n"); +#ifdef CONFIG_SYSCTL + ath_hal_sysctl_register(); +#endif + return (0); +} +module_init(init_ath_hal); + +static void __exit +exit_ath_hal(void) +{ +#ifdef CONFIG_SYSCTL + ath_hal_sysctl_unregister(); +#endif + printk(KERN_INFO "%s: driver unloaded\n", dev_info); +} +module_exit(exit_ath_hal); diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/hal/linux/ah_osdep.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/hal/linux/ah_osdep.h --- linux-2.6.11-rc4-mm1/drivers/net/wireless/hal/linux/ah_osdep.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/hal/linux/ah_osdep.h 2005-02-24 13:06:17.261137928 -0800 @@ -0,0 +1,228 @@ +/*- + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting, Atheros + * Communications, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the following conditions are met: + * 1. The materials contained herein are unmodified and are used + * unmodified. + * 2. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following NO + * ''WARRANTY'' disclaimer below (''Disclaimer''), without + * modification. + * 3. Redistributions in binary form must reproduce at minimum a + * disclaimer similar to the Disclaimer below and any redistribution + * must be conditioned upon including a substantially similar + * Disclaimer requirement for further binary redistribution. + * 4. Neither the names of the above-listed copyright holders nor the + * names of any contributors may be used to endorse or promote + * product derived from this software without specific prior written + * permission. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE + * FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGES. + * + * $Id: ah_osdep.h,v 1.4 2005/02/23 17:25:20 otaku Exp $ + */ +#ifndef _ATH_AH_OSDEP_H_ +#define _ATH_AH_OSDEP_H_ +/* + * Atheros Hardware Access Layer (HAL) OS Dependent Definitions. + */ + +/* + * Starting with 2.6.4 the kernel supports a configuration option + * to pass parameters in registers. If this is enabled we must + * mark all function interfaces in+out of the HAL to pass parameters + * on the stack as this is the convention used internally (for + * maximum portability). + */ +#ifdef CONFIG_REGPARM +#define __ahdecl __attribute__((regparm(0))) +#else +#define __ahdecl +#endif +#ifndef __packed +#define __packed __attribute__((__packed__)) +#endif + +/* + * When building the HAL proper we use no GPL-contaminated include + * files and must define these types ourself. Beware of these being + * mismatched against the contents of + */ +#ifndef _LINUX_TYPES_H +/* NB: arm defaults to unsigned so be explicit */ +typedef signed char int8_t; +typedef short int16_t; +typedef int int32_t; +typedef long long int64_t; + +typedef unsigned char u_int8_t; +typedef unsigned short u_int16_t; +typedef unsigned int u_int32_t; +typedef unsigned long long u_int64_t; + +typedef unsigned int size_t; +typedef unsigned int u_int; +typedef void *va_list; +#endif + +/* + * Linux/BSD gcc compatibility shims. + */ +#define __printflike(_a,_b) \ + __attribute__ ((__format__ (__printf__, _a, _b))) +#define __va_list va_list +#define OS_INLINE __inline + +typedef void* HAL_SOFTC; +typedef int HAL_BUS_TAG; +typedef void* HAL_BUS_HANDLE; +typedef u_int32_t HAL_BUS_ADDR; /* XXX architecture dependent */ + +/* + * Delay n microseconds. + */ +extern void __ahdecl ath_hal_delay(int); +#define OS_DELAY(_n) ath_hal_delay(_n) + +#define OS_MEMZERO(_a, _n) ath_hal_memzero((_a), (_n)) +extern void __ahdecl ath_hal_memzero(void *, size_t); +#define OS_MEMCPY(_d, _s, _n) ath_hal_memcpy(_d,_s,_n) +extern void * __ahdecl ath_hal_memcpy(void *, const void *, size_t); + +#ifndef abs +#define abs(_a) __builtin_abs(_a) +#endif + +struct ath_hal; +extern u_int32_t __ahdecl ath_hal_getuptime(struct ath_hal *); +#define OS_GETUPTIME(_ah) ath_hal_getuptime(_ah) + +/* + * Byte order/swapping support. + */ +#define AH_LITTLE_ENDIAN 1234 +#define AH_BIG_ENDIAN 4321 + +#if AH_BYTE_ORDER == AH_BIG_ENDIAN +/* + * This could be optimized but since we only use it for + * a few registers there's little reason to do so. + */ +static inline u_int32_t +__bswap32(u_int32_t _x) +{ + return ((u_int32_t)( + (((const u_int8_t *)(&_x))[0] ) | + (((const u_int8_t *)(&_x))[1]<< 8) | + (((const u_int8_t *)(&_x))[2]<<16) | + (((const u_int8_t *)(&_x))[3]<<24)) + ); +} +#else +#define __bswap32(_x) (_x) +#endif + +/* + * Register read/write; we assume the registers will always + * be memory-mapped. Note that register accesses are done + * using target-specific functions when debugging is enabled + * (AH_DEBUG) or we are explicitly configured this way. The + * latter is used on some platforms where the full i/o space + * cannot be directly mapped. + * + * The hardware registers are native little-endian byte order. + * Big-endian hosts are handled by enabling hardware byte-swap + * of register reads and writes at reset. But the PCI clock + * domain registers are not byte swapped! Thus, on big-endian + * platforms we have to byte-swap thoese registers specifically. + * Most of this code is collapsed at compile time because the + * register values are constants. + */ +#if AH_BYTE_ORDER == AH_BIG_ENDIAN + +#if ( defined(CONFIG_PPC_PMAC) || defined(CONFIG_ARCH_IXP425) || defined(CONFIG_ARCH_IXP4XX)) /* ixp4xx or PowerPC architecture */ + +#define _OS_REG_WRITE(_ah, _reg, _val) do { \ + if ( (_reg) >= 0x4000 && (_reg) < 0x5000) \ + writel((_val), (volatile unsigned long)((_ah)->ah_sh + (_reg)));\ + else \ + writel(__bswap32(_val), (volatile unsigned long)((_ah)->ah_sh + (_reg))); \ +} while (0) +#define _OS_REG_READ(_ah, _reg) \ + (((_reg) >= 0x4000 && (_reg) < 0x5000) ? \ + readl((volatile unsigned long)((_ah)->ah_sh + (_reg))) : \ + __bswap32(readl((volatile unsigned long)((_ah)->ah_sh + (_reg))))) + +#else /* normal case */ + +#define _OS_REG_WRITE(_ah, _reg, _val) do { \ + if ( (_reg) >= 0x4000 && (_reg) < 0x5000) \ + *((volatile u_int32_t *)((_ah)->ah_sh + (_reg))) = \ + __bswap32((_val)); \ + else \ + *((volatile u_int32_t *)((_ah)->ah_sh + (_reg))) = (_val); \ +} while (0) +#define _OS_REG_READ(_ah, _reg) \ + (((_reg) >= 0x4000 && (_reg) < 0x5000) ? \ + __bswap32(*((volatile u_int32_t *)((_ah)->ah_sh + (_reg)))) : \ + *((volatile u_int32_t *)((_ah)->ah_sh + (_reg)))) + +#endif +#else /* AH_LITTLE_ENDIAN */ +#define _OS_REG_WRITE(_ah, _reg, _val) do { \ + *((volatile u_int32_t *)((_ah)->ah_sh + (_reg))) = (_val); \ +} while (0) +#define _OS_REG_READ(_ah, _reg) \ + *((volatile u_int32_t *)((_ah)->ah_sh + (_reg))) +#endif /* AH_BYTE_ORDER */ + +#if defined(AH_DEBUG) || defined(AH_REGOPS_FUNC) || defined(AH_DEBUG_ALQ) +/* use functions to do register operations */ +#define OS_REG_WRITE(_ah, _reg, _val) ath_hal_reg_write(_ah, _reg, _val) +#define OS_REG_READ(_ah, _reg) ath_hal_reg_read(_ah, _reg) + +extern void __ahdecl ath_hal_reg_write(struct ath_hal *ah, + u_int reg, u_int32_t val); +extern u_int32_t __ahdecl ath_hal_reg_read(struct ath_hal *ah, u_int reg); +#else +/* inline register operations */ +#define OS_REG_WRITE(_ah, _reg, _val) _OS_REG_WRITE(_ah, _reg, _val) +#define OS_REG_READ(_ah, _reg) _OS_REG_READ(_ah, _reg) +#endif /* AH_DEBUG || AH_REGFUNC || AH_DEBUG_ALQ */ + +#ifdef AH_DEBUG_ALQ +extern void __ahdecl OS_MARK(struct ath_hal *, u_int id, u_int32_t value); +#else +#define OS_MARK(_ah, _id, _v) +#endif + +/* + * Linux-specific attach/detach methods needed for module reference counting. + * + * XXX We can't use HAL_STATUS because the type isn't defined at this + * point (circular dependency); we wack the type and patch things + * up in the function. + * + * NB: These are intentionally not marked __ahdecl since they are + * compiled with the default calling convetion and are not called + * from within the HAL. + */ +extern struct ath_hal *_ath_hal_attach(u_int16_t devid, HAL_SOFTC, + HAL_BUS_TAG, HAL_BUS_HANDLE, void* status); +extern void ath_hal_detach(struct ath_hal *); + +#endif /* _ATH_AH_OSDEP_H_ */ diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/hal/version.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/hal/version.h --- linux-2.6.11-rc4-mm1/drivers/net/wireless/hal/version.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/hal/version.h 2005-02-24 13:06:17.252139296 -0800 @@ -0,0 +1,38 @@ +/*- + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + * + * $Id: version.h,v 1.2 2005/02/22 14:17:23 otaku Exp $ + */ +#define ATH_HAL_VERSION "0.9.14.9" diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/Kconfig linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/Kconfig --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/Kconfig 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/Kconfig 2005-02-24 13:06:17.418114064 -0800 @@ -0,0 +1,3 @@ + +config NET80211 + tristate diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/Makefile linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/Makefile --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/Makefile 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/Makefile 2005-02-24 13:06:17.433111784 -0800 @@ -0,0 +1,38 @@ +# +# Makefile for the 802.11 NET80211 module. +# + + +COMPAT= drivers/net/wireless/net80211/compat + +EXTRA_CFLAGS+= -include ${COMPAT}/compat.h -I${COMPAT} -I${src}/.. + +# +# There are two authenticator mechanisms: an in-kernel implementation +# (wlan_auth+wlan_radius) and an external implementation (wlan_xauth) that +# requires a user process to manage the authentication process. By default +# the external authenticator is used. ieee80211_proto.c has a table of module +# names that defines the default module to auto-load for each authentication +# scheme; to get the in-kernel authenticator by default modify it to load +# wlan_auth instead of wlan_xauth or manually load wlan_auth prior to use. +# +MOD_AUTH := wlan_xauth.o + +obj-$(CONFIG_NET80211) += wlan.o wlan_wep.o wlan_tkip.o wlan_ccmp.o \ + $(MOD_AUTH) wlan_acl.o + +wlan-objs := if_media.o rc4.o \ + ieee80211.o ieee80211_crypto.o ieee80211_input.o \ + ieee80211_node.o ieee80211_output.o ieee80211_proto.o \ + ieee80211_wireless.o ieee80211_linux.o \ + ieee80211_crypto_none.o +wlan_wep-objs := ieee80211_crypto_wep.o +wlan_tkip-objs := ieee80211_crypto_tkip.o +wlan_ccmp-objs := ieee80211_crypto_ccmp.o +wlan_xauth-objs := ieee80211_xauth.o +wlan_auth-objs := ieee80211_dot1x.o +wlan_radius-objs:= ieee80211_radius.o +wlan_acl-objs := ieee80211_acl.o + + + diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/compat/compat.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/compat/compat.h --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/compat/compat.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/compat/compat.h 2005-02-24 13:06:17.440110720 -0800 @@ -0,0 +1,86 @@ +/*- + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + * + * $Id: compat.h,v 1.8 2005/02/16 16:08:44 samleffler Exp $ + */ +#ifndef _ATH_COMPAT_H_ +#define _ATH_COMPAT_H_ +/* + * BSD/Linux compatibility shims. These are used mainly to + * minimize differences when importing necesary BSD code. + */ +#define NBBY 8 /* number of bits/byte */ + +#define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) /* to any y */ +#define howmany(x, y) (((x)+((y)-1))/(y)) + +/* Bit map related macros. */ +#define setbit(a,i) ((a)[(i)/NBBY] |= 1<<((i)%NBBY)) +#define clrbit(a,i) ((a)[(i)/NBBY] &= ~(1<<((i)%NBBY))) +#define isset(a,i) ((a)[(i)/NBBY] & (1<<((i)%NBBY))) +#define isclr(a,i) (((a)[(i)/NBBY] & (1<<((i)%NBBY))) == 0) + +#define __packed __attribute__((__packed__)) +#define __printflike(_a,_b) \ + __attribute__ ((__format__ (__printf__, _a, _b))) + +#ifndef ALIGNED_POINTER +/* + * ALIGNED_POINTER is a boolean macro that checks whether an address + * is valid to fetch data elements of type t from on this architecture. + * This does not reflect the optimal alignment, just the possibility + * (within reasonable limits). + * + */ +#define ALIGNED_POINTER(p,t) 1 +#endif + +#ifdef __KERNEL__ +#include + +#define KASSERT(exp, msg) do { \ + if (unlikely(!(exp))) { \ + printk msg; \ + BUG(); \ + } \ +} while (0) +#endif /* __KERNEL__ */ + +/* + * NetBSD/FreeBSD defines for file version. + */ +#define __FBSDID(_s) +#define __KERNEL_RCSID(_n,_s) +#endif /* _ATH_COMPAT_H_ */ diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/compat/sys/queue.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/compat/sys/queue.h --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/compat/sys/queue.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/compat/sys/queue.h 2005-02-24 13:06:17.446109808 -0800 @@ -0,0 +1,528 @@ +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + * $FreeBSD: src/sys/sys/queue.h,v 1.54 2002/08/05 05:18:43 alfred Exp $ + * $Id: queue.h,v 1.5 2004/08/05 17:34:51 samleffler Exp $ + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +/* + * This file defines four types of data structures: singly-linked lists, + * singly-linked tail queues, lists and tail queues. + * + * A singly-linked list is headed by a single forward pointer. The elements + * are singly linked for minimum space and pointer manipulation overhead at + * the expense of O(n) removal for arbitrary elements. New elements can be + * added to the list after an existing element or at the head of the list. + * Elements being removed from the head of the list should use the explicit + * macro for this purpose for optimum efficiency. A singly-linked list may + * only be traversed in the forward direction. Singly-linked lists are ideal + * for applications with large datasets and few or no removals or for + * implementing a LIFO queue. + * + * A singly-linked tail queue is headed by a pair of pointers, one to the + * head of the list and the other to the tail of the list. The elements are + * singly linked for minimum space and pointer manipulation overhead at the + * expense of O(n) removal for arbitrary elements. New elements can be added + * to the list after an existing element, at the head of the list, or at the + * end of the list. Elements being removed from the head of the tail queue + * should use the explicit macro for this purpose for optimum efficiency. + * A singly-linked tail queue may only be traversed in the forward direction. + * Singly-linked tail queues are ideal for applications with large datasets + * and few or no removals or for implementing a FIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * For details on the use of these macros, see the queue(3) manual page. + * + * + * SLIST LIST STAILQ TAILQ + * _HEAD + + + + + * _HEAD_INITIALIZER + + + + + * _ENTRY + + + + + * _INIT + + + + + * _EMPTY + + + + + * _FIRST + + + + + * _NEXT + + + + + * _PREV - - - + + * _LAST - - + + + * _FOREACH + + + + + * _FOREACH_REVERSE - - - + + * _INSERT_HEAD + + + + + * _INSERT_BEFORE - + - + + * _INSERT_AFTER + + + + + * _INSERT_TAIL - - + + + * _CONCAT - - + + + * _REMOVE_HEAD + - + - + * _REMOVE + + + + + * + */ +#define QUEUE_MACRO_DEBUG 0 +#if QUEUE_MACRO_DEBUG +/* Store the last 2 places the queue element or head was altered */ +struct qm_trace { + char * lastfile; + int lastline; + char * prevfile; + int prevline; +}; + +#define TRACEBUF struct qm_trace trace; +#define TRASHIT(x) do {(x) = (void *)-1;} while (0) + +#define QMD_TRACE_HEAD(head) do { \ + (head)->trace.prevline = (head)->trace.lastline; \ + (head)->trace.prevfile = (head)->trace.lastfile; \ + (head)->trace.lastline = __LINE__; \ + (head)->trace.lastfile = __FILE__; \ +} while (0) + +#define QMD_TRACE_ELEM(elem) do { \ + (elem)->trace.prevline = (elem)->trace.lastline; \ + (elem)->trace.prevfile = (elem)->trace.lastfile; \ + (elem)->trace.lastline = __LINE__; \ + (elem)->trace.lastfile = __FILE__; \ +} while (0) + +#else +#define QMD_TRACE_ELEM(elem) +#define QMD_TRACE_HEAD(head) +#define TRACEBUF +#define TRASHIT(x) +#endif /* QUEUE_MACRO_DEBUG */ + +/* + * Singly-linked List declarations. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List functions. + */ +#define SLIST_EMPTY(head) ((head)->slh_first == NULL) + +#define SLIST_FIRST(head) ((head)->slh_first) + +#define SLIST_FOREACH(var, head, field) \ + for ((var) = SLIST_FIRST((head)); \ + (var); \ + (var) = SLIST_NEXT((var), field)) + +#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \ + for ((varp) = &SLIST_FIRST((head)); \ + ((var) = *(varp)) != NULL; \ + (varp) = &SLIST_NEXT((var), field)) + +#define SLIST_INIT(head) do { \ + SLIST_FIRST((head)) = NULL; \ +} while (0) + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \ + SLIST_NEXT((slistelm), field) = (elm); \ +} while (0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \ + SLIST_FIRST((head)) = (elm); \ +} while (0) + +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if (SLIST_FIRST((head)) == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = SLIST_FIRST((head)); \ + while (SLIST_NEXT(curelm, field) != (elm)) \ + curelm = SLIST_NEXT(curelm, field); \ + SLIST_NEXT(curelm, field) = \ + SLIST_NEXT(SLIST_NEXT(curelm, field), field); \ + } \ +} while (0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \ +} while (0) + +/* + * Singly-linked Tail queue declarations. + */ +#define STAILQ_HEAD(name, type) \ +struct name { \ + struct type *stqh_first;/* first element */ \ + struct type **stqh_last;/* addr of last next element */ \ +} + +#define STAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).stqh_first } + +#define STAILQ_ENTRY(type) \ +struct { \ + struct type *stqe_next; /* next element */ \ +} + +/* + * Singly-linked Tail queue functions. + */ +#define STAILQ_CONCAT(head1, head2) do { \ + if (!STAILQ_EMPTY((head2))) { \ + *(head1)->stqh_last = (head2)->stqh_first; \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_INIT((head2)); \ + } \ +} while (0) + +#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) + +#define STAILQ_FIRST(head) ((head)->stqh_first) + +#define STAILQ_FOREACH(var, head, field) \ + for((var) = STAILQ_FIRST((head)); \ + (var); \ + (var) = STAILQ_NEXT((var), field)) + +#define STAILQ_INIT(head) do { \ + STAILQ_FIRST((head)) = NULL; \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_NEXT((tqelm), field) = (elm); \ +} while (0) + +#define STAILQ_INSERT_HEAD(head, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_FIRST((head)) = (elm); \ +} while (0) + +#define STAILQ_INSERT_TAIL(head, elm, field) do { \ + STAILQ_NEXT((elm), field) = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ +} while (0) + +#define STAILQ_LAST(head, type, field) \ + (STAILQ_EMPTY((head)) ? \ + NULL : \ + ((struct type *) \ + ((char *)((head)->stqh_last) - __offsetof(struct type, field)))) + +#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) + +#define STAILQ_REMOVE(head, elm, type, field) do { \ + if (STAILQ_FIRST((head)) == (elm)) { \ + STAILQ_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = STAILQ_FIRST((head)); \ + while (STAILQ_NEXT(curelm, field) != (elm)) \ + curelm = STAILQ_NEXT(curelm, field); \ + if ((STAILQ_NEXT(curelm, field) = \ + STAILQ_NEXT(STAILQ_NEXT(curelm, field), field)) == NULL)\ + (head)->stqh_last = &STAILQ_NEXT((curelm), field);\ + } \ +} while (0) + +#define STAILQ_REMOVE_HEAD(head, field) do { \ + if ((STAILQ_FIRST((head)) = \ + STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#define STAILQ_REMOVE_HEAD_UNTIL(head, elm, field) do { \ + if ((STAILQ_FIRST((head)) = STAILQ_NEXT((elm), field)) == NULL) \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +/* + * List declarations. + */ +#define ATH_LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List functions. + */ + +#define LIST_EMPTY(head) ((head)->lh_first == NULL) + +#define LIST_FIRST(head) ((head)->lh_first) + +#define LIST_FOREACH(var, head, field) \ + for ((var) = LIST_FIRST((head)); \ + (var); \ + (var) = LIST_NEXT((var), field)) + +#define LIST_INIT(head) do { \ + LIST_FIRST((head)) = NULL; \ +} while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\ + LIST_NEXT((listelm), field)->field.le_prev = \ + &LIST_NEXT((elm), field); \ + LIST_NEXT((listelm), field) = (elm); \ + (elm)->field.le_prev = &LIST_NEXT((listelm), field); \ +} while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + LIST_NEXT((elm), field) = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &LIST_NEXT((elm), field); \ +} while (0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \ + LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\ + LIST_FIRST((head)) = (elm); \ + (elm)->field.le_prev = &LIST_FIRST((head)); \ +} while (0) + +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_REMOVE(elm, field) do { \ + if (LIST_NEXT((elm), field) != NULL) \ + LIST_NEXT((elm), field)->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = LIST_NEXT((elm), field); \ +} while (0) + +/* + * Tail queue declarations. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ + TRACEBUF \ +} + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ + TRACEBUF \ +} + +/* + * Tail queue functions. + */ +#define TAILQ_CONCAT(head1, head2, field) do { \ + if (!TAILQ_EMPTY(head2)) { \ + *(head1)->tqh_last = (head2)->tqh_first; \ + (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ + (head1)->tqh_last = (head2)->tqh_last; \ + TAILQ_INIT((head2)); \ + QMD_TRACE_HEAD(head); \ + QMD_TRACE_HEAD(head2); \ + } \ +} while (0) + +#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) + +#define TAILQ_FIRST(head) ((head)->tqh_first) + +#define TAILQ_FOREACH(var, head, field) \ + for ((var) = TAILQ_FIRST((head)); \ + (var); \ + (var) = TAILQ_NEXT((var), field)) + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for ((var) = TAILQ_LAST((head), headname); \ + (var); \ + (var) = TAILQ_PREV((var), headname, field)) + +#define TAILQ_INIT(head) do { \ + TAILQ_FIRST((head)) = NULL; \ + (head)->tqh_last = &TAILQ_FIRST((head)); \ + QMD_TRACE_HEAD(head); \ +} while (0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\ + TAILQ_NEXT((elm), field)->field.tqe_prev = \ + &TAILQ_NEXT((elm), field); \ + else { \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + QMD_TRACE_HEAD(head); \ + } \ + TAILQ_NEXT((listelm), field) = (elm); \ + (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \ + QMD_TRACE_ELEM(&(elm)->field); \ + QMD_TRACE_ELEM(&listelm->field); \ +} while (0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + TAILQ_NEXT((elm), field) = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \ + QMD_TRACE_ELEM(&(elm)->field); \ + QMD_TRACE_ELEM(&listelm->field); \ +} while (0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \ + TAILQ_FIRST((head))->field.tqe_prev = \ + &TAILQ_NEXT((elm), field); \ + else \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + TAILQ_FIRST((head)) = (elm); \ + (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \ + QMD_TRACE_HEAD(head); \ + QMD_TRACE_ELEM(&(elm)->field); \ +} while (0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + TAILQ_NEXT((elm), field) = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + QMD_TRACE_HEAD(head); \ + QMD_TRACE_ELEM(&(elm)->field); \ +} while (0) + +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) + +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) + +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) + +#define TAILQ_REMOVE(head, elm, field) do { \ + if ((TAILQ_NEXT((elm), field)) != NULL) \ + TAILQ_NEXT((elm), field)->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else { \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + QMD_TRACE_HEAD(head); \ + } \ + *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \ + TRASHIT((elm)->field.tqe_next); \ + TRASHIT((elm)->field.tqe_prev); \ + QMD_TRACE_ELEM(&(elm)->field); \ +} while (0) + + +#ifdef _KERNEL + +/* + * XXX insque() and remque() are an old way of handling certain queues. + * They bogusly assumes that all queue heads look alike. + */ + +struct quehead { + struct quehead *qh_link; + struct quehead *qh_rlink; +}; + +#ifdef __GNUC__ + +static __inline void +insque(void *a, void *b) +{ + struct quehead *element = (struct quehead *)a, + *head = (struct quehead *)b; + + element->qh_link = head->qh_link; + element->qh_rlink = head; + head->qh_link = element; + element->qh_link->qh_rlink = element; +} + +static __inline void +remque(void *a) +{ + struct quehead *element = (struct quehead *)a; + + element->qh_link->qh_rlink = element->qh_rlink; + element->qh_rlink->qh_link = element->qh_link; + element->qh_rlink = 0; +} + +#else /* !__GNUC__ */ + +void insque(void *a, void *b); +void remque(void *a); + +#endif /* __GNUC__ */ + +#endif /* _KERNEL */ + +#endif /* !_SYS_QUEUE_H_ */ diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/eapol.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/eapol.h --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/eapol.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/eapol.h 2005-02-24 13:06:17.296132608 -0800 @@ -0,0 +1,145 @@ +/*- + * Copyright (c) 2004-2005 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/net80211/ieee80211_var.h,v 1.11 2004/01/15 08:44:27 onoe Exp $ + */ +#ifndef _NET80211_EAPOL_H_ +#define _NET80211_EAPOL_H_ + +/* + * Extendible Authentication Protocol over LAN (EAPOL) aka 802.1x. + */ +struct eapol_hdr { + u_int8_t eapol_ver; /* protocol version */ +#define EAPOL_VERSION 0x01 + u_int8_t eapol_type; /* packet type */ + u_int16_t eapol_len; /* packet body length in bytes */ + /* variable length body follows */ +} __attribute__((__packed__)); + +enum { + EAPOL_TYPE_EAP = 0x00, + EAPOL_TYPE_START = 0x01, + EAPOL_TYPE_LOGOFF = 0x02, + EAPOL_TYPE_KEY = 0x03, + EAPOL_TYPE_EASFA = 0x04 /* Encapsulated ASF Alert */ +}; +#define EAPOL_TYPE_LAST EAPOL_TYPE_EASFA + +/* Ethernet frame type (belongs elsewhere) */ +#define ETH_P_EAPOL 0x888e /* EAPOL PAE/802.1x */ + +/* + * EAPOL key message. + */ +struct eapol_key { + u_int8_t ek_type; /* key type */ +#define EAPOL_KEY_TYPE_RC4 0x01 /* for WEP */ +#define EAPOL_KEY_TYPE_RSN 0x02 /* for 802.11i */ +#define EAPOL_KEY_TYPE_WPA 0xfe /* different struct, see below */ + u_int16_t ek_length; /* frame length */ + u_int64_t ek_replay; /* replay counter */ + u_int8_t ek_iv[16]; /* initialization vector */ + u_int8_t ek_index; /* key index */ +#define EAPOL_KEY_BCAST 0x00 /* broadcast */ +#define EAPOL_KEY_UCAST 0x80 /* unicast */ + u_int8_t ek_sig[16]; /* signature */ + /* variable length data follows */ +} __attribute__((__packed__)); + +/* + * EAPOL WPA/RSN key message. + */ +struct eapol_wpa_key { + u_int8_t ewk_type; /* EAPOL_KEY_TYPE_WPA */ + u_int16_t ewk_info; /* key info */ +#define EAPOL_WKEY_INFO_TYPE 0x0007 +#define EAPOL_WKEY_INFO_MD5 1 /* hmac-md5 & rc4 */ +#define EAPOL_WKEY_INFO_AES 2 /* hmac-sha1 & aes-keywrap */ +#define EAPOL_WKEY_INFO_PW 0x0008 +#define EAPOL_WKEY_INFO_GROUP 0x0000 +#define EAPOL_WKEY_INFO_INDEX 0x0030 /* key index (WPA only) */ +#define EAPOL_WKEY_INFO_INSTALL 0x0040 /* install or tx/rx */ +#define EAPOL_WKEY_INFO_ACK 0x0080 /* STA/AP handshake bit */ +#define EAPOL_WKEY_INFO_MIC 0x0100 /* msg is MIC'd */ +#define EAPOL_WKEY_INFO_SECURE 0x0200 /* pw keys in use */ +#define EAPOL_WKEY_INFO_ERROR 0x0400 /* STA found invalid MIC */ +#define EAPOL_WKEY_INFO_REQUEST 0x0800 /* STA requests new key */ +#define EAPOL_WKEY_INFO_ENCRYPT 0x1000 /* encrypted key data (WPA2 only) */ + u_int16_t ewk_keylen; /* key length */ + u_int64_t ewk_replay; /* replay counter */ + u_int8_t ewk_nonce[32]; + u_int8_t ewk_iv[16]; /* initialization vector */ + u_int64_t ewk_key_rsc; + u_int64_t ewk_key_id; + u_int8_t ewk_mic[16]; + u_int16_t ewk_datalen; + /* variable length data follows */ +} __attribute__((__packed__)); + +/* + * EAP packet format as defined in RFC2284. + */ +struct eap_hdr { + u_int8_t eap_code; /* EAP packet kind */ + u_int8_t eap_id; /* Request/response identifier */ + u_int16_t eap_len; /* Packet length, including header */ + u_int8_t eap_type; /* Request/response protocol type */ + /* variable length data follows */ +} __attribute__((__packed__)); + +enum { + EAP_CODE_REQUEST = 0x01, + EAP_CODE_RESPONSE = 0x02, + EAP_CODE_SUCCESS = 0x03, + EAP_CODE_FAILURE = 0x04, +}; + +enum { + EAP_TYPE_IDENTITY = 0x01, + EAP_TYPE_NOTIFICATION = 0x02, + EAP_TYPE_NAK = 0x03, + EAP_TYPE_MD5_CHALLENGE = 0x04, + EAP_TYPE_EAPTLS = 0x0d, + EAP_TYPE_LEAP = 0x11, +}; + +/* + * The short form for an EAP packet, used in + * SUCCESS/FAILURE messages + */ +struct eap_hdr_short { + u_int8_t eap_code; /* EAP packet kind */ + u_int8_t eap_id; /* Request/response identifier */ + u_int16_t eap_len; /* Packet length, including header */ +} __attribute__((__packed__)); + +#define EAP_IDENTITY_MAXLEN 72 /* max NAI length per RFC2486 */ +#endif /* _NET80211_EAPOL_H_ */ diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211.c --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211.c 2005-02-24 13:06:17.297132456 -0800 @@ -0,0 +1,1005 @@ +/*- + * Copyright (c) 2001 Atsushi Onoe + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EXPORT_SYMTAB +#define EXPORT_SYMTAB +#endif + +__FBSDID("$FreeBSD: src/sys/net80211/ieee80211.c,v 1.9 2004/01/15 08:44:27 onoe Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ieee80211.c,v 1.7 2003/10/16 22:25:00 matt Exp $"); + +/* + * IEEE 802.11 generic handler + */ +#include +#include +#include +#include +#include + +#include "if_media.h" + +#include +#include + +static void ieee80211_watchdog(unsigned long); +static void ieee80211_set11gbasicrates(struct ieee80211_rateset *, + enum ieee80211_phymode); + +static const char *ieee80211_phymode_name[] = { + "auto", /* IEEE80211_MODE_AUTO */ + "11a", /* IEEE80211_MODE_11A */ + "11b", /* IEEE80211_MODE_11B */ + "11g", /* IEEE80211_MODE_11G */ + "FH", /* IEEE80211_MODE_FH */ + "turbo", /* IEEE80211_MODE_TURBO */ +}; + +/* list of all instances */ +SLIST_HEAD(ieee80211_list, ieee80211com); +static struct ieee80211_list ieee80211_list = + SLIST_HEAD_INITIALIZER(ieee80211_list); + +int +ieee80211_ifattach(struct ieee80211com *ic) +{ + struct net_device *dev = ic->ic_dev; + struct ieee80211_channel *c; + int i; + + _MOD_INC_USE(THIS_MODULE, return ENODEV); + + ieee80211_crypto_attach(ic); + + /* + * Fill in 802.11 available channel set, mark + * all available channels as active, and pick + * a default channel if not already specified. + */ + memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail)); + ic->ic_modecaps |= 1<ic_channels[i]; + if (c->ic_flags) { + /* + * Verify driver passed us valid data. + */ + if (i != ieee80211_chan2ieee(ic, c)) { + if_printf(dev, "bad channel ignored; " + "freq %u flags %x number %u\n", + c->ic_freq, c->ic_flags, i); + c->ic_flags = 0; /* NB: remove */ + continue; + } + setbit(ic->ic_chan_avail, i); + /* + * Identify mode capabilities. + */ + if (IEEE80211_IS_CHAN_A(c)) + ic->ic_modecaps |= 1<ic_modecaps |= 1<ic_modecaps |= 1<ic_modecaps |= 1<ic_modecaps |= 1<ic_curmode */ + if ((ic->ic_modecaps & (1<ic_curmode)) == 0) + ic->ic_curmode = IEEE80211_MODE_AUTO; + ic->ic_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */ + + (void) ieee80211_setmode(ic, ic->ic_curmode); + + if (ic->ic_lintval == 0) + ic->ic_lintval = 100; /* default sleep */ + ic->ic_bmisstimeout = 7*ic->ic_lintval; /* default 7 beacons */ + + ieee80211_node_attach(ic); + ieee80211_proto_attach(ic); + + /* XXX lock */ + SLIST_INSERT_HEAD(&ieee80211_list, ic, ic_next); + + init_timer(&ic->ic_slowtimo); + ic->ic_slowtimo.data = (unsigned long) ic; + ic->ic_slowtimo.function = ieee80211_watchdog; + ieee80211_watchdog((unsigned long) ic); /* prime timer */ + /* + * NB: ieee80211_sysctl_register is called by the driver + * since dev->name isn't setup at this point; yech! + */ + return 0; +} +EXPORT_SYMBOL(ieee80211_ifattach); + +void +ieee80211_ifdetach(struct ieee80211com *ic) +{ + + /* XXX lock */ + SLIST_REMOVE(&ieee80211_list, ic, ieee80211com, ic_next); + + del_timer(&ic->ic_slowtimo); + ieee80211_proto_detach(ic); + ieee80211_crypto_detach(ic); + ieee80211_node_detach(ic); + ifmedia_removeall(&ic->ic_media); +#ifdef CONFIG_SYSCTL + ieee80211_sysctl_unregister(ic); +#endif + + _MOD_DEC_USE(THIS_MODULE); +} +EXPORT_SYMBOL(ieee80211_ifdetach); + +/* + * Convert MHz frequency to IEEE channel number. + */ +u_int +ieee80211_mhz2ieee(u_int freq, u_int flags) +{ + if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ + if (freq == 2484) + return 14; + if (freq < 2484) + return (freq - 2407) / 5; + else + return 15 + ((freq - 2512) / 20); + } else if (flags & IEEE80211_CHAN_5GHZ) { /* 5Ghz band */ + return (freq - 5000) / 5; + } else { /* either, guess */ + if (freq == 2484) + return 14; + if (freq < 2484) + return (freq - 2407) / 5; + if (freq < 5000) + return 15 + ((freq - 2512) / 20); + return (freq - 5000) / 5; + } +} +EXPORT_SYMBOL(ieee80211_mhz2ieee); + +/* + * Convert channel to IEEE channel number. + */ +u_int +ieee80211_chan2ieee(struct ieee80211com *ic, struct ieee80211_channel *c) +{ + if (ic->ic_channels <= c && c <= &ic->ic_channels[IEEE80211_CHAN_MAX]) + return c - ic->ic_channels; + else if (c == IEEE80211_CHAN_ANYC) + return IEEE80211_CHAN_ANY; + else if (c != NULL) { + if_printf(ic->ic_dev, "invalid channel freq %u flags %x\n", + c->ic_freq, c->ic_flags); + return 0; /* XXX */ + } else { + if_printf(ic->ic_dev, "invalid channel (NULL)\n"); + return 0; /* XXX */ + } +} +EXPORT_SYMBOL(ieee80211_chan2ieee); + +/* + * Convert IEEE channel number to MHz frequency. + */ +u_int +ieee80211_ieee2mhz(u_int chan, u_int flags) +{ + if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ + if (chan == 14) + return 2484; + if (chan < 14) + return 2407 + chan*5; + else + return 2512 + ((chan-15)*20); + } else if (flags & IEEE80211_CHAN_5GHZ) {/* 5Ghz band */ + return 5000 + (chan*5); + } else { /* either, guess */ + if (chan == 14) + return 2484; + if (chan < 14) /* 0-13 */ + return 2407 + chan*5; + if (chan < 27) /* 15-26 */ + return 2512 + ((chan-15)*20); + return 5000 + (chan*5); + } +} +EXPORT_SYMBOL(ieee80211_ieee2mhz); + +/* + * Setup the media data structures according to the channel and + * rate tables. This must be called by the driver after + * ieee80211_attach and before most anything else. + */ +void +ieee80211_media_init(struct ieee80211com *ic, + ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) +{ +#define ADD(_ic, _s, _o) \ + ifmedia_add(&(_ic)->ic_media, \ + IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL) + struct net_device *dev = ic->ic_dev; + struct ifmediareq imr; + int i, j, mode, rate, maxrate, mword, mopt, r; + struct ieee80211_rateset *rs; + struct ieee80211_rateset allrates; + + /* + * Do late attach work that must wait for any subclass + * (i.e. driver) work such as overriding methods. + */ + ieee80211_node_lateattach(ic); + + /* + * Fill in media characteristics. + */ + ifmedia_init(&ic->ic_media, 0, media_change, media_stat); + maxrate = 0; + memset(&allrates, 0, sizeof(allrates)); + for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_MAX; mode++) { + static const u_int mopts[] = { + IFM_AUTO, + IFM_IEEE80211_11A, + IFM_IEEE80211_11B, + IFM_IEEE80211_11G, + IFM_IEEE80211_FH, + IFM_IEEE80211_11A | IFM_IEEE80211_TURBO, + }; + if ((ic->ic_modecaps & (1<ic_caps & IEEE80211_C_IBSS) + ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_ADHOC); + if (ic->ic_caps & IEEE80211_C_HOSTAP) + ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_HOSTAP); + if (ic->ic_caps & IEEE80211_C_AHDEMO) + ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); + if (ic->ic_caps & IEEE80211_C_MONITOR) + ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_MONITOR); + if (mode == IEEE80211_MODE_AUTO) + continue; + rs = &ic->ic_sup_rates[mode]; + for (i = 0; i < rs->rs_nrates; i++) { + rate = rs->rs_rates[i]; + mword = ieee80211_rate2media(ic, rate, mode); + if (mword == 0) + continue; + ADD(ic, mword, mopt); + if (ic->ic_caps & IEEE80211_C_IBSS) + ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC); + if (ic->ic_caps & IEEE80211_C_HOSTAP) + ADD(ic, mword, mopt | IFM_IEEE80211_HOSTAP); + if (ic->ic_caps & IEEE80211_C_AHDEMO) + ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); + if (ic->ic_caps & IEEE80211_C_MONITOR) + ADD(ic, mword, mopt | IFM_IEEE80211_MONITOR); + /* + * Add rate to the collection of all rates. + */ + r = rate & IEEE80211_RATE_VAL; + for (j = 0; j < allrates.rs_nrates; j++) + if (allrates.rs_rates[j] == r) + break; + if (j == allrates.rs_nrates) { + /* unique, add to the set */ + allrates.rs_rates[j] = r; + allrates.rs_nrates++; + } + rate = (rate & IEEE80211_RATE_VAL) / 2; + if (rate > maxrate) + maxrate = rate; + } + } + for (i = 0; i < allrates.rs_nrates; i++) { + mword = ieee80211_rate2media(ic, allrates.rs_rates[i], + IEEE80211_MODE_AUTO); + if (mword == 0) + continue; + mword = IFM_SUBTYPE(mword); /* remove media options */ + ADD(ic, mword, 0); + if (ic->ic_caps & IEEE80211_C_IBSS) + ADD(ic, mword, IFM_IEEE80211_ADHOC); + if (ic->ic_caps & IEEE80211_C_HOSTAP) + ADD(ic, mword, IFM_IEEE80211_HOSTAP); + if (ic->ic_caps & IEEE80211_C_AHDEMO) + ADD(ic, mword, IFM_IEEE80211_ADHOC | IFM_FLAG0); + if (ic->ic_caps & IEEE80211_C_MONITOR) + ADD(ic, mword, IFM_IEEE80211_MONITOR); + } + ieee80211_media_status(dev, &imr); + ifmedia_set(&ic->ic_media, imr.ifm_active); +#ifndef __linux__ + if (maxrate) + ifp->if_baudrate = IF_Mbps(maxrate); +#endif +#undef ADD +} +EXPORT_SYMBOL(ieee80211_media_init); + +void +ieee80211_announce(struct ieee80211com *ic) +{ + struct net_device *dev = ic->ic_dev; + int i, mode, rate, mword; + struct ieee80211_rateset *rs; + + for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_MAX; mode++) { + if ((ic->ic_modecaps & (1<ic_sup_rates[mode]; + for (i = 0; i < rs->rs_nrates; i++) { + rate = rs->rs_rates[i]; + mword = ieee80211_rate2media(ic, rate, mode); + if (mword == 0) + continue; + printf("%s%d%sMbps", (i != 0 ? " " : ""), + (rate & IEEE80211_RATE_VAL) / 2, + ((rate & 0x1) != 0 ? ".5" : "")); + } + printf("\n"); + } +} +EXPORT_SYMBOL(ieee80211_announce); + +static int +findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate) +{ +#define IEEERATE(_ic,_m,_i) \ + ((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL) + int i, nrates = ic->ic_sup_rates[mode].rs_nrates; + for (i = 0; i < nrates; i++) + if (IEEERATE(ic, mode, i) == rate) + return i; + return -1; +#undef IEEERATE +} + +/* + * Find an instance by it's mac address. + */ +struct ieee80211com * +ieee80211_find_vap(const u_int8_t mac[IEEE80211_ADDR_LEN]) +{ + struct ieee80211com *ic; + + /* XXX lock */ + SLIST_FOREACH(ic, &ieee80211_list, ic_next) + if (IEEE80211_ADDR_EQ(mac, ic->ic_myaddr)) + return ic; + return NULL; +} +EXPORT_SYMBOL(ieee80211_find_vap); + +static struct ieee80211com * +ieee80211_find_instance(struct net_device *dev) +{ + struct ieee80211com *ic; + + /* XXX lock */ + /* XXX not right for multiple instances but works for now */ + SLIST_FOREACH(ic, &ieee80211_list, ic_next) + if (ic->ic_dev == dev) + return ic; + return NULL; +} + +/* + * Handle a media change request. + */ +int +ieee80211_media_change(struct net_device *dev) +{ + struct ieee80211com *ic; + struct ifmedia_entry *ime; + enum ieee80211_opmode newopmode; + enum ieee80211_phymode newphymode; + int i, j, newrate, error = 0; + + ic = ieee80211_find_instance(dev); + if (!ic) { + if_printf(dev, "%s: no 802.11 instance!\n", __func__); + return EINVAL; + } + ime = ic->ic_media.ifm_cur; + /* + * First, identify the phy mode. + */ + switch (IFM_MODE(ime->ifm_media)) { + case IFM_IEEE80211_11A: + newphymode = IEEE80211_MODE_11A; + break; + case IFM_IEEE80211_11B: + newphymode = IEEE80211_MODE_11B; + break; + case IFM_IEEE80211_11G: + newphymode = IEEE80211_MODE_11G; + break; + case IFM_IEEE80211_FH: + newphymode = IEEE80211_MODE_FH; + break; + case IFM_AUTO: + newphymode = IEEE80211_MODE_AUTO; + break; + default: + return EINVAL; + } + /* + * Turbo mode is an ``option''. Eventually it + * needs to be applied to 11g too. + */ + if (ime->ifm_media & IFM_IEEE80211_TURBO) { + if (newphymode != IEEE80211_MODE_11A) + return EINVAL; + newphymode = IEEE80211_MODE_TURBO; + } + /* + * Validate requested mode is available. + */ + if ((ic->ic_modecaps & (1<ifm_media) != IFM_AUTO) { + /* + * Convert media subtype to rate. + */ + newrate = ieee80211_media2rate(ime->ifm_media); + if (newrate == 0) + return EINVAL; + /* + * Check the rate table for the specified/current phy. + */ + if (newphymode == IEEE80211_MODE_AUTO) { + /* + * In autoselect mode search for the rate. + */ + for (j = IEEE80211_MODE_11A; + j < IEEE80211_MODE_MAX; j++) { + if ((ic->ic_modecaps & (1<ifm_media & (IFM_IEEE80211_ADHOC|IFM_FLAG0)) == + (IFM_IEEE80211_ADHOC|IFM_FLAG0)) + newopmode = IEEE80211_M_AHDEMO; + else if (ime->ifm_media & IFM_IEEE80211_HOSTAP) + newopmode = IEEE80211_M_HOSTAP; + else if (ime->ifm_media & IFM_IEEE80211_ADHOC) + newopmode = IEEE80211_M_IBSS; + else if (ime->ifm_media & IFM_IEEE80211_MONITOR) + newopmode = IEEE80211_M_MONITOR; + else + newopmode = IEEE80211_M_STA; + + /* + * Autoselect doesn't make sense when operating as an AP. + * If no phy mode has been selected, pick one and lock it + * down so rate tables can be used in forming beacon frames + * and the like. + */ + if (newopmode == IEEE80211_M_HOSTAP && + newphymode == IEEE80211_MODE_AUTO) { + for (j = IEEE80211_MODE_11A; j < IEEE80211_MODE_MAX; j++) + if (ic->ic_modecaps & (1<ic_curmode != newphymode) { /* change phy mode */ + error = ieee80211_setmode(ic, newphymode); + if (error != 0) + return error; + error = ENETRESET; + } + + /* + * Committed to changes, install the rate setting. + */ + if (ic->ic_fixed_rate != i) { + ic->ic_fixed_rate = i; /* set fixed tx rate */ + error = ENETRESET; + } + + /* + * Handle operating mode change. + */ + if (ic->ic_opmode != newopmode) { + ic->ic_opmode = newopmode; + switch (newopmode) { + case IEEE80211_M_AHDEMO: + case IEEE80211_M_HOSTAP: + case IEEE80211_M_STA: + case IEEE80211_M_MONITOR: + ic->ic_flags &= ~IEEE80211_F_IBSSON; + break; + case IEEE80211_M_IBSS: + ic->ic_flags |= IEEE80211_F_IBSSON; + break; + } + /* + * Yech, slot time may change depending on the + * operating mode so reset it to be sure everything + * is setup appropriately. + */ + ieee80211_reset_erp(ic); + error = ENETRESET; + } +#ifdef notdef + if (error == 0) + ifp->if_baudrate = ifmedia_baudrate(ime->ifm_media); +#endif + return error; +} +EXPORT_SYMBOL(ieee80211_media_change); + +void +ieee80211_media_status(struct net_device *dev, struct ifmediareq *imr) +{ + struct ieee80211com *ic; + struct ieee80211_rateset *rs; + + ic = ieee80211_find_instance(dev); + if (!ic) { + if_printf(dev, "%s: no 802.11 instance!\n", __func__); + return; + } + imr->ifm_status = IFM_AVALID; + imr->ifm_active = IFM_IEEE80211; + if (ic->ic_state == IEEE80211_S_RUN) + imr->ifm_status |= IFM_ACTIVE; + /* + * Calculate a current rate if possible. + */ + if (ic->ic_fixed_rate != -1) { + /* + * A fixed rate is set, report that. + */ + rs = &ic->ic_sup_rates[ic->ic_curmode]; + imr->ifm_active |= ieee80211_rate2media(ic, + rs->rs_rates[ic->ic_fixed_rate], ic->ic_curmode); + } else if (ic->ic_opmode == IEEE80211_M_STA) { + /* + * In station mode report the current transmit rate. + */ + rs = &ic->ic_bss->ni_rates; + imr->ifm_active |= ieee80211_rate2media(ic, + rs->rs_rates[ic->ic_bss->ni_txrate], ic->ic_curmode); + } else + imr->ifm_active |= IFM_AUTO; + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + break; + case IEEE80211_M_IBSS: + imr->ifm_active |= IFM_IEEE80211_ADHOC; + break; + case IEEE80211_M_AHDEMO: + /* should not come here */ + break; + case IEEE80211_M_HOSTAP: + imr->ifm_active |= IFM_IEEE80211_HOSTAP; + break; + case IEEE80211_M_MONITOR: + imr->ifm_active |= IFM_IEEE80211_MONITOR; + break; + } + switch (ic->ic_curmode) { + case IEEE80211_MODE_11A: + imr->ifm_active |= IFM_IEEE80211_11A; + break; + case IEEE80211_MODE_11B: + imr->ifm_active |= IFM_IEEE80211_11B; + break; + case IEEE80211_MODE_11G: + imr->ifm_active |= IFM_IEEE80211_11G; + break; + case IEEE80211_MODE_FH: + imr->ifm_active |= IFM_IEEE80211_FH; + break; + case IEEE80211_MODE_TURBO: + imr->ifm_active |= IFM_IEEE80211_11A + | IFM_IEEE80211_TURBO; + break; + } +} +EXPORT_SYMBOL(ieee80211_media_status); + +static void +ieee80211_watchdog(unsigned long data) +{ + struct ieee80211com *ic = (struct ieee80211com *) data; + + if (ic->ic_state != IEEE80211_S_INIT) { + if (ic->ic_mgt_timer && --ic->ic_mgt_timer == 0) + ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); + if (ic->ic_inact_timer && --ic->ic_inact_timer == 0) + ieee80211_timeout_nodes(ic); + } + ic->ic_slowtimo.expires = jiffies + HZ; /* once a second */ + add_timer(&ic->ic_slowtimo); +} + +/* + * Mark the basic rates for the 11g rate table based on the + * operating mode. For real 11g we mark all the 11b rates + * and 6, 12, and 24 OFDM. For 11b compatibility we mark only + * 11b rates. There's also a pseudo 11a-mode used to mark only + * the basic OFDM rates. + */ +static void +ieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode mode) +{ + static const struct ieee80211_rateset basic[] = { + { 0 }, /* IEEE80211_MODE_AUTO */ + { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_11A */ + { 2, { 2, 4 } }, /* IEEE80211_MODE_11B */ + { 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11G (mixed b/g) */ + { 0 }, /* IEEE80211_MODE_FH */ + /* IEEE80211_MODE_PUREG (not yet) */ + { 7, { 2, 4, 11, 22, 12, 24, 48 } }, + }; + int i, j; + + for (i = 0; i < rs->rs_nrates; i++) { + rs->rs_rates[i] &= IEEE80211_RATE_VAL; + for (j = 0; j < basic[mode].rs_nrates; j++) + if (basic[mode].rs_rates[j] == rs->rs_rates[i]) { + rs->rs_rates[i] |= IEEE80211_RATE_BASIC; + break; + } + } +} + +/* + * Set the current phy mode and recalculate the active channel + * set based on the available channels for this mode. Also + * select a new default/current channel if the current one is + * inappropriate for this mode. + */ +int +ieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + static const u_int chanflags[] = { + 0, /* IEEE80211_MODE_AUTO */ + IEEE80211_CHAN_A, /* IEEE80211_MODE_11A */ + IEEE80211_CHAN_B, /* IEEE80211_MODE_11B */ + IEEE80211_CHAN_PUREG, /* IEEE80211_MODE_11G */ + IEEE80211_CHAN_FHSS, /* IEEE80211_MODE_FH */ + IEEE80211_CHAN_T, /* IEEE80211_MODE_TURBO */ + }; + struct ieee80211_channel *c; + u_int modeflags; + int i; + + /* validate new mode */ + if ((ic->ic_modecaps & (1<ic_modecaps)); + return EINVAL; + } + + /* + * Verify at least one channel is present in the available + * channel list before committing to the new mode. + */ + KASSERT(mode < N(chanflags), ("Unexpected mode %u", mode)); + modeflags = chanflags[mode]; + for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { + c = &ic->ic_channels[i]; + if (mode == IEEE80211_MODE_AUTO) { + /* ignore turbo channels for autoselect */ + if ((c->ic_flags &~ IEEE80211_CHAN_TURBO) != 0) + break; + } else { + if ((c->ic_flags & modeflags) == modeflags) + break; + } + } + if (i > IEEE80211_CHAN_MAX) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + ("%s: no channels found for mode %u\n", __func__, mode)); + return EINVAL; + } + + /* + * Calculate the active channel set. + */ + memset(ic->ic_chan_active, 0, sizeof(ic->ic_chan_active)); + for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { + c = &ic->ic_channels[i]; + if (mode == IEEE80211_MODE_AUTO) { + /* take anything but pure turbo channels */ + if ((c->ic_flags &~ IEEE80211_CHAN_TURBO) != 0) + setbit(ic->ic_chan_active, i); + } else { + if ((c->ic_flags & modeflags) == modeflags) + setbit(ic->ic_chan_active, i); + } + } + /* + * If no current/default channel is setup or the current + * channel is wrong for the mode then pick the first + * available channel from the active list. This is likely + * not the right one. + */ + if (ic->ic_ibss_chan == NULL || + isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) { + for (i = 0; i <= IEEE80211_CHAN_MAX; i++) + if (isset(ic->ic_chan_active, i)) { + ic->ic_ibss_chan = &ic->ic_channels[i]; + break; + } + KASSERT(ic->ic_ibss_chan != NULL && + isset(ic->ic_chan_active, + ieee80211_chan2ieee(ic, ic->ic_ibss_chan)), + ("Bad IBSS channel %u", + ieee80211_chan2ieee(ic, ic->ic_ibss_chan))); + } + /* + * If the desired channel is set but no longer valid then reset it. + */ + if (ic->ic_des_chan != IEEE80211_CHAN_ANYC && + isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ic->ic_des_chan))) + ic->ic_des_chan = IEEE80211_CHAN_ANYC; + + /* + * Do mode-specific rate setup. + */ + if (mode == IEEE80211_MODE_11G) { + /* + * Use a mixed 11b/11g rate set. + */ + ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode], + IEEE80211_MODE_11G); + } else if (mode == IEEE80211_MODE_11B) { + /* + * Force pure 11b rate set. + */ + ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode], + IEEE80211_MODE_11B); + } + /* + * Setup an initial rate set according to the + * current/default channel selected above. This + * will be changed when scanning but must exist + * now so driver have a consistent state of ic_ibss_chan. + */ + if (ic->ic_bss) /* NB: can be called before lateattach */ + ic->ic_bss->ni_rates = ic->ic_sup_rates[mode]; + + ic->ic_curmode = mode; + ieee80211_reset_erp(ic); /* reset ERP state */ + + return 0; +#undef N +} +EXPORT_SYMBOL(ieee80211_setmode); + +/* + * Reset 11g-related state. + */ +void +ieee80211_reset_erp(struct ieee80211com *ic) +{ + ic->ic_flags &= ~IEEE80211_F_USEPROT; + ic->ic_nonerpsta = 0; + ic->ic_longslotsta = 0; + /* + * Short slot time is enabled only when operating in 11g + * and not in an IBSS. We must also honor whether or not + * the driver is capable of doing it. + */ + ieee80211_set_shortslottime(ic, + ic->ic_curmode == IEEE80211_MODE_11A || + (ic->ic_curmode == IEEE80211_MODE_11G && + ic->ic_opmode == IEEE80211_M_HOSTAP && + (ic->ic_caps & IEEE80211_C_SHSLOT))); + /* + * Set short preamble and ERP barker-preamble flags. + */ + if (ic->ic_curmode == IEEE80211_MODE_11A || + (ic->ic_caps & IEEE80211_C_SHPREAMBLE)) { + ic->ic_flags |= IEEE80211_F_SHPREAMBLE; + ic->ic_flags &= ~IEEE80211_F_USEBARKER; + } else { + ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE; + ic->ic_flags |= IEEE80211_F_USEBARKER; + } +} + +/* + * Return the phy mode for with the specified channel so the + * caller can select a rate set. This is problematic for channels + * where multiple operating modes are possible (e.g. 11g+11b). + * In those cases we defer to the current operating mode when set. + */ +enum ieee80211_phymode +ieee80211_chan2mode(struct ieee80211com *ic, struct ieee80211_channel *chan) +{ + if (IEEE80211_IS_CHAN_5GHZ(chan)) { + /* + * This assumes all 11a turbo channels are also + * usable withut turbo, which is currently true. + */ + if (ic->ic_curmode == IEEE80211_MODE_TURBO) + return IEEE80211_MODE_TURBO; + return IEEE80211_MODE_11A; + } else if (IEEE80211_IS_CHAN_FHSS(chan)) + return IEEE80211_MODE_FH; + else if (chan->ic_flags & (IEEE80211_CHAN_OFDM|IEEE80211_CHAN_DYN)) { + /* + * This assumes all 11g channels are also usable + * for 11b, which is currently true. + */ + if (ic->ic_curmode == IEEE80211_MODE_11B) + return IEEE80211_MODE_11B; + return IEEE80211_MODE_11G; + } else + return IEEE80211_MODE_11B; +} +EXPORT_SYMBOL(ieee80211_chan2mode); + +/* + * convert IEEE80211 rate value to ifmedia subtype. + * ieee80211 rate is in unit of 0.5Mbps. + */ +int +ieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode mode) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + static const struct { + u_int m; /* rate + mode */ + u_int r; /* if_media rate */ + } rates[] = { + { 2 | IFM_IEEE80211_FH, IFM_IEEE80211_FH1 }, + { 4 | IFM_IEEE80211_FH, IFM_IEEE80211_FH2 }, + { 2 | IFM_IEEE80211_11B, IFM_IEEE80211_DS1 }, + { 4 | IFM_IEEE80211_11B, IFM_IEEE80211_DS2 }, + { 11 | IFM_IEEE80211_11B, IFM_IEEE80211_DS5 }, + { 22 | IFM_IEEE80211_11B, IFM_IEEE80211_DS11 }, + { 44 | IFM_IEEE80211_11B, IFM_IEEE80211_DS22 }, + { 12 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM6 }, + { 18 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM9 }, + { 24 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM12 }, + { 36 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM18 }, + { 48 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM24 }, + { 72 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM36 }, + { 96 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM48 }, + { 108 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM54 }, + { 2 | IFM_IEEE80211_11G, IFM_IEEE80211_DS1 }, + { 4 | IFM_IEEE80211_11G, IFM_IEEE80211_DS2 }, + { 11 | IFM_IEEE80211_11G, IFM_IEEE80211_DS5 }, + { 22 | IFM_IEEE80211_11G, IFM_IEEE80211_DS11 }, + { 12 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM6 }, + { 18 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM9 }, + { 24 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM12 }, + { 36 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM18 }, + { 48 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM24 }, + { 72 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM36 }, + { 96 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM48 }, + { 108 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM54 }, + /* NB: OFDM72 doesn't realy exist so we don't handle it */ + }; + u_int mask, i; + + mask = rate & IEEE80211_RATE_VAL; + switch (mode) { + case IEEE80211_MODE_11A: + case IEEE80211_MODE_TURBO: + mask |= IFM_IEEE80211_11A; + break; + case IEEE80211_MODE_11B: + mask |= IFM_IEEE80211_11B; + break; + case IEEE80211_MODE_FH: + mask |= IFM_IEEE80211_FH; + break; + case IEEE80211_MODE_AUTO: + /* NB: ic may be NULL for some drivers */ + if (ic && ic->ic_phytype == IEEE80211_T_FH) { + mask |= IFM_IEEE80211_FH; + break; + } + /* NB: hack, 11g matches both 11b+11a rates */ + /* fall thru... */ + case IEEE80211_MODE_11G: + mask |= IFM_IEEE80211_11G; + break; + } + for (i = 0; i < N(rates); i++) + if (rates[i].m == mask) + return rates[i].r; + return IFM_AUTO; +#undef N +} +EXPORT_SYMBOL(ieee80211_rate2media); + +int +ieee80211_media2rate(int mword) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + static const int ieeerates[] = { + -1, /* IFM_AUTO */ + 0, /* IFM_MANUAL */ + 0, /* IFM_NONE */ + 2, /* IFM_IEEE80211_FH1 */ + 4, /* IFM_IEEE80211_FH2 */ + 2, /* IFM_IEEE80211_DS1 */ + 4, /* IFM_IEEE80211_DS2 */ + 11, /* IFM_IEEE80211_DS5 */ + 22, /* IFM_IEEE80211_DS11 */ + 44, /* IFM_IEEE80211_DS22 */ + 12, /* IFM_IEEE80211_OFDM6 */ + 18, /* IFM_IEEE80211_OFDM9 */ + 24, /* IFM_IEEE80211_OFDM12 */ + 36, /* IFM_IEEE80211_OFDM18 */ + 48, /* IFM_IEEE80211_OFDM24 */ + 72, /* IFM_IEEE80211_OFDM36 */ + 96, /* IFM_IEEE80211_OFDM48 */ + 108, /* IFM_IEEE80211_OFDM54 */ + 144, /* IFM_IEEE80211_OFDM72 */ + }; + return IFM_SUBTYPE(mword) < N(ieeerates) ? + ieeerates[IFM_SUBTYPE(mword)] : 0; +#undef N +} +EXPORT_SYMBOL(ieee80211_media2rate); diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211.h --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211.h 2005-02-24 13:06:17.298132304 -0800 @@ -0,0 +1,622 @@ +/* $NetBSD: ieee80211.h,v 1.4 2003/10/15 11:43:51 dyoung Exp $ */ +/*- + * Copyright (c) 2001 Atsushi Onoe + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/net80211/ieee80211.h,v 1.4 2004/01/13 06:22:55 sam Exp $ + */ +#ifndef _NET80211_IEEE80211_H_ +#define _NET80211_IEEE80211_H_ + +/* + * 802.11 protocol definitions. + */ + +#define IEEE80211_ADDR_LEN 6 /* size of 802.11 address */ +/* is 802.11 address multicast/broadcast? */ +#define IEEE80211_IS_MULTICAST(_a) (*(_a) & 0x01) + +/* IEEE 802.11 PLCP header */ +struct ieee80211_plcp_hdr { + u_int16_t i_sfd; + u_int8_t i_signal; + u_int8_t i_service; + u_int16_t i_length; + u_int16_t i_crc; +} __packed; + +#define IEEE80211_PLCP_SFD 0xF3A0 +#define IEEE80211_PLCP_SERVICE 0x00 + +/* + * generic definitions for IEEE 802.11 frames + */ +struct ieee80211_frame { + u_int8_t i_fc[2]; + u_int8_t i_dur[2]; + u_int8_t i_addr1[IEEE80211_ADDR_LEN]; + u_int8_t i_addr2[IEEE80211_ADDR_LEN]; + u_int8_t i_addr3[IEEE80211_ADDR_LEN]; + u_int8_t i_seq[2]; + /* possibly followed by addr4[IEEE80211_ADDR_LEN]; */ + /* see below */ +} __packed; + +struct ieee80211_qosframe { + u_int8_t i_fc[2]; + u_int8_t i_dur[2]; + u_int8_t i_addr1[IEEE80211_ADDR_LEN]; + u_int8_t i_addr2[IEEE80211_ADDR_LEN]; + u_int8_t i_addr3[IEEE80211_ADDR_LEN]; + u_int8_t i_seq[2]; + u_int8_t i_qos[2]; + /* possibly followed by addr4[IEEE80211_ADDR_LEN]; */ + /* see below */ +} __packed; + +struct ieee80211_qoscntl { + u_int8_t i_qos[2]; +}; + +struct ieee80211_frame_addr4 { + u_int8_t i_fc[2]; + u_int8_t i_dur[2]; + u_int8_t i_addr1[IEEE80211_ADDR_LEN]; + u_int8_t i_addr2[IEEE80211_ADDR_LEN]; + u_int8_t i_addr3[IEEE80211_ADDR_LEN]; + u_int8_t i_seq[2]; + u_int8_t i_addr4[IEEE80211_ADDR_LEN]; +} __packed; + + +struct ieee80211_qosframe_addr4 { + u_int8_t i_fc[2]; + u_int8_t i_dur[2]; + u_int8_t i_addr1[IEEE80211_ADDR_LEN]; + u_int8_t i_addr2[IEEE80211_ADDR_LEN]; + u_int8_t i_addr3[IEEE80211_ADDR_LEN]; + u_int8_t i_seq[2]; + u_int8_t i_addr4[IEEE80211_ADDR_LEN]; + u_int8_t i_qos[2]; +} __packed; + +#define IEEE80211_FC0_VERSION_MASK 0x03 +#define IEEE80211_FC0_VERSION_SHIFT 0 +#define IEEE80211_FC0_VERSION_0 0x00 +#define IEEE80211_FC0_TYPE_MASK 0x0c +#define IEEE80211_FC0_TYPE_SHIFT 2 +#define IEEE80211_FC0_TYPE_MGT 0x00 +#define IEEE80211_FC0_TYPE_CTL 0x04 +#define IEEE80211_FC0_TYPE_DATA 0x08 + +#define IEEE80211_FC0_SUBTYPE_MASK 0xf0 +#define IEEE80211_FC0_SUBTYPE_SHIFT 4 +/* for TYPE_MGT */ +#define IEEE80211_FC0_SUBTYPE_ASSOC_REQ 0x00 +#define IEEE80211_FC0_SUBTYPE_ASSOC_RESP 0x10 +#define IEEE80211_FC0_SUBTYPE_REASSOC_REQ 0x20 +#define IEEE80211_FC0_SUBTYPE_REASSOC_RESP 0x30 +#define IEEE80211_FC0_SUBTYPE_PROBE_REQ 0x40 +#define IEEE80211_FC0_SUBTYPE_PROBE_RESP 0x50 +#define IEEE80211_FC0_SUBTYPE_BEACON 0x80 +#define IEEE80211_FC0_SUBTYPE_ATIM 0x90 +#define IEEE80211_FC0_SUBTYPE_DISASSOC 0xa0 +#define IEEE80211_FC0_SUBTYPE_AUTH 0xb0 +#define IEEE80211_FC0_SUBTYPE_DEAUTH 0xc0 +/* for TYPE_CTL */ +#define IEEE80211_FC0_SUBTYPE_PS_POLL 0xa0 +#define IEEE80211_FC0_SUBTYPE_RTS 0xb0 +#define IEEE80211_FC0_SUBTYPE_CTS 0xc0 +#define IEEE80211_FC0_SUBTYPE_ACK 0xd0 +#define IEEE80211_FC0_SUBTYPE_CF_END 0xe0 +#define IEEE80211_FC0_SUBTYPE_CF_END_ACK 0xf0 +/* for TYPE_DATA (bit combination) */ +#define IEEE80211_FC0_SUBTYPE_DATA 0x00 +#define IEEE80211_FC0_SUBTYPE_CF_ACK 0x10 +#define IEEE80211_FC0_SUBTYPE_CF_POLL 0x20 +#define IEEE80211_FC0_SUBTYPE_CF_ACPL 0x30 +#define IEEE80211_FC0_SUBTYPE_NODATA 0x40 +#define IEEE80211_FC0_SUBTYPE_CFACK 0x50 +#define IEEE80211_FC0_SUBTYPE_CFPOLL 0x60 +#define IEEE80211_FC0_SUBTYPE_CF_ACK_CF_ACK 0x70 +#define IEEE80211_FC0_SUBTYPE_QOS 0x80 +#define IEEE80211_FC0_SUBTYPE_QOS_NULL 0xc0 + +#define IEEE80211_FC1_DIR_MASK 0x03 +#define IEEE80211_FC1_DIR_NODS 0x00 /* STA->STA */ +#define IEEE80211_FC1_DIR_TODS 0x01 /* STA->AP */ +#define IEEE80211_FC1_DIR_FROMDS 0x02 /* AP ->STA */ +#define IEEE80211_FC1_DIR_DSTODS 0x03 /* AP ->AP */ + +#define IEEE80211_FC1_MORE_FRAG 0x04 +#define IEEE80211_FC1_RETRY 0x08 +#define IEEE80211_FC1_PWR_MGT 0x10 +#define IEEE80211_FC1_MORE_DATA 0x20 +#define IEEE80211_FC1_WEP 0x40 +#define IEEE80211_FC1_ORDER 0x80 + +#define IEEE80211_SEQ_FRAG_MASK 0x000f +#define IEEE80211_SEQ_FRAG_SHIFT 0 +#define IEEE80211_SEQ_SEQ_MASK 0xfff0 +#define IEEE80211_SEQ_SEQ_SHIFT 4 + +#define IEEE80211_NWID_LEN 32 + +#define IEEE80211_QOS_TXOP 0x00ff +/* bit 8 is reserved */ +#define IEEE80211_QOS_ACKPOLICY 0x0600 +#define IEEE80211_QOS_ESOP 0x0800 +#define IEEE80211_QOS_TID 0xf000 + +/* does frame have QoS sequence control data */ +#define IEEE80211_QOS_HAS_SEQ(wh) \ + (((wh)->i_fc[0] & \ + (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_QOS)) == \ + (IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_QOS)) + +/* + * WME/802.11e information element. + */ +struct ieee80211_ie_wme { + u_int8_t wme_id; /* IEEE80211_ELEMID_VENDOR */ + u_int8_t wme_len; /* length in bytes */ + u_int8_t wme_oui[3]; /* 0x00, 0x50, 0xf2 */ + u_int8_t wme_type; /* OUI type */ + u_int8_t wme_subtype; /* OUI subtype */ + u_int8_t wme_version; /* spec revision */ + u_int8_t wme_info; /* AC info */ +} __packed; + +/* + * WME/802.11e Tspec Element + */ +struct ieee80211_wme_tspec { + u_int8_t ts_id; + u_int8_t ts_len; + u_int8_t ts_oui[3]; + u_int8_t ts_oui_type; + u_int8_t ts_oui_subtype; + u_int8_t ts_version; + u_int8_t ts_tsinfo[3]; + u_int8_t ts_nom_msdu[2]; + u_int8_t ts_max_msdu[2]; + u_int8_t ts_min_svc[4]; + u_int8_t ts_max_svc[4]; + u_int8_t ts_inactv_intv[4]; + u_int8_t ts_susp_intv[4]; + u_int8_t ts_start_svc[4]; + u_int8_t ts_min_rate[4]; + u_int8_t ts_mean_rate[4]; + u_int8_t ts_max_burst[4]; + u_int8_t ts_min_phy[4]; + u_int8_t ts_peak_rate[4]; + u_int8_t ts_delay[4]; + u_int8_t ts_surplus[2]; + u_int8_t ts_medium_time[2]; +} __packed; + +/* + * Management Notification Frame + */ +struct ieee80211_mnf { + u_int8_t mnf_category; + u_int8_t mnf_action; + u_int8_t mnf_dialog; + u_int8_t mnf_status; +} __packed; +#define MNF_SETUP_REQ 0 +#define MNF_SETUP_RESP 1 +#define MNF_TEARDOWN 2 + +/* + * Control frames. + */ +struct ieee80211_frame_min { + u_int8_t i_fc[2]; + u_int8_t i_dur[2]; + u_int8_t i_addr1[IEEE80211_ADDR_LEN]; + u_int8_t i_addr2[IEEE80211_ADDR_LEN]; + /* FCS */ +} __packed; + +struct ieee80211_frame_rts { + u_int8_t i_fc[2]; + u_int8_t i_dur[2]; + u_int8_t i_ra[IEEE80211_ADDR_LEN]; + u_int8_t i_ta[IEEE80211_ADDR_LEN]; + /* FCS */ +} __packed; + +struct ieee80211_frame_cts { + u_int8_t i_fc[2]; + u_int8_t i_dur[2]; + u_int8_t i_ra[IEEE80211_ADDR_LEN]; + /* FCS */ +} __packed; + +struct ieee80211_frame_ack { + u_int8_t i_fc[2]; + u_int8_t i_dur[2]; + u_int8_t i_ra[IEEE80211_ADDR_LEN]; + /* FCS */ +} __packed; + +struct ieee80211_frame_pspoll { + u_int8_t i_fc[2]; + u_int8_t i_aid[2]; + u_int8_t i_bssid[IEEE80211_ADDR_LEN]; + u_int8_t i_ta[IEEE80211_ADDR_LEN]; + /* FCS */ +} __packed; + +struct ieee80211_frame_cfend { /* NB: also CF-End+CF-Ack */ + u_int8_t i_fc[2]; + u_int8_t i_dur[2]; /* should be zero */ + u_int8_t i_ra[IEEE80211_ADDR_LEN]; + u_int8_t i_bssid[IEEE80211_ADDR_LEN]; + /* FCS */ +} __packed; + +/* + * BEACON management packets + * + * octet timestamp[8] + * octet beacon interval[2] + * octet capability information[2] + * information element + * octet elemid + * octet length + * octet information[length] + */ + +typedef u_int8_t *ieee80211_mgt_beacon_t; + +#define IEEE80211_BEACON_INTERVAL(beacon) \ + ((beacon)[8] | ((beacon)[9] << 8)) +#define IEEE80211_BEACON_CAPABILITY(beacon) \ + ((beacon)[10] | ((beacon)[11] << 8)) + +#define IEEE80211_CAPINFO_ESS 0x0001 +#define IEEE80211_CAPINFO_IBSS 0x0002 +#define IEEE80211_CAPINFO_CF_POLLABLE 0x0004 +#define IEEE80211_CAPINFO_CF_POLLREQ 0x0008 +#define IEEE80211_CAPINFO_PRIVACY 0x0010 +#define IEEE80211_CAPINFO_SHORT_PREAMBLE 0x0020 +#define IEEE80211_CAPINFO_PBCC 0x0040 +#define IEEE80211_CAPINFO_CHNL_AGILITY 0x0080 +/* bits 8-9 are reserved */ +#define IEEE80211_CAPINFO_SHORT_SLOTTIME 0x0400 +#define IEEE80211_CAPINFO_RSN 0x0800 +/* bit 12 is reserved */ +#define IEEE80211_CAPINFO_DSSSOFDM 0x2000 +/* bits 14-15 are reserved */ + +/* + * 802.11i/WPA information element (maximally sized). + */ +struct ieee80211_ie_wpa { + u_int8_t wpa_id; /* IEEE80211_ELEMID_VENDOR */ + u_int8_t wpa_len; /* length in bytes */ + u_int8_t wpa_oui[3]; /* 0x00, 0x50, 0xf2 */ + u_int8_t wpa_type; /* OUI type */ + u_int16_t wpa_version; /* spec revision */ + u_int32_t wpa_mcipher[1]; /* multicast/group key cipher */ + u_int16_t wpa_uciphercnt; /* # pairwise key ciphers */ + u_int32_t wpa_uciphers[8];/* ciphers */ + u_int16_t wpa_authselcnt; /* authentication selector cnt*/ + u_int32_t wpa_authsels[8];/* selectors */ + u_int16_t wpa_caps; /* 802.11i capabilities */ + u_int16_t wpa_pmkidcnt; /* 802.11i pmkid count */ + u_int16_t wpa_pmkids[8]; /* 802.11i pmkids */ +} __packed; + +/* + * Management information element payloads + */ +union ieee80211_information { + char ssid[IEEE80211_NWID_LEN+1]; + struct rates { + u_int8_t *p; + } rates; + struct fh { + u_int16_t dwell; + u_int8_t set; + u_int8_t pattern; + u_int8_t index; + } fh; + struct ds { + u_int8_t channel; + } ds; + struct cf { + u_int8_t count; + u_int8_t period; + u_int8_t maxdur[2]; + u_int8_t dur[2]; + } cf; + struct tim { + u_int8_t count; + u_int8_t period; + u_int8_t bitctl; + /* u_int8_t pvt[251]; The driver needs to use this. */ + } tim; + struct ibss { + u_int16_t atim; + } ibss; + struct challenge { + u_int8_t *p; + u_int8_t len; + } challenge; + struct erp { + u_int8_t flags; + } erp; + struct country { + u_int8_t cc[3]; /* ISO CC+(I)ndoor/(O)utdoor */ + struct { + u_int8_t schan; /* starting channel */ + u_int8_t nchan; /* number channels */ + u_int8_t maxtxpwr; + } band[4]; /* up to 4 sub bands */ + } country; + struct ath { + u_int8_t flags; + } ath; +}; + +enum { + IEEE80211_ELEMID_SSID = 0, + IEEE80211_ELEMID_RATES = 1, + IEEE80211_ELEMID_FHPARMS = 2, + IEEE80211_ELEMID_DSPARMS = 3, + IEEE80211_ELEMID_CFPARMS = 4, + IEEE80211_ELEMID_TIM = 5, + IEEE80211_ELEMID_IBSSPARMS = 6, + IEEE80211_ELEMID_COUNTRY = 7, + IEEE80211_ELEMID_CHALLENGE = 16, + /* 17-31 reserved for challenge text extension */ + IEEE80211_ELEMID_ERP = 42, + IEEE80211_ELEMID_RSN = 48, + IEEE80211_ELEMID_XRATES = 50, + IEEE80211_ELEMID_TPC = 150, + IEEE80211_ELEMID_CCKM = 156, + IEEE80211_ELEMID_VENDOR = 221, /* vendor private */ +}; + +#define IEEE80211_CHALLENGE_LEN 128 + +#define IEEE80211_RATE_BASIC 0x80 +#define IEEE80211_RATE_VAL 0x7f + +/* EPR information element flags */ +#define IEEE80211_ERP_NON_ERP_PRESENT 0x01 +#define IEEE80211_ERP_USE_PROTECTION 0x02 +#define IEEE80211_ERP_LONG_PREAMBLE 0x04 + +/* Atheros private advanced capabilities info */ +#define ATHEROS_CAP_TURBO_PRIME 0x01 +#define ATHEROS_CAP_COMPRESSION 0x02 +#define ATHEROS_CAP_FAST_FRAME 0x04 +/* bits 3-6 reserved */ +#define ATHEROS_CAP_BOOST 0x80 + +#define ATH_OUI 0x7f0300 /* Atheros OUI */ +#define ATH_OUI_TYPE 0x01 +#define ATH_OUI_VERSION 0x01 + +#define WPA_OUI 0xf25000 +#define WPA_OUI_TYPE 0x01 +#define WPA_VERSION 1 /* current supported version */ + +#define WPA_CSE_NULL 0x00 +#define WPA_CSE_WEP40 0x01 +#define WPA_CSE_TKIP 0x02 +#define WPA_CSE_CCMP 0x04 +#define WPA_CSE_WEP104 0x05 + +#define WPA_ASE_NONE 0x00 +#define WPA_ASE_8021X_UNSPEC 0x01 +#define WPA_ASE_8021X_PSK 0x02 + +#define RSN_OUI 0xac0f00 +#define RSN_VERSION 1 /* current supported version */ + +#define RSN_CSE_NULL 0x00 +#define RSN_CSE_WEP40 0x01 +#define RSN_CSE_TKIP 0x02 +#define RSN_CSE_WRAP 0x03 +#define RSN_CSE_CCMP 0x04 +#define RSN_CSE_WEP104 0x05 + +#define RSN_ASE_NONE 0x00 +#define RSN_ASE_8021X_UNSPEC 0x01 +#define RSN_ASE_8021X_PSK 0x02 + +#define RSN_CAP_PREAUTH 0x01 + +#define WME_OUI 0xf25000 +#define WME_OUI_TYPE 0x02 +#define WME_VERSION 1 + +/* WME stream classes */ +#define WME_AC_BE 0 /* best effort */ +#define WME_AC_BK 1 /* background */ +#define WME_AC_VI 2 /* video */ +#define WME_AC_VO 3 /* voice */ + +/* + * AUTH management packets + * + * octet algo[2] + * octet seq[2] + * octet status[2] + * octet chal.id + * octet chal.length + * octet chal.text[253] + */ + +typedef u_int8_t *ieee80211_mgt_auth_t; + +#define IEEE80211_AUTH_ALGORITHM(auth) \ + ((auth)[0] | ((auth)[1] << 8)) +#define IEEE80211_AUTH_TRANSACTION(auth) \ + ((auth)[2] | ((auth)[3] << 8)) +#define IEEE80211_AUTH_STATUS(auth) \ + ((auth)[4] | ((auth)[5] << 8)) + +#define IEEE80211_AUTH_ALG_OPEN 0x0000 +#define IEEE80211_AUTH_ALG_SHARED 0x0001 +#define IEEE80211_AUTH_ALG_LEAP 0x0080 + +enum { + IEEE80211_AUTH_OPEN_REQUEST = 1, + IEEE80211_AUTH_OPEN_RESPONSE = 2, +}; + +enum { + IEEE80211_AUTH_SHARED_REQUEST = 1, + IEEE80211_AUTH_SHARED_CHALLENGE = 2, + IEEE80211_AUTH_SHARED_RESPONSE = 3, + IEEE80211_AUTH_SHARED_PASS = 4, +}; + +/* + * Reason codes + * + * Unlisted codes are reserved + */ + +enum { + IEEE80211_REASON_UNSPECIFIED = 1, + IEEE80211_REASON_AUTH_EXPIRE = 2, + IEEE80211_REASON_AUTH_LEAVE = 3, + IEEE80211_REASON_ASSOC_EXPIRE = 4, + IEEE80211_REASON_ASSOC_TOOMANY = 5, + IEEE80211_REASON_NOT_AUTHED = 6, + IEEE80211_REASON_NOT_ASSOCED = 7, + IEEE80211_REASON_ASSOC_LEAVE = 8, + IEEE80211_REASON_ASSOC_NOT_AUTHED = 9, + + IEEE80211_REASON_RSN_REQUIRED = 11, + IEEE80211_REASON_RSN_INCONSISTENT = 12, + IEEE80211_REASON_IE_INVALID = 13, + IEEE80211_REASON_MIC_FAILURE = 14, + + IEEE80211_STATUS_SUCCESS = 0, + IEEE80211_STATUS_UNSPECIFIED = 1, + IEEE80211_STATUS_CAPINFO = 10, + IEEE80211_STATUS_NOT_ASSOCED = 11, + IEEE80211_STATUS_OTHER = 12, + IEEE80211_STATUS_ALG = 13, + IEEE80211_STATUS_SEQUENCE = 14, + IEEE80211_STATUS_CHALLENGE = 15, + IEEE80211_STATUS_TIMEOUT = 16, + IEEE80211_STATUS_TOOMANY = 17, + IEEE80211_STATUS_BASIC_RATE = 18, + IEEE80211_STATUS_SP_REQUIRED = 19, + IEEE80211_STATUS_PBCC_REQUIRED = 20, + IEEE80211_STATUS_CA_REQUIRED = 21, + IEEE80211_STATUS_TOO_MANY_STATIONS = 22, + IEEE80211_STATUS_RATES = 23, + IEEE80211_STATUS_SHORTSLOT_REQUIRED = 25, + IEEE80211_STATUS_DSSSOFDM_REQUIRED = 26, +}; + +#define IEEE80211_WEP_KEYLEN 5 /* 40bit */ +#define IEEE80211_WEP_IVLEN 3 /* 24bit */ +#define IEEE80211_WEP_KIDLEN 1 /* 1 octet */ +#define IEEE80211_WEP_CRCLEN 4 /* CRC-32 */ +#define IEEE80211_WEP_NKID 4 /* number of key ids */ + +/* + * 802.11i defines an extended IV for use with non-WEP ciphers. + * When the EXTIV bit is set in the key id byte an additional + * 4 bytes immediately follow the IV for TKIP. For CCMP the + * EXTIV bit is likewise set but the 8 bytes represent the + * CCMP header rather than IV+extended-IV. + */ +#define IEEE80211_WEP_EXTIV 0x20 +#define IEEE80211_WEP_EXTIVLEN 4 /* extended IV length */ +#define IEEE80211_WEP_MICLEN 8 /* trailing MIC */ + +#define IEEE80211_CRC_LEN 4 + +/* + * Maximum acceptable MTU is: + * IEEE80211_MAX_LEN - WEP overhead - CRC - + * QoS overhead - RSN/WPA overhead + * Min is arbitrarily chosen > IEEE80211_MIN_LEN. The default + * mtu is Ethernet-compatible; it's set by ether_ifattach. + */ +#define IEEE80211_MTU_MAX 2290 +#define IEEE80211_MTU_MIN 32 + +#define IEEE80211_MAX_LEN (2300 + IEEE80211_CRC_LEN + \ + (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_CRCLEN)) +#define IEEE80211_ACK_LEN \ + (sizeof(struct ieee80211_frame_ack) + IEEE80211_CRC_LEN) +#define IEEE80211_MIN_LEN \ + (sizeof(struct ieee80211_frame_min) + IEEE80211_CRC_LEN) + +/* + * The 802.11 spec says at most 2007 stations may be + * associated at once. For most AP's this is way more + * than is feasible so we use a default of 128. This + * number may be overridden by the driver and/or by + * user configuration. + */ +#define IEEE80211_AID_MAX 2007 +#define IEEE80211_AID_DEF 128 + +#define IEEE80211_AID(b) ((b) &~ 0xc000) +#define IEEE80211_AID_SET(b, w) \ + ((w)[IEEE80211_AID(b) / 32] |= (1 << (IEEE80211_AID(b) % 32))) +#define IEEE80211_AID_CLR(b, w) \ + ((w)[IEEE80211_AID(b) / 32] &= ~(1 << (IEEE80211_AID(b) % 32))) +#define IEEE80211_AID_ISSET(b, w) \ + ((w)[IEEE80211_AID(b) / 32] & (1 << (IEEE80211_AID(b) % 32))) + +/* + * RTS frame length parameters. The default is specified in + * the 802.11 spec. The max may be wrong for jumbo frames. + */ +#define IEEE80211_RTS_DEFAULT 512 +#define IEEE80211_RTS_MIN 1 +#define IEEE80211_RTS_MAX IEEE80211_MAX_LEN + +enum { + IEEE80211_AUTH_NONE = 0, + IEEE80211_AUTH_OPEN = 1, /* open */ + IEEE80211_AUTH_SHARED = 2, /* shared-key */ + IEEE80211_AUTH_8021X = 3, /* 802.1x */ + IEEE80211_AUTH_AUTO = 4, /* auto-select/accept */ + /* NB: these are used only for ioctls */ + IEEE80211_AUTH_WPA = 5, /* WPA/RSN w/ 802.1x/PSK */ +}; + +#endif /* _NET80211_IEEE80211_H_ */ diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_acl.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_acl.c --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_acl.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_acl.c 2005-02-24 13:06:17.299132152 -0800 @@ -0,0 +1,300 @@ +/*- + * Copyright (c) 2004-2005 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EXPORT_SYMTAB +#define EXPORT_SYMTAB +#endif + +/* + * IEEE 802.11 MAC ACL support. + * + * When this module is loaded the sender address of each received + * frame is passed to the iac_check method and the module indicates + * if the frame should be accepted or rejected. If the policy is + * set to ACL_POLICY_OPEN then all frames are accepted w/o checking + * the address. Otherwise, the address is looked up in the database + * and if found the frame is either accepted (ACL_POLICY_ALLOW) + * or rejected (ACL_POLICY_DENT). + */ +#include +#include +#include +#include +#include +#include + +#include "if_media.h" + +#include + +enum { + ACL_POLICY_OPEN = 0, /* open, don't check ACL's */ + ACL_POLICY_ALLOW = 1, /* allow traffic from MAC */ + ACL_POLICY_DENY = 2, /* deny traffic from MAC */ +}; + +#define ACL_HASHSIZE 32 + +struct acl { + TAILQ_ENTRY(acl) acl_list; + LIST_ENTRY(acl) acl_hash; + u_int8_t acl_macaddr[IEEE80211_ADDR_LEN]; +}; +struct aclstate { + acl_lock_t as_lock; + int as_policy; + TAILQ_HEAD(, acl) as_list; /* list of all ACL's */ + ATH_LIST_HEAD(, acl) as_hash[ACL_HASHSIZE]; + struct ieee80211com *as_ic; +}; + +/* simple hash is enough for variation of macaddr */ +#define ACL_HASH(addr) \ + (((u_int8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % ACL_HASHSIZE) + +MALLOC_DEFINE(M_80211_ACL, "acl", "802.11 station acl"); + +static int acl_free_all(struct ieee80211com *); + +static int +acl_attach(struct ieee80211com *ic) +{ + struct aclstate *as; + + _MOD_INC_USE(THIS_MODULE, return 0); + + MALLOC(as, struct aclstate *, sizeof(struct aclstate), + M_DEVBUF, M_NOWAIT | M_ZERO); + if (as == NULL) { + _MOD_DEC_USE(THIS_MODULE); + return 0; + } + ACL_LOCK_INIT(as, "acl"); + TAILQ_INIT(&as->as_list); + as->as_policy = ACL_POLICY_OPEN; + as->as_ic = ic; + ic->ic_as = as; + return 1; +} + +static void +acl_detach(struct ieee80211com *ic) +{ + struct aclstate *as = ic->ic_as; + + acl_free_all(ic); + ic->ic_as = NULL; + ACL_LOCK_DESTROY(as); + FREE(as, M_DEVBUF); + + _MOD_DEC_USE(THIS_MODULE); +} + +static inline struct acl * +_find_acl(struct aclstate *as, const u_int8_t *macaddr) +{ + struct acl *acl; + int hash; + + hash = ACL_HASH(macaddr); + LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) { + if (IEEE80211_ADDR_EQ(acl->acl_macaddr, macaddr)) + return acl; + } + return NULL; +} + +static void +_acl_free(struct aclstate *as, struct acl *acl) +{ + ACL_LOCK_ASSERT(as); + + TAILQ_REMOVE(&as->as_list, acl, acl_list); + LIST_REMOVE(acl, acl_hash); + FREE(acl, M_80211_ACL); +} + +static int +acl_check(struct ieee80211com *ic, const u_int8_t mac[IEEE80211_ADDR_LEN]) +{ + struct aclstate *as = ic->ic_as; + + switch (as->as_policy) { + case ACL_POLICY_OPEN: + return 1; + case ACL_POLICY_ALLOW: + return _find_acl(as, mac) != NULL; + case ACL_POLICY_DENY: + return _find_acl(as, mac) == NULL; + } + return 0; /* should not happen */ +} + +static int +acl_add(struct ieee80211com *ic, const u_int8_t mac[IEEE80211_ADDR_LEN]) +{ + struct aclstate *as = ic->ic_as; + struct acl *acl, *new; + int hash; + + MALLOC(new, struct acl *, sizeof(struct acl), M_80211_ACL, M_NOWAIT | M_ZERO); + if (new == NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, + ("ACL: add %s failed, no memory\n", + ether_sprintf(mac))); + /* XXX statistic */ + return ENOMEM; + } + + ACL_LOCK(as); + hash = ACL_HASH(mac); + LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) { + if (IEEE80211_ADDR_EQ(acl->acl_macaddr, mac)) { + ACL_UNLOCK(as); + FREE(new, M_80211_ACL); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, + ("ACL: add %s failed, already present\n", + ether_sprintf(mac))); + return EEXIST; + } + } + IEEE80211_ADDR_COPY(new->acl_macaddr, mac); + TAILQ_INSERT_TAIL(&as->as_list, new, acl_list); + LIST_INSERT_HEAD(&as->as_hash[hash], new, acl_hash); + ACL_UNLOCK(as); + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, + ("ACL: add %s\n", ether_sprintf(mac))); + return 0; +} + +static int +acl_remove(struct ieee80211com *ic, const u_int8_t mac[IEEE80211_ADDR_LEN]) +{ + struct aclstate *as = ic->ic_as; + struct acl *acl; + + ACL_LOCK(as); + acl = _find_acl(as, mac); + if (acl != NULL) + _acl_free(as, acl); + ACL_UNLOCK(as); + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, + ("ACL: remove %s%s\n", ether_sprintf(mac), + acl == NULL ? ", not present" : "")); + + return (acl == NULL ? ENOENT : 0); +} + +static int +acl_free_all(struct ieee80211com *ic) +{ + struct aclstate *as = ic->ic_as; + struct acl *acl; + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, ("ACL: free all\n")); + + ACL_LOCK(as); + while ((acl = TAILQ_FIRST(&as->as_list)) != NULL) + _acl_free(as, acl); + ACL_UNLOCK(as); + + return 0; +} + +static int +acl_setpolicy(struct ieee80211com *ic, int policy) +{ + struct aclstate *as = ic->ic_as; + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, + ("ACL: set policy to %u\n", policy)); + + switch (policy) { + case IEEE80211_MACCMD_POLICY_OPEN: + as->as_policy = ACL_POLICY_OPEN; + break; + case IEEE80211_MACCMD_POLICY_ALLOW: + as->as_policy = ACL_POLICY_ALLOW; + break; + case IEEE80211_MACCMD_POLICY_DENY: + as->as_policy = ACL_POLICY_DENY; + break; + default: + return EINVAL; + } + return 0; +} + +static int +acl_getpolicy(struct ieee80211com *ic) +{ + struct aclstate *as = ic->ic_as; + + return as->as_policy; +} + +/* + * Module glue. + */ + +MODULE_AUTHOR("Errno Consulting, Sam Leffler"); +MODULE_DESCRIPTION("802.11 wireless support: MAC-based ACL policy"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("Dual BSD/GPL"); +#endif + +static const struct ieee80211_aclator mac = { + .iac_name = "mac", + .iac_attach = acl_attach, + .iac_detach = acl_detach, + .iac_check = acl_check, + .iac_add = acl_add, + .iac_remove = acl_remove, + .iac_flush = acl_free_all, + .iac_setpolicy = acl_setpolicy, + .iac_getpolicy = acl_getpolicy, +}; + +static int __init +init_ieee80211_acl(void) +{ + ieee80211_aclator_register(&mac); + return 0; +} +module_init(init_ieee80211_acl); + +static void __exit +exit_ieee80211_acl(void) +{ + ieee80211_aclator_unregister(&mac); +} +module_exit(exit_ieee80211_acl); diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_crypto.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_crypto.c --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_crypto.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_crypto.c 2005-02-24 13:06:17.299132152 -0800 @@ -0,0 +1,557 @@ +/*- + * Copyright (c) 2001 Atsushi Onoe + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_crypto.c,v 1.3 2003/10/17 23:15:30 sam Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ieee80211_crypto.c,v 1.4 2003/09/23 16:03:46 dyoung Exp $"); + +/* + * IEEE 802.11 generic crypto support. + */ +#include +#include +#include +#include +#include +#include +#include + +#include "if_ethersubr.h" /* XXX ETHER_HDR_LEN */ +#include "if_media.h" + +#include + +/* + * Table of registered cipher modules. + */ +static const struct ieee80211_cipher *ciphers[IEEE80211_CIPHER_MAX]; + +static int _ieee80211_crypto_delkey(struct ieee80211com *, + struct ieee80211_key *); + +/* + * Default "null" key management routines. + */ +static int +null_key_alloc(struct ieee80211com *ic, const struct ieee80211_key *k) +{ + return IEEE80211_KEYIX_NONE; +} +static int +null_key_delete(struct ieee80211com *ic, const struct ieee80211_key *k) +{ + return 1; +} +static int +null_key_set(struct ieee80211com *ic, const struct ieee80211_key *k, + const u_int8_t mac[IEEE80211_ADDR_LEN]) +{ + return 1; +} +static void null_key_update(struct ieee80211com *ic) {} + +/* + * Write-arounds for common operations. + */ +static inline void +cipher_detach(struct ieee80211_key *key) +{ + key->wk_cipher->ic_detach(key); +} + +static inline void * +cipher_attach(struct ieee80211com *ic, struct ieee80211_key *key) +{ + return key->wk_cipher->ic_attach(ic, key); +} + +/* + * Wrappers for driver key management methods. + */ +static inline int +dev_key_alloc(struct ieee80211com *ic, + const struct ieee80211_key *key) +{ + return ic->ic_crypto.cs_key_alloc(ic, key); +} + +static inline int +dev_key_delete(struct ieee80211com *ic, + const struct ieee80211_key *key) +{ + return ic->ic_crypto.cs_key_delete(ic, key); +} + +static inline int +dev_key_set(struct ieee80211com *ic, const struct ieee80211_key *key, + const u_int8_t mac[IEEE80211_ADDR_LEN]) +{ + return ic->ic_crypto.cs_key_set(ic, key, mac); +} + +/* + * Setup crypto support. + */ +void +ieee80211_crypto_attach(struct ieee80211com *ic) +{ + struct ieee80211_crypto_state *cs = &ic->ic_crypto; + int i; + + /* NB: we assume everything is pre-zero'd */ + cs->cs_def_txkey = IEEE80211_KEYIX_NONE; + ciphers[IEEE80211_CIPHER_NONE] = &ieee80211_cipher_none; + for (i = 0; i < IEEE80211_WEP_NKID; i++) + ieee80211_crypto_resetkey(ic, &cs->cs_nw_keys[i], i); + /* + * Initialize the driver key support routines to noop entries. + * This is useful especially for the cipher test modules. + */ + cs->cs_key_alloc = null_key_alloc; + cs->cs_key_set = null_key_set; + cs->cs_key_delete = null_key_delete; + cs->cs_key_update_begin = null_key_update; + cs->cs_key_update_end = null_key_update; +} +EXPORT_SYMBOL(ieee80211_crypto_attach); + +/* + * Teardown crypto support. + */ +void +ieee80211_crypto_detach(struct ieee80211com *ic) +{ + ieee80211_crypto_delglobalkeys(ic); +} +EXPORT_SYMBOL(ieee80211_crypto_detach); + +/* + * Register a crypto cipher module. + */ +void +ieee80211_crypto_register(const struct ieee80211_cipher *cip) +{ + if (cip->ic_cipher >= IEEE80211_CIPHER_MAX) { + printf("%s: cipher %s has an invalid cipher index %u\n", + __func__, cip->ic_name, cip->ic_cipher); + return; + } + if (ciphers[cip->ic_cipher] != NULL && ciphers[cip->ic_cipher] != cip) { + printf("%s: cipher %s registered with a different template\n", + __func__, cip->ic_name); + return; + } + ciphers[cip->ic_cipher] = cip; +} +EXPORT_SYMBOL(ieee80211_crypto_register); + +/* + * Unregister a crypto cipher module. + */ +void +ieee80211_crypto_unregister(const struct ieee80211_cipher *cip) +{ + if (cip->ic_cipher >= IEEE80211_CIPHER_MAX) { + printf("%s: cipher %s has an invalid cipher index %u\n", + __func__, cip->ic_name, cip->ic_cipher); + return; + } + if (ciphers[cip->ic_cipher] != NULL && ciphers[cip->ic_cipher] != cip) { + printf("%s: cipher %s registered with a different template\n", + __func__, cip->ic_name); + return; + } + /* NB: don't complain about not being registered */ + /* XXX disallow if references */ + ciphers[cip->ic_cipher] = NULL; +} +EXPORT_SYMBOL(ieee80211_crypto_unregister); + +/* XXX well-known names! */ +static const char *cipher_modnames[] = { + "wlan_wep", /* IEEE80211_CIPHER_WEP */ + "wlan_tkip", /* IEEE80211_CIPHER_TKIP */ + "wlan_aes_ocb", /* IEEE80211_CIPHER_AES_OCB */ + "wlan_ccmp", /* IEEE80211_CIPHER_AES_CCM */ + "wlan_ckip", /* IEEE80211_CIPHER_CKIP */ +}; + +/* + * Establish a relationship between the specified key and cipher + * and, if not a global key, allocate a hardware index from the + * driver. Note that we may be called for global keys but they + * should have a key index already setup so the only work done + * is to setup the cipher reference. + * + * This must be the first call applied to a key; all the other key + * routines assume wk_cipher is setup. + * + * Locking must be handled by the caller using: + * ieee80211_key_update_begin(ic); + * ieee80211_key_update_end(ic); + */ +int +ieee80211_crypto_newkey(struct ieee80211com *ic, + int cipher, struct ieee80211_key *key) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + const struct ieee80211_cipher *cip; + void *keyctx; + int oflags; + + /* + * Validate cipher and set reference to cipher routines. + */ + if (cipher >= IEEE80211_CIPHER_MAX) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + ("%s: invalid cipher %u\n", __func__, cipher)); + ic->ic_stats.is_crypto_badcipher++; + return 0; + } + cip = ciphers[cipher]; + if (cip == NULL) { + /* + * Auto-load cipher module if we have a well-known name + * for it. It might be better to use string names rather + * than numbers and craft a module name based on the cipher + * name; e.g. wlan_cipher_. + */ + if (cipher < N(cipher_modnames)) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + ("%s: unregistered cipher %u, load module %s\n", + __func__, cipher, cipher_modnames[cipher])); + request_module(cipher_modnames[cipher]); + /* + * If cipher module loaded it should immediately + * call ieee80211_crypto_register which will fill + * in the entry in the ciphers array. + */ + cip = ciphers[cipher]; + } + if (cip == NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + ("%s: unable to load cipher %u, module %s\n", + __func__, cipher, + cipher < N(cipher_modnames) ? + cipher_modnames[cipher] : "")); + ic->ic_stats.is_crypto_nocipher++; + return 0; + } + } + + oflags = key->wk_flags; + /* + * If the hardware does not support the cipher then + * fallback to a host-based implementation. + */ + key->wk_flags &= ~(IEEE80211_KEY_SWCRYPT|IEEE80211_KEY_SWMIC); + if ((ic->ic_caps & (1<ic_name)); + key->wk_flags |= IEEE80211_KEY_SWCRYPT; + } + /* + * Hardware TKIP with software MIC is an important + * combination; we handle it by flagging each key, + * the cipher modules honor it. + */ + if (cipher == IEEE80211_CIPHER_TKIP && + (ic->ic_caps & IEEE80211_C_TKIPMIC) == 0) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + ("%s: no h/w support for TKIP MIC, falling back to s/w\n", + __func__)); + key->wk_flags |= IEEE80211_KEY_SWMIC; + } + + /* + * Bind cipher to key instance. Note we do this + * after checking the device capabilities so the + * cipher module can optimize space usage based on + * whether or not it needs to do the cipher work. + */ + if (key->wk_cipher != cip || key->wk_flags != oflags) { +again: + keyctx = cip->ic_attach(ic, key); + if (keyctx == NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + ("%s: unable to attach cipher %s\n", + __func__, cip->ic_name)); + key->wk_flags = oflags; /* restore old flags */ + ic->ic_stats.is_crypto_attachfail++; + return 0; + } + cipher_detach(key); + key->wk_cipher = cip; /* XXX refcnt? */ + key->wk_private = keyctx; + } + + /* + * Ask the driver for a key index if we don't have one. + * Note that entries in the global key table always have + * an index; this means it's safe to call this routine + * for these entries just to setup the reference to the + * cipher template. Note also that when using software + * crypto we also call the driver to give us a key index. + */ + if (key->wk_keyix == IEEE80211_KEYIX_NONE) { + key->wk_keyix = dev_key_alloc(ic, key); + if (key->wk_keyix == IEEE80211_KEYIX_NONE) { + /* + * Driver has no room; fallback to doing crypto + * in the host. We change the flags and start the + * procedure over. If we get back here then there's + * no hope and we bail. Note that this can leave + * the key in a inconsistent state if the caller + * continues to use it. + */ + if ((key->wk_flags & IEEE80211_KEY_SWCRYPT) == 0) { + ic->ic_stats.is_crypto_swfallback++; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + ("%s: no h/w resources for cipher %s, " + "falling back to s/w\n", __func__, + cip->ic_name)); + oflags = key->wk_flags; + key->wk_flags |= IEEE80211_KEY_SWCRYPT; + if (cipher == IEEE80211_CIPHER_TKIP) + key->wk_flags |= IEEE80211_KEY_SWMIC; + goto again; + } + ic->ic_stats.is_crypto_keyfail++; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + ("%s: unable to setup cipher %s\n", + __func__, cip->ic_name)); + return 0; + } + } + return 1; +#undef N +} +EXPORT_SYMBOL(ieee80211_crypto_newkey); + +/* + * Remove the key (no locking, for internal use). + */ +static int +_ieee80211_crypto_delkey(struct ieee80211com *ic, struct ieee80211_key *key) +{ + u_int16_t keyix; + + KASSERT(key->wk_cipher != NULL, ("No cipher!")); + + keyix = key->wk_keyix; + if (keyix != IEEE80211_KEYIX_NONE) { + /* + * Remove hardware entry. + */ + /* XXX key cache */ + if (!dev_key_delete(ic, key)) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + ("%s: driver did not delete key index %u\n", + __func__, keyix)); + ic->ic_stats.is_crypto_delkey++; + /* XXX recovery? */ + } + } + cipher_detach(key); + memset(key, 0, sizeof(*key)); + key->wk_cipher = &ieee80211_cipher_none; + key->wk_private = cipher_attach(ic, key); + /* NB: cannot depend on key index to decide this */ + if (&ic->ic_nw_keys[0] <= key && + key < &ic->ic_nw_keys[IEEE80211_WEP_NKID]) + key->wk_keyix = keyix; /* preserve shared key state */ + else + key->wk_keyix = IEEE80211_KEYIX_NONE; + return 1; +} + +/* + * Remove the specified key. + */ +int +ieee80211_crypto_delkey(struct ieee80211com *ic, struct ieee80211_key *key) +{ + int status; + + ieee80211_key_update_begin(ic); + status = _ieee80211_crypto_delkey(ic, key); + ieee80211_key_update_end(ic); + return status; +} +EXPORT_SYMBOL(ieee80211_crypto_delkey); + +/* + * Clear the global key table. + */ +void +ieee80211_crypto_delglobalkeys(struct ieee80211com *ic) +{ + int i; + + ieee80211_key_update_begin(ic); + for (i = 0; i < IEEE80211_WEP_NKID; i++) + (void) _ieee80211_crypto_delkey(ic, &ic->ic_nw_keys[i]); + ieee80211_key_update_end(ic); +} +EXPORT_SYMBOL(ieee80211_crypto_delglobalkeys); + +/* + * Set the contents of the specified key. + * + * Locking must be handled by the caller using: + * ieee80211_key_update_begin(ic); + * ieee80211_key_update_end(ic); + */ +int +ieee80211_crypto_setkey(struct ieee80211com *ic, struct ieee80211_key *key, + const u_int8_t macaddr[IEEE80211_ADDR_LEN]) +{ + const struct ieee80211_cipher *cip = key->wk_cipher; + + KASSERT(cip != NULL, ("No cipher!")); + + /* + * Give cipher a chance to validate key contents. + * XXX should happen before modifying state. + */ + if (!cip->ic_setkey(key)) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + ("%s: cipher %s rejected key index %u\n", + __func__, cip->ic_name, key->wk_keyix)); + ic->ic_stats.is_crypto_setkey_cipher++; + return 0; + } + if (key->wk_keyix == IEEE80211_KEYIX_NONE) { + /* XXX nothing allocated, should not happen */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + ("%s: no key index; should not happen!\n", __func__)); + ic->ic_stats.is_crypto_setkey_nokey++; + return 0; + } + return dev_key_set(ic, key, macaddr); +} +EXPORT_SYMBOL(ieee80211_crypto_setkey); + +/* + * Add privacy headers appropriate for the specified key. + */ +struct ieee80211_key * +ieee80211_crypto_encap(struct ieee80211com *ic, + struct ieee80211_node *ni, struct sk_buff *skb) +{ + struct ieee80211_key *k; + struct ieee80211_frame *wh; + const struct ieee80211_cipher *cip; + u_int8_t keyix; + + /* + * Multicast traffic always uses the multicast key. + * Otherwise if a unicast key is set we use that and + * it is always key index 0. When no unicast key is + * set we fall back to the default transmit key. + */ + wh = (struct ieee80211_frame *)skb->data; + if (IEEE80211_IS_MULTICAST(wh->i_addr1) || + ni->ni_ucastkey.wk_cipher == &ieee80211_cipher_none) { + if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + ("%s: No default xmit key for frame to %s\n", + __func__, ether_sprintf(wh->i_addr1))); + ic->ic_stats.is_tx_nodefkey++; + return NULL; + } + keyix = ic->ic_def_txkey; + k = &ic->ic_nw_keys[ic->ic_def_txkey]; + } else { + keyix = 0; + k = &ni->ni_ucastkey; + } + cip = k->wk_cipher; + if (skb_headroom(skb) < cip->ic_header) { + /* + * Should not happen; ieee80211_skbhdr_adjust should + * have allocated enough space for all headers. + */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + ("%s: Malformed packet for cipher %s; headroom %u\n", + __func__, cip->ic_name, skb_headroom(skb))); + ic->ic_stats.is_tx_noheadroom++; + return NULL; + } + return ((*cip->ic_encap)(k, skb, keyix<<6) ? k : NULL); +} +EXPORT_SYMBOL(ieee80211_crypto_encap); + +/* + * Validate and strip privacy headers (and trailer) for a + * received frame that has the WEP/Privacy bit set. + */ +struct ieee80211_key * +ieee80211_crypto_decap(struct ieee80211com *ic, + struct ieee80211_node *ni, struct sk_buff *skb) +{ +#define IEEE80211_WEP_HDRLEN (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN) +#define IEEE80211_WEP_MINLEN \ + (sizeof(struct ieee80211_frame) + ETHER_HDR_LEN + \ + IEEE80211_WEP_HDRLEN + IEEE80211_WEP_CRCLEN) + struct ieee80211_key *k; + struct ieee80211_frame *wh; + u_int8_t *ivp; + u_int8_t keyid; + + /* NB: this minimum size data frame could be bigger */ + if (skb->len < IEEE80211_WEP_MINLEN) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + ("%s: WEP data frame too short, len %u\n", + __func__, skb->len)); + ic->ic_stats.is_rx_tooshort++; /* XXX need unique stat? */ + return NULL; + } + /* + * Locate the key. If unicast and there is no unicast + * key then we fall back to the key id in the header. + * This assumes unicast keys are only configured when + * the key id in the header is meaningless (typically 0). + */ + wh = (struct ieee80211_frame *) skb->data; + ivp = skb->data + ieee80211_hdrsize(wh); + keyid = ivp[IEEE80211_WEP_IVLEN]; + if (IEEE80211_IS_MULTICAST(wh->i_addr1) || + ni->ni_ucastkey.wk_cipher == &ieee80211_cipher_none) + k = &ic->ic_nw_keys[keyid >> 6]; + else + k = &ni->ni_ucastkey; + return ((*k->wk_cipher->ic_decap)(k, skb) ? k : NULL); +#undef IEEE80211_WEP_MINLEN +#undef IEEE80211_WEP_HDRLEN +} +EXPORT_SYMBOL(ieee80211_crypto_decap); diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_crypto.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_crypto.h --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_crypto.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_crypto.h 2005-02-24 13:06:17.300132000 -0800 @@ -0,0 +1,221 @@ +/* $NetBSD: ieee80211_crypto.h,v 1.2 2003/09/14 01:14:55 dyoung Exp $ */ +/*- + * Copyright (c) 2001 Atsushi Onoe + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/net80211/ieee80211_crypto.h,v 1.2 2003/06/27 05:13:52 sam Exp $ + */ +#ifndef _NET80211_IEEE80211_CRYPTO_H_ +#define _NET80211_IEEE80211_CRYPTO_H_ + +/* + * 802.11 protocol crypto-related definitions. + */ +#define IEEE80211_KEYBUF_SIZE 16 +#define IEEE80211_MICBUF_SIZE (8+8) /* space for both tx+rx keys */ + +/* + * Old WEP-style key. Deprecated. + */ +struct ieee80211_wepkey { + u_int wk_len; /* key length in bytes */ + u_int8_t wk_key[IEEE80211_KEYBUF_SIZE]; +}; + +struct ieee80211_cipher; + +/* + * Crypto key state. There is sufficient room for all supported + * ciphers (see below). The underlying ciphers are handled + * separately through loadable cipher modules that register with + * the generic crypto support. A key has a reference to an instance + * of the cipher; any per-key state is hung off wk_private by the + * cipher when it is attached. Ciphers are automatically called + * to detach and cleanup any such state when the key is deleted. + * + * The generic crypto support handles encap/decap of cipher-related + * frame contents for both hardware- and software-based implementations. + * A key requiring software crypto support is automatically flagged and + * the cipher is expected to honor this and do the necessary work. + * Ciphers such as TKIP may also support mixed hardware/software + * encrypt/decrypt and MIC processing. + */ +/* XXX need key index typedef */ +/* XXX pack better? */ +/* XXX 48-bit rsc/tsc */ +struct ieee80211_key { + u_int8_t wk_keylen; /* key length in bytes */ + u_int8_t wk_flags; +#define IEEE80211_KEY_XMIT 0x01 /* key used for xmit */ +#define IEEE80211_KEY_RECV 0x02 /* key used for recv */ +#define IEEE80211_KEY_SWCRYPT 0x04 /* host-based encrypt/decrypt */ +#define IEEE80211_KEY_SWMIC 0x08 /* host-based enmic/demic */ + u_int16_t wk_keyix; /* key index */ + u_int8_t wk_key[IEEE80211_KEYBUF_SIZE+IEEE80211_MICBUF_SIZE]; +#define wk_txmic wk_key+IEEE80211_KEYBUF_SIZE+0 /* XXX can't () right */ +#define wk_rxmic wk_key+IEEE80211_KEYBUF_SIZE+8 /* XXX can't () right */ + u_int64_t wk_keyrsc; /* key receive sequence counter */ + u_int64_t wk_keytsc; /* key transmit sequence counter */ + const struct ieee80211_cipher *wk_cipher; + void *wk_private; /* private cipher state */ +}; + +/* + * NB: these values are ordered carefully; there are lots of + * of implications in any reordering. In particular beware + * that 4 is not used to avoid conflicting with IEEE80211_F_PRIVACY. + */ +#define IEEE80211_CIPHER_WEP 0 +#define IEEE80211_CIPHER_TKIP 1 +#define IEEE80211_CIPHER_AES_OCB 2 +#define IEEE80211_CIPHER_AES_CCM 3 +#define IEEE80211_CIPHER_CKIP 5 +#define IEEE80211_CIPHER_NONE 6 /* pseudo value */ + +#define IEEE80211_CIPHER_MAX (IEEE80211_CIPHER_NONE+1) + +#define IEEE80211_KEYIX_NONE ((u_int16_t) -1) + +#if defined(__KERNEL__) || defined(_KERNEL) + +struct ieee80211com; +struct ieee80211_node; +struct sk_buff; + +/* + * Crypto state kept in each ieee80211com. Some of this + * can/should be shared when virtual AP's are supported. + * + * XXX save reference to ieee80211com to properly encapsulate state. + * XXX split out crypto capabilities from ic_caps + */ +struct ieee80211_crypto_state { + struct ieee80211_key cs_nw_keys[IEEE80211_WEP_NKID]; + u_int16_t cs_def_txkey; /* default/group tx key index */ + + int (*cs_key_alloc)(struct ieee80211com *, + const struct ieee80211_key *); + int (*cs_key_delete)(struct ieee80211com *, + const struct ieee80211_key *); + int (*cs_key_set)(struct ieee80211com *, + const struct ieee80211_key *, + const u_int8_t mac[IEEE80211_ADDR_LEN]); + void (*cs_key_update_begin)(struct ieee80211com *); + void (*cs_key_update_end)(struct ieee80211com *); +}; + +extern void ieee80211_crypto_attach(struct ieee80211com *); +extern void ieee80211_crypto_detach(struct ieee80211com *); +extern int ieee80211_crypto_newkey(struct ieee80211com *, + int cipher, struct ieee80211_key *); +extern int ieee80211_crypto_delkey(struct ieee80211com *, + struct ieee80211_key *); +extern int ieee80211_crypto_setkey(struct ieee80211com *, + struct ieee80211_key *, const u_int8_t macaddr[IEEE80211_ADDR_LEN]); +extern void ieee80211_crypto_delglobalkeys(struct ieee80211com *); + +/* + * Template for a supported cipher. Ciphers register with the + * crypto code and are typically loaded as separate modules + * (the null cipher is always present). + * XXX may need refcnts + */ +struct ieee80211_cipher { + const char *ic_name; /* printable name */ + u_int ic_cipher; /* IEEE80211_CIPHER_* */ + u_int ic_header; /* size of privacy header (bytes) */ + u_int ic_trailer; /* size of privacy trailer (bytes) */ + u_int ic_miclen; /* size of mic trailer (bytes) */ + void* (*ic_attach)(struct ieee80211com *, struct ieee80211_key *); + void (*ic_detach)(struct ieee80211_key *); + int (*ic_setkey)(struct ieee80211_key *); + int (*ic_encap)(struct ieee80211_key *, struct sk_buff *, + u_int8_t keyid); + int (*ic_decap)(struct ieee80211_key *, struct sk_buff *); + int (*ic_enmic)(struct ieee80211_key *, struct sk_buff *); + int (*ic_demic)(struct ieee80211_key *, struct sk_buff *); +}; +extern const struct ieee80211_cipher ieee80211_cipher_none; + +extern void ieee80211_crypto_register(const struct ieee80211_cipher *); +extern void ieee80211_crypto_unregister(const struct ieee80211_cipher *); + +extern struct ieee80211_key *ieee80211_crypto_encap(struct ieee80211com *, + struct ieee80211_node *, struct sk_buff *); +extern struct ieee80211_key *ieee80211_crypto_decap(struct ieee80211com *, + struct ieee80211_node *, struct sk_buff *); + +/* + * Check and remove any MIC. + */ +static inline int +ieee80211_crypto_demic(struct ieee80211com *ic, struct ieee80211_key *k, + struct sk_buff *skb) +{ + const struct ieee80211_cipher *cip = k->wk_cipher; + return (cip->ic_miclen > 0 ? (*cip->ic_demic)(k, skb) : 1); +} + +/* + * Add any MIC. + */ +static inline int +ieee80211_crypto_enmic(struct ieee80211com *ic, + struct ieee80211_key *k, struct sk_buff *skb) +{ + const struct ieee80211_cipher *cip = k->wk_cipher; + return (cip->ic_miclen > 0 ? (*cip->ic_enmic)(k, skb) : 1); +} + +/* + * Reset key state to an unused state. The crypto + * key allocation mechanism insures other state (e.g. + * key data) is properly setup before a key is used. + */ +static inline void +ieee80211_crypto_resetkey(struct ieee80211com *ic, + struct ieee80211_key *k, u_int16_t ix) +{ + k->wk_cipher = &ieee80211_cipher_none;; + k->wk_private = k->wk_cipher->ic_attach(ic, k); + k->wk_keyix = ix; + k->wk_flags = IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV; +} + +/* + * Crypt-related notification methods. + */ +extern void ieee80211_notify_replay_failure(struct ieee80211com *, + const struct ieee80211_frame *, const struct ieee80211_key *, + u_int64_t rsc); +extern void ieee80211_notify_michael_failure(struct ieee80211com *, + const struct ieee80211_frame *, u_int keyix); +#endif /* defined(__KERNEL__) || defined(_KERNEL) */ +#endif /* _NET80211_IEEE80211_CRYPTO_H_ */ diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_crypto_ccmp.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_crypto_ccmp.c --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_crypto_ccmp.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_crypto_ccmp.c 2005-02-24 13:06:17.301131848 -0800 @@ -0,0 +1,528 @@ +/*- + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * IEEE 802.11i AES-CCMP crypto support. + * + * Part of this module is derived from similar code in the Host + * AP driver. The code is used with the consent of the author and + * it's license is included below. + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "if_media.h" + +#include + +#define AES_BLOCK_LEN 16 + +struct ccmp_ctx { + struct ieee80211com *cc_ic; /* for diagnostics */ + struct crypto_tfm *cc_tfm; +}; + +static void *ccmp_attach(struct ieee80211com *, struct ieee80211_key *); +static void ccmp_detach(struct ieee80211_key *); +static int ccmp_setkey(struct ieee80211_key *); +static int ccmp_encap(struct ieee80211_key *k, struct sk_buff *skb, + u_int8_t keyid); +static int ccmp_decap(struct ieee80211_key *, struct sk_buff *); +static int ccmp_enmic(struct ieee80211_key *, struct sk_buff *); +static int ccmp_demic(struct ieee80211_key *, struct sk_buff *); + +static const struct ieee80211_cipher ccmp = { + .ic_name = "AES-CCM", + .ic_cipher = IEEE80211_CIPHER_AES_CCM, + .ic_header = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + + IEEE80211_WEP_EXTIVLEN, + .ic_trailer = IEEE80211_WEP_MICLEN, + .ic_miclen = 0, + .ic_attach = ccmp_attach, + .ic_detach = ccmp_detach, + .ic_setkey = ccmp_setkey, + .ic_encap = ccmp_encap, + .ic_decap = ccmp_decap, + .ic_enmic = ccmp_enmic, + .ic_demic = ccmp_demic, +}; + +static int ccmp_encrypt(struct ieee80211_key *, struct sk_buff *, int hdrlen); +static int ccmp_decrypt(struct ieee80211_key *, u_int64_t pn, + struct sk_buff *, int hdrlen); + +static void * +ccmp_attach(struct ieee80211com *ic, struct ieee80211_key *k) +{ + struct ccmp_ctx *ctx; + + _MOD_INC_USE(THIS_MODULE, return NULL); + + MALLOC(ctx, struct ccmp_ctx *, sizeof(struct ccmp_ctx), + M_DEVBUF, M_NOWAIT | M_ZERO); + if (ctx == NULL) { + ic->ic_stats.is_crypto_nomem++; + _MOD_DEC_USE(THIS_MODULE); + return NULL; + } + + ctx->cc_ic = ic; + ctx->cc_tfm = crypto_alloc_tfm("aes", 0); + if (ctx->cc_tfm == NULL) { + FREE(ctx, M_DEVBUF); + _MOD_DEC_USE(THIS_MODULE); + return NULL; + } + return ctx; +} + +static void +ccmp_detach(struct ieee80211_key *k) +{ + struct ccmp_ctx *ctx = k->wk_private; + + if (ctx->cc_tfm != NULL) + crypto_free_tfm(ctx->cc_tfm); + FREE(ctx, M_DEVBUF); + + _MOD_DEC_USE(THIS_MODULE); +} + +static int +ccmp_setkey(struct ieee80211_key *k) +{ + struct ccmp_ctx *ctx = k->wk_private; + + if (k->wk_keylen != (128/NBBY)) { + IEEE80211_DPRINTF(ctx->cc_ic, IEEE80211_MSG_CRYPTO, + ("%s: Invalid key length %u, expecting %u\n", + __func__, k->wk_keylen, 128/NBBY)); + return 0; + } + if (k->wk_flags & IEEE80211_KEY_SWCRYPT) + crypto_cipher_setkey(ctx->cc_tfm, k->wk_key, k->wk_keylen); + return 1; +} + +/* + * Add privacy headers appropriate for the specified key. + */ +static int +ccmp_encap(struct ieee80211_key *k, struct sk_buff *skb, u_int8_t keyid) +{ + u_int8_t *ivp; + int hdrlen; + + hdrlen = ieee80211_hdrsize(skb->data); + + /* + * Copy down 802.11 header and add the IV, KeyID, and ExtIV. + */ + ivp = skb_push(skb, ccmp.ic_header); + memmove(ivp, ivp + ccmp.ic_header, hdrlen); + ivp += hdrlen; + + k->wk_keytsc++; /* XXX wrap at 48 bits */ + ivp[0] = k->wk_keytsc >> 0; /* PN0 */ + ivp[1] = k->wk_keytsc >> 8; /* PN1 */ + ivp[2] = 0; /* Reserved */ + ivp[3] = keyid | IEEE80211_WEP_EXTIV; /* KeyID | ExtID */ + ivp[4] = k->wk_keytsc >> 16; /* PN2 */ + ivp[5] = k->wk_keytsc >> 24; /* PN3 */ + ivp[6] = k->wk_keytsc >> 32; /* PN4 */ + ivp[7] = k->wk_keytsc >> 40; /* PN5 */ + + /* + * Finally, do software encrypt if neeed. + */ + if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) && + !ccmp_encrypt(k, skb, hdrlen)) + return 0; + + return 1; +} + +/* + * Add MIC to the frame as needed. + */ +static int +ccmp_enmic(struct ieee80211_key *k, struct sk_buff *skb) +{ + + return 1; +} + +static inline u_int64_t +READ_6(u_int8_t b0, u_int8_t b1, u_int8_t b2, u_int8_t b3, u_int8_t b4, u_int8_t b5) +{ + u_int32_t iv32 = (b0 << 0) | (b1 << 8) | (b2 << 16) | (b3 << 24); + u_int16_t iv16 = (b4 << 0) | (b5 << 8); + return (((u_int64_t)iv16) << 32) | iv32; +} + +/* + * Validate and strip privacy headers (and trailer) for a + * received frame. The specified key should be correct but + * is also verified. + */ +static int +ccmp_decap(struct ieee80211_key *k, struct sk_buff *skb) +{ + struct ccmp_ctx *ctx = k->wk_private; + struct ieee80211_frame *wh; + u_int8_t *ivp; + u_int64_t pn; + int hdrlen; + + /* + * Header should have extended IV and sequence number; + * verify the former and validate the latter. + */ + wh = (struct ieee80211_frame *)skb->data; + hdrlen = ieee80211_hdrsize(wh); + ivp = skb->data + hdrlen; + if ((ivp[IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) == 0) { + /* + * No extended IV; discard frame. + */ + IEEE80211_DPRINTF(ctx->cc_ic, IEEE80211_MSG_CRYPTO, + ("[%s] Missing ExtIV for AES-CCM cipher\n", + ether_sprintf(wh->i_addr2))); + ctx->cc_ic->ic_stats.is_rx_ccmpformat++; + return 0; + } + /* NB: assume IEEEE80211_WEP_MINLEN covers the extended IV */ + pn = READ_6(ivp[0], ivp[1], ivp[4], ivp[5], ivp[6], ivp[7]); + if (pn <= k->wk_keyrsc) { + /* + * Replay violation. + */ + ieee80211_notify_replay_failure(ctx->cc_ic, wh, k, pn); + ctx->cc_ic->ic_stats.is_rx_ccmpreplay++; + return 0; + } + + /* + * Check if the device handled the decrypt in hardware. + * If so we just strip the header; otherwise we need to + * handle the decrypt in software. Note that for the + * latter we leave the header in place for use in the + * decryption work. + */ + if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) && + !ccmp_decrypt(k, pn, skb, hdrlen)) + return 0; + + /* + * Copy up 802.11 header and strip crypto bits. + */ + memmove(skb->data + ccmp.ic_header, skb->data, hdrlen); + skb_pull(skb, ccmp.ic_header); + skb_trim(skb, skb->len - ccmp.ic_trailer); + + /* + * Ok to update rsc now. + */ + k->wk_keyrsc = pn; + + return 1; +} + +/* + * Verify and strip MIC from the frame. + */ +static int +ccmp_demic(struct ieee80211_key *k, struct sk_buff *skb) +{ + return 1; +} + +static inline void +xor_block(u8 *b, const u8 *a, size_t len) +{ + int i; + for (i = 0; i < len; i++) + b[i] ^= a[i]; +} + +static void +rijndael_encrypt(struct crypto_tfm *tfm, const void *src, void *dst) +{ + struct scatterlist sg_src; + struct scatterlist sg_dst; + + sg_src.page = virt_to_page(src); + sg_src.offset = offset_in_page(src); + sg_src.length = AES_BLOCK_LEN; + + sg_dst.page = virt_to_page(dst); + sg_dst.offset = offset_in_page(dst); + sg_dst.length = AES_BLOCK_LEN; + crypto_cipher_encrypt(tfm, &sg_dst, &sg_src, AES_BLOCK_LEN); +} + +/* + * Host AP crypt: host-based CCMP encryption implementation for Host AP driver + * + * Copyright (c) 2003-2004, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See README and COPYING for + * more details. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + */ + +static void +ccmp_init_blocks(struct crypto_tfm *tfm, struct ieee80211_frame *wh, + u_int64_t pn, size_t dlen, u8 *b0, u8 *aad, u8 *auth, u8 *s0) +{ +#define IS_4ADDRESS(wh) \ + ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS) +#define IS_QOS_DATA(wh) IEEE80211_QOS_HAS_SEQ(wh) + + /* CCM Initial Block: + * Flag (Include authentication header, M=3 (8-octet MIC), + * L=1 (2-octet Dlen)) + * Nonce: 0x00 | A2 | PN + * Dlen */ + b0[0] = 0x59; + /* NB: b0[1] set below */ + IEEE80211_ADDR_COPY(b0 + 2, wh->i_addr2); + b0[8] = pn >> 40; + b0[9] = pn >> 32; + b0[10] = pn >> 24; + b0[11] = pn >> 16; + b0[12] = pn >> 8; + b0[13] = pn >> 0; + b0[14] = (dlen >> 8) & 0xff; + b0[15] = dlen & 0xff; + + /* AAD: + * FC with bits 4..6 and 11..13 masked to zero; 14 is always one + * A1 | A2 | A3 + * SC with bits 4..15 (seq#) masked to zero + * A4 (if present) + * QC (if present) + */ + aad[0] = 0; /* AAD length >> 8 */ + /* NB: aad[1] set below */ + aad[2] = wh->i_fc[0] & 0x8f; /* XXX magic #s */ + aad[3] = wh->i_fc[1] & 0xc7; /* XXX magic #s */ + /* NB: we know 3 addresses are contiguous */ + memcpy(aad + 4, wh->i_addr1, 3 * IEEE80211_ADDR_LEN); + aad[22] = wh->i_seq[0] & IEEE80211_SEQ_FRAG_MASK; + aad[23] = 0; /* all bits masked */ + /* + * Construct variable-length portion of AAD based + * on whether this is a 4-address frame/QOS frame. + * We always zero-pad to 32 bytes before running it + * through the cipher. + * + * We also fill in the priority bits of the CCM + * initial block as we know whether or not we have + * a QOS frame. + */ + if (IS_4ADDRESS(wh)) { + IEEE80211_ADDR_COPY(aad + 24, + ((struct ieee80211_frame_addr4 *)wh)->i_addr4); + if (IS_QOS_DATA(wh)) { + struct ieee80211_qosframe_addr4 *qwh4 = + (struct ieee80211_qosframe_addr4 *) wh; + aad[30] = qwh4->i_qos[0] & 0x0f;/* just priority bits */ + aad[31] = 0; + b0[1] = aad[30]; + aad[1] = 22 + IEEE80211_ADDR_LEN + 2; + } else { + *(u_int16_t *)&aad[30] = 0; + b0[1] = 0; + aad[1] = 22 + IEEE80211_ADDR_LEN; + } + } else { + if (IS_QOS_DATA(wh)) { + struct ieee80211_qosframe *qwh = + (struct ieee80211_qosframe*) wh; + aad[24] = qwh->i_qos[0] & 0x0f; /* just priority bits */ + aad[25] = 0; + b0[1] = aad[24]; + aad[1] = 22 + 2; + } else { + *(u_int16_t *)&aad[24] = 0; + b0[1] = 0; + aad[1] = 22; + } + *(u_int16_t *)&aad[26] = 0; + *(u_int32_t *)&aad[28] = 0; + } + + /* Start with the first block and AAD */ + rijndael_encrypt(tfm, b0, auth); + xor_block(auth, aad, AES_BLOCK_LEN); + rijndael_encrypt(tfm, auth, auth); + xor_block(auth, &aad[AES_BLOCK_LEN], AES_BLOCK_LEN); + rijndael_encrypt(tfm, auth, auth); + b0[0] &= 0x07; + b0[14] = b0[15] = 0; + rijndael_encrypt(tfm, b0, s0); +#undef IS_QOS_DATA +#undef IS_4ADDRESS +} + +static int +ccmp_encrypt(struct ieee80211_key *key, struct sk_buff *skb, int hdrlen) +{ + struct ccmp_ctx *ctx = key->wk_private; + struct ieee80211_frame *wh; + int data_len, i, blocks, last, len; + u8 aad[2 * AES_BLOCK_LEN], b0[AES_BLOCK_LEN], b[AES_BLOCK_LEN], + e[AES_BLOCK_LEN], s0[AES_BLOCK_LEN]; + u8 *mic, *pos; + + ctx->cc_ic->ic_stats.is_crypto_ccmp++; + + wh = (struct ieee80211_frame *) skb->data; + if (skb_tailroom(skb) < ccmp.ic_trailer) { + /* NB: should not happen */ + IEEE80211_DPRINTF(ctx->cc_ic, IEEE80211_MSG_CRYPTO, + ("[%s] No room for %s MIC, tailroom %u\n", + ether_sprintf(wh->i_addr1), ccmp.ic_name, + skb_tailroom(skb))); + /* XXX statistic */ + return 0; + } + data_len = skb->len - (hdrlen + ccmp.ic_header); + ccmp_init_blocks(ctx->cc_tfm, wh, key->wk_keytsc, + data_len, b0, aad, b, s0); + + pos = skb->data + hdrlen + ccmp.ic_header; + blocks = (data_len + AES_BLOCK_LEN - 1) / AES_BLOCK_LEN; + last = data_len % AES_BLOCK_LEN; + + for (i = 1; i <= blocks; i++) { + len = (i == blocks && last) ? last : AES_BLOCK_LEN; + /* Authentication */ + xor_block(b, pos, len); + rijndael_encrypt(ctx->cc_tfm, b, b); + /* Encryption, with counter */ + b0[14] = (i >> 8) & 0xff; + b0[15] = i & 0xff; + rijndael_encrypt(ctx->cc_tfm, b0, e); + xor_block(pos, e, len); + pos += len; + } + + mic = skb_put(skb, ccmp.ic_trailer); + for (i = 0; i < ccmp.ic_trailer; i++) + mic[i] = b[i] ^ s0[i]; + + return 1; +} + +static int +ccmp_decrypt(struct ieee80211_key *key, u_int64_t pn, struct sk_buff *skb, int hdrlen) +{ + struct ccmp_ctx *ctx = key->wk_private; + struct ieee80211_frame *wh; + u8 aad[2 * AES_BLOCK_LEN]; + u8 b0[AES_BLOCK_LEN], b[AES_BLOCK_LEN], a[AES_BLOCK_LEN]; + int i, blocks, last, len; + size_t data_len; + u8 *pos, *mic; + + ctx->cc_ic->ic_stats.is_crypto_ccmp++; + + wh = (struct ieee80211_frame *) skb->data; + data_len = skb->len - (hdrlen + ccmp.ic_header + ccmp.ic_trailer); + ccmp_init_blocks(ctx->cc_tfm, wh, pn, data_len, b0, aad, a, b); + mic = skb->data + skb->len - ccmp.ic_trailer; + xor_block(mic, b, ccmp.ic_trailer); + + pos = skb->data + hdrlen + ccmp.ic_header; + blocks = (data_len + AES_BLOCK_LEN - 1) / AES_BLOCK_LEN; + last = data_len % AES_BLOCK_LEN; + + for (i = 1; i <= blocks; i++) { + len = (i == blocks && last) ? last : AES_BLOCK_LEN; + /* Decrypt, with counter */ + b0[14] = (i >> 8) & 0xff; + b0[15] = i & 0xff; + rijndael_encrypt(ctx->cc_tfm, b0, b); + xor_block(pos, b, len); + /* Authentication */ + xor_block(a, pos, len); + rijndael_encrypt(ctx->cc_tfm, a, a); + pos += len; + } + + if (memcmp(mic, a, ccmp.ic_trailer) != 0) { + IEEE80211_DPRINTF(ctx->cc_ic, IEEE80211_MSG_CRYPTO, + ("[%s] AES-CCM decrypt failed; MIC mismatch\n", + ether_sprintf(wh->i_addr2))); + ctx->cc_ic->ic_stats.is_rx_ccmpmic++; + return 0; + } + return 1; +} + +/* + * Module glue. + */ + +MODULE_AUTHOR("Errno Consulting, Sam Leffler"); +MODULE_DESCRIPTION("802.11 wireless support: AES-CCM cipher"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("Dual BSD/GPL"); +#endif + +static int __init +init_crypto_ccmp(void) +{ + ieee80211_crypto_register(&ccmp); + return 0; +} +module_init(init_crypto_ccmp); + +static void __exit +exit_crypto_ccmp(void) +{ + ieee80211_crypto_unregister(&ccmp); +} +module_exit(exit_crypto_ccmp); diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_crypto_none.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_crypto_none.c --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_crypto_none.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_crypto_none.c 2005-02-24 13:06:17.301131848 -0800 @@ -0,0 +1,143 @@ +/*- + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_crypto.c,v 1.3 2003/10/17 23:15:30 sam Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ieee80211_crypto.c,v 1.4 2003/09/23 16:03:46 dyoung Exp $"); + +/* + * IEEE 802.11 NULL crypto support. + */ +#include +#include +#include +#include +#include + +#include "if_media.h" + +#include + +static void *none_attach(struct ieee80211com *, struct ieee80211_key *); +static void none_detach(struct ieee80211_key *); +static int none_setkey(struct ieee80211_key *); +static int none_encap(struct ieee80211_key *, struct sk_buff *, u_int8_t); +static int none_decap(struct ieee80211_key *, struct sk_buff *); +static int none_enmic(struct ieee80211_key *, struct sk_buff *); +static int none_demic(struct ieee80211_key *, struct sk_buff *); + +const struct ieee80211_cipher ieee80211_cipher_none = { + .ic_name = "NONE", + .ic_cipher = IEEE80211_CIPHER_NONE, + .ic_header = 0, + .ic_trailer = 0, + .ic_miclen = 0, + .ic_attach = none_attach, + .ic_detach = none_detach, + .ic_setkey = none_setkey, + .ic_encap = none_encap, + .ic_decap = none_decap, + .ic_enmic = none_enmic, + .ic_demic = none_demic, +}; +EXPORT_SYMBOL(ieee80211_cipher_none); + +static void * +none_attach(struct ieee80211com *ic, struct ieee80211_key *k) +{ + return ic; /* for diagnostics+stats */ +} + +static void +none_detach(struct ieee80211_key *k) +{ + (void) k; +} + +static int +none_setkey(struct ieee80211_key *k) +{ + (void) k; + return 1; +} + +static int +none_encap(struct ieee80211_key *k, struct sk_buff *skb, u_int8_t keyid) +{ + struct ieee80211com *ic = k->wk_private; + struct ieee80211_frame *wh = (struct ieee80211_frame *)skb->data; + + /* + * The specified key is not setup; this can + * happen, at least, when changing keys. + */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + ("[%s] key id %u is not set (encap)\n", + ether_sprintf(wh->i_addr1), keyid>>6)); + ic->ic_stats.is_tx_badcipher++; + return 0; +} + +static int +none_decap(struct ieee80211_key *k, struct sk_buff *skb) +{ + struct ieee80211com *ic = k->wk_private; + struct ieee80211_frame *wh = (struct ieee80211_frame *)skb->data; + const u_int8_t *ivp = (const u_int8_t *)&wh[1]; + + /* + * The specified key is not setup; this can + * happen, at least, when changing keys. + */ + /* XXX useful to know dst too */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + ("[%s] key id %u is not set (decap)\n", + ether_sprintf(wh->i_addr2), ivp[IEEE80211_WEP_IVLEN] >> 6)); + ic->ic_stats.is_rx_badkeyid++; + return 0; +} + +static int +none_enmic(struct ieee80211_key *k, struct sk_buff *skb) +{ + struct ieee80211com *ic = k->wk_private; + + ic->ic_stats.is_tx_badcipher++; + return 0; +} + +static int +none_demic(struct ieee80211_key *k, struct sk_buff *skb) +{ + struct ieee80211com *ic = k->wk_private; + + ic->ic_stats.is_rx_badkeyid++; + return 0; +} diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_crypto_tkip.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_crypto_tkip.c --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_crypto_tkip.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_crypto_tkip.c 2005-02-24 13:06:17.302131696 -0800 @@ -0,0 +1,958 @@ +/*- + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * IEEE 802.11i TKIP crypto support. + * + * Part of this module is derived from similar code in the Host + * AP driver. The code is used with the consent of the author and + * it's license is included below. + */ +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_USE_CRYPTO_API +#include +#include +#include +#endif + +#include "if_media.h" + +#include + +static void *tkip_attach(struct ieee80211com *, struct ieee80211_key *); +static void tkip_detach(struct ieee80211_key *); +static int tkip_setkey(struct ieee80211_key *); +static int tkip_encap(struct ieee80211_key *, struct sk_buff *skb, + u_int8_t keyid); +static int tkip_enmic(struct ieee80211_key *, struct sk_buff *); +static int tkip_decap(struct ieee80211_key *, struct sk_buff *); +static int tkip_demic(struct ieee80211_key *, struct sk_buff *); + +static const struct ieee80211_cipher tkip = { + .ic_name = "TKIP", + .ic_cipher = IEEE80211_CIPHER_TKIP, + .ic_header = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + + IEEE80211_WEP_EXTIVLEN, + .ic_trailer = IEEE80211_WEP_CRCLEN, + .ic_miclen = IEEE80211_WEP_MICLEN, + .ic_attach = tkip_attach, + .ic_detach = tkip_detach, + .ic_setkey = tkip_setkey, + .ic_encap = tkip_encap, + .ic_decap = tkip_decap, + .ic_enmic = tkip_enmic, + .ic_demic = tkip_demic, +}; + +struct tkip_ctx { + struct ieee80211com *tc_ic; /* for diagnostics */ + + u16 tx_ttak[5]; + int tx_phase1_done; + u8 tx_rc4key[16]; /* XXX for test module; make locals? */ + + u16 rx_ttak[5]; + int rx_phase1_done; + u8 rx_rc4key[16]; /* XXX for test module; make locals? */ + u_int64_t rx_rsc; /* held until MIC verified */ + +#ifdef CONFIG_USE_CRYPTO_API + struct crypto_tfm *tfm_michael; +#endif +}; + +static void michael_mic_hdr(const struct ieee80211_frame *, u8 hdr[16]); +static void michael_mic(struct tkip_ctx *, const u8 *key, const u8 hdr[16], + const u8 *data, size_t data_len, u8 mic[IEEE80211_WEP_MICLEN]); +static int tkip_encrypt(struct tkip_ctx *, struct ieee80211_key *, + struct sk_buff *, int hdr_len); +static int tkip_decrypt(struct tkip_ctx *, struct ieee80211_key *, + struct sk_buff *, int hdr_len); + +static void * +tkip_attach(struct ieee80211com *ic, struct ieee80211_key *k) +{ + struct tkip_ctx *ctx; + + _MOD_INC_USE(THIS_MODULE, return NULL); + + MALLOC(ctx, struct tkip_ctx *, sizeof(struct tkip_ctx), + M_DEVBUF, M_NOWAIT | M_ZERO); + if (ctx == NULL) { + ic->ic_stats.is_crypto_nomem++; + _MOD_DEC_USE(THIS_MODULE); + return NULL; + } + + ctx->tc_ic = ic; + if (k->wk_flags & IEEE80211_KEY_SWMIC) { +#ifdef CONFIG_USE_CRYPTO_API + /* XXX safe for concurrent bidirectional use? */ + ctx->tfm_michael = crypto_alloc_tfm("michael_mic", 0); + if (priv->tfm_michael == NULL) + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + ("%s: Warning, no michael_mic crypto API " + "support, using private version." __func__)); +#endif + } + return ctx; +} + +static void +tkip_detach(struct ieee80211_key *k) +{ + struct tkip_ctx *ctx = k->wk_private; + +#ifdef CONFIG_USE_CRYPTO_API + if (ctx->tfm_michael != NULL) + crypto_free_tfm(ctx->tfm_michael); +#endif + FREE(ctx, M_DEVBUF); + + _MOD_DEC_USE(THIS_MODULE); +} + +static int +tkip_setkey(struct ieee80211_key *k) +{ + struct tkip_ctx *ctx = k->wk_private; + + if (k->wk_keylen != (128/NBBY)) { + IEEE80211_DPRINTF(ctx->tc_ic, IEEE80211_MSG_CRYPTO, + ("%s: Invalid key length %u, expecting %u\n", + __func__, k->wk_keylen, 128/NBBY)); + return 0; + } + k->wk_keytsc = 1; /* TSC starts at 1 */ + return 1; +} + +/* + * Add privacy headers and do any s/w encryption required. + */ +static int +tkip_encap(struct ieee80211_key *k, struct sk_buff *skb, u_int8_t keyid) +{ + struct tkip_ctx *ctx = k->wk_private; + struct ieee80211com *ic = ctx->tc_ic; + u_int8_t *ivp; + int hdrlen; + + /* + * Handle TKIP counter measures requirement. + */ + if (ic->ic_flags & IEEE80211_F_COUNTERM) { + struct ieee80211_frame *wh = + (struct ieee80211_frame *) skb->data; + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + ("[%s] Discard frame due to countermeasures (%s)\n", + ether_sprintf(wh->i_addr2), __func__)); + ic->ic_stats.is_crypto_tkipcm++; + return 0; + } + hdrlen = ieee80211_hdrsize(skb->data); + + /* + * Copy down 802.11 header and add the IV, KeyID, and ExtIV. + */ + ivp = skb_push(skb, tkip.ic_header); + memmove(ivp, ivp + tkip.ic_header, hdrlen); + ivp += hdrlen; + + ivp[0] = k->wk_keytsc >> 8; /* TSC1 */ + ivp[1] = (ivp[0] | 0x20) & 0x7f; /* WEP seed */ + ivp[2] = k->wk_keytsc >> 0; /* TSC0 */ + ivp[3] = keyid | IEEE80211_WEP_EXTIV; /* KeyID | ExtID */ + ivp[4] = k->wk_keytsc >> 16; /* TSC2 */ + ivp[5] = k->wk_keytsc >> 24; /* TSC3 */ + ivp[6] = k->wk_keytsc >> 32; /* TSC4 */ + ivp[7] = k->wk_keytsc >> 40; /* TSC5 */ + + /* + * Finally, do software encrypt if neeed. + */ + if (k->wk_flags & IEEE80211_KEY_SWCRYPT) { + if (!tkip_encrypt(ctx, k, skb, hdrlen)) + return 0; + /* NB: tkip_encrypt handles wk_keytsc */ + } else + k->wk_keytsc++; /* XXX wrap at 48 bits */ + + return 1; +} + +/* + * Add MIC to the frame as needed. + */ +static int +tkip_enmic(struct ieee80211_key *k, struct sk_buff *skb) +{ + struct tkip_ctx *ctx = k->wk_private; + u8 hdr[16]; + int hdrlen; + + if (k->wk_flags & IEEE80211_KEY_SWMIC) { + struct ieee80211_frame *wh = + (struct ieee80211_frame *) skb->data; + u_int8_t *mic; + + ctx->tc_ic->ic_stats.is_crypto_tkipenmic++; + + if (skb_tailroom(skb) < tkip.ic_miclen) { + /* NB: should not happen */ + IEEE80211_DPRINTF(ctx->tc_ic, IEEE80211_MSG_CRYPTO, + ("[%s] No room for Michael MIC, tailroom %u\n", + ether_sprintf(wh->i_addr1), skb_tailroom(skb))); + /* XXX statistic */ + return 0; + } + + hdrlen = ieee80211_hdrsize(wh); + + michael_mic_hdr(wh, hdr); + mic = skb_put(skb, tkip.ic_miclen); + michael_mic(ctx, k->wk_txmic, hdr, + skb->data + hdrlen, + skb->len - (hdrlen + tkip.ic_miclen), + mic); + } + return 1; +} + +static inline u_int64_t +READ_6(u_int8_t b0, u_int8_t b1, u_int8_t b2, u_int8_t b3, u_int8_t b4, u_int8_t b5) +{ + u_int32_t iv32 = (b0 << 0) | (b1 << 8) | (b2 << 16) | (b3 << 24); + u_int16_t iv16 = (b4 << 0) | (b5 << 8); + return (((u_int64_t)iv16) << 32) | iv32; +} + +/* + * Validate and strip privacy headers (and trailer) for a + * received frame. If necessary, decrypt the frame using + * the specified key. + */ +static int +tkip_decap(struct ieee80211_key *k, struct sk_buff *skb) +{ + struct tkip_ctx *ctx = k->wk_private; + struct ieee80211com *ic = ctx->tc_ic; + struct ieee80211_frame *wh; + u_int8_t *ivp; + int hdrlen; + + /* + * Header should have extended IV and sequence number; + * verify the former and validate the latter. + */ + wh = (struct ieee80211_frame *)skb->data; + hdrlen = ieee80211_hdrsize(wh); + ivp = skb->data + hdrlen; + if ((ivp[IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) == 0) { + /* + * No extended IV; discard frame. + */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + ("[%s] Missing ExtIV for TKIP cipher\n", + ether_sprintf(wh->i_addr2))); + ic->ic_stats.is_rx_tkipformat++; + return 0; + } + /* + * Handle TKIP counter measures requirement. + */ + if (ic->ic_flags & IEEE80211_F_COUNTERM) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + ("[%s] Discard frame due to countermeasures (%s)\n", + ether_sprintf(wh->i_addr2), __func__)); + ic->ic_stats.is_crypto_tkipcm++; + return 0; + } + + /* NB: assume IEEEE80211_WEP_MINLEN covers the extended IV */ + ctx->rx_rsc = READ_6(ivp[2], ivp[0], ivp[4], ivp[5], + ivp[6], ivp[7]); + if (ctx->rx_rsc <= k->wk_keyrsc) { + /* + * Replay violation; notify upper layer. + */ + ieee80211_notify_replay_failure(ic, wh, k, ctx->rx_rsc); + ic->ic_stats.is_rx_tkipreplay++; + return 0; + } + /* + * NB: We can't update the rsc in the key until MIC is verified. + * + * We assume we are not preempted between doing the check above + * and updating wk_keyrsc when stripping the MIC in tkip_demic. + * Otherwise we might process another packet and discard it as + * a replay. + */ + + /* + * Check if the device handled the decrypt in hardware. + * If so we just strip the header; otherwise we need to + * handle the decrypt in software. + */ + if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) && + !tkip_decrypt(ctx, k, skb, hdrlen)) + return 0; + + /* + * Copy up 802.11 header and strip crypto bits. + */ + memmove(skb->data + tkip.ic_header, skb->data, hdrlen); + skb_pull(skb, tkip.ic_header); + skb_trim(skb, skb->len - tkip.ic_trailer); + + return 1; +} + +/* + * Verify and strip MIC from the frame. + */ +static int +tkip_demic(struct ieee80211_key *k, struct sk_buff *skb) +{ + struct tkip_ctx *ctx = k->wk_private; + u8 hdr[16]; + u8 mic[IEEE80211_WEP_MICLEN]; + int hdrlen; + + if (k->wk_flags & IEEE80211_KEY_SWMIC) { + struct ieee80211_frame *wh = + (struct ieee80211_frame *) skb->data; + + ctx->tc_ic->ic_stats.is_crypto_tkipdemic++; + + hdrlen = ieee80211_hdrsize(wh); + + michael_mic_hdr(wh, hdr); + michael_mic(ctx, k->wk_rxmic, hdr, + skb->data + hdrlen, + skb->len - (hdrlen + tkip.ic_miclen), + mic); + if (memcmp(mic, skb->data+skb->len - tkip.ic_miclen, tkip.ic_miclen)) { + /* NB: 802.11 layer handles statistic and debug msg */ + ieee80211_notify_michael_failure(ctx->tc_ic, wh, + k->wk_keyix); + return 0; + } + } + /* + * Strip MIC from the tail. + */ + skb_trim(skb, skb->len - tkip.ic_miclen); + + /* + * Ok to update rsc now that MIC has been verified. + */ + k->wk_keyrsc = ctx->rx_rsc; + + return 1; +} + +/* + * Host AP crypt: host-based TKIP encryption implementation for Host AP driver + * + * Copyright (c) 2003-2004, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. See README and COPYING for + * more details. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + */ + +static const __u32 crc32_table[256] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; + +static inline u16 RotR1(u16 val) +{ + return (val >> 1) | (val << 15); +} + +static inline u8 Lo8(u16 val) +{ + return val & 0xff; +} + +static inline u8 Hi8(u16 val) +{ + return val >> 8; +} + +static inline u16 Lo16(u32 val) +{ + return val & 0xffff; +} + +static inline u16 Hi16(u32 val) +{ + return val >> 16; +} + +static inline u16 Mk16(u8 hi, u8 lo) +{ + return lo | (((u16) hi) << 8); +} + +static inline u16 Mk16_le(u16 *v) +{ + return le16_to_cpu(*v); +} + +static const u16 Sbox[256] = { + 0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154, + 0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A, + 0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B, + 0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B, + 0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F, + 0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F, + 0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5, + 0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F, + 0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB, + 0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397, + 0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED, + 0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A, + 0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194, + 0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3, + 0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104, + 0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D, + 0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39, + 0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695, + 0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83, + 0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76, + 0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4, + 0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B, + 0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0, + 0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018, + 0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751, + 0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85, + 0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12, + 0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9, + 0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7, + 0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A, + 0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8, + 0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A, +}; + +static inline u16 _S_(u16 v) +{ + u16 t = Sbox[Hi8(v)]; + return Sbox[Lo8(v)] ^ ((t << 8) | (t >> 8)); +} + +#define PHASE1_LOOP_COUNT 8 + +static void tkip_mixing_phase1(u16 *TTAK, const u8 *TK, const u8 *TA, u32 IV32) +{ + int i, j; + + /* Initialize the 80-bit TTAK from TSC (IV32) and TA[0..5] */ + TTAK[0] = Lo16(IV32); + TTAK[1] = Hi16(IV32); + TTAK[2] = Mk16(TA[1], TA[0]); + TTAK[3] = Mk16(TA[3], TA[2]); + TTAK[4] = Mk16(TA[5], TA[4]); + + for (i = 0; i < PHASE1_LOOP_COUNT; i++) { + j = 2 * (i & 1); + TTAK[0] += _S_(TTAK[4] ^ Mk16(TK[1 + j], TK[0 + j])); + TTAK[1] += _S_(TTAK[0] ^ Mk16(TK[5 + j], TK[4 + j])); + TTAK[2] += _S_(TTAK[1] ^ Mk16(TK[9 + j], TK[8 + j])); + TTAK[3] += _S_(TTAK[2] ^ Mk16(TK[13 + j], TK[12 + j])); + TTAK[4] += _S_(TTAK[3] ^ Mk16(TK[1 + j], TK[0 + j])) + i; + } +} + +#ifndef _BYTE_ORDER +#error "Don't know native byte order" +#endif + +static void tkip_mixing_phase2(u8 *WEPSeed, const u8 *TK, const u16 *TTAK, + u16 IV16) +{ + /* Make temporary area overlap WEP seed so that the final copy can be + * avoided on little endian hosts. */ + u16 *PPK = (u16 *) &WEPSeed[4]; + + /* Step 1 - make copy of TTAK and bring in TSC */ + PPK[0] = TTAK[0]; + PPK[1] = TTAK[1]; + PPK[2] = TTAK[2]; + PPK[3] = TTAK[3]; + PPK[4] = TTAK[4]; + PPK[5] = TTAK[4] + IV16; + + /* Step 2 - 96-bit bijective mixing using S-box */ + PPK[0] += _S_(PPK[5] ^ Mk16_le((u16 *) &TK[0])); + PPK[1] += _S_(PPK[0] ^ Mk16_le((u16 *) &TK[2])); + PPK[2] += _S_(PPK[1] ^ Mk16_le((u16 *) &TK[4])); + PPK[3] += _S_(PPK[2] ^ Mk16_le((u16 *) &TK[6])); + PPK[4] += _S_(PPK[3] ^ Mk16_le((u16 *) &TK[8])); + PPK[5] += _S_(PPK[4] ^ Mk16_le((u16 *) &TK[10])); + + PPK[0] += RotR1(PPK[5] ^ Mk16_le((u16 *) &TK[12])); + PPK[1] += RotR1(PPK[0] ^ Mk16_le((u16 *) &TK[14])); + PPK[2] += RotR1(PPK[1]); + PPK[3] += RotR1(PPK[2]); + PPK[4] += RotR1(PPK[3]); + PPK[5] += RotR1(PPK[4]); + + /* Step 3 - bring in last of TK bits, assign 24-bit WEP IV value + * WEPSeed[0..2] is transmitted as WEP IV */ + WEPSeed[0] = Hi8(IV16); + WEPSeed[1] = (Hi8(IV16) | 0x20) & 0x7F; + WEPSeed[2] = Lo8(IV16); + WEPSeed[3] = Lo8((PPK[5] ^ Mk16_le((u16 *) &TK[0])) >> 1); + +#if _BYTE_ORDER == _BIG_ENDIAN + { + int i; + for (i = 0; i < 6; i++) + PPK[i] = (PPK[i] << 8) | (PPK[i] >> 8); + } +#endif +} + +static void +wep_encrypt(u8 *key, u8 *buf, size_t buflen, u8 *icv) +{ + u32 i, j, k, crc; + u8 S[256]; + u8 *pos; +#define S_SWAP(a,b) do { u8 t = S[a]; S[a] = S[b]; S[b] = t; } while(0) + + /* Setup RC4 state */ + for (i = 0; i < 256; i++) + S[i] = i; + j = 0; + for (i = 0; i < 256; i++) { + j = (j + S[i] + key[i & 0x0f]) & 0xff; + S_SWAP(i, j); + } + + /* Compute CRC32 over unencrypted data and apply RC4 to data */ + crc = ~0; + i = j = 0; + pos = buf; + for (k = 0; k < buflen; k++) { + crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8); + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + *pos++ ^= S[(S[i] + S[j]) & 0xff]; + } + crc = ~crc; + + /* Append little-endian CRC32 and encrypt it to produce ICV */ + pos = icv; + pos[0] = crc; + pos[1] = crc >> 8; + pos[2] = crc >> 16; + pos[3] = crc >> 24; + for (k = 0; k < 4; k++) { + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + *pos++ ^= S[(S[i] + S[j]) & 0xff]; + } +} + +static int +wep_decrypt(u8 *key, u8 *buf, size_t plen) +{ + u32 i, j, k, crc; + u8 S[256]; + u8 *pos, icv[4]; + + /* Setup RC4 state */ + for (i = 0; i < 256; i++) + S[i] = i; + j = 0; + for (i = 0; i < 256; i++) { + j = (j + S[i] + key[i & 0x0f]) & 0xff; + S_SWAP(i, j); + } + + /* Apply RC4 to data and compute CRC32 over decrypted data */ + pos = buf; + crc = ~0; + i = j = 0; + for (k = 0; k < plen; k++) { + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + *pos ^= S[(S[i] + S[j]) & 0xff]; + crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8); + pos++; + } + crc = ~crc; + + /* Encrypt little-endian CRC32 and verify that it matches with the + * received ICV */ + icv[0] = crc; + icv[1] = crc >> 8; + icv[2] = crc >> 16; + icv[3] = crc >> 24; + for (k = 0; k < 4; k++) { + i = (i + 1) & 0xff; + j = (j + S[i]) & 0xff; + S_SWAP(i, j); + if ((icv[k] ^ S[(S[i] + S[j]) & 0xff]) != *pos++) { + /* ICV mismatch - drop frame */ + return -1; + } + } + + return 0; +} + + +static inline u32 rotl(u32 val, int bits) +{ + return (val << bits) | (val >> (32 - bits)); +} + + +static inline u32 rotr(u32 val, int bits) +{ + return (val >> bits) | (val << (32 - bits)); +} + + +static inline u32 xswap(u32 val) +{ + return ((val & 0x00ff00ff) << 8) | ((val & 0xff00ff00) >> 8); +} + + +#define michael_block(l, r) \ +do { \ + r ^= rotl(l, 17); \ + l += r; \ + r ^= xswap(l); \ + l += r; \ + r ^= rotl(l, 3); \ + l += r; \ + r ^= rotr(l, 2); \ + l += r; \ +} while (0) + + +static inline u32 get_le32(const u8 *p) +{ + return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); +} + + +static inline void put_le32(u8 *p, u32 v) +{ + p[0] = v; + p[1] = v >> 8; + p[2] = v >> 16; + p[3] = v >> 24; +} + + +static void michael_mic_private(struct tkip_ctx *ctx, const u8 *key, const u8 hdr[16], + const u8 *data, size_t data_len, u8 mic[IEEE80211_WEP_MICLEN]) +{ + u32 l, r; + int i, blocks, last; + + l = get_le32(key); + r = get_le32(key + 4); + + /* Michael MIC pseudo header: DA, SA, 3 x 0, Priority */ + l ^= get_le32(hdr); + michael_block(l, r); + l ^= get_le32(&hdr[4]); + michael_block(l, r); + l ^= get_le32(&hdr[8]); + michael_block(l, r); + l ^= get_le32(&hdr[12]); + michael_block(l, r); + + /* 32-bit blocks of data */ + blocks = data_len / 4; + last = data_len % 4; + for (i = 0; i < blocks; i++) { + l ^= get_le32(&data[4 * i]); + michael_block(l, r); + } + + /* Last block and padding (0x5a, 4..7 x 0) */ + switch (last) { + case 0: + l ^= 0x5a; + break; + case 1: + l ^= data[4 * i] | 0x5a00; + break; + case 2: + l ^= data[4 * i] | (data[4 * i + 1] << 8) | 0x5a0000; + break; + case 3: + l ^= data[4 * i] | (data[4 * i + 1] << 8) | + (data[4 * i + 2] << 16) | 0x5a000000; + break; + } + michael_block(l, r); + /* l ^= 0; */ + michael_block(l, r); + + put_le32(mic, l); + put_le32(mic + 4, r); +} + +static void +michael_mic(struct tkip_ctx *ctx, const u8 *key, const u8 hdr[16], + const u8 *data, size_t data_len, u8 mic[IEEE80211_WEP_MICLEN]) +{ +#ifdef CONFIG_USE_CRYPTO_API + if (ctx->tfm_michael != NULL) { + struct scatterlist sg[2]; + + sg[0].page = virt_to_page(hdr); + sg[0].offset = offset_in_page(hdr); + sg[0].length = 16; + + sg[1].page = virt_to_page(data); + sg[1].offset = offset_in_page(data); + sg[1].length = data_len; + + crypto_digest_init(ctx->tfm_michael); + crypto_digest_setkey(ctx->tfm_michael, key, 8); + crypto_digest_update(ctx->tfm_michael, sg, 2); + crypto_digest_final(ctx->tfm_michael, mic); + } else +#endif /* CONFIG_USE_CRYPTO_API */ + michael_mic_private(ctx, key, hdr, data, data_len, mic); +} + +/* + * Craft pseudo header used to calculate the MIC. + */ +static void +michael_mic_hdr(const struct ieee80211_frame *wh0, u8 hdr[16]) +{ + const struct ieee80211_frame_addr4 *wh = + (struct ieee80211_frame_addr4 *) wh0; + + switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { + case IEEE80211_FC1_DIR_NODS: + IEEE80211_ADDR_COPY(hdr, wh->i_addr1); /* DA */ + IEEE80211_ADDR_COPY(hdr + IEEE80211_ADDR_LEN, wh->i_addr2); + break; + case IEEE80211_FC1_DIR_TODS: + IEEE80211_ADDR_COPY(hdr, wh->i_addr3); /* DA */ + IEEE80211_ADDR_COPY(hdr + IEEE80211_ADDR_LEN, wh->i_addr2); + break; + case IEEE80211_FC1_DIR_FROMDS: + IEEE80211_ADDR_COPY(hdr, wh->i_addr1); /* DA */ + IEEE80211_ADDR_COPY(hdr + IEEE80211_ADDR_LEN, wh->i_addr3); + break; + case IEEE80211_FC1_DIR_DSTODS: + IEEE80211_ADDR_COPY(hdr, wh->i_addr3); /* DA */ + IEEE80211_ADDR_COPY(hdr + IEEE80211_ADDR_LEN, wh->i_addr4); + break; + } + + hdr[12] = 0; /* XXX qos priority */ + hdr[13] = hdr[14] = hdr[15] = 0; /* reserved */ +} + +static int +tkip_encrypt(struct tkip_ctx *ctx, struct ieee80211_key *key, + struct sk_buff *skb, int hdrlen) +{ + struct ieee80211_frame *wh; + u8 *icv; + + ctx->tc_ic->ic_stats.is_crypto_tkip++; + + wh = (struct ieee80211_frame *) skb->data; + if (skb_tailroom(skb) < tkip.ic_trailer) { + /* NB: should not happen */ + IEEE80211_DPRINTF(ctx->tc_ic, IEEE80211_MSG_CRYPTO, + ("[%s] No room for %s ICV, tailroom %u\n", + ether_sprintf(wh->i_addr1), tkip.ic_name, + skb_tailroom(skb))); + /* XXX statistic */ + return 0; + } + if (!ctx->tx_phase1_done) { + tkip_mixing_phase1(ctx->tx_ttak, key->wk_key, wh->i_addr2, + (u32)(key->wk_keytsc >> 16)); + ctx->tx_phase1_done = 1; + } + tkip_mixing_phase2(ctx->tx_rc4key, key->wk_key, ctx->tx_ttak, + (u16) key->wk_keytsc); + + icv = skb_put(skb, tkip.ic_trailer); + wep_encrypt(ctx->tx_rc4key, + skb->data + hdrlen + tkip.ic_header, + skb->len - (hdrlen + tkip.ic_header + tkip.ic_trailer), + icv); + + key->wk_keytsc++; /* XXX wrap at 48 bits */ + if ((u16)(key->wk_keytsc) == 0) + ctx->tx_phase1_done = 0; + return 1; +} + +static int +tkip_decrypt(struct tkip_ctx *ctx, struct ieee80211_key *key, + struct sk_buff *skb, int hdrlen) +{ + struct ieee80211_frame *wh; + u32 iv32; + u16 iv16; + + ctx->tc_ic->ic_stats.is_crypto_tkip++; + + wh = (struct ieee80211_frame *) skb->data; + /* NB: tkip_decap already verified header and left seq in rx_rsc */ + iv16 = (u16) ctx->rx_rsc; + iv32 = (u32) (ctx->rx_rsc >> 16); + + if (iv32 != (u32)(key->wk_keyrsc >> 16) || !ctx->rx_phase1_done) { + tkip_mixing_phase1(ctx->rx_ttak, key->wk_key, + wh->i_addr2, iv32); + ctx->rx_phase1_done = 1; + } + tkip_mixing_phase2(ctx->rx_rc4key, key->wk_key, ctx->rx_ttak, iv16); + + /* NB: skb is unstripped; deduct headers + ICV to get payload */ + if (wep_decrypt(ctx->rx_rc4key, + skb->data + hdrlen + tkip.ic_header, + skb->len - (hdrlen + tkip.ic_header + tkip.ic_trailer))) { + if (iv32 != (u32)(key->wk_keyrsc >> 16)) { + /* Previously cached Phase1 result was already lost, so + * it needs to be recalculated for the next packet. */ + ctx->rx_phase1_done = 0; + } + IEEE80211_DPRINTF(ctx->tc_ic, IEEE80211_MSG_CRYPTO, + ("[%s] TKIP ICV mismatch on decrypt\n", + ether_sprintf(wh->i_addr2))); + ctx->tc_ic->ic_stats.is_rx_tkipicv++; + return 0; + } + return 1; +} + +/* + * Module glue. + */ + +MODULE_AUTHOR("Errno Consulting, Sam Leffler"); +MODULE_DESCRIPTION("802.11 wireless support: TKIP cipher"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("Dual BSD/GPL"); +#endif + +static int __init +init_crypto_tkip(void) +{ + ieee80211_crypto_register(&tkip); + return 0; +} +module_init(init_crypto_tkip); + +static void __exit +exit_crypto_tkip(void) +{ + ieee80211_crypto_unregister(&tkip); +} +module_exit(exit_crypto_tkip); diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_crypto_wep.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_crypto_wep.c --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_crypto_wep.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_crypto_wep.c 2005-02-24 13:06:17.303131544 -0800 @@ -0,0 +1,407 @@ +/*- + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * IEEE 802.11 WEP crypto support. + */ +#include +#include +#include +#include +#include +#include +#include + +#include "if_media.h" + +#include + +#include "rc4.h" +#define arc4_ctxlen() sizeof (struct rc4_state) +#define arc4_setkey(_c,_k,_l) rc4_init(_c,_k,_l) +#define arc4_encrypt(_c,_d,_s,_l) rc4_crypt_skip(_c,_s,_d,_l,0) +#define arc4_decrypt(_c,_d,_s,_l) rc4_crypt_skip(_c,_s,_d,_l,0) + +static void *wep_attach(struct ieee80211com *, struct ieee80211_key *); +static void wep_detach(struct ieee80211_key *); +static int wep_setkey(struct ieee80211_key *); +static int wep_encap(struct ieee80211_key *, struct sk_buff *, u_int8_t keyid); +static int wep_decap(struct ieee80211_key *, struct sk_buff *); +static int wep_enmic(struct ieee80211_key *, struct sk_buff *); +static int wep_demic(struct ieee80211_key *, struct sk_buff *); + +static const struct ieee80211_cipher wep = { + .ic_name = "WEP", + .ic_cipher = IEEE80211_CIPHER_WEP, + .ic_header = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN, + .ic_trailer = IEEE80211_WEP_CRCLEN, + .ic_miclen = 0, + .ic_attach = wep_attach, + .ic_detach = wep_detach, + .ic_setkey = wep_setkey, + .ic_encap = wep_encap, + .ic_decap = wep_decap, + .ic_enmic = wep_enmic, + .ic_demic = wep_demic, +}; + +static int wep_encrypt(struct ieee80211_key *, struct sk_buff *, int hdrlen); +static int wep_decrypt(struct ieee80211_key *, struct sk_buff *, int hdrlen); + +struct wep_ctx_hw { /* for use with h/w support */ + struct ieee80211com *wc_ic; /* for diagnostics */ + u_int32_t wc_iv; /* initial vector for crypto */ +}; + +struct wep_ctx { + struct ieee80211com *wc_ic; /* for diagnostics */ + u_int32_t wc_iv; /* initial vector for crypto */ + struct rc4_state wc_rc4; /* rc4 state */ +}; + +static void * +wep_attach(struct ieee80211com *ic, struct ieee80211_key *k) +{ + struct wep_ctx *ctx; + + _MOD_INC_USE(THIS_MODULE, return NULL); + + /* NB: only allocate rc4 state when doing s/w crypto */ + MALLOC(ctx, struct wep_ctx *, + k->wk_flags & IEEE80211_KEY_SWCRYPT ? + sizeof(struct wep_ctx) : sizeof(struct wep_ctx_hw), + M_DEVBUF, M_NOWAIT | M_ZERO); + if (ctx == NULL) { + ic->ic_stats.is_crypto_nomem++; + _MOD_DEC_USE(THIS_MODULE); + return NULL; + } + + ctx->wc_ic = ic; + get_random_bytes(&ctx->wc_iv, sizeof(ctx->wc_iv)); + return ctx; +} + +static void +wep_detach(struct ieee80211_key *k) +{ + struct wep_ctx *ctx = k->wk_private; + + FREE(ctx, M_DEVBUF); + + _MOD_DEC_USE(THIS_MODULE); +} + +static int +wep_setkey(struct ieee80211_key *k) +{ + return k->wk_keylen >= 40/NBBY; +} + +#ifndef _BYTE_ORDER +#error "Don't know native byte order" +#endif + +/* + * Add privacy headers appropriate for the specified key. + */ +static int +wep_encap(struct ieee80211_key *k, struct sk_buff *skb, u_int8_t keyid) +{ + struct wep_ctx *ctx = k->wk_private; + u_int32_t iv; + u_int8_t *ivp; + int hdrlen; + + hdrlen = ieee80211_hdrsize(skb->data); + + /* + * Copy down 802.11 header and add the IV + KeyID. + */ + ivp = skb_push(skb, wep.ic_header); + memmove(ivp, ivp + wep.ic_header, hdrlen); + ivp += hdrlen; + + /* + * XXX + * IV must not duplicate during the lifetime of the key. + * But no mechanism to renew keys is defined in IEEE 802.11 + * for WEP. And the IV may be duplicated at other stations + * because the session key itself is shared. So we use a + * pseudo random IV for now, though it is not the right way. + * + * NB: Rather than use a strictly random IV we select a + * random one to start and then increment the value for + * each frame. This is an explicit tradeoff between + * overhead and security. Given the basic insecurity of + * WEP this seems worthwhile. + */ + + /* + * Skip 'bad' IVs from Fluhrer/Mantin/Shamir: + * (B, 255, N) with 3 <= B < 16 and 0 <= N <= 255 + */ + iv = ctx->wc_iv; + if ((iv & 0xff00) == 0xff00) { + int B = (iv & 0xff0000) >> 16; + if (3 <= B && B < 16) + iv += 0x0100; + } + ctx->wc_iv = iv + 1; + + /* + * NB: Preserve byte order of IV for packet + * sniffers; it doesn't matter otherwise. + */ +#if _BYTE_ORDER == _BIG_ENDIAN + ivp[0] = iv >> 0; + ivp[1] = iv >> 8; + ivp[2] = iv >> 16; +#else + ivp[2] = iv >> 0; + ivp[1] = iv >> 8; + ivp[0] = iv >> 16; +#endif + ivp[3] = keyid; + + /* + * Finally, do software encrypt if neeed. + */ + if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) && + !wep_encrypt(k, skb, hdrlen)) + return 0; + + return 1; +} + +/* + * Add MIC to the frame as needed. + */ +static int +wep_enmic(struct ieee80211_key *k, struct sk_buff *skb) +{ + + return 1; +} + +/* + * Validate and strip privacy headers (and trailer) for a + * received frame. If necessary, decrypt the frame using + * the specified key. + */ +static int +wep_decap(struct ieee80211_key *k, struct sk_buff *skb) +{ + struct wep_ctx *ctx = k->wk_private; + struct ieee80211_frame *wh; + int hdrlen; + + wh = (struct ieee80211_frame *)skb->data; + hdrlen = ieee80211_hdrsize(wh); + + /* + * Check if the device handled the decrypt in hardware. + * If so we just strip the header; otherwise we need to + * handle the decrypt in software. + */ + if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) && + !wep_decrypt(k, skb, hdrlen)) { + IEEE80211_DPRINTF(ctx->wc_ic, IEEE80211_MSG_CRYPTO, + ("[%s] WEP ICV mismatch on decrypt\n", + ether_sprintf(wh->i_addr2))); + ctx->wc_ic->ic_stats.is_rx_wepfail++; + return 0; + } + + /* + * Copy up 802.11 header and strip crypto bits. + */ + memmove(skb->data + wep.ic_header, skb->data, hdrlen); + skb_pull(skb, wep.ic_header); + skb_trim(skb, skb->len - wep.ic_trailer); + + return 1; +} + +/* + * Verify and strip MIC from the frame. + */ +static int +wep_demic(struct ieee80211_key *k, struct sk_buff *skb) +{ + return 1; +} + +/* + * CRC 32 -- routine from RFC 2083 + */ + +/* Table of CRCs of all 8-bit messages */ +static u_int32_t crc_table[256]; + +/* Make the table for a fast CRC. */ +static void +crc_init(void) +{ + u_int32_t c; + int n, k; + + for (n = 0; n < 256; n++) { + c = (u_int32_t)n; + for (k = 0; k < 8; k++) { + if (c & 1) + c = 0xedb88320UL ^ (c >> 1); + else + c = c >> 1; + } + crc_table[n] = c; + } +} + +/* + * Update a running CRC with the bytes buf[0..len-1]--the CRC + * should be initialized to all 1's, and the transmitted value + * is the 1's complement of the final running CRC + */ + +static u_int32_t +crc_update(u_int32_t crc, u_int8_t *buf, int len) +{ + u_int8_t *endbuf; + + for (endbuf = buf + len; buf < endbuf; buf++) + crc = crc_table[(crc ^ *buf) & 0xff] ^ (crc >> 8); + return crc; +} + +static int +wep_encrypt(struct ieee80211_key *key, struct sk_buff *skb, int hdrlen) +{ + struct wep_ctx *ctx = key->wk_private; + u_int8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE]; + u_int8_t crcbuf[IEEE80211_WEP_CRCLEN]; + u_int8_t *icv; + u_int32_t crc; + + KASSERT(key->wk_flags & IEEE80211_KEY_SWCRYPT, ("No s/w crypto state")); + + ctx->wc_ic->ic_stats.is_crypto_wep++; + + if (skb_tailroom(skb) < wep.ic_trailer) { + struct ieee80211_frame *wh = + (struct ieee80211_frame *) skb->data; + /* NB: should not happen */ + IEEE80211_DPRINTF(ctx->wc_ic, IEEE80211_MSG_CRYPTO, + ("[%s] No room for %s ICV, tailroom %u\n", + ether_sprintf(wh->i_addr1), wep.ic_name, + skb_tailroom(skb))); + /* XXX statistic */ + return 0; + } + memcpy(rc4key, skb->data + hdrlen, IEEE80211_WEP_IVLEN); + memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen); + arc4_setkey(&ctx->wc_rc4, rc4key, IEEE80211_WEP_IVLEN + key->wk_keylen); + + /* calculate CRC over unencrypted data */ + crc = crc_update(~0, + skb->data + hdrlen + wep.ic_header, + skb->len - (hdrlen + wep.ic_header)); + /* encrypt data */ + arc4_encrypt(&ctx->wc_rc4, + skb->data + hdrlen + wep.ic_header, + skb->data + hdrlen + wep.ic_header, + skb->len - (hdrlen + wep.ic_header)); + /* tack on ICV */ + *(u_int32_t *)crcbuf = htole32(~crc); + icv = skb_put(skb, IEEE80211_WEP_CRCLEN); + arc4_encrypt(&ctx->wc_rc4, icv, crcbuf, IEEE80211_WEP_CRCLEN); + + return 1; +} + +static int +wep_decrypt(struct ieee80211_key *key, struct sk_buff *skb, int hdrlen) +{ + struct wep_ctx *ctx = key->wk_private; + u_int8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE]; + u_int8_t crcbuf[IEEE80211_WEP_CRCLEN]; + u_int8_t *icv; + u_int32_t crc; + + KASSERT(key->wk_flags & IEEE80211_KEY_SWCRYPT, ("No s/w crypto state")); + + ctx->wc_ic->ic_stats.is_crypto_wep++; + + memcpy(rc4key, skb->data + hdrlen, IEEE80211_WEP_IVLEN); + memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen); + arc4_setkey(&ctx->wc_rc4, rc4key, IEEE80211_WEP_IVLEN + key->wk_keylen); + + /* decrypt data */ + arc4_decrypt(&ctx->wc_rc4, + skb->data + hdrlen + wep.ic_header, + skb->data + hdrlen + wep.ic_header, + skb->len - (hdrlen + wep.ic_header + wep.ic_trailer)); + /* calculate CRC over unencrypted data */ + crc = crc_update(~0, + skb->data + hdrlen + wep.ic_header, + skb->len - (hdrlen + wep.ic_header + wep.ic_trailer)); + /* decrypt ICV and compare to CRC */ + icv = skb->data + (skb->len - IEEE80211_WEP_CRCLEN); + arc4_decrypt(&ctx->wc_rc4, crcbuf, icv, IEEE80211_WEP_CRCLEN); + + return (crc == ~le32toh(*(u_int32_t *)crcbuf)); +} + +/* + * Module glue. + */ + +MODULE_AUTHOR("Errno Consulting, Sam Leffler"); +MODULE_DESCRIPTION("802.11 wireless support: WEP cipher"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("Dual BSD/GPL"); +#endif + +static int __init +init_crypto_wep(void) +{ + ieee80211_crypto_register(&wep); + crc_init(); + return 0; +} +module_init(init_crypto_wep); + +static void __exit +exit_crypto_wep(void) +{ + ieee80211_crypto_unregister(&wep); +} +module_exit(exit_crypto_wep); diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_dot1x.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_dot1x.c --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_dot1x.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_dot1x.c 2005-02-24 13:06:17.304131392 -0800 @@ -0,0 +1,1799 @@ +/*- + * Copyright (c) 2004-2005 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EXPORT_SYMTAB +#define EXPORT_SYMTAB +#endif + +/* + * 802.1x+WPA authenticator protocol handling and state machine. + * + * This support is optional; it is only used when the 802.11 layer's + * authentication mode is set to use 802.1x or WPA is enabled separately + * (for WPA-PSK). If compiled as a module this code does not need + * to be present unless 802.1x/WPA-PSK is in use. + * + * The authenticator hooks into the 802.11 layer through callbacks + * that are invoked when stations join and leave (associate and + * deassociate). Authentication state is managed separately from the + * 802.11 layer's node state. State is synchronized with a single + * lock. This scheme is also used by the optional radius client. + * + * It might be possible to generalize the 802.1x support to handle + * non-802.11 devices but for now it is tightly integrated with the + * 802.11 code. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "if_media.h" +#include "if_llc.h" +#include "if_ethersubr.h" + +#include +#include +#include + +#ifndef TRUE +enum { TRUE = 1, FALSE = 0 }; /* for consistency with spec */ +#endif + +/* + * Global settings that can be modified with sysctl's. + */ +u_int eapol_idletimo = 30; /* 30 seconds */ +u_int eapol_reauthlimit = 5; +u_int eapol_reauthmin = 60; /* 60 seconds */ +u_int eapol_reauthtimo = 5*60; /* 5 minutes */ +u_int eapol_txtimo = 3; /* 3 seconds */ +u_int eapol_supptimo = 30; /* 3O seconds */ +u_int eapol_servtimo = 3; /* 3 seconds */ +u_int eapol_suppreqlimit = 2; +u_int eapol_servreqlimit = 2; + +u_int eapol_keytxenabled = TRUE; /* ena/dis EAPOL key trasmission */ +u_int eapol_reauthenabled = TRUE; /* ena/dis reauthentication */ + +#ifdef IEEE80211_DEBUG +static const char *eapol_as_states[] = { + "EAPOL_AS_INIT", + "EAPOL_AS_DISCONNECTED", + "EAPOL_AS_CONNECTING", + "EAPOL_AS_AUTHENTICATING", + "EAPOL_AS_AUTHENTICATED", + "EAPOL_AS_ABORTING", + "EAPOL_AS_HELD", + "EAPOL_AS_FORCE_AUTH", + "EAPOL_AS_FORCE_UNAUTH", +}; +static const char *eapol_abs_states[] = { + "EAPOL_ABS_INIT", + "EAPOL_ABS_IDLE", + "EAPOL_ABS_REQUEST", + "EAPOL_ABS_RESPONSE", + "EAPOL_ABS_SUCCESS", + "EAPOL_ABS_FAIL", + "EAPOL_ABS_TIMEOUT", +}; +static const char *eapol_aks_states[] = { + "EAPOL_AKS_NO_KEY", + "EAPOL_AKS_KEY", +}; +static const char *eapol_ars_states[] = { + "EAPOL_ARS_INIT", + "EAPOL_ARS_REAUTH", +}; +#endif /* IEEE80211_DEBUG */ + +MALLOC_DEFINE(M_EAPOL_NODE, "node", "802.1x node state"); + +struct eapolcom *eapolcom = NULL; /* ``the'' authenticator */ +struct eapolstats eapolstats = { 0 }; +EXPORT_SYMBOL(eapolstats); + +static struct eapol_auth_node *eapol_new_node(struct eapolcom *, + struct ieee80211com *, struct ieee80211_node *); +static void txCannedFail(struct eapol_auth_node *); +static void txCannedSuccess(struct eapol_auth_node *); +static void txReqId(struct eapol_auth_node *); +static void eap_send_simple(struct eapol_node *, u_int8_t code, u_int8_t id); + +/* + * Input handling (messages from the supplicant). + */ + +/* + * Process an EAP frame; we're only interested in + * EAP Response messages. + */ +static int +eapol_auth_input_eap(struct eapol_auth_node *ean, struct eap_hdr *eap, + struct sk_buff *skb) +{ + struct eapolcom *ec; + + EAPOL_LOCK_ASSERT(ean->ean_ec); + + if (eap->eap_id != ean->ean_currentId) { + IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1X, + ("[%s] EAP id mismatch, got %u, expecting %u\n", + ether_sprintf(ean->ean_node->ni_macaddr), + eap->eap_id, ean->ean_currentId)); + eapolstats.eas_idmismatch++; + goto out; + } + if (eap->eap_code != EAP_CODE_RESPONSE) { + /* unexpected response from station */ + IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1X, + ("[%s] unexpected EAP code %u\n", + ether_sprintf(ean->ean_node->ni_macaddr), + eap->eap_code)); + eapolstats.eas_badcode++; + goto out; + } + ec = ean->ean_ec; + if (eap->eap_type == EAP_TYPE_IDENTITY) { + /* + * Record identity string. + */ + /* NB: known to be in host byte order */ + if (eap->eap_len > sizeof(struct eap_hdr)+EAP_IDENTITY_MAXLEN) { + IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1X, + ("[%s] EAP Identity len %u too big\n", + ether_sprintf(ean->ean_node->ni_macaddr), + eap->eap_len)); + eapolstats.eas_badidlen++; + goto out; + } + ean->ean_idlen = eap->eap_len - sizeof(struct eap_hdr); + memcpy(ean->ean_id, &eap[1], ean->ean_idlen); + ean->ean_id[ean->ean_idlen] = '\0'; + ean->ean_rxRespId = TRUE; + IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1X, + ("[%s] RECV EAP Identity \"%s\"\n", + ether_sprintf(ean->ean_node->ni_macaddr), + ean->ean_id)); + /* + * Let radius client reset it's state. + */ + if (ec->ec_radius) + (*ec->ec_radius->rc_identity_input)(ean, skb); + } else { + IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1X, + ("[%s] RECV EAP msg type %u len %u\n", + ether_sprintf(ean->ean_node->ni_macaddr), + eap->eap_type, eap->eap_len)); + if (eap->eap_type != EAP_TYPE_NAK) + ean->ean_backendNonNakResponsesForSupplicant++; + ean->ean_rxResp = TRUE; + } + /* + * Save the frame contents for later. + */ + if (ean->ean_skb) + kfree_skb(ean->ean_skb); + ean->ean_skb = skb; + + eapol_fsm_run(ean); + return 0; +out: + kfree_skb(skb); + return 0; +} + +/* + * Process an EAPOL Key frame. + */ +static int +eapol_auth_input_key(struct eapol_auth_node *ean, struct eapol_hdr *eapol, + struct sk_buff *skb) +{ + + EAPOL_LOCK_ASSERT(ean->ean_ec); + + IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1X, + ("[%s] RECV EAPOL WPA-Key\n", + ether_sprintf(ean->ean_node->ni_macaddr))); + + kfree_skb(skb); + return 0; +} + +/* + * Receive an EAPOL frame from the device layer. This + * routine is installed as a packet handler for EAPOL + * Ethernet frames when the authenticator is attached. + */ +int +eapol_input(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt) +{ + struct eapolcom *ec; + struct ieee80211com *ic; + struct ieee80211_node *ni; + struct eapol_hdr *eapol; + struct eapol_auth_node *ean; + struct eap_hdr *eap; + int error = 0; + + if (eapolcom == NULL) { + eapolstats.eps_noauth++; + goto out; + } + ec = eapolcom; + if (!pskb_may_pull(skb, sizeof(struct eapol_hdr))) { + eapolstats.eps_tooshort++; + goto out; + } + skb = skb_share_check(skb, GFP_ATOMIC); + if (skb == NULL) { + eapolstats.eps_sharecheck++; + goto out; + } + eapol = (struct eapol_hdr *)skb->data; + (void) skb_pull(skb, sizeof(struct eapol_hdr)); + /* NB: XXX give netfilter a chance? */ + + /* + * NB: The spec says backwards compatibility for packet + * types is preserved. So we are safe to accept + * packets with a version number >= to our version + * and still be able to correctly process the packets + * we know/care about. + */ + if (eapol->eapol_ver < EAPOL_VERSION) { + eapolstats.eps_badver++; + goto out; + } + /* + * Find session based on sender's MAC address. + */ + /* NB: must release reference */ + ic = ieee80211_find_vap(skb->mac.ethernet->h_dest); + if (ic == NULL) { + eapolstats.eps_noinstance++; + goto out; + } + ni = ieee80211_find_node(ic, skb->mac.ethernet->h_source); + if (ni == NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_DOT1X, + ("[%s] EAPOL msg type %u with no 802.11 node\n", + ether_sprintf(skb->mac.ethernet->h_source), + eapol->eapol_type)); + eapolstats.eps_nonode++; + goto out; + } + EAPOL_LOCK(ec); + ean = eapolcom->ec_table[IEEE80211_NODE_AID(ni)]; + if (ean == NULL && eapol->eapol_type != EAPOL_TYPE_START) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_DOT1X, + ("[%s] EAPOL msg type %u with no session\n", + ether_sprintf(ni->ni_macaddr), eapol->eapol_type)); + eapolstats.eps_nosession++; + goto unlock; + } + /* put length in host byte order */ + eapol->eapol_len = ntohs(eapol->eapol_len); + + switch (eapol->eapol_type) { + case EAPOL_TYPE_EAP: + if (!pskb_may_pull(skb, sizeof(struct eap_hdr_short))) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_DOT1X, + ("[%s] EAP msg too short, eapol len %u\n", + ether_sprintf(ean->ean_node->ni_macaddr), + eapol->eapol_len)); + eapolstats.eap_tooshort++; + goto unlock; + } + eap = (struct eap_hdr *)skb->data; + eap->eap_len = ntohs(eap->eap_len); + if (eap->eap_len < eapol->eapol_len) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_DOT1X, + ("[%s] EAP length invalid, %u < eapol len %u\n", + ether_sprintf(ean->ean_node->ni_macaddr), + eap->eap_len, eapol->eapol_len)); + eapolstats.eap_lenmismatch++; + goto unlock; + } + + switch (eap->eap_code) { + case EAP_CODE_RESPONSE: + if (!pskb_may_pull(skb, sizeof(struct eap_hdr))) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_DOT1X, + ("[%s] EAP msg too short, len %u\n", + ether_sprintf(ean->ean_node->ni_macaddr), + skb->len)); + eapolstats.eap_tooshort++; + goto unlock; + } + error = eapol_auth_input_eap(ean, eap, skb); + skb = NULL; /* consumed */ + break; + case EAP_CODE_SUCCESS: + case EAP_CODE_FAILURE: + case EAP_CODE_REQUEST: + /* XXX no need for these */ + /* fall thru... */ + default: + eapolstats.eap_badcode++; + goto unlock; + } + break; + case EAPOL_TYPE_START: + if (ean == NULL) { + ean = eapol_new_node(eapolcom, ic, ni); + if (ean == NULL) + goto unlock; + } + IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1X, + ("[%s] RECV EAPOL Start\n", + ether_sprintf(ean->ean_node->ni_macaddr))); + ean->ean_eapStart = TRUE; + eapol_fsm_run(ean); + break; + case EAPOL_TYPE_LOGOFF: + IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1X, + ("[%s] RECV EAPOL Logoff\n", + ether_sprintf(ean->ean_node->ni_macaddr))); + ean->ean_eapLogoff = TRUE; + eapol_fsm_run(ean); + break; + case EAPOL_TYPE_KEY: + if (!pskb_may_pull(skb, sizeof(struct eapol_wpa_key))) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_DOT1X, + ("[%s] EAPOL KEY msg too short, eapol len %u\n", + ether_sprintf(ean->ean_node->ni_macaddr), + eapol->eapol_len)); + eapolstats.eap_keytooshort++; + goto unlock; + } + error = eapol_auth_input_key(ean, eapol, skb); + skb = NULL; /* consumed */ + eapol_fsm_run(ean); + break; + case EAPOL_TYPE_EASFA: + /* XXX not handled */ + /* fall thru... */ + default: + eapolstats.eps_badtype++; + goto unlock; + } +unlock: + /* NB: unref is safe 'cuz ean holds a ref */ + ieee80211_unref_node(&ni); + EAPOL_UNLOCK(ec); +out: + if (skb != NULL) + kfree_skb(skb); + return error; +} + +/* + * 802.1x+WPA state machine support. We follow the 802.1x and + * WPA specs by defining independent state machines for the + * supplicant-authenticator, authenticator-backend, reauthentication + * timer, and key transmit handling. These machines are ``clocked'' + * together when various events take place (receipt of EAPOL messages + * from the supplicant, radius messages from the backened, timers, etc.) + */ + +#define STATE_DECL(type,state) \ + static void eapol_auth_##type##_##state(struct eapol_auth_node *ean) +#define STATE_ENTER(type,state, ean) eapol_auth_##type##_##state(ean) + +/* + * Authenticator state machine. + */ +#define AS_STATE_DECL(s) STATE_DECL(AS,s) +#define AS_STATE_ENTER(s,ean) STATE_ENTER(AS,s,ean) +#ifdef IEEE80211_DEBUG +#define AS_STATE_DEBUG(s,ean) \ + IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1XSM, \ + ("[%s] %s -> %s\n", \ + ether_sprintf(ean->ean_node->ni_macaddr), \ + eapol_as_states[ean->ean_authState], \ + eapol_as_states[EAPOL_AS_##s])) +#define AS_STATE_OPT_DEBUG(s,ean) do { \ + if (ean->ean_authState != EAPOL_AS_##s) \ + AS_STATE_DEBUG(s,ean); \ +} while (0) +#else +#define AS_STATE_DEBUG(s,ean) +#define AS_STATE_OPT_DEBUG(s,ean) +#endif + +AS_STATE_DECL(INIT) +{ + struct eapolcom *ec = ean->ean_ec; + + AS_STATE_OPT_DEBUG(INIT, ean); + + (*ec->ec_node_reset)(ean); /* reset non-fsm state */ + ean->ean_currentId = 0; + ean->ean_portMode = EAPOL_PORTMODE_AUTO; + ean->ean_authState = EAPOL_AS_INIT; +} + +AS_STATE_DECL(DISCONNECTED) +{ + AS_STATE_DEBUG(DISCONNECTED, ean); + + ean->ean_portStatus = EAPOL_PORTSTATUS_UNAUTH; + ieee80211_node_unauthorize(ean->ean_ic, ean->ean_node); + + ean->ean_eapLogoff = FALSE; + ean->ean_reAuthCount = 0; + if (ean->ean_authState != EAPOL_AS_INIT) { + txCannedFail(ean); + ean->ean_currentId++; + } + ean->ean_authState = EAPOL_AS_DISCONNECTED; + /* XXX remove station keys */ +} + +AS_STATE_DECL(HELD) +{ + + AS_STATE_DEBUG(HELD, ean); + + ean->ean_portStatus = EAPOL_PORTSTATUS_UNAUTH; + ieee80211_node_unauthorize(ean->ean_ic, ean->ean_node); + + ean->ean_quietWhile = eapol_idletimo; + ean->ean_eapLogoff = FALSE; + ean->ean_currentId++; + ean->ean_authState = EAPOL_AS_HELD; +} + +AS_STATE_DECL(CONNECTING) +{ + + AS_STATE_DEBUG(CONNECTING, ean); + + ean->ean_eapStart = FALSE; + ean->ean_reAuthenticate = FALSE; + ean->ean_txWhen = eapol_txtimo; + ean->ean_rxRespId = FALSE; + txReqId(ean); + ean->ean_reAuthCount++; + ean->ean_authState = EAPOL_AS_CONNECTING; +} + +AS_STATE_DECL(AUTHENTICATED) +{ + AS_STATE_DEBUG(AUTHENTICATED, ean); + + ean->ean_portStatus = EAPOL_PORTSTATUS_AUTH; + ieee80211_node_authorize(ean->ean_ic, ean->ean_node); + + ean->ean_reAuthCount = 0; + ean->ean_currentId++; + ean->ean_authState = EAPOL_AS_AUTHENTICATED; +} + +AS_STATE_DECL(AUTHENTICATING) +{ + AS_STATE_DEBUG(AUTHENTICATING, ean); + + ean->ean_authSuccess = FALSE; + ean->ean_authFail = FALSE; + ean->ean_authTimeout = FALSE; + ean->ean_authStart = TRUE; + ean->ean_authState = EAPOL_AS_AUTHENTICATING; +} + +AS_STATE_DECL(ABORTING) +{ + AS_STATE_DEBUG(ABORTING, ean); + + ean->ean_currentId++; + ean->ean_authAbort = TRUE; + ean->ean_authState = EAPOL_AS_ABORTING; +} + +AS_STATE_DECL(FORCE_AUTH) +{ + AS_STATE_DEBUG(FORCE_AUTH, ean); + + ean->ean_portStatus = EAPOL_PORTSTATUS_AUTH; + ieee80211_node_authorize(ean->ean_ic, ean->ean_node); + + ean->ean_portMode = EAPOL_PORTMODE_FORCEAUTH; + ean->ean_eapStart = FALSE; + txCannedSuccess(ean); + ean->ean_currentId++; + ean->ean_authState = EAPOL_AS_FORCE_AUTH; +} + +AS_STATE_DECL(FORCE_UNAUTH) +{ + AS_STATE_DEBUG(FORCE_UNAUTH, ean); + + ean->ean_portStatus = EAPOL_PORTSTATUS_UNAUTH; + ieee80211_node_unauthorize(ean->ean_ic, ean->ean_node); + + ean->ean_portMode = EAPOL_PORTMODE_FORCEUNAUTH; + ean->ean_eapStart = FALSE; + txCannedFail(ean); + ean->ean_currentId++; + ean->ean_authState = EAPOL_AS_FORCE_UNAUTH; +} + +/* + * Carry out a state transition in the authentication + * state machine. This routine encapsulates Figure 8-8 + * in the spec. + */ +static void +eapol_auth_step(struct eapol_auth_node *ean) +{ + + EAPOL_LOCK_ASSERT(ean->ean_ec); + + /* NB: portEnabled is implicit */ + if ((ean->ean_portControl == EAPOL_PORTCONTROL_AUTO && + ean->ean_portMode != ean->ean_portControl) || + ean->ean_initialize) { + AS_STATE_ENTER(INIT, ean); + return; + } + switch (ean->ean_authState) { + case EAPOL_AS_INIT: + AS_STATE_ENTER(DISCONNECTED, ean); + break; + case EAPOL_AS_DISCONNECTED: + AS_STATE_ENTER(CONNECTING, ean); + break; + case EAPOL_AS_HELD: + if (ean->ean_quietWhile == 0) + AS_STATE_ENTER(CONNECTING, ean); + break; + case EAPOL_AS_CONNECTING: + if (ean->ean_reAuthCount <= eapol_reauthlimit) { + if (ean->ean_rxRespId) { + ean->ean_txWhen = 0; + ean->ean_authEntersAuthenticating++; + AS_STATE_ENTER(AUTHENTICATING, ean); + } else if (ean->ean_txWhen == 0 || ean->ean_eapStart || + ean->ean_reAuthenticate) { + AS_STATE_ENTER(CONNECTING, ean); + } else if (ean->ean_eapLogoff) { + AS_STATE_ENTER(DISCONNECTED, ean); + } + } else { + /* + * Forcibly deauthenticate station and reset + * state. The spec says we go back to a + * DISCONNECTED state but the station is no + * longer associated at this point so we do + * not maintain state. + */ + IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1XSM, + ("[%s] reauth limit reached, deauth station\n", + ether_sprintf(ean->ean_node->ni_macaddr))); + IEEE80211_SEND_MGMT(ean->ean_ic, ean->ean_node, + IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_AUTH_EXPIRE); + ean->ean_gone = TRUE; + } + break; + case EAPOL_AS_AUTHENTICATED: + if (ean->ean_eapStart || ean->ean_reAuthenticate) { + if (ean->ean_eapStart) + ean->ean_authAuthEapStartsWhileAuthenticated++; + if (ean->ean_reAuthenticate) + ean->ean_authAuthReauthsWhileAuthenticated++; + AS_STATE_ENTER(CONNECTING, ean); + } else if (ean->ean_eapLogoff) { + ean->ean_authAuthEapLogoffWhileAuthenticated++; + AS_STATE_ENTER(DISCONNECTED, ean); + } + break; + case EAPOL_AS_AUTHENTICATING: + if (ean->ean_authSuccess) { + ean->ean_authAuthSuccessesWhileAuthenticating++; + AS_STATE_ENTER(AUTHENTICATED, ean); + } else if (ean->ean_authFail) { + ean->ean_authAuthFailWhileAuthenticating++; + AS_STATE_ENTER(HELD, ean); + } else if (ean->ean_reAuthenticate || ean->ean_eapStart || + ean->ean_eapLogoff || ean->ean_authTimeout) { + if (ean->ean_reAuthenticate) + ean->ean_authAuthReauthsWhileAuthenticating++; + if (ean->ean_eapStart) + ean->ean_authAuthEapStartsWhileAuthenticating++; + if (ean->ean_eapLogoff) + ean->ean_authAuthEapLogoffWhileAuthenticating++; + if (ean->ean_authTimeout) + ean->ean_authAuthTimeoutsWhileAuthenticating++; + AS_STATE_ENTER(ABORTING, ean); + } + break; + case EAPOL_AS_ABORTING: + if (ean->ean_eapLogoff && !ean->ean_authAbort) + AS_STATE_ENTER(DISCONNECTED, ean); + else if (!ean->ean_eapLogoff && !ean->ean_authAbort) + AS_STATE_ENTER(CONNECTING, ean); + break; + case EAPOL_AS_FORCE_AUTH: + if (ean->ean_eapStart) + AS_STATE_ENTER(FORCE_AUTH, ean); + break; + case EAPOL_AS_FORCE_UNAUTH: + if (ean->ean_eapStart) + AS_STATE_ENTER(FORCE_UNAUTH, ean); + break; + default: + eapolstats.eps_badauthfsm++; + break; + } +} +#undef AS_STATE_DECL +#undef AS_STATE_ENTER +#undef AS_STATE_DEBUG + +/* + * Backend state machine. + */ +#define ABS_STATE_DECL(s) STATE_DECL(ABS,s) +#define ABS_STATE_ENTER(s,ean) STATE_ENTER(ABS,s,ean) +#ifdef IEEE80211_DEBUG +#define ABS_STATE_DEBUG(s,ean) \ + IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1XSM, \ + ("[%s] %s -> %s\n", \ + ether_sprintf(ean->ean_node->ni_macaddr), \ + eapol_abs_states[ean->ean_backendState], \ + eapol_abs_states[EAPOL_ABS_##s])) +#define ABS_STATE_OPT_DEBUG(s,ean) do { \ + if (ean->ean_backendState != EAPOL_ABS_##s) \ + ABS_STATE_DEBUG(s,ean); \ +} while (0) +#else +#define ABS_STATE_DEBUG(s,ean) +#define ABS_STATE_OPT_DEBUG(s,ean) +#endif + +ABS_STATE_DECL(INIT) +{ + ABS_STATE_OPT_DEBUG(INIT, ean); + + /* abort authentication XXX */ + ean->ean_aWhile = 0; + ean->ean_authAbort = FALSE; + ean->ean_keyAvailable = FALSE; /* XXX key fsm */ + ean->ean_backendState = EAPOL_ABS_INIT; +} + +ABS_STATE_DECL(IDLE) +{ + ABS_STATE_OPT_DEBUG(IDLE, ean); + + ean->ean_authStart = FALSE; /* XXX */ + ean->ean_reqCount = 0; + ean->ean_reqSrvCount = 0; + ean->ean_backendState = EAPOL_ABS_IDLE; +} + +ABS_STATE_DECL(REQUEST) +{ +#define txReq(ean) (*ean->ean_ec->ec_radius->rc_txreq)(ean) + ABS_STATE_DEBUG(REQUEST, ean); + + ean->ean_currentId = ean->ean_idFromServer; + txReq(ean); + ean->ean_aWhile = eapol_supptimo; + ean->ean_reqCount++; + ean->ean_reqSrvCount = 0; + ean->ean_backendState = EAPOL_ABS_REQUEST; +#undef txReq +} + +ABS_STATE_DECL(RESPONSE) +{ +#define sendRespToServer(ean) (*ean->ean_ec->ec_radius->rc_sendsrvr)(ean) + ABS_STATE_DEBUG(RESPONSE, ean); + + ean->ean_aReq = FALSE; + ean->ean_aSuccess = FALSE; + ean->ean_authTimeout = FALSE; + ean->ean_rxResp = FALSE; + ean->ean_aFail = FALSE; + ean->ean_aWhile = eapol_servtimo; + ean->ean_reqCount = 0; + sendRespToServer(ean); + ean->ean_reqSrvCount++; + ean->ean_backendState = EAPOL_ABS_RESPONSE; +#undef sendRespToServer +} + +ABS_STATE_DECL(SUCCESS) +{ + ABS_STATE_DEBUG(SUCCESS, ean); + + ean->ean_aWhile = 0; + ean->ean_currentId = ean->ean_idFromServer; + txCannedSuccess(ean); + ean->ean_authSuccess = TRUE; + ean->ean_backendState = EAPOL_ABS_SUCCESS; +} + +ABS_STATE_DECL(FAIL) +{ + ABS_STATE_DEBUG(FAIL, ean); + + ean->ean_aWhile = 0; + ean->ean_currentId = ean->ean_idFromServer; + txCannedFail(ean); + ean->ean_authFail = TRUE; + ean->ean_backendState = EAPOL_ABS_FAIL; +} + +ABS_STATE_DECL(TIMEOUT) +{ + ABS_STATE_DEBUG(TIMEOUT, ean); + + if (ean->ean_portStatus == EAPOL_PORTSTATUS_UNAUTH) + txCannedFail(ean); + ean->ean_authTimeout = TRUE; + ean->ean_backendState = EAPOL_ABS_TIMEOUT; +} + +/* + * Carry out a state transition in the backend state machine. + * This routine encapsulates Figure 8-12 in the spec. + */ +static void +eapol_backend_step(struct eapol_auth_node *ean) +{ + + EAPOL_LOCK_ASSERT(ean->ean_ec); + + if (ean->ean_portControl != EAPOL_PORTCONTROL_AUTO || + ean->ean_initialize || ean->ean_authAbort) { + ABS_STATE_ENTER(INIT, ean); + return; + } + switch (ean->ean_backendState) { + case EAPOL_ABS_INIT: + ABS_STATE_ENTER(IDLE, ean); + break; + case EAPOL_ABS_IDLE: + if (ean->ean_authStart) + ABS_STATE_ENTER(RESPONSE, ean); + break; + case EAPOL_ABS_REQUEST: + if (ean->ean_rxResp) + ABS_STATE_ENTER(RESPONSE, ean); + else if (ean->ean_aWhile == 0) { + if (ean->ean_reqCount < eapol_suppreqlimit) + ABS_STATE_ENTER(REQUEST, ean); + else + ABS_STATE_ENTER(TIMEOUT, ean); + } + break; + case EAPOL_ABS_RESPONSE: + if (ean->ean_aReq) + ABS_STATE_ENTER(REQUEST, ean); + else if (ean->ean_aFail) + ABS_STATE_ENTER(FAIL, ean); + else if (ean->ean_aSuccess) + ABS_STATE_ENTER(SUCCESS, ean); + else if (ean->ean_aWhile == 0) { + if (ean->ean_reqSrvCount < eapol_servreqlimit) + ABS_STATE_ENTER(RESPONSE, ean); + else + ABS_STATE_ENTER(TIMEOUT, ean); + } + break; + case EAPOL_ABS_SUCCESS: + case EAPOL_ABS_FAIL: + case EAPOL_ABS_TIMEOUT: + ABS_STATE_ENTER(IDLE, ean); + break; + default: + eapolstats.eps_badbackendfsm++; + break; + } +} +#undef ABS_STATE_DECL +#undef ABS_STATE_ENTER +#undef ABS_STATE_DEBUG + +/* + * Reauthentication timer state machine. + */ +#define REAUTH_STATE_DECL(s) STATE_DECL(ARS,s) +#define REAUTH_STATE_ENTER(s,ean) STATE_ENTER(ARS,s,ean) +#ifdef IEEE80211_DEBUG +#define REAUTH_STATE_DEBUG(s,ean) \ + IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1XSM, \ + ("[%s] %s -> %s\n", \ + ether_sprintf(ean->ean_node->ni_macaddr), \ + eapol_ars_states[ean->ean_reAuthState], \ + eapol_ars_states[EAPOL_ARS_##s])) +#define REAUTH_STATE_OPT_DEBUG(s,ean) do { \ + if (ean->ean_reAuthState != EAPOL_ARS_##s) \ + REAUTH_STATE_DEBUG(s,ean); \ +} while (0) +#else +#define REAUTH_STATE_DEBUG(s,ean) +#define REAUTH_STATE_OPT_DEBUG(s,ean) +#endif + +REAUTH_STATE_DECL(INIT) +{ + REAUTH_STATE_OPT_DEBUG(INIT, ean); + + ean->ean_reAuthWhen = eapol_reauthtimo; + ean->ean_reAuthState = EAPOL_ARS_INIT; +} + +REAUTH_STATE_DECL(REAUTH) +{ + REAUTH_STATE_DEBUG(REAUTH, ean); + + ean->ean_reAuthenticate = TRUE; + ean->ean_reAuthState = EAPOL_ARS_REAUTH; +} + +/* + * Carry out a state transition in the reauthentication + * timer state machine. This routine encapsulates + * Figure 8-11 in the spec. + */ +static void +eapol_reauth_step(struct eapol_auth_node *ean) +{ + + EAPOL_LOCK_ASSERT(ean->ean_ec); + + if (ean->ean_portControl != EAPOL_PORTCONTROL_AUTO || + ean->ean_initialize || + ean->ean_portStatus == EAPOL_PORTSTATUS_UNAUTH || + !eapol_reauthenabled) { + REAUTH_STATE_ENTER(INIT, ean); + return; + } + switch (ean->ean_reAuthState) { + case EAPOL_ARS_INIT: + if (ean->ean_reAuthWhen == 0) + REAUTH_STATE_ENTER(REAUTH, ean); + break; + case EAPOL_ARS_REAUTH: + REAUTH_STATE_ENTER(INIT, ean); + break; + default: + eapolstats.eps_badreauthfsm++; + break; + } +} +#undef REAUTH_STATE_DECL +#undef REAUTH_STATE_ENTER +#undef REAUTH_STATE_DEBUG + +/* + * Key transmit state machine. + */ +#define KEY_STATE_DECL(s) STATE_DECL(AKS,s) +#define KEY_STATE_ENTER(s,ean) STATE_ENTER(AKS,s,ean) +#ifdef IEEE80211_DEBUG +#define KEY_STATE_DEBUG(s,ean) \ + IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1XSM, \ + ("[%s] %s -> %s\n", \ + ether_sprintf(ean->ean_node->ni_macaddr), \ + eapol_aks_states[ean->ean_keyState], \ + eapol_aks_states[EAPOL_AKS_##s])) +#define KEY_STATE_OPT_DEBUG(s,ean) do { \ + if (ean->ean_keyState != EAPOL_AKS_##s) \ + KEY_STATE_DEBUG(s,ean); \ +} while (0) +#else +#define KEY_STATE_DEBUG(s,ean) +#define KEY_STATE_OPT_DEBUG(s,ean) +#endif + +KEY_STATE_DECL(NO_KEY) +{ + KEY_STATE_OPT_DEBUG(NO_KEY, ean); + + ean->ean_keyState = EAPOL_AKS_NO_KEY; +} + +KEY_STATE_DECL(KEY) +{ +#define txKey(ean) (*ean->ean_ec->ec_radius->rc_txkey)(ean) + KEY_STATE_DEBUG(KEY, ean); + + txKey(ean); + ean->ean_keyAvailable = FALSE; + ean->ean_keyState = EAPOL_AKS_KEY; +#undef txKey +} + +/* + * Carry out a state transition in the key transmit + * state machine. This routine encapsulates + * Figure 8-9 in the spec. + */ +static void +eapol_key_step(struct eapol_auth_node *ean) +{ + + EAPOL_LOCK_ASSERT(ean->ean_ec); + + if (ean->ean_portControl != EAPOL_PORTCONTROL_AUTO || + ean->ean_initialize) { + KEY_STATE_ENTER(NO_KEY, ean); + return; + } + switch (ean->ean_keyState) { + case EAPOL_AKS_NO_KEY: + if (eapol_keytxenabled && ean->ean_keyAvailable) + KEY_STATE_ENTER(KEY, ean); + break; + case EAPOL_AKS_KEY: + if (!eapol_keytxenabled || + ean->ean_authFail || ean->ean_eapLogoff) + KEY_STATE_ENTER(NO_KEY, ean); + else if (ean->ean_keyAvailable) + KEY_STATE_ENTER(KEY, ean); + break; + default: + eapolstats.eps_badkeyfsm++; + break; + } +} +#undef KEY_STATE_DECL +#undef KEY_STATE_ENTER +#undef KEY_STATE_DEBUG + +/* + * Run the state machines so long as there are state transitions. + */ +void +eapol_fsm_run(struct eapol_auth_node *ean) +{ + int ostate, statechange; + + do { + statechange = FALSE; + + ostate = ean->ean_authState; + eapol_auth_step(ean); + if (ean->ean_gone) { + struct ieee80211com *ic = ean->ean_ic; + struct ieee80211_node *ni = ean->ean_node; + + /* + * Delayed handling of node reclamation when the + * reauthentication timer expires. We must be + * careful as the call to ieee80211_node_leave + * will reclaim our state so we cannot reference + * it past that point. However this happens the + * caller must also be careful to not reference state. + */ + ieee80211_node_leave(ic, ni); + ic->ic_stats.is_node_timeout++; + return; + } + statechange |= ostate != ean->ean_authState; + + ostate = ean->ean_backendState; + eapol_backend_step(ean); + statechange |= ostate != ean->ean_backendState; + + ostate = ean->ean_keyState; + eapol_key_step(ean); + statechange |= ostate != ean->ean_keyState; + + ostate = ean->ean_reAuthState; + eapol_reauth_step(ean); + statechange |= ostate != ean->ean_reAuthState; + } while (statechange); +} +EXPORT_SYMBOL(eapol_fsm_run); + +/* + * Process timers for an authenticator's nodes. + */ +static void +eapol_check_timers(unsigned long arg) +{ + struct eapolcom *ec = (struct eapolcom *)arg; + struct eapol_auth_node *ean; + int i, inuse, timeout; + + /* + * NB: this is very simplistic, but if the table is large + * the time spent here may be noticeable and we may want + * to optimize the work. + */ + EAPOL_LOCK(ec); + inuse = ec->ec_inuse; + for (i = 0; i < ec->ec_maxaid && inuse; i++) { + ean = ec->ec_table[i]; + if (ean == NULL) + continue; + timeout = FALSE; + if (ean->ean_aWhile && --ean->ean_aWhile == 0) { + IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1X, + ("[%s] TIMEOUT aWhile, %s\n", + ether_sprintf(ean->ean_node->ni_macaddr), + eapol_abs_states[ean->ean_backendState])); + timeout = TRUE; + } + if (ean->ean_quietWhile && --ean->ean_quietWhile == 0) { + IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1X, + ("[%s] TIMEOUT quietWhile, %s\n", + ether_sprintf(ean->ean_node->ni_macaddr), + eapol_as_states[ean->ean_authState])); + timeout = TRUE; + } + if (ean->ean_reAuthWhen && --ean->ean_reAuthWhen == 0) { + IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1X, + ("[%s] TIMEOUT reAuthWhen, %s\n", + ether_sprintf(ean->ean_node->ni_macaddr), + eapol_ars_states[ean->ean_reAuthState])); + timeout = TRUE; + } + if (ean->ean_txWhen && --ean->ean_txWhen == 0) { + IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1X, + ("[%s] TIMEOUT txWhen, %s\n", + ether_sprintf(ean->ean_node->ni_macaddr), + eapol_as_states[ean->ean_authState])); + timeout = TRUE; + } + if (timeout) + eapol_fsm_run(ean); + inuse--; + } + if (ec->ec_inuse) { + ec->ec_timer.expires = jiffies + HZ; /* once 1 second */ + add_timer(&ec->ec_timer); + } + EAPOL_UNLOCK(ec); +} + +/* + * Output routines to send EAP frames to the supplicant. + */ + +/* + * Send an EAP Failure packet to the supplicant. + */ +static void +txCannedFail(struct eapol_auth_node *ean) +{ + IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1X, + ("[%s] txCannedFail currentId %u\n", + ether_sprintf(ean->ean_node->ni_macaddr), + ean->ean_currentId)); + eap_send_simple(&ean->ean_base, EAP_CODE_FAILURE, ean->ean_currentId); +} + +/* + * Send an EAP Success packet to the supplicant. + */ +static void +txCannedSuccess(struct eapol_auth_node *ean) +{ + IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1X, + ("[%s] txCannedSuccess currentId %u\n", + ether_sprintf(ean->ean_node->ni_macaddr), + ean->ean_currentId)); + eap_send_simple(&ean->ean_base, EAP_CODE_SUCCESS, ean->ean_currentId); +} + +void +eapol_hmac_md5(struct eapolcom *ec, void *data, u_int datalen, + void *key, u_int keylen, u_int8_t hash[16]) +{ + struct scatterlist sg; + + sg.page = virt_to_page(data); + sg.offset = offset_in_page(data); + sg.length = datalen; + + crypto_hmac(ec->ec_md5, key, &keylen, &sg, 1, hash); +} +EXPORT_SYMBOL(eapol_hmac_md5); + +/* + * Allocate a buffer large enough to hold the specified + * payload preceded by an EAPOL header and the other + * headers required to send the result as an 802.11 frame. + */ +struct sk_buff * +eapol_alloc_skb(u_int payload) +{ + /* NB: these will never be encrypted */ + const int overhead = + sizeof(struct ieee80211_qosframe) + + sizeof(struct llc) + + sizeof(struct ether_header) + + sizeof(struct eapol_hdr) + ; + struct sk_buff *skb; + + skb = dev_alloc_skb(overhead + payload); + if (skb != NULL) + skb_reserve(skb, overhead); + else + eapolstats.eps_nobuf++; + return skb; +} +EXPORT_SYMBOL(eapol_alloc_skb); + +/* + * Send an EAP Request/Identity packet to the supplicant. + */ +static void +txReqId(struct eapol_auth_node *ean) +{ + struct eap_hdr *eap; + struct sk_buff *skb; + + IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1X, + ("[%s] %s currentId %u\n", + ether_sprintf(ean->ean_node->ni_macaddr), __func__, + ean->ean_currentId)); + skb = eapol_alloc_skb(sizeof(struct eap_hdr)); + if (skb == NULL) { + IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_ANY, + ("[%s] could not allocate sk_buff (%s) \n", + ether_sprintf(ean->ean_node->ni_macaddr), __func__)); + return; + } + eap = (struct eap_hdr *)skb_put(skb, sizeof(struct eap_hdr)); + eap->eap_code = EAP_CODE_REQUEST; + eap->eap_id = ean->ean_currentId; + eap->eap_len = htons(sizeof(struct eap_hdr)); + eap->eap_type = EAP_TYPE_IDENTITY; + + eapol_send(&ean->ean_base, skb, EAPOL_TYPE_EAP); + + ean->ean_txWhen = eapol_txtimo; +} + +/* + * Set the re-authentication timer based on a setting + * received from the backend server. We sanity check + * it and install it in the node for use if/when the + * reauthentication timer is started. + */ +void +eapol_reauth_setperiod(struct eapol_auth_node *ean, int timeout) +{ + + if (timeout == 0) + ean->ean_reAuthPeriod = eapol_reauthtimo; + else if (timeout < eapol_reauthmin) + ean->ean_reAuthPeriod = eapol_reauthmin; + else + ean->ean_reAuthPeriod = timeout; + IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_DOT1X, + ("[%s] reauthentication period set to %u secs\n", + ether_sprintf(ean->ean_node->ni_macaddr), + ean->ean_reAuthPeriod)); +} +EXPORT_SYMBOL(eapol_reauth_setperiod); + +/* + * Node management support. + */ + +/* + * Node management callbacks. All of these are + * subclassed when the radius client is used. + */ + +/* + * Allocate a node on station join/discovery. + */ +static struct eapol_auth_node * +eapol_node_alloc(struct eapolcom *ec) +{ + struct eapol_auth_node *ean; + + MALLOC(ean, struct eapol_auth_node *, sizeof(struct eapol_auth_node), + M_EAPOL_NODE, M_NOWAIT | M_ZERO); + if (ean == NULL) + eapolstats.eps_nonodemem++; + return ean; +} + +/* + * Reclaim a node on station leave or shutdown. + */ +static void +eapol_node_free(struct eapol_auth_node *ean) +{ + FREE(ean, M_EAPOL_NODE); +} + +/* + * Reset an existing node's non-state machine + * state when a station reassociates. + */ +static void +eapol_node_reset(struct eapol_auth_node *ean) +{ + if (ean->ean_skb != NULL) { + kfree_skb(ean->ean_skb); + ean->ean_skb = NULL; + } + ean->ean_gone = FALSE; + + /* reset timers, aWhile is reset by backend fsm */ + ean->ean_quietWhile = 0; + ean->ean_reAuthWhen = 0; + ean->ean_txWhen = 0; + ean->ean_reKeyWhen = 0; + ean->ean_gReKeyWhen = 0; + + ean->ean_portControl = EAPOL_PORTCONTROL_AUTO; +} + +/* + * Create an entry for a client. + */ +static struct eapol_auth_node * +eapol_new_node(struct eapolcom *ec, + struct ieee80211com *ic, struct ieee80211_node *ni) +{ + struct eapol_auth_node *ean; + + /* + * Allocate and initialize the node state. + */ + ean = (*ec->ec_node_alloc)(ec); + if (ean == NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + ("[%s] no memory for EAPOL node\n", + ether_sprintf(ni->ni_macaddr))); + return NULL; + } + ean->ean_ec = ec; + ean->ean_ic = ic; + ean->ean_node = ni; + + /* + * Add the new entry to the table. + */ + EAPOL_LOCK(ec); + KASSERT(ec->ec_table[IEEE80211_NODE_AID(ni)] == NULL, + ("entry already present for association id %u!", + IEEE80211_NODE_AID(ni))); + ec->ec_table[IEEE80211_NODE_AID(ni)] = ean; + if (ec->ec_inuse++ == 0) { + /* start timer on first node */ + ec->ec_timer.expires = jiffies + HZ; + add_timer(&ec->ec_timer); + } + EAPOL_UNLOCK(ec); + + return ean; +} + +/* + * Create an entry in response to a station associating + * reassociating. This also triggers the state machine + * which results in the supplicant receiving a message. + */ +static void +eapol_node_join(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + struct eapolcom *ec = ic->ic_ec; + struct eapol_auth_node *ean; + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_DOT1X, + ("[%s] 802.1x node join\n", ether_sprintf(ni->ni_macaddr))); + ean = ec->ec_table[IEEE80211_NODE_AID(ni)]; + if (ean == NULL) { + /* + * Bump the reference count so long as we hold a reference. + */ + ieee80211_ref_node(ni); + ean = eapol_new_node(ec, ic, ni); + if (ean == NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_DOT1X, + ("[%s] 802.1x node join FAILED\n", + ether_sprintf(ni->ni_macaddr))); + ieee80211_unref_node(&ni); + return; + } + } + /* + * Start/Restart state machine. Note that we crank + * the state machine(s) twice because the initialize + * handling will prematurely terminate stepping. + * This should be safe to do. + */ + EAPOL_LOCK(ec); + ean->ean_initialize = TRUE; + eapol_fsm_run(ean); + ean->ean_initialize = FALSE; +#if 0 + eapol_fsm_run(ean); +#else + /* XXX delay initial Identity request by 1 second */ + ean->ean_txWhen = 1; +#endif + EAPOL_UNLOCK(ec); +} + +/* + * Remove a node from the table and reclaim its resources. + */ +static void +eapol_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + struct eapolcom *ec = ic->ic_ec; + struct eapol_auth_node *ean; + + EAPOL_LOCK(ec); + ean = ec->ec_table[IEEE80211_NODE_AID(ni)]; + if (ean == NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_DOT1X, + ("[%s] 802.1x node leave, but not found, aid %u\n", + ether_sprintf(ni->ni_macaddr), IEEE80211_NODE_AID(ni))); + /* XXX statistic */ + } else { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_DOT1X, + ("[%s] 802.1x node leave (%s)\n", + ether_sprintf(ni->ni_macaddr), + eapol_as_states[ean->ean_authState])); + /* + * Remove the entry from the table. + */ + ec->ec_table[IEEE80211_NODE_AID(ni)] = NULL; + if (--ec->ec_inuse == 0) { + /* clear timer on last node */ + del_timer(&ec->ec_timer); + } + EAPOL_UNLOCK(ec); + /* + * Delete the reference to the node and free our memory. + */ + ieee80211_free_node(ean->ean_ic, ean->ean_node); + if (ean->ean_skb) + kfree_skb(ean->ean_skb); + (*ec->ec_node_free)(ean); + } +} + +/* + * Remove all nodes from the table and reclaim their resources. + */ +static void +eapol_delete_all_nodes(struct eapolcom *ec) +{ + struct eapol_auth_node *ean; + int i; + + EAPOL_LOCK(ec); + for (i = 0; i < ec->ec_maxaid; i++) { + ean = ec->ec_table[i]; + if (ean) { + ec->ec_table[i] = NULL; + ieee80211_free_node(ean->ean_ic, ean->ean_node); + if (ean->ean_skb) + kfree_skb(ean->ean_skb); + (*ec->ec_node_free)(ean); + } + } + ec->ec_inuse = 0; + del_timer(&ec->ec_timer); /* clear timer */ + EAPOL_UNLOCK(ec); +} + +/* + * Send a formulated EAPOL packet. + */ +void +eapol_send_raw(struct eapol_node *en, struct sk_buff *skb) +{ + struct net_device *dev = en->en_ic->ic_dev; + + /* + * Fill the device header for the EAPOL frame + */ + skb->dev = dev; + skb->protocol = __constant_htons(ETH_P_EAPOL); + skb->mac.raw = skb->nh.raw = skb->data; + if (dev->hard_header && + dev->hard_header(skb, dev, ETH_P_EAPOL, en->en_node->ni_macaddr, dev->dev_addr, skb->len) < 0) + goto out; + + /* Send it off, maybe filter it using firewalling first. */ + NF_HOOK(NF_EAPOL, NF_EAPOL_OUT, skb, NULL, dev, dev_queue_xmit); + return; +out: + kfree_skb(skb); +} +EXPORT_SYMBOL(eapol_send_raw); + +/* + * Create and send an EAPOL packet. + */ +void +eapol_send(struct eapol_node *en, struct sk_buff *skb, u_int8_t type) +{ + struct eapol_hdr *eapol; + + /* + * Craft the EAPOL header. + */ + eapol = (struct eapol_hdr *)skb_push(skb, sizeof(*eapol)); + eapol->eapol_ver = EAPOL_VERSION; + eapol->eapol_type = type; + eapol->eapol_len = htons(skb->len - sizeof(struct eapol_hdr)); + + eapol_send_raw(en, skb); +} +EXPORT_SYMBOL(eapol_send); + +#if 0 +/* + * Create and send a simple EAPOL packet. + */ +void +eapol_send_simple(struct eapol_node *en, u_int8_t type) +{ + struct sk_buff *skb; + + skb = eapol_alloc_skb(0); + if (skb == NULL) { + IEEE80211_DPRINTF(en->en_ic, IEEE80211_MSG_ANY, + ("[%s] could not allocate sk_buff (%s) \n", + ether_sprintf(en->en_node->ni_macaddr), __func__)); + return; + } + eapol_send(en, skb, type); +} +#endif + +/* + * Create and send a simple EAP packet. + */ +static void +eap_send_simple(struct eapol_node *en, u_int8_t code, u_int8_t id) +{ + struct eap_hdr_short *eap; + struct sk_buff *skb; + + skb = eapol_alloc_skb(sizeof(struct eap_hdr_short)); + if (skb == NULL) { + IEEE80211_DPRINTF(en->en_ic, IEEE80211_MSG_ANY, + ("[%s] could not allocate sk_buff (%s) \n", + ether_sprintf(en->en_node->ni_macaddr), __func__)); + return; + } + eap = (struct eap_hdr_short *) + skb_put(skb, sizeof(struct eap_hdr_short)); + eap->eap_code = code; + eap->eap_id = id; + eap->eap_len = __constant_htons(sizeof(struct eap_hdr_short)); + + eapol_send(en, skb, EAPOL_TYPE_EAP); +} + +static void +eapol_cleanup(struct eapolcom *ec) +{ + EAPOL_LOCK_DESTROY(ec); + if (ec->ec_md5 != NULL) + crypto_free_tfm(ec->ec_md5); + if (ec->ec_table != NULL) + FREE(ec->ec_table, M_DEVBUF); + FREE(ec, M_DEVBUF); +} + +static struct eapolcom * +eapol_setup(void) +{ + struct eapolcom *ec; + struct crypto_tfm *tfm; + + /* XXX WAITOK? */ + MALLOC(ec, struct eapolcom *, sizeof(*ec), M_DEVBUF, M_WAITOK | M_ZERO); + if (ec == NULL) { + printk("%s: no memory for state block!\n", __func__); + return NULL; + } + EAPOL_LOCK_INIT(ec, "eapol"); + + tfm = crypto_alloc_tfm("md5", 0); + if (tfm == NULL) { + /* XXX fallback on internal implementation? */ + printf("%s: unable to allocate md5 crypto state\n", __func__); + /* XXX statistic */ + goto bad; + } + ec->ec_md5 = tfm; + + ec->ec_node_alloc = eapol_node_alloc; + ec->ec_node_free = eapol_node_free; + ec->ec_node_reset = eapol_node_reset; + + return ec; +bad: + eapol_cleanup(ec); + return NULL; +} + +static struct packet_type eapol_packet_type = { + .type = __constant_htons(ETH_P_EAPOL), + .func = eapol_input, +}; + +/* + * Attach an authenticator. + */ +static int +eapol_authenticator_attach(struct ieee80211com *ic) +{ + struct eapolcom *ec; + + _MOD_INC_USE(THIS_MODULE, return FALSE); + + if (eapolcom == NULL) { + /* XXX cannot happen */ + printk(KERN_ERR "%s: no eapolcom!\n", __func__); + _MOD_DEC_USE(THIS_MODULE); + return FALSE; + } + ec = eapolcom; + + ec->ec_maxaid = ic->ic_max_aid; + MALLOC(ec->ec_table, struct eapol_auth_node **, + sizeof(struct eapol_auth_node *) * ic->ic_max_aid, + M_DEVBUF, M_NOWAIT | M_ZERO); + if (ec->ec_table == NULL) { + printk("%s: no memory for aid table!\n", __func__); + _MOD_DEC_USE(THIS_MODULE); + return FALSE; + } + + /* + * Startup radius client. + */ + ec->ec_backend = ieee80211_authenticator_backend_get("radius"); + if (ec->ec_backend == NULL || + !ec->ec_backend->iab_attach(ec)) + goto bad; + + init_timer(&ec->ec_timer); + ec->ec_timer.data = (unsigned long) ec; + ec->ec_timer.function = eapol_check_timers; + + /* + * Bind to the 802.11 layer. + */ + ic->ic_ec = ec; + + dev_add_pack(&eapol_packet_type); + printk(KERN_INFO "802.1x authenticator started\n"); + return TRUE; +bad: + FREE(ec->ec_table, M_DEVBUF); + ec->ec_table = NULL; + _MOD_DEC_USE(THIS_MODULE); + return FALSE; +} + +/* + * Detach an authenticator. + */ +static void +eapol_authenticator_detach(struct ieee80211com *ic) +{ + struct eapolcom *ec = ic->ic_ec; + + if (ec != NULL) { + dev_remove_pack(&eapol_packet_type); + /* + * Detach from 802.11 layer. + */ + ic->ic_ec = NULL; + + /* + * NB: must do this before detaching radius support + * as node management methods are reset on detach. + */ + eapol_delete_all_nodes(ec); + if (ec->ec_radius != NULL) + ec->ec_backend->iab_detach(ec); + if (ec->ec_table != NULL) { + FREE(ec->ec_table, M_DEVBUF); + ec->ec_table = NULL; + } + printk(KERN_INFO "802.1x authenticator stopped\n"); + } + _MOD_DEC_USE(THIS_MODULE); +} + +#define CTL_AUTO -2 /* cannot be CTL_ANY or CTL_NONE */ + +/* XXX validate/minmax settings */ +static ctl_table dot1x_sysctls[] = { + { .ctl_name = CTL_AUTO, + .procname = "reauthenabled", + .data = &eapol_reauthenabled, + .maxlen = sizeof(eapol_reauthenabled), + .mode = 0644, + .proc_handler = proc_dointvec + }, + { .ctl_name = CTL_AUTO, + .procname = "keytxenabled", + .data = &eapol_keytxenabled, + .maxlen = sizeof(eapol_keytxenabled), + .mode = 0644, + .proc_handler = proc_dointvec + }, + { .ctl_name = CTL_AUTO, + .procname = "idletimo", + .data = &eapol_idletimo, + .maxlen = sizeof(eapol_idletimo), + .mode = 0644, + .proc_handler = proc_dointvec + }, + { .ctl_name = CTL_AUTO, + .procname = "reauthlimit", + .data = &eapol_reauthlimit, + .maxlen = sizeof(eapol_reauthlimit), + .mode = 0644, + .proc_handler = proc_dointvec + }, + { .ctl_name = CTL_AUTO, + .procname = "reauthmin", + .data = &eapol_reauthmin, + .maxlen = sizeof(eapol_reauthmin), + .mode = 0644, + .proc_handler = proc_dointvec + }, + { .ctl_name = CTL_AUTO, + .procname = "reauthtimo", + .data = &eapol_reauthtimo, + .maxlen = sizeof(eapol_reauthtimo), + .mode = 0644, + .proc_handler = proc_dointvec + }, + { .ctl_name = CTL_AUTO, + .procname = "txtimo", + .data = &eapol_txtimo, + .maxlen = sizeof(eapol_txtimo), + .mode = 0644, + .proc_handler = proc_dointvec + }, + { .ctl_name = CTL_AUTO, + .procname = "supptimo", + .data = &eapol_supptimo, + .maxlen = sizeof(eapol_supptimo), + .mode = 0644, + .proc_handler = proc_dointvec + }, + { .ctl_name = CTL_AUTO, + .procname = "servtimo", + .data = &eapol_servtimo, + .maxlen = sizeof(eapol_servtimo), + .mode = 0644, + .proc_handler = proc_dointvec + }, + { .ctl_name = CTL_AUTO, + .procname = "suppreqlimit", + .data = &eapol_suppreqlimit, + .maxlen = sizeof(eapol_suppreqlimit), + .mode = 0644, + .proc_handler = proc_dointvec + }, + { .ctl_name = CTL_AUTO, + .procname = "servreqlimit", + .data = &eapol_servreqlimit, + .maxlen = sizeof(eapol_servreqlimit), + .mode = 0644, + .proc_handler = proc_dointvec + }, + { 0 } +}; +static ctl_table dot1x_table[] = { + { .ctl_name = NET_8021X, + .procname = "8021x", + .mode = 0555, + .child = dot1x_sysctls + }, { 0 } +}; +static ctl_table net_table[] = { +#ifdef CONFIG_PROC_FS + { .ctl_name = CTL_NET, + .procname = "net", + .mode = 0555, + .child = dot1x_table + }, +#endif /* CONFIG_PROC_FS */ + { 0 } +}; + +static struct ctl_table_header *eapol_sysctls; + +/* + * Module glue. + */ + +MODULE_AUTHOR("Errno Consulting, Sam Leffler"); +MODULE_DESCRIPTION("802.11 wireless support: 802.1x authenticator"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("Dual BSD/GPL"); +#endif + +/* + * One module handles everything for now. May want + * to split things up for embedded applications. + */ +static const struct ieee80211_authenticator dot1x = { + .ia_name = "802.1x", + .ia_attach = eapol_authenticator_attach, + .ia_detach = eapol_authenticator_detach, + .ia_node_join = eapol_node_join, + .ia_node_leave = eapol_node_leave, +}; + +static int __init +init_ieee80211_auth(void) +{ + eapolcom = eapol_setup(); + if (eapolcom == NULL) + return -1; /* XXX?? */ + eapol_sysctls = register_sysctl_table(net_table, 0); + ieee80211_authenticator_register(IEEE80211_AUTH_8021X, &dot1x); + return 0; +} +module_init(init_ieee80211_auth); + +static void __exit +exit_ieee80211_auth(void) +{ + if (eapolcom != NULL) + eapol_cleanup(eapolcom); + if (eapol_sysctls) + unregister_sysctl_table(eapol_sysctls); + ieee80211_authenticator_unregister(IEEE80211_AUTH_8021X); +} +module_exit(exit_ieee80211_auth); diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_dot1x.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_dot1x.h --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_dot1x.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_dot1x.h 2005-02-24 13:06:17.304131392 -0800 @@ -0,0 +1,330 @@ +/*- + * Copyright (c) 2004-2005 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/net80211/ieee80211_var.h,v 1.11 2004/01/15 08:44:27 onoe Exp $ + */ +#ifndef _NET80211_DOT1X_H_ +#define _NET80211_DOT1X_H_ + +/* + * IEEE 802.1x authenticator. + */ +#include + +/* + * Authenticators handle authentication requests from supplicants. + * Typically this is done by communicating with a backend server + * using the Radius protocol. When configured in this way an + * authenticator does little more than pass frames between the + * supplicant and the backend server. The exception to this is + * when authentication is followed by cryptographic key setup; in + * which case the authenticator must relay the key state and + * arrange for the keys to be installed prior to passing this + * information on the supplicant. This is the basis for 802.1x. + */ + +/* + * Authenticator PAE state machine. + */ +enum { + EAPOL_AS_INIT = 0, + EAPOL_AS_DISCONNECTED = 1, + EAPOL_AS_CONNECTING = 2, + EAPOL_AS_AUTHENTICATING = 3, + EAPOL_AS_AUTHENTICATED = 4, + EAPOL_AS_ABORTING = 5, + EAPOL_AS_HELD = 6, + EAPOL_AS_FORCE_AUTH = 7, + EAPOL_AS_FORCE_UNAUTH = 8, +}; + +/* + * Authenticator backend state machine. + */ +enum { + EAPOL_ABS_INIT = 0, + EAPOL_ABS_IDLE = 1, + EAPOL_ABS_REQUEST = 2, + EAPOL_ABS_RESPONSE = 3, + EAPOL_ABS_SUCCESS = 4, + EAPOL_ABS_FAIL = 5, + EAPOL_ABS_TIMEOUT = 6, +}; + +/* + * Reauthentication timer state machine. + */ +enum { + EAPOL_ARS_INIT = 0, + EAPOL_ARS_REAUTH = 1, +}; + +/* + * Authenticator key transmit state machine. + */ +enum { + EAPOL_AKS_NO_KEY = 0, + EAPOL_AKS_KEY = 1, +}; + +/* + * Port-related definitions. + */ +enum { + EAPOL_PORTCONTROL_AUTO = 0, +}; +enum { + EAPOL_PORTMODE_AUTO = 0, + EAPOL_PORTMODE_FORCEAUTH= 1, + EAPOL_PORTMODE_FORCEUNAUTH= 2, +}; +enum { + EAPOL_PORTSTATUS_AUTH = 0, /* authenticated */ + EAPOL_PORTSTATUS_UNAUTH = 1, /* unauthenticated */ +}; + +struct eapolcom; +struct ieee80211com; +struct ieee80211_node; + +/* + * Base class in case we decide to add supplicant support. + */ +struct eapol_node { + struct eapolcom *en_ec; /* associated authenticator */ + struct ieee80211com *en_ic; /* associated device */ + struct ieee80211_node *en_node; /* associated 802.11 state */ + u_int8_t en_id[EAP_IDENTITY_MAXLEN]; + u_int16_t en_idlen; +}; + +/* + * Per-station authentication state. Variable names are + * chosen to mimic those used in the 802.1x specification + * (maybe should group like state into structures). + */ +struct eapol_auth_node { + struct eapol_node ean_base; /* base class */ + u_int ean_scangen; /* scan generation # */ + u_int8_t ean_gone; /* node needs delayed reclaim */ + struct sk_buff *ean_skb; /* from supplicant */ + + /* authenticator timers 8.5.2.1 */ + u_int32_t ean_aWhile; /* with supplicant or AS */ + u_int32_t ean_quietWhile; /* delay before acquiring sup */ + u_int32_t ean_reAuthWhen; /* re-authentication interval */ + u_int32_t ean_txWhen; /* xmit timeout */ + u_int32_t ean_reKeyWhen; /* station re-key timer */ + u_int32_t ean_gReKeyWhen; /* group re-key timer */ + /* global variables 8.5.2.2 */ + u_int ean_authAbort : 1, + ean_authFail : 1, + ean_authStart : 1, + ean_authTimeout : 1, + ean_authSuccess : 1, + ean_initialize : 1, + ean_portControl : 2, + ean_portStatus : 2, + ean_reAuthenticate : 1; + u_int8_t ean_currentId; /* current id */ + u_int8_t ean_receivedId; /* most recent Identifier rx */ + /* authentication variables 8.5.4.1 */ + u_int8_t ean_authState; + u_int8_t ean_eapLogoff : 1, + ean_eapStart : 1, + ean_portMode : 2, + ean_rxRespId : 1; + u_int8_t ean_reAuthCount; + /* authentication counters 8.5.4.2 */ + u_int ean_authEntersConnecting; + u_int ean_authEapLogoffsWhileConnecting; + u_int ean_authEntersAuthenticating; + u_int ean_authAuthSuccessesWhileAuthenticating; + u_int ean_authAuthTimeoutsWhileAuthenticating; + u_int ean_authAuthFailWhileAuthenticating; + u_int ean_authAuthReauthsWhileAuthenticating; + u_int ean_authAuthEapStartsWhileAuthenticating; + u_int ean_authAuthEapLogoffWhileAuthenticating; + u_int ean_authAuthReauthsWhileAuthenticated; + u_int ean_authAuthEapStartsWhileAuthenticated; + u_int ean_authAuthEapLogoffWhileAuthenticated; + /* key state variables 8.5.5 */ + u_int8_t ean_keyState; + u_int ean_keyAvailable : 1; + /* reauthentication variables */ + u_int8_t ean_reAuthState; + u_int ean_reAuthPeriod; /* not part of spec */ + /* backend authentication variables 8.5.8.1.1 */ + u_int8_t ean_backendState; + u_int8_t ean_reqCount; + u_int8_t ean_rxResp : 1, + ean_aSuccess : 1, + ean_aFail : 1, + ean_aReq : 1; + u_int8_t ean_idFromServer; + u_int8_t ean_reqSrvCount; /* not part of spec */ + /* backend counters 8.5.8.2 */ + u_int ean_backendResponses; + u_int ean_backendAccessChallenges; + u_int ean_backendOtherRequestsToSupplicant; + u_int ean_backendNonNakResponsesForSupplicant; + u_int ean_backendAuthSuccesses; + u_int ean_backendAuthFails; +}; +#define EAPOL_AUTHNODE(_x) ((struct eapol_auth_node *)(_x)) + +/* write-arounds for base class members */ +#define ean_ec ean_base.en_ec +#define ean_ic ean_base.en_ic +#define ean_node ean_base.en_node +#define ean_id ean_base.en_id +#define ean_idlen ean_base.en_idlen +#define ean_lock ean_base.en_lock + +struct crypto_tfm; + +/* + * State for each authenticator instance. We only support + * one at the moment and it's not clear that more than one + * per-machine is desirable. + */ +struct eapolcom { + struct eapol_auth_node **ec_table; /* indexed by association id */ + u_int16_t ec_maxaid; /* copy of ic_max_aid */ + u_int16_t ec_inuse; /* number of entries in use */ + eapol_lock_t ec_lock; /* on eapolcom/node table */ + struct timer_list ec_timer; /* state machine timers */ + + struct crypto_tfm *ec_md5; + + /* backend state and related methods */ + const struct ieee80211_authenticator_backend *ec_backend; + struct radiuscom *ec_radius; + struct eapol_auth_node *(*ec_node_alloc)(struct eapolcom *); + void (*ec_node_free)(struct eapol_auth_node *); + void (*ec_node_reset)(struct eapol_auth_node *); +}; + +/* + * Statistics. We define the radius client statistics + * here too so only one structure needs to be understood + * by applications that use it. + * XXX no way to tell whether a radius client is active + */ +struct eapolstats { + u_int32_t eap_badcode; + u_int32_t eap_lenmismatch; + u_int32_t eap_tooshort; + u_int32_t eas_badcode; + u_int32_t eas_badidlen; + u_int32_t eas_badtype; + u_int32_t eas_idmismatch; + u_int32_t eak_keynotrequest; + u_int32_t eak_badkeytype; + u_int32_t eak_replay; + u_int32_t eak_nononce; + u_int32_t eak_micfailed; + u_int32_t eap_keydiscard; + u_int32_t eap_keytooshort; + u_int32_t eap_keynotwpa; + u_int32_t eps_badalloc; + u_int32_t eps_badauthfsm; + u_int32_t eps_badauthlogoff; + u_int32_t eps_badauthstart; + u_int32_t eps_badauthtimeout; + u_int32_t eps_badbackendfsm; + u_int32_t eps_badbackendtimeout; + u_int32_t eps_badkeyfsm; + u_int32_t eps_badreauthfsm; + u_int32_t eps_badtype; + u_int32_t eps_badver; + u_int32_t eps_noauth; + u_int32_t eps_nobuf; + u_int32_t eps_noinstance; + u_int32_t eps_nonode; + u_int32_t eps_nonodemem; + u_int32_t eps_nosession; + u_int32_t eps_sharecheck; + u_int32_t eps_tooshort; + /* radius server-specific statistics */ + u_int32_t rs_nosecret; + u_int32_t rs_addrmismatch; + u_int32_t rs_badattrlen; + u_int32_t rs_badcode; + u_int32_t rs_badid; + u_int32_t rs_badlen; + u_int32_t rs_badmode; + u_int32_t rs_badmsgauth; + u_int32_t rs_badrespauth; + u_int32_t rs_cannotbind; + u_int32_t rs_emptyreply; + u_int32_t rs_lenmismatch; + u_int32_t rs_noclone; + u_int32_t rs_nomsg; + u_int32_t rs_nocrypto; + u_int32_t rs_nomem; + u_int32_t rs_nomsgauth; + u_int32_t rs_nosocket; + u_int32_t rs_nothread; + u_int32_t rs_request; + u_int32_t rs_tooshort; + u_int32_t rs_vkeybadsalt; + u_int32_t rs_vkeydupsalt; + u_int32_t rs_vkeybadvid; + u_int32_t rs_vkeybadlen; + u_int32_t rs_vkeytooshort; + u_int32_t rs_vkeytoolong; +}; + +#ifdef __linux__ +#define NF_EAPOL 0 +#define NF_EAPOL_OUT 1 +#define NF_EAPOL_IN 2 + +enum { + NET_8021X = 18, /* XXX */ +}; +#endif + +#if defined(__KERNEL__) || defined(_KERNEL) +extern void eapol_fsm_run(struct eapol_auth_node *); +extern void eapol_reauth_setperiod(struct eapol_auth_node *, int timeout); +extern void eapol_send_raw(struct eapol_node *, struct sk_buff *); +extern void eapol_send(struct eapol_node *, struct sk_buff *, u_int8_t type); +extern struct sk_buff *eapol_alloc_skb(u_int payload); +extern void eapol_hmac_md5(struct eapolcom *ec, void *data, u_int datalen, + void *key, u_int keylen, u_int8_t hash[16]); + +extern struct eapolstats eapolstats; + +MALLOC_DECLARE(M_EAPOL_NODE); +#endif + +#endif /* _NET80211_DOT1X_H_ */ diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_input.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_input.c --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_input.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_input.c 2005-02-24 13:06:17.306131088 -0800 @@ -0,0 +1,2360 @@ +/*- + * Copyright (c) 2001 Atsushi Onoe + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EXPORT_SYMTAB +#define EXPORT_SYMTAB +#endif + +__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_input.c,v 1.13 2004/01/15 08:44:27 onoe Exp $"); + +/* + * IEEE 802.11 input handling. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "if_llc.h" +#include "if_ethersubr.h" +#include "if_media.h" + +#include + +static struct sk_buff *ieee80211_defrag(struct ieee80211com *, + struct ieee80211_node *, struct sk_buff *); +static struct sk_buff *ieee80211_decap(struct ieee80211com *, struct sk_buff *); +static void ieee80211_recv_pspoll(struct ieee80211com *, + struct ieee80211_node *, struct sk_buff *); + +#ifdef IEEE80211_DEBUG +/* + * Decide if a received management frame should be + * printed when debugging is enabled. This filters some + * of the less interesting frames that come frequently + * (e.g. beacons). + */ +static __inline int +doprint(struct ieee80211com *ic, int subtype) +{ + switch (subtype) { + case IEEE80211_FC0_SUBTYPE_BEACON: + return (ic->ic_state == IEEE80211_S_SCAN); + case IEEE80211_FC0_SUBTYPE_PROBE_REQ: + return (ic->ic_opmode == IEEE80211_M_IBSS); + } + return 1; +} +#endif + +/* + * Process a received frame. The node associated with the sender + * should be supplied. If nothing was found in the node table then + * the caller is assumed to supply a reference to ic_bss instead. + * The RSSI and a timestamp are also supplied. The RSSI data is used + * during AP scanning to select a AP to associate with; it can have + * any units so long as values have consistent units and higher values + * mean ``better signal''. The receive timestamp is currently not used + * by the 802.11 layer. + */ +void +ieee80211_input(struct ieee80211com *ic, struct sk_buff *skb, + struct ieee80211_node *ni, int rssi, u_int32_t rstamp) +{ +#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0) +#define HAS_SEQ(type) ((type & 0x4) == 0) + struct net_device *dev = ic->ic_dev; + struct ieee80211_frame *wh; + struct ieee80211_key *key; + struct ether_header *eh; + int len; + u_int8_t dir, type, subtype; + u_int8_t *bssid; + u_int16_t rxseq; + + KASSERT(ni != NULL, ("null node")); + KASSERT(skb->len >= sizeof(struct ieee80211_frame_min), + ("frame length too short: %u", skb->len)); + + /* + * In monitor mode, send everything directly to bpf. + * Also do not process frames w/o i_addr2 any further. + * XXX may want to include the CRC + */ + if (ic->ic_opmode == IEEE80211_M_MONITOR) + goto out; + + if (skb->len < sizeof(struct ieee80211_frame_min)) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + ("%s: frame too short, len %u\n", + __func__, skb->len)); + ic->ic_stats.is_rx_tooshort++; + goto out; + } + /* + * Bit of a cheat here, we use a pointer for a 3-address + * frame format but don't reference fields past outside + * ieee80211_frame_min w/o first validating the data is + * present. + */ + wh = (struct ieee80211_frame *)skb->data; + + if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) != + IEEE80211_FC0_VERSION_0) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + ("receive packet with wrong version: %x\n", + wh->i_fc[0])); + ic->ic_stats.is_rx_badversion++; + goto err; + } + + dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; + if (ic->ic_state != IEEE80211_S_SCAN) { + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + bssid = wh->i_addr2; + if (!IEEE80211_ADDR_EQ(bssid, ni->ni_bssid)) { + /* not interested in */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_INPUT, + ("[%s] discard frame not to bss\n", + ether_sprintf(bssid))); + ic->ic_stats.is_rx_wrongbss++; + goto out; + } + break; + case IEEE80211_M_IBSS: + case IEEE80211_M_AHDEMO: + case IEEE80211_M_HOSTAP: + if (dir != IEEE80211_FC1_DIR_NODS) + bssid = wh->i_addr1; + else if (type == IEEE80211_FC0_TYPE_CTL) + bssid = wh->i_addr1; + else { + if (skb->len < sizeof(struct ieee80211_frame)) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + ("%s: frame too short, len %u\n", + __func__, skb->len)); + ic->ic_stats.is_rx_tooshort++; + goto out; + } + bssid = wh->i_addr3; + } + if (type == IEEE80211_FC0_TYPE_DATA && + !IEEE80211_ADDR_EQ(bssid, ic->ic_bss->ni_bssid) && + !IEEE80211_ADDR_EQ(bssid, dev->broadcast)) { + /* not interested in */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_INPUT, + ("[%s] discard data frame not to bss\n", + ether_sprintf(bssid))); + ic->ic_stats.is_rx_wrongbss++; + goto out; + } + break; + default: + /* XXX catch bad values */ + goto out; + } + ni->ni_rssi = rssi; + ni->ni_rstamp = rstamp; + if (HAS_SEQ(type)) { + rxseq = le16toh(*(u_int16_t *)wh->i_seq); + if ((wh->i_fc[1] & IEEE80211_FC1_RETRY) && + SEQ_LEQ(rxseq, ni->ni_rxseq)) { + /* duplicate, discard */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_INPUT, + ("[%s] discard duplicate frame, " + "seqno <%u,%u> fragno <%u,%u>\n", + ether_sprintf(bssid), + rxseq >> IEEE80211_SEQ_SEQ_SHIFT, + ni->ni_rxseq >> IEEE80211_SEQ_SEQ_SHIFT, + rxseq & IEEE80211_SEQ_FRAG_MASK, + ni->ni_rxseq & IEEE80211_SEQ_FRAG_MASK)); + /* XXX per-station stat */ + ic->ic_stats.is_rx_dup++; + goto out; + } + ni->ni_rxseq = rxseq; + } + } + + /* + * Check for ps-poll state change for the station. + * XXX is there a response when pspoll is not supported? + */ + if (ic->ic_opmode == IEEE80211_M_HOSTAP && + ic->ic_set_tim != NULL && + ((wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) ^ + (ni->ni_flags & IEEE80211_NODE_PWR_MGT))) { + /* XXX statistics? */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, + ("[%s] power save mode %s\n", + ether_sprintf(wh->i_addr2), + (wh->i_fc[1] & IEEE80211_FC1_PWR_MGT ? "on" : "off"))); + if ((wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) == 0) { + /* turn off power save mode, dequeue stored packets */ + ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT; + (*ic->ic_set_tim)(ic, ni->ni_associd, 0); + while (!_IF_QLEN(&ni->ni_savedq) != 0) { + struct sk_buff *skb0; + IF_DEQUEUE(&ni->ni_savedq, skb0); + /* XXX need different driver interface */ + (*dev->hard_start_xmit)(skb0, dev);/* XXX??? */ + } + } else { + /* turn on power save mode */ + ni->ni_flags |= IEEE80211_NODE_PWR_MGT; + } + } + + switch (type) { + case IEEE80211_FC0_TYPE_DATA: + if (skb->len < sizeof(struct ieee80211_frame)) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + ("%s: data frame too short, len %u\n", + __func__, skb->len)); + ic->ic_stats.is_rx_tooshort++; + goto out; /* XXX */ + } + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + if (dir != IEEE80211_FC1_DIR_FROMDS) { + ic->ic_stats.is_rx_wrongdir++; + goto out; + } + if ((dev->flags & IFF_MULTICAST) && + IEEE80211_IS_MULTICAST(wh->i_addr1) && + IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_myaddr)) { + /* + * In IEEE802.11 network, multicast packet + * sent from me is broadcasted from AP. + * It should be silently discarded for + * SIMPLEX interface. + * + * NB: Linux has no IFF_ flag to indicate + * if an interface is SIMPLEX or not; + * so we always assume it to be true. + */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_INPUT, + ("discard multicast echo\n")); + ic->ic_stats.is_rx_mcastecho++; + goto out; + } + break; + case IEEE80211_M_IBSS: + case IEEE80211_M_AHDEMO: + if (dir != IEEE80211_FC1_DIR_NODS) { + ic->ic_stats.is_rx_wrongdir++; + goto out; + } + break; + case IEEE80211_M_HOSTAP: + if (dir != IEEE80211_FC1_DIR_TODS) { + ic->ic_stats.is_rx_wrongdir++; + goto out; + } + /* check if source STA is associated */ + if (ni == ic->ic_bss) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_INPUT, + ("[%s] discard data from unknown src\n", + ether_sprintf(wh->i_addr2))); +ieee80211_dump_nodes(ic);/*XXX*/ + /* NB: caller deals with reference to ic_bss */ + ni = ieee80211_dup_bss(ic, wh->i_addr2); + if (ni != NULL) { + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_NOT_AUTHED); + ieee80211_free_node(ic, ni); + } + ic->ic_stats.is_rx_notassoc++; + goto err; + } + if (ni->ni_associd == 0) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_INPUT, + ("[%s] discard data from unassoc src\n", + ether_sprintf(wh->i_addr2))); + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_DISASSOC, + IEEE80211_REASON_NOT_ASSOCED); + ic->ic_stats.is_rx_notassoc++; + goto err; + } + break; + default: + /* XXX here to keep compiler happy */ + goto out; + } + + /* + * Handle privacy requirements. Note that we + * must not be preempted from here until after + * we (potentially) call ieee80211_crypto_demic; + * otherwise we may violate assumptions in the + * crypto cipher modules used to do delayed update + * of replay sequence numbers. + */ + if (wh->i_fc[1] & IEEE80211_FC1_WEP) { + if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) { + /* + * Discard encrypted frames when privacy is off. + */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_INPUT, + ("[%s] discard WEP data frame 'cuz " + "PRIVACY off\n", + ether_sprintf(wh->i_addr2))); + ic->ic_stats.is_rx_noprivacy++; + goto out; + } + key = ieee80211_crypto_decap(ic, ni, skb); + if (key == NULL) { + /* NB: stats+msgs handled in crypto_decap */ + goto out; + } + } else { + key = NULL; + } + + /* + * Next up, any fragmentation. + */ + skb = ieee80211_defrag(ic, ni, skb); + if (skb == NULL) { + /* XXX statistic */ + /* Fragment dropped or frame not complete yet */ + goto out; + } + wh = NULL; /* no longer valid, catch any uses */ + + /* + * Next strip any MSDU crypto bits. + */ + if (key != NULL && !ieee80211_crypto_demic(ic, key, skb)) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_INPUT, + ("%s: discard frame on demic error\n", + __func__)); + /* XXX statistic? */ + goto out; + } + + /* + * Finally, strip the 802.11 header. + */ + skb = ieee80211_decap(ic, skb); + if (skb == NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_INPUT, + ("%s: decapsulation error\n", __func__)); + ic->ic_stats.is_rx_decap++; + goto err; + } + eh = (struct ether_header *) skb->data; + if ((ni->ni_flags & IEEE80211_NODE_AUTH) == 0) { + /* + * Deny any non-PAE frames received prior to + * authorization. For open/shared-key + * authentication the port is mark authorized + * after authentication completes. For 802.1x + * the port is not marked authorized by the + * authenticator until the handshake has completed. + */ + if (eh->ether_type != __constant_htons(ETHERTYPE_PAE)) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_INPUT, + ("[%s] discard data (ether type 0x%x len %u)" + " on unauthorized port\n", + ether_sprintf(eh->ether_shost), + eh->ether_type, skb->len)); + ic->ic_stats.is_rx_unauth++; + /* XXX node statistic */ + goto err; + } + ni->ni_inact = ic->ic_inact_auth; + } else { + /* + * When denying unencrypted frames, discard + * any non-PAE frames received without encryption. + */ + if ((ic->ic_flags & IEEE80211_F_DROPUNENC) && + key == NULL && + eh->ether_type != __constant_htons(ETHERTYPE_PAE)) { + /* + * Drop unencrypted frames. + */ + ic->ic_stats.is_rx_unencrypted++; + goto out; + } + ni->ni_inact = ic->ic_inact_run; + } + ic->ic_devstats->rx_packets++; + ic->ic_devstats->rx_bytes += skb->len; + + /* perform as a bridge within the AP */ + if (ic->ic_opmode == IEEE80211_M_HOSTAP && + (ic->ic_flags & IEEE80211_F_NOBRIDGE) == 0) { + struct sk_buff *skb1 = NULL; + + if (ETHER_IS_MULTICAST(eh->ether_dhost)) { + skb1 = skb_copy(skb, GFP_ATOMIC); + } else { + /* XXX this dups work done in ieee80211_encap */ + /* check if destination is associated */ + struct ieee80211_node *ni1 = + ieee80211_find_node(ic, eh->ether_dhost); + if (ni1 != NULL) { + if (ni1->ni_associd != 0) { + skb1 = skb; + skb = NULL; + } + /* XXX statistic? */ + ieee80211_free_node(ic, ni1); + } + } + if (skb1 != NULL) { + len = skb1->len; + skb1->dev = dev; + skb1->mac.raw = skb1->data; + skb1->nh.raw = skb1->data + + sizeof(struct ether_header); + skb1->protocol = __constant_htons(ETH_P_802_2); + dev_queue_xmit(skb1); + } + } + if (skb != NULL) { + skb->dev = dev; + skb->protocol = eth_type_trans(skb, dev); + if (ni->ni_vlan != 0 && ic->ic_vlgrp != NULL) { + /* attach vlan tag */ + vlan_hwaccel_receive_skb(skb, ic->ic_vlgrp, + ni->ni_vlan); + } else { + netif_rx(skb); + } + dev->last_rx = jiffies; + } + return; + + case IEEE80211_FC0_TYPE_MGT: + if (dir != IEEE80211_FC1_DIR_NODS) { + ic->ic_stats.is_rx_wrongdir++; + goto err; + } + if (skb->len < sizeof(struct ieee80211_frame)) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + ("%s: mgt data frame too short, len %u\n", + __func__, skb->len)); + ic->ic_stats.is_rx_tooshort++; + goto out; + } +#ifdef IEEE80211_DEBUG + if ((ieee80211_msg_debug(ic) && doprint(ic, subtype)) || + ieee80211_msg_dumppkts(ic)) { + if_printf(ic->ic_dev, "[%s] received %s rssi %d\n", + ether_sprintf(wh->i_addr2), + ieee80211_mgt_subtype_name[subtype >> + IEEE80211_FC0_SUBTYPE_SHIFT], rssi); + } +#endif + if (wh->i_fc[1] & IEEE80211_FC1_WEP) { + if (subtype != IEEE80211_FC0_SUBTYPE_AUTH) { + /* + * Only shared key auth frames with a challenge + * should be encrypted, discard all others. + */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_INPUT, + ("[%s] discard %s with WEP\n", + ether_sprintf(wh->i_addr2), + ieee80211_mgt_subtype_name[subtype >> + IEEE80211_FC0_SUBTYPE_SHIFT])); + ic->ic_stats.is_rx_mgtdiscard++; /* XXX */ + goto out; + } + if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) { + /* + * Discard encrypted frames when privacy is off. + */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_INPUT, + ("[%s] discard WEP mgt frame 'cuz " + "PRIVACY off\n", + ether_sprintf(wh->i_addr2))); + ic->ic_stats.is_rx_noprivacy++; + goto out; + } + key = ieee80211_crypto_decap(ic, ni, skb); + if (key == NULL) { + /* NB: stats+msgs handled in crypto_decap */ + goto out; + } + } + (*ic->ic_recv_mgmt)(ic, skb, ni, subtype, rssi, rstamp); + dev_kfree_skb(skb); + return; + + case IEEE80211_FC0_TYPE_CTL: + ic->ic_stats.is_rx_ctl++; + if (ic->ic_opmode != IEEE80211_M_HOSTAP) + goto out; + switch (subtype) { + case IEEE80211_FC0_SUBTYPE_PS_POLL: + /* XXX statistic */ + /* Dump out a single packet from the host */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, + ("got power save probe from %s\n", + ether_sprintf(wh->i_addr2))); + ieee80211_recv_pspoll(ic, ni, skb); + break; + } + goto out; + + default: + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + ("%s: bad frame type %x\n", __func__, type)); + /* should not come here */ + break; + } +err: + ic->ic_devstats->rx_errors++; +out: + if (skb != NULL) + dev_kfree_skb(skb); +#undef HAS_SEQ +#undef SEQ_LEQ +} +EXPORT_SYMBOL(ieee80211_input); + +/* + * This function reassemble fragments using the skb of the 1st fragment, + * if large enough. If not, a new skb is allocated to hold incoming + * fragments. + * + * Fragments are copied at the end of the previous fragment. A different + * strategy could have been used, where a non-linear skb is allocated and + * fragments attached to that skb. + */ +static struct sk_buff * +ieee80211_defrag(struct ieee80211com *ic, struct ieee80211_node *ni, + struct sk_buff *skb) +{ + struct ieee80211_frame *wh = (struct ieee80211_frame *) skb->data; + u_int16_t rxseq, last_rxseq; + u_int8_t fragno, last_fragno; + u_int8_t more_frag = wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG; + + if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { + /* Do not keep fragments of multicast frames */ + return skb; + } + + rxseq = le16_to_cpu(*(u_int16_t *)wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT; + fragno = le16_to_cpu(*(u_int16_t *)wh->i_seq) & IEEE80211_SEQ_FRAG_MASK; + + /* Quick way out, if there's nothing to defragment */ + if (!more_frag && fragno == 0 && ni->ni_rxfrag[0] == NULL) + return skb; + + /* + * Use this lock to make sure ni->ni_rxfrag[0] is + * not freed by the timer process while we use it. + * XXX bogus + */ + IEEE80211_NODE_LOCK(ic); + + /* + * Update the time stamp. As a side effect, it + * also makes sure that the timer will not change + * ni->ni_rxfrag[0] for at least 1 second, or in + * other words, for the remaining of this function. + */ + ni->ni_rxfragstamp = jiffies; + + IEEE80211_NODE_UNLOCK(ic); + + /* + * Validate that fragment is in order and + * related to the previous ones. + */ + if (ni->ni_rxfrag[0]) { + struct ieee80211_frame *lwh; + + lwh = (struct ieee80211_frame *) ni->ni_rxfrag[0]->data; + last_rxseq = le16_to_cpu(*(u_int16_t *)lwh->i_seq) >> + IEEE80211_SEQ_SEQ_SHIFT; + last_fragno = le16_to_cpu(*(u_int16_t *)lwh->i_seq) & + IEEE80211_SEQ_FRAG_MASK; + if (rxseq != last_rxseq + || fragno != last_fragno + 1 + || (!IEEE80211_ADDR_EQ(wh->i_addr1, lwh->i_addr1)) + || (!IEEE80211_ADDR_EQ(wh->i_addr2, lwh->i_addr2)) + || (ni->ni_rxfrag[0]->end - ni->ni_rxfrag[0]->tail < + skb->len)) { + /* + * Unrelated fragment or no space for it, + * clear current fragments + */ + dev_kfree_skb(ni->ni_rxfrag[0]); + ni->ni_rxfrag[0] = NULL; + } + } + + /* If this is the first fragment */ + if (ni->ni_rxfrag[0] == NULL && fragno == 0) { + ni->ni_rxfrag[0] = skb; + /* If more frags are coming */ + if (more_frag) { + if (skb_is_nonlinear(skb)) { + /* + * We need a continous buffer to + * assemble fragments + */ + ni->ni_rxfrag[0] = skb_copy(skb, GFP_ATOMIC); + dev_kfree_skb(skb); + } + /* + * Check that we have enough space to hold + * incoming fragments + */ + else if (skb->end - skb->head < ic->ic_dev->mtu + + sizeof(sizeof(struct ieee80211_frame))) { + ni->ni_rxfrag[0] = skb_copy_expand(skb, 0, + (ic->ic_dev->mtu + + sizeof(sizeof(struct ieee80211_frame))) + - (skb->end - skb->head), GFP_ATOMIC); + dev_kfree_skb(skb); + } + } + } else { + if (ni->ni_rxfrag[0]) { + struct ieee80211_frame *lwh = (struct ieee80211_frame *) + ni->ni_rxfrag[0]->data; + + /* + * We know we have enough space to copy, + * we've verified that before + */ + /* Copy current fragment at end of previous one */ + memcpy(ni->ni_rxfrag[0]->tail, + skb->data + sizeof(struct ieee80211_frame), + skb->len - sizeof(struct ieee80211_frame) + ); + /* Update tail and length */ + skb_put(ni->ni_rxfrag[0], + skb->len - sizeof(struct ieee80211_frame)); + /* Keep a copy of last sequence and fragno */ + *(u_int16_t *) lwh->i_seq = *(u_int16_t *) wh->i_seq; + } + /* we're done with the fragment */ + dev_kfree_skb(skb); + } + + if (more_frag) { + /* More to come */ + skb = NULL; + } else { + /* Last fragment received, we're done! */ + skb = ni->ni_rxfrag[0]; + ni->ni_rxfrag[0] = NULL; + } + return skb; +} + +static struct sk_buff * +ieee80211_decap(struct ieee80211com *ic, struct sk_buff *skb) +{ + struct ether_header *eh; + struct ieee80211_frame wh; + struct llc *llc; + u_short ether_type = 0; + + memcpy(&wh, skb->data, sizeof(struct ieee80211_frame)); + llc = (struct llc *) skb_pull(skb, sizeof(struct ieee80211_frame)); + if (skb->len >= sizeof(struct llc) && + llc->llc_dsap == LLC_SNAP_LSAP && llc->llc_ssap == LLC_SNAP_LSAP && + llc->llc_control == LLC_UI && llc->llc_snap.org_code[0] == 0 && + llc->llc_snap.org_code[1] == 0 && llc->llc_snap.org_code[2] == 0) { + ether_type = llc->llc_un.type_snap.ether_type; + skb_pull(skb, sizeof(struct llc)); + llc = NULL; + } + eh = (struct ether_header *) skb_push(skb, sizeof(struct ether_header)); + switch (wh.i_fc[1] & IEEE80211_FC1_DIR_MASK) { + case IEEE80211_FC1_DIR_NODS: + IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr1); + IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr2); + break; + case IEEE80211_FC1_DIR_TODS: + IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr3); + IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr2); + break; + case IEEE80211_FC1_DIR_FROMDS: + IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr1); + IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr3); + break; + case IEEE80211_FC1_DIR_DSTODS: + /* not yet supported */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + ("%s: discard DS to DS frame\n", __func__)); + dev_kfree_skb(skb); + return NULL; + } + if (!ALIGNED_POINTER(skb->data + sizeof(*eh), u_int32_t)) { + struct sk_buff *n; + + /* XXX does this always work? */ + n = skb_copy(skb, GFP_ATOMIC); + dev_kfree_skb(skb); + if (n == NULL) + return NULL; + skb = n; + eh = (struct ether_header *) skb->data; + } + if (llc != NULL) + eh->ether_type = htons(skb->len - sizeof(*eh)); + else + eh->ether_type = ether_type; + return skb; +} + +/* + * Install received rate set information in the node's state block. + */ +static int +ieee80211_setup_rates(struct ieee80211com *ic, struct ieee80211_node *ni, + u_int8_t *rates, u_int8_t *xrates, int flags) +{ + struct ieee80211_rateset *rs = &ni->ni_rates; + + memset(rs, 0, sizeof(*rs)); + rs->rs_nrates = rates[1]; + memcpy(rs->rs_rates, rates + 2, rs->rs_nrates); + if (xrates != NULL) { + u_int8_t nxrates; + /* + * Tack on 11g extended supported rate element. + */ + nxrates = xrates[1]; + if (rs->rs_nrates + nxrates > IEEE80211_RATE_MAXSIZE) { + nxrates = IEEE80211_RATE_MAXSIZE - rs->rs_nrates; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_XRATE, + ("%s: extended rate set too large;" + " only using %u of %u rates\n", + __func__, nxrates, xrates[1])); + ic->ic_stats.is_rx_rstoobig++; + } + memcpy(rs->rs_rates + rs->rs_nrates, xrates+2, nxrates); + rs->rs_nrates += nxrates; + } + return ieee80211_fix_rate(ic, ni, flags); +} + +static void +ieee80211_auth_open(struct ieee80211com *ic, struct ieee80211_frame *wh, + struct ieee80211_node *ni, int rssi, u_int32_t rstamp, u_int16_t seq, + u_int16_t status) +{ + int allocbs; + + switch (ic->ic_opmode) { + case IEEE80211_M_IBSS: + if (ic->ic_state != IEEE80211_S_RUN || + seq != IEEE80211_AUTH_OPEN_REQUEST) { + ic->ic_stats.is_rx_bad_auth++; + return; + } + ieee80211_new_state(ic, IEEE80211_S_AUTH, + wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); + break; + + case IEEE80211_M_AHDEMO: + /* should not come here */ + break; + + case IEEE80211_M_HOSTAP: + if (ic->ic_state != IEEE80211_S_RUN || + seq != IEEE80211_AUTH_OPEN_REQUEST) { + ic->ic_stats.is_rx_bad_auth++; + return; + } + /* always accept open authentication requests */ + if (ni == ic->ic_bss) { + ni = ieee80211_dup_bss(ic, wh->i_addr2); + if (ni == NULL) + return; + allocbs = 1; + } else + allocbs = 0; + ni->ni_rssi = rssi; + ni->ni_rstamp = rstamp; + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_AUTH, seq + 1); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, + ("station %s %s authenticated (open)\n", + ether_sprintf(ni->ni_macaddr), + (allocbs ? "newly" : "already"))); + break; + + case IEEE80211_M_STA: + if (ic->ic_state != IEEE80211_S_AUTH || + seq != IEEE80211_AUTH_OPEN_RESPONSE) { + ic->ic_stats.is_rx_bad_auth++; + return; + } + if (status != 0) { + IEEE80211_DPRINTF(ic, + IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, + ("open authentication failed (reason %d) for %s\n", + status, + ether_sprintf(wh->i_addr3))); + /* XXX can this happen? */ + if (ni != ic->ic_bss) + ni->ni_fails++; + ic->ic_stats.is_rx_auth_fail++; + return; + } + ieee80211_new_state(ic, IEEE80211_S_ASSOC, + wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); + break; + case IEEE80211_M_MONITOR: + break; + } +} + +static int +alloc_challenge(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + if (ni->ni_challenge == NULL) + MALLOC(ni->ni_challenge, u_int32_t*, IEEE80211_CHALLENGE_LEN, + M_DEVBUF, M_NOWAIT); + if (ni->ni_challenge == NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, + ("%s: challenge alloc failed\n", __func__)); + /* XXX statistic */ + } + return (ni->ni_challenge != NULL); +} + +/* XXX TODO: add statistics */ +static void +ieee80211_auth_shared(struct ieee80211com *ic, struct ieee80211_frame *wh, + u_int8_t *frm, u_int8_t *efrm, struct ieee80211_node *ni, int rssi, + u_int32_t rstamp, u_int16_t seq, u_int16_t status) +{ + u_int8_t *challenge; + int allocbs, estatus; + + /* + * NB: this can happen as we allow pre-shared key + * authentication to be enabled w/o wep being turned + * on so that configuration of these can be done + * in any order. It may be better to enforce the + * ordering in which case this check would just be + * for sanity/consistency. + */ + if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, + ("%s: WEP is off\n", __func__)); + estatus = IEEE80211_STATUS_ALG; + goto bad; + } + /* + * Pre-shared key authentication is evil; accept + * it only if explicitly configured (it is supported + * mainly for compatibility with clients like OS X). + */ + if (ni->ni_authmode != IEEE80211_AUTH_AUTO && + ni->ni_authmode != IEEE80211_AUTH_SHARED) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, + ("%s: operating in %u mode, reject\n", + __func__, ni->ni_authmode)); + ic->ic_stats.is_rx_bad_auth++; /* XXX maybe a unique error? */ + estatus = IEEE80211_STATUS_ALG; + goto bad; + } + + challenge = NULL; + if (frm + 1 < efrm) { + if ((frm[1] + 2) > (efrm - frm)) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, + ("%s: elt %d %d bytes too long\n", __func__, + frm[0], (frm[1] + 2) - (efrm - frm))); + ic->ic_stats.is_rx_bad_auth++; + estatus = IEEE80211_STATUS_CHALLENGE; + goto bad; + } + if (*frm == IEEE80211_ELEMID_CHALLENGE) + challenge = frm; + frm += frm[1] + 2; + } + switch (seq) { + case IEEE80211_AUTH_SHARED_CHALLENGE: + case IEEE80211_AUTH_SHARED_RESPONSE: + if (challenge == NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, + ("%s: no challenge sent\n", __func__)); + ic->ic_stats.is_rx_bad_auth++; + estatus = IEEE80211_STATUS_CHALLENGE; + goto bad; + } + if (challenge[1] != IEEE80211_CHALLENGE_LEN) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, + ("%s: bad challenge len %d\n", + __func__, challenge[1])); + ic->ic_stats.is_rx_bad_auth++; + estatus = IEEE80211_STATUS_CHALLENGE; + goto bad; + } + default: + break; + } + switch (ic->ic_opmode) { + case IEEE80211_M_MONITOR: + case IEEE80211_M_AHDEMO: + case IEEE80211_M_IBSS: + IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, + ("%s: unexpected operating mode\n", __func__)); + return; + case IEEE80211_M_HOSTAP: + if (ic->ic_state != IEEE80211_S_RUN) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, + ("%s: not running\n", __func__)); + estatus = IEEE80211_STATUS_ALG; /* XXX */ + goto bad; + } + switch (seq) { + case IEEE80211_AUTH_SHARED_REQUEST: + if (ni == ic->ic_bss) { + ni = ieee80211_dup_bss(ic, wh->i_addr2); + if (ni == NULL) { + /* NB: no way to return an error */ + return; + } + allocbs = 1; + } else + allocbs = 0; + ni->ni_rssi = rssi; + ni->ni_rstamp = rstamp; + if (!alloc_challenge(ic, ni)) { + /* NB: don't return error so they rexmit */ + return; + } + get_random_bytes(ni->ni_challenge, + IEEE80211_CHALLENGE_LEN); + IEEE80211_DPRINTF(ic, + IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, + ("shared key %sauth request from station %s\n", + (allocbs ? "" : "re"), + ether_sprintf(ni->ni_macaddr))); + break; + case IEEE80211_AUTH_SHARED_RESPONSE: + if (ni == ic->ic_bss) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, + ("%s: unknown STA\n", __func__)); + /* NB: don't send a response */ + return; + } + if (ni->ni_challenge == NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, + ("%s: no challenge recorded\n", __func__)); + ic->ic_stats.is_rx_bad_auth++; + estatus = IEEE80211_STATUS_CHALLENGE; + goto bad; + } + if (memcmp(ni->ni_challenge, &challenge[2], + challenge[1]) != 0) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, + ("%s: challenge mismatch\n", __func__)); + ic->ic_stats.is_rx_auth_fail++; + estatus = IEEE80211_STATUS_CHALLENGE; + goto bad; + } + IEEE80211_DPRINTF(ic, + IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, + ("station %s authenticated (shared key)\n", + ether_sprintf(ni->ni_macaddr))); + break; + default: + IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, + ("%s: bad shared key auth seq %d from %s\n", + __func__, seq, ether_sprintf(wh->i_addr2))); + ic->ic_stats.is_rx_bad_auth++; + estatus = IEEE80211_STATUS_SEQUENCE; + goto bad; + } + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_AUTH, seq + 1); + break; + + case IEEE80211_M_STA: + if (ic->ic_state != IEEE80211_S_AUTH) + return; + switch (seq) { + case IEEE80211_AUTH_SHARED_PASS: + if (ni->ni_challenge != NULL) { + FREE(ni->ni_challenge, M_DEVBUF); + ni->ni_challenge = NULL; + } + if (status != 0) { + IEEE80211_DPRINTF(ic, + IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH, + ("%s: auth failed (reason %d) for %s\n", + __func__, status, + ether_sprintf(wh->i_addr3))); + /* XXX can this happen? */ + if (ni != ic->ic_bss) + ni->ni_fails++; + ic->ic_stats.is_rx_auth_fail++; + return; + } + ieee80211_new_state(ic, IEEE80211_S_ASSOC, + wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); + break; + case IEEE80211_AUTH_SHARED_CHALLENGE: + if (!alloc_challenge(ic, ni)) + return; + /* XXX could optimize by passing recvd challenge */ + memcpy(ni->ni_challenge, &challenge[2], challenge[1]); + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_AUTH, seq + 1); + break; + default: + IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, + ("%s: bad seq %d from %s\n", + __func__, seq, ether_sprintf(wh->i_addr2))); + ic->ic_stats.is_rx_bad_auth++; + return; + } + break; + } + return; +bad: + /* + * Send an error response; but only when operating as an AP. + */ + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + /* XXX hack to workaround calling convention */ + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_AUTH, + (seq + 1) | (estatus<<16)); + } +} + +/* Verify the existence and length of __elem or get out. */ +#define IEEE80211_VERIFY_ELEMENT(__elem, __maxlen) do { \ + if ((__elem) == NULL) { \ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID, \ + ("%s: no " #__elem "in %s frame\n", \ + __func__, ieee80211_mgt_subtype_name[subtype >> \ + IEEE80211_FC0_SUBTYPE_SHIFT])); \ + ic->ic_stats.is_rx_elem_missing++; \ + return; \ + } \ + if ((__elem)[1] > (__maxlen)) { \ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID, \ + ("%s: bad " #__elem " len %d in %s frame from %s\n",\ + __func__, (__elem)[1], \ + ieee80211_mgt_subtype_name[subtype >> \ + IEEE80211_FC0_SUBTYPE_SHIFT], \ + ether_sprintf(wh->i_addr2))); \ + ic->ic_stats.is_rx_elem_toobig++; \ + return; \ + } \ +} while (0) + +#define IEEE80211_VERIFY_LENGTH(_len, _minlen) do { \ + if ((_len) < (_minlen)) { \ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID, \ + ("%s: %s frame too short from %s\n", \ + __func__, \ + ieee80211_mgt_subtype_name[subtype >> \ + IEEE80211_FC0_SUBTYPE_SHIFT], \ + ether_sprintf(wh->i_addr2))); \ + ic->ic_stats.is_rx_elem_toosmall++; \ + return; \ + } \ +} while (0) + +#ifdef IEEE80211_DEBUG +static void +ieee80211_ssid_mismatch(struct ieee80211com *ic, const char *tag, + u_int8_t mac[IEEE80211_ADDR_LEN], u_int8_t *ssid) +{ + printf("[%s] %s req ssid mismatch: ", ether_sprintf(mac), tag); + ieee80211_print_essid(ssid + 2, ssid[1]); + printf("\n"); +} + +#define IEEE80211_VERIFY_SSID(_ni, _ssid, _packet_type) do { \ + if ((_ssid)[1] != 0 && \ + ((_ssid)[1] != (_ni)->ni_esslen || \ + memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) { \ + if (ieee80211_msg_input(ic)) \ + ieee80211_ssid_mismatch(ic, _packet_type, \ + wh->i_addr2, _ssid); \ + ic->ic_stats.is_rx_ssidmismatch++; \ + return; \ + } \ +} while (0) +#else /* !IEEE80211_DEBUG */ +#define IEEE80211_VERIFY_SSID(_ni, _ssid, _packet_type) do { \ + if ((_ssid)[1] != 0 && \ + ((_ssid)[1] != (_ni)->ni_esslen || \ + memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) { \ + ic->ic_stats.is_rx_ssidmismatch++; \ + return; \ + } \ +} while (0) +#endif /* !IEEE80211_DEBUG */ + +/* unalligned little endian access */ +#define LE_READ_2(p) \ + ((u_int16_t) \ + ((((u_int8_t *)(p))[0] ) | (((u_int8_t *)(p))[1] << 8))) +#define LE_READ_4(p) \ + ((u_int32_t) \ + ((((u_int8_t *)(p))[0] ) | (((u_int8_t *)(p))[1] << 8) | \ + (((u_int8_t *)(p))[2] << 16) | (((u_int8_t *)(p))[3] << 24))) + +static int __inline +iswpaoui(const u_int8_t *frm) +{ + return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI); +} + +static int __inline +isatherosoui(const u_int8_t *frm) +{ + return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI); +} + +/* + * Convert a WPA cipher selector OUI to an internal + * cipher algorithm. Where appropriate we also + * record any key length. + */ +static int +wpa_cipher(u_int8_t *sel, u_int8_t *keylen) +{ +#define WPA_SEL(x) (((x)<<24)|WPA_OUI) + u_int32_t w = LE_READ_4(sel); + + switch (w) { + case WPA_SEL(WPA_CSE_NULL): + return IEEE80211_CIPHER_NONE; + case WPA_SEL(WPA_CSE_WEP40): + if (keylen) + *keylen = 40 / NBBY; + return IEEE80211_CIPHER_WEP; + case WPA_SEL(WPA_CSE_WEP104): + if (keylen) + *keylen = 104 / NBBY; + return IEEE80211_CIPHER_WEP; + case WPA_SEL(WPA_CSE_TKIP): + return IEEE80211_CIPHER_TKIP; + case WPA_SEL(WPA_CSE_CCMP): + return IEEE80211_CIPHER_AES_CCM; + } + return 32; /* NB: so 1<< is discarded */ +#undef WPA_SEL +} + +/* + * Convert a WPA key management/authentication algorithm + * to an internal code. + */ +static int +wpa_keymgmt(u_int8_t *sel) +{ +#define WPA_SEL(x) (((x)<<24)|WPA_OUI) + u_int32_t w = LE_READ_4(sel); + + switch (w) { + case WPA_SEL(WPA_ASE_8021X_UNSPEC): + return WPA_ASE_8021X_UNSPEC; + case WPA_SEL(WPA_ASE_8021X_PSK): + return WPA_ASE_8021X_PSK; + case WPA_SEL(WPA_ASE_NONE): + return WPA_ASE_NONE; + } + return 0; /* NB: so is discarded */ +#undef WPA_SEL +} + +/* + * Parse a WPA information element to collect parameters + * and validate the parameters against what has been + * configured for the system. + */ +static int +ieee80211_parse_wpa(struct ieee80211com *ic, u_int8_t *frm, struct ieee80211_rsnparms *rsn) +{ + u_int8_t len = frm[1]; + u_int32_t w; + int n; + + /* + * Check the length once for fixed parts: OUI, type, + * version, mcast cipher, and 2 selector counts. + * Other, variable-length data, must be checked separately. + */ + KASSERT(ic->ic_flags & IEEE80211_F_WPA1, + ("not WPA, flags 0x%x", ic->ic_flags)); + if (len < 14) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + ("%s: length %u too short\n", __func__, len)); + return IEEE80211_REASON_IE_INVALID; + } + frm += 6, len -= 4; /* NB: len is payload only */ + /* NB: iswapoui already validated the OUI and type */ + w = LE_READ_2(frm); + if (w != WPA_VERSION) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + ("%s: bad version %u\n", __func__, w)); + return IEEE80211_REASON_IE_INVALID; + } + frm += 2, len -= 2; + + /* multicast/group cipher */ + w = wpa_cipher(frm, &rsn->rsn_mcastkeylen); + if (w != rsn->rsn_mcastcipher) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + ("%s: mcast cipher mismatch; got %u, expected %u\n", + __func__, w, rsn->rsn_mcastcipher)); + return IEEE80211_REASON_IE_INVALID; + } + frm += 4, len -= 4; + + /* unicast ciphers */ + n = LE_READ_2(frm); + frm += 2, len -= 2; + if (len < n*4+2) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + ("%s: not enough data for ucast ciphers; len %u, n %u\n", + __func__, len, n)); + return IEEE80211_REASON_IE_INVALID; + } + w = 0; + for (; n > 0; n--) { + w |= 1<rsn_ucastkeylen); + frm += 4, len -= 4; + } + w &= rsn->rsn_ucastcipherset; + if (w == 0) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + ("%s: ucast cipher set empty\n", __func__)); + return IEEE80211_REASON_IE_INVALID; + } + if (w & (1<rsn_ucastcipher = IEEE80211_CIPHER_TKIP; + else + rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM; + + /* key management algorithms */ + n = LE_READ_2(frm); + frm += 2, len -= 2; + if (len < n*4) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + ("%s: not enough data for key mgmt algorithms; len %u, n %u\n", + __func__, len, n)); + return IEEE80211_REASON_IE_INVALID; + } + w = 0; + for (; n > 0; n--) { + w |= wpa_keymgmt(frm); + frm += 4, len -= 4; + } + w &= rsn->rsn_keymgmtset; + if (w == 0) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + ("%s: no acceptable key mgmt algorithms\n", __func__)); + return IEEE80211_REASON_IE_INVALID; + } + if (w & WPA_ASE_8021X_UNSPEC) + rsn->rsn_keymgmt = WPA_ASE_8021X_UNSPEC; + else + rsn->rsn_keymgmt = WPA_ASE_8021X_PSK; + + if (len > 2) /* optional capabilities */ + rsn->rsn_caps = LE_READ_2(frm); + + return 0; +} + +/* + * Convert an RSN cipher selector OUI to an internal + * cipher algorithm. Where appropriate we also + * record any key length. + */ +static int +rsn_cipher(u_int8_t *sel, u_int8_t *keylen) +{ +#define RSN_SEL(x) (((x)<<24)|RSN_OUI) + u_int32_t w = LE_READ_4(sel); + + switch (w) { + case RSN_SEL(RSN_CSE_NULL): + return IEEE80211_CIPHER_NONE; + case RSN_SEL(RSN_CSE_WEP40): + if (keylen) + *keylen = 40 / NBBY; + return IEEE80211_CIPHER_WEP; + case RSN_SEL(RSN_CSE_WEP104): + if (keylen) + *keylen = 104 / NBBY; + return IEEE80211_CIPHER_WEP; + case RSN_SEL(RSN_CSE_TKIP): + return IEEE80211_CIPHER_TKIP; + case RSN_SEL(RSN_CSE_CCMP): + return IEEE80211_CIPHER_AES_CCM; + case RSN_SEL(RSN_CSE_WRAP): + return IEEE80211_CIPHER_AES_OCB; + } + return 32; /* NB: so 1<< is discarded */ +#undef WPA_SEL +} + +/* + * Convert an RSN key management/authentication algorithm + * to an internal code. + */ +static int +rsn_keymgmt(u_int8_t *sel) +{ +#define RSN_SEL(x) (((x)<<24)|RSN_OUI) + u_int32_t w = LE_READ_4(sel); + + switch (w) { + case RSN_SEL(RSN_ASE_8021X_UNSPEC): + return RSN_ASE_8021X_UNSPEC; + case RSN_SEL(RSN_ASE_8021X_PSK): + return RSN_ASE_8021X_PSK; + case RSN_SEL(RSN_ASE_NONE): + return RSN_ASE_NONE; + } + return 0; /* NB: so is discarded */ +#undef RSN_SEL +} + +/* + * Parse a WPA/RSN information element to collect parameters + * and validate the parameters against what has been + * configured for the system. + */ +static int +ieee80211_parse_rsn(struct ieee80211com *ic, u_int8_t *frm, struct ieee80211_rsnparms *rsn) +{ + u_int8_t len = frm[1]; + u_int32_t w; + int n; + + /* + * Check the length once for fixed parts: + * version, mcast cipher, and 2 selector counts. + * Other, variable-length data, must be checked separately. + */ + KASSERT(ic->ic_flags & IEEE80211_F_WPA2, + ("not RSN, flags 0x%x", ic->ic_flags)); + if (len < 10) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + ("%s: length %u too short\n", __func__, len)); + return IEEE80211_REASON_IE_INVALID; + } + frm += 2; /* skip id+len */ + w = LE_READ_2(frm); + if (w != RSN_VERSION) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + ("%s: bad version %u\n", __func__, w)); + return IEEE80211_REASON_IE_INVALID; + } + frm += 2, len -= 2; + + /* multicast/group cipher */ + w = rsn_cipher(frm, &rsn->rsn_mcastkeylen); + if (w != rsn->rsn_mcastcipher) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + ("%s: mcast cipher mismatch; got %u, expected %u\n", + __func__, w, rsn->rsn_mcastcipher)); + return IEEE80211_REASON_IE_INVALID; + } + frm += 4, len -= 4; + + /* unicast ciphers */ + n = LE_READ_2(frm); + frm += 2, len -= 2; + if (len < n*4+2) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + ("%s: not enough data for ucast ciphers; len %u, n %u\n", + __func__, len, n)); + return IEEE80211_REASON_IE_INVALID; + } + w = 0; + for (; n > 0; n--) { + w |= 1<rsn_ucastkeylen); + frm += 4, len -= 4; + } + w &= rsn->rsn_ucastcipherset; + if (w == 0) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + ("%s: ucast cipher set empty\n", __func__)); + return IEEE80211_REASON_IE_INVALID; + } + if (w & (1<rsn_ucastcipher = IEEE80211_CIPHER_TKIP; + else + rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM; + + /* key management algorithms */ + n = LE_READ_2(frm); + frm += 2, len -= 2; + if (len < n*4) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + ("%s: not enough data for key mgmt algorithms; len %u, n %u\n", + __func__, len, n)); + return IEEE80211_REASON_IE_INVALID; + } + w = 0; + for (; n > 0; n--) { + w |= rsn_keymgmt(frm); + frm += 4, len -= 4; + } + w &= rsn->rsn_keymgmtset; + if (w == 0) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA, + ("%s: no acceptable key mgmt algorithms\n", __func__)); + return IEEE80211_REASON_IE_INVALID; + } + if (w & RSN_ASE_8021X_UNSPEC) + rsn->rsn_keymgmt = RSN_ASE_8021X_UNSPEC; + else + rsn->rsn_keymgmt = RSN_ASE_8021X_PSK; + + /* optional RSN capabilities */ + if (len > 2) + rsn->rsn_caps = LE_READ_2(frm); + /* XXXPMKID */ + + return 0; +} + +static void +ieee80211_saveie(u_int8_t **iep, const u_int8_t *ie) +{ + u_int ielen = ie[1]+2; + /* + * Record information element for later use. + */ + if (*iep == NULL || (*iep)[1] != ie[1]) { + if (*iep != NULL) + FREE(*iep, M_DEVBUF); + MALLOC(*iep, void*, ielen, M_DEVBUF, M_NOWAIT); + } + if (*iep != NULL) + memcpy(*iep, ie, ielen); +} + +#ifdef IEEE80211_DEBUG +static void +dump_probe_beacon(u_int8_t subtype, int isnew, + const u_int8_t mac[IEEE80211_ADDR_LEN], + u_int8_t chan, u_int8_t bchan, u_int16_t capinfo, u_int16_t bintval, + u_int8_t erp, u_int8_t *ssid, u_int8_t *country) +{ + printf("[%s] %s%s on chan %u (bss chan %u) ", + ether_sprintf(mac), isnew ? "new " : "", + (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) ? "probe response" : "beacon", + chan, bchan); + ieee80211_print_essid(ssid + 2, ssid[1]); + printf("\n"); + + if (isnew) { + printf("[%s] caps 0x%x bintval %u erp 0x%x", + ether_sprintf(mac), capinfo, bintval, erp); + if (country) { +#ifdef __FreeBSD__ + printf(" country info %*D", country[1], country+2, " "); +#else + int i; + printf(" country info"); + for (i = 0; i < country[1]; i++) + printf(" %02x", country[i+2]); +#endif + } + printf("\n"); + } +} +#endif /* IEEE80211_DEBUG */ + +void +ieee80211_recv_mgmt(struct ieee80211com *ic, struct sk_buff *skb, + struct ieee80211_node *ni, + int subtype, int rssi, u_int32_t rstamp) +{ +#define ISPROBE(_st) ((_st) == IEEE80211_FC0_SUBTYPE_PROBE_RESP) +#define ISREASSOC(_st) ((_st) == IEEE80211_FC0_SUBTYPE_REASSOC_RESP) + struct ieee80211_frame *wh; + u_int8_t *frm, *efrm; + u_int8_t *ssid, *rates, *xrates, *wpa; + int reassoc, resp, allocbs; + + wh = (struct ieee80211_frame *) skb->data; + frm = (u_int8_t *)&wh[1]; + efrm = skb->data + skb->len; + switch (subtype) { + case IEEE80211_FC0_SUBTYPE_PROBE_RESP: + case IEEE80211_FC0_SUBTYPE_BEACON: { + u_int8_t *tstamp, *country, *wpa; + u_int8_t chan, bchan, fhindex, erp; + u_int16_t capinfo, bintval, timoff; + u_int16_t fhdwell; + + if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) { + /* + * Count beacon frames specially, some drivers + * use this info to do things like update LED's. + */ + ic->ic_stats.is_rx_beacon++; + } + /* + * We process beacon/probe response frames for: + * o station mode when associated: to collect state + * updates such as 802.11g slot time + * o adhoc mode: to discover neighbors + * o when scanning + * Frames otherwise received are discarded. + */ + if (!((ic->ic_opmode == IEEE80211_M_STA && ni->ni_associd != 0) + || ic->ic_opmode == IEEE80211_M_IBSS + || ic->ic_state == IEEE80211_S_SCAN)) { + /* XXX: may be useful for background scan */ + ic->ic_stats.is_rx_mgtdiscard++; + return; + } + /* + * beacon/probe response frame format + * [8] time stamp + * [2] beacon interval + * [2] capability information + * [tlv] ssid + * [tlv] supported rates + * [tlv] country information + * [tlv] parameter set (FH/DS) + * [tlv] erp information + * [tlv] extended supported rates + * [tlv] WPA or RSN + */ + IEEE80211_VERIFY_LENGTH(efrm - frm, 12); + tstamp = frm; frm += 8; + bintval = le16toh(*(u_int16_t *)frm); frm += 2; + capinfo = le16toh(*(u_int16_t *)frm); frm += 2; + ssid = rates = xrates = country = wpa = NULL; + bchan = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan); + chan = bchan; + fhdwell = 0; + fhindex = 0; + erp = 0; + timoff = 0; + while (frm < efrm) { + switch (*frm) { + case IEEE80211_ELEMID_SSID: + ssid = frm; + break; + case IEEE80211_ELEMID_RATES: + rates = frm; + break; + case IEEE80211_ELEMID_COUNTRY: + country = frm; + break; + case IEEE80211_ELEMID_FHPARMS: + if (ic->ic_phytype == IEEE80211_T_FH) { + fhdwell = LE_READ_2(&frm[2]); + chan = IEEE80211_FH_CHAN(frm[4], frm[5]); + fhindex = frm[6]; + } + break; + case IEEE80211_ELEMID_DSPARMS: + /* + * XXX hack this since depending on phytype + * is problematic for multi-mode devices. + */ + if (ic->ic_phytype != IEEE80211_T_FH) + chan = frm[2]; + break; + case IEEE80211_ELEMID_TIM: + /* XXX ATIM? */ + timoff = frm - skb->data; + break; + case IEEE80211_ELEMID_IBSSPARMS: + break; + case IEEE80211_ELEMID_XRATES: + xrates = frm; + break; + case IEEE80211_ELEMID_ERP: + if (frm[1] != 1) { + IEEE80211_DPRINTF(ic, + IEEE80211_MSG_ELEMID, + ("%s: invalid ERP element; " + "length %u, expecting 1\n", + __func__, frm[1])); + ic->ic_stats.is_rx_elem_toobig++; + break; + } + erp = frm[2]; + break; + case IEEE80211_ELEMID_RSN: + wpa = frm; + break; + case IEEE80211_ELEMID_VENDOR: + if (iswpaoui(frm)) + wpa = frm; + /* XXX Atheros OUI support */ + break; + default: + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID, + ("%s: element id %u/len %u ignored\n", + __func__, *frm, frm[1])); + ic->ic_stats.is_rx_elem_unknown++; + break; + } + frm += frm[1] + 2; + } + IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE); + IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN); + if ( +#if IEEE80211_CHAN_MAX < 255 + chan > IEEE80211_CHAN_MAX || +#endif + isclr(ic->ic_chan_active, chan)) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID, + ("%s: ignore %s with invalid channel %u\n", + __func__, + ISPROBE(subtype) ? "probe response" : "beacon", + chan)); + ic->ic_stats.is_rx_badchan++; + return; + } + if (chan != bchan && ic->ic_phytype != IEEE80211_T_FH) { + /* + * Frame was received on a channel different from the + * one indicated in the DS params element id; + * silently discard it. + * + * NB: this can happen due to signal leakage. + * But we should take it for FH phy because + * the rssi value should be correct even for + * different hop pattern in FH. + */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ELEMID, + ("%s: ignore %s on channel %u marked " + "for channel %u\n", __func__, + ISPROBE(subtype) ? "probe response" : "beacon", + bchan, chan)); + ic->ic_stats.is_rx_chanmismatch++; + return; + } + + /* + * Station mode, check for state updates. We + * consider only 11g stuff right now. + */ + if (ic->ic_opmode == IEEE80211_M_STA && ni->ni_associd != 0) { + if (ni->ni_erp != erp) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + ("erp change from %s: was 0x%x, now 0x%x\n", + ether_sprintf(wh->i_addr2), + ni->ni_erp, erp)); + if (erp & IEEE80211_ERP_USE_PROTECTION) + ic->ic_flags |= IEEE80211_F_USEPROT; + else + ic->ic_flags &= ~IEEE80211_F_USEPROT; + ni->ni_erp = erp; + /* XXX statistic */ + } + if ((ni->ni_capinfo ^ capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + ("capabilities change from %s: before 0x%x," + " now 0x%x\n", ether_sprintf(wh->i_addr2), + ni->ni_capinfo, capinfo)); + /* + * NB: we assume short preamble doesn't + * change dynamically + */ + ieee80211_set_shortslottime(ic, + ic->ic_curmode == IEEE80211_MODE_11A || + (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)); + ni->ni_capinfo = capinfo; + /* XXX statistic */ + } + return; + } + + /* + * Use mac and channel for lookup so we collect all + * potential AP's when scanning. Otherwise we may + * see the same AP on multiple channels and will only + * record the last one. We could filter APs here based + * on rssi, etc. but leave that to the end of the scan + * so we can keep the selection criteria in one spot. + * This may result in a bloat of the scanned AP list but + * it shouldn't be too much. + */ + ni = ieee80211_find_node_with_channel(ic, wh->i_addr2, + &ic->ic_channels[chan]); + if (ni == NULL) { +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_scan(ic)) + dump_probe_beacon(subtype, 1, + wh->i_addr2, chan, bchan, capinfo, + bintval, erp, ssid, country); +#endif + ni = ieee80211_dup_bss(ic, wh->i_addr2); + if (ni == NULL) + return; + ni->ni_esslen = ssid[1]; + memset(ni->ni_essid, 0, sizeof(ni->ni_essid)); + memcpy(ni->ni_essid, ssid + 2, ssid[1]); + } else if (ssid[1] != 0 && + (ISPROBE(subtype) || ni->ni_esslen == 0)) { + /* + * Update ESSID at probe response to adopt hidden AP by + * Lucent/Cisco, which announces null ESSID in beacon. + */ + ni->ni_esslen = ssid[1]; + memset(ni->ni_essid, 0, sizeof(ni->ni_essid)); + memcpy(ni->ni_essid, ssid + 2, ssid[1]); +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_scan(ic) || ieee80211_msg_debug(ic)) + dump_probe_beacon(subtype, 0, + wh->i_addr2, chan, bchan, capinfo, + bintval, erp, ssid, country); +#endif + } + IEEE80211_ADDR_COPY(ni->ni_bssid, wh->i_addr3); + ni->ni_rssi = rssi; + ni->ni_rstamp = rstamp; + memcpy(ni->ni_tstamp.data, tstamp, sizeof(ni->ni_tstamp)); + ni->ni_intval = bintval; + ni->ni_capinfo = capinfo; + /* XXX validate channel # */ + ni->ni_chan = &ic->ic_channels[chan]; + ni->ni_fhdwell = fhdwell; + ni->ni_fhindex = fhindex; + ni->ni_erp = erp; + /* + * Record the byte offset from the mac header to + * the start of the TIM information element for + * use by hardware and/or to speedup software + * processing of beacon frames. + */ + ni->ni_timoff = timoff; + /* + * Record optional information elements that might be + * used by applications or drivers. + */ + if (wpa != NULL) + ieee80211_saveie(&ni->ni_wpa_ie, wpa); + /* NB: must be after ni_chan is setup */ + ieee80211_setup_rates(ic, ni, rates, xrates, IEEE80211_F_DOSORT); + ieee80211_unref_node(&ni); /* NB: do not free */ + break; + } + + case IEEE80211_FC0_SUBTYPE_PROBE_REQ: { + u_int8_t rate; + + if (ic->ic_opmode == IEEE80211_M_STA || + ic->ic_state != IEEE80211_S_RUN) { + ic->ic_stats.is_rx_mgtdiscard++; + return; + } + + /* + * prreq frame format + * [tlv] ssid + * [tlv] supported rates + * [tlv] extended supported rates + */ + ssid = rates = xrates = NULL; + while (frm < efrm) { + switch (*frm) { + case IEEE80211_ELEMID_SSID: + ssid = frm; + break; + case IEEE80211_ELEMID_RATES: + rates = frm; + break; + case IEEE80211_ELEMID_XRATES: + xrates = frm; + break; + } + frm += frm[1] + 2; + } + IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE); + IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN); + IEEE80211_VERIFY_SSID(ic->ic_bss, ssid, "probe"); + + if (ni == ic->ic_bss) { + ni = ieee80211_dup_bss(ic, wh->i_addr2); + if (ni == NULL) + return; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + ("%s: new probe req from %s\n", + __func__, ether_sprintf(wh->i_addr2))); + allocbs = 1; + } else + allocbs = 0; + ni->ni_rssi = rssi; + ni->ni_rstamp = rstamp; + rate = ieee80211_setup_rates(ic, ni, rates, xrates, + IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE + | IEEE80211_F_DONEGO | IEEE80211_F_DODEL); + if (rate & IEEE80211_RATE_BASIC) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_XRATE, + ("%s: rate negotiation failed: %s\n", + __func__,ether_sprintf(wh->i_addr2))); + } else { + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_PROBE_RESP, 0); + } + if (allocbs) { + /* + * When operating as an AP we discard the node's + * state until the station requests authentication. + * This may be better done by holding it and setting + * a short timer for reclaiming it but reduces the + * possibility of stations flooding us with probe + * requests causing our memory use to grow quickly + * (though this can still happen if they send + * authentication requests). When operating in ibss + * mode we hold the node but with a zero reference + * count; this is the current convention (XXX). + */ + if (ic->ic_opmode == IEEE80211_M_HOSTAP) + ieee80211_free_node(ic, ni); + else + ieee80211_unref_node(&ni); + } + break; + } + + case IEEE80211_FC0_SUBTYPE_AUTH: { + u_int16_t algo, seq, status; + /* + * auth frame format + * [2] algorithm + * [2] sequence + * [2] status + * [tlv*] challenge + */ + IEEE80211_VERIFY_LENGTH(efrm - frm, 6); + algo = le16toh(*(u_int16_t *)frm); + seq = le16toh(*(u_int16_t *)(frm + 2)); + status = le16toh(*(u_int16_t *)(frm + 4)); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, + ("%s: algorithm %d seq %d from %s\n", + __func__, algo, seq, ether_sprintf(wh->i_addr2))); + + /* + * Consult the ACL policy module if setup. + */ + if (ic->ic_acl != NULL && + !ic->ic_acl->iac_check(ic, wh->i_addr2)) { + IEEE80211_DPRINTF(ic, + IEEE80211_MSG_AUTH | IEEE80211_MSG_ACL, + ("[%s] reject auth request by station due to ACL\n", + ether_sprintf(wh->i_addr2))); + ic->ic_stats.is_rx_acl++; + return; + } + if (ic->ic_flags & IEEE80211_F_COUNTERM) { + /* XXX only in ap mode? */ + IEEE80211_DPRINTF(ic, + IEEE80211_MSG_AUTH | IEEE80211_MSG_CRYPTO, + ("[%s] reject auth request by station due to TKIP " + "countermeasures\n", + ether_sprintf(wh->i_addr2))); + ic->ic_stats.is_rx_auth_countermeasures++; + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_AUTH, + IEEE80211_REASON_MIC_FAILURE); + } + return; + } + if (algo == IEEE80211_AUTH_ALG_SHARED) + ieee80211_auth_shared(ic, wh, frm + 6, efrm, ni, rssi, + rstamp, seq, status); + else if (algo == IEEE80211_AUTH_ALG_OPEN) + ieee80211_auth_open(ic, wh, ni, rssi, rstamp, seq, + status); + else { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + ("%s: unsupported auth algorithm %d from %s\n", + __func__, algo, ether_sprintf(wh->i_addr2))); + ic->ic_stats.is_rx_auth_unsupported++; + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + /* XXX not right */ + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_AUTH, + (seq+1) | (IEEE80211_STATUS_ALG<<16)); + } + return; + } + break; + } + + case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: + case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: { + u_int16_t capinfo, bintval; + struct ieee80211_rsnparms rsn; + u_int8_t reason; + + if (ic->ic_opmode != IEEE80211_M_HOSTAP || + ic->ic_state != IEEE80211_S_RUN) { + ic->ic_stats.is_rx_mgtdiscard++; + return; + } + + if (subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) { + reassoc = 1; + resp = IEEE80211_FC0_SUBTYPE_REASSOC_RESP; + } else { + reassoc = 0; + resp = IEEE80211_FC0_SUBTYPE_ASSOC_RESP; + } + /* + * asreq frame format + * [2] capability information + * [2] listen interval + * [6*] current AP address (reassoc only) + * [tlv] ssid + * [tlv] supported rates + * [tlv] extended supported rates + * [tlv] WPA or RSN + */ + IEEE80211_VERIFY_LENGTH(efrm - frm, (reassoc ? 10 : 4)); + if (!IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_bss->ni_bssid)) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + ("%s: ignore assoc request with bss %s not " + "our own\n", + __func__, ether_sprintf(wh->i_addr2))); + ic->ic_stats.is_rx_assoc_bss++; + return; + } + capinfo = le16toh(*(u_int16_t *)frm); frm += 2; + bintval = le16toh(*(u_int16_t *)frm); frm += 2; + if (reassoc) + frm += 6; /* ignore current AP info */ + ssid = rates = xrates = wpa = NULL; + while (frm < efrm) { + switch (*frm) { + case IEEE80211_ELEMID_SSID: + ssid = frm; + break; + case IEEE80211_ELEMID_RATES: + rates = frm; + break; + case IEEE80211_ELEMID_XRATES: + xrates = frm; + break; + /* XXX verify only one of RSN and WPA ie's? */ + case IEEE80211_ELEMID_RSN: + wpa = frm; + break; + case IEEE80211_ELEMID_VENDOR: + if (iswpaoui(frm)) { + if (ic->ic_flags & IEEE80211_F_WPA1) + wpa = frm; + } + /* XXX Atheros OUI support */ + break; + } + frm += frm[1] + 2; + } + IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE); + IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN); + IEEE80211_VERIFY_SSID(ic->ic_bss, ssid, + reassoc ? "reassoc" : "assoc"); + + if (ni == ic->ic_bss) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + ("[%s] deny %sassoc, not authenticated\n", + ether_sprintf(wh->i_addr2), reassoc ? "re" : "")); + ni = ieee80211_dup_bss(ic, wh->i_addr2); + if (ni != NULL) { + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_ASSOC_NOT_AUTHED); + ieee80211_free_node(ic, ni); + } + ic->ic_stats.is_rx_assoc_notauth++; + return; + } + if (wpa != NULL) { + /* + * Parse WPA information element. Note that + * we initialize the param block from the node + * state so that information in the IE overrides + * our defaults. The resulting parameters are + * installed below after the association is assured. + */ + rsn = ni->ni_rsn; + if (wpa[0] != IEEE80211_ELEMID_RSN) + reason = ieee80211_parse_wpa(ic, wpa, &rsn); + else + reason = ieee80211_parse_rsn(ic, wpa, &rsn); + if (reason != 0) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + ("%s: bad %s ie from %s, reason %u\n", + __func__, wpa[0] != IEEE80211_ELEMID_RSN ? + "WPA" : "RSN", + ether_sprintf(wh->i_addr2), reason)); + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_DEAUTH, reason); + ieee80211_node_leave(ic, ni); + /* XXX distinguish WPA/RSN? */ + ic->ic_stats.is_rx_assoc_badwpaie++; + return; + } + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA, + ("%s: %s ie from %s, " + "mc %u/%u uc %u/%u key %u caps 0x%x\n", + __func__, wpa[0] != IEEE80211_ELEMID_RSN ? + "WPA" : "RSN", + ether_sprintf(wh->i_addr2), + rsn.rsn_mcastcipher, rsn.rsn_mcastkeylen, + rsn.rsn_ucastcipher, rsn.rsn_ucastkeylen, + rsn.rsn_keymgmt, rsn.rsn_caps)); + } + /* discard challenge after association */ + if (ni->ni_challenge != NULL) { + FREE(ni->ni_challenge, M_DEVBUF); + ni->ni_challenge = NULL; + } + /* XXX some stations use the privacy bit for handling APs + that suport both encrypted and unencrypted traffic */ + /* NB: PRIVACY flag bits are assumed to match */ + if ((capinfo & IEEE80211_CAPINFO_ESS) == 0 || + (capinfo & IEEE80211_CAPINFO_PRIVACY) ^ + (ic->ic_flags & IEEE80211_F_PRIVACY)) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + ("%s: capability mismatch 0x%x for %s\n", + __func__, capinfo, ether_sprintf(wh->i_addr2))); + IEEE80211_SEND_MGMT(ic, ni, resp, + IEEE80211_STATUS_CAPINFO); + ieee80211_node_leave(ic, ni); + ic->ic_stats.is_rx_assoc_capmismatch++; + return; + } + ieee80211_setup_rates(ic, ni, rates, xrates, + IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE | + IEEE80211_F_DONEGO | IEEE80211_F_DODEL); + if (ni->ni_rates.rs_nrates == 0) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + ("%s: rate mismatch for %s\n", + __func__, ether_sprintf(wh->i_addr2))); + IEEE80211_SEND_MGMT(ic, ni, resp, + IEEE80211_STATUS_BASIC_RATE); + ieee80211_node_leave(ic, ni); + ic->ic_stats.is_rx_assoc_norate++; + return; + } + ni->ni_rssi = rssi; + ni->ni_rstamp = rstamp; + ni->ni_intval = bintval; + ni->ni_capinfo = capinfo; + ni->ni_chan = ic->ic_bss->ni_chan; + ni->ni_fhdwell = ic->ic_bss->ni_fhdwell; + ni->ni_fhindex = ic->ic_bss->ni_fhindex; + if (wpa != NULL) { + /* + * Record WPA/RSN parameters for station, mark + * node as using WPA and record information element + * for applications that require it. + */ + ni->ni_rsn = rsn; + ieee80211_saveie(&ni->ni_wpa_ie, wpa); + } + ieee80211_node_join(ic, ni, resp); + break; + } + + case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: + case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: { + u_int16_t capinfo, associd; + u_int16_t status; + + if (ic->ic_opmode != IEEE80211_M_STA || + ic->ic_state != IEEE80211_S_ASSOC) { + ic->ic_stats.is_rx_mgtdiscard++; + return; + } + + /* + * asresp frame format + * [2] capability information + * [2] status + * [2] association ID + * [tlv] supported rates + * [tlv] extended supported rates + */ + IEEE80211_VERIFY_LENGTH(efrm - frm, 6); + ni = ic->ic_bss; + capinfo = le16toh(*(u_int16_t *)frm); + frm += 2; + status = le16toh(*(u_int16_t *)frm); + frm += 2; + if (status != 0) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + ("%sassociation failed (reason %d) for %s\n", + ISREASSOC(subtype) ? "re" : "", + status, ether_sprintf(wh->i_addr3))); + if (ni != ic->ic_bss) /* XXX never true? */ + ni->ni_fails++; + ic->ic_stats.is_rx_auth_fail++; /* XXX */ + return; + } + associd = le16toh(*(u_int16_t *)frm); + frm += 2; + + rates = xrates = NULL; + while (frm < efrm) { + switch (*frm) { + case IEEE80211_ELEMID_RATES: + rates = frm; + break; + case IEEE80211_ELEMID_XRATES: + xrates = frm; + break; + } + frm += frm[1] + 2; + } + + IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE); + ieee80211_setup_rates(ic, ni, rates, xrates, + IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE | + IEEE80211_F_DONEGO | IEEE80211_F_DODEL); + if (ni->ni_rates.rs_nrates == 0) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + ("%sassociation failed (rate set mismatch) " + "for %s\n", + ISREASSOC(subtype) ? "re" : "", + ether_sprintf(wh->i_addr3))); + if (ni != ic->ic_bss) /* XXX never true? */ + ni->ni_fails++; + ic->ic_stats.is_rx_assoc_norate++; + return; + } + + ni->ni_capinfo = capinfo; + ni->ni_associd = associd; + /* + * Configure state now that we are associated. + * + * XXX may need different/additional driver callbacks? + */ + if (ic->ic_curmode == IEEE80211_MODE_11A || + (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) { + ic->ic_flags |= IEEE80211_F_SHPREAMBLE; + ic->ic_flags &= ~IEEE80211_F_USEBARKER; + } else { + ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE; + ic->ic_flags |= IEEE80211_F_USEBARKER; + } + ieee80211_set_shortslottime(ic, + ic->ic_curmode == IEEE80211_MODE_11A || + (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)); + /* + * Honor ERP protection. + * + * NB: ni_erp should zero for non-11g operation. + * XXX check ic_curmode anyway? + */ + if (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION) + ic->ic_flags |= IEEE80211_F_USEPROT; + else + ic->ic_flags &= ~IEEE80211_F_USEPROT; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + ("%sassociate with %s: %s preamble, %s slot time%s\n", + ISREASSOC(subtype) ? "re" : "", + ether_sprintf(wh->i_addr2), + ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long", + ic->ic_flags&IEEE80211_F_SHSLOT ? "short" : "long", + ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : "") + ); + ieee80211_new_state(ic, IEEE80211_S_RUN, subtype); + break; + } + + case IEEE80211_FC0_SUBTYPE_DEAUTH: { + u_int16_t reason; + + if (ic->ic_state == IEEE80211_S_SCAN) { + ic->ic_stats.is_rx_mgtdiscard++; + return; + } + /* + * deauth frame format + * [2] reason + */ + IEEE80211_VERIFY_LENGTH(efrm - frm, 2); + reason = le16toh(*(u_int16_t *)frm); + ic->ic_stats.is_rx_deauth++; + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + ieee80211_new_state(ic, IEEE80211_S_AUTH, + wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); + break; + case IEEE80211_M_HOSTAP: + if (ni != ic->ic_bss) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, + ("station %s deauthenticated by " + "peer (reason %d)\n", + ether_sprintf(ni->ni_macaddr), reason)); + ieee80211_node_leave(ic, ni); + } + break; + default: + ic->ic_stats.is_rx_mgtdiscard++; + break; + } + break; + } + + case IEEE80211_FC0_SUBTYPE_DISASSOC: { + u_int16_t reason; + + if (ic->ic_state != IEEE80211_S_RUN && + ic->ic_state != IEEE80211_S_AUTH) { + ic->ic_stats.is_rx_mgtdiscard++; + return; + } + /* + * disassoc frame format + * [2] reason + */ + IEEE80211_VERIFY_LENGTH(efrm - frm, 2); + reason = le16toh(*(u_int16_t *)frm); + ic->ic_stats.is_rx_disassoc++; + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + ieee80211_new_state(ic, IEEE80211_S_ASSOC, + wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); + break; + case IEEE80211_M_HOSTAP: + if (ni != ic->ic_bss) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + ("station %s disassociated by " + "peer (reason %d)\n", + ether_sprintf(ni->ni_macaddr), reason)); + ieee80211_node_leave(ic, ni); + } + break; + default: + ic->ic_stats.is_rx_mgtdiscard++; + break; + } + break; + } + default: + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + ("%s: mgmt frame with subtype 0x%x not handled\n", + __func__, subtype)); + ic->ic_stats.is_rx_badsubtype++; + break; + } +#undef ISREASSOC +#undef ISPROBE +} +#undef IEEE80211_VERIFY_LENGTH +#undef IEEE80211_VERIFY_ELEMENT + +static void +ieee80211_recv_pspoll(struct ieee80211com *ic, + struct ieee80211_node *ni, struct sk_buff *skb0) +{ + struct ieee80211_frame_min *wh; + struct sk_buff *skb; + u_int16_t aid; + + if (ic->ic_set_tim == NULL) /* No powersaving functionality */ + return; + if (ni == ic->ic_bss) { + wh = (struct ieee80211_frame_min *)skb0->data; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG, + ("station %s sent bogus power save poll\n", + ether_sprintf(wh->i_addr2))); + return; + } + + wh = (struct ieee80211_frame_min *)skb0->data; + memcpy(&aid, wh->i_dur, sizeof(wh->i_dur)); + if ((aid & 0xc000) != 0xc000) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG, + ("station %s sent bogus aid %x\n", + ether_sprintf(wh->i_addr2), aid)); + /* XXX statistic */ + return; + } + if (aid != ni->ni_associd) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG, + ("station %s aid %x doesn't match pspoll aid %x\n", + ether_sprintf(wh->i_addr2), ni->ni_associd, aid)); + /* XXX statistic */ + return; + } + + /* Okay, take the first queued packet and put it out... */ + IF_DEQUEUE(&ni->ni_savedq, skb); + if (skb == NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, + ("station %s sent pspoll, but no packets are saved\n", + ether_sprintf(wh->i_addr2))); + /* XXX statistic */ + return; + } + /* + * If this is the last packet, turn off the TIM fields. + * If there are more packets, set the more packets bit. + */ + if (_IF_QLEN(&ni->ni_savedq) == 0) { + if (ic->ic_set_tim) + (*ic->ic_set_tim)(ic, ni->ni_associd, 0); + } else { + wh = (struct ieee80211_frame_min *) skb->data; + wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA; + } + IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, + ("enqueued power saving packet for station %s\n", + ether_sprintf(ni->ni_macaddr))); + /* XXX need different driver interface */ + (*ic->ic_dev->hard_start_xmit)(skb, ic->ic_dev); +} diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_ioctl.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_ioctl.h --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_ioctl.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_ioctl.h 2005-02-24 13:06:17.308130784 -0800 @@ -0,0 +1,389 @@ +/* $NetBSD: ieee80211_ioctl.h,v 1.5 2003/10/13 04:16:59 dyoung Exp $ */ +/*- + * Copyright (c) 2001 Atsushi Onoe + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/net80211/ieee80211_ioctl.h,v 1.4 2003/10/17 23:15:30 sam Exp $ + */ +#ifndef _NET80211_IEEE80211_IOCTL_H_ +#define _NET80211_IEEE80211_IOCTL_H_ + +/* + * IEEE 802.11 ioctls. + */ + +/* + * Per/node (station) statistics available when operating as an AP. + */ +struct ieee80211_nodestats { + u_int32_t ns_rx_data; /* rx data frames */ + u_int32_t ns_rx_mgmt; /* rx management frames */ + u_int32_t ns_rx_ctrl; /* rx control frames */ + u_int32_t ns_rx_ucast; /* rx unicast frames */ + u_int32_t ns_rx_mcast; /* rx multi/broadcast frames */ + u_int64_t ns_rx_bytes; /* rx data count (bytes) */ + + u_int32_t ns_rx_dup; /* rx discard 'cuz dup */ + u_int32_t ns_rx_noprivacy; /* rx w/ wep but privacy off */ + u_int32_t ns_rx_wepfail; /* rx wep processing failed */ + u_int32_t ns_rx_decap; /* rx decapsulation failed */ + u_int32_t ns_rx_disassoc; /* rx disassociation */ + u_int32_t ns_rx_deauth; /* rx deauthentication */ + u_int32_t ns_rx_decryptcrc; /* rx decrypt failed on crc */ + u_int32_t ns_rx_unauth; /* rx on unauthorized port */ + + u_int32_t ns_tx_data; /* tx data frames */ + u_int32_t ns_tx_mgmt; /* tx management frames */ + u_int32_t ns_tx_ucast; /* tx unicast frames */ + u_int32_t ns_tx_mcast; /* tx multi/broadcast frames */ + u_int64_t ns_tx_bytes; /* tx data count (bytes) */ + + u_int32_t ns_tx_novlantag; /* tx discard 'cuz no tag */ + u_int32_t ns_tx_vlanmismatch; /* tx discard 'cuz bad tag */ + + /* MIB-related state */ + u_int32_t ns_mib_assoc; /* [re]associations */ + u_int32_t ns_mib_assoc_fail; /* [re]association failures */ + u_int32_t ns_mib_auth; /* [re]authentications */ + u_int32_t ns_mib_auth_fail; /* [re]authentication failures*/ + u_int32_t ns_mib_deauth; /* deauthentications */ + u_int32_t ns_mib_deauth_code; /* last deauth reason */ + u_int32_t ns_mib_disassoc; /* disassociations */ + u_int32_t ns_mib_disassoc_code; /* last disassociation reason */ +}; + +/* + * Summary statistics. + */ +struct ieee80211_stats { + u_int32_t is_rx_badversion; /* rx frame with bad version */ + u_int32_t is_rx_tooshort; /* rx frame too short */ + u_int32_t is_rx_wrongbss; /* rx from wrong bssid */ + u_int32_t is_rx_dup; /* rx discard 'cuz dup */ + u_int32_t is_rx_wrongdir; /* rx w/ wrong direction */ + u_int32_t is_rx_mcastecho; /* rx discard 'cuz mcast echo */ + u_int32_t is_rx_notassoc; /* rx discard 'cuz sta !assoc */ + u_int32_t is_rx_noprivacy; /* rx w/ wep but privacy off */ + u_int32_t is_rx_unencrypted; /* rx w/o wep and privacy on */ + u_int32_t is_rx_wepfail; /* rx wep processing failed */ + u_int32_t is_rx_decap; /* rx decapsulation failed */ + u_int32_t is_rx_mgtdiscard; /* rx discard mgt frames */ + u_int32_t is_rx_ctl; /* rx discard ctrl frames */ + u_int32_t is_rx_beacon; /* rx beacon frames */ + u_int32_t is_rx_rstoobig; /* rx rate set truncated */ + u_int32_t is_rx_elem_missing; /* rx required element missing*/ + u_int32_t is_rx_elem_toobig; /* rx element too big */ + u_int32_t is_rx_elem_toosmall; /* rx element too small */ + u_int32_t is_rx_elem_unknown; /* rx element unknown */ + u_int32_t is_rx_badchan; /* rx frame w/ invalid chan */ + u_int32_t is_rx_chanmismatch; /* rx frame chan mismatch */ + u_int32_t is_rx_nodealloc; /* rx frame dropped */ + u_int32_t is_rx_ssidmismatch; /* rx frame ssid mismatch */ + u_int32_t is_rx_auth_unsupported; /* rx w/ unsupported auth alg */ + u_int32_t is_rx_auth_fail; /* rx sta auth failure */ + u_int32_t is_rx_auth_countermeasures;/* rx auth discard 'cuz CM */ + u_int32_t is_rx_assoc_bss; /* rx assoc from wrong bssid */ + u_int32_t is_rx_assoc_notauth; /* rx assoc w/o auth */ + u_int32_t is_rx_assoc_capmismatch;/* rx assoc w/ cap mismatch */ + u_int32_t is_rx_assoc_norate; /* rx assoc w/ no rate match */ + u_int32_t is_rx_assoc_badwpaie; /* rx assoc w/ bad WPA IE */ + u_int32_t is_rx_deauth; /* rx deauthentication */ + u_int32_t is_rx_disassoc; /* rx disassociation */ + u_int32_t is_rx_badsubtype; /* rx frame w/ unknown subtype*/ + u_int32_t is_rx_nobuf; /* rx failed for lack of buf */ + u_int32_t is_rx_decryptcrc; /* rx decrypt failed on crc */ + u_int32_t is_rx_ahdemo_mgt; /* rx discard ahdemo mgt frame*/ + u_int32_t is_rx_bad_auth; /* rx bad auth request */ + u_int32_t is_rx_unauth; /* rx on unauthorized port */ + u_int32_t is_rx_badkeyid; /* rx w/ incorrect keyid */ + u_int32_t is_rx_ccmpreplay; /* rx seq# violation (CCMP) */ + u_int32_t is_rx_ccmpformat; /* rx format bad (CCMP) */ + u_int32_t is_rx_ccmpmic; /* rx MIC check failed (CCMP) */ + u_int32_t is_rx_tkipreplay; /* rx seq# violation (TKIP) */ + u_int32_t is_rx_tkipformat; /* rx format bad (TKIP) */ + u_int32_t is_rx_tkipmic; /* rx MIC check failed (TKIP) */ + u_int32_t is_rx_tkipicv; /* rx ICV check failed (TKIP) */ + u_int32_t is_rx_badcipher; /* rx failed 'cuz key type */ + u_int32_t is_rx_nocipherctx; /* rx failed 'cuz key !setup */ + u_int32_t is_rx_acl; /* rx discard 'cuz acl policy */ + u_int32_t is_tx_nobuf; /* tx failed for lack of buf */ + u_int32_t is_tx_nonode; /* tx failed for no node */ + u_int32_t is_tx_unknownmgt; /* tx of unknown mgt frame */ + u_int32_t is_tx_badcipher; /* tx failed 'cuz key type */ + u_int32_t is_tx_nodefkey; /* tx failed 'cuz no defkey */ + u_int32_t is_tx_noheadroom; /* tx failed 'cuz no space */ + u_int32_t is_scan_active; /* active scans started */ + u_int32_t is_scan_passive; /* passive scans started */ + u_int32_t is_node_timeout; /* nodes timed out inactivity */ + u_int32_t is_crypto_nomem; /* no memory for crypto ctx */ + u_int32_t is_crypto_tkip; /* tkip crypto done in s/w */ + u_int32_t is_crypto_tkipenmic; /* tkip en-MIC done in s/w */ + u_int32_t is_crypto_tkipdemic; /* tkip de-MIC done in s/w */ + u_int32_t is_crypto_tkipcm; /* tkip counter measures */ + u_int32_t is_crypto_ccmp; /* ccmp crypto done in s/w */ + u_int32_t is_crypto_wep; /* wep crypto done in s/w */ + u_int32_t is_crypto_setkey_cipher;/* cipher rejected key */ + u_int32_t is_crypto_setkey_nokey; /* no key index for setkey */ + u_int32_t is_crypto_delkey; /* driver key delete failed */ + u_int32_t is_crypto_badcipher; /* unknown cipher */ + u_int32_t is_crypto_nocipher; /* cipher not available */ + u_int32_t is_crypto_attachfail; /* cipher attach failed */ + u_int32_t is_crypto_swfallback; /* cipher fallback to s/w */ + u_int32_t is_crypto_keyfail; /* driver key alloc failed */ +}; + +/* + * Max size of optional information elements. We artificially + * constrain this; it's limited only by the max frame size (and + * the max parameter size of the wireless extensions). + */ +#define IEEE80211_MAX_OPT_IE 256 + +/* + * WPA/RSN get/set key request. Specify the key/cipher + * type and whether the key is to be used for sending and/or + * receiving. The key index should be set only when working + * with global keys (use IEEE80211_KEYIX_NONE for ``no index''). + * Otherwise a unicast/pairwise key is specified by the bssid + * (on a station) or mac address (on an ap). They key length + * must include any MIC key data; otherwise it should be no + more than IEEE80211_KEYBUF_SIZE. + */ +struct ieee80211req_key { + u_int8_t ik_type; /* key/cipher type */ + u_int8_t ik_pad; + u_int16_t ik_keyix; /* key index */ + u_int8_t ik_keylen; /* key length in bytes */ + u_int8_t ik_flags; +/* NB: IEEE80211_KEY_XMIT and IEEE80211_KEY_RECV defined elsewhere */ +#define IEEE80211_KEY_DEFAULT 0x80 /* default xmit key */ + u_int8_t ik_macaddr[IEEE80211_ADDR_LEN]; + u_int64_t ik_keyrsc; /* key receive sequence counter */ + u_int64_t ik_keytsc; /* key transmit sequence counter */ + u_int8_t ik_keydata[IEEE80211_KEYBUF_SIZE+IEEE80211_MICBUF_SIZE]; +}; + +/* + * Delete a key either by index or address. Set the index + * to IEEE80211_KEYIX_NONE when deleting a unicast key. + */ +struct ieee80211req_del_key { + u_int8_t idk_keyix; /* key index */ + u_int8_t idk_macaddr[IEEE80211_ADDR_LEN]; +}; + +/* + * MLME state manipulation request. IEEE80211_MLME_ASSOC + * only makes sense when operating as a station. The other + * requests can be used when operating as a station or an + * ap (to effect a station). + */ +struct ieee80211req_mlme { + u_int8_t im_op; /* operation to perform */ +#define IEEE80211_MLME_ASSOC 1 /* associate station */ +#define IEEE80211_MLME_DISASSOC 2 /* disassociate station */ +#define IEEE80211_MLME_DEAUTH 3 /* deauthenticate station */ +#define IEEE80211_MLME_AUTHORIZE 4 /* authorize station */ +#define IEEE80211_MLME_UNAUTHORIZE 5 /* unauthorize station */ + u_int16_t im_reason; /* 802.11 reason code */ + u_int8_t im_macaddr[IEEE80211_ADDR_LEN]; +}; + +/* + * MAC ACL operations. + */ +enum { + IEEE80211_MACCMD_POLICY_OPEN = 0, /* set policy: no ACL's */ + IEEE80211_MACCMD_POLICY_ALLOW = 1, /* set policy: allow traffic */ + IEEE80211_MACCMD_POLICY_DENY = 2, /* set policy: deny traffic */ + IEEE80211_MACCMD_FLUSH = 3, /* flush ACL database */ + IEEE80211_MACCMD_DETACH = 4, /* detach ACL policy */ +}; + +/* + * Set the active channel list. Note this list is + * intersected with the available channel list in + * calculating the set of channels actually used in + * scanning. + */ +struct ieee80211req_chanlist { + u_int8_t ic_channels[32]; /* 256 channels */ +}; + +/* + * Retrieve the WPA/RSN information element for an associated station. + */ +struct ieee80211req_wpaie { + u_int8_t wpa_macaddr[IEEE80211_ADDR_LEN]; + u_int8_t wpa_ie[IEEE80211_MAX_OPT_IE]; +}; + +#ifdef __FreeBSD__ +/* + * FreeBSD-style ioctls. + */ +/* the first member must be matched with struct ifreq */ +struct ieee80211req { + char i_name[IFNAMSIZ]; /* if_name, e.g. "wi0" */ + u_int16_t i_type; /* req type */ + int16_t i_val; /* Index or simple value */ + int16_t i_len; /* Index or simple value */ + void *i_data; /* Extra data */ +}; +#define SIOCS80211 _IOW('i', 234, struct ieee80211req) +#define SIOCG80211 _IOWR('i', 235, struct ieee80211req) + +#define IEEE80211_IOC_SSID 1 +#define IEEE80211_IOC_NUMSSIDS 2 +#define IEEE80211_IOC_WEP 3 +#define IEEE80211_WEP_NOSUP -1 +#define IEEE80211_WEP_OFF 0 +#define IEEE80211_WEP_ON 1 +#define IEEE80211_WEP_MIXED 2 +#define IEEE80211_IOC_WEPKEY 4 +#define IEEE80211_IOC_NUMWEPKEYS 5 +#define IEEE80211_IOC_WEPTXKEY 6 +#define IEEE80211_IOC_AUTHMODE 7 +#define IEEE80211_IOC_STATIONNAME 8 +#define IEEE80211_IOC_CHANNEL 9 +#define IEEE80211_IOC_POWERSAVE 10 +#define IEEE80211_POWERSAVE_NOSUP -1 +#define IEEE80211_POWERSAVE_OFF 0 +#define IEEE80211_POWERSAVE_CAM 1 +#define IEEE80211_POWERSAVE_PSP 2 +#define IEEE80211_POWERSAVE_PSP_CAM 3 +#define IEEE80211_POWERSAVE_ON IEEE80211_POWERSAVE_CAM +#define IEEE80211_IOC_POWERSAVESLEEP 11 +#define IEEE80211_IOC_RTSTHRESHOLD 12 +#define IEEE80211_IOC_PROTMODE 13 +#define IEEE80211_PROTMODE_OFF 0 +#define IEEE80211_PROTMODE_CTS 1 +#define IEEE80211_PROTMODE_RTSCTS 2 +#define IEEE80211_IOC_TXPOWER 14 +#define IEEE80211_IOC_BSSID 15 +#define IEEE80211_IOC_ROAMING 16 +#define IEEE80211_IOC_PRIVACY 17 +#define IEEE80211_IOC_DROP_UNENCRYPTED 18 +#define IEEE80211_IOC_WPAKEY 19 +#define IEEE80211_IOC_DELKEY 20 +#define IEEE80211_IOC_MLME 21 +#define IEEE80211_IOC_OPTIE 22 +#define IEEE80211_IOC_SCAN_REQ 23 +#define IEEE80211_IOC_SCAN_RESULTS 24 +#define IEEE80211_IOC_COUNTERMEASURES 25 +#define IEEE80211_IOC_WPA 26 +#define IEEE80211_IOC_CHANLIST 27 +#define IEEE80211_IOC_WME 28 +#define IEEE80211_IOC_HIDESSID 29 +#define IEEE80211_IOC_APBRIDGE 30 + +#ifndef IEEE80211_CHAN_ANY +#define IEEE80211_CHAN_ANY 0xffff /* token for ``any channel'' */ +#endif + +struct ieee80211req_scan_req { + u_int8_t isq_ssid_len; /* SSID length */ + u_int8_t isq_ssid[IEEE80211_NWID_LEN]; +}; + +struct ieee80211req_scan_result { + u_int16_t isr_len; /* length (mult of 4) */ + u_int16_t isr_freq; /* MHz */ + u_int16_t isr_flags; /* channel flags */ + u_int8_t isr_noise; + u_int8_t isr_rssi; + u_int8_t isr_intval; /* beacon interval */ + u_int8_t isr_capinfo; /* capabilities */ + u_int8_t isr_erp; /* ERP element */ + u_int8_t isr_bssid[IEEE80211_ADDR_LEN]; + u_int8_t isr_nrates; + u_int8_t isr_rates[15]; /* XXX */ + u_int8_t isr_ssid_len; /* SSID length */ + u_int8_t isr_ie_len; /* IE length */ + u_int8_t isr_scangen; /* scan generation # */ + /* variable length SSID followed by IE data */ +}; + +#define SIOCG80211STATS _IOWR('i', 236, struct ifreq) +#endif /* __FreeBSD__ */ + +#ifdef __linux__ +/* + * Wireless Extensions API, private ioctl interfaces. + * + * NB: Even-numbered ioctl numbers have set semantics and are privileged! + * (regardless of the incorrect comment in wireless.h!) + */ +#define IEEE80211_IOCTL_SETPARAM (SIOCIWFIRSTPRIV+0) +#define IEEE80211_IOCTL_GETPARAM (SIOCIWFIRSTPRIV+1) +#define IEEE80211_IOCTL_SETKEY (SIOCIWFIRSTPRIV+2) +#define IEEE80211_IOCTL_DELKEY (SIOCIWFIRSTPRIV+4) +#define IEEE80211_IOCTL_SETMLME (SIOCIWFIRSTPRIV+6) +#define IEEE80211_IOCTL_SETOPTIE (SIOCIWFIRSTPRIV+8) +#define IEEE80211_IOCTL_GETOPTIE (SIOCIWFIRSTPRIV+9) +#define IEEE80211_IOCTL_ADDMAC (SIOCIWFIRSTPRIV+10) +#define IEEE80211_IOCTL_DELMAC (SIOCIWFIRSTPRIV+12) +#define IEEE80211_IOCTL_CHANLIST (SIOCIWFIRSTPRIV+14) + +enum { + IEEE80211_PARAM_TURBO = 1, /* turbo mode */ + IEEE80211_PARAM_MODE = 2, /* phy mode (11a, 11b, etc.) */ + IEEE80211_PARAM_AUTHMODE = 3, /* authentication mode */ + IEEE80211_PARAM_PROTMODE = 4, /* 802.11g protection */ + IEEE80211_PARAM_MCASTCIPHER = 5, /* multicast/default cipher */ + IEEE80211_PARAM_MCASTKEYLEN = 6, /* multicast key length */ + IEEE80211_PARAM_UCASTCIPHERS = 7, /* unicast cipher suites */ + IEEE80211_PARAM_UCASTCIPHER = 8, /* unicast cipher */ + IEEE80211_PARAM_UCASTKEYLEN = 9, /* unicast key length */ + IEEE80211_PARAM_WPA = 10, /* WPA mode (0,1,2) */ + IEEE80211_PARAM_ROAMING = 12, /* roaming mode */ + IEEE80211_PARAM_PRIVACY = 13, /* privacy invoked */ + IEEE80211_PARAM_COUNTERMEASURES = 14, /* WPA/TKIP countermeasures */ + IEEE80211_PARAM_DROPUNENCRYPTED = 15, /* discard unencrypted frames */ + IEEE80211_PARAM_DRIVER_CAPS = 16, /* driver capabilities */ + IEEE80211_PARAM_MACCMD = 17, /* MAC ACL operation */ + IEEE80211_PARAM_WME = 18, /* WME mode (on, off) */ + IEEE80211_PARAM_HIDESSID = 19, /* hide SSID mode (on, off) */ + IEEE80211_PARAM_APBRIDGE = 20, /* AP inter-sta bridging */ + IEEE80211_PARAM_KEYMGTALGS = 21, /* key management algorithms */ + IEEE80211_PARAM_RSNCAPS = 22, /* RSN capabilities */ + IEEE80211_PARAM_INACT = 23, /* station inactivity timeout */ + IEEE80211_PARAM_INACT_AUTH = 24, /* station auth inact timeout */ + IEEE80211_PARAM_INACT_INIT = 25, /* station init inact timeout */ +}; + +#define SIOCG80211STATS (SIOCDEVPRIVATE+2) +/* NB: require in+out parameters so cannot use wireless extensions, yech */ +#define IEEE80211_IOCTL_GETKEY (SIOCDEVPRIVATE+3) +#define IEEE80211_IOCTL_GETWPAIE (SIOCDEVPRIVATE+4) + +#endif /* __linux__ */ + +#endif /* _NET80211_IEEE80211_IOCTL_H_ */ diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_linux.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_linux.c --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_linux.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_linux.c 2005-02-24 13:06:17.308130784 -0800 @@ -0,0 +1,500 @@ +/*- + * Copyright (c) 2003-2005 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EXPORT_SYMTAB +#define EXPORT_SYMTAB +#endif + +/* + * IEEE 802.11 support (Linux-specific code) + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include /* XXX for ARPHRD_* */ + +#include + +#include "if_media.h" +#include "if_ethersubr.h" + +#include + +/* + * Print a console message with the device name prepended. + */ +void +if_printf(struct net_device *dev, const char *fmt, ...) +{ + va_list ap; + char buf[512]; /* XXX */ + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + + printk("%s: %s", dev->name, buf); +} + +/* + * Allocate and setup a management frame of the specified + * size. We return the sk_buff and a pointer to the start + * of the contiguous data area that's been reserved based + * on the packet length. The data area is forced to 32-bit + * alignment and the buffer length to a multiple of 4 bytes. + * This is done mainly so beacon frames (that require this) + * can use this interface too. + */ +struct sk_buff * +ieee80211_getmgtframe(u_int8_t **frm, u_int pktlen) +{ + const u_int align = sizeof(u_int32_t); + struct ieee80211_cb *cb; + struct sk_buff *skb; + u_int len; + + len = roundup(sizeof(struct ieee80211_frame) + pktlen, 4); + skb = dev_alloc_skb(len + align-1); + if (skb != NULL) { + u_int off = ((unsigned long) skb->data) % align; + if (off != 0) + skb_reserve(skb, align - off); + + cb = (struct ieee80211_cb *)skb->cb; + cb->ni = NULL; + cb->flags = 0; + + skb_reserve(skb, sizeof(struct ieee80211_frame)); + *frm = skb_put(skb, pktlen); + } + return skb; +} + +/* + * Drain a queue of sk_buff's. + */ +void +skb_queue_drain(struct sk_buff_head *q) +{ + struct sk_buff *skb; + unsigned long flags; + + spin_lock_irqsave(&q->lock, flags); + while ((skb = __skb_dequeue(q)) != NULL) + dev_kfree_skb(skb); + spin_unlock_irqrestore(&q->lock, flags); +} + +#if IEEE80211_VLAN_TAG_USED +/* + * VLAN support. + */ + +/* + * Register a vlan group. + */ +void +ieee80211_vlan_register(struct ieee80211com *ic, struct vlan_group *grp) +{ + ic->ic_vlgrp = grp; +} +EXPORT_SYMBOL(ieee80211_vlan_register); + +/* + * Kill (i.e. delete) a vlan identifier. + */ +void +ieee80211_vlan_kill_vid(struct ieee80211com *ic, unsigned short vid) +{ + if (ic->ic_vlgrp) + ic->ic_vlgrp->vlan_devices[vid] = NULL; +} +EXPORT_SYMBOL(ieee80211_vlan_kill_vid); +#endif /* IEEE80211_VLAN_TAG_USED */ + +void +ieee80211_notify_node_join(struct ieee80211com *ic, struct ieee80211_node *ni, int newassoc) +{ + union iwreq_data wreq; + + if (ni == ic->ic_bss) { + if (newassoc) + netif_carrier_on(ic->ic_dev); + memset(&wreq, 0, sizeof(wreq)); + IEEE80211_ADDR_COPY(wreq.addr.sa_data, ni->ni_bssid); + wreq.addr.sa_family = ARPHRD_ETHER; + wireless_send_event(ic->ic_dev, SIOCGIWAP, &wreq, NULL); + } else if (newassoc) { + /* fire off wireless event only for new station */ + memset(&wreq, 0, sizeof(wreq)); + IEEE80211_ADDR_COPY(wreq.addr.sa_data, ni->ni_macaddr); + wreq.addr.sa_family = ARPHRD_ETHER; + wireless_send_event(ic->ic_dev, IWEVREGISTERED, &wreq, NULL); + } +} + +void +ieee80211_notify_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + union iwreq_data wreq; + + if (ni == ic->ic_bss) { + netif_carrier_off(ic->ic_dev); + memset(wreq.ap_addr.sa_data, 0, ETHER_ADDR_LEN); + wreq.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(ic->ic_dev, SIOCGIWAP, &wreq, NULL); + } else { + /* fire off wireless event station leaving */ + memset(&wreq, 0, sizeof(wreq)); + IEEE80211_ADDR_COPY(wreq.addr.sa_data, ni->ni_macaddr); + wreq.addr.sa_family = ARPHRD_ETHER; + wireless_send_event(ic->ic_dev, IWEVEXPIRED, &wreq, NULL); + } +} + +void +ieee80211_notify_scan_done(struct ieee80211com *ic) +{ + union iwreq_data wreq; + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + ("%s: notify scan done\n", ic->ic_dev->name)); + + /* dispatch wireless event indicating scan completed */ + wreq.data.length = 0; + wreq.data.flags = 0; + wireless_send_event(ic->ic_dev, SIOCGIWSCAN, &wreq, NULL); +} + +void +ieee80211_notify_replay_failure(struct ieee80211com *ic, + const struct ieee80211_frame *wh, const struct ieee80211_key *k, + u_int64_t rsc) +{ + static const char * tag = "MLME-REPLAYFAILURE.indication"; + union iwreq_data wrqu; + char buf[128]; + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + ("[%s] %s replay detected \n", + ether_sprintf(wh->i_addr2), k->wk_cipher->ic_name, + rsc, k->wk_keyrsc)); + + if (ic->ic_dev == NULL) /* NB: for cipher test modules */ + return; + + /* TODO: needed parameters: count, keyid, key type, src address, TSC */ + snprintf(buf, sizeof(buf), "%s(keyid=%d %scast addr=%s)", tag, + k->wk_keyix, IEEE80211_IS_MULTICAST(wh->i_addr1) ? "broad" : "uni", + ether_sprintf(wh->i_addr1)); + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = strlen(buf); + wireless_send_event(ic->ic_dev, IWEVCUSTOM, &wrqu, buf); +} +EXPORT_SYMBOL(ieee80211_notify_replay_failure); + +void +ieee80211_notify_michael_failure(struct ieee80211com *ic, + const struct ieee80211_frame *wh, u_int keyix) +{ + static const char * tag = "MLME-MICHAELMICFAILURE.indication"; + union iwreq_data wrqu; + char buf[128]; + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + ("[%s] Michael MIC verification failed \n", + ether_sprintf(wh->i_addr2), keyix)); + ic->ic_stats.is_rx_tkipmic++; + + if (ic->ic_dev == NULL) /* NB: for cipher test modules */ + return; + + /* TODO: needed parameters: count, keyid, key type, src address, TSC */ + snprintf(buf, sizeof(buf), "%s(keyid=%d %scast addr=%s)", tag, + keyix, IEEE80211_IS_MULTICAST(wh->i_addr1) ? "broad" : "uni", + ether_sprintf(wh->i_addr1)); + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = strlen(buf); + wireless_send_event(ic->ic_dev, IWEVCUSTOM, &wrqu, buf); +} +EXPORT_SYMBOL(ieee80211_notify_michael_failure); + +#ifdef CONFIG_SYSCTL +#include + +static int +proc_read_node(char *page, int space, struct ieee80211com *ic, void *arg) +{ + TAILQ_HEAD(, ieee80211_node) *head = arg; + char *p = page; + struct ieee80211_node *ni; + struct ieee80211_rateset *rs; + int i; + + IEEE80211_NODE_LOCK_BH(ic); + TAILQ_FOREACH(ni, head, ni_list) { + /* Assume each node needs 300 bytes */ + if (p - page > space - 300) + break; + + p += sprintf(p, "\nmacaddr: <%s>\n", ether_sprintf(ni->ni_macaddr)); + p += sprintf(p, " rssi: %d dBm\n", + (*ic->ic_node_getrssi)(ic, ni)); + + p += sprintf(p, " capinfo:"); + if (ni->ni_capinfo & IEEE80211_CAPINFO_ESS) + p += sprintf(p, " ess"); + if (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) + p += sprintf(p, " ibss"); + if (ni->ni_capinfo & IEEE80211_CAPINFO_CF_POLLABLE) + p += sprintf(p, " cfpollable"); + if (ni->ni_capinfo & IEEE80211_CAPINFO_CF_POLLREQ) + p += sprintf(p, " cfpollreq"); + if (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) + p += sprintf(p, " privacy"); + if (ni->ni_capinfo & IEEE80211_CAPINFO_CHNL_AGILITY) + p += sprintf(p, " chanagility"); + if (ni->ni_capinfo & IEEE80211_CAPINFO_PBCC) + p += sprintf(p, " pbcc"); + if (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE) + p += sprintf(p, " shortpreamble"); + if (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) + p += sprintf(p, " shortslot"); + if (ni->ni_capinfo & IEEE80211_CAPINFO_DSSSOFDM) + p += sprintf(p, " dsssofdm"); + p += sprintf(p, "\n"); + + p += sprintf(p, " freq: %d MHz (channel %d)\n", + ni->ni_chan->ic_freq, + ieee80211_mhz2ieee(ni->ni_chan->ic_freq, 0)); + + p += sprintf(p, " opmode:"); + if (IEEE80211_IS_CHAN_A(ni->ni_chan)) + p += sprintf(p, " a"); + if (IEEE80211_IS_CHAN_B(ni->ni_chan)) + p += sprintf(p, " b"); + if (IEEE80211_IS_CHAN_PUREG(ni->ni_chan)) + p += sprintf(p, " pureg"); + if (IEEE80211_IS_CHAN_G(ni->ni_chan)) + p += sprintf(p, " g"); + p += sprintf(p, "\n"); + + rs = &ni->ni_rates; + if (ni->ni_txrate >= 0 && ni->ni_txrate < rs->rs_nrates) { + p += sprintf(p, " txrate: "); + for (i = 0; i < rs->rs_nrates; i++) { + p += sprintf(p, "%s%d%sMbps", + (i != 0 ? " " : ""), + (rs->rs_rates[i] & IEEE80211_RATE_VAL) / 2, + ((rs->rs_rates[i] & 0x1) != 0 ? ".5" : "")); + if (i == ni->ni_txrate) + p += sprintf(p, "*"); /* current rate */ + } + p += sprintf(p, "\n"); + } else + p += sprintf(p, " txrate: %d ? (rs_nrates: %d)\n", + ni->ni_txrate, ni->ni_rates.rs_nrates); + + p += sprintf(p, " txseq: %d rxseq: %d\n", + ni->ni_txseq, ni->ni_rxseq); + p += sprintf(p, " fails: %d inact: %d\n", + ni->ni_fails, ni->ni_inact); + + } + IEEE80211_NODE_UNLOCK_BH(ic); + return (p - page); +} + +static int +IEEE80211_SYSCTL_DECL(ieee80211_sysctl_stations, ctl, write, filp, buffer, + lenp, ppos) +{ + struct ieee80211com *ic = ctl->extra1; + int len = *lenp; + + if (ic->ic_opmode != IEEE80211_M_HOSTAP && + ic->ic_opmode != IEEE80211_M_IBSS) + return -EINVAL; + if (len && filp->f_pos == 0) { + *lenp = proc_read_node(buffer, len, ic, &ic->ic_node); + filp->f_pos += *lenp; + } else { + *lenp = 0; + } + return 0; +} + + +static int +IEEE80211_SYSCTL_DECL(ieee80211_sysctl_debug, ctl, write, filp, buffer, + lenp, ppos) +{ + struct ieee80211com *ic = ctl->extra1; + u_int val; + int ret; + + ctl->data = &val; + ctl->maxlen = sizeof(val); + if (write) { + ret = IEEE80211_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer, + lenp, ppos); + if (ret == 0) + ic->msg_enable = val; + } else { + val = ic->msg_enable; + ret = IEEE80211_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer, + lenp, ppos); + } + return ret; +} + +#define CTL_AUTO -2 /* cannot be CTL_ANY or CTL_NONE */ + +static const ctl_table ieee80211_sysctl_template[] = { + { .ctl_name = CTL_AUTO, + .procname = "debug", + .mode = 0644, + .proc_handler = ieee80211_sysctl_debug + }, + { .ctl_name = CTL_AUTO, + .procname = "associated_sta", + .mode = 0444, + .proc_handler = ieee80211_sysctl_stations + }, + { 0 } +}; + +void +ieee80211_sysctl_register(struct ieee80211com *ic) +{ + const char *cp; + int i, space; + + space = 5*sizeof(struct ctl_table) + sizeof(ieee80211_sysctl_template); + ic->ic_sysctls = kmalloc(space, GFP_KERNEL); + if (ic->ic_sysctls == NULL) { + printk("%s: no memory for sysctl table!\n", __func__); + return; + } + + /* setup the table */ + memset(ic->ic_sysctls, 0, space); + ic->ic_sysctls[0].ctl_name = CTL_NET; + ic->ic_sysctls[0].procname = "net"; + ic->ic_sysctls[0].mode = 0555; + ic->ic_sysctls[0].child = &ic->ic_sysctls[2]; + /* [1] is NULL terminator */ + ic->ic_sysctls[2].ctl_name = CTL_AUTO; + for (cp = ic->ic_dev->name; *cp && !isdigit(*cp); cp++) + ; + snprintf(ic->ic_procname, sizeof(ic->ic_procname), "wlan%s", cp); + ic->ic_sysctls[2].procname = ic->ic_procname; + ic->ic_sysctls[2].mode = 0555; + ic->ic_sysctls[2].child = &ic->ic_sysctls[4]; + /* [3] is NULL terminator */ + /* copy in pre-defined data */ + memcpy(&ic->ic_sysctls[4], ieee80211_sysctl_template, + sizeof(ieee80211_sysctl_template)); + + /* add in dynamic data references */ + for (i = 4; ic->ic_sysctls[i].ctl_name; i++) + if (ic->ic_sysctls[i].extra1 == NULL) + ic->ic_sysctls[i].extra1 = ic; + + /* and register everything */ + ic->ic_sysctl_header = register_sysctl_table(ic->ic_sysctls, 1); + if (!ic->ic_sysctl_header) { + printk("%s: failed to register sysctls!\n", ic->ic_procname); + kfree(ic->ic_sysctls); + ic->ic_sysctls = NULL; + } +} +EXPORT_SYMBOL(ieee80211_sysctl_register); + +void +ieee80211_sysctl_unregister(struct ieee80211com *ic) +{ + if (ic->ic_sysctl_header) { + unregister_sysctl_table(ic->ic_sysctl_header); + ic->ic_sysctl_header = NULL; + } + if (ic->ic_sysctls) { + kfree(ic->ic_sysctls); + ic->ic_sysctls = NULL; + } +} +EXPORT_SYMBOL(ieee80211_sysctl_unregister); +#endif /* CONFIG_SYSCTL */ + +/* + * Format an Ethernet MAC for printing. + */ +const char* +ether_sprintf(const u_int8_t *mac) +{ + static char etherbuf[18]; + snprintf(etherbuf, sizeof(etherbuf), "%02x:%02x:%02x:%02x:%02x:%02x", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + return etherbuf; +} +EXPORT_SYMBOL(ether_sprintf); /* XXX */ + +/* + * Module glue. + */ +#include "version.h" +static char *version = WLAN_VERSION " (EXPERIMENTAL)"; +static char *dev_info = "wlan"; + +MODULE_AUTHOR("Errno Consulting, Sam Leffler"); +MODULE_DESCRIPTION("802.11 wireless LAN protocol support"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("Dual BSD/GPL"); +#endif + +static int __init +init_wlan(void) +{ + printk(KERN_INFO "%s: %s\n", dev_info, version); + return 0; +} +module_init(init_wlan); + +static void __exit +exit_wlan(void) +{ + printk(KERN_INFO "%s: driver unloaded\n", dev_info); +} +module_exit(exit_wlan); diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_linux.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_linux.h --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_linux.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_linux.h 2005-02-24 13:06:17.309130632 -0800 @@ -0,0 +1,347 @@ +/*- + * Copyright (c) 2003-2005 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: ieee80211_linux.h,v 1.6 2005/02/16 16:08:47 samleffler Exp $ + */ +#ifndef _NET80211_IEEE80211_LINUX_H_ +#define _NET80211_IEEE80211_LINUX_H_ + +/* + * Node locking definitions. + */ +typedef rwlock_t ieee80211_node_lock_t; +#define IEEE80211_NODE_LOCK_INIT(_ic, _name) rwlock_init(&(_ic)->ic_nodelock) +#define IEEE80211_NODE_LOCK_DESTROY(_ic) +#define IEEE80211_NODE_LOCK(_ic) write_lock(&(_ic)->ic_nodelock) +#define IEEE80211_NODE_UNLOCK(_ic) write_unlock(&(_ic)->ic_nodelock) +#define IEEE80211_NODE_LOCK_BH(_ic) write_lock_bh(&(_ic)->ic_nodelock) +#define IEEE80211_NODE_UNLOCK_BH(_ic) write_unlock_bh(&(_ic)->ic_nodelock) +/* NB: beware, *_is_locked() are boguly defined for UP+!PREEMPT */ +#if (defined(CONFIG_SMP) || defined(CONFIG_PREEMPT)) && defined(rwlock_is_locked) +#define IEEE80211_NODE_LOCK_ASSERT(_ic) \ + KASSERT(rwlock_is_locked(&(_ic)->ic_nodelock), \ + ("802.11 node not locked!")) +#else +#define IEEE80211_NODE_LOCK_ASSERT(_ic) +#endif + +/* + * 802.1x state locking definitions. + */ +typedef spinlock_t eapol_lock_t; +#define EAPOL_LOCK_INIT(_ec, _name) spin_lock_init(&(_ec)->ec_lock) +#define EAPOL_LOCK_DESTROY(_ec) +#define EAPOL_LOCK(_ec) spin_lock_bh(&(_ec)->ec_lock) +#define EAPOL_UNLOCK(_ec) spin_unlock_bh(&(_ec)->ec_lock) +/* NB: beware, *_is_locked() are boguly defined for UP+!PREEMPT */ +#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT) +#define EAPOL_LOCK_ASSERT(_ec) \ + KASSERT(spin_is_locked(&(_ec)->ec_lock), ("EAPOL not locked!")) +#else +#define EAPOL_LOCK_ASSERT(_ec) +#endif + +/* + * 802.1x MAC ACL database locking definitions. + */ +typedef spinlock_t acl_lock_t; +#define ACL_LOCK_INIT(_as, _name) spin_lock_init(&(_as)->as_lock) +#define ACL_LOCK_DESTROY(_as) +#define ACL_LOCK(_as) spin_lock_bh(&(_as)->as_lock) +#define ACL_UNLOCK(_as) spin_unlock_bh(&(_as)->as_lock) +/* NB: beware, *_is_locked() are boguly defined for UP+!PREEMPT */ +#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT) +#define ACL_LOCK_ASSERT(_as) \ + KASSERT(spin_is_locked(&(_as)->as_lock), ("ACL not locked!")) +#else +#define ACL_LOCK_ASSERT(_as) +#endif + +#define M_LINK0 0x01 /* frame needs WEP encryption */ + +/* + * Node reference counting definitions. + * + * ieee80211_node_initref initialize the reference count to 1 + * ieee80211_node_incref add a reference + * ieee80211_node_decref remove a reference + * ieee80211_node_dectestref remove a reference and return 1 if this + * is the last reference, otherwise 0 + * ieee80211_node_refcnt reference count for printing (only) + */ +#define ieee80211_node_initref(_ni) \ + atomic_set(&(_ni)->ni_refcnt, 1) +#define ieee80211_node_incref(_ni) \ + atomic_inc(&(_ni)->ni_refcnt) +#define ieee80211_node_decref(_ni) \ + atomic_dec(&(_ni)->ni_refcnt) +#define ieee80211_node_dectestref(_ni) \ + atomic_dec_and_test(&(_ni)->ni_refcnt) +#define ieee80211_node_refcnt(_ni) (_ni)->ni_refcnt.counter + +#define le16toh(_x) le16_to_cpu(_x) +#define htole16(_x) cpu_to_le16(_x) +#define le32toh(_x) le32_to_cpu(_x) +#define htole32(_x) cpu_to_le32(_x) +#define be32toh(_x) be32_to_cpu(_x) +#define htobe32(_x) cpu_to_be32(_x) + +/* + * Linux has no equivalents to malloc types so null these out. + */ +#define MALLOC_DEFINE(type, shortdesc, longdesc) +#define MALLOC_DECLARE(type) + +/* + * flags to malloc. + */ +#define M_NOWAIT 0x0001 /* do not block */ +#define M_WAITOK 0x0002 /* ok to block */ +#define M_ZERO 0x0100 /* bzero the allocation */ + +static inline void * +ieee80211_malloc(size_t size, int flags) +{ + void *p = kmalloc(size, flags & M_NOWAIT ? GFP_ATOMIC : GFP_KERNEL); + if (p && (flags & M_ZERO)) + memset(p, 0, size); + return p; +} +#define MALLOC(_ptr, cast, _size, _type, _flags) \ + ((_ptr) = (cast)ieee80211_malloc(_size, _flags)) +#define FREE(addr, type) kfree((addr)) + +/* + * This unlikely to be popular but it dramatically reduces diffs. + */ +#define printf printk +struct ieee80211com; +extern void if_printf(struct net_device *, const char *, ...); +extern const char *ether_sprintf(const u_int8_t *); + +/* + * Queue write-arounds and support routines. + */ +extern struct sk_buff *ieee80211_getmgtframe(u_int8_t **frm, u_int pktlen); +#define IF_ENQUEUE(_q,_skb) skb_queue_tail(_q,_skb) +#define IF_DEQUEUE(_q,_skb) (_skb = skb_dequeue(_q)) +#define _IF_QLEN(_q) skb_queue_len(_q) +#define IF_DRAIN(_q) skb_queue_drain(_q) +extern void skb_queue_drain(struct sk_buff_head *q); + +extern struct net_device_stats *ieee80211_getstats(struct net_device *); + +#ifndef __MOD_INC_USE_COUNT +#define _MOD_INC_USE(_m, _err) \ + if (!try_module_get(_m)) { \ + printk(KERN_WARNING "%s: try_module_get failed\n", \ + __func__); \ + _err; \ + } +#define _MOD_DEC_USE(_m) module_put(_m) +#else +#define _MOD_INC_USE(_m, _err) MOD_INC_USE_COUNT +#define _MOD_DEC_USE(_m) MOD_DEC_USE_COUNT +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) +static inline u_int64_t +get_jiffies_64(void) +{ + return (u_int64_t) jiffies; /* XXX not right */ +} +#endif + +#ifndef CLONE_KERNEL +/* + * List of flags we want to share for kernel threads, + * if only because they are not used by them anyway. + */ +#define CLONE_KERNEL (CLONE_FS | CLONE_FILES | CLONE_SIGHAND) +#endif + +#ifndef offset_in_page +#define offset_in_page(p) ((unsigned long) (p) & ~PAGE_MASK) +#endif + +#ifndef module_put_and_exit +#define module_put_and_exit(code) do { \ + _MOD_DEC_USE(THIS_MODULE); \ + do_exit(code); \ +} while (0) +#endif + +/* + * Linux uses __BIG_ENDIAN and __LITTLE_ENDIAN while BSD uses _foo + * and an explicit _BYTE_ORDER. Sorry, BSD got there first--define + * things in the BSD way... + */ +#define _LITTLE_ENDIAN 1234 /* LSB first: i386, vax */ +#define _BIG_ENDIAN 4321 /* MSB first: 68000, ibm, net */ +#include +#if defined(__LITTLE_ENDIAN) +#define _BYTE_ORDER _LITTLE_ENDIAN +#elif defined(__BIG_ENDIAN) +#define _BYTE_ORDER _BIG_ENDIAN +#else +#error "Please fix asm/byteorder.h" +#endif + +#ifdef CONFIG_SYSCTL +/* + * Deal with the sysctl handler api changing. + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8) +#define IEEE80211_SYSCTL_DECL(f, ctl, write, filp, buffer, lenp, ppos) \ + f(ctl_table *ctl, int write, struct file *filp, void *buffer, \ + size_t *lenp) +#define IEEE80211_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer, lenp, ppos) \ + proc_dointvec(ctl, write, filp, buffer, lenp) +#else +#define IEEE80211_SYSCTL_DECL(f, ctl, write, filp, buffer, lenp, ppos) \ + f(ctl_table *ctl, int write, struct file *filp, void *buffer,\ + size_t *lenp, loff_t *ppos) +#define IEEE80211_SYSCTL_PROC_DOINTVEC(ctl, write, filp, buffer, lenp, ppos) \ + proc_dointvec(ctl, write, filp, buffer, lenp, ppos) +#endif + +extern void ieee80211_sysctl_register(struct ieee80211com *); +extern void ieee80211_sysctl_unregister(struct ieee80211com *); +#endif /* CONFIG_SYSCTL */ + +#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) +#define IEEE80211_VLAN_TAG_USED 1 + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,20) +#define vlan_hwaccel_receive_skb(skb, grp, tag) vlan_hwaccel_rx(skb, grp, tag) +#endif + +extern void ieee80211_vlan_register(struct ieee80211com *, struct vlan_group*); +extern void ieee80211_vlan_kill_vid(struct ieee80211com *, unsigned short); +#else +#define IEEE80211_VLAN_TAG_USED 0 +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) +#define free_netdev(dev) kfree(dev) +#endif + +#ifdef CONFIG_NET_WIRELESS +struct iw_statistics; +extern void ieee80211_iw_getstats(struct ieee80211com*, struct iw_statistics*); +struct iw_request_info; +struct iw_point; +extern int ieee80211_ioctl_giwname(struct ieee80211com *, + struct iw_request_info *, char *name, char *extra); +extern int ieee80211_ioctl_siwencode(struct ieee80211com *, + struct iw_request_info *, struct iw_point *, char *); +extern int ieee80211_ioctl_giwencode(struct ieee80211com *, + struct iw_request_info *, struct iw_point *, char *key); +struct iw_param; +extern int ieee80211_ioctl_siwrate(struct ieee80211com *, + struct iw_request_info *, struct iw_param *, char *); +extern int ieee80211_ioctl_giwrate(struct ieee80211com *, + struct iw_request_info *, struct iw_param *, char *); +extern int ieee80211_ioctl_siwsens(struct ieee80211com *, + struct iw_request_info *, struct iw_param *, char *); +extern int ieee80211_ioctl_giwsens(struct ieee80211com *, + struct iw_request_info *, struct iw_param *, char *); +extern int ieee80211_ioctl_siwrts(struct ieee80211com *, + struct iw_request_info *, struct iw_param *, char *); +extern int ieee80211_ioctl_giwrts(struct ieee80211com *, + struct iw_request_info *, struct iw_param *, char *); +extern int ieee80211_ioctl_siwfrag(struct ieee80211com *, + struct iw_request_info *, struct iw_param *, char *); +extern int ieee80211_ioctl_giwfrag(struct ieee80211com *, + struct iw_request_info *, struct iw_param *, char *); +struct sockaddr; +extern int ieee80211_ioctl_siwap(struct ieee80211com *, + struct iw_request_info *, struct sockaddr *, char *); +extern int ieee80211_ioctl_giwap(struct ieee80211com *, + struct iw_request_info *, struct sockaddr *, char *); +extern int ieee80211_ioctl_siwnickn(struct ieee80211com *, + struct iw_request_info *, struct iw_point *, char *); +extern int ieee80211_ioctl_giwnickn(struct ieee80211com *, + struct iw_request_info *, struct iw_point *, char *); +struct iw_freq; +extern int ieee80211_ioctl_siwfreq(struct ieee80211com *, + struct iw_request_info *, struct iw_freq *, char *); +extern int ieee80211_ioctl_giwfreq(struct ieee80211com *, + struct iw_request_info *, struct iw_freq *, char *); +extern int ieee80211_ioctl_siwessid(struct ieee80211com *, + struct iw_request_info *, struct iw_point *, char *); +extern int ieee80211_ioctl_giwessid(struct ieee80211com *, + struct iw_request_info *, struct iw_point *, char *); +extern int ieee80211_ioctl_giwrange(struct ieee80211com *, + struct iw_request_info *, struct iw_point *, char *); +extern int ieee80211_ioctl_siwmode(struct ieee80211com *, + struct iw_request_info *, __u32 *, char *); +extern int ieee80211_ioctl_giwmode(struct ieee80211com *, + struct iw_request_info *, __u32 *, char *); +extern int ieee80211_ioctl_siwpower(struct ieee80211com *, + struct iw_request_info *, struct iw_param *, char *); +extern int ieee80211_ioctl_giwpower(struct ieee80211com *, + struct iw_request_info *, struct iw_param *, char *); +extern int ieee80211_ioctl_siwretry(struct ieee80211com *, + struct iw_request_info *, struct iw_param *, char *); +extern int ieee80211_ioctl_giwretry(struct ieee80211com *, + struct iw_request_info *, struct iw_param *, char *); +extern int ieee80211_ioctl_siwtxpow(struct ieee80211com *, + struct iw_request_info *, struct iw_param *, char *); +extern int ieee80211_ioctl_giwtxpow(struct ieee80211com *, + struct iw_request_info *, struct iw_param *, char *); +extern int ieee80211_ioctl_iwaplist(struct ieee80211com *, + struct iw_request_info *, struct iw_point *, char *); +extern int ieee80211_ioctl_siwscan(struct ieee80211com *, + struct iw_request_info *, struct iw_point *, char *); +extern int ieee80211_ioctl_giwscan(struct ieee80211com *, + struct iw_request_info *, struct iw_point *, char *); + +extern int ieee80211_ioctl_setparam(struct ieee80211com *, + struct iw_request_info *, void *, char *); +extern int ieee80211_ioctl_getparam(struct ieee80211com *, + struct iw_request_info *, void *, char *); +extern int ieee80211_ioctl_setoptie(struct ieee80211com *, + struct iw_request_info *, void *, char *); +extern int ieee80211_ioctl_getoptie(struct ieee80211com *, + struct iw_request_info *, void *, char *); +extern int ieee80211_ioctl_setkey(struct ieee80211com *, + struct iw_request_info *, void *, char *); +extern int ieee80211_ioctl_delkey(struct ieee80211com *, + struct iw_request_info *, void *, char *); +extern int ieee80211_ioctl_setmlme(struct ieee80211com *, + struct iw_request_info *, void *, char *); +extern int ieee80211_ioctl_addmac(struct ieee80211com *, + struct iw_request_info *, void *, char *); +extern int ieee80211_ioctl_delmac(struct ieee80211com *, + struct iw_request_info *, void *, char *); +extern int ieee80211_ioctl_chanlist(struct ieee80211com *, + struct iw_request_info *, void *, char *); + +extern void ieee80211_ioctl_iwsetup(struct iw_handler_def *); +#endif + +#endif /* _NET80211_IEEE80211_LINUX_H_ */ diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_node.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_node.c --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_node.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_node.c 2005-02-24 13:06:17.317129416 -0800 @@ -0,0 +1,1273 @@ +/*- + * Copyright (c) 2001 Atsushi Onoe + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EXPORT_SYMTAB +#define EXPORT_SYMTAB +#endif + +__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_node.c,v 1.13 2003/11/09 23:36:46 sam Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ieee80211_node.c,v 1.8 2003/11/02 01:29:05 dyoung Exp $"); + +/* + * IEEE 802.11 node handling support. + */ +#include +#include +#include +#include +#include +#include +#include + +#include "if_media.h" + +#include + +static struct ieee80211_node *ieee80211_node_alloc(struct ieee80211com *); +static void node_cleanup(struct ieee80211com *, struct ieee80211_node *); +static void node_free(struct ieee80211com *, struct ieee80211_node *); +static void ieee80211_node_copy(struct ieee80211com *, + struct ieee80211_node *, const struct ieee80211_node *); +static u_int8_t ieee80211_node_getrssi(struct ieee80211com *, + struct ieee80211_node *); + +static void ieee80211_setup_node(struct ieee80211com *ic, + struct ieee80211_node *ni, u_int8_t *macaddr); +static void _ieee80211_free_node(struct ieee80211com *, + struct ieee80211_node *); + +MALLOC_DEFINE(M_80211_NODE, "node", "802.11 node state"); + +void +ieee80211_node_attach(struct ieee80211com *ic) +{ + + /* XXX need unit */ + IEEE80211_NODE_LOCK_INIT(ic, ic->ic_ifp->if_xname); + TAILQ_INIT(&ic->ic_node); + ic->ic_node_alloc = ieee80211_node_alloc; + ic->ic_node_free = node_free; + ic->ic_node_cleanup = node_cleanup; + ic->ic_node_copy = ieee80211_node_copy; + ic->ic_node_getrssi = ieee80211_node_getrssi; + ic->ic_scangen = 1; + /* default inactivity timer setings */ + ic->ic_inact_init = IEEE80211_INACT_INIT; + ic->ic_inact_auth = IEEE80211_INACT_AUTH; + ic->ic_inact_run = IEEE80211_INACT_RUN; + + if (ic->ic_max_aid == 0) + ic->ic_max_aid = IEEE80211_AID_DEF; + else if (ic->ic_max_aid > IEEE80211_AID_MAX) + ic->ic_max_aid = IEEE80211_AID_MAX; + MALLOC(ic->ic_aid_bitmap, u_int32_t *, + howmany(ic->ic_max_aid, 32) * sizeof(u_int32_t), + M_DEVBUF, M_NOWAIT | M_ZERO); + if (ic->ic_aid_bitmap == NULL) { + /* XXX no way to recover */ + printf("%s: no memory for AID bitmap!\n", __func__); + ic->ic_max_aid = 0; + } +} + +void +ieee80211_node_lateattach(struct ieee80211com *ic) +{ + struct ieee80211_node *ni; + struct ieee80211_rsnparms *rsn; + + ni = ic->ic_node_alloc(ic); + KASSERT(ni != NULL, ("unable to setup inital BSS node")); + /* + * Setup "global settings" in the bss node so that + * each new station automatically inherits them. + */ + rsn = &ni->ni_rsn; + /* WEP, TKIP, and AES-CCM are always supported */ + rsn->rsn_ucastcipherset |= 1<rsn_ucastcipherset |= 1<rsn_ucastcipherset |= 1<ic_caps & IEEE80211_C_AES) + rsn->rsn_ucastcipherset |= 1<ic_caps & IEEE80211_C_CKIP) + rsn->rsn_ucastcipherset |= 1<rsn_ucastcipher = IEEE80211_CIPHER_WEP; + rsn->rsn_ucastkeylen = 104 / NBBY; + /* + * WPA says the multicast cipher is the lowest unicast + * cipher supported. But we skip WEP which would + * otherwise be used based on this criteria. + */ + rsn->rsn_mcastcipher = IEEE80211_CIPHER_TKIP; + rsn->rsn_mcastkeylen = 128 / NBBY; + + /* + * We support both WPA-PSK and 802.1x; the one used + * is determined by the authentication mode and the + * setting of the PSK state. + */ + rsn->rsn_keymgmtset = WPA_ASE_8021X_UNSPEC | WPA_ASE_8021X_PSK; + rsn->rsn_keymgmt = WPA_ASE_8021X_PSK; + + ni->ni_chan = IEEE80211_CHAN_ANYC; + ni->ni_authmode = IEEE80211_AUTH_OPEN; + ni->ni_txpower = IEEE80211_TXPOWER_MAX; + ieee80211_crypto_resetkey(ic, &ni->ni_ucastkey, IEEE80211_KEYIX_NONE); + ic->ic_bss = ni; + ic->ic_auth = ieee80211_authenticator_get(ni->ni_authmode); +} + +void +ieee80211_node_detach(struct ieee80211com *ic) +{ + + if (ic->ic_bss != NULL) { + ic->ic_node_free(ic, ic->ic_bss); + ic->ic_bss = NULL; + } + ieee80211_free_allnodes(ic); + IEEE80211_NODE_LOCK_DESTROY(ic); + if (ic->ic_aid_bitmap != NULL) + FREE(ic->ic_aid_bitmap, M_DEVBUF); +} + +/* + * Port authorize/unauthorize interfaces for use by an authenticator. + */ + +void +ieee80211_node_authorize(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + ni->ni_flags |= IEEE80211_NODE_AUTH; +} +EXPORT_SYMBOL(ieee80211_node_authorize); + +void +ieee80211_node_unauthorize(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + ni->ni_flags &= ~IEEE80211_NODE_AUTH; +} +EXPORT_SYMBOL(ieee80211_node_unauthorize); + +/* + * Set/change the channel. The rate set is also updated as + * to insure a consistent view by drivers. + */ +static inline void +ieee80211_set_chan(struct ieee80211com *ic, + struct ieee80211_node *ni, struct ieee80211_channel *chan) +{ + ni->ni_chan = chan; + ni->ni_rates = ic->ic_sup_rates[ieee80211_chan2mode(ic, chan)]; +} + +/* + * AP scanning support. + */ + +#ifdef IEEE80211_DEBUG +static void +dump_chanlist(const u_char chans[]) +{ + const char *sep; + int i; + + sep = " "; + for (i = 0; i < IEEE80211_CHAN_MAX; i++) + if (isset(chans, i)) { + printf("%s%u", sep, i); + sep = ", "; + } +} +#endif /* IEEE80211_DEBUG */ + +/* + * Initialize the active channel set based on the set + * of available channels and the current PHY mode. + */ +static void +ieee80211_reset_scan(struct ieee80211com *ic) +{ + + /* XXX ic_des_chan should be handled with ic_chan_active */ + if (ic->ic_des_chan != IEEE80211_CHAN_ANYC) { + memset(ic->ic_chan_scan, 0, sizeof(ic->ic_chan_scan)); + setbit(ic->ic_chan_scan, + ieee80211_chan2ieee(ic, ic->ic_des_chan)); + } else + memcpy(ic->ic_chan_scan, ic->ic_chan_active, + sizeof(ic->ic_chan_active)); + /* NB: hack, setup so next_scan starts with the first channel */ + if (ic->ic_bss->ni_chan == IEEE80211_CHAN_ANYC) + ieee80211_set_chan(ic, ic->ic_bss, + &ic->ic_channels[IEEE80211_CHAN_MAX]); +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_scan(ic)) { + printf("%s: scan set:", __func__); + dump_chanlist(ic->ic_chan_scan); + printf(" start chan %u\n", + ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan)); + } +#endif /* IEEE80211_DEBUG */ +} + +/* + * Begin an active scan. + */ +void +ieee80211_begin_scan(struct ieee80211com *ic, int reset) +{ + + /* + * In all but hostap mode scanning starts off in + * an active mode before switching to passive. + */ + if (ic->ic_opmode != IEEE80211_M_HOSTAP) { + ic->ic_flags |= IEEE80211_F_ASCAN; + ic->ic_stats.is_scan_active++; + } else + ic->ic_stats.is_scan_passive++; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, ("begin %s scan\n", + (ic->ic_flags & IEEE80211_F_ASCAN) ? "active" : "passive")); + /* + * Clear scan state and flush any previously seen + * AP's. Note that the latter assumes we don't act + * as both an AP and a station, otherwise we'll + * potentially flush state of stations associated + * with us. + */ + ieee80211_reset_scan(ic); + if (reset) + ieee80211_free_allnodes(ic); + + /* Scan the next channel. */ + ieee80211_next_scan(ic); +} + +/* + * Switch to the next channel marked for scanning. + */ +int +ieee80211_next_scan(struct ieee80211com *ic) +{ + struct ieee80211_channel *chan; + + /* + * Insure any previous mgt frame timeouts don't fire. + * This assumes the driver does the right thing in + * flushing anything queued in the driver and below. + */ + ic->ic_mgt_timer = 0; + + chan = ic->ic_bss->ni_chan; + do { + if (++chan > &ic->ic_channels[IEEE80211_CHAN_MAX]) + chan = &ic->ic_channels[0]; + if (isset(ic->ic_chan_scan, ieee80211_chan2ieee(ic, chan))) { + clrbit(ic->ic_chan_scan, ieee80211_chan2ieee(ic, chan)); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + ("%s: chan %d->%d\n", __func__, + ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan), + ieee80211_chan2ieee(ic, chan))); + ieee80211_set_chan(ic, ic->ic_bss, chan); + ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); + return 1; + } + } while (chan != ic->ic_bss->ni_chan); + ieee80211_end_scan(ic); + return 0; +} +EXPORT_SYMBOL(ieee80211_next_scan); + +void +ieee80211_create_ibss(struct ieee80211com* ic, struct ieee80211_channel *chan) +{ + struct ieee80211_node *ni; + + ni = ic->ic_bss; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, ("creating ibss\n")); + ic->ic_flags |= IEEE80211_F_SIBSS; + ieee80211_set_chan(ic, ic->ic_bss, chan); + IEEE80211_ADDR_COPY(ni->ni_macaddr, ic->ic_myaddr); + IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_myaddr); + if (ic->ic_opmode == IEEE80211_M_IBSS) + ni->ni_bssid[0] |= 0x02; /* local bit for IBSS */ + ni->ni_esslen = ic->ic_des_esslen; + memcpy(ni->ni_essid, ic->ic_des_essid, ni->ni_esslen); + ni->ni_rssi = 0; + ni->ni_rstamp = 0; + ni->ni_tstamp.tsf = 0; + ni->ni_intval = ic->ic_lintval; + ni->ni_capinfo = IEEE80211_CAPINFO_IBSS; + if (ic->ic_flags & IEEE80211_F_PRIVACY) + ni->ni_capinfo |= IEEE80211_CAPINFO_PRIVACY; + if (ic->ic_phytype == IEEE80211_T_FH) { + ni->ni_fhdwell = 200; /* XXX */ + ni->ni_fhindex = 1; + } + ni->ni_erp = 0; + ieee80211_new_state(ic, IEEE80211_S_RUN, -1); +} + +static int +ieee80211_match_bss(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + u_int8_t rate; + int fail; + + fail = 0; + if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ni->ni_chan))) + fail |= 0x01; + if (ic->ic_des_chan != IEEE80211_CHAN_ANYC && + ni->ni_chan != ic->ic_des_chan) + fail |= 0x01; + if (ic->ic_opmode == IEEE80211_M_IBSS) { + if ((ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) == 0) + fail |= 0x02; + } else { + if ((ni->ni_capinfo & IEEE80211_CAPINFO_ESS) == 0) + fail |= 0x02; + } + if (ic->ic_flags & IEEE80211_F_PRIVACY) { + if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0) + fail |= 0x04; + } else { + /* XXX does this mean privacy is supported or required? */ + if (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) + fail |= 0x04; + } + rate = ieee80211_fix_rate(ic, ni, IEEE80211_F_DONEGO); + if (rate & IEEE80211_RATE_BASIC) + fail |= 0x08; + if (ic->ic_des_esslen != 0 && + (ni->ni_esslen != ic->ic_des_esslen || + memcmp(ni->ni_essid, ic->ic_des_essid, ic->ic_des_esslen) != 0)) + fail |= 0x10; + if ((ic->ic_flags & IEEE80211_F_DESBSSID) && + !IEEE80211_ADDR_EQ(ic->ic_des_bssid, ni->ni_bssid)) + fail |= 0x20; +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_scan(ic)) { + printf(" %c %s", fail ? '-' : '+', + ether_sprintf(ni->ni_macaddr)); + printf(" %s%c", ether_sprintf(ni->ni_bssid), + fail & 0x20 ? '!' : ' '); + printf(" %3d%c", ieee80211_chan2ieee(ic, ni->ni_chan), + fail & 0x01 ? '!' : ' '); + printf(" %+4d", ni->ni_rssi); + printf(" %2dM%c", (rate & IEEE80211_RATE_VAL) / 2, + fail & 0x08 ? '!' : ' '); + printf(" %4s%c", + (ni->ni_capinfo & IEEE80211_CAPINFO_ESS) ? "ess" : + (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) ? "ibss" : + "????", + fail & 0x02 ? '!' : ' '); + printf(" %3s%c ", + (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) ? + "wep" : "no", + fail & 0x04 ? '!' : ' '); + ieee80211_print_essid(ni->ni_essid, ni->ni_esslen); + printf("%s\n", fail & 0x10 ? "!" : ""); + } +#endif + return fail; +} + +/* + * Complete a scan of potential channels. + */ +void +ieee80211_end_scan(struct ieee80211com *ic) +{ + struct ieee80211_node *ni, *nextbs, *selbs; + int i, fail; + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, ("end %s scan\n", + (ic->ic_flags & IEEE80211_F_ASCAN) ? "active" : "passive")); + + ic->ic_flags &= ~IEEE80211_F_ASCAN; + ni = TAILQ_FIRST(&ic->ic_node); + + ieee80211_notify_scan_done(ic); + + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + /* XXX off stack? */ + u_char occupied[IEEE80211_CHAN_BYTES]; + /* + * The passive scan to look for existing AP's completed, + * select a channel to camp on. Identify the channels + * that already have one or more AP's and try to locate + * an unnoccupied one. If that fails, pick a random + * channel from the active set. + */ + for (; ni != NULL; ni = nextbs) { + ieee80211_ref_node(ni); + nextbs = TAILQ_NEXT(ni, ni_list); + setbit(occupied, ieee80211_chan2ieee(ic, ni->ni_chan)); + ieee80211_free_node(ic, ni); + } + for (i = 0; i < IEEE80211_CHAN_MAX; i++) + if (isset(ic->ic_chan_active, i) && isclr(occupied, i)) + break; + if (i == IEEE80211_CHAN_MAX) { + get_random_bytes(&fail, sizeof(fail)); + fail &= 3; /* random 0-3 */ + for (i = 0; i < IEEE80211_CHAN_MAX; i++) + if (isset(ic->ic_chan_active, i) && fail-- == 0) + break; + } + ieee80211_create_ibss(ic, &ic->ic_channels[i]); + return; + } + + /* + * When manually sequencing the state machine; scan just once + * regardless of whether we have a candidate or not. The + * controlling application is expected to setup state and + * initiate an association. + */ + if (ic->ic_roaming == IEEE80211_ROAMING_MANUAL) + return; + /* + * Automatic sequencing; look for a candidate and + * if found join the network. + */ + if (ni == NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + ("%s: no scan candidate\n", __func__)); + notfound: + if (ic->ic_opmode == IEEE80211_M_IBSS && + (ic->ic_flags & IEEE80211_F_IBSSON) && + ic->ic_des_esslen != 0) { + ieee80211_create_ibss(ic, ic->ic_ibss_chan); + return; + } + /* + * Reset the list of channels to scan and start again. + */ + ieee80211_reset_scan(ic); + ieee80211_next_scan(ic); + return; + } + selbs = NULL; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + ("\tmacaddr bssid chan rssi rate flag wep essid\n")); + for (; ni != NULL; ni = nextbs) { + ieee80211_ref_node(ni); + nextbs = TAILQ_NEXT(ni, ni_list); + if (ni->ni_fails) { + /* + * The configuration of the access points may change + * during my scan. So delete the entry for the AP + * and retry to associate if there is another beacon. + */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, + ("%s: skip scan candidate %s, fails %u\n", + __func__, ether_sprintf(ni->ni_macaddr), + ni->ni_fails)); + if (ni->ni_fails++ > 2) + ieee80211_free_node(ic, ni); + continue; + } + if (ieee80211_match_bss(ic, ni) == 0) { + if (selbs == NULL) + selbs = ni; + else if (ni->ni_rssi > selbs->ni_rssi) { + ieee80211_unref_node(&selbs); + selbs = ni; + } else + ieee80211_unref_node(&ni); + } else { + ieee80211_unref_node(&ni); + } + } + if (selbs == NULL) + goto notfound; + if (!ieee80211_sta_join(ic, selbs)) { + ieee80211_unref_node(&selbs); + goto notfound; + } +} + +/* + * Join the specified IBSS/BSS network. The node is assumed to + * be passed in with a held reference. + */ +int +ieee80211_sta_join(struct ieee80211com *ic, struct ieee80211_node *selbs) +{ + + /* XXX leak existing state in ic_bss? */ + ic->ic_node_copy(ic, ic->ic_bss, selbs); + /* + * Set the erp state (mostly the slot time) to deal with + * the auto-select case; this should be redundant if the + * mode is locked. + */ + ic->ic_curmode = ieee80211_chan2mode(ic, ic->ic_bss->ni_chan); + ieee80211_reset_erp(ic); + if (ic->ic_opmode == IEEE80211_M_IBSS) { + ieee80211_fix_rate(ic, ic->ic_bss, IEEE80211_F_DOFRATE | + IEEE80211_F_DONEGO | IEEE80211_F_DODEL); + if (ic->ic_bss->ni_rates.rs_nrates == 0) { + selbs->ni_fails++; + return 0; + } + ieee80211_unref_node(&selbs); + /* + * Discard scan set; the nodes have a refcnt of zero + * and have not asked the driver to setup private + * node state. Let them be repopulated on demand either + * through transmission (ieee80211_find_txnode) or receipt + * of a probe response (to be added). + */ + ieee80211_free_allnodes(ic); + ieee80211_new_state(ic, IEEE80211_S_RUN, -1); + } else { + ieee80211_unref_node(&selbs); + ieee80211_new_state(ic, IEEE80211_S_AUTH, -1); + } + return 1; +} + +/* + * Leave the specified IBSS/BSS network. The node is assumed to + * be passed in with a held reference. + */ +void +ieee80211_sta_leave(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + + ic->ic_node_cleanup(ic, ni); + ieee80211_notify_node_leave(ic, ni); +} + +static struct ieee80211_node * +ieee80211_node_alloc(struct ieee80211com *ic) +{ + struct ieee80211_node *ni; + MALLOC(ni, struct ieee80211_node *, sizeof(struct ieee80211_node), + M_80211_NODE, M_NOWAIT | M_ZERO); + return ni; +} + +/* + * Reclaim any resources in a node and reset any critical + * state. Typically nodes are free'd immediately after, + * but in some cases the storage may be reused so we need + * to insure consistent state (should probably fix that). + */ +static void +node_cleanup(struct ieee80211com *ic, struct ieee80211_node *ni) +{ +#define N(a) (sizeof(a)/sizeof(a[0])) + int i; + + ni->ni_flags = 0; + ni->ni_associd = 0; + ni->ni_esslen = 0; + /* XXX ni_savedq */ + if (ni->ni_challenge != NULL) { + FREE(ni->ni_challenge, M_DEVBUF); + ni->ni_challenge = NULL; + } + if (ni->ni_wpa_ie) { + FREE(ni->ni_wpa_ie, M_DEVBUF); + ni->ni_wpa_ie = NULL; + } + for (i = 0; i < N(ni->ni_rxfrag); i++) + if (ni->ni_rxfrag[i] != NULL) { + kfree_skb(ni->ni_rxfrag[i]); + ni->ni_rxfrag[i] = NULL; + } + ieee80211_crypto_delkey(ic, &ni->ni_ucastkey); +#undef N +} + +static void +node_free(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + ic->ic_node_cleanup(ic, ni); + FREE(ni, M_80211_NODE); +} + +/* + * Copy state from one node to another. The semantics are + * unclear for non-refcnt'd data; we leave them in the src + * and clear the dst. This is what is needed for the one + * (current) use of this operation (setting up ic_bss after + * an AP scan). In practice the fields in question should + * always be unused. + */ +static void +ieee80211_node_copy(struct ieee80211com *ic, + struct ieee80211_node *dst, const struct ieee80211_node *src) +{ +#define N(a) (sizeof(a)/sizeof(a[0])) + int i; + + ic->ic_node_cleanup(ic, dst); + *dst = *src; + dst->ni_challenge = NULL; + dst->ni_wpa_ie = NULL; + for (i = 0; i < N(dst->ni_rxfrag); i++) + dst->ni_rxfrag[i] = NULL; + /* NB: unicast key index reset by ieee80211_crypto_delkey */ +#undef N +} + +static u_int8_t +ieee80211_node_getrssi(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + return ni->ni_rssi; +} + +static void +ieee80211_setup_node(struct ieee80211com *ic, + struct ieee80211_node *ni, u_int8_t *macaddr) +{ + int hash; + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, + ("%s %s\n", __func__, ether_sprintf(macaddr))); + IEEE80211_ADDR_COPY(ni->ni_macaddr, macaddr); + hash = IEEE80211_NODE_HASH(macaddr); + skb_queue_head_init(&ni->ni_savedq); + ieee80211_node_initref(ni); /* mark referenced */ + ieee80211_crypto_resetkey(ic, &ni->ni_ucastkey, IEEE80211_KEYIX_NONE); + ni->ni_inact = ic->ic_inact_init; + + IEEE80211_NODE_LOCK_BH(ic); + TAILQ_INSERT_TAIL(&ic->ic_node, ni, ni_list); + LIST_INSERT_HEAD(&ic->ic_hash[hash], ni, ni_hash); + /* + * Note we don't enable the inactive timer when acting + * as a station. Nodes created in this mode represent + * AP's identified while scanning. If we time them out + * then several things happen: we can't return the data + * to users to show the list of AP's we encountered, and + * more importantly, we'll incorrectly deauthenticate + * ourself because the inactivity timer will kick us off. + */ + if (ic->ic_opmode != IEEE80211_M_STA) + ic->ic_inact_timer = IEEE80211_INACT_WAIT; + IEEE80211_NODE_UNLOCK_BH(ic); +} + +struct ieee80211_node * +ieee80211_alloc_node(struct ieee80211com *ic, u_int8_t *macaddr) +{ + struct ieee80211_node *ni = ic->ic_node_alloc(ic); + if (ni != NULL) + ieee80211_setup_node(ic, ni, macaddr); + else + ic->ic_stats.is_rx_nodealloc++; + return ni; +} + +struct ieee80211_node * +ieee80211_dup_bss(struct ieee80211com *ic, u_int8_t *macaddr) +{ + struct ieee80211_node *ni = ic->ic_node_alloc(ic); + if (ni != NULL) { + ieee80211_setup_node(ic, ni, macaddr); + /* + * Inherit from ic_bss. + */ + ni->ni_authmode = ic->ic_bss->ni_authmode; + ni->ni_txpower = ic->ic_bss->ni_txpower; + ni->ni_vlan = ic->ic_bss->ni_vlan; /* XXX?? */ + IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_bss->ni_bssid); + ieee80211_set_chan(ic, ni, ic->ic_bss->ni_chan); + ni->ni_rsn = ic->ic_bss->ni_rsn; + } else + ic->ic_stats.is_rx_nodealloc++; + return ni; +} + +struct ieee80211_node * +_ieee80211_find_node(struct ieee80211com *ic, u_int8_t *macaddr) +{ + struct ieee80211_node *ni; + int hash; + + IEEE80211_NODE_LOCK_ASSERT(ic); + + hash = IEEE80211_NODE_HASH(macaddr); + LIST_FOREACH(ni, &ic->ic_hash[hash], ni_hash) { + if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr)) { + ieee80211_node_incref(ni); /* mark referenced */ + return ni; + } + } + return NULL; +} + +struct ieee80211_node * +ieee80211_find_node(struct ieee80211com *ic, u_int8_t *macaddr) +{ + struct ieee80211_node *ni; + + IEEE80211_NODE_LOCK(ic); + ni = _ieee80211_find_node(ic, macaddr); + IEEE80211_NODE_UNLOCK(ic); + return ni; +} +EXPORT_SYMBOL(ieee80211_find_node); + +/* + * Return a reference to the appropriate node for sending + * a data frame. This handles node discovery in adhoc networks. + */ +struct ieee80211_node * +ieee80211_find_txnode(struct ieee80211com *ic, u_int8_t *macaddr) +{ + struct ieee80211_node *ni; + + /* + * The destination address should be in the node table + * unless we are operating in station mode or this is a + * multicast/broadcast frame. + */ + if (ic->ic_opmode == IEEE80211_M_STA || IEEE80211_IS_MULTICAST(macaddr)) + return ic->ic_bss; + + /* XXX can't hold lock across dup_bss 'cuz of recursive locking */ + IEEE80211_NODE_LOCK(ic); + ni = _ieee80211_find_node(ic, macaddr); + IEEE80211_NODE_UNLOCK(ic); + if (ni == NULL && + (ic->ic_opmode == IEEE80211_M_IBSS || + ic->ic_opmode == IEEE80211_M_AHDEMO)) { + /* + * Fake up a node; this handles node discovery in + * adhoc mode. Note that for the driver's benefit + * we we treat this like an association so the driver + * has an opportunity to setup it's private state. + * + * XXX need better way to handle this; issue probe + * request so we can deduce rate set, etc. + */ + ni = ieee80211_dup_bss(ic, macaddr); + if (ni != NULL) { + /* XXX no rate negotiation; just dup */ + ni->ni_rates = ic->ic_bss->ni_rates; + if (ic->ic_newassoc) + (*ic->ic_newassoc)(ic, ni, 1); + /* XXX not sure how 802.1x works w/ IBSS */ + ieee80211_node_authorize(ic, ni); + } + } + return ni; +} + +/* + * Like find but search based on the channel too. + */ +struct ieee80211_node * +ieee80211_find_node_with_channel(struct ieee80211com *ic, u_int8_t *macaddr, + struct ieee80211_channel *chan) +{ + struct ieee80211_node *ni; + int hash; + + hash = IEEE80211_NODE_HASH(macaddr); + IEEE80211_NODE_LOCK(ic); + LIST_FOREACH(ni, &ic->ic_hash[hash], ni_hash) { + if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr) && + ni->ni_chan == chan) { + ieee80211_node_incref(ni);/* mark referenced */ + break; + } + } + IEEE80211_NODE_UNLOCK(ic); + return ni; +} + +/* + * Like find but search based on the ssid too. + */ +struct ieee80211_node * +ieee80211_find_node_with_ssid(struct ieee80211com *ic, u_int8_t *macaddr, + u_int ssidlen, const u_int8_t *ssid) +{ + struct ieee80211_node *ni; + int hash; + + hash = IEEE80211_NODE_HASH(macaddr); + IEEE80211_NODE_LOCK(ic); + LIST_FOREACH(ni, &ic->ic_hash[hash], ni_hash) { + if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr) && + ni->ni_esslen == ic->ic_des_esslen && + memcmp(ni->ni_essid, ic->ic_des_essid, ni->ni_esslen) == 0) { + ieee80211_node_incref(ni);/* mark referenced */ + break; + } + } + IEEE80211_NODE_UNLOCK(ic); + return ni; +} + +static void +_ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + + KASSERT(ni != ic->ic_bss, ("freeing bss node")); + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, + ("%s %s\n", __func__, ether_sprintf(ni->ni_macaddr))); + IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap); + TAILQ_REMOVE(&ic->ic_node, ni, ni_list); + if (TAILQ_EMPTY(&ic->ic_node)) + ic->ic_inact_timer = 0; + LIST_REMOVE(ni, ni_hash); + if (_IF_QLEN(&ni->ni_savedq) != 0) { + /* + * Drain power save queue. + */ + IF_DRAIN(&ni->ni_savedq); + if (ic->ic_set_tim) + (*ic->ic_set_tim)(ic, ni->ni_associd, 0); + } + ic->ic_node_free(ic, ni); +} + +void +ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + KASSERT(ni != ic->ic_bss, ("freeing ic_bss")); + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, + ("%s %s refcnt %d\n", __func__, + ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni))); + if (ieee80211_node_dectestref(ni)) { + IEEE80211_NODE_LOCK_BH(ic); + _ieee80211_free_node(ic, ni); + IEEE80211_NODE_UNLOCK_BH(ic); + } +} +EXPORT_SYMBOL(ieee80211_free_node); + +void +ieee80211_free_allnodes(struct ieee80211com *ic) +{ + struct ieee80211_node *ni; + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, ("free all nodes\n")); + IEEE80211_NODE_LOCK_BH(ic); + while ((ni = TAILQ_FIRST(&ic->ic_node)) != NULL) { + if (ni->ni_associd != 0) { + if (ic->ic_auth->ia_node_leave != NULL) + ic->ic_auth->ia_node_leave(ic, ni); + IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap); + } + _ieee80211_free_node(ic, ni); + } + ieee80211_reset_erp(ic); + IEEE80211_NODE_UNLOCK_BH(ic); + + if (ic->ic_bss != NULL) + ic->ic_node_cleanup(ic, ic->ic_bss); /* for station mode */ +} + +/* + * Timeout inactive nodes. Note that we cannot hold the node + * lock while sending a frame as this would lead to a LOR. + * Instead we use a generation number to mark nodes that we've + * scanned and drop the lock and restart a scan if we have to + * time out a node. Since we are single-threaded by virtue of + * controlling the inactivity timer we can be sure this will + * process each node only once. + */ +void +ieee80211_timeout_nodes(struct ieee80211com *ic) +{ + struct ieee80211_node *ni; + u_int gen = ic->ic_scangen++; /* NB: ok 'cuz single-threaded*/ + +restart: + IEEE80211_NODE_LOCK(ic); + TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { + if (ni->ni_scangen == gen) /* previously handled */ + continue; + ni->ni_scangen = gen; + /* + * Free fragment skb if not needed anymore + * (last fragment older than 1s). + * XXX doesn't belong here + */ + if (ni->ni_rxfrag[0] && jiffies > ni->ni_rxfragstamp + HZ) { + kfree_skb(ni->ni_rxfrag[0]); + ni->ni_rxfrag[0] = NULL; + } + if (ni->ni_inact && --ni->ni_inact <= 0) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE, + ("station %s timed out due to inactivity\n", + ether_sprintf(ni->ni_macaddr))); + /* + * Send a deauthenticate frame. + * + * Drop the node lock before sending the + * deauthentication frame in case the driver takes + * a lock, as this will result in a LOR between the + * node lock and the driver lock. + */ + IEEE80211_NODE_UNLOCK(ic); + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_AUTH_EXPIRE); + ieee80211_node_leave(ic, ni); + ic->ic_stats.is_node_timeout++; + goto restart; + } + } + if (!TAILQ_EMPTY(&ic->ic_node)) + ic->ic_inact_timer = IEEE80211_INACT_WAIT; + IEEE80211_NODE_UNLOCK(ic); +} + +void +ieee80211_iterate_nodes(struct ieee80211com *ic, ieee80211_iter_func *f, void *arg) +{ + struct ieee80211_node *ni; + + IEEE80211_NODE_LOCK(ic); + TAILQ_FOREACH(ni, &ic->ic_node, ni_list) + (*f)(arg, ni); + IEEE80211_NODE_UNLOCK(ic); +} +EXPORT_SYMBOL(ieee80211_iterate_nodes); + +void +ieee80211_dump_node(struct ieee80211_node *ni) +{ + printf("0x%p: mac %s refcnt %d\n", ni, + ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)); + printf("\tscangen %u authmode %u flags 0x%x\n", + ni->ni_scangen, ni->ni_authmode, ni->ni_flags); + printf("\tassocid 0x%x txpower %u vlan %u\n", + ni->ni_associd, ni->ni_txpower, ni->ni_vlan); + printf("\ttxseq %u rxseq %u fragno %u rxfragstamp %u\n", + ni->ni_txseq, + ni->ni_rxseq >> IEEE80211_SEQ_SEQ_SHIFT, + ni->ni_rxseq & IEEE80211_SEQ_FRAG_MASK, + ni->ni_rxfragstamp); + printf("\trstamp %u rssi %u intval %u capinfo 0x%x\n", + ni->ni_rstamp, ni->ni_rssi, ni->ni_intval, ni->ni_capinfo); + printf("\tbssid %s essid \"%.*s\" channel %u:0x%x\n", + ether_sprintf(ni->ni_bssid), + ni->ni_esslen, ni->ni_essid, + ni->ni_chan->ic_freq, ni->ni_chan->ic_flags); + printf("\tfails %u inact %u txrate %u\n", + ni->ni_fails, ni->ni_inact, ni->ni_txrate); +} + +void +ieee80211_dump_nodes(struct ieee80211com *ic) +{ + struct ieee80211_node *ni; + + IEEE80211_NODE_LOCK(ic); + TAILQ_FOREACH(ni, &ic->ic_node, ni_list) + ieee80211_dump_node(ni); + IEEE80211_NODE_UNLOCK(ic); +} + +/* + * Handle a station joining an 11g network. + */ +static void +ieee80211_node_join_11g(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + + /* + * Station isn't capable of short slot time. Bump + * the count of long slot time stations and disable + * use of short slot time. Note that the actual switch + * over to long slot time use may not occur until the + * next beacon transmission (per sec. 7.3.1.4 of 11g). + */ + if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) == 0) { + ic->ic_longslotsta++; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + ("station %s needs long slot time, " + "count %d\n", + ether_sprintf(ni->ni_macaddr), + ic->ic_longslotsta)); + /* XXX vap's w/ conflicting needs won't work */ + ieee80211_set_shortslottime(ic, 0); + } + /* + * If the new station is not an ERP station + * then bump the counter and enable protection + * if configured. + */ + if (!ieee80211_iserp_rateset(ic, &ni->ni_rates)) { + ic->ic_nonerpsta++; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + ("station %s is !ERP, %d non-ERP stations " + "associated\n", + ether_sprintf(ni->ni_macaddr), + ic->ic_nonerpsta)); + /* + * If protection is configured, enable it. + */ + if (ic->ic_protmode != IEEE80211_PROT_NONE) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + ("enable use of protection\n")); + ic->ic_flags |= IEEE80211_F_USEPROT; + } + /* + * If station does not support short preamble + * then we must enable use of Barker preamble. + */ + if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE) == 0) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + ("station %s needs long preamble\n", + ether_sprintf(ni->ni_macaddr))); + ic->ic_flags |= IEEE80211_F_USEBARKER; + ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE; + } + } else + ni->ni_flags |= IEEE80211_NODE_ERP; +} + +void +ieee80211_node_join(struct ieee80211com *ic, struct ieee80211_node *ni, int resp) +{ + int newassoc; + + if (ni->ni_associd == 0) { + u_int16_t aid; + + /* + * It would be good to search the bitmap + * more efficiently, but this will do for now. + */ + for (aid = 1; aid < ic->ic_max_aid; aid++) { + if (!IEEE80211_AID_ISSET(aid, + ic->ic_aid_bitmap)) + break; + } + if (aid >= ic->ic_max_aid) { + IEEE80211_SEND_MGMT(ic, ni, resp, + IEEE80211_REASON_ASSOC_TOOMANY); + ieee80211_node_leave(ic, ni); + return; + } + ni->ni_associd = aid | 0xc000; + IEEE80211_AID_SET(ni->ni_associd, ic->ic_aid_bitmap); + newassoc = 1; + if (ic->ic_curmode == IEEE80211_MODE_11G) + ieee80211_node_join_11g(ic, ni); + } else + newassoc = 0; + + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, + ("station %s %s associated at aid %d\n", + ether_sprintf(ni->ni_macaddr), + newassoc ? "newly" : "already", + IEEE80211_NODE_AID(ni))); + + /* give driver a chance to setup state like ni_txrate */ + if (ic->ic_newassoc) + (*ic->ic_newassoc)(ic, ni, newassoc); + IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_SUCCESS); + /* tell the authenticator about new station */ + if (ic->ic_auth->ia_node_join != NULL) + (*ic->ic_auth->ia_node_join)(ic, ni); + ieee80211_notify_node_join(ic, ni, newassoc); +} + +/* + * Handle a station leaving an 11g network. + */ +static void +ieee80211_node_leave_11g(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + + KASSERT(ic->ic_curmode == IEEE80211_MODE_11G, + ("not in 11g, curmode %x", ic->ic_curmode)); + + /* + * If a long slot station do the slot time bookkeeping. + */ + if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) == 0) { + KASSERT(ic->ic_longslotsta > 0, + ("bogus long slot station count %d", ic->ic_longslotsta)); + ic->ic_longslotsta--; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + ("long slot time station %s leaves, count now %d\n", + ether_sprintf(ni->ni_macaddr), ic->ic_longslotsta)); + if (ic->ic_longslotsta == 0) { + /* + * Re-enable use of short slot time if supported + * and not operating in IBSS mode (per spec). + */ + if ((ic->ic_caps & IEEE80211_C_SHSLOT) && + ic->ic_opmode != IEEE80211_M_IBSS) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + ("re-enable use of short slot time\n")); + ieee80211_set_shortslottime(ic, 1); + } + } + } + /* + * If a non-ERP station do the protection-related bookkeeping. + */ + if ((ni->ni_flags & IEEE80211_NODE_ERP) == 0) { + KASSERT(ic->ic_nonerpsta > 0, + ("bogus non-ERP station count %d", ic->ic_nonerpsta)); + ic->ic_nonerpsta--; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + ("non-ERP station %s leaves, count now %d\n", + ether_sprintf(ni->ni_macaddr), ic->ic_nonerpsta)); + if (ic->ic_nonerpsta == 0) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + ("disable use of protection\n")); + ic->ic_flags &= ~IEEE80211_F_USEPROT; + /* XXX verify mode? */ + if (ic->ic_caps & IEEE80211_C_SHPREAMBLE) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + ("re-enable use of short preamble\n")); + ic->ic_flags |= IEEE80211_F_SHPREAMBLE; + ic->ic_flags &= ~IEEE80211_F_USEBARKER; + } + } + } +} + +/* + * Handle bookkeeping for station deauthentication/disassociation + * when operating as an ap. + */ +void +ieee80211_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + + KASSERT(ic->ic_opmode == IEEE80211_M_HOSTAP, + ("not in ap mode, mode %u", ic->ic_opmode)); + /* + * If node wasn't previously associated all + * we need to do is reclaim the reference. + */ + if (ni->ni_associd == 0) + goto done; + /* + * Tell the authenticator the station is leaving. + * Note that we must do this before yanking the + * association id as the authenticator uses the + * associd to locate it's state block. + */ + if (ic->ic_auth->ia_node_leave != NULL) + (*ic->ic_auth->ia_node_leave)(ic, ni); + IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap); + ni->ni_associd = 0; + + if (ic->ic_curmode == IEEE80211_MODE_11G) + ieee80211_node_leave_11g(ic, ni); + /* + * Cleanup station state. In particular clear various + * state that might otherwise be reused if the node + * is reused before the reference count goes to zero + * (and memory is reclaimed). + */ + ieee80211_sta_leave(ic, ni); +done: + ieee80211_free_node(ic, ni); +} +EXPORT_SYMBOL(ieee80211_node_leave); + +u_int8_t +ieee80211_getrssi(struct ieee80211com *ic) +{ +#define NZ(x) ((x) == 0 ? 1 : (x)) + u_int32_t rssi_samples, rssi_total; + struct ieee80211_node *ni; + + rssi_total = 0; + rssi_samples = 0; + switch (ic->ic_opmode) { + case IEEE80211_M_IBSS: /* average of all ibss neighbors */ + /* XXX locking */ + TAILQ_FOREACH(ni, &ic->ic_node, ni_list) + if (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) { + rssi_samples++; + rssi_total += ic->ic_node_getrssi(ic, ni); + } + break; + case IEEE80211_M_AHDEMO: /* average of all neighbors */ + /* XXX locking */ + TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { + rssi_samples++; + rssi_total += ic->ic_node_getrssi(ic, ni); + } + break; + case IEEE80211_M_HOSTAP: /* average of all associated stations */ + /* XXX locking */ + TAILQ_FOREACH(ni, &ic->ic_node, ni_list) + if (IEEE80211_AID(ni->ni_associd) != 0) { + rssi_samples++; + rssi_total += ic->ic_node_getrssi(ic, ni); + } + break; + case IEEE80211_M_MONITOR: /* XXX */ + case IEEE80211_M_STA: /* use stats from associated ap */ + default: + if (ic->ic_bss != NULL) + rssi_total = ic->ic_node_getrssi(ic, ic->ic_bss); + rssi_samples = 1; + break; + } + return rssi_total / NZ(rssi_samples); +#undef NZ +} +EXPORT_SYMBOL(ieee80211_getrssi); + +/* + * Set the short slot time state and notify the driver. + */ +void +ieee80211_set_shortslottime(struct ieee80211com *ic, int onoff) +{ + if (onoff) + ic->ic_flags |= IEEE80211_F_SHSLOT; + else + ic->ic_flags &= ~IEEE80211_F_SHSLOT; + /* notify driver */ + if (ic->ic_updateslot != NULL) + ic->ic_updateslot(ic->ic_dev); +} diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_node.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_node.h --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_node.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_node.h 2005-02-24 13:06:17.318129264 -0800 @@ -0,0 +1,220 @@ +/* $NetBSD: ieee80211_node.h,v 1.7 2003/10/29 21:50:57 dyoung Exp $ */ +/*- + * Copyright (c) 2001 Atsushi Onoe + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/net80211/ieee80211_node.h,v 1.7 2003/10/17 21:41:52 sam Exp $ + */ +#ifndef _NET80211_IEEE80211_NODE_H_ +#define _NET80211_IEEE80211_NODE_H_ + +#include /* for ieee80211_nodestats */ + +/* + * Each ieee80211com instance has a single timer that fires once a + * second. This is used to initiate various work depending on the + * state of the instance: scanning (passive or active), ``transition'' + * (waiting for a response to a management frame when operating + * as a station), and node inactivity processing (when operating + * as an AP). For inactivity processing each node has a timeout + * set in it's ni_intval field that is decremented on each timeout + * and the node is reclaimed when the counter goes to zero. We + * use different inactivity timeout values depending on whether + * the node is associated and authorized (either by 802.1x or + * open/shared key authentication) or associated but yet to be + * authorized. The latter timeout is shorter to more aggressively + * reclaim nodes that leave part way through the 802.1x exchange. + */ +#define IEEE80211_PSCAN_WAIT 5 /* passive scan intvl (secs) */ +#define IEEE80211_TRANS_WAIT 5 /* transition interval (secs) */ +#define IEEE80211_INACT_WAIT 5 /* inactivity interval (secs) */ +#define IEEE80211_INACT_INIT (30/IEEE80211_INACT_WAIT) /* initial */ +#define IEEE80211_INACT_AUTH (180/IEEE80211_INACT_WAIT) /* associated but not authorized */ +#define IEEE80211_INACT_RUN (300/IEEE80211_INACT_WAIT) /* authorized */ + +#define IEEE80211_NODE_HASHSIZE 32 +/* simple hash is enough for variation of macaddr */ +#define IEEE80211_NODE_HASH(addr) \ + (((u_int8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % IEEE80211_NODE_HASHSIZE) + +#define IEEE80211_RATE_SIZE 8 /* 802.11 standard */ +#define IEEE80211_RATE_MAXSIZE 15 /* max rates we'll handle */ + +struct ieee80211_rateset { + u_int8_t rs_nrates; + u_int8_t rs_rates[IEEE80211_RATE_MAXSIZE]; +}; + +struct ieee80211_rsnparms { + u_int8_t rsn_mcastcipher; /* mcast/group cipher */ + u_int8_t rsn_mcastkeylen; /* mcast key length */ + u_int8_t rsn_ucastcipherset; /* unicast cipher set */ + u_int8_t rsn_ucastcipher; /* selected unicast cipher */ + u_int8_t rsn_ucastkeylen; /* unicast key length */ + u_int8_t rsn_keymgmtset; /* key mangement algorithms */ + u_int8_t rsn_keymgmt; /* selected key mgmt algo */ + u_int16_t rsn_caps; /* capabilities */ +}; + +/* + * Node specific information. Note that drivers are expected + * to derive from this structure to add device-specific per-node + * state. This is done by overriding the ic_node_* methods in + * the ieee80211com structure. + */ +struct ieee80211_node { + TAILQ_ENTRY(ieee80211_node) ni_list; + LIST_ENTRY(ieee80211_node) ni_hash; + atomic_t ni_refcnt; + u_int ni_scangen; /* gen# for timeout scan */ + u_int8_t ni_authmode; /* authentication algorithm */ + u_int16_t ni_flags; /* special-purpose state */ +#define IEEE80211_NODE_AUTH 0x0001 /* authorized for data */ +#define IEEE80211_NODE_QOS 0x0002 /* QoS enabled */ +#define IEEE80211_NODE_ERP 0x0004 /* ERP enabled */ +/* NB: this must have the same value as IEEE80211_FC1_PWR_MGT */ +#define IEEE80211_NODE_PWR_MGT 0x0010 /* power save mode enabled */ + u_int16_t ni_associd; /* assoc response */ + u_int16_t ni_txpower; /* current transmit power */ + u_int16_t ni_vlan; /* vlan tag */ + u_int32_t *ni_challenge; /* shared-key challenge */ + u_int8_t *ni_wpa_ie; /* captured WPA/RSN ie */ + u_int16_t ni_txseq; /* seq to be transmitted */ + u_int16_t ni_rxseq; /* seq previous received */ + u_int16_t ni_rxseqs[16]; /* seq previous for qos frames*/ + u_int32_t ni_rxfragstamp; /* time stamp of last rx frag */ + struct sk_buff *ni_rxfrag[3]; /* rx frag reassembly */ + struct ieee80211_rsnparms ni_rsn; /* RSN/WPA parameters */ + struct ieee80211_key ni_ucastkey; /* unicast key */ + + /* hardware */ + u_int32_t ni_rstamp; /* recv timestamp */ + u_int8_t ni_rssi; /* recv ssi */ + + /* header */ + u_int8_t ni_macaddr[IEEE80211_ADDR_LEN]; + u_int8_t ni_bssid[IEEE80211_ADDR_LEN]; + + /* beacon, probe response */ + union { + u_int8_t data[8]; + u_int64_t tsf; + } ni_tstamp; /* from last rcv'd beacon */ + u_int16_t ni_intval; /* beacon interval */ + u_int16_t ni_capinfo; /* capabilities */ + u_int8_t ni_esslen; + u_int8_t ni_essid[IEEE80211_NWID_LEN]; + struct ieee80211_rateset ni_rates; /* negotiated rate set */ + struct ieee80211_channel *ni_chan; + u_int16_t ni_fhdwell; /* FH only */ + u_int8_t ni_fhindex; /* FH only */ + u_int8_t ni_erp; /* ERP from beacon/probe resp */ + u_int16_t ni_timoff; /* byte offset to TIM ie */ + + /* others */ + struct sk_buff_head ni_savedq; /* packets queued for pspoll */ + u_int32_t ni_pwrsavedrops;/* pspoll packets dropped */ + int ni_fails; /* failure count to associate */ + int ni_inact; /* current inactivity count */ + int ni_txrate; /* index to ni_rates[] */ + struct ieee80211_nodestats ni_stats; /* per-node statistics */ +}; +MALLOC_DECLARE(M_80211_NODE); + +#define IEEE80211_NODE_AID(ni) IEEE80211_AID(ni->ni_associd) + +static __inline struct ieee80211_node * +ieee80211_ref_node(struct ieee80211_node *ni) +{ + ieee80211_node_incref(ni); + return ni; +} + +static __inline void +ieee80211_unref_node(struct ieee80211_node **ni) +{ + ieee80211_node_decref(*ni); + *ni = NULL; /* guard against use */ +} + +struct ieee80211com; + +extern void ieee80211_node_attach(struct ieee80211com *); +extern void ieee80211_node_lateattach(struct ieee80211com *); +extern void ieee80211_node_detach(struct ieee80211com *); + +extern void ieee80211_node_authorize(struct ieee80211com *, + struct ieee80211_node *); +extern void ieee80211_node_unauthorize(struct ieee80211com *, + struct ieee80211_node *); + +extern void ieee80211_begin_scan(struct ieee80211com *, int); +extern int ieee80211_next_scan(struct ieee80211com *); +extern void ieee80211_create_ibss(struct ieee80211com*, + struct ieee80211_channel *); +extern void ieee80211_end_scan(struct ieee80211com *); +extern int ieee80211_sta_join(struct ieee80211com *, + struct ieee80211_node *); +extern void ieee80211_sta_leave(struct ieee80211com *, + struct ieee80211_node *); + +extern struct ieee80211_node *ieee80211_alloc_node(struct ieee80211com *, + u_int8_t *); +extern struct ieee80211_node *ieee80211_dup_bss(struct ieee80211com *, + u_int8_t *); +extern struct ieee80211_node *ieee80211_find_node(struct ieee80211com *, + u_int8_t *); +extern struct ieee80211_node *ieee80211_find_txnode(struct ieee80211com *, + u_int8_t *); +extern struct ieee80211_node *ieee80211_find_node_with_channel( + struct ieee80211com *, u_int8_t *macaddr, + struct ieee80211_channel *); +extern struct ieee80211_node *ieee80211_find_node_with_ssid( + struct ieee80211com *, u_int8_t *macaddr, u_int ssidlen, + const u_int8_t *ssid); +extern void ieee80211_free_node(struct ieee80211com *, + struct ieee80211_node *); +extern void ieee80211_free_allnodes(struct ieee80211com *); + +typedef void ieee80211_iter_func(void *, struct ieee80211_node *); +extern void ieee80211_iterate_nodes(struct ieee80211com *, + ieee80211_iter_func *, void *); + +extern void ieee80211_dump_node(struct ieee80211_node *); +extern void ieee80211_dump_nodes(struct ieee80211com *); +extern void ieee80211_timeout_nodes(struct ieee80211com *); + +extern void ieee80211_node_join(struct ieee80211com *, + struct ieee80211_node *, int); +extern void ieee80211_node_leave(struct ieee80211com *, + struct ieee80211_node *); +extern u_int8_t ieee80211_getrssi(struct ieee80211com *); +extern void ieee80211_set_shortslottime(struct ieee80211com *, int onoff); +#endif /* _NET80211_IEEE80211_NODE_H_ */ diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_output.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_output.c --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_output.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_output.c 2005-02-24 13:06:17.319129112 -0800 @@ -0,0 +1,1162 @@ +/*- + * Copyright (c) 2001 Atsushi Onoe + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EXPORT_SYMTAB +#define EXPORT_SYMTAB +#endif + +__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_output.c,v 1.9 2003/10/17 23:15:30 sam Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ieee80211_output.c,v 1.9 2003/11/02 00:17:27 dyoung Exp $"); + +/* + * IEEE 802.11 output handling. + */ +#include +#include +#include +#include +#include +#include + +#include "if_llc.h" +#include "if_ethersubr.h" +#include "if_media.h" + +#include + +#ifdef IEEE80211_DEBUG +/* + * Decide if an outbound management frame should be + * printed when debugging is enabled. This filters some + * of the less interesting frames that come frequently + * (e.g. beacons). + */ +static __inline int +doprint(struct ieee80211com *ic, int subtype) +{ + switch (subtype) { + case IEEE80211_FC0_SUBTYPE_PROBE_RESP: + return (ic->ic_opmode == IEEE80211_M_IBSS); + } + return 1; +} +#endif + +/* + * Send a management frame to the specified node. The node pointer + * must have a reference as the pointer will be passed to the driver + * and potentially held for a long time. If the frame is successfully + * dispatched to the driver, then it is responsible for freeing the + * reference (and potentially free'ing up any associated storage). + */ +static void +ieee80211_mgmt_output(struct ieee80211com *ic, struct ieee80211_node *ni, + struct sk_buff *skb, int type) +{ + struct ieee80211_frame *wh; + struct ieee80211_cb *cb = (struct ieee80211_cb *)skb->cb; + + KASSERT(ni != NULL, ("null node")); + ni->ni_inact = ic->ic_inact_auth; + + /* + * Yech, hack alert! We want to pass the node down to the + * driver's start routine. If we don't do so then the start + * routine must immediately look it up again and that can + * cause a lock order reversal if, for example, this frame + * is being sent because the station is being timedout and + * the frame being sent is a DEAUTH message. + */ + cb->ni = ni; + + wh = (struct ieee80211_frame *) + skb_push(skb, sizeof(struct ieee80211_frame)); + wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | type; + wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; + *(u_int16_t *)&wh->i_dur[0] = 0; + *(u_int16_t *)&wh->i_seq[0] = + htole16(ni->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT); + ni->ni_txseq++; + IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr); + IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr); + IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid); + + if ((cb->flags & M_LINK0) != 0 && ni->ni_challenge != NULL) { + cb->flags &= ~M_LINK0; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, + ("%s: encrypting frame for %s\n", + __func__, ether_sprintf(wh->i_addr1))); + wh->i_fc[1] |= IEEE80211_FC1_WEP; + } +#ifdef IEEE80211_DEBUG + /* avoid printing too many frames */ + if ((ieee80211_msg_debug(ic) && doprint(ic, type)) || + ieee80211_msg_dumppkts(ic)) { + if_printf(ic->ic_dev, "sending %s to %s on channel %u\n", + ieee80211_mgt_subtype_name[ + (type & IEEE80211_FC0_SUBTYPE_MASK) + >> IEEE80211_FC0_SUBTYPE_SHIFT], + ether_sprintf(ni->ni_macaddr), + ieee80211_chan2ieee(ic, ni->ni_chan)); + } +#endif + (void) (*ic->ic_mgtstart)(ic, skb); +} + +/* + * Insure there is sufficient headroom and tailroom to + * encapsulate the 802.11 data frame. If room isn't + * already there, reallocate so there is enough space. + * Drivers and cipher modules assume we have done the + * necessary work and fail rudely if they don't find + * the space they need. + */ +static struct sk_buff * +ieee80211_skbhdr_adjust(struct ieee80211com *ic, struct ieee80211_key *key, + struct sk_buff *skb) +{ + /* XXX too conservative, e.g. qos frame */ + /* XXX pre-calculate per node? */ + int need_headroom = sizeof(struct ieee80211_qosframe) + + sizeof(struct llc) + + IEEE80211_ADDR_LEN + ; + int need_tailroom = 0; + + if (key != NULL) { + const struct ieee80211_cipher *cip = key->wk_cipher; + /* + * Adjust for crypto needs. When hardware crypto is + * being used we assume the hardware/driver will deal + * with any padding (on the fly, without needing to + * expand the frame contents). When software crypto + * is used we need to insure room is available at the + * front and back and also for any per-MSDU additions. + */ + /* XXX belongs in crypto code? */ + need_headroom += cip->ic_header; + /* XXX pre-calculate per key */ + if (key->wk_flags & IEEE80211_KEY_SWCRYPT) + need_tailroom += cip->ic_trailer; + /* XXX frags */ + if (key->wk_flags & IEEE80211_KEY_SWMIC) + need_tailroom += cip->ic_miclen; + } + + skb = skb_unshare(skb, GFP_ATOMIC); + if (skb == NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT, + ("%s: cannot unshare for encapsulation\n", __func__)); + ic->ic_stats.is_tx_nobuf++; + } else if (skb_tailroom(skb) < need_tailroom) { + int headroom = skb_headroom(skb) < need_headroom ? + need_headroom - skb_headroom(skb) : 0; + if (pskb_expand_head(skb, headroom, + need_tailroom - skb_tailroom(skb), GFP_ATOMIC)) { + dev_kfree_skb(skb); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT, + ("%s: cannot expand storage (tail)\n", __func__)); + ic->ic_stats.is_tx_nobuf++; + skb = NULL; + } + } else if (skb_headroom(skb) < need_headroom) { + struct sk_buff *tmp = skb; + skb = skb_realloc_headroom(skb, need_headroom); + dev_kfree_skb(tmp); + if (skb == NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT, + ("%s: cannot expand storage (head)\n", __func__)); + ic->ic_stats.is_tx_nobuf++; + } + } + return skb; +} + +/* + * Return the transmit key to use in sending a frame to + * the specified destination. Multicast traffic always + * uses the group key. Otherwise if a unicast key is + * set we use that. When no unicast key is set we fall + * back to the default transmit key. + */ +static inline struct ieee80211_key * +ieee80211_crypto_getkey(struct ieee80211com *ic, + const u_int8_t mac[IEEE80211_ADDR_LEN], struct ieee80211_node *ni) +{ +#define KEY_UNDEFINED(k) ((k).wk_cipher == &ieee80211_cipher_none) + if (IEEE80211_IS_MULTICAST(mac) || KEY_UNDEFINED(ni->ni_ucastkey)) { + if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE || + KEY_UNDEFINED(ic->ic_nw_keys[ic->ic_def_txkey])) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT, + ("%s: no transmit key, def_txkey %u\n", + __func__, ic->ic_def_txkey)); + /* XXX statistic */ + return NULL; + } + return &ic->ic_nw_keys[ic->ic_def_txkey]; + } else { + return &ni->ni_ucastkey; + } +#undef KEY_UNDEFINED +} + +/* + * Encapsulate an outbound data frame. The mbuf chain is updated and + * a reference to the destination node is returned. If an error is + * encountered NULL is returned and the node reference will also be NULL. + * + * NB: The caller is responsible for free'ing a returned node reference. + * The convention is ic_bss is not reference counted; the caller must + * maintain that. + */ +struct sk_buff * +ieee80211_encap(struct ieee80211com *ic, struct sk_buff *skb, + struct ieee80211_node **pni) +{ + struct ether_header eh; + struct ieee80211_frame *wh; + struct ieee80211_node *ni; + struct ieee80211_key *key; + struct llc *llc; + + memcpy(&eh, skb->data, sizeof(struct ether_header)); + skb_pull(skb, sizeof(struct ether_header)); + + ni = ieee80211_find_txnode(ic, eh.ether_dhost); + if (ni == NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT, + ("%s: no node for dst %s, discard frame\n", + __func__, ether_sprintf(eh.ether_dhost))); + ic->ic_stats.is_tx_nonode++; + goto bad; + } + + /* + * If node has a vlan tag then all traffic + * to it must have a matching tag. + */ + if (ni->ni_vlan != 0) { + if (ic->ic_vlgrp == NULL || !vlan_tx_tag_present(skb)) { + ni->ni_stats.ns_tx_novlantag++; + goto bad; + } + if (vlan_tx_tag_get(skb) != ni->ni_vlan) { + ni->ni_stats.ns_tx_vlanmismatch++; + goto bad; + } + } + + /* + * Insure space for additional headers. First + * identify transmit key to use in calculating any + * buffer adjustments required. This is also used + * below to do privacy encapsulation work. + * + * Note key may be NULL if we fall back to the default + * transmit key and that is not set. In that case the + * buffer may not be expanded as needed by the cipher + * routines, but they will/should discard it. + */ + if (ic->ic_flags & IEEE80211_F_PRIVACY) + key = ieee80211_crypto_getkey(ic, eh.ether_dhost, ni); + else + key = NULL; + skb = ieee80211_skbhdr_adjust(ic, key, skb); + if (skb == NULL) { + /* NB: ieee80211_skbhdr_adjust handles msgs+statistics */ + goto bad; + } + + llc = (struct llc *) skb_push(skb, sizeof(struct llc)); + llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP; + llc->llc_control = LLC_UI; + llc->llc_snap.org_code[0] = 0; + llc->llc_snap.org_code[1] = 0; + llc->llc_snap.org_code[2] = 0; + llc->llc_snap.ether_type = eh.ether_type; + + wh = (struct ieee80211_frame *) + skb_push(skb, sizeof(struct ieee80211_frame));; + wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA; + *(u_int16_t *)&wh->i_dur[0] = 0; + *(u_int16_t *)&wh->i_seq[0] = + htole16(ni->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT); + ni->ni_txseq++; + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + wh->i_fc[1] = IEEE80211_FC1_DIR_TODS; + IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_bssid); + IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost); + IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost); + break; + case IEEE80211_M_IBSS: + case IEEE80211_M_AHDEMO: + wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; + IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); + IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost); + IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid); + break; + case IEEE80211_M_HOSTAP: + wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS; + IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost); + IEEE80211_ADDR_COPY(wh->i_addr2, ni->ni_bssid); + IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_shost); + break; + case IEEE80211_M_MONITOR: + goto bad; + } + if (eh.ether_type != __constant_htons(ETHERTYPE_PAE) || + (key != NULL && (ic->ic_flags & IEEE80211_F_WPA))) { + /* + * IEEE 802.1X: send EAPOL frames always in the clear. + * WPA/WPA2: encrypt EAPOL keys when pairwise keys are set. + */ + if (key != NULL) { + wh->i_fc[1] |= IEEE80211_FC1_WEP; + /* XXX do fragmentation */ + if (!ieee80211_crypto_enmic(ic, key, skb)) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO, + ("[%s] enmic failed, discard frame\n", + ether_sprintf(eh.ether_dhost))); + /* XXX statistic */ + goto bad; + } + } + } + if (eh.ether_type != __constant_htons(ETHERTYPE_PAE)) { + /* + * Reset the inactivity timer only for non-PAE traffic + * to avoid a problem where the station leaves w/o + * notice while we're requesting Identity. In this + * situation the 802.1x state machine will continue + * to retransmit the requests because it assumes the + * station will be timed out for inactivity, but our + * retransmits will reset the inactivity timer. + */ + ni->ni_inact = ic->ic_inact_run; + } + *pni = ni; + return skb; +bad: + if (skb != NULL) + dev_kfree_skb(skb); + if (ni && ni != ic->ic_bss) + ieee80211_free_node(ic, ni); + *pni = NULL; + return NULL; +} +EXPORT_SYMBOL(ieee80211_encap); + +/* + * Add a supported rates element id to a frame. + */ +static u_int8_t * +ieee80211_add_rates(u_int8_t *frm, const struct ieee80211_rateset *rs) +{ + int nrates; + + *frm++ = IEEE80211_ELEMID_RATES; + nrates = rs->rs_nrates; + if (nrates > IEEE80211_RATE_SIZE) + nrates = IEEE80211_RATE_SIZE; + *frm++ = nrates; + memcpy(frm, rs->rs_rates, nrates); + return frm + nrates; +} + +/* + * Add an extended supported rates element id to a frame. + */ +static u_int8_t * +ieee80211_add_xrates(u_int8_t *frm, const struct ieee80211_rateset *rs) +{ + /* + * Add an extended supported rates element if operating in 11g mode. + */ + if (rs->rs_nrates > IEEE80211_RATE_SIZE) { + int nrates = rs->rs_nrates - IEEE80211_RATE_SIZE; + *frm++ = IEEE80211_ELEMID_XRATES; + *frm++ = nrates; + memcpy(frm, rs->rs_rates + IEEE80211_RATE_SIZE, nrates); + frm += nrates; + } + return frm; +} + +/* + * Add an ssid elemet to a frame. + */ +static u_int8_t * +ieee80211_add_ssid(u_int8_t *frm, const u_int8_t *ssid, u_int len) +{ + *frm++ = IEEE80211_ELEMID_SSID; + *frm++ = len; + memcpy(frm, ssid, len); + return frm + len; +} + +/* + * Add an erp element to a frame. + */ +static u_int8_t * +ieee80211_add_erp(u_int8_t *frm, struct ieee80211com *ic) +{ + u_int8_t erp; + + *frm++ = IEEE80211_ELEMID_ERP; + *frm++ = 1; + erp = 0; + if (ic->ic_nonerpsta != 0) + erp |= IEEE80211_ERP_NON_ERP_PRESENT; + if (ic->ic_flags & IEEE80211_F_USEPROT) + erp |= IEEE80211_ERP_USE_PROTECTION; + if (ic->ic_flags & IEEE80211_F_USEBARKER) + erp |= IEEE80211_ERP_LONG_PREAMBLE; + *frm++ = erp; + return frm; +} + +static u_int8_t * +ieee80211_setup_wpa_ie(struct ieee80211com *ic, u_int8_t *ie) +{ +#define WPA_OUI_BYTES 0x00, 0x50, 0xf2 +#define ADDSHORT(frm, v) do { \ + frm[0] = (v) & 0xff; \ + frm[1] = (v) >> 8; \ + frm += 2; \ +} while (0) +#define ADDSELECTOR(frm, sel) do { \ + memcpy(frm, sel, 4); \ + frm += 4; \ +} while (0) + static const u_int8_t oui[4] = { WPA_OUI_BYTES, WPA_OUI_TYPE }; + static const u_int8_t cipher_suite[][4] = { + { WPA_OUI_BYTES, WPA_CSE_WEP40 }, /* NB: 40-bit */ + { WPA_OUI_BYTES, WPA_CSE_TKIP }, + { 0x00, 0x00, 0x00, 0x00 }, /* XXX WRAP */ + { WPA_OUI_BYTES, WPA_CSE_CCMP }, + { 0x00, 0x00, 0x00, 0x00 }, /* XXX CKIP */ + { WPA_OUI_BYTES, WPA_CSE_NULL }, + }; + static const u_int8_t wep104_suite[4] = + { WPA_OUI_BYTES, WPA_CSE_WEP104 }; + static const u_int8_t key_mgt_unspec[4] = + { WPA_OUI_BYTES, WPA_ASE_8021X_UNSPEC }; + static const u_int8_t key_mgt_psk[4] = + { WPA_OUI_BYTES, WPA_ASE_8021X_PSK }; + const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn; + u_int8_t *frm = ie; + u_int8_t *selcnt; + + *frm++ = IEEE80211_ELEMID_VENDOR; + *frm++ = 0; /* length filled in below */ + memcpy(frm, oui, sizeof(oui)); /* WPA OUI */ + frm += sizeof(oui); + ADDSHORT(frm, WPA_VERSION); + + /* XXX filter out CKIP */ + + /* multicast cipher */ + if (rsn->rsn_mcastcipher == IEEE80211_CIPHER_WEP && + rsn->rsn_mcastkeylen >= 13) + ADDSELECTOR(frm, wep104_suite); + else + ADDSELECTOR(frm, cipher_suite[rsn->rsn_mcastcipher]); + + /* unicast cipher list */ + selcnt = frm; + ADDSHORT(frm, 0); /* selector count */ + if (rsn->rsn_ucastcipherset & (1<rsn_ucastcipherset & (1<rsn_keymgmtset & WPA_ASE_8021X_UNSPEC) { + selcnt[0]++; + ADDSELECTOR(frm, key_mgt_unspec); + } + if (rsn->rsn_keymgmtset & WPA_ASE_8021X_PSK) { + selcnt[0]++; + ADDSELECTOR(frm, key_mgt_psk); + } + + /* optional capabilities */ + if (rsn->rsn_caps != 0) + ADDSHORT(frm, rsn->rsn_caps); + + /* calculate element length */ + ie[1] = frm - ie - 2; + KASSERT(ie[1]+2 <= sizeof(struct ieee80211_ie_wpa), + ("WPA IE too big, %u > %u", + ie[1]+2, sizeof(struct ieee80211_ie_wpa))); + return frm; +#undef ADDSHORT +#undef ADDSELECTOR +#undef WPA_OUI_BYTES +} + +static u_int8_t * +ieee80211_setup_rsn_ie(struct ieee80211com *ic, u_int8_t *ie) +{ +#define RSN_OUI_BYTES 0x00, 0x0f, 0xac +#define ADDSHORT(frm, v) do { \ + frm[0] = (v) & 0xff; \ + frm[1] = (v) >> 8; \ + frm += 2; \ +} while (0) +#define ADDSELECTOR(frm, sel) do { \ + memcpy(frm, sel, 4); \ + frm += 4; \ +} while (0) + static const u_int8_t cipher_suite[][4] = { + { RSN_OUI_BYTES, RSN_CSE_WEP40 }, /* NB: 40-bit */ + { RSN_OUI_BYTES, RSN_CSE_TKIP }, + { RSN_OUI_BYTES, RSN_CSE_WRAP }, + { RSN_OUI_BYTES, RSN_CSE_CCMP }, + { 0x00, 0x00, 0x00, 0x00 }, /* XXX CKIP */ + { RSN_OUI_BYTES, RSN_CSE_NULL }, + }; + static const u_int8_t wep104_suite[4] = + { RSN_OUI_BYTES, RSN_CSE_WEP104 }; + static const u_int8_t key_mgt_unspec[4] = + { RSN_OUI_BYTES, RSN_ASE_8021X_UNSPEC }; + static const u_int8_t key_mgt_psk[4] = + { RSN_OUI_BYTES, RSN_ASE_8021X_PSK }; + const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn; + u_int8_t *frm = ie; + u_int8_t *selcnt; + + *frm++ = IEEE80211_ELEMID_RSN; + *frm++ = 0; /* length filled in below */ + ADDSHORT(frm, RSN_VERSION); + + /* XXX filter out CKIP */ + + /* multicast cipher */ + if (rsn->rsn_mcastcipher == IEEE80211_CIPHER_WEP && + rsn->rsn_mcastkeylen >= 13) + ADDSELECTOR(frm, wep104_suite); + else + ADDSELECTOR(frm, cipher_suite[rsn->rsn_mcastcipher]); + + /* unicast cipher list */ + selcnt = frm; + ADDSHORT(frm, 0); /* selector count */ + if (rsn->rsn_ucastcipherset & (1<rsn_ucastcipherset & (1<rsn_keymgmtset & WPA_ASE_8021X_UNSPEC) { + selcnt[0]++; + ADDSELECTOR(frm, key_mgt_unspec); + } + if (rsn->rsn_keymgmtset & WPA_ASE_8021X_PSK) { + selcnt[0]++; + ADDSELECTOR(frm, key_mgt_psk); + } + + /* optional capabilities */ + if (rsn->rsn_caps != 0) + ADDSHORT(frm, rsn->rsn_caps); + /* XXX PMKID */ + + /* calculate element length */ + ie[1] = frm - ie - 2; + KASSERT(ie[1]+2 <= sizeof(struct ieee80211_ie_wpa), + ("RSN IE too big, %u > %u", + ie[1]+2, sizeof(struct ieee80211_ie_wpa))); + return frm; +#undef ADDSELECTOR +#undef ADDSHORT +#undef RSN_OUI_BYTES +} + +/* + * Add a WPA/RSN element to a frame. + */ +static u_int8_t * +ieee80211_add_wpa(u_int8_t *frm, struct ieee80211com *ic) +{ + + KASSERT(ic->ic_flags & IEEE80211_F_WPA, ("no WPA/RSN!")); + if (ic->ic_flags & IEEE80211_F_WPA2) + frm = ieee80211_setup_rsn_ie(ic, frm); + if (ic->ic_flags & IEEE80211_F_WPA1) + frm = ieee80211_setup_wpa_ie(ic, frm); + return frm; +} + +/* + * Send a management frame. The node is for the destination (or ic_bss + * when in station mode). Nodes other than ic_bss have their reference + * count bumped to reflect our use for an indeterminant time. + */ +int +ieee80211_send_mgmt(struct ieee80211com *ic, struct ieee80211_node *ni, + int type, int arg) +{ +#define senderr(_x, _v) do { ic->ic_stats._v++; ret = _x; goto bad; } while (0) + struct sk_buff *skb; + u_int8_t *frm; + enum ieee80211_phymode mode; + u_int16_t capinfo; + int has_challenge, is_shared_key, ret, timer, status; + + KASSERT(ni != NULL, ("null node")); + + /* + * Hold a reference on the node so it doesn't go away until after + * the xmit is complete all the way in the driver. On error we + * will remove our reference. + */ + if (ni != ic->ic_bss) + ieee80211_ref_node(ni); + timer = 0; + switch (type) { + case IEEE80211_FC0_SUBTYPE_PROBE_REQ: + /* + * prreq frame format + * [tlv] ssid + * [tlv] supported rates + * [tlv] extended supported rates + * [tlv] user-specified ie's + */ + skb = ieee80211_getmgtframe(&frm, + 2 + ic->ic_des_esslen + + 2 + IEEE80211_RATE_SIZE + + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + + (ic->ic_opt_ie != NULL ? ic->ic_opt_ie_len : 0)); + if (skb == NULL) + senderr(ENOMEM, is_tx_nobuf); + + frm = ieee80211_add_ssid(frm, ic->ic_des_essid, ic->ic_des_esslen); + mode = ieee80211_chan2mode(ic, ni->ni_chan); + frm = ieee80211_add_rates(frm, &ic->ic_sup_rates[mode]); + frm = ieee80211_add_xrates(frm, &ic->ic_sup_rates[mode]); + if (ic->ic_opt_ie != NULL) { + memcpy(frm, ic->ic_opt_ie, ic->ic_opt_ie_len); + frm += ic->ic_opt_ie_len; + } + skb_trim(skb, frm - skb->data); + + timer = IEEE80211_TRANS_WAIT; + break; + + case IEEE80211_FC0_SUBTYPE_PROBE_RESP: + /* + * probe response frame format + * [8] time stamp + * [2] beacon interval + * [2] cabability information + * [tlv] ssid + * [tlv] supported rates + * [tlv] parameter set (FH/DS) + * [tlv] parameter set (IBSS) + * [tlv] extended rate phy (ERP) + * [tlv] extended supported rates + * [tlv] WPA + */ + skb = ieee80211_getmgtframe(&frm, + 8 + 2 + 2 + 2 + + 2 + ni->ni_esslen + + 2 + IEEE80211_RATE_SIZE + + (ic->ic_phytype == IEEE80211_T_FH ? 7 : 3) + + 6 + + (ic->ic_curmode == IEEE80211_MODE_11G ? 3 : 0) + + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + + (ic->ic_flags & IEEE80211_F_WPA ? + 2*sizeof(struct ieee80211_ie_wpa) : 0) + ); + if (skb == NULL) + senderr(ENOMEM, is_tx_nobuf); + + memset(frm, 0, 8); /* timestamp should be filled later */ + frm += 8; + *(u_int16_t *)frm = htole16(ic->ic_bss->ni_intval); + frm += 2; + if (ic->ic_opmode == IEEE80211_M_IBSS) + capinfo = IEEE80211_CAPINFO_IBSS; + else + capinfo = IEEE80211_CAPINFO_ESS; + if (ic->ic_flags & IEEE80211_F_PRIVACY) + capinfo |= IEEE80211_CAPINFO_PRIVACY; + if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && + IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) + capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; + if (ic->ic_flags & IEEE80211_F_SHSLOT) + capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; + *(u_int16_t *)frm = htole16(capinfo); + frm += 2; + + frm = ieee80211_add_ssid(frm, ic->ic_bss->ni_essid, + ic->ic_bss->ni_esslen); + frm = ieee80211_add_rates(frm, &ic->ic_bss->ni_rates); + + if (ic->ic_phytype == IEEE80211_T_FH) { + *frm++ = IEEE80211_ELEMID_FHPARMS; + *frm++ = 5; + *frm++ = ni->ni_fhdwell & 0x00ff; + *frm++ = (ni->ni_fhdwell >> 8) & 0x00ff; + *frm++ = IEEE80211_FH_CHANSET( + ieee80211_chan2ieee(ic, ni->ni_chan)); + *frm++ = IEEE80211_FH_CHANPAT( + ieee80211_chan2ieee(ic, ni->ni_chan)); + *frm++ = ni->ni_fhindex; + } else { + *frm++ = IEEE80211_ELEMID_DSPARMS; + *frm++ = 1; + *frm++ = ieee80211_chan2ieee(ic, ni->ni_chan); + } + + if (ic->ic_opmode == IEEE80211_M_IBSS) { + *frm++ = IEEE80211_ELEMID_IBSSPARMS; + *frm++ = 2; + *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */ + } + if (ic->ic_flags & IEEE80211_F_WPA) + frm = ieee80211_add_wpa(frm, ic); + if (ic->ic_curmode == IEEE80211_MODE_11G) + frm = ieee80211_add_erp(frm, ic); + frm = ieee80211_add_xrates(frm, &ic->ic_bss->ni_rates); + skb_trim(skb, frm - skb->data); + break; + + case IEEE80211_FC0_SUBTYPE_AUTH: + status = arg >> 16; + arg &= 0xffff; + has_challenge = ((arg == IEEE80211_AUTH_SHARED_CHALLENGE || + arg == IEEE80211_AUTH_SHARED_RESPONSE) && + ni->ni_challenge != NULL); + + /* + * Deduce whether we're doing open authentication or + * shared key authentication. We do the latter if + * we're in the middle of a shared key authentication + * handshake or if we're initiating an authentication + * request and configured to use shared key. + */ + is_shared_key = has_challenge || + arg >= IEEE80211_AUTH_SHARED_RESPONSE || + (arg == IEEE80211_AUTH_SHARED_REQUEST && + ic->ic_bss->ni_authmode == IEEE80211_AUTH_SHARED); + + skb = ieee80211_getmgtframe(&frm, + 3 * sizeof(u_int16_t) + + (has_challenge && status == IEEE80211_STATUS_SUCCESS ? + sizeof(u_int16_t)+IEEE80211_CHALLENGE_LEN : 0)); + if (skb == NULL) + senderr(ENOMEM, is_tx_nobuf); + + ((u_int16_t *)frm)[0] = + (is_shared_key) ? htole16(IEEE80211_AUTH_ALG_SHARED) + : htole16(IEEE80211_AUTH_ALG_OPEN); + ((u_int16_t *)frm)[1] = htole16(arg); /* sequence number */ + ((u_int16_t *)frm)[2] = htole16(status);/* status */ + + if (has_challenge && status == IEEE80211_STATUS_SUCCESS) { + ((u_int16_t *)frm)[3] = + htole16((IEEE80211_CHALLENGE_LEN << 8) | + IEEE80211_ELEMID_CHALLENGE); + memcpy(&((u_int16_t *)frm)[4], ni->ni_challenge, + IEEE80211_CHALLENGE_LEN); + if (arg == IEEE80211_AUTH_SHARED_RESPONSE) { + struct ieee80211_cb *cb = + (struct ieee80211_cb *)skb->cb; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, + ("%s: request encrypt frame\n", __func__)); + cb->flags |= M_LINK0; /* WEP-encrypt, please */ + } + } + /* + * When 802.1x is not in use mark the port + * authorized at this point so traffic can flow. + * XXX shared key auth + */ + if (ic->ic_opmode == IEEE80211_M_HOSTAP && + status == IEEE80211_STATUS_SUCCESS && + ni->ni_authmode != IEEE80211_AUTH_8021X) + ieee80211_node_authorize(ic, ni); + if (ic->ic_opmode == IEEE80211_M_STA) + timer = IEEE80211_TRANS_WAIT; + break; + + case IEEE80211_FC0_SUBTYPE_DEAUTH: + IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH, + ("send station %s deauthenticate (reason %d)\n", + ether_sprintf(ni->ni_macaddr), arg)); + skb = ieee80211_getmgtframe(&frm, sizeof(u_int16_t)); + if (skb == NULL) + senderr(ENOMEM, is_tx_nobuf); + *(u_int16_t *)frm = htole16(arg); /* reason */ + ieee80211_node_unauthorize(ic, ni); /* port closed */ + break; + + case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: + case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: + /* + * asreq frame format + * [2] capability information + * [2] listen interval + * [6*] current AP address (reassoc only) + * [tlv] ssid + * [tlv] supported rates + * [tlv] extended supported rates + * [tlv] user-specified ie's + */ + skb = ieee80211_getmgtframe(&frm, + sizeof(capinfo) + + sizeof(u_int16_t) + + IEEE80211_ADDR_LEN + + 2 + ni->ni_esslen + + 2 + IEEE80211_RATE_SIZE + + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + + (ic->ic_opt_ie != NULL ? ic->ic_opt_ie_len : 0)); + if (skb == NULL) + senderr(ENOMEM, is_tx_nobuf); + + capinfo = 0; + if (ic->ic_opmode == IEEE80211_M_IBSS) + capinfo |= IEEE80211_CAPINFO_IBSS; + else /* IEEE80211_M_STA */ + capinfo |= IEEE80211_CAPINFO_ESS; + if (ic->ic_flags & IEEE80211_F_PRIVACY) + capinfo |= IEEE80211_CAPINFO_PRIVACY; + /* + * NB: Some 11a AP's reject the request when + * short premable is set. + */ + if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && + IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) + capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; + if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) && + (ic->ic_caps & IEEE80211_C_SHSLOT)) + capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; + *(u_int16_t *)frm = htole16(capinfo); + frm += 2; + + *(u_int16_t *)frm = htole16(ic->ic_lintval); + frm += 2; + + if (type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) { + IEEE80211_ADDR_COPY(frm, ic->ic_bss->ni_bssid); + frm += IEEE80211_ADDR_LEN; + } + + frm = ieee80211_add_ssid(frm, ni->ni_essid, ni->ni_esslen); + frm = ieee80211_add_rates(frm, &ni->ni_rates); + frm = ieee80211_add_xrates(frm, &ni->ni_rates); + if (ic->ic_opt_ie != NULL) { + memcpy(frm, ic->ic_opt_ie, ic->ic_opt_ie_len); + frm += ic->ic_opt_ie_len; + } + skb_trim(skb, frm - skb->data); + + timer = IEEE80211_TRANS_WAIT; + break; + + case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: + case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: + /* + * asreq frame format + * [2] capability information + * [2] status + * [2] association ID + * [tlv] supported rates + * [tlv] extended supported rates + */ + skb = ieee80211_getmgtframe(&frm, + sizeof(capinfo) + + sizeof(u_int16_t) + + sizeof(u_int16_t) + + 2 + IEEE80211_RATE_SIZE + + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)); + if (skb == NULL) + senderr(ENOMEM, is_tx_nobuf); + + capinfo = IEEE80211_CAPINFO_ESS; + if (ic->ic_flags & IEEE80211_F_PRIVACY) + capinfo |= IEEE80211_CAPINFO_PRIVACY; + if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && + IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) + capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; + if (ic->ic_flags & IEEE80211_F_SHSLOT) + capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; + *(u_int16_t *)frm = htole16(capinfo); + frm += 2; + + *(u_int16_t *)frm = htole16(arg); /* status */ + frm += 2; + + if (arg == IEEE80211_STATUS_SUCCESS) + *(u_int16_t *)frm = htole16(ni->ni_associd); + frm += 2; + + frm = ieee80211_add_rates(frm, &ni->ni_rates); + frm = ieee80211_add_xrates(frm, &ni->ni_rates); + skb_trim(skb, frm - skb->data); + break; + + case IEEE80211_FC0_SUBTYPE_DISASSOC: + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC, + ("send station %s disassociate (reason %d)\n", + ether_sprintf(ni->ni_macaddr), arg)); + skb = ieee80211_getmgtframe(&frm, sizeof(u_int16_t)); + if (skb == NULL) + senderr(ENOMEM, is_tx_nobuf); + *(u_int16_t *)frm = htole16(arg); /* reason */ + break; + + default: + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + ("%s: invalid mgmt frame type %u\n", __func__, type)); + senderr(EINVAL, is_tx_unknownmgt); + /* NOTREACHED */ + } + + ieee80211_mgmt_output(ic, ni, skb, type); + if (timer) + ic->ic_mgt_timer = timer; + return 0; +bad: + if (ni != ic->ic_bss) /* remove ref we added */ + ieee80211_free_node(ic, ni); + return ret; +#undef senderr +} + +/* + * Allocate a beacon frame and fillin the appropriate bits. + */ +struct sk_buff * +ieee80211_beacon_alloc(struct ieee80211com *ic, struct ieee80211_node *ni, + struct ieee80211_beacon_offsets *bo) +{ + struct net_device *dev = ic->ic_dev; + struct ieee80211_frame *wh; + struct sk_buff *skb; + int pktlen; + u_int8_t *frm, *efrm; + u_int16_t capinfo; + struct ieee80211_rateset *rs; + + /* + * beacon frame format + * [8] time stamp + * [2] beacon interval + * [2] cabability information + * [tlv] ssid + * [tlv] supported rates + * [3] parameter set (DS) + * [tlv] parameter set (IBSS/TIM) + * [tlv] extended rate phy (ERP) + * [tlv] extended supported rates + * [tlv] WPA/RSN parameters + * XXX WME, etc. + * XXX Vendor-specific OIDs (e.g. Atheros) + */ + rs = &ni->ni_rates; + /* XXX may be better to just allocate a max-sized buffer */ + pktlen = 8 /* time stamp */ + + sizeof(u_int16_t) /* beacon interval */ + + sizeof(u_int16_t) /* capabilities */ + + 2 + ni->ni_esslen /* ssid */ + + 2 + IEEE80211_RATE_SIZE /* supported rates */ + + 2 + 1 /* DS parameters */ + + 2 + 4 /* DTIM/IBSSPARMS */ + + 2 + 1 /* ERP */ + + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + + 2*sizeof(struct ieee80211_ie_wpa) /* WPA 1+2 */ + ; + skb = ieee80211_getmgtframe(&frm, pktlen); + if (skb == NULL) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + ("%s: cannot get buf; size %u\n", __func__, pktlen)); + ic->ic_stats.is_tx_nobuf++; + return NULL; + } + + memset(frm, 0, 8); /* XXX timestamp is set by hardware */ + frm += 8; + *(u_int16_t *)frm = cpu_to_le16(ni->ni_intval); + frm += 2; + if (ic->ic_opmode == IEEE80211_M_IBSS) + capinfo = IEEE80211_CAPINFO_IBSS; + else + capinfo = IEEE80211_CAPINFO_ESS; + if (ic->ic_flags & IEEE80211_F_PRIVACY) + capinfo |= IEEE80211_CAPINFO_PRIVACY; + if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && + IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) + capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; + if (ic->ic_flags & IEEE80211_F_SHSLOT) + capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; + bo->bo_caps = (u_int16_t *)frm; + *(u_int16_t *)frm = cpu_to_le16(capinfo); + frm += 2; + *frm++ = IEEE80211_ELEMID_SSID; + if ((ic->ic_flags & IEEE80211_F_HIDESSID) == 0) { + *frm++ = ni->ni_esslen; + memcpy(frm, ni->ni_essid, ni->ni_esslen); + frm += ni->ni_esslen; + } else + *frm++ = 0; + frm = ieee80211_add_rates(frm, rs); + if (ic->ic_curmode != IEEE80211_MODE_FH) { + *frm++ = IEEE80211_ELEMID_DSPARMS; + *frm++ = 1; + *frm++ = ieee80211_chan2ieee(ic, ni->ni_chan); + } + bo->bo_tim = frm; + if (ic->ic_opmode == IEEE80211_M_IBSS) { + *frm++ = IEEE80211_ELEMID_IBSSPARMS; + *frm++ = 2; + *frm++ = 0; *frm++ = 0; /* TODO: ATIM window */ + bo->bo_tim_len = 4; + } else { + /* TODO: TIM */ + *frm++ = IEEE80211_ELEMID_TIM; + *frm++ = 4; /* length */ + *frm++ = 0; /* DTIM count */ + *frm++ = 1; /* DTIM period */ + *frm++ = 0; /* bitmap control */ + *frm++ = 0; /* Partial Virtual Bitmap (variable length) */ + bo->bo_tim_len = 6; + } + bo->bo_trailer = frm; + if (ic->ic_flags & IEEE80211_F_WPA) + frm = ieee80211_add_wpa(frm, ic); + if (ic->ic_curmode == IEEE80211_MODE_11G) + frm = ieee80211_add_erp(frm, ic); + efrm = ieee80211_add_xrates(frm, rs); + bo->bo_trailer_len = efrm - bo->bo_trailer; + skb_trim(skb, efrm - skb->data); + + wh = (struct ieee80211_frame *) + skb_push(skb, sizeof(struct ieee80211_frame)); + wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | + IEEE80211_FC0_SUBTYPE_BEACON; + wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; + *(u_int16_t *)wh->i_dur = 0; + IEEE80211_ADDR_COPY(wh->i_addr1, dev->broadcast); + IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr); + IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid); + *(u_int16_t *)wh->i_seq = 0; + + return skb; +} +EXPORT_SYMBOL(ieee80211_beacon_alloc); + +/* + * Update the dynamic parts of a beacon frame based on the current state. + */ +int +ieee80211_beacon_update(struct ieee80211com *ic, struct ieee80211_node *ni, + struct ieee80211_beacon_offsets *bo, struct sk_buff **skb0) +{ + u_int16_t capinfo; + + /* XXX lock out changes */ + /* XXX only update as needed */ + /* XXX faster to recalculate entirely or just changes? */ + if (ic->ic_opmode == IEEE80211_M_IBSS) + capinfo = IEEE80211_CAPINFO_IBSS; + else + capinfo = IEEE80211_CAPINFO_ESS; + if (ic->ic_flags & IEEE80211_F_PRIVACY) + capinfo |= IEEE80211_CAPINFO_PRIVACY; + if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && + IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) + capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; + if (ic->ic_flags & IEEE80211_F_SHSLOT) + capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; + *bo->bo_caps = htole16(capinfo); + + if (ic->ic_flags & IEEE80211_F_TIMUPDATE) { + /* + * ATIM/DTIM needs updating. If it fits in the + * current space allocated then just copy in the + * new bits. Otherwise we need to move any extended + * rate set the follows and, possibly, allocate a + * new buffer if the this current one isn't large + * enough. XXX It may be better to just allocate + * a max-sized buffer so we don't re-allocate. + */ + /* XXX fillin */ + ic->ic_flags &= ~IEEE80211_F_TIMUPDATE; + } + return 0; +} +EXPORT_SYMBOL(ieee80211_beacon_update); + +/* + * Save an outbound packet for a node in power-save sleep state. + * The new packet is placed on the node's saved queue, and the TIM + * is changed, if necessary. + */ +void +ieee80211_pwrsave(struct ieee80211com *ic, struct ieee80211_node *ni, + struct sk_buff *skb) +{ + if (_IF_QLEN(&ni->ni_savedq) == 0) + (*ic->ic_set_tim)(ic, ni->ni_associd, 1); + if (_IF_QLEN(&ni->ni_savedq) >= IEEE80211_PS_MAX_QUEUE) { + ni->ni_pwrsavedrops++; /* XXX atomic_inc */ + dev_kfree_skb(skb); + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + ("station %s pwr save q overflow of size %d drops %d\n", + ether_sprintf(ni->ni_macaddr), + IEEE80211_PS_MAX_QUEUE, ni->ni_pwrsavedrops)); + } else { + struct ieee80211_cb *cb = (struct ieee80211_cb *)skb->cb; + /* + * Similar to ieee80211_mgmt_output, save the node in + * the packet for use by the driver start routine. + */ + /* XXX do we know we have a reference? */ + cb->ni = ni; + IF_ENQUEUE(&ni->ni_savedq, skb); + } +} diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_proto.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_proto.c --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_proto.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_proto.c 2005-02-24 13:06:17.341125768 -0800 @@ -0,0 +1,714 @@ +/*- + * Copyright (c) 2001 Atsushi Onoe + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EXPORT_SYMTAB +#define EXPORT_SYMTAB +#endif + +__FBSDID("$FreeBSD: src/sys/net80211/ieee80211_proto.c,v 1.6 2003/10/31 18:32:09 brooks Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ieee80211_proto.c,v 1.5 2003/10/13 04:23:56 dyoung Exp $"); + +/* + * IEEE 802.11 protocol support. + */ +#include +#include +#include +#include +#include +#include + +#include "if_media.h" + +#include +#include + +#define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2) + +const char *ieee80211_mgt_subtype_name[] = { + "assoc_req", "assoc_resp", "reassoc_req", "reassoc_resp", + "probe_req", "probe_resp", "reserved#6", "reserved#7", + "beacon", "atim", "disassoc", "auth", + "deauth", "reserved#13", "reserved#14", "reserved#15" +}; +EXPORT_SYMBOL(ieee80211_mgt_subtype_name); +const char *ieee80211_ctl_subtype_name[] = { + "reserved#0", "reserved#1", "reserved#2", "reserved#3", + "reserved#3", "reserved#5", "reserved#6", "reserved#7", + "reserved#8", "reserved#9", "ps_poll", "rts", + "cts", "ack", "cf_end", "cf_end_ack" +}; +EXPORT_SYMBOL(ieee80211_ctl_subtype_name); +const char *ieee80211_state_name[IEEE80211_S_MAX] = { + "INIT", /* IEEE80211_S_INIT */ + "SCAN", /* IEEE80211_S_SCAN */ + "AUTH", /* IEEE80211_S_AUTH */ + "ASSOC", /* IEEE80211_S_ASSOC */ + "RUN" /* IEEE80211_S_RUN */ +}; +EXPORT_SYMBOL(ieee80211_state_name); + +#define IEEE80211_AUTH_MAX (IEEE80211_AUTH_WPA+1) +/* XXX well-known names */ +static const char *auth_modnames[IEEE80211_AUTH_MAX] = { + "wlan_internal", /* IEEE80211_AUTH_NONE */ + "wlan_internal", /* IEEE80211_AUTH_OPEN */ + "wlan_internal", /* IEEE80211_AUTH_SHARED */ + "wlan_xauth", /* IEEE80211_AUTH_8021X */ + "wlan_internal", /* IEEE80211_AUTH_AUTO */ + "wlan_xauth", /* IEEE80211_AUTH_WPA */ +}; +static const struct ieee80211_authenticator *authenticators[IEEE80211_AUTH_MAX]; + +static const struct ieee80211_authenticator auth_internal = { + .ia_name = "wlan_internal", + .ia_attach = NULL, + .ia_detach = NULL, + .ia_node_join = NULL, + .ia_node_leave = NULL, +}; + +static int ieee80211_newstate(struct ieee80211com *, enum ieee80211_state, int); + +void +ieee80211_proto_attach(struct ieee80211com *ic) +{ + +#ifdef notdef + ic->ic_rtsthreshold = IEEE80211_RTS_DEFAULT; +#else + ic->ic_rtsthreshold = IEEE80211_RTS_MAX; +#endif + ic->ic_fragthreshold = 2346; /* XXX not used yet */ + ic->ic_fixed_rate = -1; /* no fixed rate */ + ic->ic_protmode = IEEE80211_PROT_CTSONLY; + ic->ic_roaming = IEEE80211_ROAMING_AUTO; + + /* protocol state change handler */ + ic->ic_newstate = ieee80211_newstate; + + /* initialize management frame handlers */ + ic->ic_recv_mgmt = ieee80211_recv_mgmt; + ic->ic_send_mgmt = ieee80211_send_mgmt; + + ieee80211_authenticator_register(IEEE80211_AUTH_OPEN, &auth_internal); + ieee80211_authenticator_register(IEEE80211_AUTH_SHARED, &auth_internal); + ieee80211_authenticator_register(IEEE80211_AUTH_AUTO, &auth_internal); +} + +void +ieee80211_proto_detach(struct ieee80211com *ic) +{ + /* + * This should not be needed as we detach when reseting + * the state but be conservative here since the + * authenticator may do things like spawn kernel threads. + */ + if (ic->ic_auth->ia_detach) + ic->ic_auth->ia_detach(ic); + + ieee80211_authenticator_unregister(IEEE80211_AUTH_OPEN); + ieee80211_authenticator_unregister(IEEE80211_AUTH_SHARED); + ieee80211_authenticator_unregister(IEEE80211_AUTH_AUTO); + + /* + * Detach any ACL'ator. + */ + if (ic->ic_acl != NULL) + ic->ic_acl->iac_detach(ic); +} + +/* + * Simple-minded authenticator module support. + */ + +const struct ieee80211_authenticator * +ieee80211_authenticator_get(int auth) +{ + if (auth >= IEEE80211_AUTH_MAX) + return NULL; + if (authenticators[auth] == NULL) + request_module(auth_modnames[auth]); + return authenticators[auth]; +} + +void +ieee80211_authenticator_register(int type, + const struct ieee80211_authenticator *auth) +{ + if (type >= IEEE80211_AUTH_MAX) + return; + authenticators[type] = auth; +} +EXPORT_SYMBOL(ieee80211_authenticator_register); + +void +ieee80211_authenticator_unregister(int type) +{ + + if (type >= IEEE80211_AUTH_MAX) + return; + authenticators[type] = NULL; +} +EXPORT_SYMBOL(ieee80211_authenticator_unregister); + +/* + * Very simple-minded authenticator backend module support. + */ +/* XXX just one for now */ +static const struct ieee80211_authenticator_backend *backend = NULL; + +void +ieee80211_authenticator_backend_register( + const struct ieee80211_authenticator_backend *be) +{ + printk(KERN_INFO "wlan: %s backend registered\n", be->iab_name); + backend = be; +} +EXPORT_SYMBOL(ieee80211_authenticator_backend_register); + +void +ieee80211_authenticator_backend_unregister( + const struct ieee80211_authenticator_backend * be) +{ + if (backend == be) + backend = NULL; + printk(KERN_INFO "wlan: %s backend unregistered\n", + be->iab_name); +} +EXPORT_SYMBOL(ieee80211_authenticator_backend_unregister); + +const struct ieee80211_authenticator_backend * +ieee80211_authenticator_backend_get(const char *name) +{ + if (backend == NULL) + request_module("wlan_radius"); + return backend && strcmp(backend->iab_name, name) == 0 ? backend : NULL; +} +EXPORT_SYMBOL(ieee80211_authenticator_backend_get); + +/* + * Very simple-minded ACL module support. + */ +/* XXX just one for now */ +static const struct ieee80211_aclator *acl = NULL; + +void +ieee80211_aclator_register(const struct ieee80211_aclator *iac) +{ + printk(KERN_INFO "wlan: %s acl policy registered\n", iac->iac_name); + acl = iac; +} +EXPORT_SYMBOL(ieee80211_aclator_register); + +void +ieee80211_aclator_unregister(const struct ieee80211_aclator *iac) +{ + if (acl == iac) + acl = NULL; + printk(KERN_INFO "wlan: %s acl policy unregistered\n", iac->iac_name); +} +EXPORT_SYMBOL(ieee80211_aclator_unregister); + +const struct ieee80211_aclator * +ieee80211_aclator_get(const char *name) +{ + if (acl == NULL) + request_module("wlan_acl"); + return acl && strcmp(acl->iac_name, name) == 0 ? acl : NULL; +} +EXPORT_SYMBOL(ieee80211_aclator_get); + +void +ieee80211_print_essid(const u_int8_t *essid, int len) +{ + int i; + const u_int8_t *p; + + if (len > IEEE80211_NWID_LEN) + len = IEEE80211_NWID_LEN; + /* determine printable or not */ + for (i = 0, p = essid; i < len; i++, p++) { + if (*p < ' ' || *p > 0x7e) + break; + } + if (i == len) { + printf("\""); + for (i = 0, p = essid; i < len; i++, p++) + printf("%c", *p); + printf("\""); + } else { + printf("0x"); + for (i = 0, p = essid; i < len; i++, p++) + printf("%02x", *p); + } +} + +void +ieee80211_dump_pkt(const u_int8_t *buf, int len, int rate, int rssi) +{ + struct ieee80211_frame *wh; + int i; + + wh = (struct ieee80211_frame *)buf; + switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { + case IEEE80211_FC1_DIR_NODS: + printf("NODS %s", ether_sprintf(wh->i_addr2)); + printf("->%s", ether_sprintf(wh->i_addr1)); + printf("(%s)", ether_sprintf(wh->i_addr3)); + break; + case IEEE80211_FC1_DIR_TODS: + printf("TODS %s", ether_sprintf(wh->i_addr2)); + printf("->%s", ether_sprintf(wh->i_addr3)); + printf("(%s)", ether_sprintf(wh->i_addr1)); + break; + case IEEE80211_FC1_DIR_FROMDS: + printf("FRDS %s", ether_sprintf(wh->i_addr3)); + printf("->%s", ether_sprintf(wh->i_addr1)); + printf("(%s)", ether_sprintf(wh->i_addr2)); + break; + case IEEE80211_FC1_DIR_DSTODS: + printf("DSDS %s", ether_sprintf((u_int8_t *)&wh[1])); + printf("->%s", ether_sprintf(wh->i_addr3)); + printf("(%s", ether_sprintf(wh->i_addr2)); + printf("->%s)", ether_sprintf(wh->i_addr1)); + break; + } + switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { + case IEEE80211_FC0_TYPE_DATA: + printf(" data"); + break; + case IEEE80211_FC0_TYPE_MGT: + printf(" %s", ieee80211_mgt_subtype_name[ + (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) + >> IEEE80211_FC0_SUBTYPE_SHIFT]); + break; + default: + printf(" type#%d", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK); + break; + } + if (wh->i_fc[1] & IEEE80211_FC1_WEP) { + int i; + printf(" WEP [IV"); + for (i = 0; i < IEEE80211_WEP_IVLEN; i++) + printf(" %.02x", buf[sizeof(*wh)+i]); + printf(" KID %u]", buf[sizeof(*wh)+i] >> 6); + } + if (rate >= 0) + printf(" %dM", rate / 2); + if (rssi >= 0) + printf(" +%d", rssi); + printf("\n"); + if (len > 0) { + for (i = 0; i < len; i++) { + if ((i & 1) == 0) + printf(" "); + printf("%02x", buf[i]); + } + printf("\n"); + } +} +EXPORT_SYMBOL(ieee80211_dump_pkt); + +int +ieee80211_fix_rate(struct ieee80211com *ic, struct ieee80211_node *ni, int flags) +{ +#define RV(v) ((v) & IEEE80211_RATE_VAL) + int i, j, ignore, error; + int okrate, badrate, fixedrate; + struct ieee80211_rateset *srs, *nrs; + u_int8_t r; + + /* + * If the fixed rate check was requested but no + * fixed has been defined then just remove it. + */ + if ((flags & IEEE80211_F_DOFRATE) && ic->ic_fixed_rate < 0) + flags &= ~IEEE80211_F_DOFRATE; + error = 0; + okrate = badrate = fixedrate = 0; + srs = &ic->ic_sup_rates[ieee80211_chan2mode(ic, ni->ni_chan)]; + nrs = &ni->ni_rates; + for (i = 0; i < nrs->rs_nrates; ) { + ignore = 0; + if (flags & IEEE80211_F_DOSORT) { + /* + * Sort rates. + */ + for (j = i + 1; j < nrs->rs_nrates; j++) { + if (RV(nrs->rs_rates[i]) > RV(nrs->rs_rates[j])) { + r = nrs->rs_rates[i]; + nrs->rs_rates[i] = nrs->rs_rates[j]; + nrs->rs_rates[j] = r; + } + } + } + r = nrs->rs_rates[i] & IEEE80211_RATE_VAL; + badrate = r; + if (flags & IEEE80211_F_DOFRATE) { + /* + * Check any fixed rate is included. + */ + if (r == RV(srs->rs_rates[ic->ic_fixed_rate])) + fixedrate = r; + } + if (flags & IEEE80211_F_DONEGO) { + /* + * Check against supported rates. + */ + for (j = 0; j < srs->rs_nrates; j++) { + if (r == RV(srs->rs_rates[j])) { + /* + * Overwrite with the supported rate + * value so any basic rate bit is set. + * This insures that response we send + * to stations have the necessary basic + * rate bit set. + */ + nrs->rs_rates[i] = srs->rs_rates[j]; + break; + } + } + if (j == srs->rs_nrates) { + /* + * A rate in the node's rate set is not + * supported. If this is a basic rate and we + * are operating as an AP then this is an error. + * Otherwise we just discard/ignore the rate. + * Note that this is important for 11b stations + * when they want to associate with an 11g AP. + */ + if (ic->ic_opmode == IEEE80211_M_HOSTAP && + (nrs->rs_rates[i] & IEEE80211_RATE_BASIC)) + error++; + ignore++; + } + } + if (flags & IEEE80211_F_DODEL) { + /* + * Delete unacceptable rates. + */ + if (ignore) { + nrs->rs_nrates--; + for (j = i; j < nrs->rs_nrates; j++) + nrs->rs_rates[j] = nrs->rs_rates[j + 1]; + nrs->rs_rates[j] = 0; + continue; + } + } + if (!ignore) + okrate = nrs->rs_rates[i]; + i++; + } + if (okrate == 0 || error != 0 || + ((flags & IEEE80211_F_DOFRATE) && fixedrate == 0)) + return badrate | IEEE80211_RATE_BASIC; + else + return RV(okrate); +#undef RV +} + +/* + * Check if the specified rate set supports ERP. + * NB: the rate set is assumed to be sorted. + */ +int +ieee80211_iserp_rateset(struct ieee80211com *ic, struct ieee80211_rateset *rs) +{ +#define N(a) (sizeof(a) / sizeof(a[0])) + static const int rates[] = { 2, 4, 11, 22, 12, 24, 48 }; + int i, j; + + if (rs->rs_nrates < N(rates)) + return 0; + for (i = 0; i < N(rates); i++) { + for (j = 0; j < rs->rs_nrates; j++) { + int r = rs->rs_rates[j] & IEEE80211_RATE_VAL; + if (rates[i] == r) + goto next; + if (r > rates[i]) + return 0; + } + return 0; + next: + ; + } + return 1; +#undef N +} + +static int +ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) +{ + struct net_device *dev = ic->ic_dev; + struct ieee80211_node *ni; + enum ieee80211_state ostate; + + ostate = ic->ic_state; + IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE, ("%s: %s -> %s\n", __func__, + ieee80211_state_name[ostate], ieee80211_state_name[nstate])); + ic->ic_state = nstate; /* state transition */ + ni = ic->ic_bss; /* NB: no reference held */ + switch (nstate) { + case IEEE80211_S_INIT: + switch (ostate) { + case IEEE80211_S_INIT: + break; + case IEEE80211_S_RUN: + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_DISASSOC, + IEEE80211_REASON_ASSOC_LEAVE); + ieee80211_sta_leave(ic, ni); + break; + case IEEE80211_M_HOSTAP: + IEEE80211_NODE_LOCK(ic); + TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { + if (ni->ni_associd == 0) + continue; + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_DISASSOC, + IEEE80211_REASON_ASSOC_LEAVE); + } + IEEE80211_NODE_UNLOCK(ic); + break; + default: + break; + } + goto reset; + case IEEE80211_S_ASSOC: + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_AUTH_LEAVE); + break; + case IEEE80211_M_HOSTAP: + IEEE80211_NODE_LOCK(ic); + TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_AUTH_LEAVE); + } + IEEE80211_NODE_UNLOCK(ic); + break; + default: + break; + } + goto reset; + case IEEE80211_S_AUTH: + case IEEE80211_S_SCAN: + reset: + ic->ic_mgt_timer = 0; + ic->ic_inact_timer = 0; + ieee80211_free_allnodes(ic); + break; + } + if (ic->ic_auth->ia_detach != NULL) + ic->ic_auth->ia_detach(ic); + break; + case IEEE80211_S_SCAN: + ic->ic_flags &= ~IEEE80211_F_SIBSS; + /* initialize bss for probe request */ + IEEE80211_ADDR_COPY(ni->ni_macaddr, dev->broadcast); + IEEE80211_ADDR_COPY(ni->ni_bssid, dev->broadcast); + ni->ni_rates = ic->ic_sup_rates[ + ieee80211_chan2mode(ic, ni->ni_chan)]; + ni->ni_associd = 0; + ni->ni_rstamp = 0; + switch (ostate) { + case IEEE80211_S_INIT: + if ((ic->ic_opmode == IEEE80211_M_HOSTAP || + ic->ic_opmode == IEEE80211_M_IBSS || + ic->ic_opmode == IEEE80211_M_AHDEMO) && + ic->ic_des_chan != IEEE80211_CHAN_ANYC) { + /* + * AP operation and we already have a channel; + * bypass the scan and startup immediately. + */ + ieee80211_create_ibss(ic, ic->ic_des_chan); + } else { + ieee80211_begin_scan(ic, arg); + } + break; + case IEEE80211_S_SCAN: + /* + * Scan next. If doing an active scan and the + * channel is not marked passive-only then send + * a probe request. Otherwise just listen for + * beacons on the channel. + */ + if ((ic->ic_flags & IEEE80211_F_ASCAN) && + (ni->ni_chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0) { + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_PROBE_REQ, 0); + } + break; + case IEEE80211_S_RUN: + /* beacon miss */ + IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE, + ("no recent beacons from %s; rescanning\n", + ether_sprintf(ic->ic_bss->ni_bssid))); + ieee80211_sta_leave(ic, ni); + /* XXX this clears the scan set */ + ieee80211_free_allnodes(ic); + /* FALLTHRU */ + case IEEE80211_S_AUTH: + case IEEE80211_S_ASSOC: + /* XXX doesn't work, ni_macaddr rewritten above */ + /* timeout restart scan */ + ni = ieee80211_find_node(ic, ic->ic_bss->ni_macaddr); + if (ni != NULL) { + ni->ni_fails++; + ieee80211_unref_node(&ni); + } + ieee80211_begin_scan(ic, arg); + break; + } + break; + case IEEE80211_S_AUTH: + switch (ostate) { + case IEEE80211_S_INIT: + case IEEE80211_S_SCAN: + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_AUTH, 1); + break; + case IEEE80211_S_AUTH: + case IEEE80211_S_ASSOC: + switch (arg) { + case IEEE80211_FC0_SUBTYPE_AUTH: + /* ??? */ + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_AUTH, 2); + break; + case IEEE80211_FC0_SUBTYPE_DEAUTH: + /* ignore and retry scan on timeout */ + break; + } + break; + case IEEE80211_S_RUN: + switch (arg) { + case IEEE80211_FC0_SUBTYPE_AUTH: + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_AUTH, 2); + ic->ic_state = ostate; /* stay RUN */ + break; + case IEEE80211_FC0_SUBTYPE_DEAUTH: + /* try to reauth */ + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_AUTH, 1); + ieee80211_sta_leave(ic, ni); + break; + } + break; + } + break; + case IEEE80211_S_ASSOC: + switch (ostate) { + case IEEE80211_S_INIT: + case IEEE80211_S_SCAN: + case IEEE80211_S_ASSOC: + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + ("%s: invalid transition\n", __func__)); + break; + case IEEE80211_S_AUTH: + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0); + break; + case IEEE80211_S_RUN: + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 1); + ieee80211_sta_leave(ic, ni); + break; + } + break; + case IEEE80211_S_RUN: + if (ic->ic_flags & IEEE80211_F_WPA) { + /* XXX validate prerequisites */ + } + switch (ostate) { + case IEEE80211_S_INIT: + if (ic->ic_opmode == IEEE80211_M_MONITOR) + break; + /* fall thru... */ + case IEEE80211_S_AUTH: + case IEEE80211_S_RUN: + IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, + ("%s: invalid transition\n", __func__)); + break; + case IEEE80211_S_SCAN: /* adhoc/hostap mode */ + case IEEE80211_S_ASSOC: /* infra mode */ + KASSERT(ni->ni_txrate < ni->ni_rates.rs_nrates, + ("%s: bogus xmit rate %u setup\n", __func__, + ni->ni_txrate)); + if (ieee80211_msg_debug(ic)) { + if_printf(ic->ic_dev, " "); + if (ic->ic_opmode == IEEE80211_M_STA) + printf("associated "); + else + printf("synchronized "); + printf("with %s ssid ", + ether_sprintf(ni->ni_bssid)); + ieee80211_print_essid(ic->ic_bss->ni_essid, + ni->ni_esslen); + printf(" channel %d start %uMb\n", + ieee80211_chan2ieee(ic, ni->ni_chan), + IEEE80211_RATE2MBS(ni->ni_rates.rs_rates[ni->ni_txrate])); + } + ic->ic_mgt_timer = 0; + if (ic->ic_opmode == IEEE80211_M_STA) + ieee80211_notify_node_join(ic, ni, + arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP); + break; + } + /* + * Start/stop the authenticator when operating as an + * AP. We delay until here to allow configuration to + * happen out of order. + */ + if (ic->ic_opmode == IEEE80211_M_HOSTAP && /* XXX IBSS/AHDEMO */ + ic->ic_auth->ia_attach != NULL) { + /* XXX check failure */ + ic->ic_auth->ia_attach(ic); + } else if (ic->ic_auth->ia_detach != NULL) { + ic->ic_auth->ia_detach(ic); + } + /* + * When 802.1x is not in use mark the port authorized + * at this point so traffic can flow. + */ + if (ni->ni_authmode != IEEE80211_AUTH_8021X) + ieee80211_node_authorize(ic, ni); + break; + } + return 0; +} diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_proto.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_proto.h --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_proto.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_proto.h 2005-02-24 13:06:17.342125616 -0800 @@ -0,0 +1,229 @@ +/* $NetBSD: ieee80211_proto.h,v 1.3 2003/10/13 04:23:56 dyoung Exp $ */ +/*- + * Copyright (c) 2001 Atsushi Onoe + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/net80211/ieee80211_proto.h,v 1.4 2003/08/19 22:17:03 sam Exp $ + */ +#ifndef _NET80211_IEEE80211_PROTO_H_ +#define _NET80211_IEEE80211_PROTO_H_ + +/* + * 802.11 protocol implementation definitions. + */ + +enum ieee80211_state { + IEEE80211_S_INIT = 0, /* default state */ + IEEE80211_S_SCAN = 1, /* scanning */ + IEEE80211_S_AUTH = 2, /* try to authenticate */ + IEEE80211_S_ASSOC = 3, /* try to assoc */ + IEEE80211_S_RUN = 4, /* associated */ +}; +#define IEEE80211_S_MAX (IEEE80211_S_RUN+1) + +#define IEEE80211_SEND_MGMT(_ic,_ni,_type,_arg) \ + ((*(_ic)->ic_send_mgmt)(_ic, _ni, _type, _arg)) + +/* + * Transmitted frames have the following information + * held in the sk_buff control buffer. This is used to + * communicate various inter-procedural state that needs + * to be associated with the frame for the duration of + * it's existence. + */ +struct ieee80211_cb { + struct ieee80211_node *ni; + struct ieee80211_key *key; + u_int8_t flags; +}; + +extern const char *ieee80211_mgt_subtype_name[]; +extern const char *ieee80211_ctl_subtype_name[]; + +extern void ieee80211_proto_attach(struct ieee80211com *); +extern void ieee80211_proto_detach(struct ieee80211com *); + +struct ieee80211_node; +extern void ieee80211_input(struct ieee80211com *, struct sk_buff *, + struct ieee80211_node *, int, u_int32_t); +extern void ieee80211_recv_mgmt(struct ieee80211com *, struct sk_buff *, + struct ieee80211_node *, int, int, u_int32_t); +extern int ieee80211_send_mgmt(struct ieee80211com *, struct ieee80211_node *, + int, int); +extern void ieee80211_pwrsave(struct ieee80211com *, struct ieee80211_node *, + struct sk_buff *); +extern struct sk_buff *ieee80211_encap(struct ieee80211com *, struct sk_buff *, + struct ieee80211_node **); + +/* + * Return the size of the 802.11 header for a management or data frame. + */ +static inline int +ieee80211_hdrsize(const void *data) +{ + const struct ieee80211_frame *wh = data; + int size = sizeof(struct ieee80211_frame); + + /* NB: we don't handle control frames */ + KASSERT((wh->i_fc[0]&IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_CTL, + ("%s: control frame", __func__)); + if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS) + size += IEEE80211_ADDR_LEN; + if (IEEE80211_QOS_HAS_SEQ(wh)) + size += sizeof(u_int16_t); + return size; +} + +/* + * Return the size of the 802.11 header; handles any type of frame. + */ +static inline int +ieee80211_anyhdrsize(const void *data) +{ + const struct ieee80211_frame *wh = data; + + if ((wh->i_fc[0]&IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL) { + switch (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) { + case IEEE80211_FC0_SUBTYPE_CTS: + case IEEE80211_FC0_SUBTYPE_ACK: + return sizeof(struct ieee80211_frame_ack); + } + return sizeof(struct ieee80211_frame_min); + } else + return ieee80211_hdrsize(data); +} + +/* + * Template for an in-kernel authenticator. Authenticators + * register with the protocol code and are typically loaded + * as separate modules as needed. + */ +struct ieee80211_authenticator { + const char *ia_name; /* printable name */ + int (*ia_attach)(struct ieee80211com *); + void (*ia_detach)(struct ieee80211com *); + void (*ia_node_join)(struct ieee80211com *, + struct ieee80211_node *); + void (*ia_node_leave)(struct ieee80211com *, + struct ieee80211_node *); +}; +extern void ieee80211_authenticator_register(int type, + const struct ieee80211_authenticator *); +extern void ieee80211_authenticator_unregister(int type); +extern const struct ieee80211_authenticator * + ieee80211_authenticator_get(int auth); + +struct eapolcom; +/* + * Template for an in-kernel authenticator backend. Backends + * register with the protocol code and are typically loaded + * as separate modules as needed. + */ +struct ieee80211_authenticator_backend { + const char *iab_name; /* printable name */ + int (*iab_attach)(struct eapolcom *); + void (*iab_detach)(struct eapolcom *); +}; +extern void ieee80211_authenticator_backend_register( + const struct ieee80211_authenticator_backend *); +extern void ieee80211_authenticator_backend_unregister( + const struct ieee80211_authenticator_backend *); +extern const struct ieee80211_authenticator_backend * + ieee80211_authenticator_backend_get(const char *name); + +/* + * Template for an MAC ACL policy module. Such modules + * register with the protocol code and are passed the sender's + * address of each received frame for validation. + */ +struct ieee80211_aclator { + const char *iac_name; /* printable name */ + int (*iac_attach)(struct ieee80211com *); + void (*iac_detach)(struct ieee80211com *); + int (*iac_check)(struct ieee80211com *, + const u_int8_t mac[IEEE80211_ADDR_LEN]); + int (*iac_add)(struct ieee80211com *, + const u_int8_t mac[IEEE80211_ADDR_LEN]); + int (*iac_remove)(struct ieee80211com *, + const u_int8_t mac[IEEE80211_ADDR_LEN]); + int (*iac_flush)(struct ieee80211com *); + int (*iac_setpolicy)(struct ieee80211com *, int); + int (*iac_getpolicy)(struct ieee80211com *); +}; +extern void ieee80211_aclator_register(const struct ieee80211_aclator *); +extern void ieee80211_aclator_unregister(const struct ieee80211_aclator *); +extern const struct ieee80211_aclator *ieee80211_aclator_get(const char *name); + +/* flags for ieee80211_fix_rate() */ +#define IEEE80211_F_DOSORT 0x00000001 /* sort rate list */ +#define IEEE80211_F_DOFRATE 0x00000002 /* use fixed rate */ +#define IEEE80211_F_DONEGO 0x00000004 /* calc negotiated rate */ +#define IEEE80211_F_DODEL 0x00000008 /* delete ignore rate */ +extern int ieee80211_fix_rate(struct ieee80211com *, + struct ieee80211_node *, int); + +extern int ieee80211_iserp_rateset(struct ieee80211com *, + struct ieee80211_rateset *); +#define ieee80211_new_state(_ic, _nstate, _arg) \ + (((_ic)->ic_newstate)((_ic), (_nstate), (_arg))) +extern void ieee80211_print_essid(const u_int8_t *, int); +extern void ieee80211_dump_pkt(const u_int8_t *, int, int, int); + +extern const char *ieee80211_state_name[IEEE80211_S_MAX]; + +/* + * Beacon frames constructed by ieee80211_beacon_alloc + * have the following structure filled in so drivers + * can update the frame later w/ minimal overhead. + */ +struct ieee80211_beacon_offsets { + u_int16_t *bo_caps; /* capabilities */ + u_int8_t *bo_tim; /* start of atim/dtim */ + u_int8_t *bo_trailer; /* start of fixed-size trailer */ + u_int16_t bo_tim_len; /* atim/dtim length in bytes */ + u_int16_t bo_trailer_len; /* trailer length in bytes */ +}; +extern struct sk_buff *ieee80211_beacon_alloc(struct ieee80211com *, + struct ieee80211_node *, struct ieee80211_beacon_offsets *); +extern int ieee80211_beacon_update(struct ieee80211com *, + struct ieee80211_node *, struct ieee80211_beacon_offsets *, + struct sk_buff **); + +/* + * Notification methods called from the 802.11 state machine. + * Note that while these are defined here, their implementation + * is OS-specific. + */ +extern void ieee80211_notify_node_join(struct ieee80211com *, + struct ieee80211_node *, int newassoc); +extern void ieee80211_notify_node_leave(struct ieee80211com *, + struct ieee80211_node *); +extern void ieee80211_notify_scan_done(struct ieee80211com *); +#endif /* _NET80211_IEEE80211_PROTO_H_ */ diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_radiotap.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_radiotap.h --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_radiotap.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_radiotap.h 2005-02-24 13:06:17.342125616 -0800 @@ -0,0 +1,202 @@ +/* $FreeBSD: src/sys/net80211/ieee80211_radiotap.h,v 1.2 2003/12/28 06:57:28 sam Exp $ */ +/* $NetBSD: ieee80211_radiotap.h,v 1.3 2003/11/16 09:02:42 dyoung Exp $ */ + +/*- + * Copyright (c) 2003, 2004 David Young. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of David Young may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY DAVID YOUNG ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DAVID + * YOUNG BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ +#ifndef _NET_IF_IEEE80211RADIOTAP_H_ +#define _NET_IF_IEEE80211RADIOTAP_H_ + +/* A generic radio capture format is desirable. There is one for + * Linux, but it is neither rigidly defined (there were not even + * units given for some fields) nor easily extensible. + * + * I suggest the following extensible radio capture format. It is + * based on a bitmap indicating which fields are present. + * + * I am trying to describe precisely what the application programmer + * should expect in the following, and for that reason I tell the + * units and origin of each measurement (where it applies), or else I + * use sufficiently weaselly language ("is a monotonically nondecreasing + * function of...") that I cannot set false expectations for lawyerly + * readers. + */ +#if defined(__KERNEL__) || defined(_KERNEL) +#ifndef DLT_IEEE802_11_RADIO +#define DLT_IEEE802_11_RADIO 127 /* 802.11 plus WLAN header */ +#endif +#endif /* defined(__KERNEL__) || defined(_KERNEL) */ + +/* The radio capture header precedes the 802.11 header. */ +struct ieee80211_radiotap_header { + u_int8_t it_version; /* Version 0. Only increases + * for drastic changes, + * introduction of compatible + * new fields does not count. + */ + u_int8_t it_pad; + u_int16_t it_len; /* length of the whole + * header in bytes, including + * it_version, it_pad, + * it_len, and data fields. + */ + u_int32_t it_present; /* A bitmap telling which + * fields are present. Set bit 31 + * (0x80000000) to extend the + * bitmap by another 32 bits. + * Additional extensions are made + * by setting bit 31. + */ +} __attribute__((__packed__)); + +/* Name Data type Units + * ---- --------- ----- + * + * IEEE80211_RADIOTAP_TSFT u_int64_t microseconds + * + * Value in microseconds of the MAC's 64-bit 802.11 Time + * Synchronization Function timer when the first bit of the + * MPDU arrived at the MAC. For received frames, only. + * + * IEEE80211_RADIOTAP_CHANNEL 2 x u_int16_t MHz, bitmap + * + * Tx/Rx frequency in MHz, followed by flags (see below). + * + * IEEE80211_RADIOTAP_FHSS u_int16_t see below + * + * For frequency-hopping radios, the hop set (first byte) + * and pattern (second byte). + * + * IEEE80211_RADIOTAP_RATE u_int8_t 500kb/s + * + * Tx/Rx data rate + * + * IEEE80211_RADIOTAP_DBM_ANTSIGNAL int8_t decibels from + * one milliwatt (dBm) + * + * RF signal power at the antenna, decibel difference from + * one milliwatt. + * + * IEEE80211_RADIOTAP_DBM_ANTNOISE int8_t decibels from + * one milliwatt (dBm) + * + * RF noise power at the antenna, decibel difference from one + * milliwatt. + * + * IEEE80211_RADIOTAP_DB_ANTSIGNAL u_int8_t decibel (dB) + * + * RF signal power at the antenna, decibel difference from an + * arbitrary, fixed reference. + * + * IEEE80211_RADIOTAP_DB_ANTNOISE u_int8_t decibel (dB) + * + * RF noise power at the antenna, decibel difference from an + * arbitrary, fixed reference point. + * + * IEEE80211_RADIOTAP_BARKER_CODE_LOCK u_int16_t unitless + * + * Quality of Barker code lock. Unitless. Monotonically + * nondecreasing with "better" lock strength. Called "Signal + * Quality" in datasheets. (Is there a standard way to measure + * this?) + * + * IEEE80211_RADIOTAP_TX_ATTENUATION u_int16_t unitless + * + * Transmit power expressed as unitless distance from max + * power set at factory calibration. 0 is max power. + * Monotonically nondecreasing with lower power levels. + * + * IEEE80211_RADIOTAP_DB_TX_ATTENUATION u_int16_t decibels (dB) + * + * Transmit power expressed as decibel distance from max power + * set at factory calibration. 0 is max power. Monotonically + * nondecreasing with lower power levels. + * + * IEEE80211_RADIOTAP_DBM_TX_POWER int8_t decibels from + * one milliwatt (dBm) + * + * Transmit power expressed as dBm (decibels from a 1 milliwatt + * reference). This is the absolute power level measured at + * the antenna port. + * + * IEEE80211_RADIOTAP_FLAGS u_int8_t bitmap + * + * Properties of transmitted and received frames. See flags + * defined below. + * + * IEEE80211_RADIOTAP_ANTENNA u_int8_t antenna index + * + * Unitless indication of the Rx/Tx antenna for this packet. + * The first antenna is antenna 0. + */ +enum ieee80211_radiotap_type { + IEEE80211_RADIOTAP_TSFT = 0, + IEEE80211_RADIOTAP_FLAGS = 1, + IEEE80211_RADIOTAP_RATE = 2, + IEEE80211_RADIOTAP_CHANNEL = 3, + IEEE80211_RADIOTAP_FHSS = 4, + IEEE80211_RADIOTAP_DBM_ANTSIGNAL = 5, + IEEE80211_RADIOTAP_DBM_ANTNOISE = 6, + IEEE80211_RADIOTAP_LOCK_QUALITY = 7, + IEEE80211_RADIOTAP_TX_ATTENUATION = 8, + IEEE80211_RADIOTAP_DB_TX_ATTENUATION = 9, + IEEE80211_RADIOTAP_DBM_TX_POWER = 10, + IEEE80211_RADIOTAP_ANTENNA = 11, + IEEE80211_RADIOTAP_DB_ANTSIGNAL = 12, + IEEE80211_RADIOTAP_DB_ANTNOISE = 13, + IEEE80211_RADIOTAP_EXT = 31, +}; + +#if !defined(__KERNEL__) && !defined(_KERNEL)) +/* Channel flags. */ +#define IEEE80211_CHAN_TURBO 0x0010 /* Turbo channel */ +#define IEEE80211_CHAN_CCK 0x0020 /* CCK channel */ +#define IEEE80211_CHAN_OFDM 0x0040 /* OFDM channel */ +#define IEEE80211_CHAN_2GHZ 0x0080 /* 2 GHz spectrum channel. */ +#define IEEE80211_CHAN_5GHZ 0x0100 /* 5 GHz spectrum channel */ +#define IEEE80211_CHAN_PASSIVE 0x0200 /* Only passive scan allowed */ +#define IEEE80211_CHAN_DYN 0x0400 /* Dynamic CCK-OFDM channel */ +#define IEEE80211_CHAN_GFSK 0x0800 /* GFSK channel (FHSS PHY) */ +#endif /* !defined(__KERNEL__) && !defined(_KERNEL) */ + +/* For IEEE80211_RADIOTAP_FLAGS */ +#define IEEE80211_RADIOTAP_F_CFP 0x01 /* sent/received + * during CFP + */ +#define IEEE80211_RADIOTAP_F_SHORTPRE 0x02 /* sent/received + * with short + * preamble + */ +#define IEEE80211_RADIOTAP_F_WEP 0x04 /* sent/received + * with WEP encryption + */ +#define IEEE80211_RADIOTAP_F_FRAG 0x08 /* sent/received + * with fragmentation + */ + +#endif /* _NET_IF_IEEE80211RADIOTAP_H_ */ diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_radius.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_radius.c --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_radius.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_radius.c 2005-02-24 13:06:17.392118016 -0800 @@ -0,0 +1,1834 @@ +/*- + * Copyright (c) 2004-2005 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EXPORT_SYMTAB +#define EXPORT_SYMTAB +#endif + +/* + * Radius client support for the 802.1x+WPA authenticator. + * + * This support is optional (it is not present when the authenticator + * uses only pre-shared keys). We override the authenticator's node + * management methods to allocate additional space for the radius client + * and supply methods for the 802.1x backend state machine to communicate + * with a radius server. This module creates one thread for receiving + * messages from the radius server. All other communication happens + * asynchronously through callbacks from the 802.1x authenticator code. + * Likewise we communicate state changes to the authenticator by setting + * the state variables in a node's state block and then running the + * authenticator state machine for that node. + * + * To use this support the radius server state must be configured before + * enabling 802.1x authentication. Specifically you must set the radius + * server's IP address and port and the shared secret used to communicate. + * These are all set using sysctl's (or through /proc). To change any + * of these settings while the client is running the device must be marked + * down and up again, or similar (e.g. change authentication mode). + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include /* XXX for sk_allocation/allocation */ + +#include "if_media.h" +#include "if_ethersubr.h" /* for ETHER_MAX_LEN */ +#include "if_llc.h" /* for LLC_SNAPFRAMELEN */ + +#include +#include +#include + +#include "rc4.h" /* XXX */ +#define arc4_ctxlen() sizeof (struct rc4_state) +#define arc4_setkey(_c,_k,_l) rc4_init(_c,_k,_l) +#define arc4_encrypt(_c,_d,_s,_l) rc4_crypt_skip(_c,_s,_d,_l,0) + +struct auth_hdr { + u_int8_t ah_code; + u_int8_t ah_id; + u_int16_t ah_len; + u_int8_t ah_auth[16]; /* MD5 hash */ + /* variable length attributes follow */ +} __attribute__((__packed__)); + +enum { + RAD_ACCESS_REQUEST = 1, + RAD_ACCESS_ACCEPT = 2, + RAD_ACCESS_REJECT = 3, + RAD_ACCESS_CHALLENGE = 11, +}; +enum { + RAD_ATTR_USER_NAME = 1, + RAD_ATTR_USER_PASSWD = 2, + RAD_ATTR_NAS_IP_ADDRESS = 4, + RAD_ATTR_NAS_PORT = 5, + RAD_ATTR_SERVICE_TYPE = 6, +#define RAD_SERVICE_TYPE_FRAMED 2 + RAD_ATTR_FRAMED_MTU = 12, + RAD_ATTR_REPLY_MESSAGE = 18, + RAD_ATTR_STATE = 24, + RAD_ATTR_CLASS = 25, + RAD_ATTR_VENDOR_SPECIFIC = 26, + RAD_ATTR_SESSION_TIMEOUT = 27, + RAD_ATTR_CALLED_STATION_ID = 30, + RAD_ATTR_CALLING_STATION_ID = 31, + RAD_ATTR_NAS_IDENTIFIER = 32, + RAD_ATTR_NAS_PORT_TYPE = 61, +#define RAD_NAS_PORT_TYPE_WIRELESS 19 /* IEEE 802.11 */ + RAD_ATTR_CONNECT_INFO = 77, + RAD_ATTR_EAP_MESSAGE = 79, + RAD_ATTR_MESSAGE_AUTHENTICATOR = 80, + RAD_ATTR_NAS_PORT_ID = 87, +}; + +#define RAD_MAX_ATTR_LEN (255 - 2) +#define RAD_MAX_SECRET 48 /* NB: must be a multiple of 16 */ + +#ifndef TRUE +enum { TRUE = 1, FALSE = 0 }; /* for consistency with spec */ +#endif + +static struct sockaddr_in radius_serveraddr; /* IP address of radius server */ +static struct sockaddr_in radius_clientaddr; /* IP address of client */ +static u_int8_t radius_secret[RAD_MAX_SECRET+1];/* backend shared secret */ +static u_int radius_secretlen = 0; + +static int radius_check_auth(struct radiuscom *, + struct eapol_auth_radius_node *, struct auth_hdr *); +static int radius_check_msg_auth(struct radiuscom *, + struct eapol_auth_radius_node *, struct auth_hdr *); +static struct sk_buff *radius_create_reply(struct radiuscom *, + struct auth_hdr *, struct eapol_auth_node *); +static int radius_extract_key(struct radiuscom *, + struct eapol_auth_radius_node *, struct auth_hdr *); +static void radius_dumppkt(const char *tag, const struct eapol_auth_node *, + const struct auth_hdr *); +static u_int8_t * radius_find_attr(struct auth_hdr *, int attr); +static void sendRespToServer(struct eapol_auth_node *); +static struct eapol_auth_radius_node *radius_get_reply(struct radiuscom *, + u_int8_t id); +static void radius_add_reply(struct radiuscom *, + struct eapol_auth_radius_node *); +static void radius_cleanup(struct radiuscom *); + +#ifdef IEEE80211_DEBUG +static void radius_dumpbytes(const char *tag, const void *data, u_int len); +#endif + +static int +radius_get_integer(u_int8_t *ap) +{ + u_int32_t v; + + if (ap[1] >= 2+sizeof(v)) + memcpy(&v, ap+2, sizeof(v)); + else + v = 0; + return ntohl(v); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + +#include + +static void +_daemonize(const char *procname) +{ + lock_kernel(); + + daemonize(); + current->tty = NULL; + strcpy(current->comm, procname); + + unlock_kernel(); +} + +static int +allow_signal(int sig) +{ + if (sig < 1 || sig > _NSIG) + return -EINVAL; + +#ifdef INIT_SIGHAND + /* + * "The test on INIT_SIGHAND is not perfect but will at + * least allow this to compile on RedHat kernels." + * + * http://www.mail-archive.com/jfs-discussion@www-124.ibm.com/msg00803.html + */ + spin_lock_irq(¤t->sighand->siglock); + sigdelset(¤t->blocked, sig); + /* XXX current->mm? */ + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); +#else + spin_lock_irq(¤t->sigmask_lock); + sigdelset(¤t->blocked, sig); + /* XXX current->mm? */ + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); +#endif + + return 0; +} +#else +#define _daemonize(s) daemonize(s) +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) */ + +/* + * Thread to handle responses from the radius server. + * This just listens for packets, decodes them, and + * applies their contents to the appropriate session + * state, then kicks the state machine as necessary. + */ +static int +radiusd(void *arg) +{ + struct radiuscom *rc = arg; + struct eapolcom *ec = rc->rc_ec; + mm_segment_t oldfs; + struct auth_hdr *ahp; + struct msghdr msg; + struct iovec iov; + struct sockaddr_in sin; + struct eapol_auth_node *ean; + struct eapol_auth_radius_node *ern; + u_int8_t *ap; + int len; + + /* XXX return what */ + _MOD_INC_USE(THIS_MODULE, return -1); + + _daemonize("kradiusd"); + allow_signal(SIGKILL); + + for (;;) { + msg.msg_name = &sin; + msg.msg_namelen = sizeof(sin); + iov.iov_base = rc->rc_buf; + iov.iov_len = sizeof(rc->rc_buf); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + /* + * Gag, override task limits to tell sock_recvmsg & co + * that our recv buffer is in kernel space and not user! + */ + oldfs = get_fs(); + set_fs(KERNEL_DS); + len = sock_recvmsg(rc->rc_sock, &msg, iov.iov_len, 0); + set_fs(oldfs); + /* + * We receive a SIGKILL to detach; ignore any + * message that might have been received. + */ + if (signal_pending(current)) + break; + if (len <= 0) { + printf("%s: sock_recvmsg returns %d\n", __func__, len); + continue; + } + /* + * Warn about msgs not from the server (note + * this is not meant to be security measure...). + */ + if (msg.msg_namelen != sizeof(sin) || + sin.sin_addr.s_addr != rc->rc_server.sin_addr.s_addr) { + /* + * NB: some versions of Linux apparently + * do not return the sender's address in + * which case we'll want to conditionally + * disable this complaint. + */ + sin.sin_addr.s_addr = ntohl(sin.sin_addr.s_addr); + printf("%s: Warning, bad sender address, " + "len %u addr %u.%u.%u.%u\n", + __func__, msg.msg_namelen, + (sin.sin_addr.s_addr>>24)&0xff, + (sin.sin_addr.s_addr>>16)&0xff, + (sin.sin_addr.s_addr>> 8)&0xff, + (sin.sin_addr.s_addr>> 0)&0xff); + eapolstats.rs_addrmismatch++; + /* fall thru... */ + } + /* + * Process the radius packet. + */ + if (len < sizeof(struct auth_hdr)) { + printf("%s: msg too short, len %u, discarded\n", + __func__, len); + eapolstats.rs_tooshort++; + continue; + } + ahp = (struct auth_hdr *)rc->rc_buf; + if (ahp->ah_code == RAD_ACCESS_REQUEST) { + printf("%s: bad code %u, msg discarded\n", + __func__, ahp->ah_code); + eapolstats.rs_request++; + continue; + } + /* + * NB: many places we assume the packet contents + * are not altered so don't do "optimizations" + * like fixing the byte order of ah_len. + */ + if (ntohs(ahp->ah_len) > len) { + printf("%s: auth len %u > msg len %u, msg discarded\n", + __func__, ahp->ah_len, len); + eapolstats.rs_badlen++; + continue; + } + /* + * We use the EAPOL state lock to synchronize our + * work (which should not take long to complete). + */ + EAPOL_LOCK(ec); + /* + * Check the radius server response against the + * list of outstanding requests. Only one should + * have a matching id. + */ + ern = radius_get_reply(rc, ahp->ah_id); + if (ern == NULL) { + EAPOL_UNLOCK(ec); + printf("%s: RADIUS msg id %u has no pending request\n", + __func__, ahp->ah_id); + eapolstats.rs_badid++; + continue; + } + ean = &ern->ern_base; +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_dumpradius(ean->ean_ic)) + radius_dumppkt("RECV", ean, ahp); +#endif + /* + * Check the response authenticator. + */ + if (!radius_check_auth(rc, ern, ahp)) { + EAPOL_UNLOCK(ec); + IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_RADIUS, + ("[%s] BAD response authenticator\n", + ether_sprintf(ean->ean_node->ni_macaddr))); + eapolstats.rs_badrespauth++; + /* XXX put ean back on reply list? */ + continue; + } + if (ahp->ah_code != RAD_ACCESS_REJECT) { + /* + * Check the Message-Authenticator hash + * before trusting the message contents. + */ + if (!radius_check_msg_auth(rc, ern, ahp)) { + EAPOL_UNLOCK(ec); + IEEE80211_DPRINTF(ean->ean_ic, + IEEE80211_MSG_RADIUS, + ("[%s] BAD message-auth hash\n", + ether_sprintf(ean->ean_node->ni_macaddr))); + eapolstats.rs_badmsgauth++; + continue; + } + ap = radius_find_attr(ahp, RAD_ATTR_STATE); + if (ap != NULL) { + ern->ern_statelen = ap[1] - 2; + memcpy(ern->ern_state, ap+2, ern->ern_statelen); + } else + ern->ern_statelen = 0; + ap = radius_find_attr(ahp, RAD_ATTR_CLASS); + if (ap != NULL) { + ern->ern_classlen = ap[1] - 2; + memcpy(ern->ern_class, ap+2, ern->ern_classlen); + } else + ern->ern_classlen = 0; + /* + * Construct EAPOL reply frame based on the + * contents of the radius message just received. + * radius_create_reply will only return a reply + * buffer if the radius message has at least one EAP + * message in it; this is used below. + */ + if (ern->ern_msg != NULL) + kfree_skb(ern->ern_msg); + ern->ern_msg = radius_create_reply(rc, ahp, ean); + } else + ern->ern_msg = NULL; + + switch (ahp->ah_code) { + case RAD_ACCESS_ACCEPT: + if (ern->ern_msg == NULL) { + IEEE80211_DPRINTF(ean->ean_ic, + IEEE80211_MSG_RADIUS, + ("[%s] RECV RAD_ACCESS_ACCEPT, no EAP\n", + ether_sprintf(ean->ean_node->ni_macaddr))); + ean->ean_backendAuthFails++; + break; + } + IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_RADIUS, + ("[%s] RECV RAD_ACCESS_ACCEPT, success\n", + ether_sprintf(ean->ean_node->ni_macaddr))); + /* + * Setup the re-authentication timer based on + * any supplied timeout. Any value we supply + * from the packet is sanity-checked before use. + */ + ap = radius_find_attr(ahp, RAD_ATTR_SESSION_TIMEOUT); + eapol_reauth_setperiod(ean, ap ? + radius_get_integer(ap) : 0); + /* + * Collect key state from the message. + */ + if (radius_extract_key(rc, ern, ahp)) { + IEEE80211_DPRINTF(ean->ean_ic, + IEEE80211_MSG_RADIUS, + ("[%s] KEYS available\n", + ether_sprintf(ean->ean_node->ni_macaddr))); + ean->ean_keyAvailable = TRUE; + } + ean->ean_aFail = FALSE; + ean->ean_aSuccess = TRUE; + ean->ean_backendAuthSuccesses++; + eapol_fsm_run(ean); + break; + case RAD_ACCESS_REJECT: + IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_RADIUS, + ("[%s] RECV RAD_ACCESS_REJECT\n", + ether_sprintf(ean->ean_node->ni_macaddr))); + ean->ean_aFail = TRUE; + ean->ean_aSuccess = FALSE; + ean->ean_backendAuthFails++; + /* NB: it's ok for a reject to omit EAP */ + if (ern->ern_msg != NULL) + ean->ean_currentId = ean->ean_idFromServer; + eapol_fsm_run(ean); + break; + case RAD_ACCESS_CHALLENGE: + if (ern->ern_msg == NULL) { + IEEE80211_DPRINTF(ean->ean_ic, + IEEE80211_MSG_RADIUS, + ("[%s] RECV RAD_ACCESS_CHALLENGE, no EAP\n", + ether_sprintf(ean->ean_node->ni_macaddr))); + /* XXX */ + break; + } + IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_RADIUS, + ("[%s] RECV RAD_ACCESS_CHALLENGE, ok\n", + ether_sprintf(ean->ean_node->ni_macaddr))); + ean->ean_currentId = ean->ean_idFromServer; + ean->ean_aReq = TRUE; + ean->ean_backendAccessChallenges++; + eapol_fsm_run(ean); + break; + default: + IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_RADIUS, + ("[%s] RECV RADIUS msg code %u ignored\n", + ether_sprintf(ean->ean_node->ni_macaddr), + ahp->ah_code)); + if (ern->ern_msg != NULL) { + kfree_skb(ern->ern_msg); + ern->ern_msg = NULL; + } + eapolstats.rs_badcode++; + break; + } + EAPOL_UNLOCK(ec); + } + /* + * ieee80211_radius_detach leaves the final + * cleanup work to us to avoid race conditions. + */ + radius_cleanup(rc); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + module_put_and_exit(0); +#else + return 0; +#endif +} + +/* + * Write-around for bogus crypto API; requiring the address + * address be specified twice can easily cause mistakes. + */ +static __inline void +digest_update(struct crypto_tfm *md5, void *data, u_int len) +{ + struct scatterlist sg; + + sg.page = virt_to_page(data); + sg.offset = offset_in_page(data); + sg.length = len; + crypto_digest_update(md5, &sg, 1); +} + +/* + * Verify the authenticator hash in each reply using + * the shared secret and the hash calculated for the + * associated request. The frame contents are assumed + * to have been checked that they have a valid length. + */ +static int +radius_check_auth(struct radiuscom *rc, struct eapol_auth_radius_node *ern, + struct auth_hdr *ahp) +{ + u_int8_t orighash[16]; + u_int8_t hash[16]; + struct crypto_tfm *md5 = rc->rc_ec->ec_md5; + + crypto_digest_init(md5); + + memcpy(orighash, ahp->ah_auth, sizeof(ahp->ah_auth)); + memcpy(ahp->ah_auth, ern->ern_reqauth, sizeof(ern->ern_reqauth)); + + digest_update(md5, ahp, ntohs(ahp->ah_len)); + digest_update(md5, rc->rc_secret, rc->rc_secretlen); + crypto_digest_final(md5, hash); + + memcpy(ahp->ah_auth, orighash, sizeof(ahp->ah_auth)); + return (memcmp(ahp->ah_auth, hash, sizeof(hash)) == 0); +} + +/* + * Validate the Message-Authenticator hash. + */ +static int +radius_check_msg_auth(struct radiuscom *rc, struct eapol_auth_radius_node *ern, + struct auth_hdr *ahp) +{ + u_int8_t ahash[16], hash[16]; + u_int8_t *ap; + int authlen; + + ap = radius_find_attr(ahp, RAD_ATTR_MESSAGE_AUTHENTICATOR); + if (ap == NULL) { + eapolstats.rs_nomsgauth++; + return FALSE; + } + authlen = ap[1] - 2; + if (authlen != sizeof(ahp->ah_auth)) { + eapolstats.rs_lenmismatch++; + return FALSE; + } + memcpy(ahash, ap+2, authlen); /* save hash */ + memset(ap+2, 0, authlen); /* zero it for calculations */ + /* NB: we smash ah_auth and don't repair it... */ + memcpy(ahp->ah_auth, ern->ern_reqauth, authlen); + + eapol_hmac_md5(rc->rc_ec, ahp, ntohs(ahp->ah_len), + rc->rc_secret, rc->rc_secretlen, hash); + if (memcmp(ahash, hash, sizeof(hash)) == 0) { + memcpy(ap+2, ahash, authlen); /* restore original data */ + return TRUE; /* pass */ + } else { + return FALSE; /* fail */ + } +} + +/* + * Create an EAPOL/EAP reply message for the supplicant + * based on a message from the radius server. We reserve + * space at the front for the EAPOL header and then copy + * all the EAP message items from the radius packet. + */ +static struct sk_buff * +radius_create_reply(struct radiuscom *rc, + struct auth_hdr *ahp, struct eapol_auth_node *ean) +{ + int len = ntohs(ahp->ah_len); + const u_int8_t *ap = (const u_int8_t *)&ahp[1]; + const u_int8_t *ep = ap + (len - sizeof(*ahp)); + struct sk_buff *skb; + + /* + * Use the received length to size the payload + * in the reply. This is likely an overestimate but + * simplifies things. Note also that eapol_alloc_skb + * reserves headroom for the headers, including the + * EAPOL header. + */ + skb = eapol_alloc_skb(len); + if (skb == NULL) { + IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_ANY, + ("[%s] could not alloc sk_buff (%s)\n", + ether_sprintf(ean->ean_node->ni_macaddr), __func__)); + return NULL; + } + /* + * Copy EAP messages from radius message to the sk_buff. + */ + for (; ap < ep; ap += ap[1]) { + /* validate attribute length */ + if (ap[1] < 2 || ap + ap[1] > ep) { + IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_RADIUS, + ("[%s] bad attribute length %u (%s)\n", + ether_sprintf(ean->ean_node->ni_macaddr), + ap[1], __func__)); + eapolstats.rs_badattrlen++; + goto bad; + } + if (ap[0] == RAD_ATTR_EAP_MESSAGE) { + int alen = ap[1] - 2; + memcpy(skb_put(skb, alen), ap+2, alen); + } + } + /* + * Check result. + */ + if (skb->len < sizeof(struct eap_hdr_short)) { + /* no EAP messages were found, discard the buf */ + IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_RADIUS, + ("[%s] no EAP msg found (%s)\n", + ether_sprintf(ean->ean_node->ni_macaddr), __func__)); + eapolstats.rs_emptyreply++; +bad: + dev_kfree_skb(skb); + return NULL; + } else { + /* extract EAP identifier for later use */ + struct eap_hdr_short *eap = (struct eap_hdr_short *)skb->data; + ean->ean_idFromServer = eap->eap_id; + return skb; + } +} + +#define RAD_VENDOR_MICROSOFT 311 +#define RAD_KEYTYPE_SEND 16 /* MPPE-Send-Key */ +#define RAD_KEYTYPE_RECV 17 /* MPPE-Recv-Key */ + +/* + * Microsoft Vendor-specific radius attributes used for + * distributing keys. See MS-MPPE-Send-Key and + * MS-MPPE-Recv-Key in RFC 2548 for documentation on the + * handling of this data. + */ +struct vendor_key { + u_int32_t vk_vid; /* vendor id */ + u_int8_t vk_type; /* key type */ + u_int8_t vk_len; /* length of type+len+salt+key */ + u_int16_t vk_salt; + /* variable length key data follows */ +} __attribute__((__packed__)); + +/* + * Extract a station transmit/receive key by mixing the + * MPPE key with an md5 hash of the shared secret, request + * authenticator, salt, and MPPE key contents. + */ +static int +radius_extract_mppe(struct radiuscom *rc, struct eapol_auth_radius_node *ern, + struct vendor_key *vk, struct eapol_radius_key *key) +{ + u_int8_t hash[16], keybuf[sizeof(key->rk_data)]; + u_int8_t *vkey; + u_int8_t *dkey; + int keylen, total, i, j; + struct crypto_tfm *md5 = rc->rc_ec->ec_md5; + + /* + * vk_len is the vendor attribute payload size, so deduct + * the header (including salt) to get the key length. If + * the key length is not a multiple of the md5 hash size; + * then it is zero-padded (the rfc talks about an optional + * padding sub-field but we assume clients may not include + * it--this may be wrong if they include it and do not use + * zero for padding, in practice everyone so far just sends + * keys that are a multiple of 16). Also, verify the result + * fits in the storage we have set aside for it. + */ + keylen = vk->vk_len - (sizeof(vk->vk_type) + + sizeof(vk->vk_len) + sizeof(vk->vk_salt)); + /* XXX is keylen == 0 ok? */ + total = roundup(keylen, 16); + if (total > sizeof(keybuf)) { + IEEE80211_DPRINTF(ern->ern_ic, IEEE80211_MSG_ANY, + ("[%s] MPPE key too large, total %u (%s)\n", + ether_sprintf(ern->ern_node->ni_macaddr), + total, __func__)); + /* XXX should we just truncate it? */ + eapolstats.rs_vkeytoolong++; + return FALSE; + } + + /* + * Create intermediate, potentially zero-padded, copy. + */ + vkey = (u_int8_t *)&vk[1]; + memcpy(keybuf, vkey, keylen); + if (keylen != total) /* zero-pad key */ + memset(keybuf+keylen, 0, total-keylen); + + /* + * The first 16 bytes are xor'd with a hash constructed + * from (shared secret | req-authenticator | salt). + * Subsequent 16-byte chunks of the key are xor'd with + * the hash of (shared secret | key). + */ + dkey = key->rk_data; + for (i = 0; i < total; i += 16) { + crypto_digest_init(md5); + digest_update(md5, rc->rc_secret, rc->rc_secretlen); + if (i == 0) { + digest_update(md5, + ern->ern_reqauth, sizeof(ern->ern_reqauth)); + digest_update(md5, &vk->vk_salt, sizeof(vk->vk_salt)); + } else { + digest_update(md5, &keybuf[i-16], 16); + } + crypto_digest_final(md5, hash); + + for (j = 0; j < 16; j++) + dkey[i+j] = keybuf[i+j] ^ hash[j]; + } + return TRUE; +} + +/* + * Extract Microsoft MPPE key state from the frame. + */ +static int +radius_extract_key(struct radiuscom *rc, + struct eapol_auth_radius_node *ern, struct auth_hdr *ahp) +{ +#define ALLKEYS (RAD_KEYTYPE_SEND+RAD_KEYTYPE_RECV) + int len = ntohs(ahp->ah_len); + u_int8_t *ap = (u_int8_t *)&ahp[1]; + u_int8_t *ep = ap + (len - sizeof(*ahp)); + struct vendor_key *vk; + int found = 0; + + for (; ap < ep && found != ALLKEYS; ap += ap[1]) { + /* validate attribute length */ + if (ap[1] < 2 || ap + ap[1] > ep) { + eapolstats.rs_badattrlen++; + break; + } + if (ap[0] != RAD_ATTR_VENDOR_SPECIFIC) + continue; + if (ap[1] < 2+sizeof(struct vendor_key)) { + eapolstats.rs_vkeytooshort++; + continue; + } + vk = (struct vendor_key *)&ap[2]; + /* XXX ntohl alignment */ + if (ntohl(vk->vk_vid) != RAD_VENDOR_MICROSOFT) { + eapolstats.rs_vkeybadvid++; + continue; + } + if ((ntohs(vk->vk_salt) & 0x8000) == 0) { + eapolstats.rs_vkeybadsalt++; + continue; + } + switch (vk->vk_type) { + case RAD_KEYTYPE_SEND: + if (found && vk->vk_salt == ern->ern_rxkey.rk_salt) { + eapolstats.rs_vkeydupsalt++; + continue; + } + if (radius_extract_mppe(rc, ern, vk, &ern->ern_txkey)) { +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_dumpradkeys(ern->ern_ic)) + radius_dumpbytes("send key", + &ern->ern_txkey.rk_data[1], + ern->ern_txkey.rk_len); +#endif + ern->ern_txkey.rk_salt = vk->vk_salt; + found += RAD_KEYTYPE_SEND; + } + break; + case RAD_KEYTYPE_RECV: + if (found && vk->vk_salt == ern->ern_txkey.rk_salt) { + eapolstats.rs_vkeydupsalt++; + continue; + } + if (radius_extract_mppe(rc, ern, vk, &ern->ern_rxkey)) { +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_dumpradkeys(ern->ern_ic)) + radius_dumpbytes("recv key", + &ern->ern_rxkey.rk_data[1], + ern->ern_rxkey.rk_len); +#endif + ern->ern_rxkey.rk_salt = vk->vk_salt; + found += RAD_KEYTYPE_RECV; + } + break; + } + } + return (found == ALLKEYS); +#undef ALLKEYS +} + +/* + * Locate an attribute in the message and sanity + * check the length field so the caller can assume + * it's minimally valid (i.e. it covers the attribute + * itself and any data is within the bound of the + * message as specified in the header. + */ +static u_int8_t * +radius_find_attr(struct auth_hdr *ahp, int attr) +{ + int len = ntohs(ahp->ah_len); + u_int8_t *ap = (u_int8_t *)&ahp[1]; + u_int8_t *ep = ap + (len - sizeof(*ahp)); + + for (; ap < ep; ap += ap[1]) { + /* validate attribute length */ + if (ap[1] < 2 || ap + ap[1] > ep) { + eapolstats.rs_badattrlen++; + return NULL; + } + if (ap[0] == attr) + return ap; + } + return NULL; +} + +/* + * Helper functions for constructing a radius message. + */ + +static u_int8_t * +radius_add_bytes(u_int8_t *dp, int attr, const void *val, u_int len) +{ + dp[0] = attr; + dp[1] = 2 + len; + memcpy(&dp[2], val, len); + return dp + dp[1]; +} + +static u_int8_t * +radius_add_space(u_int8_t *dp, int attr, u_int len) +{ + dp[0] = attr; + dp[1] = 2 + len; + memset(&dp[2], 0, len); + return dp + dp[1]; +} + +static u_int8_t * +radius_add_integer(u_int8_t *dp, int attr, u_int32_t val) +{ + val = htonl(val); + return radius_add_bytes(dp, attr, &val, sizeof(val)); +} + +static u_int8_t * +radius_add_string(u_int8_t *dp, int attr, const char *val) +{ + return radius_add_bytes(dp, attr, val, strlen(val)); +} + +static u_int8_t * +radius_add_stationid(u_int8_t *dp, int attr, struct eapol_auth_node *ean) +{ + struct ieee80211com *ic = ean->ean_ic; + const u_int8_t *mac = ic->ic_bss->ni_macaddr; + char buf[255]; + + snprintf(buf, sizeof(buf), "%02x-%02x-%02x-%02x-%02x-%02x:%.*s", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], + ic->ic_des_esslen, ic->ic_des_essid); + return radius_add_string(dp, attr, buf); +} + +static u_int8_t * +radius_add_macaddr(u_int8_t *dp, int attr, struct eapol_auth_node *ean) +{ + const u_int8_t *mac = ean->ean_node->ni_macaddr; + char buf[20]; + + snprintf(buf, sizeof(buf), "%02x-%02x-%02x-%02x-%02x-%02x", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + return radius_add_string(dp, attr, buf); +} + +static u_int8_t * +radius_add_nasport(u_int8_t *dp, int port) +{ + char buf[32]; + int len = snprintf(buf, sizeof(buf), "STA port # %d", port); + return radius_add_bytes(dp, RAD_ATTR_NAS_PORT_ID, buf, len); +} + +#ifdef IEEE80211_DEBUG +/* + * Return the name for certain attributes. + */ +static const char * +radius_attrname(int attr) +{ + static char buf[8]; + + switch (attr) { + case RAD_ATTR_USER_NAME: return "User-Name"; + case RAD_ATTR_USER_PASSWD: return "User-Passwd"; + case RAD_ATTR_NAS_IP_ADDRESS: return "NAS-Ip-Address"; + case RAD_ATTR_NAS_PORT: return "NAS-Port"; + case RAD_ATTR_SERVICE_TYPE: return "Service-Type"; + case RAD_ATTR_FRAMED_MTU: return "Framed-MTU"; + case RAD_ATTR_REPLY_MESSAGE: return "Reply-Message"; + case RAD_ATTR_STATE: return "State"; + case RAD_ATTR_CLASS: return "Class"; + case RAD_ATTR_VENDOR_SPECIFIC: return "Vendor-Specific"; + case RAD_ATTR_SESSION_TIMEOUT: return "Session-Timeout"; + case RAD_ATTR_CALLED_STATION_ID:return "Called-Station-ID"; + case RAD_ATTR_CALLING_STATION_ID:return "Calling-Station-ID"; + case RAD_ATTR_NAS_IDENTIFIER: return "NAS-Identifier"; + case RAD_ATTR_NAS_PORT_TYPE: return "NAS-Port-Type"; + case RAD_ATTR_CONNECT_INFO: return "Connect-Info"; + case RAD_ATTR_EAP_MESSAGE: return "EAP-Message"; + case RAD_ATTR_MESSAGE_AUTHENTICATOR: return "Message-Authenticator"; + case RAD_ATTR_NAS_PORT_ID: return "NAS-Port-ID"; + } + snprintf(buf, sizeof(buf), "%u", attr); + return buf; +}; + +static void +radius_dumpbytes(const char *tag, const void *data, u_int len) +{ + const u_int8_t *tp; + + printf("%s = 0x", tag); + for (tp = data; len > 0; tp++, len--) + printf("%02x", *tp); + printf("\n"); +} + +/* + * Dump the contents of a RADIUS msg to the console. + */ +static void +radius_dumppkt(const char *tag, const struct eapol_auth_node *ean, + const struct auth_hdr *ahp) +{ + int len = ntohs(ahp->ah_len); + const u_int8_t *ap = (const u_int8_t *)&ahp[1]; + const u_int8_t *ep = ap + (len - sizeof(*ahp)); + const char *attrname; + u_int32_t v; + + printf("[%s] %s RADIUS msg: code %u id %u len %u\n", + ether_sprintf(ean->ean_node->ni_macaddr), + tag, ahp->ah_code, ahp->ah_id, len); + for (; ap < ep; ap += ap[1]) { + if (ap + ap[1] > ep) { + printf("%s: bogus attribute length %u for attr %u\n", + __func__, ap[1], ap[0]); + return; + } + attrname = radius_attrname(ap[0]); + len = ap[1] - 2; + switch (ap[0]) { + case RAD_ATTR_SERVICE_TYPE: + case RAD_ATTR_FRAMED_MTU: + case RAD_ATTR_NAS_PORT_TYPE: + case RAD_ATTR_NAS_PORT: + memcpy(&v, ap+2, sizeof(v)); + v = ntohl(v); + printf("\t%s = %u (0x%x)\n", attrname, v, v); + break; + case RAD_ATTR_USER_NAME: + case RAD_ATTR_CALLED_STATION_ID: + case RAD_ATTR_CALLING_STATION_ID: + case RAD_ATTR_NAS_IDENTIFIER: + case RAD_ATTR_CONNECT_INFO: + case RAD_ATTR_NAS_PORT_ID: + printf("\t%s = \"%.*s\"\n", attrname, len, ap+2); + break; + case RAD_ATTR_NAS_IP_ADDRESS: + memcpy(&v, ap+2, sizeof(v)); + v = ntohl(v); + printf("\t%s = %u.%u.%u.%u\n", attrname, + (v>>24)&0xff, (v>>16)&0xff, + (v>>8)&0xff, (v>>0)&0xff); + break; + default: + printf("\t"); + radius_dumpbytes(attrname, ap+2, len); + break; + } + } +} +#endif /* IEEE80211_DEBUG */ + +/* + * Create a response msg for transmission to the radius server. + * The response is an EAP-encapsulated version of the supplicant + * request that is left in an outbound packet buffer. + * + * XXX bounds checking + */ +int +radius_make_response(struct radiuscom *rc, struct eapol_auth_radius_node *ern) +{ + struct eapol_auth_node *ean = &ern->ern_base; + struct sk_buff *skb = ean->ean_skb; + struct eap_hdr *eap; + u_int8_t *dp, *cp, *map; + u_int8_t msgauth[16]; + struct auth_hdr *ahp; + u_int len, cc; + + if (skb == NULL) { + /* XXX something is wrong */ + return FALSE; + } + eap = (struct eap_hdr *)skb->data; + + if (ern->ern_radmsg == NULL) { + /* XXX 4096 */ + MALLOC(ern->ern_radmsg, u_int8_t *, 4096, M_DEVBUF, M_NOWAIT); + if (ern->ern_radmsg == NULL) { + eapolstats.rs_nomem++; /* XXX unique stat */ + return FALSE; + } + } + ahp = (struct auth_hdr *)ern->ern_radmsg; + + ahp->ah_code = RAD_ACCESS_REQUEST; + ahp->ah_id = eap->eap_id; /* copy from supplicant */ + /* + * Save authenticator hash for use in validating replies + * and for extracting MPPE keys returned by the server. + */ + get_random_bytes(ern->ern_reqauth, sizeof(ern->ern_reqauth)); + memcpy(ahp->ah_auth, ern->ern_reqauth, sizeof(ern->ern_reqauth)); + + /* + * Add the attribute-value pairs. + */ + dp = (u_int8_t *)&ahp[1]; + map = dp; /* hold a pointer for fixup later */ + dp = radius_add_space(dp, RAD_ATTR_MESSAGE_AUTHENTICATOR, 16); + dp = radius_add_integer(dp, RAD_ATTR_SERVICE_TYPE, + RAD_SERVICE_TYPE_FRAMED); + dp = radius_add_bytes(dp, RAD_ATTR_USER_NAME, + ean->ean_id, ean->ean_idlen); + /* NB: use Ethernet MTU rather than 802.11 MTU */ + dp = radius_add_integer(dp, RAD_ATTR_FRAMED_MTU, + ETHERMTU - LLC_SNAPFRAMELEN - sizeof(struct eapol_hdr)); + if (ern->ern_statelen != 0) + dp = radius_add_bytes(dp, RAD_ATTR_STATE, + ern->ern_state, ern->ern_statelen); + if (ern->ern_classlen != 0) + dp = radius_add_bytes(dp, RAD_ATTR_CLASS, + ern->ern_class, ern->ern_classlen); + dp = radius_add_stationid(dp, RAD_ATTR_CALLED_STATION_ID, ean); + dp = radius_add_macaddr(dp, RAD_ATTR_CALLING_STATION_ID, ean); + dp = radius_add_string(dp, RAD_ATTR_NAS_IDENTIFIER, + system_utsname.nodename); + dp = radius_add_integer(dp, RAD_ATTR_NAS_PORT_TYPE, + RAD_NAS_PORT_TYPE_WIRELESS); + switch (ean->ean_ic->ic_curmode) { + case IEEE80211_MODE_11A: + case IEEE80211_MODE_TURBO: + dp = radius_add_string(dp, RAD_ATTR_CONNECT_INFO, + "CONNECT 54Mbps 802.11a"); + break; + case IEEE80211_MODE_11B: + dp = radius_add_string(dp, RAD_ATTR_CONNECT_INFO, + "CONNECT 11Mbps 802.11b"); + break; + case IEEE80211_MODE_11G: + dp = radius_add_string(dp, RAD_ATTR_CONNECT_INFO, + "CONNECT 54Mbps 802.11g"); + break; + default: + /* XXX bitch */ + eapolstats.rs_badmode++; + break; + } + + /* + * Copy in the original EAP message. Note that + * we have to byte swap the length field in the + * header as it is put in host order on receipt. + */ + len = eap->eap_len; + eap->eap_len = htons(len); + for (cp = (u_int8_t *)eap; len > 0; cp += cc, len -= cc) { + cc = len; + if (cc > RAD_MAX_ATTR_LEN) + cc = RAD_MAX_ATTR_LEN; + dp = radius_add_bytes(dp, RAD_ATTR_EAP_MESSAGE, cp, cc); + } + eap->eap_len = ntohs(eap->eap_len); + + dp = radius_add_bytes(dp, RAD_ATTR_NAS_IP_ADDRESS, + &rc->rc_local.sin_addr.s_addr, sizeof(struct in_addr)); + dp = radius_add_integer(dp, RAD_ATTR_NAS_PORT, + IEEE80211_NODE_AID(ean->ean_node)); + dp = radius_add_nasport(dp, IEEE80211_NODE_AID(ean->ean_node)); + + ern->ern_radmsglen = dp - ern->ern_radmsg; + + /* + * Now patch up stuff that required the message be + * formulated and calculate the message authenticator + * hash. + */ + ahp->ah_len = htons(ern->ern_radmsglen); + eapol_hmac_md5(rc->rc_ec, ern->ern_radmsg, ern->ern_radmsglen, + rc->rc_secret, rc->rc_secretlen, msgauth); + memcpy(map+2, msgauth, sizeof(msgauth)); + + return TRUE; +} + +/* + * Callback from the 802.1x state machine on + * receiving an Identity packet from the supplicant. + */ +static void +radius_identity_input(struct eapol_auth_node *ean, struct sk_buff *skb) +{ + struct eapol_auth_radius_node *ern = EAPOL_RADIUSNODE(ean); + + memset(ern->ern_state, 0, sizeof(ern->ern_state)); + ern->ern_statelen = 0; + memset(ern->ern_class, 0, sizeof(ern->ern_class)); + ern->ern_classlen = 0; +} + +/* + * Callback from the 802.1x backend state machine to + * send/resend a response message to the server. + */ +static void +sendRespToServer(struct eapol_auth_node *ean) +{ + struct eapol_auth_radius_node *ern = EAPOL_RADIUSNODE(ean); + struct radiuscom *rc = ean->ean_ec->ec_radius; + struct msghdr msg; + struct iovec iov; + + if (ean->ean_reqSrvCount == 0) { + /* + * Not a retry; formulate the message from scratch + * and add an entry to the reply list. This entry + * stays until a response is received from the server + * or the node is reset/reclaimed. + */ + radius_make_response(rc, ern); + radius_add_reply(rc, ern); + } + IEEE80211_DPRINTF(ean->ean_ic, + IEEE80211_MSG_RADIUS | IEEE80211_MSG_DOT1X, + ("[%s] %s buf 0x%p len %u\n", + ether_sprintf(ean->ean_node->ni_macaddr), + __func__, ern->ern_radmsg, ern->ern_radmsglen)); +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_dumpradius(ean->ean_ic)) + radius_dumppkt("SEND", ean, (struct auth_hdr *)ern->ern_radmsg); +#endif + msg.msg_name = &rc->rc_server; + msg.msg_namelen = sizeof(rc->rc_server); + iov.iov_base = ern->ern_radmsg; + iov.iov_len = ern->ern_radmsglen; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + /* XXXX check return */ + (void) sock_sendmsg(rc->rc_sock, &msg, iov.iov_len); +} + +/* + * Callback from the 802.1x backend state machine to + * transmit/retransmit the EAP message from the radius sever + * to the supplicant. The message should have been setup by + * the thread that receives messages from the radius server + * (see radius_create_reply). + */ +static void +txReq(struct eapol_auth_node *ean) +{ + struct eapol_auth_radius_node *ern = EAPOL_RADIUSNODE(ean); + struct sk_buff *skb; + + if (ern->ern_msg == NULL) { + IEEE80211_DPRINTF(ean->ean_ic, + IEEE80211_MSG_RADIUS | IEEE80211_MSG_DOT1X, + ("[%s] no msg to send (%s)!\n", + ether_sprintf(ean->ean_node->ni_macaddr), __func__)); + eapolstats.rs_nomsg++; + return; + } + skb = skb_clone(ern->ern_msg, GFP_ATOMIC); + if (skb == NULL) { + IEEE80211_DPRINTF(ean->ean_ic, + IEEE80211_MSG_RADIUS | IEEE80211_MSG_DOT1X, + ("[%s] could not clone msg for xmit (%s)\n", + ether_sprintf(ean->ean_node->ni_macaddr), __func__)); + eapolstats.rs_noclone++; + } else { + IEEE80211_DPRINTF(ean->ean_ic, + IEEE80211_MSG_RADIUS | IEEE80211_MSG_DOT1X, + ("[%s] %s msg 0x%p len %u\n", + ether_sprintf(ean->ean_node->ni_macaddr), + __func__, skb, skb->len)); + eapol_send(&ean->ean_base, skb, EAPOL_TYPE_EAP); + } +} + +/* + * Construct and send an EAPOL RC4 Key message to the supplicant. + */ +static void +radius_txkey(struct eapol_auth_radius_node *ern, + int keytype, int keyix, struct ieee80211_key *key) +{ + struct eapol_auth_node *ean = &ern->ern_base; + struct radiuscom *rc = ean->ean_ec->ec_radius; + struct sk_buff *skb; + struct eapol_key *kp; + struct eapol_hdr *eapol; + struct rc4_state ctx; + u_int8_t *keydata, rc4key[16+64], hash[16]; + struct crypto_tfm *md5 = rc->rc_ec->ec_md5; + + KASSERT(keyix != IEEE80211_KEYIX_NONE, ("no key index")); + if (key->wk_cipher->ic_cipher != IEEE80211_CIPHER_WEP) { + IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_ANY, + ("[%s] key type %u not WEP\n", + ether_sprintf(ean->ean_node->ni_macaddr), + key->wk_cipher->ic_cipher)); + return; + } + if (key->wk_keylen != 16 && key->wk_keylen != 13 && key->wk_keylen != 5) { + IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_ANY, + ("[%s] bad key length %u\n", + ether_sprintf(ean->ean_node->ni_macaddr), key->wk_keylen)); + return; + } + skb = eapol_alloc_skb(sizeof(struct eapol_key) + key->wk_keylen); + if (skb == NULL) { + IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_ANY, + ("[%s] could not alloc sk_buff (%s)\n", + ether_sprintf(ean->ean_node->ni_macaddr), __func__)); + return; + } + kp = (struct eapol_key *)skb_put(skb, sizeof(struct eapol_key)); + memset(kp, 0, sizeof(struct eapol_key)); + kp->ek_type = EAPOL_KEY_TYPE_RC4; + kp->ek_length = htons(key->wk_keylen); + /* NB: there is no htonll */ + kp->ek_replay = cpu_to_be64(get_jiffies_64()); + + get_random_bytes(kp->ek_iv, sizeof(kp->ek_iv)); + crypto_digest_init(md5); + digest_update(md5, kp->ek_iv, sizeof(kp->ek_iv)); + crypto_digest_final(md5, kp->ek_iv); + + kp->ek_index = keyix | keytype; + + /* + * NB: this ``cannot happen'' as the rxkey has already been + * verified to fit in ern_rxkey. + */ + if (sizeof(kp->ek_iv)+ern->ern_rxkey.rk_len > sizeof(rc4key)) { + IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_ANY, + ("[%s] intermediate key buf too small, key len %u (%s)\n", + ether_sprintf(ean->ean_node->ni_macaddr), + ern->ern_rxkey.rk_len, __func__)); + return; + } + + /* encrypt the station key with rc4 key of (iv | rxkey) */ + memcpy(rc4key, kp->ek_iv, sizeof(kp->ek_iv)); + memcpy(rc4key+sizeof(kp->ek_iv), &ern->ern_rxkey.rk_data[1], + ern->ern_rxkey.rk_len); + + keydata = (u_int8_t *)skb_put(skb, key->wk_keylen); + arc4_setkey(&ctx, rc4key, sizeof(kp->ek_iv) + ern->ern_rxkey.rk_len); + arc4_encrypt(&ctx, keydata, key->wk_key, key->wk_keylen); + memset(rc4key, 0, sizeof(rc4key)); + + /* + * Finally, sign the message. To do this we must + * formulate the EAPOL header so we can calculate + * the signature that goes in the key payload. This + * violates the normal layering (sigh, who designs + * these protocols). + */ + eapol = (struct eapol_hdr *)skb_push(skb, sizeof(struct eapol_hdr)); + eapol->eapol_ver = EAPOL_VERSION; + eapol->eapol_type = EAPOL_TYPE_KEY; + eapol->eapol_len = htons(skb->len - sizeof(struct eapol_hdr)); + + eapol_hmac_md5(rc->rc_ec, eapol, skb->len, + &ern->ern_txkey.rk_data[1], ern->ern_txkey.rk_len, hash); + memcpy(kp->ek_sig, hash, sizeof(hash)); + + IEEE80211_DPRINTF(ean->ean_ic, IEEE80211_MSG_RADIUS, + ("[%s] SEND %s EAPOL-Key ix %u len %u bits\n", + ether_sprintf(ean->ean_node->ni_macaddr), + keytype == EAPOL_KEY_BCAST ? "bcast" : "ucast", + keyix, key->wk_keylen * NBBY)); + + eapol_send_raw(&ean->ean_base, skb); +} + +/* + * Construct unicast key for station. + * + * XXX just use random data for now; probably want something better + * XXX cipher suites + */ +static int +setUnicastKey(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + struct ieee80211_key *key = &ni->ni_ucastkey; + int ok = 1; + + ieee80211_key_update_begin(ic); + key->wk_keylen = ni->ni_rsn.rsn_ucastkeylen; + get_random_bytes(key->wk_key, key->wk_keylen); + if (!ieee80211_crypto_newkey(ic, ni->ni_rsn.rsn_ucastcipher, key)) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_RADIUS, + ("[%s] problem creating unicast key for station\n", + ether_sprintf(ni->ni_macaddr))); + ok = 0; + } else if (!ieee80211_crypto_setkey(ic, key, ni->ni_macaddr)) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_RADIUS, + ("[%s] problem installing unicast key for station\n", + ether_sprintf(ni->ni_macaddr))); + /* XXX recovery? */ + ok = 0; + } + ieee80211_key_update_end(ic); + + return ok; +} + +/* + * Callback from the 802.1x backend state machine to transmit + * broadcast+unicast keys to the supplicant. The keys are + * constructed from the key material received from the radius + * server when access is granted. + * + * NB: there must be a multicast key; the unicast key is optional. + */ +static void +txKey(struct eapol_auth_node *ean) +{ + struct eapol_auth_radius_node *ern = EAPOL_RADIUSNODE(ean); + struct ieee80211com *ic = ean->ean_ic; + struct ieee80211_node *ni = ean->ean_node; + + KASSERT(ean->ean_keyAvailable == TRUE, ("no keys!")); + + /* + * Send multicast key with index > 0. + */ + if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE) { + IEEE80211_DPRINTF(ic, IEEE80211_MSG_RADIUS, + ("[%s] no multicast key for station\n", + ether_sprintf(ni->ni_macaddr))); + return; + } + radius_txkey(ern, EAPOL_KEY_BCAST, + ic->ic_def_txkey, &ic->ic_nw_keys[ic->ic_def_txkey]); + /* + * Construct unicast key for station and send it with index 0. + */ + if (setUnicastKey(ic, ni)) + radius_txkey(ern, EAPOL_KEY_UCAST, 0, &ni->ni_ucastkey); +} + +/* + * Look for a session waiting for a reply from the + * radius server. If found the session is taken off + * the list. + */ +static struct eapol_auth_radius_node * +radius_get_reply(struct radiuscom *rc, u_int8_t id) +{ + struct eapol_auth_radius_node *ern; + + EAPOL_LOCK_ASSERT(rc->rc_ec); + + LIST_FOREACH(ern, &rc->rc_replies, ern_next) + if (ern->ern_base.ean_currentId == id) { + LIST_REMOVE(ern, ern_next); + ern->ern_onreply = FALSE; + return ern; + } + return NULL; +} + +static void +radius_remove_reply(struct radiuscom *rc, struct eapol_auth_radius_node *p) +{ + struct eapol_auth_radius_node *ern; + + EAPOL_LOCK_ASSERT(rc->rc_ec); + + LIST_FOREACH(ern, &rc->rc_replies, ern_next) + if (ern == p) { + LIST_REMOVE(ern, ern_next); + ern->ern_onreply = FALSE; + break; + } +} + +/* + * Add a session to the list of those waiting for a + * reply from the radius server. + */ +static void +radius_add_reply(struct radiuscom *rc, struct eapol_auth_radius_node *ern) +{ + + EAPOL_LOCK_ASSERT(rc->rc_ec); + + if (!ern->ern_onreply) { + LIST_INSERT_HEAD(&rc->rc_replies, ern, ern_next); + ern->ern_onreply = TRUE; + } +} + +/* + * Radius-specific node allocate/free routines that + * allocate the larger nodes needed when communicating + * with a radius server. This routine is installed + * when we attach to the authenticator + */ +static struct eapol_auth_node * +radius_node_alloc(struct eapolcom *ec) +{ + struct eapol_auth_radius_node *ern; + + MALLOC(ern, struct eapol_auth_radius_node *, + sizeof(struct eapol_auth_radius_node), + M_EAPOL_NODE, M_NOWAIT | M_ZERO); + return ern ? &ern->ern_base : NULL; +} + +/* + * Reclaim resources specific to the radius server. This + * routine is installed when we attach to the authenticator. + */ +static void +radius_node_free(struct eapol_auth_node *ean) +{ + struct eapol_auth_radius_node *ern = EAPOL_RADIUSNODE(ean); + struct radiuscom *rc = ean->ean_ec->ec_radius; + + /* + * NB: we're called from eapol_node_leave which drops it's + * EAPOL lock before calling us as it's already removed the + * reference to us from the table and needs to drop the lock + * before calling back in to the 802.11 layer. However we + * need to grab the lock again to safeguard removing any + * reference to use on the reply list. There's probably + * a better way to do this. + */ + EAPOL_LOCK(ean->ean_ec); + radius_remove_reply(rc, ern); + EAPOL_UNLOCK(ean->ean_ec); + + if (ern->ern_msg != NULL) + kfree_skb(ern->ern_msg); + if (ern->ern_radmsg != NULL) + FREE(ern->ern_radmsg, M_DEVBUF); + (*rc->rc_node_free)(ean); +} + +/* + * Reset non-fsm state for the specified node. This + * routine is installed when we attach to the authenticator. + */ +static void +radius_node_reset(struct eapol_auth_node *ean) +{ + struct eapol_auth_radius_node *ern = EAPOL_RADIUSNODE(ean); + struct radiuscom *rc = ean->ean_ec->ec_radius; + + EAPOL_LOCK_ASSERT(rc->rc_ec); + + radius_remove_reply(rc, ern); + if (ern->ern_msg != NULL) { + kfree_skb(ern->ern_msg); + ern->ern_msg = NULL; + } + (*rc->rc_node_reset)(ean); +} + +/* + * Cleanup radius client state. + */ +static void +radius_cleanup(struct radiuscom *rc) +{ + if (rc->rc_sock != NULL) + sock_release(rc->rc_sock); + if (rc->rc_secret != NULL) + FREE(rc->rc_secret, M_DEVBUF); + FREE(rc, M_DEVBUF); + + printk(KERN_INFO "802.1x radius client stopped\n"); +} + +/* + * Detach a radius client from an authenticator. + */ +static void +ieee80211_radius_detach(struct eapolcom *ec) +{ + struct radiuscom *rc = ec->ec_radius; + + if (rc != NULL) { + printk(KERN_INFO "802.1x radius client stopping\n"); + ec->ec_radius = NULL; + /* restore original methods */ + rc->rc_ec->ec_node_alloc = rc->rc_node_alloc; + rc->rc_ec->ec_node_free = rc->rc_node_free; + rc->rc_ec->ec_node_reset = rc->rc_node_reset; + /* + * If we started the receiver thread then just signal + * it and let it complete the cleanup work when it's + * dropped out of the receive loop. Otherwise we must + * do the cleanup directly. + * + * XXX wrong, must wait to remove module reference + */ + if (rc->rc_pid != -1) + kill_proc(rc->rc_pid, SIGKILL, 1); + else + radius_cleanup(rc); + + _MOD_DEC_USE(THIS_MODULE); + } +} + +/* + * Attach a radius client to an authenticator. We setup + * private state, override the node allocation alloc/free + * methods, and start a thread to receive messages from + * the radius server. We also setup callsbacks used by + * the 802.1x backend state machine. + */ +static int +ieee80211_radius_attach(struct eapolcom *ec) +{ + struct radiuscom *rc; + int error, secretlen; + + _MOD_INC_USE(THIS_MODULE, return FALSE); + + /* XXX require master key be setup? */ + secretlen = strlen(radius_secret); + if (secretlen == 0) { + printf("%s: no radius server shared secret setup", __func__); + eapolstats.rs_nosecret++; + _MOD_DEC_USE(THIS_MODULE); + return FALSE; + } + MALLOC(rc, struct radiuscom *, sizeof(struct radiuscom), + M_DEVBUF, M_NOWAIT | M_ZERO); + if (rc == NULL) { + printf("%s: unable to allocate memory for client state\n", + __func__); + eapolstats.rs_nomem++; + _MOD_DEC_USE(THIS_MODULE); + return FALSE; + } + LIST_INIT(&rc->rc_replies); + rc->rc_pid = -1; + MALLOC(rc->rc_secret, u_int8_t*, radius_secretlen, M_DEVBUF, M_NOWAIT); + if (rc->rc_secret == NULL) { + eapolstats.rs_nomem++; + printf("%s: unable to allocate memory for shared secret\n", + __func__); + goto bad; + } + memcpy(rc->rc_secret, radius_secret, radius_secretlen); + rc->rc_secretlen = radius_secretlen; + + rc->rc_local = radius_clientaddr; + error = sock_create(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &rc->rc_sock); + if (error < 0) { + printf("%s: cannot create socket, error %u\n", + __func__, -error); + eapolstats.rs_nosocket++; + goto bad; + } + /* + * Force atomic allocation since we sometimes send + * messages from interrupt level. + */ +/* XXX 2.6.0 is just a guess */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + rc->rc_sock->sk->allocation = GFP_ATOMIC; +#else + rc->rc_sock->sk->sk_allocation = GFP_ATOMIC; +#endif + error = (*rc->rc_sock->ops->bind)(rc->rc_sock, + (struct sockaddr *)&rc->rc_local, sizeof(rc->rc_local)); + if (error < 0) { + /* XXX */ + printf("%s: cannot bind local address, error %u\n", + __func__, -error); + eapolstats.rs_cannotbind++; + goto bad; + } + /* XXX check result */ + + rc->rc_server = radius_serveraddr; + rc->rc_pid = kernel_thread(radiusd, rc, CLONE_KERNEL); + if (rc->rc_pid < 0) { + printf("%s: cannot start radiusd thread; error %d\n", + __func__, -rc->rc_pid); + eapolstats.rs_nothread++; + goto bad; + } + rc->rc_identity_input = radius_identity_input; + rc->rc_txreq = txReq; + rc->rc_sendsrvr = sendRespToServer; + rc->rc_txkey = txKey; + /* + * Override node management methods so we have the + * extra space needed for the radius client and so + * we properly manage our private state. + */ + rc->rc_node_alloc = ec->ec_node_alloc; + ec->ec_node_alloc = radius_node_alloc; + rc->rc_node_free = ec->ec_node_free; + ec->ec_node_free = radius_node_free; + rc->rc_node_reset = ec->ec_node_reset; + ec->ec_node_reset = radius_node_reset; + + rc->rc_ec = ec; + ec->ec_radius = rc; + + printk(KERN_INFO "802.1x radius client started\n"); + return TRUE; +bad: + ieee80211_radius_detach(ec); /* NB: does _MOD_DEC_USE */ + return FALSE; +} + +/* + * Handle a sysctl that read/writes an IP address. + */ +static int +radius_sysctl_ipaddr(ctl_table *ctl, int write, struct file *filp, + void *buf, size_t *lenp) +{ + struct sockaddr_in *sin = (struct sockaddr_in *) ctl->data; + + if (write) { + u_int a1, a2, a3, a4; + if (sscanf(buf, "%d.%d.%d.%d", &a1, &a2, &a3, &a4) != 4) + return -EINVAL; + sin->sin_addr.s_addr = htonl( + ((a1 & 0xff) << 24) | ((a2 & 0xff) << 16) + | ((a3 & 0xff) << 8) | ((a4 & 0xff) << 0)); + } else if (*lenp && filp->f_pos == 0) { + u_int32_t v = ntohl(sin->sin_addr.s_addr); + *lenp = snprintf(buf, *lenp, "%d.%d.%d.%d\n", + (v >> 24) & 0xff, (v >> 16) & 0xff, + (v >> 8) & 0xff, (v >> 0) & 0xff); + filp->f_pos += *lenp; + } else { + *lenp = 0; + } + return 0; +} + +/* + * Handle a sysctl that reads/writes an IP port number. + */ +static int +radius_sysctl_ipport(ctl_table *ctl, int write, struct file *filp, + void *buf, size_t *lenp) +{ + struct sockaddr_in *sin = (struct sockaddr_in *) ctl->data; + + if (write) { + u_int a1; + if (sscanf(buf, "%d", &a1) != 1) + return -EINVAL; + sin->sin_port = htons(a1 & 0xffff); + } else if (*lenp && filp->f_pos == 0) { + *lenp = snprintf(buf, *lenp, "%d\n", ntohs(sin->sin_port)); + filp->f_pos += *lenp; + } else { + *lenp = 0; + } + return 0; +} + +/* + * Handle a sysctl to read/write the radius server shared secret. + */ +static int +radius_sysctl_secret(ctl_table *ctl, int write, struct file *filp, + void *buf, size_t *lenp) +{ + int len = *lenp; + + if (write) { + if (len > RAD_MAX_SECRET) /* should not happen */ + len = RAD_MAX_SECRET; + memset(radius_secret, 0, sizeof(radius_secret)); + memcpy(radius_secret, buf, len); + radius_secret[RAD_MAX_SECRET] = '\0'; + radius_secretlen = len; + } else if (*lenp && filp->f_pos == 0) { + if (len >= radius_secretlen) + len = strlen(radius_secret); + memcpy(buf, radius_secret, len); + *lenp = len; + filp->f_pos += *lenp; + } else { + *lenp = 0; + } + return 0; +} + +#define CTL_AUTO -2 /* cannot be CTL_ANY or CTL_NONE */ + +static ctl_table radius_sysctls[] = { + { .ctl_name = CTL_AUTO, + .procname = "serveraddr", + .data = &radius_serveraddr, + .mode = 0644, + .proc_handler = radius_sysctl_ipaddr + }, + { .ctl_name = CTL_AUTO, + .procname = "serverport", + .data = &radius_serveraddr, + .mode = 0644, + .proc_handler = radius_sysctl_ipport + }, + { .ctl_name = CTL_AUTO, + .procname = "clientaddr", + .data = &radius_clientaddr, + .mode = 0644, + .proc_handler = radius_sysctl_ipaddr + }, + { .ctl_name = CTL_AUTO, + .procname = "secret", + .maxlen = RAD_MAX_SECRET, + .mode = 0600, + .proc_handler = radius_sysctl_secret + }, + { 0 } +}; +static ctl_table radius_table[] = { + { .ctl_name = CTL_AUTO, + .procname = "radius", + .mode = 0555, + .child = radius_sysctls + }, { 0 } +}; +static ctl_table dot1x_table[] = { + { .ctl_name = NET_8021X, + .procname = "8021x", + .mode = 0555, + .child = radius_table + }, { 0 } +}; +static ctl_table net_table[] = { +#ifdef CONFIG_PROC_FS + { .ctl_name = CTL_NET, + .procname = "net", + .mode = 0555, + .child = dot1x_table + }, +#endif /* CONFIG_PROC_FS */ + { 0 } +}; + +static struct ctl_table_header *radius_sys; + +/* + * Module glue. + */ + +MODULE_AUTHOR("Errno Consulting, Sam Leffler"); +MODULE_DESCRIPTION("802.11 wireless support: radius backend "); +#ifdef MODULE_LICENSE +MODULE_LICENSE("Dual BSD/GPL"); +#endif + +static const struct ieee80211_authenticator_backend radius = { + .iab_name = "radius", + .iab_attach = ieee80211_radius_attach, + .iab_detach = ieee80211_radius_detach, +}; + +static int __init +init_ieee80211_radius(void) +{ +#ifndef __linux__ + radius_clientaddr.sin_len = sizeof(radius_clientaddr); +#endif + radius_clientaddr.sin_family = AF_INET; +#ifndef __linux__ + radius_serveraddr.sin_len = sizeof(radius_serveraddr); +#endif + radius_serveraddr.sin_family = AF_INET; + radius_serveraddr.sin_port = htons(1812); /* default port */ + radius_sys = register_sysctl_table(net_table, 0); + + ieee80211_authenticator_backend_register(&radius); + return 0; +} +module_init(init_ieee80211_radius); + +static void __exit +exit_ieee80211_radius(void) +{ + if (radius_sys) + unregister_sysctl_table(radius_sys); + + ieee80211_authenticator_backend_unregister(&radius); +} +module_exit(exit_ieee80211_radius); diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_radius.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_radius.h --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_radius.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_radius.h 2005-02-24 13:06:17.398117104 -0800 @@ -0,0 +1,98 @@ +/*- + * Copyright (c) 2004-2005 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _NET80211_RADIUS_H_ +#define _NET80211_RADIUS_H_ + +/* + * IEEE 802.1x radius client support. + * + * Radius client support for the 802.1x authenticator. + * This could be a user process that communicates with + * the authenticator through messages; but for now its + * in the kernel where it's easy to share state with the + * authenticator state machine. We structure this as a + * module so it is not always resident. Also we instantiate + * this support only as needed so the cost of having it + * is only incurred when in use (e.g. not for stations). + */ + +struct eapol_radius_key { + u_int8_t rk_data[64]; /* decoded key length+data */ +#define rk_len rk_data[0] /* decoded key length */ + u_int16_t rk_salt; /* salt from server */ +} __attribute__((__packed__)); + +struct eapol_auth_radius_node { + struct eapol_auth_node ern_base; + LIST_ENTRY(eapol_auth_radius_node) ern_next; /* reply list */ + struct sk_buff *ern_msg; /* supplicant reply */ + u_int8_t *ern_radmsg; /* radius server message */ + u_int16_t ern_radmsglen; /* length of server message */ + u_int8_t ern_onreply; /* on reply pending list */ + u_int8_t ern_reqauth[16];/* request authenticator */ + u_int8_t ern_state[255]; + u_int8_t ern_statelen; + u_int8_t ern_class[255]; + u_int8_t ern_classlen; + struct eapol_radius_key ern_txkey; + struct eapol_radius_key ern_rxkey; +}; +#define EAPOL_RADIUSNODE(_x) ((struct eapol_auth_radius_node *)(_x)) + +#define ern_ic ern_base.ean_ic +#define ern_node ern_base.ean_node + +#define RAD_MAXMSG 4096 /* max message size */ + +struct crypto_tfm; + +struct radiuscom { + struct eapolcom *rc_ec; /* back reference */ + struct socket *rc_sock; /* open socket */ + pid_t rc_pid; /* pid of client thread */ + struct sockaddr_in rc_server; /* server's address */ + struct sockaddr_in rc_local; /* local address */ + u_int8_t *rc_secret; /* shared secret */ + u_int rc_secretlen; /* length of shared secret */ + u_int8_t rc_buf[RAD_MAXMSG]; /* recv thread msg buffer */ + ATH_LIST_HEAD(, eapol_auth_radius_node) rc_replies; + /* saved copies of eapolcom methods */ + struct eapol_auth_node *(*rc_node_alloc)(struct eapolcom *); + void (*rc_node_free)(struct eapol_auth_node *); + void (*rc_node_reset)(struct eapol_auth_node *); + /* callbacks used by the main .1x code */ + void (*rc_identity_input)(struct eapol_auth_node *, + struct sk_buff *); + void (*rc_txreq)(struct eapol_auth_node *); + void (*rc_sendsrvr)(struct eapol_auth_node *); + void (*rc_txkey)(struct eapol_auth_node *); +}; +#endif /* _NET80211_RADIUS_H_ */ diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_var.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_var.h --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_var.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_var.h 2005-02-24 13:06:17.406115888 -0800 @@ -0,0 +1,442 @@ +/* $NetBSD: ieee80211_var.h,v 1.4 2003/10/13 04:27:40 dyoung Exp $ */ +/*- + * Copyright (c) 2001 Atsushi Onoe + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/net80211/ieee80211_var.h,v 1.11 2004/01/15 08:44:27 onoe Exp $ + */ +#ifndef _NET80211_IEEE80211_VAR_H_ +#define _NET80211_IEEE80211_VAR_H_ + +/* + * Definitions for IEEE 802.11 drivers. + */ + +/* NB: portability glue must go first */ +#ifdef __NetBSD__ +#include +#elif __FreeBSD__ +#include +#elif __linux__ +#include +#else +#error "No support for your operating system!" +#endif + +#include + +#include +#include +#include /* for ieee80211_stats */ +#include +#include + +#define IEEE80211_CHAN_MAX 255 +#define IEEE80211_CHAN_BYTES howmany(IEEE80211_CHAN_MAX, NBBY) +#define IEEE80211_CHAN_ANY 0xffff /* token for ``any channel'' */ +#define IEEE80211_CHAN_ANYC \ + ((struct ieee80211_channel *) IEEE80211_CHAN_ANY) + +#define IEEE80211_TXPOWER_MAX 100 /* .5 dbM (XXX units?) */ +#define IEEE80211_TXPOWER_MIN 0 /* kill radio */ + +enum ieee80211_phytype { + IEEE80211_T_DS, /* direct sequence spread spectrum */ + IEEE80211_T_FH, /* frequency hopping */ + IEEE80211_T_OFDM, /* frequency division multiplexing */ + IEEE80211_T_TURBO, /* high rate OFDM, aka turbo mode */ +}; +#define IEEE80211_T_CCK IEEE80211_T_DS /* more common nomenclature */ + +/* XXX not really a mode; there are really multiple PHY's */ +enum ieee80211_phymode { + IEEE80211_MODE_AUTO = 0, /* autoselect */ + IEEE80211_MODE_11A = 1, /* 5GHz, OFDM */ + IEEE80211_MODE_11B = 2, /* 2GHz, CCK */ + IEEE80211_MODE_11G = 3, /* 2GHz, OFDM */ + IEEE80211_MODE_FH = 4, /* 2GHz, GFSK */ + IEEE80211_MODE_TURBO = 5, /* 5GHz, OFDM, 2x clock */ +}; +#define IEEE80211_MODE_MAX (IEEE80211_MODE_TURBO+1) + +enum ieee80211_opmode { + IEEE80211_M_STA = 1, /* infrastructure station */ + IEEE80211_M_IBSS = 0, /* IBSS (adhoc) station */ + IEEE80211_M_AHDEMO = 3, /* Old lucent compatible adhoc demo */ + IEEE80211_M_HOSTAP = 6, /* Software Access Point */ + IEEE80211_M_MONITOR = 8 /* Monitor mode */ +}; + +/* + * 802.11g protection mode. + */ +enum ieee80211_protmode { + IEEE80211_PROT_NONE = 0, /* no protection */ + IEEE80211_PROT_CTSONLY = 1, /* CTS to self */ + IEEE80211_PROT_RTSCTS = 2, /* RTS-CTS */ +}; + +/* + * Roaming mode is effectively who controls the operation + * of the 802.11 state machine when operating as a station. + * State transitions are controlled either by the driver + * (typically when management frames are processed by the + * hardware/firmware), the host (auto/normal operation of + * the 802.11 layer), or explicitly through ioctl requests + * when applications like wpa_supplicant want control. + */ +enum ieee80211_roamingmode { + IEEE80211_ROAMING_DEVICE= 0, /* driver/hardware control */ + IEEE80211_ROAMING_AUTO = 1, /* 802.11 layer control */ + IEEE80211_ROAMING_MANUAL= 2, /* application control */ +}; + +/* + * Channels are specified by frequency and attributes. + */ +struct ieee80211_channel { + u_int16_t ic_freq; /* setting in Mhz */ + u_int16_t ic_flags; /* see below */ +}; + +/* bits 0-3 are for private use by drivers */ +/* channel attributes */ +#define IEEE80211_CHAN_TURBO 0x0010 /* Turbo channel */ +#define IEEE80211_CHAN_CCK 0x0020 /* CCK channel */ +#define IEEE80211_CHAN_OFDM 0x0040 /* OFDM channel */ +#define IEEE80211_CHAN_2GHZ 0x0080 /* 2 GHz spectrum channel. */ +#define IEEE80211_CHAN_5GHZ 0x0100 /* 5 GHz spectrum channel */ +#define IEEE80211_CHAN_PASSIVE 0x0200 /* Only passive scan allowed */ +#define IEEE80211_CHAN_DYN 0x0400 /* Dynamic CCK-OFDM channel */ +#define IEEE80211_CHAN_GFSK 0x0800 /* GFSK channel (FHSS PHY) */ + +/* + * Useful combinations of channel characteristics. + */ +#define IEEE80211_CHAN_FHSS \ + (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_GFSK) +#define IEEE80211_CHAN_A \ + (IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM) +#define IEEE80211_CHAN_B \ + (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_CCK) +#define IEEE80211_CHAN_PUREG \ + (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_OFDM) +#define IEEE80211_CHAN_G \ + (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_DYN) +#define IEEE80211_CHAN_T \ + (IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_TURBO) + +#define IEEE80211_IS_CHAN_FHSS(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_FHSS) == IEEE80211_CHAN_FHSS) +#define IEEE80211_IS_CHAN_A(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A) +#define IEEE80211_IS_CHAN_B(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B) +#define IEEE80211_IS_CHAN_PUREG(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_PUREG) == IEEE80211_CHAN_PUREG) +#define IEEE80211_IS_CHAN_G(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G) +#define IEEE80211_IS_CHAN_T(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_T) == IEEE80211_CHAN_T) + +#define IEEE80211_IS_CHAN_2GHZ(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_2GHZ) != 0) +#define IEEE80211_IS_CHAN_5GHZ(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_5GHZ) != 0) +#define IEEE80211_IS_CHAN_OFDM(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_OFDM) != 0) +#define IEEE80211_IS_CHAN_CCK(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_CCK) != 0) +#define IEEE80211_IS_CHAN_GFSK(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_GFSK) != 0) + +/* ni_chan encoding for FH phy */ +#define IEEE80211_FH_CHANMOD 80 +#define IEEE80211_FH_CHAN(set,pat) (((set)-1)*IEEE80211_FH_CHANMOD+(pat)) +#define IEEE80211_FH_CHANSET(chan) ((chan)/IEEE80211_FH_CHANMOD+1) +#define IEEE80211_FH_CHANPAT(chan) ((chan)%IEEE80211_FH_CHANMOD) + +#define IEEE80211_PS_SLEEP 0x1 /* STA is in power saving mode */ + +#define IEEE80211_PS_MAX_QUEUE 50 /* maximum saved packets */ + +struct vlan_group; +struct eapolcom; +struct ieee80211_aclator; + +struct ieee80211com { + SLIST_ENTRY(ieee80211com) ic_next; + struct net_device *ic_dev; /* associated device */ + struct net_device_stats *ic_devstats; /* interface statistics */ + u_int32_t msg_enable; /* interface message flags */ + struct timer_list ic_slowtimo; /* mgmt/inactivity timer */ + struct ieee80211_stats ic_stats; /* private statistics */ +#ifdef CONFIG_SYSCTL + char ic_procname[12];/* e.g. wlan%d */ + struct ctl_table_header *ic_sysctl_header; + struct ctl_table *ic_sysctls; +#endif + struct vlan_group *ic_vlgrp; /* vlan group state */ + + int (*ic_init)(struct net_device *); + int (*ic_reset)(struct net_device *); + int (*ic_mgtstart)(struct ieee80211com *, + struct sk_buff *); + void (*ic_recv_mgmt)(struct ieee80211com *, + struct sk_buff *, struct ieee80211_node *, + int, int, u_int32_t); + int (*ic_send_mgmt)(struct ieee80211com *, + struct ieee80211_node *, int, int); + int (*ic_newstate)(struct ieee80211com *, + enum ieee80211_state, int); + void (*ic_newassoc)(struct ieee80211com *, + struct ieee80211_node *, int); + void (*ic_updateslot)(struct net_device *); + int (*ic_set_tim)(struct ieee80211com *, int, int); + u_int8_t ic_myaddr[IEEE80211_ADDR_LEN]; + struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX]; + struct ieee80211_channel ic_channels[IEEE80211_CHAN_MAX+1]; + u_int8_t ic_chan_avail[IEEE80211_CHAN_BYTES]; + u_int8_t ic_chan_active[IEEE80211_CHAN_BYTES]; + u_int8_t ic_chan_scan[IEEE80211_CHAN_BYTES]; + u_int32_t ic_flags; /* state flags */ + u_int32_t ic_caps; /* capabilities */ + u_int16_t ic_modecaps; /* set of mode capabilities */ + u_int16_t ic_curmode; /* current mode */ + enum ieee80211_phytype ic_phytype; /* XXX wrong for multi-mode */ + enum ieee80211_opmode ic_opmode; /* operation mode */ + enum ieee80211_state ic_state; /* 802.11 state */ + enum ieee80211_protmode ic_protmode; /* 802.11g protection mode */ + enum ieee80211_roamingmode ic_roaming; /* roaming mode */ + u_int32_t *ic_aid_bitmap; + u_int16_t ic_max_aid; + struct ifmedia ic_media; /* interface media config */ +#if NBPFILTER > 0 + struct bpf_if *ic_rawbpf; /* packet filter structure */ +#endif + struct ieee80211_node *ic_bss; /* information for this node */ + struct ieee80211_channel *ic_ibss_chan; + int ic_fixed_rate; /* index to ic_sup_rates[] */ + u_int16_t ic_rtsthreshold; + u_int16_t ic_fragthreshold; + ieee80211_node_lock_t ic_nodelock; /* on node table */ + u_int ic_scangen; /* gen# for timeout scan */ + struct ieee80211_node *(*ic_node_alloc)(struct ieee80211com *); + void (*ic_node_free)(struct ieee80211com *, + struct ieee80211_node *); + void (*ic_node_cleanup)(struct ieee80211com *, + struct ieee80211_node *); + void (*ic_node_copy)(struct ieee80211com *, + struct ieee80211_node *, + const struct ieee80211_node *); + u_int8_t (*ic_node_getrssi)(struct ieee80211com *, + struct ieee80211_node *); + TAILQ_HEAD(, ieee80211_node) ic_node; /* information of all nodes */ + ATH_LIST_HEAD(, ieee80211_node) ic_hash[IEEE80211_NODE_HASHSIZE]; + u_int16_t ic_lintval; /* listen interval */ + u_int16_t ic_holdover; /* PM hold over duration */ + u_int16_t ic_txmin; /* min tx retry count */ + u_int16_t ic_txmax; /* max tx retry count */ + u_int16_t ic_txlifetime; /* tx lifetime */ + u_int16_t ic_bmisstimeout;/* beacon miss threshold (ms) */ + u_int16_t ic_nonerpsta; /* # non-ERP stations */ + u_int16_t ic_longslotsta; /* # long slot time stations */ + int ic_mgt_timer; /* mgmt timeout */ + int ic_inact_timer; /* inactivity timer wait */ + int ic_nicknamelen; + u_int8_t ic_nickname[IEEE80211_NWID_LEN]; + int ic_des_esslen; + u_int8_t ic_des_essid[IEEE80211_NWID_LEN]; + struct ieee80211_channel *ic_des_chan; /* desired channel */ + u_int8_t ic_des_bssid[IEEE80211_ADDR_LEN]; + void *ic_opt_ie; /* user-specified IE's */ + u_int16_t ic_opt_ie_len; /* length of ni_opt_ie */ + /* + * Inactivity timer settings for nodes. + */ + int ic_inact_init; /* initial setting */ + int ic_inact_auth; /* assoc but not auth setting */ + int ic_inact_run; /* authorized setting */ + + /* + * Cipher state/configuration. + */ + struct ieee80211_crypto_state ic_crypto; +#define ic_nw_keys ic_crypto.cs_nw_keys /* XXX compatibility */ +#define ic_def_txkey ic_crypto.cs_def_txkey /* XXX compatibility */ + + /* + * 802.1x glue. When an authenticator attaches it + * fills in this section. We assume that when ic_ec + * is setup that the methods are safe to call. + */ + const struct ieee80211_authenticator *ic_auth; + struct eapolcom *ic_ec; + + /* + * Access control glue. When a control agent attaches + * it fills in this section. We assume that when ic_ac + * is setup that the methods are safe to call. + */ + const struct ieee80211_aclator *ic_acl; + void *ic_as; +}; + +#define IEEE80211_ADDR_EQ(a1,a2) (memcmp(a1,a2,IEEE80211_ADDR_LEN) == 0) +#define IEEE80211_ADDR_COPY(dst,src) memcpy(dst,src,IEEE80211_ADDR_LEN) + +/* ic_flags */ +/* NB: this is intentionally setup to be IEEE80211_CAPINFO_PRIVACY */ +#define IEEE80211_F_PRIVACY 0x00000010 /* CONF: privacy enabled */ +#define IEEE80211_F_ASCAN 0x00000100 /* STATUS: active scan */ +#define IEEE80211_F_SIBSS 0x00000200 /* STATUS: start IBSS */ +/* NB: this is intentionally setup to be IEEE80211_CAPINFO_SHORT_SLOTTIME */ +#define IEEE80211_F_SHSLOT 0x00000400 /* STATUS: use short slot time*/ +#define IEEE80211_F_PMGTON 0x00000800 /* CONF: Power mgmt enable */ +#define IEEE80211_F_DESBSSID 0x00001000 /* CONF: des_bssid is set */ +#define IEEE80211_F_WME 0x00002000 /* CONF: enable WME use */ +#define IEEE80211_F_ROAMING 0x00004000 /* CONF: roaming enabled */ +#define IEEE80211_F_SWRETRY 0x00008000 /* CONF: sw tx retry enabled */ +#define IEEE80211_F_TXPOW_FIXED 0x00010000 /* TX Power: fixed rate */ +#define IEEE80211_F_IBSSON 0x00020000 /* CONF: IBSS creation enable */ +#define IEEE80211_F_SHPREAMBLE 0x00040000 /* STATUS: use short preamble */ +#define IEEE80211_F_DATAPAD 0x00080000 /* CONF: do alignment pad */ +#define IEEE80211_F_USEPROT 0x00100000 /* STATUS: protection enabled */ +#define IEEE80211_F_USEBARKER 0x00200000 /* STATUS: use barker preamble*/ +#define IEEE80211_F_TIMUPDATE 0x00400000 /* STATUS: update beacon tim */ +#define IEEE80211_F_WPA1 0x00800000 /* CONF: WPA enabled */ +#define IEEE80211_F_WPA2 0x01000000 /* CONF: WPA2 enabled */ +#define IEEE80211_F_WPA 0x01800000 /* CONF: WPA/WPA2 enabled */ +#define IEEE80211_F_DROPUNENC 0x02000000 /* CONF: drop unencrypted */ +#define IEEE80211_F_COUNTERM 0x04000000 /* CONF: TKIP countermeasures */ +#define IEEE80211_F_HIDESSID 0x08000000 /* CONF: hide SSID in beacon */ +#define IEEE80211_F_NOBRIDGE 0x10000000 /* CONF: dis. internal bridge */ + +/* ic_caps */ +#define IEEE80211_C_WEP 0x00000001 /* CAPABILITY: WEP available */ +#define IEEE80211_C_TKIP 0x00000002 /* CAPABILITY: TKIP available */ +#define IEEE80211_C_AES 0x00000004 /* CAPABILITY: AES OCB avail */ +#define IEEE80211_C_AES_CCM 0x00000008 /* CAPABILITY: AES CCM avail */ +#define IEEE80211_C_CKIP 0x00000020 /* CAPABILITY: CKIP available */ +#define IEEE80211_C_IBSS 0x00000100 /* CAPABILITY: IBSS available */ +#define IEEE80211_C_PMGT 0x00000200 /* CAPABILITY: Power mgmt */ +#define IEEE80211_C_HOSTAP 0x00000400 /* CAPABILITY: HOSTAP avail */ +#define IEEE80211_C_AHDEMO 0x00000800 /* CAPABILITY: Old Adhoc Demo */ +#define IEEE80211_C_SWRETRY 0x00001000 /* CAPABILITY: sw tx retry */ +#define IEEE80211_C_TXPMGT 0x00002000 /* CAPABILITY: tx power mgmt */ +#define IEEE80211_C_SHSLOT 0x00004000 /* CAPABILITY: short slottime */ +#define IEEE80211_C_SHPREAMBLE 0x00008000 /* CAPABILITY: short preamble */ +#define IEEE80211_C_MONITOR 0x00010000 /* CAPABILITY: monitor mode */ +#define IEEE80211_C_TKIPMIC 0x00020000 /* CAPABILITY: TKIP MIC avail */ +/* XXX protection/barker? */ + +#define IEEE80211_C_CRYPTO 0x0000002f /* CAPABILITY: crypto alg's */ + +int ieee80211_ifattach(struct ieee80211com *); +void ieee80211_ifdetach(struct ieee80211com *); +void ieee80211_announce(struct ieee80211com *); +void ieee80211_media_init(struct ieee80211com *, ifm_change_cb_t, ifm_stat_cb_t); +struct ieee80211com *ieee80211_find_vap(const u_int8_t mac[IEEE80211_ADDR_LEN]); +int ieee80211_media_change(struct net_device *); +void ieee80211_media_status(struct net_device *, struct ifmediareq *); +int ieee80211_ioctl(struct ieee80211com *, struct ifreq *, int); +int ieee80211_rate2media(struct ieee80211com *, int, + enum ieee80211_phymode); +int ieee80211_media2rate(int); +u_int ieee80211_mhz2ieee(u_int, u_int); +u_int ieee80211_chan2ieee(struct ieee80211com *, struct ieee80211_channel *); +u_int ieee80211_ieee2mhz(u_int, u_int); +int ieee80211_setmode(struct ieee80211com *, enum ieee80211_phymode); +void ieee80211_reset_erp(struct ieee80211com *); +enum ieee80211_phymode ieee80211_chan2mode(struct ieee80211com *, + struct ieee80211_channel *); + +/* + * Key update synchronization methods. XXX should not be visible. + */ +static inline void +ieee80211_key_update_begin(struct ieee80211com *ic) +{ + ic->ic_crypto.cs_key_update_begin(ic); +} +static inline void +ieee80211_key_update_end(struct ieee80211com *ic) +{ + ic->ic_crypto.cs_key_update_end(ic); +} + +#define IEEE80211_MSG_DEBUG 0x40000000 /* IFF_DEBUG equivalent */ +#define IEEE80211_MSG_DUMPPKTS 0x20000000 /* IFF_LINK2 equivalant */ +#define IEEE80211_MSG_CRYPTO 0x10000000 /* crypto work */ +#define IEEE80211_MSG_INPUT 0x08000000 /* input handling */ +#define IEEE80211_MSG_XRATE 0x04000000 /* rate set handling */ +#define IEEE80211_MSG_ELEMID 0x02000000 /* element id parsing */ +#define IEEE80211_MSG_NODE 0x01000000 /* node handling */ +#define IEEE80211_MSG_ASSOC 0x00800000 /* association handling */ +#define IEEE80211_MSG_AUTH 0x00400000 /* authentication handling */ +#define IEEE80211_MSG_SCAN 0x00200000 /* scanning */ +#define IEEE80211_MSG_OUTPUT 0x00100000 /* output handling */ +#define IEEE80211_MSG_STATE 0x00080000 /* state machine */ +#define IEEE80211_MSG_POWER 0x00040000 /* power save handling */ +#define IEEE80211_MSG_DOT1X 0x00020000 /* 802.1x authenticator */ +#define IEEE80211_MSG_DOT1XSM 0x00010000 /* 802.1x state machine */ +#define IEEE80211_MSG_RADIUS 0x00008000 /* 802.1x radius client */ +#define IEEE80211_MSG_RADDUMP 0x00004000 /* dump 802.1x radius packets */ +#define IEEE80211_MSG_RADKEYS 0x00002000 /* dump 802.1x keys */ +#define IEEE80211_MSG_WPA 0x00001000 /* WPA/RSN protocol */ +#define IEEE80211_MSG_ACL 0x00000800 /* ACL handling */ + +#define IEEE80211_MSG_ANY 0xffffffff /* anything */ + +#define IEEE80211_DEBUG +#ifdef IEEE80211_DEBUG +#define IEEE80211_DPRINTF(_ic, _m, _args) do { \ + if (_ic->msg_enable & (_m)) \ + printk _args; \ +} while (0) +#define ieee80211_msg_debug(_ic) \ + ((_ic)->msg_enable & IEEE80211_MSG_DEBUG) +#define ieee80211_msg_dumppkts(_ic) \ + ((_ic)->msg_enable & IEEE80211_MSG_DUMPPKTS) +#define ieee80211_msg_input(_ic) \ + ((_ic)->msg_enable & IEEE80211_MSG_INPUT) +#define ieee80211_msg_radius(_ic) \ + ((_ic)->msg_enable & IEEE80211_MSG_RADIUS) +#define ieee80211_msg_dumpradius(_ic) \ + ((_ic)->msg_enable & IEEE80211_MSG_RADDUMP) +#define ieee80211_msg_dumpradkeys(_ic) \ + ((_ic)->msg_enable & IEEE80211_MSG_RADKEYS) +#define ieee80211_msg_scan(_ic) \ + ((_ic)->msg_enable & IEEE80211_MSG_SCAN) +#else +#define IEEE80211_DPRINTF(_ic, _fmt, ...) +#endif + +#endif /* _NET80211_IEEE80211_VAR_H_ */ diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_wireless.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_wireless.c --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_wireless.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_wireless.c 2005-02-24 13:06:17.408115584 -0800 @@ -0,0 +1,2206 @@ +/*- + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + * + * $Id: ieee80211_wireless.c,v 1.19 2005/02/16 16:09:03 samleffler Exp $ + */ +#ifndef EXPORT_SYMTAB +#define EXPORT_SYMTAB +#endif + +#ifdef CONFIG_NET_WIRELESS +/* + * Wireless extensions support for 802.11 common code. + */ +#include +#include +#include +#include +#include +#include /* XXX for ARPHRD_ETHER */ +#include + +#include + +#include "if_media.h" + +#include + +#define IS_UP(_dev) \ + (((_dev)->flags & (IFF_RUNNING|IFF_UP)) == (IFF_RUNNING|IFF_UP)) +#define IS_UP_AUTO(_ic) \ + (IS_UP((_ic)->ic_dev) && (_ic)->ic_roaming == IEEE80211_ROAMING_AUTO) + +/* + * Units are in db above the noise floor. That means the + * rssi values reported in the tx/rx descriptors in the + * driver are the SNR expressed in db. + * + * If you assume that the noise floor is -95, which is an + * excellent assumption 99.5 % of the time, then you can + * derive the absolute signal level (i.e. -95 + rssi). + * There are some other slight factors to take into account + * depending on whether the rssi measurement is from 11b, + * 11g, or 11a. These differences are at most 2db and + * can be documented. + * + * NB: various calculations are based on the orinoco/wavelan + * drivers for compatibility + */ +static void +set_quality(struct iw_quality *iq, u_int rssi) +{ + iq->qual = rssi; + /* NB: max is 94 because noise is hardcoded to 161 */ + if (iq->qual > 94) + iq->qual = 94; + + iq->noise = 161; /* -95dBm */ + iq->level = iq->noise + iq->qual; + iq->updated = 7; +} + +void +ieee80211_iw_getstats(struct ieee80211com *ic, struct iw_statistics *is) +{ +#define NZ(x) ((x) ? (x) : 1) + + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + /* use stats from associated ap */ + if (ic->ic_bss) + set_quality(&is->qual, + (*ic->ic_node_getrssi)(ic, ic->ic_bss)); + else + set_quality(&is->qual, 0); + break; + case IEEE80211_M_IBSS: + case IEEE80211_M_AHDEMO: + case IEEE80211_M_HOSTAP: { + struct ieee80211_node* ni; + u_int32_t rssi_samples = 0; + u_int32_t rssi_total = 0; + + /* average stats from all nodes */ + TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { + rssi_samples++; + rssi_total += (*ic->ic_node_getrssi)(ic, ni); + } + set_quality(&is->qual, rssi_total / NZ(rssi_samples)); + break; + } + case IEEE80211_M_MONITOR: + default: + /* no stats */ + set_quality(&is->qual, 0); + break; + } + is->status = ic->ic_state; + is->discard.nwid = ic->ic_stats.is_rx_wrongbss + + ic->ic_stats.is_rx_ssidmismatch; + is->discard.code = ic->ic_stats.is_rx_wepfail + + ic->ic_stats.is_rx_decryptcrc; + is->discard.fragment = 0; + is->discard.retries = 0; + is->discard.misc = 0; + + is->miss.beacon = 0; +#undef NZ +} +EXPORT_SYMBOL(ieee80211_iw_getstats); + +int +ieee80211_ioctl_giwname(struct ieee80211com *ic, + struct iw_request_info *info, + char *name, char *extra) +{ + + /* XXX should use media status but IFM_AUTO case gets tricky */ + switch (ic->ic_curmode) { + case IEEE80211_MODE_11A: + strncpy(name, "IEEE 802.11a", IFNAMSIZ); + break; + case IEEE80211_MODE_11B: + strncpy(name, "IEEE 802.11b", IFNAMSIZ); + break; + case IEEE80211_MODE_11G: + strncpy(name, "IEEE 802.11g", IFNAMSIZ); + break; + case IEEE80211_MODE_TURBO: + strncpy(name, "IEEE 802.11-TURBO", IFNAMSIZ); + break; + default: + strncpy(name, "IEEE 802.11", IFNAMSIZ); + break; + } + return 0; +} +EXPORT_SYMBOL(ieee80211_ioctl_giwname); + +/* + * Get a key index from a request. If nothing is + * specified in the request we use the current xmit + * key index. Otherwise we just convert the index + * to be base zero. + */ +static int +getiwkeyix(struct ieee80211com *ic, const struct iw_point* erq, int *kix) +{ + int kid; + + kid = erq->flags & IW_ENCODE_INDEX; + if (kid < 1 || kid > IEEE80211_WEP_NKID) { + kid = ic->ic_def_txkey; + if (kid == IEEE80211_KEYIX_NONE) + kid = 0; + } else + --kid; + if (0 <= kid && kid < IEEE80211_WEP_NKID) { + *kix = kid; + return 0; + } else + return EINVAL; +} + +int +ieee80211_ioctl_siwencode(struct ieee80211com *ic, + struct iw_request_info *info, + struct iw_point *erq, char *keybuf) +{ + int kid, error; + int wepchange = 0; + + if ((erq->flags & IW_ENCODE_DISABLED) == 0) { + /* + * Enable crypto, set key contents, and + * set the default transmit key. + */ + error = getiwkeyix(ic, erq, &kid); + if (error) + return -error; + if (erq->length > IEEE80211_KEYBUF_SIZE) + return -EINVAL; + /* XXX no way to install 0-length key */ + ieee80211_key_update_begin(ic); + if (erq->length > 0) { + struct ieee80211_key *k = &ic->ic_nw_keys[kid]; + + /* + * Set key contents. This interface only supports WEP. + */ + if (ieee80211_crypto_newkey(ic, IEEE80211_CIPHER_WEP, k)) { + k->wk_keylen = erq->length; + /* NB: preserve flags set by newkey */ + k->wk_flags |= + IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV; + memcpy(k->wk_key, keybuf, erq->length); + memset(k->wk_key + erq->length, 0, + IEEE80211_KEYBUF_SIZE - erq->length); + if (!ieee80211_crypto_setkey(ic, k, ic->ic_myaddr)) + error = -EINVAL; /* XXX */ + } else { + error = -EINVAL; + } + } else { + /* + * When the length is zero the request only changes + * the default transmit key. Verify the new key has + * a non-zero length. + */ + if (ic->ic_nw_keys[kid].wk_keylen == 0) + error = -EINVAL; + } + if (error == 0) { + /* + * The default transmit key is only changed when: + * 1. Privacy is enabled and no key matter is + * specified. + * 2. Privacy is currently disabled. + * This is deduced from the iwconfig man page. + */ + if (erq->length == 0 || + (ic->ic_flags & IEEE80211_F_PRIVACY) == 0) + ic->ic_def_txkey = kid; + wepchange = (ic->ic_flags & IEEE80211_F_PRIVACY) == 0; + ic->ic_flags |= IEEE80211_F_PRIVACY; + } + ieee80211_key_update_end(ic); + } else { + if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) + return 0; + ic->ic_flags &= ~IEEE80211_F_PRIVACY; + wepchange = 1; + error = 0; + } + if (error == 0) { + if (erq->flags & IW_ENCODE_OPEN) + ic->ic_flags &= ~IEEE80211_F_DROPUNENC; + else + ic->ic_flags |= IEEE80211_F_DROPUNENC; + } + if (error == 0 && IS_UP(ic->ic_dev)) { + /* + * Device is up and running; we must kick it to + * effect the change. If we're enabling/disabling + * crypto use then we must re-initialize the device + * so the 802.11 state machine is reset. Otherwise + * the key state should have been updated above. + */ + if (wepchange && ic->ic_roaming == IEEE80211_ROAMING_AUTO) + error = -(*ic->ic_init)(ic->ic_dev); + } + return error; +} +EXPORT_SYMBOL(ieee80211_ioctl_siwencode); + +int +ieee80211_ioctl_giwencode(struct ieee80211com *ic, + struct iw_request_info *info, + struct iw_point *erq, char *key) +{ + struct ieee80211_key *k; + int error, kid; + + if (ic->ic_flags & IEEE80211_F_PRIVACY) { + error = getiwkeyix(ic, erq, &kid); + if (error != 0) + return -error; + k = &ic->ic_nw_keys[kid]; + /* XXX no way to return cipher/key type */ + + erq->flags = kid + 1; /* NB: base 1 */ + if (erq->length > k->wk_keylen) + erq->length = k->wk_keylen; + memcpy(key, k->wk_key, erq->length); + erq->flags |= IW_ENCODE_ENABLED; + } else { + erq->length = 0; + erq->flags = IW_ENCODE_DISABLED; + } + if (ic->ic_flags & IEEE80211_F_DROPUNENC) + erq->flags |= IW_ENCODE_RESTRICTED; + else + erq->flags |= IW_ENCODE_OPEN; + return 0; +} +EXPORT_SYMBOL(ieee80211_ioctl_giwencode); + +#ifndef ifr_media +#define ifr_media ifr_ifru.ifru_ivalue +#endif + +int +ieee80211_ioctl_siwrate(struct ieee80211com *ic, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ + struct ifreq ifr; + int rate; + + if (!ic->ic_media.ifm_cur) + return -EINVAL; + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_media = ic->ic_media.ifm_cur->ifm_media &~ IFM_TMASK; + if (rrq->fixed) { + /* XXX fudge checking rates */ + rate = ieee80211_rate2media(ic, 2 * rrq->value / 1000000, + ic->ic_curmode); + if (rate == IFM_AUTO) /* NB: unknown rate */ + return -EINVAL; + } else + rate = IFM_AUTO; + ifr.ifr_media |= IFM_SUBTYPE(rate); + + return -ifmedia_ioctl(ic->ic_dev, &ifr, &ic->ic_media, SIOCSIFMEDIA); +} +EXPORT_SYMBOL(ieee80211_ioctl_siwrate); + +int +ieee80211_ioctl_giwrate(struct ieee80211com *ic, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ + struct ifmediareq imr; + int rate; + + memset(&imr, 0, sizeof(imr)); + (*ic->ic_media.ifm_status)(ic->ic_dev, &imr); + + rrq->fixed = IFM_SUBTYPE(ic->ic_media.ifm_media) != IFM_AUTO; + /* media status will have the current xmit rate if available */ + rate = ieee80211_media2rate(imr.ifm_active); + if (rate == -1) /* IFM_AUTO */ + rate = 0; + rrq->value = 1000000 * (rate / 2); + + return 0; +} +EXPORT_SYMBOL(ieee80211_ioctl_giwrate); + +int +ieee80211_ioctl_siwsens(struct ieee80211com *ic, + struct iw_request_info *info, + struct iw_param *sens, char *extra) +{ + return 0; +} +EXPORT_SYMBOL(ieee80211_ioctl_siwsens); + +int +ieee80211_ioctl_giwsens(struct ieee80211com *ic, + struct iw_request_info *info, + struct iw_param *sens, char *extra) +{ + sens->value = 0; + sens->fixed = 1; + + return 0; +} +EXPORT_SYMBOL(ieee80211_ioctl_giwsens); + +int +ieee80211_ioctl_siwrts(struct ieee80211com *ic, + struct iw_request_info *info, + struct iw_param *rts, char *extra) +{ + u16 val; + + if (rts->disabled) + val = IEEE80211_RTS_MAX; + else if (IEEE80211_RTS_MIN <= rts->value && + rts->value <= IEEE80211_RTS_MAX) + val = rts->value; + else + return -EINVAL; + if (val != ic->ic_rtsthreshold) { + ic->ic_rtsthreshold = val; + if (IS_UP(ic->ic_dev)) + return -(*ic->ic_reset)(ic->ic_dev); + } + return 0; +} +EXPORT_SYMBOL(ieee80211_ioctl_siwrts); + +int +ieee80211_ioctl_giwrts(struct ieee80211com *ic, + struct iw_request_info *info, + struct iw_param *rts, char *extra) +{ + + rts->value = ic->ic_rtsthreshold; + rts->disabled = (rts->value == IEEE80211_RTS_MAX); + rts->fixed = 1; + + return 0; +} +EXPORT_SYMBOL(ieee80211_ioctl_giwrts); + +int +ieee80211_ioctl_siwfrag(struct ieee80211com *ic, + struct iw_request_info *info, + struct iw_param *rts, char *extra) +{ + u16 val; + + if (rts->disabled) + val = __constant_cpu_to_le16(2346); + else if (rts->value < 256 || rts->value > 2346) + return -EINVAL; + else + val = __cpu_to_le16(rts->value & ~0x1); /* even numbers only */ + if (val != ic->ic_fragthreshold) { + ic->ic_fragthreshold = val; + if (IS_UP(ic->ic_dev)) + return -(*ic->ic_reset)(ic->ic_dev); + } + return 0; +} +EXPORT_SYMBOL(ieee80211_ioctl_siwfrag); + +int +ieee80211_ioctl_giwfrag(struct ieee80211com *ic, + struct iw_request_info *info, + struct iw_param *rts, char *extra) +{ + + rts->value = ic->ic_fragthreshold; + rts->disabled = (rts->value == 2346); + rts->fixed = 1; + + return 0; +} +EXPORT_SYMBOL(ieee80211_ioctl_giwfrag); + +int +ieee80211_ioctl_siwap(struct ieee80211com *ic, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra) +{ + static const u_int8_t zero_bssid[IEEE80211_ADDR_LEN]; + + /* NB: should only be set when in STA mode */ + if (ic->ic_opmode != IEEE80211_M_STA) + return -EINVAL; + IEEE80211_ADDR_COPY(ic->ic_des_bssid, &ap_addr->sa_data); + /* looks like a zero address disables */ + if (IEEE80211_ADDR_EQ(ic->ic_des_bssid, zero_bssid)) + ic->ic_flags &= ~IEEE80211_F_DESBSSID; + else + ic->ic_flags |= IEEE80211_F_DESBSSID; + return IS_UP_AUTO(ic) ? -(*ic->ic_init)(ic->ic_dev) : 0; +} +EXPORT_SYMBOL(ieee80211_ioctl_siwap); + +int +ieee80211_ioctl_giwap(struct ieee80211com *ic, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra) +{ + + if (ic->ic_flags & IEEE80211_F_DESBSSID) + IEEE80211_ADDR_COPY(&ap_addr->sa_data, ic->ic_des_bssid); + else + IEEE80211_ADDR_COPY(&ap_addr->sa_data, ic->ic_bss->ni_bssid); + ap_addr->sa_family = ARPHRD_ETHER; + return 0; +} +EXPORT_SYMBOL(ieee80211_ioctl_giwap); + +int +ieee80211_ioctl_siwnickn(struct ieee80211com *ic, + struct iw_request_info *info, + struct iw_point *data, char *nickname) +{ + + if (data->length > IEEE80211_NWID_LEN) + return -EINVAL; + + memset(ic->ic_nickname, 0, IEEE80211_NWID_LEN); + memcpy(ic->ic_nickname, nickname, data->length); + ic->ic_nicknamelen = data->length; + + return 0; +} +EXPORT_SYMBOL(ieee80211_ioctl_siwnickn); + +int +ieee80211_ioctl_giwnickn(struct ieee80211com *ic, + struct iw_request_info *info, + struct iw_point *data, char *nickname) +{ + + if (data->length > ic->ic_nicknamelen + 1) + data->length = ic->ic_nicknamelen + 1; + if (data->length > 0) { + memcpy(nickname, ic->ic_nickname, data->length-1); + nickname[data->length-1] = '\0'; + } + return 0; +} +EXPORT_SYMBOL(ieee80211_ioctl_giwnickn); + +static struct ieee80211_channel * +getcurchan(struct ieee80211com *ic) +{ + switch (ic->ic_state) { + case IEEE80211_S_INIT: + case IEEE80211_S_SCAN: + if (ic->ic_opmode == IEEE80211_M_STA) + return ic->ic_des_chan; + break; + default: + break; + } + return ic->ic_ibss_chan; +} + +int +ieee80211_ioctl_siwfreq(struct ieee80211com *ic, + struct iw_request_info *info, + struct iw_freq *freq, char *extra) +{ + struct ieee80211_channel *c; + int i; + + if (freq->e > 1) + return -EINVAL; + if (freq->e == 1) + i = ieee80211_mhz2ieee(freq->m / 100000, 0); + else + i = freq->m; + if (i != 0) { + if (i > IEEE80211_CHAN_MAX || isclr(ic->ic_chan_active, i)) + return -EINVAL; + c = &ic->ic_channels[i]; + if (c == getcurchan(ic)) { /* no change, just return */ + ic->ic_des_chan = c; /* XXX */ + return 0; + } + ic->ic_des_chan = c; + if (c != IEEE80211_CHAN_ANYC) + ic->ic_ibss_chan = c; + } else { + /* + * Intepret channel 0 to mean "no desired channel"; + * otherwise there's no way to undo fixing the desired + * channel. + */ + if (ic->ic_des_chan == IEEE80211_CHAN_ANYC) + return 0; + ic->ic_des_chan = IEEE80211_CHAN_ANYC; + } + if (ic->ic_opmode == IEEE80211_M_MONITOR) + return IS_UP(ic->ic_dev) ? -(*ic->ic_reset)(ic->ic_dev) : 0; + else + return IS_UP_AUTO(ic) ? -(*ic->ic_init)(ic->ic_dev) : 0; +} +EXPORT_SYMBOL(ieee80211_ioctl_siwfreq); + +int +ieee80211_ioctl_giwfreq(struct ieee80211com *ic, + struct iw_request_info *info, + struct iw_freq *freq, char *extra) +{ + + if (!ic->ic_ibss_chan) + return -EINVAL; + + freq->m = ic->ic_ibss_chan->ic_freq * 100000; + freq->e = 1; + + return 0; +} +EXPORT_SYMBOL(ieee80211_ioctl_giwfreq); + +int +ieee80211_ioctl_siwessid(struct ieee80211com *ic, + struct iw_request_info *info, + struct iw_point *data, char *ssid) +{ + + if (data->flags == 0) { /* ANY */ + memset(ic->ic_des_essid, 0, sizeof(ic->ic_des_essid)); + ic->ic_des_esslen = 0; + } else { + if (data->length > sizeof(ic->ic_des_essid)) + data->length = sizeof(ic->ic_des_essid); + memcpy(ic->ic_des_essid, ssid, data->length); + ic->ic_des_esslen = data->length; + /* + * Deduct a trailing \0 since iwconfig passes a string + * length that includes this. Unfortunately this means + * that specifying a string with multiple trailing \0's + * won't be handled correctly. Not sure there's a good + * solution; the API is botched (the length should be + * exactly those bytes that are meaningful and not include + * extraneous stuff). + */ + if (ic->ic_des_esslen > 0 && + ic->ic_des_essid[ic->ic_des_esslen-1] == '\0') + ic->ic_des_esslen--; + } + return IS_UP_AUTO(ic) ? -(*ic->ic_init)(ic->ic_dev) : 0; +} +EXPORT_SYMBOL(ieee80211_ioctl_siwessid); + +int +ieee80211_ioctl_giwessid(struct ieee80211com *ic, + struct iw_request_info *info, + struct iw_point *data, char *essid) +{ + + data->flags = 1; /* active */ + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + if (data->length > ic->ic_des_esslen) + data->length = ic->ic_des_esslen; + memcpy(essid, ic->ic_des_essid, data->length); + } else { + if (ic->ic_des_esslen == 0) { + if (data->length > ic->ic_bss->ni_esslen) + data->length = ic->ic_bss->ni_esslen; + memcpy(essid, ic->ic_bss->ni_essid, data->length); + } else { + if (data->length > ic->ic_des_esslen) + data->length = ic->ic_des_esslen; + memcpy(essid, ic->ic_des_essid, data->length); + } + } + return 0; +} +EXPORT_SYMBOL(ieee80211_ioctl_giwessid); + +int +ieee80211_ioctl_giwrange(struct ieee80211com *ic, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct ieee80211_node *ni = ic->ic_bss; + struct iw_range *range = (struct iw_range *) extra; + struct ieee80211_rateset *rs; + int i, r; + + data->length = sizeof(struct iw_range); + memset(range, 0, sizeof(struct iw_range)); + + /* TODO: could fill num_txpower and txpower array with + * something; however, there are 128 different values.. */ + + range->txpower_capa = IW_TXPOW_DBM; + + if (ic->ic_opmode == IEEE80211_M_STA || + ic->ic_opmode == IEEE80211_M_IBSS) { + range->min_pmp = 1 * 1024; + range->max_pmp = 65535 * 1024; + range->min_pmt = 1 * 1024; + range->max_pmt = 1000 * 1024; + range->pmp_flags = IW_POWER_PERIOD; + range->pmt_flags = IW_POWER_TIMEOUT; + range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | + IW_POWER_UNICAST_R | IW_POWER_ALL_R; + } + + range->we_version_compiled = WIRELESS_EXT; + range->we_version_source = 13; + + range->retry_capa = IW_RETRY_LIMIT; + range->retry_flags = IW_RETRY_LIMIT; + range->min_retry = 0; + range->max_retry = 255; + + range->num_channels = IEEE80211_CHAN_MAX; /* XXX */ + + range->num_frequency = 0; + for (i = 0; i <= IEEE80211_CHAN_MAX; i++) + if (isset(ic->ic_chan_active, i)) { + range->freq[range->num_frequency].i = i; + range->freq[range->num_frequency].m = + ic->ic_channels[i].ic_freq * 100000; + range->freq[range->num_frequency].e = 1; + if (++range->num_frequency == IW_MAX_FREQUENCIES) + break; + } + + /* Max quality is max field value minus noise floor */ + range->max_qual.qual = 0xff - 161; + + /* + * In order to use dBm measurements, 'level' must be lower + * than any possible measurement (see iw_print_stats() in + * wireless tools). It's unclear how this is meant to be + * done, but setting zero in these values forces dBm and + * the actual numbers are not used. + */ + range->max_qual.level = 0; + range->max_qual.noise = 0; + + range->sensitivity = 3; + + range->max_encoding_tokens = IEEE80211_WEP_NKID; + /* XXX query driver to find out supported key sizes */ + range->num_encoding_sizes = 3; + range->encoding_size[0] = 5; /* 40-bit */ + range->encoding_size[1] = 13; /* 104-bit */ + range->encoding_size[2] = 16; /* 128-bit */ + + /* XXX this only works for station mode */ + rs = &ni->ni_rates; + range->num_bitrates = rs->rs_nrates; + if (range->num_bitrates > IW_MAX_BITRATES) + range->num_bitrates = IW_MAX_BITRATES; + for (i = 0; i < range->num_bitrates; i++) { + r = rs->rs_rates[i] & IEEE80211_RATE_VAL; + range->bitrate[i] = (r * 1000000) / 2; + } + + /* estimated maximum TCP throughput values (bps) */ + range->throughput = 5500000; + + range->min_rts = 0; + range->max_rts = 2347; + range->min_frag = 256; + range->max_frag = 2346; + + return 0; +} +EXPORT_SYMBOL(ieee80211_ioctl_giwrange); + +int +ieee80211_ioctl_siwmode(struct ieee80211com *ic, + struct iw_request_info *info, + __u32 *mode, char *extra) +{ + struct ifreq ifr; + + if (!ic->ic_media.ifm_cur) + return -EINVAL; + memset(&ifr, 0, sizeof(ifr)); + /* NB: remove any fixed-rate at the same time */ + ifr.ifr_media = ic->ic_media.ifm_cur->ifm_media &~ + (IFM_OMASK | IFM_TMASK); + switch (*mode) { + case IW_MODE_INFRA: + /* NB: this is the default */ + ic->ic_des_chan = IEEE80211_CHAN_ANYC; + break; + case IW_MODE_ADHOC: + ifr.ifr_media |= IFM_IEEE80211_ADHOC; + break; + case IW_MODE_MASTER: + ifr.ifr_media |= IFM_IEEE80211_HOSTAP; + break; +#if WIRELESS_EXT >= 15 + case IW_MODE_MONITOR: + ifr.ifr_media |= IFM_IEEE80211_MONITOR; + break; +#endif + default: + return -EINVAL; + } + return -ifmedia_ioctl(ic->ic_dev, &ifr, &ic->ic_media, SIOCSIFMEDIA); +} +EXPORT_SYMBOL(ieee80211_ioctl_siwmode); + +int +ieee80211_ioctl_giwmode(struct ieee80211com *ic, + struct iw_request_info *info, + __u32 *mode, char *extra) +{ + struct ifmediareq imr; + + memset(&imr, 0, sizeof(imr)); + (*ic->ic_media.ifm_status)(ic->ic_dev, &imr); + + if (imr.ifm_active & IFM_IEEE80211_HOSTAP) + *mode = IW_MODE_MASTER; +#if WIRELESS_EXT >= 15 + else if (imr.ifm_active & IFM_IEEE80211_MONITOR) + *mode = IW_MODE_MONITOR; +#endif + else if (imr.ifm_active & IFM_IEEE80211_ADHOC) + *mode = IW_MODE_ADHOC; + else + *mode = IW_MODE_INFRA; + return 0; +} +EXPORT_SYMBOL(ieee80211_ioctl_giwmode); + +int +ieee80211_ioctl_siwpower(struct ieee80211com *ic, + struct iw_request_info *info, + struct iw_param *wrq, char *extra) +{ + + if (wrq->disabled) { + if (ic->ic_flags & IEEE80211_F_PMGTON) { + ic->ic_flags &= ~IEEE80211_F_PMGTON; + goto done; + } + return 0; + } + + if ((ic->ic_caps & IEEE80211_C_PMGT) == 0) + return -EOPNOTSUPP; + switch (wrq->flags & IW_POWER_MODE) { + case IW_POWER_UNICAST_R: + case IW_POWER_ALL_R: + case IW_POWER_ON: + ic->ic_flags |= IEEE80211_F_PMGTON; + break; + default: + return -EINVAL; + } + if (wrq->flags & IW_POWER_TIMEOUT) { + ic->ic_holdover = wrq->value / 1024; + ic->ic_flags |= IEEE80211_F_PMGTON; + } + if (wrq->flags & IW_POWER_PERIOD) { + ic->ic_lintval = wrq->value / 1024; + ic->ic_flags |= IEEE80211_F_PMGTON; + } +done: + return IS_UP(ic->ic_dev) ? -(*ic->ic_reset)(ic->ic_dev) : 0; +} +EXPORT_SYMBOL(ieee80211_ioctl_siwpower); + +int +ieee80211_ioctl_giwpower(struct ieee80211com *ic, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ + + rrq->disabled = (ic->ic_flags & IEEE80211_F_PMGTON) == 0; + if (!rrq->disabled) { + switch (rrq->flags & IW_POWER_TYPE) { + case IW_POWER_TIMEOUT: + rrq->flags = IW_POWER_TIMEOUT; + rrq->value = ic->ic_holdover * 1024; + break; + case IW_POWER_PERIOD: + rrq->flags = IW_POWER_PERIOD; + rrq->value = ic->ic_lintval * 1024; + break; + } + rrq->flags |= IW_POWER_ALL_R; + } + return 0; +} +EXPORT_SYMBOL(ieee80211_ioctl_giwpower); + +int +ieee80211_ioctl_siwretry(struct ieee80211com *ic, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ + + if (rrq->disabled) { + if (ic->ic_flags & IEEE80211_F_SWRETRY) { + ic->ic_flags &= ~IEEE80211_F_SWRETRY; + goto done; + } + return 0; + } + + if ((ic->ic_caps & IEEE80211_C_SWRETRY) == 0) + return -EOPNOTSUPP; + if (rrq->flags == IW_RETRY_LIMIT) { + if (rrq->value >= 0) { + ic->ic_txmin = rrq->value; + ic->ic_txmax = rrq->value; /* XXX */ + ic->ic_txlifetime = 0; /* XXX */ + ic->ic_flags |= IEEE80211_F_SWRETRY; + } else { + ic->ic_flags &= ~IEEE80211_F_SWRETRY; + } + return 0; + } +done: + return IS_UP(ic->ic_dev) ? -(*ic->ic_reset)(ic->ic_dev) : 0; +} +EXPORT_SYMBOL(ieee80211_ioctl_siwretry); + +int +ieee80211_ioctl_giwretry(struct ieee80211com *ic, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ + + rrq->disabled = (ic->ic_flags & IEEE80211_F_SWRETRY) == 0; + if (!rrq->disabled) { + switch (rrq->flags & IW_RETRY_TYPE) { + case IW_RETRY_LIFETIME: + rrq->flags = IW_RETRY_LIFETIME; + rrq->value = ic->ic_txlifetime * 1024; + break; + case IW_RETRY_LIMIT: + rrq->flags = IW_RETRY_LIMIT; + switch (rrq->flags & IW_RETRY_MODIFIER) { + case IW_RETRY_MIN: + rrq->flags |= IW_RETRY_MAX; + rrq->value = ic->ic_txmin; + break; + case IW_RETRY_MAX: + rrq->flags |= IW_RETRY_MAX; + rrq->value = ic->ic_txmax; + break; + } + break; + } + } + return 0; +} +EXPORT_SYMBOL(ieee80211_ioctl_giwretry); + +int +ieee80211_ioctl_siwtxpow(struct ieee80211com *ic, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ + int fixed, disabled; + + fixed = (ic->ic_flags & IEEE80211_F_TXPOW_FIXED); + disabled = (fixed && ic->ic_bss->ni_txpower == 0); + if (rrq->disabled) { + if (!disabled) { + if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0) + return -EOPNOTSUPP; + ic->ic_flags |= IEEE80211_F_TXPOW_FIXED; + ic->ic_bss->ni_txpower = 0; + goto done; + } + return 0; + } + + if (rrq->fixed) { + if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0) + return -EOPNOTSUPP; + if (rrq->flags != IW_TXPOW_DBM) + return -EOPNOTSUPP; + ic->ic_bss->ni_txpower = 2*rrq->value; + ic->ic_flags |= IEEE80211_F_TXPOW_FIXED; + } else { + if (!fixed) /* no change */ + return 0; + ic->ic_flags &= ~IEEE80211_F_TXPOW_FIXED; + } +done: + return IS_UP(ic->ic_dev) ? -(*ic->ic_reset)(ic->ic_dev) : 0; +} +EXPORT_SYMBOL(ieee80211_ioctl_siwtxpow); + +int +ieee80211_ioctl_giwtxpow(struct ieee80211com *ic, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ + + rrq->value = ic->ic_bss->ni_txpower/2; + rrq->fixed = (ic->ic_flags & IEEE80211_F_TXPOW_FIXED) != 0; + rrq->disabled = (rrq->fixed && rrq->value == 0); + rrq->flags = IW_TXPOW_DBM; + return 0; +} +EXPORT_SYMBOL(ieee80211_ioctl_giwtxpow); + +int +ieee80211_ioctl_iwaplist(struct ieee80211com *ic, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct ieee80211_node *ni; + struct sockaddr addr[IW_MAX_AP]; + struct iw_quality qual[IW_MAX_AP]; + int i; + + i = 0; + /* XXX lock node list */ + TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { + addr[i].sa_family = ARPHRD_ETHER; + if (ic->ic_opmode == IEEE80211_M_HOSTAP) + IEEE80211_ADDR_COPY(addr[i].sa_data, ni->ni_macaddr); + else + IEEE80211_ADDR_COPY(addr[i].sa_data, ni->ni_bssid); + set_quality(&qual[i], (*ic->ic_node_getrssi)(ic, ni)); + if (++i >= IW_MAX_AP) + break; + } + data->length = i; + memcpy(extra, &addr, i*sizeof(addr[0])); + data->flags = 1; /* signal quality present (sort of) */ + memcpy(extra + i*sizeof(addr[0]), &qual, i*sizeof(qual[i])); + + return 0; + +} +EXPORT_SYMBOL(ieee80211_ioctl_iwaplist); + +#ifdef SIOCGIWSCAN +int +ieee80211_ioctl_siwscan(struct ieee80211com *ic, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + u_char *chanlist = ic->ic_chan_active; + int i; + + if (ic->ic_opmode == IEEE80211_M_HOSTAP) + return -EINVAL; + /* + * XXX don't permit a scan to be started unless we + * know the device is ready. For the moment this means + * the device is marked up as this is the required to + * initialize the hardware. It would be better to permit + * scanning prior to being up but that'll require some + * changes to the infrastructure. + */ + if (!IS_UP(ic->ic_dev)) + return -EINVAL; /* XXX */ + if (ic->ic_state == IEEE80211_S_SCAN && (ic->ic_flags & IEEE80211_F_ASCAN)) + return -EINPROGRESS; + if (ic->ic_ibss_chan == NULL || + isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) { + for (i = 0; i <= IEEE80211_CHAN_MAX; i++) + if (isset(chanlist, i)) { + ic->ic_ibss_chan = &ic->ic_channels[i]; + goto found; + } + return -EINVAL; /* no active channels */ +found: + ; + } + if (ic->ic_bss->ni_chan == IEEE80211_CHAN_ANYC || + isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan))) + ic->ic_bss->ni_chan = ic->ic_ibss_chan; + /* + * We force the state to INIT before calling ieee80211_new_state + * to get ieee80211_begin_scan called. We really want to scan w/o + * altering the current state but that's not possible right now. + */ + /* XXX handle proberequest case */ + if (ic->ic_state != IEEE80211_S_INIT) + ieee80211_new_state(ic, IEEE80211_S_INIT, -1); + ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); + return 0; +} +EXPORT_SYMBOL(ieee80211_ioctl_siwscan); + +#if WIRELESS_EXT > 14 +/* + * Encode a WPA or RSN information element as a custom + * element using the hostap format. + */ +static u_int +encode_ie(void *buf, size_t bufsize, + const u_int8_t *ie, size_t ielen, + const char *leader, size_t leader_len) +{ + u_int8_t *p; + int i; + + if (bufsize < leader_len) + return 0; + p = buf; + memcpy(p, leader, leader_len); + bufsize -= leader_len; + p += leader_len; + for (i = 0; i < ielen && bufsize > 2; i++) + p += sprintf(p, "%02x", ie[i]); + return (i == ielen ? p - (u_int8_t *)buf : 0); +} +#endif /* WIRELESS_EXT > 14 */ + +int +ieee80211_ioctl_giwscan(struct ieee80211com *ic, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct ieee80211_node *ni; + char *current_ev = extra; + char *end_buf = extra + IW_SCAN_MAX_DATA; + char *current_val; + struct iw_event iwe; +#if WIRELESS_EXT > 14 + char buf[64*2 + 30]; +#endif + int j, startmode, mode; + + /* XXX use generation number and always return current results */ + if (ic->ic_state == IEEE80211_S_SCAN && (ic->ic_flags & IEEE80211_F_ASCAN)) { + /* + * Still scanning, indicate the caller should try again. + */ + return -EAGAIN; + } + + /* + * Do two passes to insure WPA/non-WPA scan candidates + * are sorted to the front. This is a hack to deal with + * the wireless extensions capping scan results at + * IW_SCAN_MAX_DATA bytes. In densely populated environments + * it's easy to overflow this buffer (especially with WPA/RSN + * information elements). Note this sorting hack does not + * guarantee we won't overflow anyway. + */ + startmode = ic->ic_flags & IEEE80211_F_WPA; + mode = startmode; +again: + /* + * Translate data to WE format. + */ + TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { + if (current_ev >= end_buf) + goto done; + /* WPA/!WPA sort criteria */ + if ((mode != 0) ^ (ni->ni_wpa_ie != NULL)) + continue; + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + if (ic->ic_opmode == IEEE80211_M_HOSTAP) + IEEE80211_ADDR_COPY(iwe.u.ap_addr.sa_data, ni->ni_macaddr); + else + IEEE80211_ADDR_COPY(iwe.u.ap_addr.sa_data, ni->ni_bssid); + current_ev = iwe_stream_add_event(current_ev, + end_buf, &iwe, IW_EV_ADDR_LEN); + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWESSID; + iwe.u.data.flags = 1; + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + iwe.u.data.length = ic->ic_des_esslen; + current_ev = iwe_stream_add_point(current_ev, + end_buf, &iwe, ic->ic_des_essid); + } else { + iwe.u.data.length = ni->ni_esslen; + current_ev = iwe_stream_add_point(current_ev, + end_buf, &iwe, ni->ni_essid); + } + + if (ni->ni_capinfo & (IEEE80211_CAPINFO_ESS|IEEE80211_CAPINFO_IBSS)) { + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWMODE; + iwe.u.mode = ni->ni_capinfo & IEEE80211_CAPINFO_ESS ? + IW_MODE_MASTER : IW_MODE_ADHOC; + current_ev = iwe_stream_add_event(current_ev, + end_buf, &iwe, IW_EV_UINT_LEN); + } + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = ni->ni_chan->ic_freq * 100000; + iwe.u.freq.e = 1; + current_ev = iwe_stream_add_event(current_ev, + end_buf, &iwe, IW_EV_FREQ_LEN); + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVQUAL; + set_quality(&iwe.u.qual, (*ic->ic_node_getrssi)(ic, ni)); + current_ev = iwe_stream_add_event(current_ev, + end_buf, &iwe, IW_EV_QUAL_LEN); + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWENCODE; + if (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) + iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; + else + iwe.u.data.flags = IW_ENCODE_DISABLED; + iwe.u.data.length = 0; + current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, ""); + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWRATE; + current_val = current_ev + IW_EV_LCP_LEN; + for (j = 0; j < ni->ni_rates.rs_nrates; j++) { + if (ni->ni_rates.rs_rates[j]) { + iwe.u.bitrate.value = ((ni->ni_rates.rs_rates[j] & + IEEE80211_RATE_VAL) / 2) * 1000000; + current_val = iwe_stream_add_value(current_ev, + current_val, end_buf, &iwe, + IW_EV_PARAM_LEN); + } + } + /* remove fixed header if no rates were added */ + if ((current_val - current_ev) > IW_EV_LCP_LEN) + current_ev = current_val; + +#if WIRELESS_EXT > 14 + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + snprintf(buf, sizeof(buf), "bcn_int=%d", ni->ni_intval); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, buf); + + if (ni->ni_wpa_ie != NULL) { + static const char rsn_leader[] = "rsn_ie="; + static const char wpa_leader[] = "wpa_ie="; + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + if (ni->ni_wpa_ie[0] == IEEE80211_ELEMID_RSN) + iwe.u.data.length = encode_ie(buf, sizeof(buf), + ni->ni_wpa_ie, ni->ni_wpa_ie[1]+2, + rsn_leader, sizeof(rsn_leader)-1); + else + iwe.u.data.length = encode_ie(buf, sizeof(buf), + ni->ni_wpa_ie, ni->ni_wpa_ie[1]+2, + wpa_leader, sizeof(wpa_leader)-1); + if (iwe.u.data.length != 0) + current_ev = iwe_stream_add_point(current_ev, end_buf, + &iwe, buf); + } +#endif /* WIRELESS_EXT > 14 */ + } + if (mode == startmode) { + mode = mode ? 0 : IEEE80211_F_WPA; + goto again; /* sort of an Algol-style for loop */ + } +done: + data->length = current_ev - extra; + return 0; +} +EXPORT_SYMBOL(ieee80211_ioctl_giwscan); +#endif /* SIOCGIWSCAN */ + +static int +cipher2cap(int cipher) +{ + switch (cipher) { + case IEEE80211_CIPHER_WEP: return IEEE80211_C_WEP; + case IEEE80211_CIPHER_AES_OCB: return IEEE80211_C_AES; + case IEEE80211_CIPHER_AES_CCM: return IEEE80211_C_AES_CCM; + case IEEE80211_CIPHER_CKIP: return IEEE80211_C_CKIP; + case IEEE80211_CIPHER_TKIP: return IEEE80211_C_TKIP; + } + return 0; +} + +int +ieee80211_ioctl_setparam(struct ieee80211com *ic, struct iw_request_info *info, + void *w, char *extra) +{ + struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn; + int *i = (int *) extra; + int param = i[0]; /* parameter id is 1st */ + int value = i[1]; /* NB: most values are TYPE_INT */ + struct ifreq ifr; + int retv = 0; + int j, caps; + const struct ieee80211_authenticator *auth; + const struct ieee80211_aclator *acl; + + switch (param) { + case IEEE80211_PARAM_TURBO: + if (!ic->ic_media.ifm_cur) + return -EINVAL; + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_media = ic->ic_media.ifm_cur->ifm_media; + if (value) + ifr.ifr_media |= IFM_IEEE80211_TURBO; + else + ifr.ifr_media &= ~IFM_IEEE80211_TURBO; + retv = ifmedia_ioctl(ic->ic_dev, &ifr, &ic->ic_media, SIOCSIFMEDIA); + break; + case IEEE80211_PARAM_MODE: + if (!ic->ic_media.ifm_cur) + return -EINVAL; + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_media = ic->ic_media.ifm_cur->ifm_media &~ IFM_MMASK; + ifr.ifr_media |= IFM_MAKEMODE(value); + retv = ifmedia_ioctl(ic->ic_dev, &ifr, &ic->ic_media, SIOCSIFMEDIA); + break; + case IEEE80211_PARAM_AUTHMODE: + switch (value) { + case IEEE80211_AUTH_WPA: /* WPA */ + case IEEE80211_AUTH_8021X: /* 802.1x */ + case IEEE80211_AUTH_OPEN: /* open */ + case IEEE80211_AUTH_SHARED: /* shared-key */ + case IEEE80211_AUTH_AUTO: /* auto */ + auth = ieee80211_authenticator_get(value); + if (auth == NULL) + return -EINVAL; + break; + default: + return -EINVAL; + } + switch (value) { + case IEEE80211_AUTH_WPA: /* WPA w/ 802.1x */ + ic->ic_flags |= IEEE80211_F_PRIVACY; + value = IEEE80211_AUTH_8021X; + break; + case IEEE80211_AUTH_OPEN: /* open */ + ic->ic_flags &= ~(IEEE80211_F_WPA|IEEE80211_F_PRIVACY); + break; + case IEEE80211_AUTH_SHARED: /* shared-key */ + case IEEE80211_AUTH_8021X: /* 802.1x */ + ic->ic_flags &= ~IEEE80211_F_WPA; + /* both require a key so mark the PRIVACY capability */ + ic->ic_flags |= IEEE80211_F_PRIVACY; + break; + case IEEE80211_AUTH_AUTO: /* auto */ + ic->ic_flags &= ~IEEE80211_F_WPA; + /* XXX PRIVACY handling? */ + /* XXX what's the right way to do this? */ + break; + } + /* NB: authenticator attach/detach happens on state change */ + ic->ic_bss->ni_authmode = value; + /* XXX mixed/mode/usage? */ + ic->ic_auth = auth; + retv = ENETRESET; + break; + case IEEE80211_PARAM_PROTMODE: + if (value > IEEE80211_PROT_RTSCTS) + return -EINVAL; + ic->ic_protmode = value; + /* NB: if not operating in 11g this can wait */ + if (ic->ic_curmode == IEEE80211_MODE_11G) + retv = ENETRESET; + break; + case IEEE80211_PARAM_MCASTCIPHER: + /* XXX s/w implementations */ + if ((ic->ic_caps & cipher2cap(value)) == 0) + return -EINVAL; + rsn->rsn_mcastcipher = value; + if (ic->ic_flags & IEEE80211_F_WPA) + retv = ENETRESET; + break; + case IEEE80211_PARAM_MCASTKEYLEN: + if (!(0 < value && value < IEEE80211_KEYBUF_SIZE)) + return -EINVAL; + /* XXX no way to verify driver capability */ + rsn->rsn_mcastkeylen = value; + if (ic->ic_flags & IEEE80211_F_WPA) + retv = ENETRESET; + break; + case IEEE80211_PARAM_UCASTCIPHERS: + /* + * Convert cipher set to equivalent capabilities. + * NB: this logic intentionally ignores unknown and + * unsupported ciphers so folks can specify 0xff or + * similar and get all available ciphers. + */ + caps = 0; + for (j = 1; j < 32; j++) /* NB: skip WEP */ + if (value & (1<ic_caps; /* restrict to supported ciphers */ + /* XXX verify ciphers ok for unicast use? */ + /* XXX disallow if running as it'll have no effect */ + rsn->rsn_ucastcipherset = caps; + if (ic->ic_flags & IEEE80211_F_WPA) + retv = ENETRESET; + break; + case IEEE80211_PARAM_UCASTCIPHER: + if ((rsn->rsn_ucastcipherset & cipher2cap(value)) == 0) + return -EINVAL; + rsn->rsn_ucastcipher = value; + break; + case IEEE80211_PARAM_UCASTKEYLEN: + if (!(0 < value && value < IEEE80211_KEYBUF_SIZE)) + return -EINVAL; + /* XXX no way to verify driver capability */ + rsn->rsn_ucastkeylen = value; + break; + case IEEE80211_PARAM_KEYMGTALGS: + /* XXX check */ + rsn->rsn_keymgmtset = value; + if (ic->ic_flags & IEEE80211_F_WPA) + retv = ENETRESET; + break; + case IEEE80211_PARAM_RSNCAPS: + /* XXX check */ + rsn->rsn_caps = value; + if (ic->ic_flags & IEEE80211_F_WPA) + retv = ENETRESET; + break; + case IEEE80211_PARAM_WPA: + if (value > 3) + return -EINVAL; + /* XXX verify ciphers available */ + ic->ic_flags &= ~IEEE80211_F_WPA; + switch (value) { + case 1: + ic->ic_flags |= IEEE80211_F_WPA1; + break; + case 2: + ic->ic_flags |= IEEE80211_F_WPA2; + break; + case 3: + ic->ic_flags |= IEEE80211_F_WPA1 | IEEE80211_F_WPA2; + break; + } + retv = ENETRESET; /* XXX? */ + break; + case IEEE80211_PARAM_ROAMING: + if (!(IEEE80211_ROAMING_DEVICE <= value && + value <= IEEE80211_ROAMING_MANUAL)) + return -EINVAL; + ic->ic_roaming = value; + break; + case IEEE80211_PARAM_PRIVACY: + if (value) { + /* XXX check for key state? */ + ic->ic_flags |= IEEE80211_F_PRIVACY; + } else + ic->ic_flags &= ~IEEE80211_F_PRIVACY; + break; + case IEEE80211_PARAM_DROPUNENCRYPTED: + if (value) + ic->ic_flags |= IEEE80211_F_DROPUNENC; + else + ic->ic_flags &= ~IEEE80211_F_DROPUNENC; + break; + case IEEE80211_PARAM_COUNTERMEASURES: + if (value) { + if ((ic->ic_flags & IEEE80211_F_WPA) == 0) + return -EINVAL; + ic->ic_flags |= IEEE80211_F_COUNTERM; + } else + ic->ic_flags &= ~IEEE80211_F_COUNTERM; + break; + case IEEE80211_PARAM_DRIVER_CAPS: + ic->ic_caps = value; /* NB: for testing */ + break; + case IEEE80211_PARAM_MACCMD: + acl = ic->ic_acl; + switch (value) { + case IEEE80211_MACCMD_POLICY_OPEN: + case IEEE80211_MACCMD_POLICY_ALLOW: + case IEEE80211_MACCMD_POLICY_DENY: + if (acl == NULL) { + acl = ieee80211_aclator_get("mac"); + if (acl == NULL || !acl->iac_attach(ic)) + return -EINVAL; + ic->ic_acl = acl; + } + acl->iac_setpolicy(ic, value); + break; + case IEEE80211_MACCMD_FLUSH: + if (acl != NULL) + acl->iac_flush(ic); + /* NB: silently ignore when not in use */ + break; + case IEEE80211_MACCMD_DETACH: + if (acl != NULL) { + ic->ic_acl = NULL; + acl->iac_detach(ic); + } + break; + } + break; + case IEEE80211_PARAM_WME: + if (ic->ic_opmode != IEEE80211_M_STA) + return -EINVAL; + if (value) + ic->ic_flags |= IEEE80211_F_WME; + else + ic->ic_flags &= ~IEEE80211_F_WME; + break; + case IEEE80211_PARAM_HIDESSID: + if (value) + ic->ic_flags |= IEEE80211_F_HIDESSID; + else + ic->ic_flags &= ~IEEE80211_F_HIDESSID; + retv = ENETRESET; + break; + case IEEE80211_PARAM_APBRIDGE: + if (value == 0) + ic->ic_flags |= IEEE80211_F_NOBRIDGE; + else + ic->ic_flags &= ~IEEE80211_F_NOBRIDGE; + break; + case IEEE80211_PARAM_INACT: + ic->ic_inact_run = value / IEEE80211_INACT_WAIT; + break; + case IEEE80211_PARAM_INACT_AUTH: + ic->ic_inact_auth = value / IEEE80211_INACT_WAIT; + break; + case IEEE80211_PARAM_INACT_INIT: + ic->ic_inact_init = value / IEEE80211_INACT_WAIT; + break; + default: + retv = EOPNOTSUPP; + break; + } + if (retv == ENETRESET) + retv = IS_UP_AUTO(ic) ? (*ic->ic_init)(ic->ic_dev) : 0; + return -retv; +} +EXPORT_SYMBOL(ieee80211_ioctl_setparam); + +static int +cap2cipher(int flag) +{ + switch (flag) { + case IEEE80211_C_WEP: return IEEE80211_CIPHER_WEP; + case IEEE80211_C_AES: return IEEE80211_CIPHER_AES_OCB; + case IEEE80211_C_AES_CCM: return IEEE80211_CIPHER_AES_CCM; + case IEEE80211_C_CKIP: return IEEE80211_CIPHER_CKIP; + case IEEE80211_C_TKIP: return IEEE80211_CIPHER_TKIP; + } + return -1; +} + +int +ieee80211_ioctl_getparam(struct ieee80211com *ic, struct iw_request_info *info, + void *w, char *extra) +{ + struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn; + struct ifmediareq imr; + int *param = (int *) extra; + u_int m; + + switch (param[0]) { + case IEEE80211_PARAM_TURBO: + (*ic->ic_media.ifm_status)(ic->ic_dev, &imr); + param[0] = (imr.ifm_active & IFM_IEEE80211_TURBO) != 0; + break; + case IEEE80211_PARAM_MODE: + (*ic->ic_media.ifm_status)(ic->ic_dev, &imr); + switch (IFM_MODE(imr.ifm_active)) { + case IFM_IEEE80211_11A: + param[0] = 1; + break; + case IFM_IEEE80211_11B: + param[0] = 2; + break; + case IFM_IEEE80211_11G: + param[0] = 3; + break; + case IFM_IEEE80211_FH: + param[0] = 4; + break; + case IFM_AUTO: + param[0] = 0; + break; + default: + return -EINVAL; + } + break; + case IEEE80211_PARAM_AUTHMODE: + if (ic->ic_flags & IEEE80211_F_WPA) + param[0] = IEEE80211_AUTH_WPA; + else + param[0] = ic->ic_bss->ni_authmode; + break; + case IEEE80211_PARAM_PROTMODE: + param[0] = ic->ic_protmode; + break; + case IEEE80211_PARAM_MCASTCIPHER: + param[0] = rsn->rsn_mcastcipher; + break; + case IEEE80211_PARAM_MCASTKEYLEN: + param[0] = rsn->rsn_mcastkeylen; + break; + case IEEE80211_PARAM_UCASTCIPHERS: + param[0] = 0; + for (m = 0x1; m != 0; m <<= 1) + if (rsn->rsn_ucastcipherset & m) + param[0] |= 1<rsn_ucastcipher; + break; + case IEEE80211_PARAM_UCASTKEYLEN: + param[0] = rsn->rsn_ucastkeylen; + break; + case IEEE80211_PARAM_KEYMGTALGS: + param[0] = rsn->rsn_keymgmtset; + break; + case IEEE80211_PARAM_RSNCAPS: + param[0] = rsn->rsn_caps; + break; + case IEEE80211_PARAM_WPA: + switch (ic->ic_flags & IEEE80211_F_WPA) { + case IEEE80211_F_WPA1: + param[0] = 1; + break; + case IEEE80211_F_WPA2: + param[0] = 2; + break; + case IEEE80211_F_WPA1 | IEEE80211_F_WPA2: + param[0] = 3; + break; + default: + param[0] = 0; + break; + } + break; + case IEEE80211_PARAM_ROAMING: + param[0] = ic->ic_roaming; + break; + case IEEE80211_PARAM_PRIVACY: + param[0] = (ic->ic_flags & IEEE80211_F_PRIVACY) != 0; + break; + case IEEE80211_PARAM_DROPUNENCRYPTED: + param[0] = (ic->ic_flags & IEEE80211_F_DROPUNENC) != 0; + break; + case IEEE80211_PARAM_COUNTERMEASURES: + param[0] = (ic->ic_flags & IEEE80211_F_COUNTERM) != 0; + break; + case IEEE80211_PARAM_DRIVER_CAPS: + param[0] = ic->ic_caps; + break; + case IEEE80211_PARAM_WME: + param[0] = (ic->ic_flags & IEEE80211_F_WME) != 0; + break; + case IEEE80211_PARAM_HIDESSID: + param[0] = (ic->ic_flags & IEEE80211_F_HIDESSID) != 0; + break; + case IEEE80211_PARAM_APBRIDGE: + param[0] = (ic->ic_flags & IEEE80211_F_NOBRIDGE) == 0; + break; + case IEEE80211_PARAM_INACT: + param[0] = ic->ic_inact_run * IEEE80211_INACT_WAIT; + break; + case IEEE80211_PARAM_INACT_AUTH: + param[0] = ic->ic_inact_auth * IEEE80211_INACT_WAIT; + break; + case IEEE80211_PARAM_INACT_INIT: + param[0] = ic->ic_inact_init * IEEE80211_INACT_WAIT; + break; + default: + return -EOPNOTSUPP; + } + return 0; +} +EXPORT_SYMBOL(ieee80211_ioctl_getparam); + +int +ieee80211_ioctl_setoptie(struct ieee80211com *ic, struct iw_request_info *info, + void *w, char *extra) +{ + union iwreq_data *u = w; + void *ie; + + /* + * NB: Doing this for ap operation could be useful (e.g. for + * WPA and/or WME) except that it typically is worthless + * without being able to intervene when processing + * association response frames--so disallow it for now. + */ + if (ic->ic_opmode != IEEE80211_M_STA) + return -EINVAL; + /* NB: data.length is validated by the wireless extensions code */ + MALLOC(ie, void *, u->data.length, M_DEVBUF, M_WAITOK); + if (ie == NULL) + return -ENOMEM; + memcpy(ie, extra, u->data.length); + /* XXX sanity check data? */ + if (ic->ic_opt_ie != NULL) + FREE(ic->ic_opt_ie, M_DEVBUF); + ic->ic_opt_ie = ie; + ic->ic_opt_ie_len = u->data.length; + return 0; +} +EXPORT_SYMBOL(ieee80211_ioctl_setoptie); + +int +ieee80211_ioctl_getoptie(struct ieee80211com *ic, struct iw_request_info *info, + void *w, char *extra) +{ + union iwreq_data *u = w; + + if (ic->ic_opt_ie == NULL) + return -EINVAL; + if (u->data.length < ic->ic_opt_ie_len) + return -EINVAL; + u->data.length = ic->ic_opt_ie_len; + memcpy(extra, ic->ic_opt_ie, u->data.length); + return 0; +} +EXPORT_SYMBOL(ieee80211_ioctl_getoptie); + +int +ieee80211_ioctl_setkey(struct ieee80211com *ic, struct iw_request_info *info, + void *w, char *extra) +{ + struct ieee80211req_key *ik = (struct ieee80211req_key *)extra; + struct ieee80211_node *ni; + struct ieee80211_key *wk; + u_int16_t kid; + int error; + + /* NB: cipher support is verified by ieee80211_crypt_newkey */ + /* NB: this also checks ik->ik_keylen > sizeof(wk->wk_key) */ + if (ik->ik_keylen > sizeof(ik->ik_keydata)) + return -E2BIG; + kid = ik->ik_keyix; + if (kid == IEEE80211_KEYIX_NONE) { + /* XXX unicast keys currently must be tx/rx */ + if (ik->ik_flags != (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV)) + return -EINVAL; + if (ic->ic_opmode == IEEE80211_M_STA) { + ni = ic->ic_bss; + if (!IEEE80211_ADDR_EQ(ik->ik_macaddr, ni->ni_bssid)) + return -EADDRNOTAVAIL; + } else + ni = ieee80211_find_node(ic, ik->ik_macaddr); + if (ni == NULL) + return -ENOENT; + wk = &ni->ni_ucastkey; + } else { + if (kid >= IEEE80211_WEP_NKID) + return -EINVAL; + wk = &ic->ic_nw_keys[kid]; + ni = NULL; + } + error = 0; + ieee80211_key_update_begin(ic); + if (ieee80211_crypto_newkey(ic, ik->ik_type, wk)) { + wk->wk_keylen = ik->ik_keylen; + /* NB: MIC presence is implied by cipher type */ + if (wk->wk_keylen > IEEE80211_KEYBUF_SIZE) + wk->wk_keylen = IEEE80211_KEYBUF_SIZE; + wk->wk_keyrsc = ik->ik_keyrsc; + wk->wk_keytsc = 0; /* new key, reset */ + wk->wk_flags |= + ik->ik_flags & (IEEE80211_KEY_XMIT|IEEE80211_KEY_RECV); + memset(wk->wk_key, 0, sizeof(wk->wk_key)); + memcpy(wk->wk_key, ik->ik_keydata, ik->ik_keylen); + if (!ieee80211_crypto_setkey(ic, wk, + ni != NULL ? ni->ni_macaddr : ik->ik_macaddr)) + error = -EIO; + else if ((ik->ik_flags & IEEE80211_KEY_DEFAULT)) + ic->ic_def_txkey = kid; + } else + error = -ENXIO; + ieee80211_key_update_end(ic); + if (ni != NULL && ni != ic->ic_bss) + ieee80211_free_node(ic, ni); + return error; +} +EXPORT_SYMBOL(ieee80211_ioctl_setkey); + +static int +ieee80211_ioctl_getkey(struct ieee80211com *ic, struct iwreq *iwr) +{ + struct ieee80211_node *ni; + struct ieee80211req_key ik; + struct ieee80211_key *wk; + const struct ieee80211_cipher *cip; + u_int kid; + + if (iwr->u.data.length != sizeof(ik)) + return -EINVAL; + if (copy_from_user(&ik, iwr->u.data.pointer, sizeof(ik))) + return -EFAULT; + kid = ik.ik_keyix; + if (kid == IEEE80211_KEYIX_NONE) { + ni = ieee80211_find_node(ic, ik.ik_macaddr); + if (ni == NULL) + return -EINVAL; /* XXX */ + wk = &ni->ni_ucastkey; + } else { + if (kid >= IEEE80211_WEP_NKID) + return -EINVAL; + wk = &ic->ic_nw_keys[kid]; + IEEE80211_ADDR_COPY(&ik.ik_macaddr, ic->ic_bss->ni_macaddr); + ni = NULL; + } + cip = wk->wk_cipher; + ik.ik_type = cip->ic_cipher; + ik.ik_keylen = wk->wk_keylen; + ik.ik_flags = wk->wk_flags & (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV); + if (wk->wk_keyix == ic->ic_def_txkey) + ik.ik_flags |= IEEE80211_KEY_DEFAULT; + if (capable(CAP_NET_ADMIN)) { + /* NB: only root can read key data */ + ik.ik_keyrsc = wk->wk_keyrsc; + ik.ik_keytsc = wk->wk_keytsc; + memcpy(ik.ik_keydata, wk->wk_key, wk->wk_keylen); + if (cip->ic_cipher == IEEE80211_CIPHER_TKIP) { + memcpy(ik.ik_keydata+wk->wk_keylen, + wk->wk_key + IEEE80211_KEYBUF_SIZE, + IEEE80211_MICBUF_SIZE); + ik.ik_keylen += IEEE80211_MICBUF_SIZE; + } + } else { + ik.ik_keyrsc = 0; + ik.ik_keytsc = 0; + memset(ik.ik_keydata, 0, sizeof(ik.ik_keydata)); + } + if (ni != NULL) + ieee80211_free_node(ic, ni); + return (copy_to_user(iwr->u.data.pointer, &ik, sizeof(ik)) ? + -EFAULT : 0); +} + +int +ieee80211_ioctl_delkey(struct ieee80211com *ic, struct iw_request_info *info, + void *w, char *extra) +{ + struct ieee80211req_del_key *dk = (struct ieee80211req_del_key *)extra; + int kid; + + kid = dk->idk_keyix; + /* XXX u_int8_t -> u_int16_t */ + if (dk->idk_keyix == (u_int8_t) IEEE80211_KEYIX_NONE) { + struct ieee80211_node *ni = + ieee80211_find_node(ic, dk->idk_macaddr); + if (ni == NULL) + return -EINVAL; /* XXX */ + /* XXX error return */ + ieee80211_crypto_delkey(ic, &ni->ni_ucastkey); + ieee80211_free_node(ic, ni); + } else { + if (kid >= IEEE80211_WEP_NKID) + return -EINVAL; + /* XXX error return */ + ieee80211_crypto_delkey(ic, &ic->ic_nw_keys[kid]); + } + return 0; +} +EXPORT_SYMBOL(ieee80211_ioctl_delkey); + +int +ieee80211_ioctl_setmlme(struct ieee80211com *ic, struct iw_request_info *info, + void *w, char *extra) +{ + struct ieee80211req_mlme *mlme = (struct ieee80211req_mlme *)extra; + struct ieee80211_node *ni; + + switch (mlme->im_op) { + case IEEE80211_MLME_ASSOC: + if (ic->ic_opmode != IEEE80211_M_STA) + return -EINVAL; + /* XXX must be in S_SCAN state? */ + + if (ic->ic_des_esslen != 0) { + /* + * Desired ssid specified; must match both bssid and + * ssid to distinguish ap advertising multiple ssid's. + */ + ni = ieee80211_find_node_with_ssid(ic, mlme->im_macaddr, + ic->ic_des_esslen, ic->ic_des_essid); + } else { + /* + * Normal case; just match bssid. + */ + ni = ieee80211_find_node(ic, mlme->im_macaddr); + } + if (ni == NULL) + return -EINVAL; + if (!ieee80211_sta_join(ic, ni)) { + /* NB: from AP scan; don't free, just unref */ + ieee80211_unref_node(&ni); + return -EINVAL; + } + break; + case IEEE80211_MLME_DISASSOC: + case IEEE80211_MLME_DEAUTH: + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + /* XXX not quite right */ + ieee80211_new_state(ic, IEEE80211_S_INIT, + mlme->im_reason); + break; + case IEEE80211_M_HOSTAP: + ni = ieee80211_find_node(ic, mlme->im_macaddr); + if (ni == NULL) + return -EINVAL; + IEEE80211_SEND_MGMT(ic, ni, + mlme->im_op == IEEE80211_MLME_DEAUTH ? + IEEE80211_FC0_SUBTYPE_DEAUTH + : IEEE80211_FC0_SUBTYPE_DISASSOC, + mlme->im_reason); + ieee80211_node_leave(ic, ni); + break; + default: + return -EINVAL; + } + break; + case IEEE80211_MLME_AUTHORIZE: + case IEEE80211_MLME_UNAUTHORIZE: + if (ic->ic_opmode != IEEE80211_M_HOSTAP) + return -EINVAL; + ni = ieee80211_find_node(ic, mlme->im_macaddr); + if (ni == NULL) + return -EINVAL; + if (mlme->im_op == IEEE80211_MLME_AUTHORIZE) + ieee80211_node_authorize(ic, ni); + else + ieee80211_node_unauthorize(ic, ni); + ieee80211_free_node(ic, ni); + break; + default: + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL(ieee80211_ioctl_setmlme); + +int +ieee80211_ioctl_addmac(struct ieee80211com *ic, struct iw_request_info *info, + void *w, char *extra) +{ + struct sockaddr *sa = (struct sockaddr *)extra; + const struct ieee80211_aclator *acl = ic->ic_acl; + + if (acl == NULL) { + acl = ieee80211_aclator_get("mac"); + if (acl == NULL || !acl->iac_attach(ic)) + return -EINVAL; + ic->ic_acl = acl; + } + acl->iac_add(ic, sa->sa_data); + return 0; +} +EXPORT_SYMBOL(ieee80211_ioctl_addmac); + +int +ieee80211_ioctl_delmac(struct ieee80211com *ic, struct iw_request_info *info, + void *w, char *extra) +{ + struct sockaddr *sa = (struct sockaddr *)extra; + const struct ieee80211_aclator *acl = ic->ic_acl; + + if (acl == NULL) { + acl = ieee80211_aclator_get("mac"); + if (acl == NULL || !acl->iac_attach(ic)) + return -EINVAL; + ic->ic_acl = acl; + } + acl->iac_remove(ic, sa->sa_data); + return 0; +} +EXPORT_SYMBOL(ieee80211_ioctl_delmac); + +int +ieee80211_ioctl_chanlist(struct ieee80211com *ic, struct iw_request_info *info, + void *w, char *extra) +{ + struct ieee80211req_chanlist *list = + (struct ieee80211req_chanlist *)extra; + u_char chanlist[roundup(IEEE80211_CHAN_MAX, NBBY)]; + int i, j; + + memset(chanlist, 0, sizeof(chanlist)); + /* + * Since channel 0 is not available for DS, channel 1 + * is assigned to LSB on WaveLAN. + */ + if (ic->ic_phytype == IEEE80211_T_DS) + i = 1; + else + i = 0; + for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) { + /* + * NB: silently discard unavailable channels so users + * can specify 1-255 to get all available channels. + */ + if (isset(list->ic_channels, j) && isset(ic->ic_chan_avail, i)) + setbit(chanlist, i); + } + if (ic->ic_ibss_chan == NULL || + isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) { + for (i = 0; i <= IEEE80211_CHAN_MAX; i++) + if (isset(chanlist, i)) { + ic->ic_ibss_chan = &ic->ic_channels[i]; + goto found; + } + return EINVAL; /* no active channels */ +found: + ; + } + memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active)); + if (ic->ic_bss->ni_chan == IEEE80211_CHAN_ANYC || + isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan))) + ic->ic_bss->ni_chan = ic->ic_ibss_chan; + return IS_UP_AUTO(ic) ? -(*ic->ic_init)(ic->ic_dev) : 0; +} +EXPORT_SYMBOL(ieee80211_ioctl_chanlist); + +static int +ieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct iwreq *iwr) +{ + struct ieee80211_node *ni; + struct ieee80211req_wpaie wpaie; + + if (iwr->u.data.length != sizeof(wpaie)) + return -EINVAL; + if (copy_from_user(&wpaie, iwr->u.data.pointer, IEEE80211_ADDR_LEN)) + return -EFAULT; + ni = ieee80211_find_node(ic, wpaie.wpa_macaddr); + if (ni == NULL) + return -EINVAL; /* XXX */ + memset(wpaie.wpa_ie, 0, sizeof(wpaie.wpa_ie)); + if (ni->ni_wpa_ie != NULL) { + int ielen = ni->ni_wpa_ie[1] + 2; + if (ielen > sizeof(wpaie.wpa_ie)) + ielen = sizeof(wpaie.wpa_ie); + memcpy(wpaie.wpa_ie, ni->ni_wpa_ie, ielen); + } + ieee80211_free_node(ic, ni); + return (copy_to_user(iwr->u.data.pointer, &wpaie, sizeof(wpaie)) ? + -EFAULT : 0); +} + +#define IW_PRIV_TYPE_OPTIE IW_PRIV_TYPE_BYTE | IEEE80211_MAX_OPT_IE +#define IW_PRIV_TYPE_KEY \ + IW_PRIV_TYPE_BYTE | sizeof(struct ieee80211req_key) +#define IW_PRIV_TYPE_DELKEY \ + IW_PRIV_TYPE_BYTE | sizeof(struct ieee80211req_del_key) +#define IW_PRIV_TYPE_MLME \ + IW_PRIV_TYPE_BYTE | sizeof(struct ieee80211req_mlme) +#define IW_PRIV_TYPE_CHANLIST \ + IW_PRIV_TYPE_BYTE | sizeof(struct ieee80211req_chanlist) + +static const struct iw_priv_args ieee80211_priv_args[] = { + /* NB: setoptie & getoptie are !IW_PRIV_SIZE_FIXED */ + { IEEE80211_IOCTL_SETOPTIE, + IW_PRIV_TYPE_OPTIE, 0, "setoptie" }, + { IEEE80211_IOCTL_GETOPTIE, + 0, IW_PRIV_TYPE_OPTIE, "getoptie" }, + { IEEE80211_IOCTL_SETKEY, + IW_PRIV_TYPE_KEY | IW_PRIV_SIZE_FIXED, 0, "setkey" }, + { IEEE80211_IOCTL_DELKEY, + IW_PRIV_TYPE_DELKEY | IW_PRIV_SIZE_FIXED, 0, "delkey" }, + { IEEE80211_IOCTL_SETMLME, + IW_PRIV_TYPE_MLME | IW_PRIV_SIZE_FIXED, 0, "setmlme" }, + { IEEE80211_IOCTL_ADDMAC, + IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0,"addmac" }, + { IEEE80211_IOCTL_DELMAC, + IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0,"delmac" }, + { IEEE80211_IOCTL_CHANLIST, + IW_PRIV_TYPE_CHANLIST | IW_PRIV_SIZE_FIXED, 0,"chanlist" }, +#if WIRELESS_EXT >= 12 + { IEEE80211_IOCTL_SETPARAM, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "setparam" }, + /* + * These depends on sub-ioctl support which added in version 12. + */ + { IEEE80211_IOCTL_GETPARAM, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getparam" }, + + /* sub-ioctl handlers */ + { IEEE80211_IOCTL_SETPARAM, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "" }, + { IEEE80211_IOCTL_GETPARAM, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "" }, + + /* sub-ioctl definitions */ + { IEEE80211_PARAM_TURBO, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "turbo" }, + { IEEE80211_PARAM_TURBO, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_turbo" }, + { IEEE80211_PARAM_MODE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "mode" }, + { IEEE80211_PARAM_MODE, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_mode" }, + { IEEE80211_PARAM_AUTHMODE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "authmode" }, + { IEEE80211_PARAM_AUTHMODE, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_authmode" }, + { IEEE80211_PARAM_PROTMODE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "protmode" }, + { IEEE80211_PARAM_PROTMODE, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_protmode" }, + { IEEE80211_PARAM_MCASTCIPHER, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "mcastcipher" }, + { IEEE80211_PARAM_MCASTCIPHER, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_mcastcipher" }, + { IEEE80211_PARAM_MCASTKEYLEN, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "mcastkeylen" }, + { IEEE80211_PARAM_MCASTKEYLEN, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_mcastkeylen" }, + { IEEE80211_PARAM_UCASTCIPHERS, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ucastciphers" }, + { IEEE80211_PARAM_UCASTCIPHERS, + /* + * NB: can't use "get_ucastciphers" 'cuz iwpriv command names + * must be = 12 */ +}; + +void +ieee80211_ioctl_iwsetup(struct iw_handler_def *def) +{ +#define N(a) (sizeof(a)/sizeof(a[0])) + def->private_args = (struct iw_priv_args *) ieee80211_priv_args; + def->num_private_args = N(ieee80211_priv_args); +#undef N +} +EXPORT_SYMBOL(ieee80211_ioctl_iwsetup); + +/* + * Handle private ioctl requests. + */ +int +ieee80211_ioctl(struct ieee80211com *ic, struct ifreq *ifr, int cmd) +{ + + switch (cmd) { + case SIOCG80211STATS: + return copy_to_user(ifr->ifr_data, &ic->ic_stats, + sizeof (ic->ic_stats)) ? -EFAULT : 0; + case IEEE80211_IOCTL_GETKEY: + return ieee80211_ioctl_getkey(ic, (struct iwreq *) ifr); + case IEEE80211_IOCTL_GETWPAIE: + return ieee80211_ioctl_getwpaie(ic, (struct iwreq *) ifr); + } + return -EOPNOTSUPP; +} +EXPORT_SYMBOL(ieee80211_ioctl); +#endif /* CONFIG_NET_WIRELESS */ diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_xauth.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_xauth.c --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/ieee80211_xauth.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/ieee80211_xauth.c 2005-02-24 13:06:17.409115432 -0800 @@ -0,0 +1,100 @@ +/*- + * Copyright (c) 2004 Video54 Technologies, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef EXPORT_SYMTAB +#define EXPORT_SYMTAB +#endif + +/* + * External authenticator placeholder module. + * + * This support is optional; it is only used when the 802.11 layer's + * authentication mode is set to use 802.1x or WPA is enabled separately + * (for WPA-PSK). If compiled as a module this code does not need + * to be present unless 802.1x/WPA is in use. + * + * The authenticator hooks into the 802.11 layer. At present we use none + * of the available callbacks--the user mode authenticator process works + * entirely from messages about stations joining and leaving. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "if_media.h" +#include "if_llc.h" +#include "if_ethersubr.h" + +#include + +/* + * Module glue. + */ + +MODULE_AUTHOR("Errno Consulting, Sam Leffler"); +MODULE_DESCRIPTION("802.11 wireless support: external (user mode) authenticator"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("Dual BSD/GPL"); +#endif + +/* + * One module handles everything for now. May want + * to split things up for embedded applications. + */ +static const struct ieee80211_authenticator xauth = { + .ia_name = "external", + .ia_attach = NULL, + .ia_detach = NULL, + .ia_node_join = NULL, + .ia_node_leave = NULL, +}; + +static int __init +init_ieee80211_xauth(void) +{ + ieee80211_authenticator_register(IEEE80211_AUTH_8021X, &xauth); + ieee80211_authenticator_register(IEEE80211_AUTH_WPA, &xauth); + return 0; +} +module_init(init_ieee80211_xauth); + +static void __exit +exit_ieee80211_xauth(void) +{ + ieee80211_authenticator_unregister(IEEE80211_AUTH_8021X); + ieee80211_authenticator_unregister(IEEE80211_AUTH_WPA); +} +module_exit(exit_ieee80211_xauth); diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/if_ethersubr.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/if_ethersubr.h --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/if_ethersubr.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/if_ethersubr.h 2005-02-24 13:06:17.409115432 -0800 @@ -0,0 +1,75 @@ +/*- + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + * + * $Id: if_ethersubr.h,v 1.3 2005/02/16 16:09:04 samleffler Exp $ + */ + +#ifndef _NET_IF_ETHERSUBR_H_ +#define _NET_IF_ETHERSUBR_H_ + +#define ETHER_ADDR_LEN 6 /* length of an Ethernet address */ +#define ETHER_TYPE_LEN 2 /* length of the Ethernet type field */ +#define ETHER_CRC_LEN 4 /* length of the Ethernet CRC */ +#define ETHER_HDR_LEN (ETHER_ADDR_LEN*2+ETHER_TYPE_LEN) +#define ETHER_MAX_LEN 1518 + +#define ETHERMTU (ETHER_MAX_LEN-ETHER_HDR_LEN-ETHER_CRC_LEN) + +/* + * Structure of a 10Mb/s Ethernet header. + */ +struct ether_header { + u_char ether_dhost[ETHER_ADDR_LEN]; + u_char ether_shost[ETHER_ADDR_LEN]; + u_short ether_type; +}; + +#ifndef ETHERTYPE_PAE +#define ETHERTYPE_PAE 0x888e /* EAPOL PAE/802.1x */ +#endif +#ifndef ETHERTYPE_IP +#define ETHERTYPE_IP 0x0800 /* IP protocol */ +#endif + +/* + * Structure of a 48-bit Ethernet address. + */ +struct ether_addr { + u_char octet[ETHER_ADDR_LEN]; +}; + +#define ETHER_IS_MULTICAST(addr) (*(addr) & 0x01) /* is address mcast/bcast? */ + +#endif /* _NET_IF_ETHERSUBR_H_ */ diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/if_llc.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/if_llc.h --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/if_llc.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/if_llc.h 2005-02-24 13:06:17.410115280 -0800 @@ -0,0 +1,198 @@ +/*- + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + * + * $Id: if_llc.h,v 1.3 2005/02/16 16:09:09 samleffler Exp $ + * $NetBSD: if_llc.h,v 1.12 1999/11/19 20:41:19 thorpej Exp $ + * $Id: if_llc.h,v 1.3 2005/02/16 16:09:09 samleffler Exp $ + */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)if_llc.h 8.1 (Berkeley) 6/10/93 + * $FreeBSD: src/sys/net/if_llc.h,v 1.9 2002/09/23 06:25:08 alfred Exp $ + */ + +#ifndef _NET_IF_LLC_H_ +#define _NET_IF_LLC_H_ + +/* + * IEEE 802.2 Link Level Control headers, for use in conjunction with + * 802.{3,4,5} media access control methods. + * + * Headers here do not use bit fields due to shortcommings in many + * compilers. + */ + +struct llc { + u_int8_t llc_dsap; + u_int8_t llc_ssap; + union { + struct { + u_int8_t control; + u_int8_t format_id; + u_int8_t class; + u_int8_t window_x2; + } type_u __packed; + struct { + u_int8_t num_snd_x2; + u_int8_t num_rcv_x2; + } type_i __packed; + struct { + u_int8_t control; + u_int8_t num_rcv_x2; + } type_s __packed; + struct { + u_int8_t control; + /* + * We cannot put the following fields in a structure because + * the structure rounding might cause padding. + */ + u_int8_t frmr_rej_pdu0; + u_int8_t frmr_rej_pdu1; + u_int8_t frmr_control; + u_int8_t frmr_control_ext; + u_int8_t frmr_cause; + } type_frmr __packed; + struct { + u_int8_t control; + u_int8_t org_code[3]; + u_int16_t ether_type; + } type_snap __packed; + struct { + u_int8_t control; + u_int8_t control_ext; + } type_raw __packed; + } llc_un /* XXX __packed ??? */; +} __packed; + +struct frmrinfo { + u_int8_t frmr_rej_pdu0; + u_int8_t frmr_rej_pdu1; + u_int8_t frmr_control; + u_int8_t frmr_control_ext; + u_int8_t frmr_cause; +} __packed; + +#define llc_control llc_un.type_u.control +#define llc_control_ext llc_un.type_raw.control_ext +#define llc_fid llc_un.type_u.format_id +#define llc_class llc_un.type_u.class +#define llc_window llc_un.type_u.window_x2 +#define llc_frmrinfo llc_un.type_frmr.frmr_rej_pdu0 +#define llc_frmr_pdu0 llc_un.type_frmr.frmr_rej_pdu0 +#define llc_frmr_pdu1 llc_un.type_frmr.frmr_rej_pdu1 +#define llc_frmr_control llc_un.type_frmr.frmr_control +#define llc_frmr_control_ext llc_un.type_frmr.frmr_control_ext +#define llc_frmr_cause llc_un.type_frmr.frmr_cause +#define llc_snap llc_un.type_snap + +/* + * Don't use sizeof(struct llc_un) for LLC header sizes + */ +#define LLC_ISFRAMELEN 4 +#define LLC_UFRAMELEN 3 +#define LLC_FRMRLEN 7 +#define LLC_SNAPFRAMELEN 8 + +/* + * Unnumbered LLC format commands + */ +#define LLC_UI 0x3 +#define LLC_UI_P 0x13 +#define LLC_DISC 0x43 +#define LLC_DISC_P 0x53 +#define LLC_UA 0x63 +#define LLC_UA_P 0x73 +#define LLC_TEST 0xe3 +#define LLC_TEST_P 0xf3 +#define LLC_FRMR 0x87 +#define LLC_FRMR_P 0x97 +#define LLC_DM 0x0f +#define LLC_DM_P 0x1f +#define LLC_XID 0xaf +#define LLC_XID_P 0xbf +#define LLC_SABME 0x6f +#define LLC_SABME_P 0x7f + +/* + * Supervisory LLC commands + */ +#define LLC_RR 0x01 +#define LLC_RNR 0x05 +#define LLC_REJ 0x09 + +/* + * Info format - dummy only + */ +#define LLC_INFO 0x00 + +/* + * ISO PDTR 10178 contains among others + */ +#define LLC_X25_LSAP 0x7e +#define LLC_SNAP_LSAP 0xaa +#define LLC_ISO_LSAP 0xfe + +#endif /* _NET_IF_LLC_H_ */ diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/if_media.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/if_media.c --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/if_media.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/if_media.c 2005-02-24 13:06:17.411115128 -0800 @@ -0,0 +1,520 @@ +/* $NetBSD: if_media.c,v 1.1 1997/03/17 02:55:15 thorpej Exp $ */ +/* $FreeBSD: src/sys/net/if_media.c,v 1.17 2002/06/26 03:34:50 ken Exp $ */ +/* $Id: if_media.c,v 1.2 2004/08/05 17:34:54 samleffler Exp $ */ + +/* + * Copyright (c) 1997 + * Jonathan Stone and Jason R. Thorpe. All rights reserved. + * + * This software is derived from information provided by Matt Thomas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Jonathan Stone + * and Jason R. Thorpe for the NetBSD Project. + * 4. The names of the authors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * BSD/OS-compatible network interface media selection. + * + * Where it is safe to do so, this code strays slightly from the BSD/OS + * design. Software which uses the API (device drivers, basically) + * shouldn't notice any difference. + * + * Many thanks to Matt Thomas for providing the information necessary + * to implement this interface. + */ + +#ifndef EXPORT_SYMTAB +#define EXPORT_SYMTAB +#endif + +#include +#include +#include +#include +#include +#include +#include +#ifndef ifr_media +#define ifr_media ifr_ifru.ifru_ivalue +#endif + +#include + +#include "if_media.h" + +/* + * Compile-time options: + * IFMEDIA_DEBUG: + * turn on implementation-level debug printfs. + * Useful for debugging newly-ported drivers. + */ + +struct ifmedia_entry *ifmedia_match(struct ifmedia *ifm, int flags, int mask); + +#ifdef IFMEDIA_DEBUG +int ifmedia_debug = 0; +static void ifmedia_printword(int); +#endif + +/* + * Initialize if_media struct for a specific interface instance. + */ +void +ifmedia_init( + struct ifmedia *ifm, + int dontcare_mask, + ifm_change_cb_t change_callback, + ifm_stat_cb_t status_callback) +{ + + LIST_INIT(&ifm->ifm_list); + ifm->ifm_cur = NULL; + ifm->ifm_media = 0; + ifm->ifm_mask = dontcare_mask; /* IF don't-care bits */ + ifm->ifm_change = change_callback; + ifm->ifm_status = status_callback; +} + +void +ifmedia_removeall(struct ifmedia *ifm) +{ + struct ifmedia_entry *entry; + + for (entry = LIST_FIRST(&ifm->ifm_list); entry; + entry = LIST_FIRST(&ifm->ifm_list)) { + LIST_REMOVE(entry, ifm_list); + kfree(entry); + } +} + +/* + * Add a media configuration to the list of supported media + * for a specific interface instance. + */ +void +ifmedia_add(struct ifmedia *ifm, int mword, int data, void *aux) +{ + register struct ifmedia_entry *entry; + +#ifdef IFMEDIA_DEBUG + if (ifmedia_debug) { + if (ifm == NULL) { + printk("ifmedia_add: null ifm\n"); + return; + } + printk("Adding entry for "); + ifmedia_printword(mword); + } +#endif + + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + if (entry == NULL) + panic("ifmedia_add: can't malloc entry"); + + entry->ifm_media = mword; + entry->ifm_data = data; + entry->ifm_aux = aux; + + LIST_INSERT_HEAD(&ifm->ifm_list, entry, ifm_list); +} + +/* + * Add an array of media configurations to the list of + * supported media for a specific interface instance. + */ +void +ifmedia_list_add(struct ifmedia *ifm, struct ifmedia_entry *lp, int count) +{ + int i; + + for (i = 0; i < count; i++) + ifmedia_add(ifm, lp[i].ifm_media, lp[i].ifm_data, + lp[i].ifm_aux); +} + +/* + * Set the default active media. + * + * Called by device-specific code which is assumed to have already + * selected the default media in hardware. We do _not_ call the + * media-change callback. + */ +void +ifmedia_set(struct ifmedia *ifm, int target) + +{ + struct ifmedia_entry *match; + + match = ifmedia_match(ifm, target, ifm->ifm_mask); + + if (match == NULL) { + printk("ifmedia_set: no match for 0x%x/0x%x\n", + target, ~ifm->ifm_mask); + panic("ifmedia_set"); + } + ifm->ifm_cur = match; + +#ifdef IFMEDIA_DEBUG + if (ifmedia_debug) { + printk("ifmedia_set: target "); + ifmedia_printword(target); + printk("ifmedia_set: setting to "); + ifmedia_printword(ifm->ifm_cur->ifm_media); + } +#endif +} + +/* + * Device-independent media ioctl support function. + */ +int +ifmedia_ioctl(struct net_device *dev, struct ifreq *ifr, + struct ifmedia *ifm, u_long cmd) +{ + struct ifmedia_entry *match; + struct ifmediareq *ifmr = (struct ifmediareq *) ifr; + int error = 0, sticky; + + if (dev == NULL || ifr == NULL || ifm == NULL) + return(EINVAL); + + switch (cmd) { + + /* + * Set the current media. + */ + case SIOCSIFMEDIA: + { + struct ifmedia_entry *oldentry; + int oldmedia; + int newmedia = ifr->ifr_media; + + match = ifmedia_match(ifm, newmedia, ifm->ifm_mask); + if (match == NULL) { +#ifdef IFMEDIA_DEBUG + if (ifmedia_debug) { + printk( + "ifmedia_ioctl: no media found for 0x%x\n", + newmedia); + } +#endif + return (ENXIO); + } + + /* + * If no change, we're done. + * XXX Automedia may invole software intervention. + * Keep going in case the the connected media changed. + * Similarly, if best match changed (kernel debugger?). + */ + if ((IFM_SUBTYPE(newmedia) != IFM_AUTO) && + (newmedia == ifm->ifm_media) && + (match == ifm->ifm_cur)) + return 0; + + /* + * We found a match, now make the driver switch to it. + * Make sure to preserve our old media type in case the + * driver can't switch. + */ +#ifdef IFMEDIA_DEBUG + if (ifmedia_debug) { + printk("ifmedia_ioctl: switching %s to ", dev->name); + ifmedia_printword(match->ifm_media); + } +#endif + oldentry = ifm->ifm_cur; + oldmedia = ifm->ifm_media; + ifm->ifm_cur = match; + ifm->ifm_media = newmedia; + error = (*ifm->ifm_change)(dev); + if (error) { + ifm->ifm_cur = oldentry; + ifm->ifm_media = oldmedia; + } + break; + } + + /* + * Get list of available media and current media on interface. + */ + case SIOCGIFMEDIA: + { + struct ifmedia_entry *ep; + int *kptr, count; + int usermax; /* user requested max */ + + kptr = NULL; /* XXX gcc */ + + ifmr->ifm_active = ifmr->ifm_current = ifm->ifm_cur ? + ifm->ifm_cur->ifm_media : IFM_NONE; + ifmr->ifm_mask = ifm->ifm_mask; + ifmr->ifm_status = 0; + (*ifm->ifm_status)(dev, ifmr); + + count = 0; + usermax = 0; + + /* + * If there are more interfaces on the list, count + * them. This allows the caller to set ifmr->ifm_count + * to 0 on the first call to know how much space to + * allocate. + */ + LIST_FOREACH(ep, &ifm->ifm_list, ifm_list) + usermax++; + + /* + * Don't allow the user to ask for too many + * or a negative number. + */ + if (ifmr->ifm_count > usermax) + ifmr->ifm_count = usermax; + else if (ifmr->ifm_count < 0) + return (EINVAL); + + if (ifmr->ifm_count != 0) { + kptr = (int *)kmalloc(ifmr->ifm_count * sizeof(int), + GFP_KERNEL); + + if (kptr == NULL) + return (ENOMEM); + /* + * Get the media words from the interface's list. + */ + ep = LIST_FIRST(&ifm->ifm_list); + for (; ep != NULL && count < ifmr->ifm_count; + ep = LIST_NEXT(ep, ifm_list), count++) + kptr[count] = ep->ifm_media; + + if (ep != NULL) + error = E2BIG; /* oops! */ + } else { + count = usermax; + } + + /* + * We do the copyout on E2BIG, because that's + * just our way of telling userland that there + * are more. This is the behavior I've observed + * under BSD/OS 3.0 + */ + sticky = error; + if ((error == 0 || error == E2BIG) && ifmr->ifm_count != 0) { + error = copy_to_user((caddr_t)ifmr->ifm_ulist, + (caddr_t)kptr, ifmr->ifm_count * sizeof(int)); + } + + if (error == 0) + error = sticky; + + if (ifmr->ifm_count != 0) + kfree(kptr); + + ifmr->ifm_count = count; + break; + } + + default: + return (EINVAL); + } + + return (error); +} + +/* + * Find media entry matching a given ifm word. + * + */ +struct ifmedia_entry * +ifmedia_match(struct ifmedia *ifm, int target, int mask) +{ + struct ifmedia_entry *match, *next; + + match = NULL; + mask = ~mask; + + LIST_FOREACH(next, &ifm->ifm_list, ifm_list) { + if ((next->ifm_media & mask) == (target & mask)) { +#if defined(IFMEDIA_DEBUG) || defined(DIAGNOSTIC) + if (match) { + printk("ifmedia_match: multiple match for " + "0x%x/0x%x\n", target, mask); + } +#endif + match = next; + } + } + + return match; +} + +#ifdef IFMEDIA_DEBUG +struct ifmedia_description ifm_type_descriptions[] = + IFM_TYPE_DESCRIPTIONS; + +struct ifmedia_description ifm_subtype_ethernet_descriptions[] = + IFM_SUBTYPE_ETHERNET_DESCRIPTIONS; + +struct ifmedia_description ifm_subtype_ethernet_option_descriptions[] = + IFM_SUBTYPE_ETHERNET_OPTION_DESCRIPTIONS; + +struct ifmedia_description ifm_subtype_tokenring_descriptions[] = + IFM_SUBTYPE_TOKENRING_DESCRIPTIONS; + +struct ifmedia_description ifm_subtype_tokenring_option_descriptions[] = + IFM_SUBTYPE_TOKENRING_OPTION_DESCRIPTIONS; + +struct ifmedia_description ifm_subtype_fddi_descriptions[] = + IFM_SUBTYPE_FDDI_DESCRIPTIONS; + +struct ifmedia_description ifm_subtype_fddi_option_descriptions[] = + IFM_SUBTYPE_FDDI_OPTION_DESCRIPTIONS; + +struct ifmedia_description ifm_subtype_ieee80211_descriptions[] = + IFM_SUBTYPE_IEEE80211_DESCRIPTIONS; + +struct ifmedia_description ifm_subtype_ieee80211_option_descriptions[] = + IFM_SUBTYPE_IEEE80211_OPTION_DESCRIPTIONS; + +struct ifmedia_description ifm_subtype_ieee80211_mode_descriptions[] = + IFM_SUBTYPE_IEEE80211_MODE_DESCRIPTIONS; + +struct ifmedia_description ifm_subtype_shared_descriptions[] = + IFM_SUBTYPE_SHARED_DESCRIPTIONS; + +struct ifmedia_description ifm_shared_option_descriptions[] = + IFM_SHARED_OPTION_DESCRIPTIONS; + +struct ifmedia_type_to_subtype { + struct ifmedia_description *subtypes; + struct ifmedia_description *options; + struct ifmedia_description *modes; +}; + +/* must be in the same order as IFM_TYPE_DESCRIPTIONS */ +struct ifmedia_type_to_subtype ifmedia_types_to_subtypes[] = { + { + &ifm_subtype_ethernet_descriptions[0], + &ifm_subtype_ethernet_option_descriptions[0], + NULL, + }, + { + &ifm_subtype_tokenring_descriptions[0], + &ifm_subtype_tokenring_option_descriptions[0], + NULL, + }, + { + &ifm_subtype_fddi_descriptions[0], + &ifm_subtype_fddi_option_descriptions[0], + NULL, + }, + { + &ifm_subtype_ieee80211_descriptions[0], + &ifm_subtype_ieee80211_option_descriptions[0], + &ifm_subtype_ieee80211_mode_descriptions[0] + }, +}; + +/* + * print a media word. + */ +static void +ifmedia_printword(int ifmw) +{ + struct ifmedia_description *desc; + struct ifmedia_type_to_subtype *ttos; + int seen_option = 0; + + /* Find the top-level interface type. */ + for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes; + desc->ifmt_string != NULL; desc++, ttos++) + if (IFM_TYPE(ifmw) == desc->ifmt_word) + break; + if (desc->ifmt_string == NULL) { + printk("\n"); + return; + } + printk(desc->ifmt_string); + + /* Any mode. */ + for (desc = ttos->modes; desc && desc->ifmt_string != NULL; desc++) + if (IFM_MODE(ifmw) == desc->ifmt_word) { + if (desc->ifmt_string != NULL) + printk(" mode %s", desc->ifmt_string); + break; + } + + /* + * Check for the shared subtype descriptions first, then the + * type-specific ones. + */ + for (desc = ifm_subtype_shared_descriptions; + desc->ifmt_string != NULL; desc++) + if (IFM_SUBTYPE(ifmw) == desc->ifmt_word) + goto got_subtype; + + for (desc = ttos->subtypes; desc->ifmt_string != NULL; desc++) + if (IFM_SUBTYPE(ifmw) == desc->ifmt_word) + break; + if (desc->ifmt_string == NULL) { + printk(" \n"); + return; + } + + got_subtype: + printk(" %s", desc->ifmt_string); + + /* + * Look for shared options. + */ + for (desc = ifm_shared_option_descriptions; + desc->ifmt_string != NULL; desc++) { + if (ifmw & desc->ifmt_word) { + if (seen_option == 0) + printk(" <"); + printk("%s%s", seen_option++ ? "," : "", + desc->ifmt_string); + } + } + + /* + * Look for subtype-specific options. + */ + for (desc = ttos->options; desc->ifmt_string != NULL; desc++) { + if (ifmw & desc->ifmt_word) { + if (seen_option == 0) + printk(" <"); + printk("%s%s", seen_option++ ? "," : "", + desc->ifmt_string); + } + } + printk("%s\n", seen_option ? ">" : ""); +} +#endif /* IFMEDIA_DEBUG */ + +EXPORT_SYMBOL(ifmedia_ioctl); diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/if_media.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/if_media.h --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/if_media.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/if_media.h 2005-02-24 13:06:17.411115128 -0800 @@ -0,0 +1,478 @@ +/* $NetBSD: if_media.h,v 1.3 1997/03/26 01:19:27 thorpej Exp $ */ +/* $FreeBSD: src/sys/net/if_media.h,v 1.18 2002/07/14 21:58:19 kbyanc Exp $ */ +/* $Id: if_media.h,v 1.2 2004/08/05 17:34:54 samleffler Exp $ */ + +/* + * Copyright (c) 1997 + * Jonathan Stone and Jason R. Thorpe. All rights reserved. + * + * This software is derived from information provided by Matt Thomas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Jonathan Stone + * and Jason R. Thorpe for the NetBSD Project. + * 4. The names of the authors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _NET_IF_MEDIA_H_ +#define _NET_IF_MEDIA_H_ + +/* + * Prototypes and definitions for BSD/OS-compatible network interface + * media selection. + * + * Where it is safe to do so, this code strays slightly from the BSD/OS + * design. Software which uses the API (device drivers, basically) + * shouldn't notice any difference. + * + * Many thanks to Matt Thomas for providing the information necessary + * to implement this interface. + */ + +struct ifmediareq { + char ifm_name[IFNAMSIZ]; /* if name, e.g. "en0" */ + int ifm_current; /* current media options */ + int ifm_mask; /* don't care mask */ + int ifm_status; /* media status */ + int ifm_active; /* active options */ + int ifm_count; /* # entries in ifm_ulist array */ + int *ifm_ulist; /* media words */ +}; +#define SIOCSIFMEDIA _IOWR('i', 55, struct ifreq) /* set net media */ +#define SIOCGIFMEDIA _IOWR('i', 56, struct ifmediareq) /* get net media */ + +#ifdef __KERNEL__ + +#include + +/* + * Driver callbacks for media status and change requests. + */ +struct net_device; +typedef int (*ifm_change_cb_t)(struct net_device *); +typedef void (*ifm_stat_cb_t)(struct net_device *, struct ifmediareq *req); + +/* + * In-kernel representation of a single supported media type. + */ +struct ifmedia_entry { + LIST_ENTRY(ifmedia_entry) ifm_list; + int ifm_media; /* description of this media attachment */ + int ifm_data; /* for driver-specific use */ + void *ifm_aux; /* for driver-specific use */ +}; + +/* + * One of these goes into a network interface's softc structure. + * It is used to keep general media state. + */ +struct ifmedia { + int ifm_mask; /* mask of changes we don't care about */ + int ifm_media; /* current user-set media word */ + struct ifmedia_entry *ifm_cur; /* currently selected media */ + ATH_LIST_HEAD(, ifmedia_entry) ifm_list; /* list of all supported media */ + ifm_change_cb_t ifm_change; /* media change driver callback */ + ifm_stat_cb_t ifm_status; /* media status driver callback */ +}; + +/* Initialize an interface's struct if_media field. */ +void ifmedia_init(struct ifmedia *ifm, int dontcare_mask, + ifm_change_cb_t change_callback, ifm_stat_cb_t status_callback); + +/* Remove all mediums from a struct ifmedia. */ +void ifmedia_removeall( struct ifmedia *ifm); + +/* Add one supported medium to a struct ifmedia. */ +void ifmedia_add(struct ifmedia *ifm, int mword, int data, void *aux); + +/* Add an array (of ifmedia_entry) media to a struct ifmedia. */ +void ifmedia_list_add(struct ifmedia *mp, struct ifmedia_entry *lp, + int count); + +/* Set default media type on initialization. */ +void ifmedia_set(struct ifmedia *ifm, int mword); + +/* Common ioctl function for getting/setting media, called by driver. */ +int ifmedia_ioctl(struct net_device *, struct ifreq *ifr, + struct ifmedia *ifm, u_long cmd); + +#endif /*_KERNEL */ + +/* + * if_media Options word: + * Bits Use + * ---- ------- + * 0-4 Media variant + * 5-7 Media type + * 8-15 Type specific options + * 16-18 Mode (for multi-mode devices) + * 19 RFU + * 20-27 Shared (global) options + * 28-31 Instance + */ + +/* + * Ethernet + */ +#define IFM_ETHER 0x00000020 +#define IFM_10_T 3 /* 10BaseT - RJ45 */ +#define IFM_10_2 4 /* 10Base2 - Thinnet */ +#define IFM_10_5 5 /* 10Base5 - AUI */ +#define IFM_100_TX 6 /* 100BaseTX - RJ45 */ +#define IFM_100_FX 7 /* 100BaseFX - Fiber */ +#define IFM_100_T4 8 /* 100BaseT4 - 4 pair cat 3 */ +#define IFM_100_VG 9 /* 100VG-AnyLAN */ +#define IFM_100_T2 10 /* 100BaseT2 */ +#define IFM_1000_SX 11 /* 1000BaseSX - multi-mode fiber */ +#define IFM_10_STP 12 /* 10BaseT over shielded TP */ +#define IFM_10_FL 13 /* 10BaseFL - Fiber */ +#define IFM_1000_LX 14 /* 1000baseLX - single-mode fiber */ +#define IFM_1000_CX 15 /* 1000baseCX - 150ohm STP */ +#define IFM_1000_T 16 /* 1000baseT - 4 pair cat 5 */ +#define IFM_HPNA_1 17 /* HomePNA 1.0 (1Mb/s) */ +/* note 31 is the max! */ + +#define IFM_ETH_MASTER 0x00000100 /* master mode (1000baseT) */ + +/* + * Token ring + */ +#define IFM_TOKEN 0x00000040 +#define IFM_TOK_STP4 3 /* Shielded twisted pair 4m - DB9 */ +#define IFM_TOK_STP16 4 /* Shielded twisted pair 16m - DB9 */ +#define IFM_TOK_UTP4 5 /* Unshielded twisted pair 4m - RJ45 */ +#define IFM_TOK_UTP16 6 /* Unshielded twisted pair 16m - RJ45 */ +#define IFM_TOK_STP100 7 /* Shielded twisted pair 100m - DB9 */ +#define IFM_TOK_UTP100 8 /* Unshielded twisted pair 100m - RJ45 */ +#define IFM_TOK_ETR 0x00000200 /* Early token release */ +#define IFM_TOK_SRCRT 0x00000400 /* Enable source routing features */ +#define IFM_TOK_ALLR 0x00000800 /* All routes / Single route bcast */ +#define IFM_TOK_DTR 0x00002000 /* Dedicated token ring */ +#define IFM_TOK_CLASSIC 0x00004000 /* Classic token ring */ +#define IFM_TOK_AUTO 0x00008000 /* Automatic Dedicate/Classic token ring */ + +/* + * FDDI + */ +#define IFM_FDDI 0x00000060 +#define IFM_FDDI_SMF 3 /* Single-mode fiber */ +#define IFM_FDDI_MMF 4 /* Multi-mode fiber */ +#define IFM_FDDI_UTP 5 /* CDDI / UTP */ +#define IFM_FDDI_DA 0x00000100 /* Dual attach / single attach */ + +/* + * IEEE 802.11 Wireless + */ +#define IFM_IEEE80211 0x00000080 +/* NB: 0,1,2 are auto, manual, none defined below */ +#define IFM_IEEE80211_FH1 3 /* Frequency Hopping 1Mbps */ +#define IFM_IEEE80211_FH2 4 /* Frequency Hopping 2Mbps */ +#define IFM_IEEE80211_DS1 5 /* Direct Sequence 1Mbps */ +#define IFM_IEEE80211_DS2 6 /* Direct Sequence 2Mbps */ +#define IFM_IEEE80211_DS5 7 /* Direct Sequence 5.5Mbps */ +#define IFM_IEEE80211_DS11 8 /* Direct Sequence 11Mbps */ +#define IFM_IEEE80211_DS22 9 /* Direct Sequence 22Mbps */ +#define IFM_IEEE80211_OFDM6 10 /* OFDM 6Mbps */ +#define IFM_IEEE80211_OFDM9 11 /* OFDM 9Mbps */ +#define IFM_IEEE80211_OFDM12 12 /* OFDM 12Mbps */ +#define IFM_IEEE80211_OFDM18 13 /* OFDM 18Mbps */ +#define IFM_IEEE80211_OFDM24 14 /* OFDM 24Mbps */ +#define IFM_IEEE80211_OFDM36 15 /* OFDM 36Mbps */ +#define IFM_IEEE80211_OFDM48 16 /* OFDM 48Mbps */ +#define IFM_IEEE80211_OFDM54 17 /* OFDM 54Mbps */ +#define IFM_IEEE80211_OFDM72 18 /* OFDM 72Mbps */ + +#define IFM_IEEE80211_ADHOC 0x00000100 /* Operate in Adhoc mode */ +#define IFM_IEEE80211_HOSTAP 0x00000200 /* Operate in Host AP mode */ +#define IFM_IEEE80211_IBSS 0x00000400 /* Operate in IBSS mode */ +#define IFM_IEEE80211_IBSSMASTER 0x00000800 /* Operate as an IBSS master */ +#define IFM_IEEE80211_TURBO 0x00001000 /* Operate in turbo mode */ +#define IFM_IEEE80211_MONITOR 0x00002000 /* Operate in monitor mode */ + +/* operating mode for multi-mode devices */ +#define IFM_IEEE80211_11A 0x00010000 /* 5Ghz, OFDM mode */ +#define IFM_IEEE80211_11B 0x00020000 /* Direct Sequence mode */ +#define IFM_IEEE80211_11G 0x00030000 /* 2Ghz, CCK mode */ +#define IFM_IEEE80211_FH 0x00040000 /* 2Ghz, GFSK mode */ + +/* + * Shared media sub-types + */ +#define IFM_AUTO 0 /* Autoselect best media */ +#define IFM_MANUAL 1 /* Jumper/dipswitch selects media */ +#define IFM_NONE 2 /* Deselect all media */ + +/* + * Shared options + */ +#define IFM_FDX 0x00100000 /* Force full duplex */ +#define IFM_HDX 0x00200000 /* Force half duplex */ +#define IFM_FLAG0 0x01000000 /* Driver defined flag */ +#define IFM_FLAG1 0x02000000 /* Driver defined flag */ +#define IFM_FLAG2 0x04000000 /* Driver defined flag */ +#define IFM_LOOP 0x08000000 /* Put hardware in loopback */ + +/* + * Masks + */ +#define IFM_NMASK 0x000000e0 /* Network type */ +#define IFM_TMASK 0x0000001f /* Media sub-type */ +#define IFM_IMASK 0xf0000000 /* Instance */ +#define IFM_ISHIFT 28 /* Instance shift */ +#define IFM_OMASK 0x0000ff00 /* Type specific options */ +#define IFM_MMASK 0x00070000 /* Mode */ +#define IFM_MSHIFT 16 /* Mode shift */ +#define IFM_GMASK 0x0ff00000 /* Global options */ + +/* + * Status bits + */ +#define IFM_AVALID 0x00000001 /* Active bit valid */ +#define IFM_ACTIVE 0x00000002 /* Interface attached to working net */ + +/* + * Macros to extract various bits of information from the media word. + */ +#define IFM_TYPE(x) ((x) & IFM_NMASK) +#define IFM_SUBTYPE(x) ((x) & IFM_TMASK) +#define IFM_TYPE_OPTIONS(x) ((x) & IFM_OMASK) +#define IFM_INST(x) (((x) & IFM_IMASK) >> IFM_ISHIFT) +#define IFM_OPTIONS(x) ((x) & (IFM_OMASK|IFM_GMASK)) +#define IFM_MODE(x) ((x) & IFM_MMASK) + +#define IFM_INST_MAX IFM_INST(IFM_IMASK) + +/* + * Macro to create a media word. + */ +#define IFM_MAKEWORD(type, subtype, options, instance) \ + ((type) | (subtype) | (options) | ((instance) << IFM_ISHIFT)) +#define IFM_MAKEMODE(mode) \ + (((mode) << IFM_MSHIFT) & IFM_MMASK) + +/* + * NetBSD extension not defined in the BSDI API. This is used in various + * places to get the canonical description for a given type/subtype. + * + * NOTE: all but the top-level type descriptions must contain NO whitespace! + * Otherwise, parsing these in ifconfig(8) would be a nightmare. + */ +struct ifmedia_description { + int ifmt_word; /* word value; may be masked */ + const char *ifmt_string; /* description */ +}; + +#define IFM_TYPE_DESCRIPTIONS { \ + { IFM_ETHER, "Ethernet" }, \ + { IFM_TOKEN, "Token ring" }, \ + { IFM_FDDI, "FDDI" }, \ + { IFM_IEEE80211, "IEEE 802.11 Wireless Ethernet" }, \ + { 0, NULL }, \ +} + +#define IFM_SUBTYPE_ETHERNET_DESCRIPTIONS { \ + { IFM_10_T, "10baseT/UTP" }, \ + { IFM_10_2, "10base2/BNC" }, \ + { IFM_10_5, "10base5/AUI" }, \ + { IFM_100_TX, "100baseTX" }, \ + { IFM_100_FX, "100baseFX" }, \ + { IFM_100_T4, "100baseT4" }, \ + { IFM_100_VG, "100baseVG" }, \ + { IFM_100_T2, "100baseT2" }, \ + { IFM_10_STP, "10baseSTP" }, \ + { IFM_10_FL, "10baseFL" }, \ + { IFM_1000_SX, "1000baseSX" }, \ + { IFM_1000_LX, "1000baseLX" }, \ + { IFM_1000_CX, "1000baseCX" }, \ + { IFM_1000_T, "1000baseTX" }, \ + { IFM_1000_T, "1000baseT" }, \ + { IFM_HPNA_1, "homePNA" }, \ + { 0, NULL }, \ +} + +#define IFM_SUBTYPE_ETHERNET_ALIASES { \ + { IFM_10_T, "UTP" }, \ + { IFM_10_T, "10UTP" }, \ + { IFM_10_2, "BNC" }, \ + { IFM_10_2, "10BNC" }, \ + { IFM_10_5, "AUI" }, \ + { IFM_10_5, "10AUI" }, \ + { IFM_100_TX, "100TX" }, \ + { IFM_100_T4, "100T4" }, \ + { IFM_100_VG, "100VG" }, \ + { IFM_100_T2, "100T2" }, \ + { IFM_10_STP, "10STP" }, \ + { IFM_10_FL, "10FL" }, \ + { IFM_1000_SX, "1000SX" }, \ + { IFM_1000_LX, "1000LX" }, \ + { IFM_1000_CX, "1000CX" }, \ + { IFM_1000_T, "1000TX" }, \ + { IFM_1000_T, "1000T" }, \ + { 0, NULL }, \ +} + +#define IFM_SUBTYPE_ETHERNET_OPTION_DESCRIPTIONS { \ + { 0, NULL }, \ +} + +#define IFM_SUBTYPE_TOKENRING_DESCRIPTIONS { \ + { IFM_TOK_STP4, "DB9/4Mbit" }, \ + { IFM_TOK_STP16, "DB9/16Mbit" }, \ + { IFM_TOK_UTP4, "UTP/4Mbit" }, \ + { IFM_TOK_UTP16, "UTP/16Mbit" }, \ + { IFM_TOK_STP100, "STP/100Mbit" }, \ + { IFM_TOK_UTP100, "UTP/100Mbit" }, \ + { 0, NULL }, \ +} + +#define IFM_SUBTYPE_TOKENRING_ALIASES { \ + { IFM_TOK_STP4, "4STP" }, \ + { IFM_TOK_STP16, "16STP" }, \ + { IFM_TOK_UTP4, "4UTP" }, \ + { IFM_TOK_UTP16, "16UTP" }, \ + { IFM_TOK_STP100, "100STP" }, \ + { IFM_TOK_UTP100, "100UTP" }, \ + { 0, NULL }, \ +} + +#define IFM_SUBTYPE_TOKENRING_OPTION_DESCRIPTIONS { \ + { IFM_TOK_ETR, "EarlyTokenRelease" }, \ + { IFM_TOK_SRCRT, "SourceRouting" }, \ + { IFM_TOK_ALLR, "AllRoutes" }, \ + { IFM_TOK_DTR, "Dedicated" }, \ + { IFM_TOK_CLASSIC,"Classic" }, \ + { IFM_TOK_AUTO, " " }, \ + { 0, NULL }, \ +} + +#define IFM_SUBTYPE_FDDI_DESCRIPTIONS { \ + { IFM_FDDI_SMF, "Single-mode" }, \ + { IFM_FDDI_MMF, "Multi-mode" }, \ + { IFM_FDDI_UTP, "UTP" }, \ + { 0, NULL }, \ +} + +#define IFM_SUBTYPE_FDDI_ALIASES { \ + { IFM_FDDI_SMF, "SMF" }, \ + { IFM_FDDI_MMF, "MMF" }, \ + { IFM_FDDI_UTP, "CDDI" }, \ + { 0, NULL }, \ +} + +#define IFM_SUBTYPE_FDDI_OPTION_DESCRIPTIONS { \ + { IFM_FDDI_DA, "Dual-attach" }, \ + { 0, NULL }, \ +} + +#define IFM_SUBTYPE_IEEE80211_DESCRIPTIONS { \ + { IFM_IEEE80211_FH1, "FH/1Mbps" }, \ + { IFM_IEEE80211_FH2, "FH/2Mbps" }, \ + { IFM_IEEE80211_DS1, "DS/1Mbps" }, \ + { IFM_IEEE80211_DS2, "DS/2Mbps" }, \ + { IFM_IEEE80211_DS5, "DS/5.5Mbps" }, \ + { IFM_IEEE80211_DS11, "DS/11Mbps" }, \ + { IFM_IEEE80211_DS22, "DS/22Mbps" }, \ + { IFM_IEEE80211_OFDM6, "OFDM/6Mbps" }, \ + { IFM_IEEE80211_OFDM9, "OFDM/9Mbps" }, \ + { IFM_IEEE80211_OFDM12, "OFDM/12Mbps" }, \ + { IFM_IEEE80211_OFDM18, "OFDM/18Mbps" }, \ + { IFM_IEEE80211_OFDM24, "OFDM/24Mbps" }, \ + { IFM_IEEE80211_OFDM36, "OFDM/36Mbps" }, \ + { IFM_IEEE80211_OFDM48, "OFDM/48Mbps" }, \ + { IFM_IEEE80211_OFDM54, "OFDM/54Mbps" }, \ + { IFM_IEEE80211_OFDM72, "OFDM/72Mbps" }, \ + { 0, NULL }, \ +} + +#define IFM_SUBTYPE_IEEE80211_ALIASES { \ + { IFM_IEEE80211_FH1, "FH1" }, \ + { IFM_IEEE80211_FH2, "FH2" }, \ + { IFM_IEEE80211_FH1, "FrequencyHopping/1Mbps" }, \ + { IFM_IEEE80211_FH2, "FrequencyHopping/2Mbps" }, \ + { IFM_IEEE80211_DS1, "DS1" }, \ + { IFM_IEEE80211_DS2, "DS2" }, \ + { IFM_IEEE80211_DS5, "DS5.5" }, \ + { IFM_IEEE80211_DS11, "DS11" }, \ + { IFM_IEEE80211_DS22, "DS22" }, \ + { IFM_IEEE80211_DS1, "DirectSequence/1Mbps" }, \ + { IFM_IEEE80211_DS2, "DirectSequence/2Mbps" }, \ + { IFM_IEEE80211_DS5, "DirectSequence/5.5Mbps" }, \ + { IFM_IEEE80211_DS11, "DirectSequence/11Mbps" }, \ + { IFM_IEEE80211_DS22, "DirectSequence/22Mbps" }, \ + { IFM_IEEE80211_OFDM6, "OFDM6" }, \ + { IFM_IEEE80211_OFDM9, "OFDM9" }, \ + { IFM_IEEE80211_OFDM12, "OFDM12" }, \ + { IFM_IEEE80211_OFDM18, "OFDM18" }, \ + { IFM_IEEE80211_OFDM24, "OFDM24" }, \ + { IFM_IEEE80211_OFDM36, "OFDM36" }, \ + { IFM_IEEE80211_OFDM48, "OFDM48" }, \ + { IFM_IEEE80211_OFDM54, "OFDM54" }, \ + { IFM_IEEE80211_OFDM72, "OFDM72" }, \ + { IFM_IEEE80211_DS1, "CCK1" }, \ + { IFM_IEEE80211_DS2, "CCK2" }, \ + { IFM_IEEE80211_DS5, "CCK5.5" }, \ + { IFM_IEEE80211_DS11, "CCK11" }, \ + { 0, NULL }, \ +} + +#define IFM_SUBTYPE_IEEE80211_OPTION_DESCRIPTIONS { \ + { IFM_IEEE80211_ADHOC, "adhoc" }, \ + { IFM_IEEE80211_HOSTAP, "hostap" }, \ + { IFM_IEEE80211_IBSS, "ibss" }, \ + { IFM_IEEE80211_IBSSMASTER, "ibss-master" }, \ + { IFM_IEEE80211_TURBO, "turbo" }, \ + { 0, NULL }, \ +} + +#define IFM_SUBTYPE_IEEE80211_MODE_DESCRIPTIONS { \ + { IFM_IEEE80211_11A, "11a" }, \ + { IFM_IEEE80211_11B, "11b" }, \ + { IFM_IEEE80211_11G, "11g" }, \ + { 0, NULL }, \ +} + +#define IFM_SUBTYPE_SHARED_DESCRIPTIONS { \ + { IFM_AUTO, "autoselect" }, \ + { IFM_MANUAL, "manual" }, \ + { IFM_NONE, "none" }, \ + { 0, NULL }, \ +} + +#define IFM_SUBTYPE_SHARED_ALIASES { \ + { IFM_AUTO, "auto" }, \ + { 0, NULL }, \ +} + +#define IFM_SHARED_OPTION_DESCRIPTIONS { \ + { IFM_FDX, "full-duplex" }, \ + { IFM_HDX, "half-duplex" }, \ + { IFM_FLAG0, "flag0" }, \ + { IFM_FLAG1, "flag1" }, \ + { IFM_FLAG2, "flag2" }, \ + { IFM_LOOP, "hw-loopback" }, \ + { 0, NULL }, \ +} + +#endif /* _NET_IF_MEDIA_H_ */ diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/if_wavelan_ieee.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/if_wavelan_ieee.h --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/if_wavelan_ieee.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/if_wavelan_ieee.h 2005-02-24 13:06:17.412114976 -0800 @@ -0,0 +1,748 @@ +/* + * Copyright (c) 1997, 1998, 1999 + * Bill Paul . All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/dev/wi/if_wavelan_ieee.h,v 1.17 2003/01/15 20:11:31 sam Exp $ + * $Id: if_wavelan_ieee.h,v 1.2 2004/08/05 17:34:54 samleffler Exp $ + */ + +#ifndef _IF_WAVELAN_IEEE_H +#define _IF_WAVELAN_IEEE_H + +/* + * This header defines a simple command interface to the FreeBSD + * WaveLAN/IEEE driver (wi) driver, which is used to set certain + * device-specific parameters which can't be easily managed through + * ifconfig(8). No, sysctl(2) is not the answer. I said a _simple_ + * interface, didn't I. + */ + +#ifndef SIOCSWAVELAN +#define SIOCSWAVELAN SIOCSIFGENERIC +#endif + +#ifndef SIOCGWAVELAN +#define SIOCGWAVELAN SIOCGIFGENERIC +#endif + +/* + * Technically I don't think there's a limit to a record + * length. The largest record is the one that contains the CIS + * data, which is 240 words long, so 256 should be a safe + * value. + */ +#define WI_MAX_DATALEN 512 + +struct wi_req { + u_int16_t wi_len; + u_int16_t wi_type; + u_int16_t wi_val[WI_MAX_DATALEN]; +}; + +/* + * Private LTV records (interpreted only by the driver). This is + * a minor kludge to allow reading the interface statistics from + * the driver. + */ +#define WI_RID_IFACE_STATS 0x0100 +#define WI_RID_MGMT_XMIT 0x0200 +#define WI_RID_ZERO_CACHE 0x0300 +#define WI_RID_READ_CACHE 0x0400 +#define WI_RID_FWDOWNLOAD 0x0500 +#define WI_RID_MONITOR_MODE 0x0600 +#define WI_RID_MIF 0x0700 +#define WI_RID_SCAN_APS 0x0800 +#define WI_RID_READ_APS 0x0900 + +struct wi_80211_hdr { + u_int16_t frame_ctl; + u_int16_t dur_id; + u_int8_t addr1[6]; + u_int8_t addr2[6]; + u_int8_t addr3[6]; + u_int16_t seq_ctl; + u_int8_t addr4[6]; +}; + +#define WI_FCTL_VERS 0x0002 +#define WI_FCTL_FTYPE 0x000C +#define WI_FCTL_STYPE 0x00F0 +#define WI_FCTL_TODS 0x0100 +#define WI_FCTL_FROMDS 0x0200 +#define WI_FCTL_MOREFRAGS 0x0400 +#define WI_FCTL_RETRY 0x0800 +#define WI_FCTL_PM 0x1000 +#define WI_FCTL_MOREDATA 0x2000 +#define WI_FCTL_WEP 0x4000 +#define WI_FCTL_ORDER 0x8000 + +#define WI_FTYPE_MGMT 0x0000 +#define WI_FTYPE_CTL 0x0004 +#define WI_FTYPE_DATA 0x0008 + +#define WI_STYPE_MGMT_ASREQ 0x0000 /* association request */ +#define WI_STYPE_MGMT_ASRESP 0x0010 /* association response */ +#define WI_STYPE_MGMT_REASREQ 0x0020 /* reassociation request */ +#define WI_STYPE_MGMT_REASRESP 0x0030 /* reassociation response */ +#define WI_STYPE_MGMT_PROBEREQ 0x0040 /* probe request */ +#define WI_STYPE_MGMT_PROBERESP 0x0050 /* probe response */ +#define WI_STYPE_MGMT_BEACON 0x0080 /* beacon */ +#define WI_STYPE_MGMT_ATIM 0x0090 /* announcement traffic ind msg */ +#define WI_STYPE_MGMT_DISAS 0x00A0 /* disassociation */ +#define WI_STYPE_MGMT_AUTH 0x00B0 /* authentication */ +#define WI_STYPE_MGMT_DEAUTH 0x00C0 /* deauthentication */ + +#define WI_STYPE_CTL_PSPOLL 0x00A0 +#define WI_STYPE_CTL_RTS 0x00B0 +#define WI_STYPE_CTL_CTS 0x00C0 +#define WI_STYPE_CTL_ACK 0x00D0 +#define WI_STYPE_CTL_CFEND 0x00E0 +#define WI_STYPE_CTL_CFENDACK 0x00F0 + +struct wi_mgmt_hdr { + u_int16_t frame_ctl; + u_int16_t duration; + u_int8_t dst_addr[6]; + u_int8_t src_addr[6]; + u_int8_t bssid[6]; + u_int16_t seq_ctl; +}; + +/* + * Lucent/wavelan IEEE signal strength cache + * + * driver keeps cache of last + * MAXWICACHE packets to arrive including signal strength info. + * daemons may read this via ioctl + * + * Each entry in the wi_sigcache has a unique macsrc. + */ +struct wi_sigcache { + char macsrc[6]; /* unique MAC address for entry */ + int ipsrc; /* ip address associated with packet */ + int signal; /* signal strength of the packet */ + int noise; /* noise value */ + int quality; /* quality of the packet */ +}; + +/* + * Firmware downloading API. We support downloading into RAM and into + * flash. We copy the entire .hex file for both the primary and secondary + * firmware into the kernel, which is minorly gross, but matches the + * format of the compiled in firmware. + */ +struct wi_fwdownload { + int type; /* What type of download. */ +#define WI_FW_RAM 1 +#define WI_FW_FLASH 2 + size_t pri_len; /* Primary firmware length */ + size_t sec_len; /* Secondary firmware length */ + caddr_t pri_data; /* Pointer (user) to primary data */ + caddr_t sec_data; /* Pointer (user) to secondary data */ +}; + +struct wi_counters { + u_int32_t wi_tx_unicast_frames; + u_int32_t wi_tx_multicast_frames; + u_int32_t wi_tx_fragments; + u_int32_t wi_tx_unicast_octets; + u_int32_t wi_tx_multicast_octets; + u_int32_t wi_tx_deferred_xmits; + u_int32_t wi_tx_single_retries; + u_int32_t wi_tx_multi_retries; + u_int32_t wi_tx_retry_limit; + u_int32_t wi_tx_discards; + u_int32_t wi_rx_unicast_frames; + u_int32_t wi_rx_multicast_frames; + u_int32_t wi_rx_fragments; + u_int32_t wi_rx_unicast_octets; + u_int32_t wi_rx_multicast_octets; + u_int32_t wi_rx_fcs_errors; + u_int32_t wi_rx_discards_nobuf; + u_int32_t wi_tx_discards_wrong_sa; + u_int32_t wi_rx_WEP_cant_decrypt; + u_int32_t wi_rx_msg_in_msg_frags; + u_int32_t wi_rx_msg_in_bad_msg_frags; +}; + +/* + * Network parameters, static configuration entities. + */ +#define WI_RID_PORTTYPE 0xFC00 /* Connection control characteristics */ +#define WI_RID_MAC_NODE 0xFC01 /* MAC address of this station */ +#define WI_RID_DESIRED_SSID 0xFC02 /* Service Set ID for connection */ +#define WI_RID_OWN_CHNL 0xFC03 /* Comm channel for BSS creation */ +#define WI_RID_OWN_SSID 0xFC04 /* IBSS creation ID */ +#define WI_RID_OWN_ATIM_WIN 0xFC05 /* ATIM window time for IBSS creation */ +#define WI_RID_SYSTEM_SCALE 0xFC06 /* scale that specifies AP density */ +#define WI_RID_MAX_DATALEN 0xFC07 /* Max len of MAC frame body data */ +#define WI_RID_MAC_WDS 0xFC08 /* MAC addr of corresponding WDS node */ +#define WI_RID_PM_ENABLED 0xFC09 /* ESS power management enable */ +#define WI_RID_PM_EPS 0xFC0A /* PM EPS/PS mode */ +#define WI_RID_MCAST_RX 0xFC0B /* ESS PM mcast reception */ +#define WI_RID_MAX_SLEEP 0xFC0C /* max sleep time for ESS PM */ +#define WI_RID_HOLDOVER 0xFC0D /* holdover time for ESS PM */ +#define WI_RID_NODENAME 0xFC0E /* ID name of this node for diag */ +#define WI_RID_DTIM_PERIOD 0xFC10 /* beacon interval between DTIMs */ +#define WI_RID_WDS_ADDR1 0xFC11 /* port 1 MAC of WDS link node */ +#define WI_RID_WDS_ADDR2 0xFC12 /* port 1 MAC of WDS link node */ +#define WI_RID_WDS_ADDR3 0xFC13 /* port 1 MAC of WDS link node */ +#define WI_RID_WDS_ADDR4 0xFC14 /* port 1 MAC of WDS link node */ +#define WI_RID_WDS_ADDR5 0xFC15 /* port 1 MAC of WDS link node */ +#define WI_RID_WDS_ADDR6 0xFC16 /* port 1 MAC of WDS link node */ +#define WI_RID_MCAST_PM_BUF 0xFC17 /* PM buffering of mcast */ +#define WI_RID_ENCRYPTION 0xFC20 /* enable/disable WEP */ +#define WI_RID_AUTHTYPE 0xFC21 /* specify authentication type */ +#define WI_RID_P2_TX_CRYPT_KEY 0xFC23 +#define WI_RID_P2_CRYPT_KEY0 0xFC24 +#define WI_RID_P2_CRYPT_KEY1 0xFC25 +#define WI_RID_MICROWAVE_OVEN 0xFC25 +#define WI_RID_P2_CRYPT_KEY2 0xFC26 +#define WI_RID_P2_CRYPT_KEY3 0xFC27 +#define WI_RID_P2_ENCRYPTION 0xFC28 +#define PRIVACY_INVOKED 0x01 +#define EXCLUDE_UNENCRYPTED 0x02 +#define HOST_ENCRYPT 0x10 +#define IV_EVERY_FRAME 0x00 /* IV = Initialization Vector */ +#define IV_EVERY10_FRAME 0x20 /* every 10 frame IV reuse */ +#define IV_EVERY50_FRAME 0x40 /* every 50 frame IV reuse */ +#define IV_EVERY100_FRAME 0x60 /* every 100 frame IV reuse */ +#define HOST_DECRYPT 0x80 +#define WI_RID_WEP_MAPTABLE 0xFC29 +#define WI_RID_CNFAUTHMODE 0xFC2A +#define WI_RID_ROAMING_MODE 0xFC2D +#define WI_RID_OWN_BEACON_INT 0xFC33 /* beacon xmit time for BSS creation */ +#define WI_RID_CNF_DBM_ADJUST 0xFC46 +#define WI_RID_DBM_ADJUST 0xFC46 /* RSSI - WI_RID_DBM_ADJUST ~ dBm */ +#define WI_RID_BASIC_RATE 0xFCB3 +#define WI_RID_SUPPORT_RATE 0xFCB4 + +/* + * Network parameters, dynamic configuration entities + */ +#define WI_RID_MCAST_LIST 0xFC80 /* list of multicast addrs */ +#define WI_RID_CREATE_IBSS 0xFC81 /* create IBSS */ +#define WI_RID_FRAG_THRESH 0xFC82 /* frag len, unicast msg xmit */ +#define WI_RID_RTS_THRESH 0xFC83 /* frame len for RTS/CTS handshake */ +#define WI_RID_TX_RATE 0xFC84 /* data rate for message xmit + * 0 == Fixed 1mbps + * 1 == Fixed 2mbps + * 2 == auto fallback + */ +#define WI_RID_PROMISC 0xFC85 /* enable promisc mode */ +#define WI_RID_FRAG_THRESH0 0xFC90 +#define WI_RID_FRAG_THRESH1 0xFC91 +#define WI_RID_FRAG_THRESH2 0xFC92 +#define WI_RID_FRAG_THRESH3 0xFC93 +#define WI_RID_FRAG_THRESH4 0xFC94 +#define WI_RID_FRAG_THRESH5 0xFC95 +#define WI_RID_FRAG_THRESH6 0xFC96 +#define WI_RID_RTS_THRESH0 0xFC97 +#define WI_RID_RTS_THRESH1 0xFC98 +#define WI_RID_RTS_THRESH2 0xFC99 +#define WI_RID_RTS_THRESH3 0xFC9A +#define WI_RID_RTS_THRESH4 0xFC9B +#define WI_RID_RTS_THRESH5 0xFC9C +#define WI_RID_RTS_THRESH6 0xFC9D +#define WI_RID_TX_RATE0 0xFC9E +#define WI_RID_TX_RATE1 0xFC9F +#define WI_RID_TX_RATE2 0xFCA0 +#define WI_RID_TX_RATE3 0xFCA1 +#define WI_RID_TX_RATE4 0xFCA2 +#define WI_RID_TX_RATE5 0xFCA3 +#define WI_RID_TX_RATE6 0xFCA4 +#define WI_RID_DEFLT_CRYPT_KEYS 0xFCB0 +#define WI_RID_TX_CRYPT_KEY 0xFCB1 +#define WI_RID_TICK_TIME 0xFCE0 + +struct wi_key { + u_int16_t wi_keylen; + u_int8_t wi_keydat[14]; +}; + +#define WI_NLTV_KEYS 4 +struct wi_ltv_keys { + u_int16_t wi_len; + u_int16_t wi_type; + struct wi_key wi_keys[WI_NLTV_KEYS]; +}; + +/* + * NIC information + */ +#define WI_RID_DNLD_BUF 0xFD01 +#define WI_RID_MEMSZ 0xFD02 /* memory size info (XXX Lucent) */ + /* Looks like on lucnet pri firm too */ +#define WI_RID_PRI_IDENTITY 0xFD02 /* primary funcs firmware ident (PRISM2) */ +#define WI_RID_PRI_SUP_RANGE 0xFD03 /* primary supplier compatibility */ +#define WI_RID_CIF_ACT_RANGE 0xFD04 /* controller sup. compatibility */ +#define WI_RID_SERIALNO 0xFD0A /* card serial number */ +#define WI_RID_CARD_ID 0xFD0B /* card identification */ +#define WI_RID_MFI_SUP_RANGE 0xFD0C /* modem supplier compatibility */ +#define WI_RID_CFI_SUP_RANGE 0xFD0D /* controller sup. compatibility */ +#define WI_RID_CHANNEL_LIST 0xFD10 /* allowd comm. frequencies. */ +#define WI_RID_REG_DOMAINS 0xFD11 /* list of intendted regulatory doms */ +#define WI_RID_TEMP_TYPE 0xFD12 /* hw temp range code */ +#define WI_RID_CIS 0xFD13 /* PC card info struct */ +#define WI_RID_STA_IDENTITY 0xFD20 /* station funcs firmware ident */ +#define WI_RID_STA_SUP_RANGE 0xFD21 /* station supplier compat */ +#define WI_RID_MFI_ACT_RANGE 0xFD22 +#define WI_RID_SYMBOL_IDENTITY 0xFD24 +#define WI_RID_CFI_ACT_RANGE 0xFD33 +#define WI_RID_COMMQUAL 0xFD43 +#define WI_RID_SCALETHRESH 0xFD46 +#define WI_RID_PCF 0xFD87 + +/* + * MAC information + */ +#define WI_RID_PORT_STAT 0xFD40 /* actual MAC port con control stat */ +#define WI_RID_CURRENT_SSID 0xFD41 /* ID of actually connected SS */ +#define WI_RID_CURRENT_BSSID 0xFD42 /* ID of actually connected BSS */ +#define WI_RID_COMMS_QUALITY 0xFD43 /* quality of BSS connection */ +#define WI_RID_CUR_TX_RATE 0xFD44 /* current TX rate */ +#define WI_RID_CUR_BEACON_INT 0xFD45 /* current beacon interval */ +#define WI_RID_CUR_SCALE_THRESH 0xFD46 /* actual system scane thresh setting */ +#define WI_RID_PROT_RESP_TIME 0xFD47 /* time to wait for resp to req msg */ +#define WI_RID_SHORT_RTR_LIM 0xFD48 /* max tx attempts for short frames */ +#define WI_RID_LONG_RTS_LIM 0xFD49 /* max tx attempts for long frames */ +#define WI_RID_MAX_TX_LIFE 0xFD4A /* max tx frame handling duration */ +#define WI_RID_MAX_RX_LIFE 0xFD4B /* max rx frame handling duration */ +#define WI_RID_CF_POLL 0xFD4C /* contention free pollable ind */ +#define WI_RID_AUTH_ALGS 0xFD4D /* auth algorithms available */ +#define WI_RID_AUTH_TYPE 0xFD4E /* availanle auth types */ +#define WI_RID_WEP_AVAIL 0xFD4F /* WEP privacy option available */ +#define WI_RID_DBM_COMMS_QUAL 0xFD51 /* CommQuality normalized to dBm */ +#define WI_RID_CUR_TX_RATE1 0xFD80 +#define WI_RID_CUR_TX_RATE2 0xFD81 +#define WI_RID_CUR_TX_RATE3 0xFD82 +#define WI_RID_CUR_TX_RATE4 0xFD83 +#define WI_RID_CUR_TX_RATE5 0xFD84 +#define WI_RID_CUR_TX_RATE6 0xFD85 +#define WI_RID_OWN_MAC 0xFD86 /* unique local MAC addr */ +#define WI_RID_PCI_INFO 0xFD87 /* point coordination func cap */ + +/* + * Scan Information + */ +#define WI_RID_BCAST_SCAN_REQ 0xFCAB /* Broadcast Scan request (Symbol) */ +#define BSCAN_5SEC 0x01 +#define BSCAN_ONETIME 0x02 +#define BSCAN_PASSIVE 0x40 +#define BSCAN_BCAST 0x80 +#define WI_RID_SCAN_REQ 0xFCE1 /* Scan request (STA only) */ +#define WI_RID_JOIN_REQ 0xFCE2 /* Join request (STA only) */ +#define WI_RID_AUTH_STATION 0xFCE3 /* Authenticates Station (AP) */ +#define WI_RID_CHANNEL_REQ 0xFCE4 /* Channel Information Request (AP) */ +#define WI_RID_SCAN_RESULTS 0xFD88 /* Scan Results Table */ + +struct wi_apinfo { + int scanreason; /* ScanReason */ + char bssid[6]; /* BSSID (mac address) */ + int channel; /* Channel */ + int signal; /* Signal level */ + int noise; /* Average Noise Level*/ + int quality; /* Quality */ + int namelen; /* Length of SSID string */ + char name[32]; /* SSID string */ + int capinfo; /* Capability info. */ + int interval; /* BSS Beacon Interval */ + int rate; /* Data Rate */ +}; + +/* + * Modem information + */ +#define WI_RID_PHY_TYPE 0xFDC0 /* phys layer type indication */ +#define WI_RID_CURRENT_CHAN 0xFDC1 /* current frequency */ +#define WI_RID_PWR_STATE 0xFDC2 /* pwr consumption status */ +#define WI_RID_CCA_MODE 0xFDC3 /* clear chan assess mode indication */ +#define WI_RID_CCA_TIME 0xFDC4 /* clear chan assess time */ +#define WI_RID_MAC_PROC_DELAY 0xFDC5 /* MAC processing delay time */ +#define WI_RID_DATA_RATES 0xFDC6 /* supported data rates */ + +/* + * bsd-airtools v0.2 - source-mods v0.2 [common.h] + * by h1kari - (c) Dachb0den Labs 2001 + */ + +/* + * Copyright (c) 2001 Dachb0den Labs. + * David Hulton . All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by David Hulton. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY David Hulton AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL David Hulton OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * standard hermes recieve frame used by wavelan/prism2 cards + */ +struct wi_rx_frame { + /* + * hermes prefix header. supplies information on the current status of + * the network and various other statistics gathered from the + * management/control frames as used internally. + */ + u_int16_t wi_status; + u_int16_t wi_ts0; + u_int16_t wi_ts1; + u_int8_t wi_silence; + u_int8_t wi_signal; + u_int8_t wi_rate; + u_int8_t wi_rx_flow; + u_int16_t wi_rsvd0; + u_int16_t wi_rsvd1; + /* + * standard 80211 frame header. all packets have to use this header as + * per the AN9900 from intersil, even management/control. for + * management packets, they just threw the header into the data field, + * but for control packets the headers are lost in translation and + * therefore not all control packet info can be displayed. + */ + u_int16_t wi_frame_ctl; + u_int16_t wi_id; + u_int8_t wi_addr1[6]; + u_int8_t wi_addr2[6]; + u_int8_t wi_addr3[6]; + u_int16_t wi_seq_ctl; + u_int8_t wi_addr4[6]; + u_int16_t wi_dat_len; + /* + * another wierdity with the drivers. they append a 802.3 header which + * is somewhat redundant, since all the same data is provided in the + * 802.11 header. + */ + u_int8_t wi_dst_addr[6]; + u_int8_t wi_src_addr[6]; + u_int16_t wi_len; +}; +#define WI_DATA_HDRLEN WI_802_11_OFFSET +#define WI_MGMT_HDRLEN WI_802_11_OFFSET_RAW +#define WI_CTL_HDRLEN WI_802_11_OFFSET_RAW + + +/* + * all data packets have a snap (sub-network access protocol) header that + * isn't entirely definied, but added for ethernet compatibility. + */ +struct wi_snap_frame { + u_int16_t wi_dat[3]; + u_int16_t wi_type; +}; + + +/* + * management frame headers + * note: all management frames consist of a static header and variable length + * fields. + */ + +/* + * variable length field structure + */ +struct wi_mgmt_var_hdr { + u_int8_t wi_code; + u_int8_t wi_len; + u_int8_t wi_data[256]; +}; + +/* + * management beacon frame prefix + */ +struct wi_mgmt_beacon_hdr { + u_int32_t wi_ts0; + u_int32_t wi_ts1; + u_int16_t wi_interval; + u_int16_t wi_capinfo; +}; + +/* + * ibss announcement traffic indication message (atim) frame + * note: no parameters + */ + +/* + * management disassociation frame + */ +struct wi_mgmt_disas_hdr { + u_int16_t wi_reason; +}; + +/* + * management association request frame prefix + */ +struct wi_mgmt_asreq_hdr { + u_int16_t wi_capinfo; + u_int16_t wi_interval; +}; + +/* + * management association response frame prefix + */ +struct wi_mgmt_asresp_hdr { + u_int16_t wi_capinfo; + u_int16_t wi_status; + u_int16_t wi_aid; +}; + +/* + * management reassociation request frame prefix + */ +struct wi_mgmt_reasreq_hdr { + u_int16_t wi_capinfo; + u_int16_t wi_interval; + u_int8_t wi_currap[6]; +}; + +/* + * management reassociation response frame prefix + */ +struct wi_mgmt_reasresp_hdr { + u_int16_t wi_capinfo; + u_int16_t wi_status; + u_int16_t wi_aid; +}; + +/* + * management probe request frame prefix + * note: no static parameters, only variable length + */ + +/* + * management probe response frame prefix + */ +struct wi_mgmt_proberesp_hdr { + u_int32_t wi_ts0; + u_int32_t wi_ts1; + u_int16_t wi_interval; + u_int16_t wi_capinfo; +}; + +/* + * management authentication frame prefix + */ +struct wi_mgmt_auth_hdr { + u_int16_t wi_algo; + u_int16_t wi_seq; + u_int16_t wi_status; +}; + +/* + * management deauthentication frame + */ +struct wi_mgmt_deauth_hdr { + u_int16_t wi_reason; +}; + + +/* + * rid configuration register definitions + */ +#define WI_RID_SCAN_REQ 0xFCE1 /* scan request information */ +#define WI_RID_SCAN_RES 0xFD88 /* scan result information */ + +#define WI_RID_PROCFRAME 0x3137 /* Return full frame information */ +#define WI_RID_PRISM2 0x3138 /* tell if we're a prism2 card or not */ + + +/* + * 802.11 definitions + */ +#define WI_STAT_BADCRC 0x0001 +#define WI_STAT_UNDECRYPTABLE 0x0002 +#define WI_STAT_ERRSTAT 0x0003 +#define WI_STAT_MAC_PORT 0x0700 +#define WI_STAT_1042 0x2000 +#define WI_STAT_TUNNEL 0x4000 +#define WI_STAT_WMP_MSG 0x6000 +#define WI_RXSTAT_MSG_TYPE 0xE000 + +#define WI_FCTL_OPT_MASK 0xFF00 +#define WI_AID_SET 0xC000 +#define WI_AID_MASK 0x3FFF +#define WI_SCTL_FRAGNUM_MASK 0x000F +#define WI_SCTL_SEQNUM_MASK 0xFFF0 + +#define WI_STAT_UNSPEC_FAIL 1 +#define WI_STAT_CAPINFO_FAIL 10 +#define WI_STAT_REAS_DENY 11 +#define WI_STAT_ASSOC_DENY 12 +#define WI_STAT_ALGO_FAIL 13 +#define WI_STAT_SEQ_FAIL 14 +#define WI_STAT_CHAL_FAIL 15 +#define WI_STAT_TOUT_FAIL 16 +#define WI_STAT_OVERL_DENY 17 +#define WI_STAT_RATE_DENY 18 + +#define WI_FTYPE_MGMT 0x0000 +#define WI_FTYPE_CTL 0x0004 +#define WI_FTYPE_DATA 0x0008 + +#define WI_FCTL_VERS 0x0002 +#define WI_FCTL_FTYPE 0x000C +#define WI_FCTL_STYPE 0x00F0 +#define WI_FCTL_TODS 0x0100 +#define WI_FCTL_FROMDS 0x0200 +#define WI_FCTL_MOREFRAGS 0x0400 +#define WI_FCTL_RETRY 0x0800 +#define WI_FCTL_PM 0x1000 +#define WI_FCTL_MOREDATA 0x2000 +#define WI_FCTL_WEP 0x4000 +#define WI_FCTL_ORDER 0x8000 + +#define WI_FCS_LEN 0x4 /* checksum length */ + + +/* + * management definitions + */ +#define WI_STYPE_MGMT_ASREQ 0x0000 +#define WI_STYPE_MGMT_ASRESP 0x0010 +#define WI_STYPE_MGMT_REASREQ 0x0020 +#define WI_STYPE_MGMT_REASRESP 0x0030 +#define WI_STYPE_MGMT_PROBEREQ 0x0040 +#define WI_STYPE_MGMT_PROBERESP 0x0050 +#define WI_STYPE_MGMT_BEACON 0x0080 +#define WI_STYPE_MGMT_ATIM 0x0090 +#define WI_STYPE_MGMT_DISAS 0x00A0 +#define WI_STYPE_MGMT_AUTH 0x00B0 +#define WI_STYPE_MGMT_DEAUTH 0x00C0 + +#define WI_CAPINFO_ESS 0x01 +#define WI_CAPINFO_IBSS 0x02 +#define WI_CAPINFO_CFPOLL 0x04 +#define WI_CAPINFO_CFPOLLREQ 0x08 +#define WI_CAPINFO_PRIV 0x10 + +#define WI_REASON_UNSPEC 1 +#define WI_REASON_AUTH_INVALID 2 +#define WI_REASON_DEAUTH_LEAVE 3 +#define WI_REASON_DISAS_INACT 4 +#define WI_REASON_DISAS_OVERL 5 +#define WI_REASON_CLASS2 6 +#define WI_REASON_CLASS3 7 +#define WI_REASON_DISAS_LEAVE 8 +#define WI_REASON_NOAUTH 9 + +#define WI_VAR_SSID 0 +#define WI_VAR_SRATES 1 +#define WI_VAR_FH 2 +#define WI_VAR_DS 3 +#define WI_VAR_CF 4 +#define WI_VAR_TIM 5 +#define WI_VAR_IBSS 6 +#define WI_VAR_CHAL 16 + +#define WI_VAR_SRATES_MASK 0x7F + + +/* + * control definitions + */ +#define WI_STYPE_CTL_PSPOLL 0x00A0 +#define WI_STYPE_CTL_RTS 0x00B0 +#define WI_STYPE_CTL_CTS 0x00C0 +#define WI_STYPE_CTL_ACK 0x00D0 +#define WI_STYPE_CTL_CFEND 0x00E0 +#define WI_STYPE_CTL_CFENDCFACK 0x00F0 + + +/* + * ap scanning structures + */ +struct wi_scan_res { + u_int16_t wi_chan; + u_int16_t wi_noise; + u_int16_t wi_signal; + u_int8_t wi_bssid[6]; + u_int16_t wi_interval; + u_int16_t wi_capinfo; + u_int16_t wi_ssid_len; + u_int8_t wi_ssid[32]; + u_int8_t wi_srates[10]; + u_int8_t wi_rate; + u_int8_t wi_rsvd; +}; +#define WI_WAVELAN_RES_SIZE 50 + +struct wi_scan_p2_hdr { + u_int16_t wi_rsvd; + u_int16_t wi_reason; +}; +#define WI_PRISM2_RES_SIZE 62 + + +/* + * prism2 debug mode definitions + */ +#define SIOCSPRISM2DEBUG _IOW('i', 137, struct ifreq) +#define SIOCGPRISM2DEBUG _IOWR('i', 138, struct ifreq) + +#define WI_DEBUG_RESET 0x00 +#define WI_DEBUG_INIT 0x01 +#define WI_DEBUG_SLEEP 0x02 +#define WI_DEBUG_WAKE 0x03 +#define WI_DEBUG_CHAN 0x08 +#define WI_DEBUG_DELAYSUPP 0x09 +#define WI_DEBUG_TXSUPP 0x0A +#define WI_DEBUG_MONITOR 0x0B +#define WI_DEBUG_LEDTEST 0x0C +#define WI_DEBUG_CONTTX 0x0E +#define WI_DEBUG_STOPTEST 0x0F +#define WI_DEBUG_CONTRX 0x10 +#define WI_DEBUG_SIGSTATE 0x11 +#define WI_DEBUG_CALENABLE 0x13 +#define WI_DEBUG_CONFBITS 0x15 + +#endif diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/md5.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/md5.c --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/md5.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/md5.c 2005-02-24 13:06:17.412114976 -0800 @@ -0,0 +1,353 @@ +/* + * MD5 hash implementation and interface functions + * Copyright (c) 2003-2004, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include +#include + +#include + +#if defined(__BIG_ENDIAN) +#define WORDS_BIGENDIAN +#endif + +#include "md5.h" + + +void md5_mac(u8 *key, size_t key_len, u8 *data, size_t data_len, u8 *mac) +{ + MD5_CTX context; + MD5Init(&context); + MD5Update(&context, key, key_len); + MD5Update(&context, data, data_len); + MD5Update(&context, key, key_len); + MD5Final(mac, &context); +} + + +/* HMAC code is based on RFC 2104 */ +void hmac_md5_vector(u8 *key, size_t key_len, size_t num_elem, + u8 *addr[], size_t *len, u8 *mac) +{ + MD5_CTX context; + u8 k_ipad[65]; /* inner padding - key XORd with ipad */ + u8 k_opad[65]; /* outer padding - key XORd with opad */ + u8 tk[16]; + int i; + + /* if key is longer than 64 bytes reset it to key = MD5(key) */ + if (key_len > 64) { + MD5Init(&context); + MD5Update(&context, key, key_len); + MD5Final(tk, &context); + + key = tk; + key_len = 16; + } + + /* the HMAC_MD5 transform looks like: + * + * MD5(K XOR opad, MD5(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 64 times + * opad is the byte 0x5c repeated 64 times + * and text is the data being protected */ + + /* start out by storing key in pads */ + memset(k_ipad, 0, sizeof(k_ipad)); + memset(k_opad, 0, sizeof(k_opad)); + memcpy(k_ipad, key, key_len); + memcpy(k_opad, key, key_len); + + /* XOR key with ipad and opad values */ + for (i = 0; i < 64; i++) { + k_ipad[i] ^= 0x36; + k_opad[i] ^= 0x5c; + } + + /* perform inner MD5 */ + MD5Init(&context); /* init context for 1st pass */ + MD5Update(&context, k_ipad, 64); /* start with inner pad */ + /* then text of datagram; all fragments */ + for (i = 0; i < num_elem; i++) { + MD5Update(&context, addr[i], len[i]); + } + MD5Final(mac, &context); /* finish up 1st pass */ + + /* perform outer MD5 */ + MD5Init(&context); /* init context for 2nd pass */ + MD5Update(&context, k_opad, 64); /* start with outer pad */ + MD5Update(&context, mac, 16); /* then results of 1st hash */ + MD5Final(mac, &context); /* finish up 2nd pass */ +} + + +void hmac_md5(u8 *key, size_t key_len, u8 *data, size_t data_len, u8 *mac) +{ + hmac_md5_vector(key, key_len, 1, &data, &data_len, mac); +} + + +/* ===== start - public domain MD5 implementation ===== */ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +#ifndef WORDS_BIGENDIAN +#define byteReverse(buf, len) /* Nothing */ +#else +void byteReverse(unsigned char *buf, unsigned longs); + +#ifndef ASM_MD5 +/* + * Note: this code is harmless on little-endian machines. + */ +void byteReverse(unsigned char *buf, unsigned longs) +{ + u32 t; + do { + t = (u32) ((unsigned) buf[3] << 8 | buf[2]) << 16 | + ((unsigned) buf[1] << 8 | buf[0]); + *(u32 *) buf = t; + buf += 4; + } while (--longs); +} +#endif +#endif + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void MD5Init(struct MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len) +{ + u32 t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((u32) len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if (t) { + unsigned char *p = (unsigned char *) ctx->in + t; + + t = 64 - t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (u32 *) ctx->in); + buf += t; + len -= t; + } + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (u32 *) ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void MD5Final(unsigned char digest[16], struct MD5Context *ctx) +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (u32 *) ctx->in); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count - 8); + } + byteReverse(ctx->in, 14); + + /* Append length in bits and transform */ + ((u32 *) ctx->in)[14] = ctx->bits[0]; + ((u32 *) ctx->in)[15] = ctx->bits[1]; + + MD5Transform(ctx->buf, (u32 *) ctx->in); + byteReverse((unsigned char *) ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ +} + +#ifndef ASM_MD5 + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +void MD5Transform(u32 buf[4], u32 const in[16]) +{ + register u32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +#endif +/* ===== end - public domain MD5 implementation ===== */ diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/md5.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/md5.h --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/md5.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/md5.h 2005-02-24 13:06:17.413114824 -0800 @@ -0,0 +1,26 @@ +#ifndef MD5_H +#define MD5_H + +#define MD5_MAC_LEN 16 + +struct MD5Context { + u32 buf[4]; + u32 bits[2]; + u8 in[64]; +}; + +void MD5Init(struct MD5Context *context); +void MD5Update(struct MD5Context *context, unsigned char const *buf, + unsigned len); +void MD5Final(unsigned char digest[16], struct MD5Context *context); +void MD5Transform(u32 buf[4], u32 const in[16]); + +typedef struct MD5Context MD5_CTX; + + +void md5_mac(u8 *key, size_t key_len, u8 *data, size_t data_len, u8 *mac); +void hmac_md5_vector(u8 *key, size_t key_len, size_t num_elem, + u8 *addr[], size_t *len, u8 *mac); +void hmac_md5(u8 *key, size_t key_len, u8 *data, size_t data_len, u8 *mac); + +#endif /* MD5_H */ diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/rc4.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/rc4.c --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/rc4.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/rc4.c 2005-02-24 13:06:17.413114824 -0800 @@ -0,0 +1,122 @@ +/* + * rc4.c + * + * Copyright (c) 1996-2000 Whistle Communications, Inc. + * All rights reserved. + * + * Subject to the following obligations and disclaimer of warranty, use and + * redistribution of this software, in source or object code forms, with or + * without modifications are expressly permitted by Whistle Communications; + * provided, however, that: + * 1. Any and all reproductions of the source or object code must include the + * copyright notice above and the following disclaimer of warranties; and + * 2. No rights are granted, in any manner or form, to use Whistle + * Communications, Inc. trademarks, including the mark "WHISTLE + * COMMUNICATIONS" on advertising, endorsements, or otherwise except as + * such appears in the above copyright notice or in the software. + * + * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND + * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO + * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, + * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. + * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY + * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS + * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. + * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES + * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING + * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/crypto/rc4/rc4.c,v 1.4 2003/01/15 19:55:16 sam Exp $ + * $Id: rc4.c,v 1.2 2004/08/05 17:34:54 samleffler Exp $ + */ + +#include +#include +#include +#include + +#include "rc4.h" + +static __inline void +swap_bytes(u_char *a, u_char *b) +{ + u_char temp; + + temp = *a; + *a = *b; + *b = temp; +} + +/* + * Initialize an RC4 state buffer using the supplied key, + * which can have arbitrary length. + */ +void +rc4_init(struct rc4_state *const state, const u_char *key, int keylen) +{ + u_char j; + int i; + + /* Initialize state with identity permutation */ + for (i = 0; i < 256; i++) + state->perm[i] = (u_char)i; + state->index1 = 0; + state->index2 = 0; + + /* Randomize the permutation using key data */ + for (j = i = 0; i < 256; i++) { + j += state->perm[i] + key[i % keylen]; + swap_bytes(&state->perm[i], &state->perm[j]); + } +} +EXPORT_SYMBOL(rc4_init); + +/* + * Encrypt some data using the supplied RC4 state buffer. + * The input and output buffers may be the same buffer. + * Since RC4 is a stream cypher, this function is used + * for both encryption and decryption. + */ +void +rc4_crypt_skip(struct rc4_state *const state, + const u_char *inbuf, u_char *outbuf, int buflen, int skip) +{ + int i; + u_char j; + + for (i = 0; i < skip; i++) { + + /* Update modification indicies */ + state->index1++; + state->index2 += state->perm[state->index1]; + + /* Modify permutation */ + swap_bytes(&state->perm[state->index1], + &state->perm[state->index2]); + + /* Encrypt/decrypt next byte */ + j = state->perm[state->index1] + state->perm[state->index2]; + } + for (i = 0; i < buflen; i++) { + + /* Update modification indicies */ + state->index1++; + state->index2 += state->perm[state->index1]; + + /* Modify permutation */ + swap_bytes(&state->perm[state->index1], + &state->perm[state->index2]); + + /* Encrypt/decrypt next byte */ + j = state->perm[state->index1] + state->perm[state->index2]; + outbuf[i] = inbuf[i] ^ state->perm[j]; + } +} +EXPORT_SYMBOL(rc4_crypt_skip); diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/rc4.h linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/rc4.h --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/rc4.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/rc4.h 2005-02-24 13:06:17.413114824 -0800 @@ -0,0 +1,53 @@ +/* + * rc4.h + * + * Copyright (c) 1996-2000 Whistle Communications, Inc. + * All rights reserved. + * + * Subject to the following obligations and disclaimer of warranty, use and + * redistribution of this software, in source or object code forms, with or + * without modifications are expressly permitted by Whistle Communications; + * provided, however, that: + * 1. Any and all reproductions of the source or object code must include the + * copyright notice above and the following disclaimer of warranties; and + * 2. No rights are granted, in any manner or form, to use Whistle + * Communications, Inc. trademarks, including the mark "WHISTLE + * COMMUNICATIONS" on advertising, endorsements, or otherwise except as + * such appears in the above copyright notice or in the software. + * + * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND + * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO + * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, + * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. + * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY + * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS + * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. + * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES + * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING + * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/crypto/rc4/rc4.h,v 1.3 2000/07/16 05:53:14 peter Exp $ + * $Id: rc4.h,v 1.2 2004/08/05 17:34:54 samleffler Exp $ + */ + +#ifndef _SYS_CRYPTO_RC4_RC4_H_ +#define _SYS_CRYPTO_RC4_RC4_H_ + +struct rc4_state { + u_char perm[256]; + u_char index1; + u_char index2; +}; + +extern void rc4_init(struct rc4_state *state, const u_char *key, int keylen); +extern void rc4_crypt_skip(struct rc4_state *state, + const u_char *inbuf, u_char *outbuf, int buflen, int skip); +#endif + diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/test_ccmp.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/test_ccmp.c --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/test_ccmp.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/test_ccmp.c 2005-02-24 13:06:17.414114672 -0800 @@ -0,0 +1,726 @@ +/*- + * Copyright (c) 2004-2005 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * CCMP test module. + * + * Test vectors come from section I.7.4 of P802.11i/D7.0, October 2003. + * + * To use this tester load the net80211 layer (either as a module or + * by statically configuring it into your kernel), then insmod this + * module. It should automatically run all test cases and print + * information for each. To run one or more tests you can specify a + * tests parameter to the module that is a bit mask of the set of tests + * you want; e.g. insmod ccmp_test tests=7 will run only test mpdu's + * 1, 2, and 3. + */ +#include +#include +#include +#include +#include + +#include +#include "if_media.h" +#include + +/* +==== CCMP test mpdu 1 ==== + +-- MPDU Fields + +7 Version = 0 +8 Type = 2 SubType = 0 Data +9 ToDS = 0 FromDS = 0 +10 MoreFrag = 0 Retry = 1 +11 PwrMgt = 0 moreData = 0 +12 Encrypt = 1 +13 Order = 0 +14 Duration = 11459 +15 A1 = 0f-d2-e1-28-a5-7c DA +16 A2 = 50-30-f1-84-44-08 SA +17 A3 = ab-ae-a5-b8-fc-ba BSSID +18 SC = 0x3380 +19 seqNum = 824 (0x0338) fraqNum = 0 (0x00) +20 Algorithm = AES_CCM +21 Key ID = 0 +22 TK = c9 7c 1f 67 ce 37 11 85 51 4a 8a 19 f2 bd d5 2f +23 PN = 199027030681356 (0xB5039776E70C) +24 802.11 Header = 08 48 c3 2c 0f d2 e1 28 a5 7c 50 30 f1 84 44 08 +25 ab ae a5 b8 fc ba 80 33 +26 Muted 802.11 Header = 08 40 0f d2 e1 28 a5 7c 50 30 f1 84 44 08 +27 ab ae a5 b8 fc ba 00 00 +28 CCMP Header = 0c e7 00 20 76 97 03 b5 +29 CCM Nonce = 00 50 30 f1 84 44 08 b5 03 97 76 e7 0c +30 Plaintext Data = f8 ba 1a 55 d0 2f 85 ae 96 7b b6 2f b6 cd a8 eb +1 7e 78 a0 50 +2 CCM MIC = 78 45 ce 0b 16 f9 76 23 +3 -- Encrypted MPDU with FCS +4 08 48 c3 2c 0f d2 e1 28 a5 7c 50 30 f1 84 44 08 ab ae a5 b8 fc ba +5 80 33 0c e7 00 20 76 97 03 b5 f3 d0 a2 fe 9a 3d bf 23 42 a6 43 e4 +6 32 46 e8 0c 3c 04 d0 19 78 45 ce 0b 16 f9 76 23 1d 99 f0 66 +*/ +static const u_int8_t test1_key[] = { /* TK */ + 0xc9, 0x7c, 0x1f, 0x67, 0xce, 0x37, 0x11, 0x85, 0x51, 0x4a, 0x8a, + 0x19, 0xf2, 0xbd, 0xd5, 0x2f +}; +static const u_int8_t test1_plaintext[] = { /* Plaintext MPDU w/o MIC */ + 0x08, 0x48, 0xc3, 0x2c, 0x0f, 0xd2, 0xe1, 0x28, /* 802.11 Header */ + 0xa5, 0x7c, 0x50, 0x30, 0xf1, 0x84, 0x44, 0x08, + 0xab, 0xae, 0xa5, 0xb8, 0xfc, 0xba, 0x80, 0x33, + 0xf8, 0xba, 0x1a, 0x55, 0xd0, 0x2f, 0x85, 0xae, /* Plaintext Data */ + 0x96, 0x7b, 0xb6, 0x2f, 0xb6, 0xcd, 0xa8, 0xeb, + 0x7e, 0x78, 0xa0, 0x50, +}; +static const u_int8_t test1_encrypted[] = { /* Encrypted MPDU with MIC */ + 0x08, 0x48, 0xc3, 0x2c, 0x0f, 0xd2, 0xe1, 0x28, + 0xa5, 0x7c, 0x50, 0x30, 0xf1, 0x84, 0x44, 0x08, + 0xab, 0xae, 0xa5, 0xb8, 0xfc, 0xba, 0x80, 0x33, + 0x0c, 0xe7, 0x00, 0x20, 0x76, 0x97, 0x03, 0xb5, + 0xf3, 0xd0, 0xa2, 0xfe, 0x9a, 0x3d, 0xbf, 0x23, + 0x42, 0xa6, 0x43, 0xe4, 0x32, 0x46, 0xe8, 0x0c, + 0x3c, 0x04, 0xd0, 0x19, 0x78, 0x45, 0xce, 0x0b, + 0x16, 0xf9, 0x76, 0x23, +}; + +/* +==== CCMP test mpdu 2 ==== + +-- MPDU Fields + + 9 Version = 0 +10 Type = 2 SubType = 3 Data+CF-Ack+CF-Poll +11 ToDS = 0 FromDS = 0 +12 MoreFrag = 0 Retry = 0 +13 PwrMgt = 0 moreData = 0 +14 Encrypt = 1 +15 Order = 1 +16 Duration = 20842 +17 A1 = ea-10-0c-84-68-50 DA +18 A2 = ee-c1-76-2c-88-de SA +19 A3 = af-2e-e9-f4-6a-07 BSSID +20 SC = 0xCCE0 +21 seqNum = 3278 (0x0CCE) fraqNum = 0 (0x00) +22 Algorithm = AES_CCM +23 Key ID = 2 +24 TK = 8f 7a 05 3f a5 77 a5 59 75 29 27 20 97 a6 03 d5 +25 PN = 54923164817386 (0x31F3CBBA97EA) +26 802.11 Header = 38 c0 6a 51 ea 10 0c 84 68 50 ee c1 76 2c 88 de +27 af 2e e9 f4 6a 07 e0 cc +28 Muted 802.11 Header = 08 c0 ea 10 0c 84 68 50 ee c1 76 2c 88 de +29 af 2e e9 f4 6a 07 00 00 +30 CCMP Header = ea 97 00 a0 ba cb f3 31 +31 CCM Nonce = 00 ee c1 76 2c 88 de 31 f3 cb ba 97 ea +32 Plaintext Data = 83 a0 63 4b 5e d7 62 7e b9 df 22 5e 05 74 03 42 +33 de 19 41 17 +34 CCM MIC = 54 2f bf 8d a0 6a a4 ae +35 -- Encrypted MPDU with FCS +36 38 c0 6a 51 ea 10 0c 84 68 50 ee c1 76 2c 88 de af 2e e9 f4 6a 07 +37 e0 cc ea 97 00 a0 ba cb f3 31 81 4b 69 65 d0 5b f2 b2 ed 38 d4 be +38 b0 69 fe 82 71 4a 61 0b 54 2f bf 8d a0 6a a4 ae 25 3c 47 38 +*/ +static const u_int8_t test2_key[] = { /* TK */ + 0x8f, 0x7a, 0x05, 0x3f, 0xa5, 0x77, 0xa5, 0x59, 0x75, 0x29, 0x27, + 0x20, 0x97, 0xa6, 0x03, 0xd5 +}; +static const u_int8_t test2_plaintext[] = { /* Plaintext MPDU w/o MIC */ + 0x38, 0xc0, 0x6a, 0x51, 0xea, 0x10, 0x0c, 0x84, 0x68, 0x50, 0xee, + 0xc1, 0x76, 0x2c, 0x88, 0xde, 0xaf, 0x2e, 0xe9, 0xf4, 0x6a, 0x07, + 0xe0, 0xcc, + 0x83, 0xa0, 0x63, 0x4b, 0x5e, 0xd7, 0x62, 0x7e, 0xb9, 0xdf, 0x22, + 0x5e, 0x05, 0x74, 0x03, 0x42, 0xde, 0x19, 0x41, 0x17 +}; +static const u_int8_t test2_encrypted[] = { /* Encrypted MPDU with MIC */ + 0x38, 0xc0, 0x6a, 0x51, 0xea, 0x10, 0x0c, 0x84, 0x68, 0x50, 0xee, + 0xc1, 0x76, 0x2c, 0x88, 0xde, 0xaf, 0x2e, 0xe9, 0xf4, 0x6a, 0x07, + 0xe0, 0xcc, 0xea, 0x97, 0x00, 0xa0, 0xba, 0xcb, 0xf3, 0x31, 0x81, + 0x4b, 0x69, 0x65, 0xd0, 0x5b, 0xf2, 0xb2, 0xed, 0x38, 0xd4, 0xbe, + 0xb0, 0x69, 0xfe, 0x82, 0x71, 0x4a, 0x61, 0x0b, 0x54, 0x2f, 0xbf, + 0x8d, 0xa0, 0x6a, 0xa4, 0xae, +}; + +/* +==== CCMP test mpdu 3 ==== + +-- MPDU Fields + +41 Version = 0 +42 Type = 2 SubType = 11 +43 ToDS = 0 FromDS = 0 +44 MoreFrag = 0 Retry = 1 +45 PwrMgt = 0 moreData = 0 +46 Encrypt = 1 +47 Order = 1 +48 Duration = 25052 +49 A1 = d9-57-7d-f7-63-c8 DA +50 A2 = b6-a8-8a-df-36-91 SA +1 A3 = dc-4a-8b-ca-94-dd BSSID +2 SC = 0x8260 +3 seqNum = 2086 (0x0826) fraqNum = 0 (0x00) +4 QC = 0x0000 +5 MSDU Priority = 0 (0x0) +6 Algorithm = AES_CCM +7 Key ID = 2 +8 TK = 40 cf b7 a6 2e 88 01 3b d6 d3 af fc c1 91 04 1e +9 PN = 52624639632814 (0x2FDCA0F3A5AE) +10 802.11 Header = b8 c8 dc 61 d9 57 7d f7 63 c8 b6 a8 8a df 36 91 +11 dc 4a 8b ca 94 dd 60 82 20 85 +12 Muted 802.11 Header = 88 c0 d9 57 7d f7 63 c8 b6 a8 8a df 36 91 +13 dc 4a 8b ca 94 dd 00 00 00 00 +14 CCMP Header = ae a5 00 a0 f3 a0 dc 2f +15 CCM Nonce = 00 b6 a8 8a df 36 91 2f dc a0 f3 a5 ae +16 Plaintext Data = 2c 1b d0 36 83 1c 95 49 6c 5f 4d bf 3d 55 9e 72 +17 de 80 2a 18 +18 CCM MIC = fd 1f 1f 61 a9 fb 4b b3 +19 -- Encrypted MPDU with FCS +20 b8 c8 dc 61 d9 57 7d f7 63 c8 b6 a8 8a df 36 91 dc 4a 8b ca 94 dd +21 60 82 20 85 ae a5 00 a0 f3 a0 dc 2f 89 d8 58 03 40 b6 26 a0 b6 d4 +22 d0 13 bf 18 f2 91 b8 96 46 c8 fd 1f 1f 61 a9 fb 4b b3 60 3f 5a ad +*/ +static const u_int8_t test3_key[] = { /* TK */ + 0x40, 0xcf, 0xb7, 0xa6, 0x2e, 0x88, 0x01, 0x3b, 0xd6, 0xd3, + 0xaf, 0xfc, 0xc1, 0x91, 0x04, 0x1e +}; +static const u_int8_t test3_plaintext[] = { /* Plaintext MPDU w/o MIC */ + 0xb8, 0xc8, 0xdc, 0x61, 0xd9, 0x57, 0x7d, 0xf7, 0x63, 0xc8, + 0xb6, 0xa8, 0x8a, 0xdf, 0x36, 0x91, 0xdc, 0x4a, 0x8b, 0xca, + 0x94, 0xdd, 0x60, 0x82, 0x20, 0x85, + 0x2c, 0x1b, 0xd0, 0x36, 0x83, 0x1c, 0x95, 0x49, 0x6c, 0x5f, + 0x4d, 0xbf, 0x3d, 0x55, 0x9e, 0x72, 0xde, 0x80, 0x2a, 0x18 +}; +static const u_int8_t test3_encrypted[] = { /* Encrypted MPDU with MIC */ + 0xb8, 0xc8, 0xdc, 0x61, 0xd9, 0x57, 0x7d, 0xf7, 0x63, 0xc8, + 0xb6, 0xa8, 0x8a, 0xdf, 0x36, 0x91, 0xdc, 0x4a, 0x8b, 0xca, + 0x94, 0xdd, 0x60, 0x82, 0x20, 0x85, 0xae, 0xa5, 0x00, 0xa0, + 0xf3, 0xa0, 0xdc, 0x2f, 0x89, 0xd8, 0x58, 0x03, 0x40, 0xb6, + 0x26, 0xa0, 0xb6, 0xd4, 0xd0, 0x13, 0xbf, 0x18, 0xf2, 0x91, + 0xb8, 0x96, 0x46, 0xc8, 0xfd, 0x1f, 0x1f, 0x61, 0xa9, 0xfb, + 0x4b, 0xb3, +}; + +/* +==== CCMP test mpdu 4 ==== + +-- MPDU Fields +25 Version = 0 +26 Type = 2 SubType = 10 +27 ToDS = 0 FromDS = 1 +28 MoreFrag = 0 Retry = 1 +29 PwrMgt = 0 moreData = 0 +30 Encrypt = 1 +31 Order = 1 +32 Duration = 4410 +33 A1 = 71-2a-9d-df-11-db DA +34 A2 = 8e-f8-22-73-47-01 BSSID +35 A3 = 59-14-0d-d6-46-a2 SA +36 SC = 0x2FC0 +37 seqNum = 764 (0x02FC) fraqNum = 0 (0x00) +38 QC = 0x0007 +39 MSDU Priority = 7 (0x0) +40 Algorithm = AES_CCM +41 Key ID = 0 +42 TK = 8c 89 a2 eb c9 6c 76 02 70 7f cf 24 b3 2d 38 33 +43 PN = 270963670912995 (0xF670A55A0FE3) +44 802.11 Header = a8 ca 3a 11 71 2a 9d df 11 db 8e f8 22 73 47 01 +45 59 14 0d d6 46 a2 c0 2f 67 a5 +46 Muted 802.11 Header = 88 c2 71 2a 9d df 11 db 8e f8 22 73 47 01 +47 59 14 0d d6 46 a2 00 00 07 00 +48 CCMP Header = e3 0f 00 20 5a a5 70 f6 +49 CCM Nonce = 07 8e f8 22 73 47 01 f6 70 a5 5a 0f e3 +50 Plaintext Data = 4f ad 2b 1c 29 0f a5 eb d8 72 fb c3 f3 a0 74 89 +51 8f 8b 2f bb +52 CCM MIC = 31 fc 88 00 4f 35 ee 3d +-- Encrypted MPDU with FCS +2 a8 ca 3a 11 71 2a 9d df 11 db 8e f8 22 73 47 01 59 14 0d d6 46 a2 +3 c0 2f 67 a5 e3 0f 00 20 5a a5 70 f6 9d 59 b1 5f 37 14 48 c2 30 f4 +4 d7 39 05 2e 13 ab 3b 1a 7b 10 31 fc 88 00 4f 35 ee 3d 45 a7 4a 30 +*/ +static const u_int8_t test4_key[] = { /* TK */ + 0x8c, 0x89, 0xa2, 0xeb, 0xc9, 0x6c, 0x76, 0x02, + 0x70, 0x7f, 0xcf, 0x24, 0xb3, 0x2d, 0x38, 0x33, +}; +static const u_int8_t test4_plaintext[] = { /* Plaintext MPDU w/o MIC */ + 0xa8, 0xca, 0x3a, 0x11, 0x71, 0x2a, 0x9d, 0xdf, 0x11, 0xdb, + 0x8e, 0xf8, 0x22, 0x73, 0x47, 0x01, 0x59, 0x14, 0x0d, 0xd6, + 0x46, 0xa2, 0xc0, 0x2f, 0x67, 0xa5, + 0x4f, 0xad, 0x2b, 0x1c, 0x29, 0x0f, 0xa5, 0xeb, 0xd8, 0x72, + 0xfb, 0xc3, 0xf3, 0xa0, 0x74, 0x89, 0x8f, 0x8b, 0x2f, 0xbb, +}; +static const u_int8_t test4_encrypted[] = { /* Encrypted MPDU with MIC */ + 0xa8, 0xca, 0x3a, 0x11, 0x71, 0x2a, 0x9d, 0xdf, 0x11, 0xdb, + 0x8e, 0xf8, 0x22, 0x73, 0x47, 0x01, 0x59, 0x14, 0x0d, 0xd6, + 0x46, 0xa2, 0xc0, 0x2f, 0x67, 0xa5, 0xe3, 0x0f, 0x00, 0x20, + 0x5a, 0xa5, 0x70, 0xf6, 0x9d, 0x59, 0xb1, 0x5f, 0x37, 0x14, + 0x48, 0xc2, 0x30, 0xf4, 0xd7, 0x39, 0x05, 0x2e, 0x13, 0xab, + 0x3b, 0x1a, 0x7b, 0x10, 0x31, 0xfc, 0x88, 0x00, 0x4f, 0x35, + 0xee, 0x3d, +}; + +/* +==== CCMP test mpdu 5 ==== + +-- MPDU Fields + +7 Version = 0 +8 Type = 2 SubType = 8 +9 ToDS = 0 FromDS = 1 +10 MoreFrag = 0 Retry = 1 +11 PwrMgt = 1 moreData = 0 +12 Encrypt = 1 +13 Order = 1 +14 Duration = 16664 +15 A1 = 45-de-c6-9a-74-80 DA +16 A2 = f3-51-94-6b-c9-6b BSSID +17 A3 = e2-76-fb-e6-c1-27 SA +18 SC = 0xF280 +19 seqNum = 3880 (0x0F28) fraqNum = 0 (0x00) +20 QC = 0x000b +21 MSDU Priority = 0 (0x0) +22 Algorithm = AES_CCM +23 Key ID = 2 +24 TK = a5 74 d5 14 3b b2 5e fd de ff 30 12 2f df d0 66 +25 PN = 184717420531255 (0xA7FFE03C0E37) +26 802.11 Header = 88 da 18 41 45 de c6 9a 74 80 f3 51 94 6b c9 6b +27 e2 76 fb e6 c1 27 80 f2 4b 19 +28 Muted 802.11 Header = 88 c2 45 de c6 9a 74 80 f3 51 94 6b c9 6b +29 e2 76 fb e6 c1 27 00 00 0b 00 +30 CCMP Header = 37 0e 00 a0 3c e0 ff a7 +31 CCM Nonce = 0b f3 51 94 6b c9 6b a7 ff e0 3c 0e 37 +32 Plaintext Data = 28 96 9b 95 4f 26 3a 80 18 a9 ef 70 a8 b0 51 46 +33 24 81 92 2e +34 CCM MIC = ce 0c 3b e1 97 d3 05 eb +35 -- Encrypted MPDU with FCS +36 88 da 18 41 45 de c6 9a 74 80 f3 51 94 6b c9 6b e2 76 fb e6 c1 27 +37 80 f2 4b 19 37 0e 00 a0 3c e0 ff a7 eb 4a e4 95 6a 80 1d a9 62 4b +38 7e 0c 18 b2 3e 61 5e c0 3a f6 ce 0c 3b e1 97 d3 05 eb c8 9e a1 b5 +*/ +static const u_int8_t test5_key[] = { /* TK */ + 0xa5, 0x74, 0xd5, 0x14, 0x3b, 0xb2, 0x5e, 0xfd, + 0xde, 0xff, 0x30, 0x12, 0x2f, 0xdf, 0xd0, 0x66, +}; +static const u_int8_t test5_plaintext[] = { /* Plaintext MPDU w/o MIC */ + 0x88, 0xda, 0x18, 0x41, 0x45, 0xde, 0xc6, 0x9a, 0x74, 0x80, + 0xf3, 0x51, 0x94, 0x6b, 0xc9, 0x6b, 0xe2, 0x76, 0xfb, 0xe6, + 0xc1, 0x27, 0x80, 0xf2, 0x4b, 0x19, + 0x28, 0x96, 0x9b, 0x95, 0x4f, 0x26, 0x3a, 0x80, 0x18, 0xa9, + 0xef, 0x70, 0xa8, 0xb0, 0x51, 0x46, 0x24, 0x81, 0x92, 0x2e, +}; +static const u_int8_t test5_encrypted[] = { /* Encrypted MPDU with MIC */ + 0x88, 0xda, 0x18, 0x41, 0x45, 0xde, 0xc6, 0x9a, 0x74, 0x80, + 0xf3, 0x51, 0x94, 0x6b, 0xc9, 0x6b, 0xe2, 0x76, 0xfb, 0xe6, + 0xc1, 0x27, 0x80, 0xf2, 0x4b, 0x19, 0x37, 0x0e, 0x00, 0xa0, + 0x3c, 0xe0, 0xff, 0xa7, 0xeb, 0x4a, 0xe4, 0x95, 0x6a, 0x80, + 0x1d, 0xa9, 0x62, 0x4b, 0x7e, 0x0c, 0x18, 0xb2, 0x3e, 0x61, + 0x5e, 0xc0, 0x3a, 0xf6, 0xce, 0x0c, 0x3b, 0xe1, 0x97, 0xd3, + 0x05, 0xeb, +}; + +/* +==== CCMP test mpdu 6 ==== + +-- MPDU Fields + +41 Version = 0 +42 Type = 2 SubType = 8 +43 ToDS = 0 FromDS = 1 +44 MoreFrag = 0 Retry = 0 +45 PwrMgt = 1 moreData = 0 +46 Encrypt = 1 +47 Order = 0 +48 Duration = 8161 +49 A1 = 5a-f2-84-30-fd-ab DA +50 A2 = bf-f9-43-b9-f9-a6 BSSID +1 A3 = ab-1d-98-c7-fe-73 SA +2 SC = 0x7150 +3 seqNum = 1813 (0x0715) fraqNum = 0 (0x00) +4 QC = 0x000d +5 PSDU Priority = 13 (0xd) +6 Algorithm = AES_CCM +7 Key ID = 1 +8 TK = f7 1e ea 4e 1f 58 80 4b 97 17 23 0a d0 61 46 41 +9 PN = 118205765159305 (0x6B81ECA48989) +10 802.11 Header = 88 52 e1 1f 5a f2 84 30 fd ab bf f9 43 b9 f9 a6 +11 ab 1d 98 c7 fe 73 50 71 3d 6a +12 Muted 802.11 Header = 88 42 5a f2 84 30 fd ab bf f9 43 b9 f9 a6 +13 ab 1d 98 c7 fe 73 00 00 0d 00 +14 CCMP Header = 89 89 00 60 a4 ec 81 6b +15 CCM Nonce = 0d bf f9 43 b9 f9 a6 6b 81 ec a4 89 89 +16 Plaintext Data = ab fd a2 2d 3a 0b fc 9c c1 fc 07 93 63 c2 fc a1 +17 43 e6 eb 1d +18 CCM MIC = 30 9a 8d 5c 46 6b bb 71 +19 -- Encrypted MPDU with FCS +20 88 52 e1 1f 5a f2 84 30 fd ab bf f9 43 b9 f9 a6 ab 1d 98 c7 fe 73 +21 50 71 3d 6a 89 89 00 60 a4 ec 81 6b 9a 70 9b 60 a3 9d 40 b1 df b6 +22 12 e1 8b 5f 11 4b ad b6 cc 86 30 9a 8d 5c 46 6b bb 71 86 c0 4e 97 +*/ +static const u_int8_t test6_key[] = { /* TK */ + 0xf7, 0x1e, 0xea, 0x4e, 0x1f, 0x58, 0x80, 0x4b, + 0x97, 0x17, 0x23, 0x0a, 0xd0, 0x61, 0x46, 0x41, +}; +static const u_int8_t test6_plaintext[] = { /* Plaintext MPDU w/o MIC */ + 0x88, 0x52, 0xe1, 0x1f, 0x5a, 0xf2, 0x84, 0x30, 0xfd, 0xab, + 0xbf, 0xf9, 0x43, 0xb9, 0xf9, 0xa6, 0xab, 0x1d, 0x98, 0xc7, + 0xfe, 0x73, 0x50, 0x71, 0x3d, 0x6a, + 0xab, 0xfd, 0xa2, 0x2d, 0x3a, 0x0b, 0xfc, 0x9c, 0xc1, 0xfc, + 0x07, 0x93, 0x63, 0xc2, 0xfc, 0xa1, 0x43, 0xe6, 0xeb, 0x1d, +}; +static const u_int8_t test6_encrypted[] = { /* Encrypted MPDU with MIC */ + 0x88, 0x52, 0xe1, 0x1f, 0x5a, 0xf2, 0x84, 0x30, 0xfd, 0xab, + 0xbf, 0xf9, 0x43, 0xb9, 0xf9, 0xa6, 0xab, 0x1d, 0x98, 0xc7, + 0xfe, 0x73, 0x50, 0x71, 0x3d, 0x6a, 0x89, 0x89, 0x00, 0x60, + 0xa4, 0xec, 0x81, 0x6b, 0x9a, 0x70, 0x9b, 0x60, 0xa3, 0x9d, + 0x40, 0xb1, 0xdf, 0xb6, 0x12, 0xe1, 0x8b, 0x5f, 0x11, 0x4b, + 0xad, 0xb6, 0xcc, 0x86, 0x30, 0x9a, 0x8d, 0x5c, 0x46, 0x6b, + 0xbb, 0x71, +}; + +/* +==== CCMP test mpdu 7 ==== + +-- MPDU Fields + +25 Version = 0 +26 Type = 2 SubType = 1 Data+CF-Ack +27 ToDS = 1 FromDS = 0 +28 MoreFrag = 0 Retry = 1 +29 PwrMgt = 1 moreData = 1 +30 Encrypt = 1 +31 Order = 0 +32 Duration = 18049 +33 A1 = 9b-50-f4-fd-56-f6 BSSID +34 A2 = ef-ec-95-20-16-91 SA +35 A3 = 83-57-0c-4c-cd-ee DA +36 SC = 0xA020 +37 seqNum = 2562 (0x0A02) fraqNum = 0 (0x00) +38 Algorithm = AES_CCM +39 Key ID = 3 +40 TK = 1b db 34 98 0e 03 81 24 a1 db 1a 89 2b ec 36 6a +41 PN = 104368786630435 (0x5EEC4073E723) +42 Header = 18 79 81 46 9b 50 f4 fd 56 f6 ef ec 95 20 16 91 83 57 +43 0c 4c cd ee 20 a0 +44 Muted MAC Header = 08 41 9b 50 f4 fd 56 f6 ef ec 95 20 16 91 +45 83 57 0c 4c cd ee 00 00 +46 CCMP Header = 23 e7 00 e0 73 40 ec 5e +47 CCM Nonce = 00 ef ec 95 20 16 91 5e ec 40 73 e7 23 +48 Plaintext Data = 98 be ca 86 f4 b3 8d a2 0c fd f2 47 24 c5 8e b8 +49 35 66 53 39 +50 CCM MIC = 2d 09 57 ec fa be 95 b9 +-- Encrypted MPDU with FCS +1 18 79 81 46 9b 50 f4 fd 56 f6 ef ec 95 20 16 91 83 57 0c 4c cd ee +2 20 a0 23 e7 00 e0 73 40 ec 5e 12 c5 37 eb f3 ab 58 4e f1 fe f9 a1 +3 f3 54 7a 8c 13 b3 22 5a 2d 09 57 ec fa be 95 b9 aa fa 0c c8 +*/ +static const u_int8_t test7_key[] = { /* TK */ + 0x1b, 0xdb, 0x34, 0x98, 0x0e, 0x03, 0x81, 0x24, + 0xa1, 0xdb, 0x1a, 0x89, 0x2b, 0xec, 0x36, 0x6a, +}; +static const u_int8_t test7_plaintext[] = { /* Plaintext MPDU w/o MIC */ + 0x18, 0x79, 0x81, 0x46, 0x9b, 0x50, 0xf4, 0xfd, 0x56, 0xf6, + 0xef, 0xec, 0x95, 0x20, 0x16, 0x91, 0x83, 0x57, 0x0c, 0x4c, + 0xcd, 0xee, 0x20, 0xa0, + 0x98, 0xbe, 0xca, 0x86, 0xf4, 0xb3, 0x8d, 0xa2, 0x0c, 0xfd, + 0xf2, 0x47, 0x24, 0xc5, 0x8e, 0xb8, 0x35, 0x66, 0x53, 0x39, +}; +static const u_int8_t test7_encrypted[] = { /* Encrypted MPDU with MIC */ + 0x18, 0x79, 0x81, 0x46, 0x9b, 0x50, 0xf4, 0xfd, 0x56, 0xf6, + 0xef, 0xec, 0x95, 0x20, 0x16, 0x91, 0x83, 0x57, 0x0c, 0x4c, + 0xcd, 0xee, 0x20, 0xa0, 0x23, 0xe7, 0x00, 0xe0, 0x73, 0x40, + 0xec, 0x5e, 0x12, 0xc5, 0x37, 0xeb, 0xf3, 0xab, 0x58, 0x4e, + 0xf1, 0xfe, 0xf9, 0xa1, 0xf3, 0x54, 0x7a, 0x8c, 0x13, 0xb3, + 0x22, 0x5a, 0x2d, 0x09, 0x57, 0xec, 0xfa, 0xbe, 0x95, 0xb9, +}; + +/* +==== CCMP test mpdu 8 ==== + +-- MPDU Fields + +6 Version = 0 +7 Type = 2 SubType = 11 +8 ToDS = 1 FromDS = 0 +9 MoreFrag = 0 Retry = 1 +10 PwrMgt = 1 moreData = 0 +11 Encrypt = 1 +12 Order = 1 +13 Duration = 29260 +14 A1 = 55-2d-5f-72-bb-70 BSSID +15 A2 = ca-3f-3a-ae-60-c4 SA +16 A3 = 8b-a9-b5-f8-2c-2f DA +17 SC = 0xEB50 +18 seqNum = 3765 (0x0EB5) fraqNum = 0 (0x00) +19 QC = 0x000a +20 MSDU Priority = 10 (0xa) +21 Algorithm = AES_CCM +22 Key ID = 2 +23 TK = 6e ac 1b f5 4b d5 4e db 23 21 75 43 03 02 4c 71 +24 PN = 227588596223197 (0xCEFD996ECCDD) +25 802.11 Header = b8 d9 4c 72 55 2d 5f 72 bb 70 ca 3f 3a ae 60 c4 +26 8b a9 b5 f8 2c 2f 50 eb 2a 55 +27 Muted 802.11 Header = 88 c1 55 2d 5f 72 bb 70 ca 3f 3a ae 60 c4 +28 8b a9 b5 f8 2c 2f 00 00 0a 00 +29 CCMP Header = dd cc 00 a0 6e 99 fd ce +30 CCM Nonce = 0a ca 3f 3a ae 60 c4 ce fd 99 6e cc dd +31 Plaintext Data = 57 cb 5c 0e 5f cd 88 5e 9a 42 39 e9 b9 ca d6 0d +32 64 37 59 79 +33 CCM MIC = 6d ba 8e f7 f0 80 87 dd +-- Encrypted MPDU with FCS +35 b8 d9 4c 72 55 2d 5f 72 bb 70 ca 3f 3a ae 60 c4 8b a9 b5 f8 2c 2f +36 50 eb 2a 55 dd cc 00 a0 6e 99 fd ce 4b f2 81 ef 8e c7 73 9f 91 59 +37 1b 97 a8 7d c1 4b 3f a1 74 62 6d ba 8e f7 f0 80 87 dd 0c 65 74 3f +*/ +static const u_int8_t test8_key[] = { /* TK */ + 0x6e, 0xac, 0x1b, 0xf5, 0x4b, 0xd5, 0x4e, 0xdb, + 0x23, 0x21, 0x75, 0x43, 0x03, 0x02, 0x4c, 0x71, +}; +static const u_int8_t test8_plaintext[] = { /* Plaintext MPDU w/o MIC */ + 0xb8, 0xd9, 0x4c, 0x72, 0x55, 0x2d, 0x5f, 0x72, 0xbb, 0x70, + 0xca, 0x3f, 0x3a, 0xae, 0x60, 0xc4, 0x8b, 0xa9, 0xb5, 0xf8, + 0x2c, 0x2f, 0x50, 0xeb, 0x2a, 0x55, + 0x57, 0xcb, 0x5c, 0x0e, 0x5f, 0xcd, 0x88, 0x5e, 0x9a, 0x42, + 0x39, 0xe9, 0xb9, 0xca, 0xd6, 0x0d, 0x64, 0x37, 0x59, 0x79, +}; +static const u_int8_t test8_encrypted[] = { /* Encrypted MPDU with MIC */ + 0xb8, 0xd9, 0x4c, 0x72, 0x55, 0x2d, 0x5f, 0x72, 0xbb, 0x70, + 0xca, 0x3f, 0x3a, 0xae, 0x60, 0xc4, 0x8b, 0xa9, 0xb5, 0xf8, + 0x2c, 0x2f, 0x50, 0xeb, 0x2a, 0x55, 0xdd, 0xcc, 0x00, 0xa0, + 0x6e, 0x99, 0xfd, 0xce, 0x4b, 0xf2, 0x81, 0xef, 0x8e, 0xc7, + 0x73, 0x9f, 0x91, 0x59, 0x1b, 0x97, 0xa8, 0x7d, 0xc1, 0x4b, + 0x3f, 0xa1, 0x74, 0x62, 0x6d, 0xba, 0x8e, 0xf7, 0xf0, 0x80, + 0x87, 0xdd, +}; + +#define TEST(n,name,cipher,keyix,pn) { \ + name, IEEE80211_CIPHER_##cipher,keyix, pn##LL, \ + test##n##_key, sizeof(test##n##_key), \ + test##n##_plaintext, sizeof(test##n##_plaintext), \ + test##n##_encrypted, sizeof(test##n##_encrypted) \ +} + +struct ciphertest { + const char *name; + int cipher; + int keyix; + u_int64_t pn; + const u_int8_t *key; + size_t key_len; + const u_int8_t *plaintext; + size_t plaintext_len; + const u_int8_t *encrypted; + size_t encrypted_len; +} ccmptests[] = { + TEST(1, "CCMP test mpdu 1", AES_CCM, 0, 199027030681356), + TEST(2, "CCMP test mpdu 2", AES_CCM, 2, 54923164817386), + TEST(3, "CCMP test mpdu 3", AES_CCM, 2, 52624639632814), + TEST(4, "CCMP test mpdu 4", AES_CCM, 0, 270963670912995), + TEST(5, "CCMP test mpdu 5", AES_CCM, 2, 184717420531255), + TEST(6, "CCMP test mpdu 6", AES_CCM, 1, 118205765159305), + TEST(7, "CCMP test mpdu 7", AES_CCM, 3, 104368786630435), + TEST(8, "CCMP test mpdu 8", AES_CCM, 2, 227588596223197), +}; + +static void +dumpdata(const char *tag, const void *p, size_t len) +{ + int i; + + printk("%s: 0x%p len %u", tag, p, len); + for (i = 0; i < len; i++) { + if ((i % 16) == 0) + printk("\n%03d:", i); + printk(" %02x", ((u_int8_t *)p)[i]); + } + printk("\n"); +} + +static void +cmpfail(const void *gen, size_t genlen, const void *ref, size_t reflen) +{ + int i; + + for (i = 0; i < genlen; i++) + if (((u_int8_t *)gen)[i] != ((u_int8_t *)ref)[i]) { + printk("first difference at byte %u\n", i); + break; + } + dumpdata("Generated", gen, genlen); + dumpdata("Reference", ref, reflen); +} + +int +runtest(struct ieee80211com *ic, struct ciphertest *t) +{ + struct ieee80211_key key; + struct sk_buff *skb = NULL; + const struct ieee80211_cipher *cip; + u_int8_t mac[IEEE80211_ADDR_LEN]; + + printk("%s: ", t->name); + + /* + * Setup key. + */ + memset(&key, 0, sizeof(key)); + key.wk_flags = IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV; + key.wk_cipher = &ieee80211_cipher_none; + if (!ieee80211_crypto_newkey(ic, t->cipher, &key)) { + printk("FAIL: ieee80211_crypto_newkey failed\n"); + goto bad; + } + + memcpy(key.wk_key, t->key, t->key_len); + key.wk_keylen = t->key_len; + key.wk_keyrsc = 0; + key.wk_keytsc = t->pn-1; /* PN-1 since we do encap */ + if (!ieee80211_crypto_setkey(ic, &key, mac)) { + printk("FAIL: ieee80211_crypto_setkey failed\n"); + goto bad; + } + + /* + * Craft frame from plaintext data. + */ + cip = key.wk_cipher; + skb = dev_alloc_skb(t->plaintext_len + + cip->ic_header + cip->ic_trailer); + if (skb == NULL) { + printk("FAIL: unable to allocate skbuff\n"); + goto bad; + } + skb_reserve(skb, cip->ic_header); + memcpy(skb_put(skb, t->plaintext_len), + t->plaintext, t->plaintext_len); + + /* + * Encrypt frame w/ MIC. + */ + if (!(*cip->ic_encap)(&key, skb, t->keyix<<6)) { + printk("FAIL: ccmp encap failed\n"); + goto bad; + } + /* + * Verify: frame length, frame contents. + */ + if (skb->len != t->encrypted_len) { + printk("FAIL: encap data length mismatch\n"); + cmpfail(skb->data, skb->len, + t->encrypted, t->encrypted_len); + goto bad; + } else if (memcmp(skb->data, t->encrypted, skb->len)) { + printk("FAIL: encrypt data does not compare\n"); + cmpfail(skb->data, skb->len, + t->encrypted, t->encrypted_len); + dumpdata("Plaintext", t->plaintext, t->plaintext_len); + goto bad; + } + + /* + * Decrypt frame; strip MIC. + */ + if (!(*cip->ic_decap)(&key, skb)) { + printk("FAIL: ccmp decap failed\n"); + cmpfail(skb->data, skb->len, + t->plaintext, t->plaintext_len); + goto bad; + } + /* + * Verify: frame length, frame contents. + */ + if (skb->len != t->plaintext_len) { + printk("FAIL: decap botch; length mismatch\n"); + cmpfail(skb->data, skb->len, + t->plaintext, t->plaintext_len); + goto bad; + } else if (memcmp(skb->data, t->plaintext, t->plaintext_len)) { + printk("FAIL: decap botch; data does not compare\n"); + cmpfail(skb->data, skb->len, + t->plaintext, sizeof(t->plaintext)); + goto bad; + } + dev_kfree_skb(skb); + ieee80211_crypto_delkey(ic, &key); + printk("PASS\n"); + return 1; +bad: + if (skb != NULL) + dev_kfree_skb(skb); + ieee80211_crypto_delkey(ic, &key); + return 0; +} + +/* + * Module glue. + */ + +MODULE_AUTHOR("Errno Consulting, Sam Leffler"); +MODULE_DESCRIPTION("802.11 wireless support: AES-CCMP cipher tester"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("Dual BSD/GPL"); +#endif + +static int tests = -1; +MODULE_PARM(tests, "i"); +MODULE_PARM_DESC(tests, "Specify which tests to run"); + +static int debug = 0; +MODULE_PARM(debug, "i"); +MODULE_PARM_DESC(debug, "Enable IEEE80211_MSG_CRYPTO"); + +static int __init +init_crypto_ccmp_test(void) +{ +#define N(a) (sizeof(a)/sizeof(a[0])) + struct ieee80211com ic; + int i, pass, total; + + memset(&ic, 0, sizeof(ic)); + if (debug) + ic.msg_enable = IEEE80211_MSG_CRYPTO; + ieee80211_crypto_attach(&ic); + + pass = 0; + total = 0; + for (i = 0; i < N(ccmptests); i++) + if (tests & (1< +#include +#include +#include +#include + +#include +#include "if_media.h" +#include + +/* +Key 12 34 56 78 90 12 34 56 78 90 12 34 56 78 90 12 + 34 56 78 90 12 34 56 78 90 12 34 56 78 90 12 34 +PN 0x000000000001 +IV 00 20 01 20 00 00 00 00 +Phase1 bb 58 07 1f 9e 93 b4 38 25 4b +Phase2 00 20 01 4c fe 67 be d2 7c 86 7b 1b f8 02 8b 1c +*/ + +static const u_int8_t ref_key[] = { + 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, + 0x34, 0x56, 0x78, 0x90, 0x12, + + 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, /* TX MIC */ + /* + * NB: 11i test vector specifies a RX MIC key different + * from the TX key. But this doesn't work to enmic, + * encrypt, then decrypt, demic. So instead we use + * the same key for doing the MIC in each direction. + * + * XXX need additional vectors to test alternate MIC keys + */ +#if 0 + 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, /* 11i RX MIC */ +#else + 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, /* TX copy */ +#endif +}; +static const u_int8_t ref_phase1[] = { + 0xbb, 0x58, 0x07, 0x1f, 0x9e, 0x93, 0xb4, 0x38, 0x25, 0x4b +}; +static const u_int8_t ref_phase2[] = { + 0x00, 0x20, 0x01, 0x4c, 0xfe, 0x67, 0xbe, 0xd2, 0x7c, 0x86, + 0x7b, 0x1b, 0xf8, 0x02, 0x8b, 0x1c, +}; + +/* Plaintext MPDU with MIC */ +static const u_int8_t ref_plaintext[] = { +0x08,0x42,0x2c,0x00,0x02,0x03,0x04,0x05,0x06,0x08,0x02,0x03,0x04,0x05,0x06,0x07, +0x02,0x03,0x04,0x05,0x06,0x07,0xd0,0x02, +0xaa,0xaa,0x03,0x00,0x00,0x00,0x08,0x00,0x45,0x00,0x00,0x54,0x00,0x00,0x40,0x00, +0x40,0x01,0xa5,0x55,0xc0,0xa8,0x0a,0x02,0xc0,0xa8,0x0a,0x01,0x08,0x00,0x3a,0xb0, +0x00,0x00,0x00,0x00,0xcd,0x4c,0x05,0x00,0x00,0x00,0x00,0x00,0x08,0x09,0x0a,0x0b, +0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b, +0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b, +0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, +/* MIC */ 0x68,0x81,0xa3,0xf3,0xd6,0x48,0xd0,0x3c +}; + +/* Encrypted MPDU with MIC and ICV */ +static const u_int8_t ref_encrypted[] = { +0x08,0x42,0x2c,0x00,0x02,0x03,0x04,0x05,0x06,0x08,0x02,0x03,0x04,0x05,0x06,0x07, +0x02,0x03,0x04,0x05,0x06,0x07,0xd0,0x02,0x00,0x20,0x01,0x20,0x00,0x00,0x00,0x00, +0xc0,0x0e,0x14,0xfc,0xe7,0xcf,0xab,0xc7,0x75,0x47,0xe6,0x66,0xe5,0x7c,0x0d,0xac, +0x70,0x4a,0x1e,0x35,0x8a,0x88,0xc1,0x1c,0x8e,0x2e,0x28,0x2e,0x38,0x01,0x02,0x7a, +0x46,0x56,0x05,0x5e,0xe9,0x3e,0x9c,0x25,0x47,0x02,0xe9,0x73,0x58,0x05,0xdd,0xb5, +0x76,0x9b,0xa7,0x3f,0x1e,0xbb,0x56,0xe8,0x44,0xef,0x91,0x22,0x85,0xd3,0xdd,0x6e, +0x54,0x1e,0x82,0x38,0x73,0x55,0x8a,0xdb,0xa0,0x79,0x06,0x8a,0xbd,0x7f,0x7f,0x50, +0x95,0x96,0x75,0xac,0xc4,0xb4,0xde,0x9a,0xa9,0x9c,0x05,0xf2,0x89,0xa7,0xc5,0x2f, +0xee,0x5b,0xfc,0x14,0xf6,0xf8,0xe5,0xf8 +}; + +struct tkip_ctx { + struct ieee80211com *tc_ic; /* for diagnostics */ + + u16 tx_ttak[5]; + int tx_phase1_done; + u8 tx_rc4key[16]; + + u16 rx_ttak[5]; + int rx_phase1_done; + u8 rx_rc4key[16]; + u_int64_t rx_rsc; /* held until MIC verified */ +}; + +static void +dumpdata(const char *tag, const void *p, size_t len) +{ + int i; + + printk("%s: 0x%p len %u", tag, p, len); + for (i = 0; i < len; i++) { + if ((i % 16) == 0) + printk("\n%03d:", i); + printk(" %02x", ((u_int8_t *)p)[i]); + } + printk("\n"); +} + +static void +cmpfail(const void *gen, size_t genlen, const void *ref, size_t reflen) +{ + int i; + + for (i = 0; i < genlen; i++) + if (((u_int8_t *)gen)[i] != ((u_int8_t *)ref)[i]) { + printk("first difference at byte %u\n", i); + break; + } + dumpdata("Generated", gen, genlen); + dumpdata("Reference", ref, reflen); +} + +void +tkip_test(struct ieee80211com *ic) +{ + struct tkip_ctx *ctx; + struct ieee80211_key key; + struct sk_buff *skb = NULL; + const struct ieee80211_cipher *cip; + u_int8_t mac[IEEE80211_ADDR_LEN]; + + /* + * Setup key. + */ + memset(&key, 0, sizeof(key)); + key.wk_flags = IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV; + key.wk_cipher = &ieee80211_cipher_none; + if (!ieee80211_crypto_newkey(ic, IEEE80211_CIPHER_TKIP, &key)) { + printk("ieee80211_crypto_newkey failed\n"); + goto bad; + } + + memcpy(key.wk_key, ref_key, sizeof(ref_key)); + key.wk_keylen = 128/NBBY; + key.wk_keyrsc = 0; + key.wk_keytsc = 0; + if (!ieee80211_crypto_setkey(ic, &key, mac)) { + printk("ieee80211_crypto_setkey failed\n"); + goto bad; + } + + /* + * Craft frame from plaintext data. Note that + * we leave the MIC off as we'll add it ourself + * and then check it against the reference data. + */ + cip = key.wk_cipher; + skb = dev_alloc_skb(sizeof(ref_plaintext) + + cip->ic_miclen + cip->ic_header + cip->ic_trailer); + if (skb == NULL) { + printk("unable to allocate skbuff\n"); + goto bad; + } + skb_reserve(skb, cip->ic_header); + memcpy(skb_put(skb, sizeof(ref_plaintext) - cip->ic_miclen), + ref_plaintext, sizeof(ref_plaintext) - cip->ic_miclen); + + /* + * Add MIC. + */ + if (!ieee80211_crypto_enmic(ic, &key, skb)) { + printk("tkip enmic failed\n"); + goto bad; + } + /* + * Verify: frame length, frame contents. + */ + if (skb->len != sizeof(ref_plaintext)) { + printk("enmic botch; length mismatch\n"); + cmpfail(skb->data, skb->len, + ref_plaintext, sizeof(ref_plaintext)); + goto bad; + } + if (memcmp(skb->data, ref_plaintext, sizeof(ref_plaintext))) { + printk("enmic botch\n"); + cmpfail(skb->data, skb->len, + ref_plaintext, sizeof(ref_plaintext)); + goto bad; + } + /* + * Encrypt frame w/ MIC. + */ + if (!(*cip->ic_encap)(&key, skb, 0<<6)) { + printk("tkip encap failed\n"); + goto bad; + } + /* + * Verify: phase1, phase2, frame length, frame contents. + */ + ctx = key.wk_private; + if (memcmp(ctx->tx_ttak, ref_phase1, sizeof(ref_phase1))) { + printk("encrypt phase1 botch\n"); + cmpfail(ctx->tx_ttak, sizeof(ctx->tx_ttak), + ref_phase1, sizeof(ref_phase1)); + goto bad; + } else if (memcmp(ctx->tx_rc4key, ref_phase2, sizeof(ref_phase2))) { + printf("encrypt phase2 botch\n"); + cmpfail(ctx->tx_rc4key, sizeof(ctx->tx_rc4key), + ref_phase2, sizeof(ref_phase2)); + goto bad; + } else if (skb->len != sizeof(ref_encrypted)) { + printk("encrypt data length mismatch\n"); + cmpfail(skb->data, skb->len, + ref_encrypted, sizeof(ref_encrypted)); + goto bad; + } else if (memcmp(skb->data, ref_encrypted, skb->len)) { + printk("encrypt data does not compare\n"); + cmpfail(skb->data, skb->len, + ref_encrypted, sizeof(ref_encrypted)); + dumpdata("Plaintext", ref_plaintext, sizeof(ref_plaintext)); + goto bad; + } + + /* + * Decrypt frame. + */ + if (!(*cip->ic_decap)(&key, skb)) { + printk("tkip decap failed\n"); + /* + * Check reason for failure: phase1, phase2, frame data (ICV). + */ + if (memcmp(ctx->rx_ttak, ref_phase1, sizeof(ref_phase1))) { + printk("decrypt phase1 botch\n"); + cmpfail(ctx->rx_ttak, sizeof(ctx->rx_ttak), + ref_phase1, sizeof(ref_phase1)); + } else if (memcmp(ctx->rx_rc4key, ref_phase2, sizeof(ref_phase2))) { + printf("decrypt phase2 botch\n"); + cmpfail(ctx->rx_rc4key, sizeof(ctx->rx_rc4key), + ref_phase2, sizeof(ref_phase2)); + } else { + printk("decrypt data does not compare\n"); + cmpfail(skb->data, skb->len, + ref_plaintext, sizeof(ref_plaintext)); + } + goto bad; + } + /* + * Verify: frame length, frame contents. + */ + if (skb->len != sizeof(ref_plaintext)) { + printk("decap botch; length mismatch\n"); + cmpfail(skb->data, skb->len, + ref_plaintext, sizeof(ref_plaintext)); + goto bad; + } + if (memcmp(skb->data, ref_plaintext, sizeof(ref_plaintext))) { + printk("decap botch; data does not compare\n"); + cmpfail(skb->data, skb->len, + ref_plaintext, sizeof(ref_plaintext)); + goto bad; + } + /* + * De-MIC decrypted frame. + */ + if (!ieee80211_crypto_demic(ic, &key, skb)) { + printk("tkip demic failed\n"); + goto bad; + } + /* XXX check frame length and contents... */ + printk("802.11i TKIP test vectors passed\n"); +bad: + if (skb != NULL) + dev_kfree_skb(skb); + ieee80211_crypto_delkey(ic, &key); +} + +/* + * Module glue. + */ + +MODULE_AUTHOR("Errno Consulting, Sam Leffler"); +MODULE_DESCRIPTION("802.11 wireless support: TKIP cipher tester"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("Dual BSD/GPL"); +#endif + +static int debug = 0; +MODULE_PARM(debug, "i"); +MODULE_PARM_DESC(debug, "Enable IEEE80211_MSG_CRYPTO"); + +static int __init +init_crypto_tkip_test(void) +{ + struct ieee80211com ic; + + memset(&ic, 0, sizeof(ic)); + if (debug) + ic.msg_enable = IEEE80211_MSG_CRYPTO; + ieee80211_crypto_attach(&ic); + + tkip_test(&ic); + + ieee80211_crypto_detach(&ic); + return 0; +} +module_init(init_crypto_tkip_test); + +static void __exit +exit_crypto_tkip_test(void) +{ +} +module_exit(exit_crypto_tkip_test); diff -Naur linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/test_wep.c linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/test_wep.c --- linux-2.6.11-rc4-mm1/drivers/net/wireless/net80211/test_wep.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.11-rc4-mm1-wifi/drivers/net/wireless/net80211/test_wep.c 2005-02-24 13:06:17.415114520 -0800 @@ -0,0 +1,326 @@ +/*- + * Copyright (c) 2004-2005 Sam Leffler, Errno Consulting + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * WEP test module. + * + * Test vectors come from section I.7.2 of P802.11i/D7.0, October 2003. + * + * To use this tester load the net80211 layer (either as a module or + * by statically configuring it into your kernel), then insmod this + * module. It should automatically run all test cases and print + * information for each. To run one or more tests you can specify a + * tests parameter to the module that is a bit mask of the set of tests + * you want; e.g. insmod wep_test tests=7 will run only test mpdu's + * 1, 2, and 3. + */ +#include +#include +#include +#include +#include + +#include +#include "if_media.h" +#include + +/* +MPDU data + aa aa 03 00 00 00 08 00 45 00 00 4e 66 1a 00 00 80 11 be 64 0a 00 01 22 + 0a ff ff ff 00 89 00 89 00 3a 00 00 80 a6 01 10 00 01 00 00 00 00 00 00 + 20 45 43 45 4a 45 48 45 43 46 43 45 50 46 45 45 49 45 46 46 43 43 41 43 + 41 43 41 43 41 43 41 41 41 00 00 20 00 01 + +RC4 encryption is performed as follows: +17 +18 Key fb 02 9e 30 31 32 33 34 +Plaintext + aa aa 03 00 00 00 08 00 45 00 00 4e 66 1a 00 00 80 11 be 64 0a 00 01 + 22 0a ff ff ff 00 89 00 89 00 3a 00 00 80 a6 01 10 00 01 00 00 00 00 + 00 00 20 45 43 45 4a 45 48 45 43 46 43 45 50 46 45 45 49 45 46 46 43 + 43 41 43 41 43 41 43 41 43 41 41 41 00 00 20 00 01 1b d0 b6 04 +Ciphertext + f6 9c 58 06 bd 6c e8 46 26 bc be fb 94 74 65 0a ad 1f 79 09 b0 f6 4d + 5f 58 a5 03 a2 58 b7 ed 22 eb 0e a6 49 30 d3 a0 56 a5 57 42 fc ce 14 + 1d 48 5f 8a a8 36 de a1 8d f4 2c 53 80 80 5a d0 c6 1a 5d 6f 58 f4 10 + 40 b2 4b 7d 1a 69 38 56 ed 0d 43 98 e7 ae e3 bf 0e 2a 2c a8 f7 +The plaintext consists of the MPDU data, followed by a 4-octet CRC-32 +calculated over the MPDU data. +19 The expanded MPDU, after WEP encapsulation, is as follows: +20 +21 IV fb 02 9e 80 +MPDU data + f6 9c 58 06 bd 6c e8 46 26 bc be fb 94 74 65 0a ad 1f 79 09 b0 f6 4d 5f 58 a5 + 03 a2 58 b7 ed 22 eb 0e a6 49 30 d3 a0 56 a5 57 42 fc ce 14 1d 48 5f 8a a8 36 + de a1 8d f4 2c 53 80 80 5a d0 c6 1a 5d 6f 58 f4 10 40 b2 4b 7d 1a 69 38 56 ed + 0d 43 98 e7 ae e3 bf 0e +ICV 2a 2c a8 f7 +*/ +static const u_int8_t test1_key[] = { /* TK (w/o IV) */ + 0x30, 0x31, 0x32, 0x33, 0x34, +}; +static const u_int8_t test1_plaintext[] = { /* Plaintext MPDU */ + 0x08, 0x48, 0xc3, 0x2c, 0x0f, 0xd2, 0xe1, 0x28, /* 802.11 Header */ + 0xa5, 0x7c, 0x50, 0x30, 0xf1, 0x84, 0x44, 0x08, + 0xab, 0xae, 0xa5, 0xb8, 0xfc, 0xba, 0x80, 0x33, + 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, /* Plaintext data */ + 0x45, 0x00, 0x00, 0x4e, 0x66, 0x1a, 0x00, 0x00, + 0x80, 0x11, 0xbe, 0x64, 0x0a, 0x00, 0x01, 0x22, + 0x0a, 0xff, 0xff, 0xff, 0x00, 0x89, 0x00, 0x89, + 0x00, 0x3a, 0x00, 0x00, 0x80, 0xa6, 0x01, 0x10, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x45, 0x43, 0x45, 0x4a, 0x45, 0x48, 0x45, + 0x43, 0x46, 0x43, 0x45, 0x50, 0x46, 0x45, 0x45, + 0x49, 0x45, 0x46, 0x46, 0x43, 0x43, 0x41, 0x43, + 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x41, + 0x41, 0x00, 0x00, 0x20, 0x00, 0x01, +}; +static const u_int8_t test1_encrypted[] = { /* Encrypted MPDU */ + 0x08, 0x48, 0xc3, 0x2c, 0x0f, 0xd2, 0xe1, 0x28, + 0xa5, 0x7c, 0x50, 0x30, 0xf1, 0x84, 0x44, 0x08, + 0xab, 0xae, 0xa5, 0xb8, 0xfc, 0xba, 0x80, 0x33, + 0xfb, 0x02, 0x9e, 0x80, 0xf6, 0x9c, 0x58, 0x06, + 0xbd, 0x6c, 0xe8, 0x46, 0x26, 0xbc, 0xbe, 0xfb, + 0x94, 0x74, 0x65, 0x0a, 0xad, 0x1f, 0x79, 0x09, + 0xb0, 0xf6, 0x4d, 0x5f, 0x58, 0xa5, 0x03, 0xa2, + 0x58, 0xb7, 0xed, 0x22, 0xeb, 0x0e, 0xa6, 0x49, + 0x30, 0xd3, 0xa0, 0x56, 0xa5, 0x57, 0x42, 0xfc, + 0xce, 0x14, 0x1d, 0x48, 0x5f, 0x8a, 0xa8, 0x36, + 0xde, 0xa1, 0x8d, 0xf4, 0x2c, 0x53, 0x80, 0x80, + 0x5a, 0xd0, 0xc6, 0x1a, 0x5d, 0x6f, 0x58, 0xf4, + 0x10, 0x40, 0xb2, 0x4b, 0x7d, 0x1a, 0x69, 0x38, + 0x56, 0xed, 0x0d, 0x43, 0x98, 0xe7, 0xae, 0xe3, + 0xbf, 0x0e, 0x2a, 0x2c, 0xa8, 0xf7, +}; + +/* XXX fix byte order of iv */ +#define TEST(n,name,cipher,keyix,iv0,iv1,iv2,iv3) { \ + name, IEEE80211_CIPHER_##cipher,keyix, { iv2,iv1,iv0,iv3 }, \ + test##n##_key, sizeof(test##n##_key), \ + test##n##_plaintext, sizeof(test##n##_plaintext), \ + test##n##_encrypted, sizeof(test##n##_encrypted) \ +} + +struct ciphertest { + const char *name; + int cipher; + int keyix; + u_int8_t iv[4]; + const u_int8_t *key; + size_t key_len; + const u_int8_t *plaintext; + size_t plaintext_len; + const u_int8_t *encrypted; + size_t encrypted_len; +} weptests[] = { + TEST(1, "WEP test mpdu 1", WEP, 2, 0xfb, 0x02, 0x9e, 0x80), +}; + +static void +dumpdata(const char *tag, const void *p, size_t len) +{ + int i; + + printk("%s: 0x%p len %u", tag, p, len); + for (i = 0; i < len; i++) { + if ((i % 16) == 0) + printk("\n%03d:", i); + printk(" %02x", ((u_int8_t *)p)[i]); + } + printk("\n"); +} + +static void +cmpfail(const void *gen, size_t genlen, const void *ref, size_t reflen) +{ + int i; + + for (i = 0; i < genlen; i++) + if (((u_int8_t *)gen)[i] != ((u_int8_t *)ref)[i]) { + printk("first difference at byte %u\n", i); + break; + } + dumpdata("Generated", gen, genlen); + dumpdata("Reference", ref, reflen); +} + +struct wep_ctx_hw { /* for use with h/w support */ + struct ieee80211com *wc_ic; /* for diagnostics */ + u_int32_t wc_iv; /* initial vector for crypto */ +}; + +int +runtest(struct ieee80211com *ic, struct ciphertest *t) +{ + struct ieee80211_key key; + struct sk_buff *skb = NULL; + const struct ieee80211_cipher *cip; + u_int8_t mac[IEEE80211_ADDR_LEN]; + struct wep_ctx_hw *ctx; + + printk("%s: ", t->name); + + /* + * Setup key. + */ + memset(&key, 0, sizeof(key)); + key.wk_flags = IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV; + key.wk_cipher = &ieee80211_cipher_none; + if (!ieee80211_crypto_newkey(ic, t->cipher, &key)) { + printk("FAIL: ieee80211_crypto_newkey failed\n"); + goto bad; + } + + memcpy(key.wk_key, t->key, t->key_len); + key.wk_keylen = t->key_len; + if (!ieee80211_crypto_setkey(ic, &key, mac)) { + printk("FAIL: ieee80211_crypto_setkey failed\n"); + goto bad; + } + cip = key.wk_cipher; + + /* + * Craft encrypted frame from known data. + */ + skb = dev_alloc_skb(t->encrypted_len); + if (skb == NULL) { + printk("FAIL: unable to allocate skbuff\n"); + goto bad; + } + memcpy(skb_put(skb, t->encrypted_len), t->encrypted, t->encrypted_len); + + /* + * Decrypt frame. + */ + if (!(*cip->ic_decap)(&key, skb)) { + printk("FAIL: wep decap failed\n"); + cmpfail(skb->data, skb->len, + t->plaintext, t->plaintext_len); + goto bad; + } + /* + * Verify: frame length, frame contents. + */ + if (skb->len != t->plaintext_len) { + printk("FAIL: decap botch; length mismatch\n"); + cmpfail(skb->data, skb->len, + t->plaintext, t->plaintext_len); + goto bad; + } else if (memcmp(skb->data, t->plaintext, t->plaintext_len)) { + printk("FAIL: decap botch; data does not compare\n"); + cmpfail(skb->data, skb->len, + t->plaintext, sizeof(t->plaintext)); + goto bad; + } + + /* + * Encrypt frame. + */ + ctx = (struct wep_ctx_hw *) key.wk_private; + memcpy(&ctx->wc_iv, t->iv, sizeof(t->iv)); /* for encap/encrypt */ + if (!(*cip->ic_encap)(&key, skb, t->keyix<<6)) { + printk("FAIL: wep encap failed\n"); + goto bad; + } + /* + * Verify: frame length, frame contents. + */ + if (skb->len != t->encrypted_len) { + printk("FAIL: encap data length mismatch\n"); + cmpfail(skb->data, skb->len, + t->encrypted, t->encrypted_len); + goto bad; + } else if (memcmp(skb->data, t->encrypted, skb->len)) { + printk("FAIL: encrypt data does not compare\n"); + cmpfail(skb->data, skb->len, + t->encrypted, t->encrypted_len); + dumpdata("Plaintext", t->plaintext, t->plaintext_len); + goto bad; + } + dev_kfree_skb(skb); + ieee80211_crypto_delkey(ic, &key); + printk("PASS\n"); + return 1; +bad: + if (skb != NULL) + dev_kfree_skb(skb); + ieee80211_crypto_delkey(ic, &key); + return 0; +} + +/* + * Module glue. + */ + +MODULE_AUTHOR("Errno Consulting, Sam Leffler"); +MODULE_DESCRIPTION("802.11 wireless support: WEP cipher tester"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("Dual BSD/GPL"); +#endif + +static int tests = -1; +MODULE_PARM(tests, "i"); +MODULE_PARM_DESC(tests, "Specify which tests to run"); + +static int debug = 0; +MODULE_PARM(debug, "i"); +MODULE_PARM_DESC(debug, "Enable IEEE80211_MSG_CRYPTO"); + +static int __init +init_crypto_wep_test(void) +{ +#define N(a) (sizeof(a)/sizeof(a[0])) + struct ieee80211com ic; + int i, pass, total; + + memset(&ic, 0, sizeof(ic)); + if (debug) + ic.msg_enable = IEEE80211_MSG_CRYPTO; + ieee80211_crypto_attach(&ic); + pass = 0; + total = 0; + for (i = 0; i < N(weptests); i++) + if (tests & (1<