From: Kalle Valo Date: Tue, 17 Nov 2015 17:49:20 +0000 (+0200) Subject: atmel: move under atmel vendor directory X-Git-Tag: v4.5-rc1~128^2~185^2~60 X-Git-Url: http://git.cascardo.info/?p=cascardo%2Flinux.git;a=commitdiff_plain;h=30fe0f9b8c755d9aab04bb7c98ce9c7835c3bd24 atmel: move under atmel vendor directory Part of reorganising wireless drivers directory and Kconfig. Signed-off-by: Kalle Valo --- diff --git a/MAINTAINERS b/MAINTAINERS index f1adb66094f1..2f3ea01c101c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1992,7 +1992,7 @@ L: linux-wireless@vger.kernel.org W: http://www.thekelleys.org.uk/atmel W: http://atmelwlandriver.sourceforge.net/ S: Maintained -F: drivers/net/wireless/atmel* +F: drivers/net/wireless/atmel/atmel* ATMEL MAXTOUCH DRIVER M: Nick Dyer diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index ce5211ba1b29..275b6ed93f15 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -18,6 +18,7 @@ menuconfig WLAN if WLAN source "drivers/net/wireless/admtek/Kconfig" +source "drivers/net/wireless/atmel/Kconfig" source "drivers/net/wireless/cisco/Kconfig" config PCMCIA_RAYCS @@ -54,49 +55,6 @@ config LIBERTAS_THINFIRM_USB ---help--- A driver for Marvell Libertas 8388 USB devices using thinfirm. -config ATMEL - tristate "Atmel at76c50x chipset 802.11b support" - depends on CFG80211 && (PCI || PCMCIA) - select WIRELESS_EXT - select WEXT_PRIV - 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. - -config PCMCIA_ATMEL - tristate "Atmel at76c502/at76c504 PCMCIA cards" - depends on ATMEL && PCMCIA - select WIRELESS_EXT - select FW_LOADER - select CRC32 - ---help--- - Enable support for PCMCIA cards containing the - Atmel at76c502 and at76c504 chips. - -config AT76C50X_USB - tristate "Atmel at76c503/at76c505/at76c505a USB cards" - depends on MAC80211 && USB - select FW_LOADER - ---help--- - Enable support for USB Wireless devices using Atmel at76c503, - at76c505 or at76c505a chips. - config PCMCIA_WL3501 tristate "Planet WL3501 PCMCIA cards" depends on CFG80211 && PCMCIA diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 853294d84fa9..87eb6b2f3227 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -3,6 +3,7 @@ # obj-$(CONFIG_WLAN_VENDOR_ADMTEK) += admtek/ +obj-$(CONFIG_WLAN_VENDOR_ATMEL) += atmel/ obj-$(CONFIG_WLAN_VENDOR_CISCO) += cisco/ obj-$(CONFIG_IPW2100) += ipw2x00/ @@ -10,12 +11,6 @@ obj-$(CONFIG_IPW2200) += ipw2x00/ obj-$(CONFIG_HERMES) += orinoco/ -obj-$(CONFIG_ATMEL) += atmel.o -obj-$(CONFIG_PCI_ATMEL) += atmel_pci.o -obj-$(CONFIG_PCMCIA_ATMEL) += atmel_cs.o - -obj-$(CONFIG_AT76C50X_USB) += at76c50x-usb.o - obj-$(CONFIG_PRISM54) += prism54/ obj-$(CONFIG_HOSTAP) += hostap/ diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c deleted file mode 100644 index dab25136214a..000000000000 --- a/drivers/net/wireless/at76c50x-usb.c +++ /dev/null @@ -1,2617 +0,0 @@ -/* - * at76c503/at76c505 USB driver - * - * Copyright (c) 2002 - 2003 Oliver Kurth - * Copyright (c) 2004 Joerg Albert - * Copyright (c) 2004 Nick Jones - * Copyright (c) 2004 Balint Seeber - * Copyright (c) 2007 Guido Guenther - * Copyright (c) 2007 Kalle Valo - * Copyright (c) 2010 Sebastian Smolorz - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This file is part of the Berlios driver for WLAN USB devices based on the - * Atmel AT76C503A/505/505A. - * - * Some iw_handler code was taken from airo.c, (C) 1999 Benjamin Reed - * - * TODO list is at the wiki: - * - * http://wireless.kernel.org/en/users/Drivers/at76c50x-usb#TODO - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "at76c50x-usb.h" - -/* Version information */ -#define DRIVER_NAME "at76c50x-usb" -#define DRIVER_VERSION "0.17" -#define DRIVER_DESC "Atmel at76x USB Wireless LAN Driver" - -/* at76_debug bits */ -#define DBG_PROGRESS 0x00000001 /* authentication/accociation */ -#define DBG_BSS_TABLE 0x00000002 /* show BSS table after scans */ -#define DBG_IOCTL 0x00000004 /* ioctl calls / settings */ -#define DBG_MAC_STATE 0x00000008 /* MAC state transitions */ -#define DBG_TX_DATA 0x00000010 /* tx header */ -#define DBG_TX_DATA_CONTENT 0x00000020 /* tx content */ -#define DBG_TX_MGMT 0x00000040 /* tx management */ -#define DBG_RX_DATA 0x00000080 /* rx data header */ -#define DBG_RX_DATA_CONTENT 0x00000100 /* rx data content */ -#define DBG_RX_MGMT 0x00000200 /* rx mgmt frame headers */ -#define DBG_RX_BEACON 0x00000400 /* rx beacon */ -#define DBG_RX_CTRL 0x00000800 /* rx control */ -#define DBG_RX_MGMT_CONTENT 0x00001000 /* rx mgmt content */ -#define DBG_RX_FRAGS 0x00002000 /* rx data fragment handling */ -#define DBG_DEVSTART 0x00004000 /* fw download, device start */ -#define DBG_URB 0x00008000 /* rx urb status, ... */ -#define DBG_RX_ATMEL_HDR 0x00010000 /* Atmel-specific Rx headers */ -#define DBG_PROC_ENTRY 0x00020000 /* procedure entries/exits */ -#define DBG_PM 0x00040000 /* power management settings */ -#define DBG_BSS_MATCH 0x00080000 /* BSS match failures */ -#define DBG_PARAMS 0x00100000 /* show configured parameters */ -#define DBG_WAIT_COMPLETE 0x00200000 /* command completion */ -#define DBG_RX_FRAGS_SKB 0x00400000 /* skb header of Rx fragments */ -#define DBG_BSS_TABLE_RM 0x00800000 /* purging bss table entries */ -#define DBG_MONITOR_MODE 0x01000000 /* monitor mode */ -#define DBG_MIB 0x02000000 /* dump all MIBs on startup */ -#define DBG_MGMT_TIMER 0x04000000 /* dump mgmt_timer ops */ -#define DBG_WE_EVENTS 0x08000000 /* dump wireless events */ -#define DBG_FW 0x10000000 /* firmware download */ -#define DBG_DFU 0x20000000 /* device firmware upgrade */ -#define DBG_CMD 0x40000000 -#define DBG_MAC80211 0x80000000 - -#define DBG_DEFAULTS 0 - -/* Use our own dbg macro */ -#define at76_dbg(bits, format, arg...) \ -do { \ - if (at76_debug & (bits)) \ - printk(KERN_DEBUG DRIVER_NAME ": " format "\n", ##arg); \ -} while (0) - -#define at76_dbg_dump(bits, buf, len, format, arg...) \ -do { \ - if (at76_debug & (bits)) { \ - printk(KERN_DEBUG DRIVER_NAME ": " format "\n", ##arg); \ - print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, buf, len); \ - } \ -} while (0) - -static uint at76_debug = DBG_DEFAULTS; - -/* Protect against concurrent firmware loading and parsing */ -static struct mutex fw_mutex; - -static struct fwentry firmwares[] = { - [0] = { "" }, - [BOARD_503_ISL3861] = { "atmel_at76c503-i3861.bin" }, - [BOARD_503_ISL3863] = { "atmel_at76c503-i3863.bin" }, - [BOARD_503] = { "atmel_at76c503-rfmd.bin" }, - [BOARD_503_ACC] = { "atmel_at76c503-rfmd-acc.bin" }, - [BOARD_505] = { "atmel_at76c505-rfmd.bin" }, - [BOARD_505_2958] = { "atmel_at76c505-rfmd2958.bin" }, - [BOARD_505A] = { "atmel_at76c505a-rfmd2958.bin" }, - [BOARD_505AMX] = { "atmel_at76c505amx-rfmd.bin" }, -}; -MODULE_FIRMWARE("atmel_at76c503-i3861.bin"); -MODULE_FIRMWARE("atmel_at76c503-i3863.bin"); -MODULE_FIRMWARE("atmel_at76c503-rfmd.bin"); -MODULE_FIRMWARE("atmel_at76c503-rfmd-acc.bin"); -MODULE_FIRMWARE("atmel_at76c505-rfmd.bin"); -MODULE_FIRMWARE("atmel_at76c505-rfmd2958.bin"); -MODULE_FIRMWARE("atmel_at76c505a-rfmd2958.bin"); -MODULE_FIRMWARE("atmel_at76c505amx-rfmd.bin"); - -#define USB_DEVICE_DATA(__ops) .driver_info = (kernel_ulong_t)(__ops) - -static struct usb_device_id dev_table[] = { - /* - * at76c503-i3861 - */ - /* Generic AT76C503/3861 device */ - { USB_DEVICE(0x03eb, 0x7603), USB_DEVICE_DATA(BOARD_503_ISL3861) }, - /* Linksys WUSB11 v2.1/v2.6 */ - { USB_DEVICE(0x066b, 0x2211), USB_DEVICE_DATA(BOARD_503_ISL3861) }, - /* Netgear MA101 rev. A */ - { USB_DEVICE(0x0864, 0x4100), USB_DEVICE_DATA(BOARD_503_ISL3861) }, - /* Tekram U300C / Allnet ALL0193 */ - { USB_DEVICE(0x0b3b, 0x1612), USB_DEVICE_DATA(BOARD_503_ISL3861) }, - /* HP HN210W J7801A */ - { USB_DEVICE(0x03f0, 0x011c), USB_DEVICE_DATA(BOARD_503_ISL3861) }, - /* Sitecom/Z-Com/Zyxel M4Y-750 */ - { USB_DEVICE(0x0cde, 0x0001), USB_DEVICE_DATA(BOARD_503_ISL3861) }, - /* Dynalink/Askey WLL013 (intersil) */ - { USB_DEVICE(0x069a, 0x0320), USB_DEVICE_DATA(BOARD_503_ISL3861) }, - /* EZ connect 11Mpbs Wireless USB Adapter SMC2662W v1 */ - { USB_DEVICE(0x0d5c, 0xa001), USB_DEVICE_DATA(BOARD_503_ISL3861) }, - /* BenQ AWL300 */ - { USB_DEVICE(0x04a5, 0x9000), USB_DEVICE_DATA(BOARD_503_ISL3861) }, - /* Addtron AWU-120, Compex WLU11 */ - { USB_DEVICE(0x05dd, 0xff31), USB_DEVICE_DATA(BOARD_503_ISL3861) }, - /* Intel AP310 AnyPoint II USB */ - { USB_DEVICE(0x8086, 0x0200), USB_DEVICE_DATA(BOARD_503_ISL3861) }, - /* Dynalink L11U */ - { USB_DEVICE(0x0d8e, 0x7100), USB_DEVICE_DATA(BOARD_503_ISL3861) }, - /* Arescom WL-210, FCC id 07J-GL2411USB */ - { USB_DEVICE(0x0d8e, 0x7110), USB_DEVICE_DATA(BOARD_503_ISL3861) }, - /* I-O DATA WN-B11/USB */ - { USB_DEVICE(0x04bb, 0x0919), USB_DEVICE_DATA(BOARD_503_ISL3861) }, - /* BT Voyager 1010 */ - { USB_DEVICE(0x069a, 0x0821), USB_DEVICE_DATA(BOARD_503_ISL3861) }, - /* - * at76c503-i3863 - */ - /* Generic AT76C503/3863 device */ - { USB_DEVICE(0x03eb, 0x7604), USB_DEVICE_DATA(BOARD_503_ISL3863) }, - /* Samsung SWL-2100U */ - { USB_DEVICE(0x055d, 0xa000), USB_DEVICE_DATA(BOARD_503_ISL3863) }, - /* - * at76c503-rfmd - */ - /* Generic AT76C503/RFMD device */ - { USB_DEVICE(0x03eb, 0x7605), USB_DEVICE_DATA(BOARD_503) }, - /* Dynalink/Askey WLL013 (rfmd) */ - { USB_DEVICE(0x069a, 0x0321), USB_DEVICE_DATA(BOARD_503) }, - /* Linksys WUSB11 v2.6 */ - { USB_DEVICE(0x077b, 0x2219), USB_DEVICE_DATA(BOARD_503) }, - /* Network Everywhere NWU11B */ - { USB_DEVICE(0x077b, 0x2227), USB_DEVICE_DATA(BOARD_503) }, - /* Netgear MA101 rev. B */ - { USB_DEVICE(0x0864, 0x4102), USB_DEVICE_DATA(BOARD_503) }, - /* D-Link DWL-120 rev. E */ - { USB_DEVICE(0x2001, 0x3200), USB_DEVICE_DATA(BOARD_503) }, - /* Actiontec 802UAT1, HWU01150-01UK */ - { USB_DEVICE(0x1668, 0x7605), USB_DEVICE_DATA(BOARD_503) }, - /* AirVast W-Buddie WN210 */ - { USB_DEVICE(0x03eb, 0x4102), USB_DEVICE_DATA(BOARD_503) }, - /* Dick Smith Electronics XH1153 802.11b USB adapter */ - { USB_DEVICE(0x1371, 0x5743), USB_DEVICE_DATA(BOARD_503) }, - /* CNet CNUSB611 */ - { USB_DEVICE(0x1371, 0x0001), USB_DEVICE_DATA(BOARD_503) }, - /* FiberLine FL-WL200U */ - { USB_DEVICE(0x1371, 0x0002), USB_DEVICE_DATA(BOARD_503) }, - /* BenQ AWL400 USB stick */ - { USB_DEVICE(0x04a5, 0x9001), USB_DEVICE_DATA(BOARD_503) }, - /* 3Com 3CRSHEW696 */ - { USB_DEVICE(0x0506, 0x0a01), USB_DEVICE_DATA(BOARD_503) }, - /* Siemens Santis ADSL WLAN USB adapter WLL 013 */ - { USB_DEVICE(0x0681, 0x001b), USB_DEVICE_DATA(BOARD_503) }, - /* Belkin F5D6050, version 2 */ - { USB_DEVICE(0x050d, 0x0050), USB_DEVICE_DATA(BOARD_503) }, - /* iBlitzz, BWU613 (not *B or *SB) */ - { USB_DEVICE(0x07b8, 0xb000), USB_DEVICE_DATA(BOARD_503) }, - /* Gigabyte GN-WLBM101 */ - { USB_DEVICE(0x1044, 0x8003), USB_DEVICE_DATA(BOARD_503) }, - /* Planex GW-US11S */ - { USB_DEVICE(0x2019, 0x3220), USB_DEVICE_DATA(BOARD_503) }, - /* Internal WLAN adapter in h5[4,5]xx series iPAQs */ - { USB_DEVICE(0x049f, 0x0032), USB_DEVICE_DATA(BOARD_503) }, - /* Corega Wireless LAN USB-11 mini */ - { USB_DEVICE(0x07aa, 0x0011), USB_DEVICE_DATA(BOARD_503) }, - /* Corega Wireless LAN USB-11 mini2 */ - { USB_DEVICE(0x07aa, 0x0018), USB_DEVICE_DATA(BOARD_503) }, - /* Uniden PCW100 */ - { USB_DEVICE(0x05dd, 0xff35), USB_DEVICE_DATA(BOARD_503) }, - /* - * at76c503-rfmd-acc - */ - /* SMC2664W */ - { USB_DEVICE(0x083a, 0x3501), USB_DEVICE_DATA(BOARD_503_ACC) }, - /* Belkin F5D6050, SMC2662W v2, SMC2662W-AR */ - { USB_DEVICE(0x0d5c, 0xa002), USB_DEVICE_DATA(BOARD_503_ACC) }, - /* - * at76c505-rfmd - */ - /* Generic AT76C505/RFMD */ - { USB_DEVICE(0x03eb, 0x7606), USB_DEVICE_DATA(BOARD_505) }, - /* - * at76c505-rfmd2958 - */ - /* Generic AT76C505/RFMD, OvisLink WL-1130USB */ - { USB_DEVICE(0x03eb, 0x7613), USB_DEVICE_DATA(BOARD_505_2958) }, - /* Fiberline FL-WL240U */ - { USB_DEVICE(0x1371, 0x0014), USB_DEVICE_DATA(BOARD_505_2958) }, - /* CNet CNUSB-611G */ - { USB_DEVICE(0x1371, 0x0013), USB_DEVICE_DATA(BOARD_505_2958) }, - /* Linksys WUSB11 v2.8 */ - { USB_DEVICE(0x1915, 0x2233), USB_DEVICE_DATA(BOARD_505_2958) }, - /* Xterasys XN-2122B, IBlitzz BWU613B/BWU613SB */ - { USB_DEVICE(0x12fd, 0x1001), USB_DEVICE_DATA(BOARD_505_2958) }, - /* Corega WLAN USB Stick 11 */ - { USB_DEVICE(0x07aa, 0x7613), USB_DEVICE_DATA(BOARD_505_2958) }, - /* Microstar MSI Box MS6978 */ - { USB_DEVICE(0x0db0, 0x1020), USB_DEVICE_DATA(BOARD_505_2958) }, - /* - * at76c505a-rfmd2958 - */ - /* Generic AT76C505A device */ - { USB_DEVICE(0x03eb, 0x7614), USB_DEVICE_DATA(BOARD_505A) }, - /* Generic AT76C505AS device */ - { USB_DEVICE(0x03eb, 0x7617), USB_DEVICE_DATA(BOARD_505A) }, - /* Siemens Gigaset USB WLAN Adapter 11 */ - { USB_DEVICE(0x1690, 0x0701), USB_DEVICE_DATA(BOARD_505A) }, - /* OQO Model 01+ Internal Wi-Fi */ - { USB_DEVICE(0x1557, 0x0002), USB_DEVICE_DATA(BOARD_505A) }, - /* - * at76c505amx-rfmd - */ - /* Generic AT76C505AMX device */ - { USB_DEVICE(0x03eb, 0x7615), USB_DEVICE_DATA(BOARD_505AMX) }, - { } -}; - -MODULE_DEVICE_TABLE(usb, dev_table); - -/* Supported rates of this hardware, bit 7 marks basic rates */ -static const u8 hw_rates[] = { 0x82, 0x84, 0x0b, 0x16 }; - -static const char *const preambles[] = { "long", "short", "auto" }; - -/* Firmware download */ -/* DFU states */ -#define STATE_IDLE 0x00 -#define STATE_DETACH 0x01 -#define STATE_DFU_IDLE 0x02 -#define STATE_DFU_DOWNLOAD_SYNC 0x03 -#define STATE_DFU_DOWNLOAD_BUSY 0x04 -#define STATE_DFU_DOWNLOAD_IDLE 0x05 -#define STATE_DFU_MANIFEST_SYNC 0x06 -#define STATE_DFU_MANIFEST 0x07 -#define STATE_DFU_MANIFEST_WAIT_RESET 0x08 -#define STATE_DFU_UPLOAD_IDLE 0x09 -#define STATE_DFU_ERROR 0x0a - -/* DFU commands */ -#define DFU_DETACH 0 -#define DFU_DNLOAD 1 -#define DFU_UPLOAD 2 -#define DFU_GETSTATUS 3 -#define DFU_CLRSTATUS 4 -#define DFU_GETSTATE 5 -#define DFU_ABORT 6 - -#define FW_BLOCK_SIZE 1024 - -struct dfu_status { - unsigned char status; - unsigned char poll_timeout[3]; - unsigned char state; - unsigned char string; -} __packed; - -static inline int at76_is_intersil(enum board_type board) -{ - return (board == BOARD_503_ISL3861 || board == BOARD_503_ISL3863); -} - -static inline int at76_is_503rfmd(enum board_type board) -{ - return (board == BOARD_503 || board == BOARD_503_ACC); -} - -static inline int at76_is_505a(enum board_type board) -{ - return (board == BOARD_505A || board == BOARD_505AMX); -} - -/* Load a block of the first (internal) part of the firmware */ -static int at76_load_int_fw_block(struct usb_device *udev, int blockno, - void *block, int size) -{ - return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), DFU_DNLOAD, - USB_TYPE_CLASS | USB_DIR_OUT | - USB_RECIP_INTERFACE, blockno, 0, block, size, - USB_CTRL_GET_TIMEOUT); -} - -static int at76_dfu_get_status(struct usb_device *udev, - struct dfu_status *status) -{ - int ret; - - ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), DFU_GETSTATUS, - USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE, - 0, 0, status, sizeof(struct dfu_status), - USB_CTRL_GET_TIMEOUT); - return ret; -} - -static int at76_dfu_get_state(struct usb_device *udev, u8 *state) -{ - int ret; - - ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), DFU_GETSTATE, - USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE, - 0, 0, state, 1, USB_CTRL_GET_TIMEOUT); - return ret; -} - -/* Convert timeout from the DFU status to jiffies */ -static inline unsigned long at76_get_timeout(struct dfu_status *s) -{ - return msecs_to_jiffies((s->poll_timeout[2] << 16) - | (s->poll_timeout[1] << 8) - | (s->poll_timeout[0])); -} - -/* Load internal firmware from the buffer. If manifest_sync_timeout > 0, use - * its value in jiffies in the MANIFEST_SYNC state. */ -static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size, - int manifest_sync_timeout) -{ - int ret = 0; - int need_dfu_state = 1; - int is_done = 0; - u32 dfu_timeout = 0; - int bsize = 0; - int blockno = 0; - struct dfu_status *dfu_stat_buf = NULL; - u8 *dfu_state = NULL; - u8 *block = NULL; - - at76_dbg(DBG_DFU, "%s( %p, %u, %d)", __func__, buf, size, - manifest_sync_timeout); - - if (!size) { - dev_err(&udev->dev, "FW buffer length invalid!\n"); - return -EINVAL; - } - - dfu_stat_buf = kmalloc(sizeof(struct dfu_status), GFP_KERNEL); - if (!dfu_stat_buf) { - ret = -ENOMEM; - goto exit; - } - - block = kmalloc(FW_BLOCK_SIZE, GFP_KERNEL); - if (!block) { - ret = -ENOMEM; - goto exit; - } - - dfu_state = kmalloc(sizeof(u8), GFP_KERNEL); - if (!dfu_state) { - ret = -ENOMEM; - goto exit; - } - *dfu_state = 0; - - do { - if (need_dfu_state) { - ret = at76_dfu_get_state(udev, dfu_state); - if (ret < 0) { - dev_err(&udev->dev, - "cannot get DFU state: %d\n", ret); - goto exit; - } - need_dfu_state = 0; - } - - switch (*dfu_state) { - case STATE_DFU_DOWNLOAD_SYNC: - at76_dbg(DBG_DFU, "STATE_DFU_DOWNLOAD_SYNC"); - ret = at76_dfu_get_status(udev, dfu_stat_buf); - if (ret >= 0) { - *dfu_state = dfu_stat_buf->state; - dfu_timeout = at76_get_timeout(dfu_stat_buf); - need_dfu_state = 0; - } else - dev_err(&udev->dev, - "at76_dfu_get_status returned %d\n", - ret); - break; - - case STATE_DFU_DOWNLOAD_BUSY: - at76_dbg(DBG_DFU, "STATE_DFU_DOWNLOAD_BUSY"); - need_dfu_state = 1; - - at76_dbg(DBG_DFU, "DFU: Resetting device"); - schedule_timeout_interruptible(dfu_timeout); - break; - - case STATE_DFU_DOWNLOAD_IDLE: - at76_dbg(DBG_DFU, "DOWNLOAD..."); - /* fall through */ - case STATE_DFU_IDLE: - at76_dbg(DBG_DFU, "DFU IDLE"); - - bsize = min_t(int, size, FW_BLOCK_SIZE); - memcpy(block, buf, bsize); - at76_dbg(DBG_DFU, "int fw, size left = %5d, " - "bsize = %4d, blockno = %2d", size, bsize, - blockno); - ret = - at76_load_int_fw_block(udev, blockno, block, bsize); - buf += bsize; - size -= bsize; - blockno++; - - if (ret != bsize) - dev_err(&udev->dev, - "at76_load_int_fw_block returned %d\n", - ret); - need_dfu_state = 1; - break; - - case STATE_DFU_MANIFEST_SYNC: - at76_dbg(DBG_DFU, "STATE_DFU_MANIFEST_SYNC"); - - ret = at76_dfu_get_status(udev, dfu_stat_buf); - if (ret < 0) - break; - - *dfu_state = dfu_stat_buf->state; - dfu_timeout = at76_get_timeout(dfu_stat_buf); - need_dfu_state = 0; - - /* override the timeout from the status response, - needed for AT76C505A */ - if (manifest_sync_timeout > 0) - dfu_timeout = manifest_sync_timeout; - - at76_dbg(DBG_DFU, "DFU: Waiting for manifest phase"); - schedule_timeout_interruptible(dfu_timeout); - break; - - case STATE_DFU_MANIFEST: - at76_dbg(DBG_DFU, "STATE_DFU_MANIFEST"); - is_done = 1; - break; - - case STATE_DFU_MANIFEST_WAIT_RESET: - at76_dbg(DBG_DFU, "STATE_DFU_MANIFEST_WAIT_RESET"); - is_done = 1; - break; - - case STATE_DFU_UPLOAD_IDLE: - at76_dbg(DBG_DFU, "STATE_DFU_UPLOAD_IDLE"); - break; - - case STATE_DFU_ERROR: - at76_dbg(DBG_DFU, "STATE_DFU_ERROR"); - ret = -EPIPE; - break; - - default: - at76_dbg(DBG_DFU, "DFU UNKNOWN STATE (%d)", *dfu_state); - ret = -EINVAL; - break; - } - } while (!is_done && (ret >= 0)); - -exit: - kfree(dfu_state); - kfree(block); - kfree(dfu_stat_buf); - - if (ret >= 0) - ret = 0; - - return ret; -} - -/* LED trigger */ -static int tx_activity; -static void at76_ledtrig_tx_timerfunc(unsigned long data); -static DEFINE_TIMER(ledtrig_tx_timer, at76_ledtrig_tx_timerfunc, 0, 0); -DEFINE_LED_TRIGGER(ledtrig_tx); - -static void at76_ledtrig_tx_timerfunc(unsigned long data) -{ - static int tx_lastactivity; - - if (tx_lastactivity != tx_activity) { - tx_lastactivity = tx_activity; - led_trigger_event(ledtrig_tx, LED_FULL); - mod_timer(&ledtrig_tx_timer, jiffies + HZ / 4); - } else - led_trigger_event(ledtrig_tx, LED_OFF); -} - -static void at76_ledtrig_tx_activity(void) -{ - tx_activity++; - if (!timer_pending(&ledtrig_tx_timer)) - mod_timer(&ledtrig_tx_timer, jiffies + HZ / 4); -} - -static int at76_remap(struct usb_device *udev) -{ - int ret; - ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x0a, - USB_TYPE_VENDOR | USB_DIR_OUT | - USB_RECIP_INTERFACE, 0, 0, NULL, 0, - USB_CTRL_GET_TIMEOUT); - if (ret < 0) - return ret; - return 0; -} - -static int at76_get_op_mode(struct usb_device *udev) -{ - int ret; - u8 saved; - u8 *op_mode; - - op_mode = kmalloc(1, GFP_NOIO); - if (!op_mode) - return -ENOMEM; - ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x33, - USB_TYPE_VENDOR | USB_DIR_IN | - USB_RECIP_INTERFACE, 0x01, 0, op_mode, 1, - USB_CTRL_GET_TIMEOUT); - saved = *op_mode; - kfree(op_mode); - - if (ret < 0) - return ret; - else if (ret < 1) - return -EIO; - else - return saved; -} - -/* Load a block of the second ("external") part of the firmware */ -static inline int at76_load_ext_fw_block(struct usb_device *udev, int blockno, - void *block, int size) -{ - return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x0e, - USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, - 0x0802, blockno, block, size, - USB_CTRL_GET_TIMEOUT); -} - -static inline int at76_get_hw_cfg(struct usb_device *udev, - union at76_hwcfg *buf, int buf_size) -{ - return usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x33, - USB_TYPE_VENDOR | USB_DIR_IN | - USB_RECIP_INTERFACE, 0x0a02, 0, - buf, buf_size, USB_CTRL_GET_TIMEOUT); -} - -/* Intersil boards use a different "value" for GetHWConfig requests */ -static inline int at76_get_hw_cfg_intersil(struct usb_device *udev, - union at76_hwcfg *buf, int buf_size) -{ - return usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x33, - USB_TYPE_VENDOR | USB_DIR_IN | - USB_RECIP_INTERFACE, 0x0902, 0, - buf, buf_size, USB_CTRL_GET_TIMEOUT); -} - -/* Get the hardware configuration for the adapter and put it to the appropriate - * fields of 'priv' (the GetHWConfig request and interpretation of the result - * depends on the board type) */ -static int at76_get_hw_config(struct at76_priv *priv) -{ - int ret; - union at76_hwcfg *hwcfg = kmalloc(sizeof(*hwcfg), GFP_KERNEL); - - if (!hwcfg) - return -ENOMEM; - - if (at76_is_intersil(priv->board_type)) { - ret = at76_get_hw_cfg_intersil(priv->udev, hwcfg, - sizeof(hwcfg->i)); - if (ret < 0) - goto exit; - memcpy(priv->mac_addr, hwcfg->i.mac_addr, ETH_ALEN); - priv->regulatory_domain = hwcfg->i.regulatory_domain; - } else if (at76_is_503rfmd(priv->board_type)) { - ret = at76_get_hw_cfg(priv->udev, hwcfg, sizeof(hwcfg->r3)); - if (ret < 0) - goto exit; - memcpy(priv->mac_addr, hwcfg->r3.mac_addr, ETH_ALEN); - priv->regulatory_domain = hwcfg->r3.regulatory_domain; - } else { - ret = at76_get_hw_cfg(priv->udev, hwcfg, sizeof(hwcfg->r5)); - if (ret < 0) - goto exit; - memcpy(priv->mac_addr, hwcfg->r5.mac_addr, ETH_ALEN); - priv->regulatory_domain = hwcfg->r5.regulatory_domain; - } - -exit: - kfree(hwcfg); - if (ret < 0) - wiphy_err(priv->hw->wiphy, "cannot get HW Config (error %d)\n", - ret); - - return ret; -} - -static struct reg_domain const *at76_get_reg_domain(u16 code) -{ - int i; - static struct reg_domain const fd_tab[] = { - { 0x10, "FCC (USA)", 0x7ff }, /* ch 1-11 */ - { 0x20, "IC (Canada)", 0x7ff }, /* ch 1-11 */ - { 0x30, "ETSI (most of Europe)", 0x1fff }, /* ch 1-13 */ - { 0x31, "Spain", 0x600 }, /* ch 10-11 */ - { 0x32, "France", 0x1e00 }, /* ch 10-13 */ - { 0x40, "MKK (Japan)", 0x2000 }, /* ch 14 */ - { 0x41, "MKK1 (Japan)", 0x3fff }, /* ch 1-14 */ - { 0x50, "Israel", 0x3fc }, /* ch 3-9 */ - { 0x00, "", 0xffffffff } /* ch 1-32 */ - }; - - /* Last entry is fallback for unknown domain code */ - for (i = 0; i < ARRAY_SIZE(fd_tab) - 1; i++) - if (code == fd_tab[i].code) - break; - - return &fd_tab[i]; -} - -static inline int at76_get_mib(struct usb_device *udev, u16 mib, void *buf, - int buf_size) -{ - int ret; - - ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x33, - USB_TYPE_VENDOR | USB_DIR_IN | - USB_RECIP_INTERFACE, mib << 8, 0, buf, buf_size, - USB_CTRL_GET_TIMEOUT); - if (ret >= 0 && ret != buf_size) - return -EIO; - return ret; -} - -/* Return positive number for status, negative for an error */ -static inline int at76_get_cmd_status(struct usb_device *udev, u8 cmd) -{ - u8 *stat_buf; - int ret; - - stat_buf = kmalloc(40, GFP_NOIO); - if (!stat_buf) - return -ENOMEM; - - ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x22, - USB_TYPE_VENDOR | USB_DIR_IN | - USB_RECIP_INTERFACE, cmd, 0, stat_buf, - 40, USB_CTRL_GET_TIMEOUT); - if (ret >= 0) - ret = stat_buf[5]; - kfree(stat_buf); - - return ret; -} - -#define MAKE_CMD_CASE(c) case (c): return #c -static const char *at76_get_cmd_string(u8 cmd_status) -{ - switch (cmd_status) { - MAKE_CMD_CASE(CMD_SET_MIB); - MAKE_CMD_CASE(CMD_GET_MIB); - MAKE_CMD_CASE(CMD_SCAN); - MAKE_CMD_CASE(CMD_JOIN); - MAKE_CMD_CASE(CMD_START_IBSS); - MAKE_CMD_CASE(CMD_RADIO_ON); - MAKE_CMD_CASE(CMD_RADIO_OFF); - MAKE_CMD_CASE(CMD_STARTUP); - } - - return "UNKNOWN"; -} - -static int at76_set_card_command(struct usb_device *udev, u8 cmd, void *buf, - int buf_size) -{ - int ret; - struct at76_command *cmd_buf = kmalloc(sizeof(struct at76_command) + - buf_size, GFP_KERNEL); - - if (!cmd_buf) - return -ENOMEM; - - cmd_buf->cmd = cmd; - cmd_buf->reserved = 0; - cmd_buf->size = cpu_to_le16(buf_size); - memcpy(cmd_buf->data, buf, buf_size); - - at76_dbg_dump(DBG_CMD, cmd_buf, sizeof(struct at76_command) + buf_size, - "issuing command %s (0x%02x)", - at76_get_cmd_string(cmd), cmd); - - ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x0e, - USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, - 0, 0, cmd_buf, - sizeof(struct at76_command) + buf_size, - USB_CTRL_GET_TIMEOUT); - kfree(cmd_buf); - return ret; -} - -#define MAKE_CMD_STATUS_CASE(c) case (c): return #c -static const char *at76_get_cmd_status_string(u8 cmd_status) -{ - switch (cmd_status) { - MAKE_CMD_STATUS_CASE(CMD_STATUS_IDLE); - MAKE_CMD_STATUS_CASE(CMD_STATUS_COMPLETE); - MAKE_CMD_STATUS_CASE(CMD_STATUS_UNKNOWN); - MAKE_CMD_STATUS_CASE(CMD_STATUS_INVALID_PARAMETER); - MAKE_CMD_STATUS_CASE(CMD_STATUS_FUNCTION_NOT_SUPPORTED); - MAKE_CMD_STATUS_CASE(CMD_STATUS_TIME_OUT); - MAKE_CMD_STATUS_CASE(CMD_STATUS_IN_PROGRESS); - MAKE_CMD_STATUS_CASE(CMD_STATUS_HOST_FAILURE); - MAKE_CMD_STATUS_CASE(CMD_STATUS_SCAN_FAILED); - } - - return "UNKNOWN"; -} - -/* Wait until the command is completed */ -static int at76_wait_completion(struct at76_priv *priv, int cmd) -{ - int status = 0; - unsigned long timeout = jiffies + CMD_COMPLETION_TIMEOUT; - - do { - status = at76_get_cmd_status(priv->udev, cmd); - if (status < 0) { - wiphy_err(priv->hw->wiphy, - "at76_get_cmd_status failed: %d\n", - status); - break; - } - - at76_dbg(DBG_WAIT_COMPLETE, - "%s: Waiting on cmd %d, status = %d (%s)", - wiphy_name(priv->hw->wiphy), cmd, status, - at76_get_cmd_status_string(status)); - - if (status != CMD_STATUS_IN_PROGRESS - && status != CMD_STATUS_IDLE) - break; - - schedule_timeout_interruptible(HZ / 10); /* 100 ms */ - if (time_after(jiffies, timeout)) { - wiphy_err(priv->hw->wiphy, - "completion timeout for command %d\n", cmd); - status = -ETIMEDOUT; - break; - } - } while (1); - - return status; -} - -static int at76_set_mib(struct at76_priv *priv, struct set_mib_buffer *buf) -{ - int ret; - - ret = at76_set_card_command(priv->udev, CMD_SET_MIB, buf, - offsetof(struct set_mib_buffer, - data) + buf->size); - if (ret < 0) - return ret; - - ret = at76_wait_completion(priv, CMD_SET_MIB); - if (ret != CMD_STATUS_COMPLETE) { - wiphy_info(priv->hw->wiphy, - "set_mib: at76_wait_completion failed with %d\n", - ret); - ret = -EIO; - } - - return ret; -} - -/* Return < 0 on error, == 0 if no command sent, == 1 if cmd sent */ -static int at76_set_radio(struct at76_priv *priv, int enable) -{ - int ret; - int cmd; - - if (priv->radio_on == enable) - return 0; - - cmd = enable ? CMD_RADIO_ON : CMD_RADIO_OFF; - - ret = at76_set_card_command(priv->udev, cmd, NULL, 0); - if (ret < 0) - wiphy_err(priv->hw->wiphy, - "at76_set_card_command(%d) failed: %d\n", cmd, ret); - else - ret = 1; - - priv->radio_on = enable; - return ret; -} - -/* Set current power save mode (AT76_PM_OFF/AT76_PM_ON/AT76_PM_SMART) */ -static int at76_set_pm_mode(struct at76_priv *priv) -{ - int ret = 0; - - priv->mib_buf.type = MIB_MAC_MGMT; - priv->mib_buf.size = 1; - priv->mib_buf.index = offsetof(struct mib_mac_mgmt, power_mgmt_mode); - priv->mib_buf.data.byte = priv->pm_mode; - - ret = at76_set_mib(priv, &priv->mib_buf); - if (ret < 0) - wiphy_err(priv->hw->wiphy, "set_mib (pm_mode) failed: %d\n", - ret); - - return ret; -} - -static int at76_set_preamble(struct at76_priv *priv, u8 type) -{ - int ret = 0; - - priv->mib_buf.type = MIB_LOCAL; - priv->mib_buf.size = 1; - priv->mib_buf.index = offsetof(struct mib_local, preamble_type); - priv->mib_buf.data.byte = type; - - ret = at76_set_mib(priv, &priv->mib_buf); - if (ret < 0) - wiphy_err(priv->hw->wiphy, "set_mib (preamble) failed: %d\n", - ret); - - return ret; -} - -static int at76_set_frag(struct at76_priv *priv, u16 size) -{ - int ret = 0; - - priv->mib_buf.type = MIB_MAC; - priv->mib_buf.size = 2; - priv->mib_buf.index = offsetof(struct mib_mac, frag_threshold); - priv->mib_buf.data.word = cpu_to_le16(size); - - ret = at76_set_mib(priv, &priv->mib_buf); - if (ret < 0) - wiphy_err(priv->hw->wiphy, - "set_mib (frag threshold) failed: %d\n", ret); - - return ret; -} - -static int at76_set_rts(struct at76_priv *priv, u16 size) -{ - int ret = 0; - - priv->mib_buf.type = MIB_MAC; - priv->mib_buf.size = 2; - priv->mib_buf.index = offsetof(struct mib_mac, rts_threshold); - priv->mib_buf.data.word = cpu_to_le16(size); - - ret = at76_set_mib(priv, &priv->mib_buf); - if (ret < 0) - wiphy_err(priv->hw->wiphy, "set_mib (rts) failed: %d\n", ret); - - return ret; -} - -static int at76_set_autorate_fallback(struct at76_priv *priv, int onoff) -{ - int ret = 0; - - priv->mib_buf.type = MIB_LOCAL; - priv->mib_buf.size = 1; - priv->mib_buf.index = offsetof(struct mib_local, txautorate_fallback); - priv->mib_buf.data.byte = onoff; - - ret = at76_set_mib(priv, &priv->mib_buf); - if (ret < 0) - wiphy_err(priv->hw->wiphy, - "set_mib (autorate fallback) failed: %d\n", ret); - - return ret; -} - -static void at76_dump_mib_mac_addr(struct at76_priv *priv) -{ - int i; - int ret; - struct mib_mac_addr *m = kmalloc(sizeof(struct mib_mac_addr), - GFP_KERNEL); - - if (!m) - return; - - ret = at76_get_mib(priv->udev, MIB_MAC_ADDR, m, - sizeof(struct mib_mac_addr)); - if (ret < 0) { - wiphy_err(priv->hw->wiphy, - "at76_get_mib (MAC_ADDR) failed: %d\n", ret); - goto exit; - } - - at76_dbg(DBG_MIB, "%s: MIB MAC_ADDR: mac_addr %pM res 0x%x 0x%x", - wiphy_name(priv->hw->wiphy), - m->mac_addr, m->res[0], m->res[1]); - for (i = 0; i < ARRAY_SIZE(m->group_addr); i++) - at76_dbg(DBG_MIB, "%s: MIB MAC_ADDR: group addr %d: %pM, " - "status %d", wiphy_name(priv->hw->wiphy), i, - m->group_addr[i], m->group_addr_status[i]); -exit: - kfree(m); -} - -static void at76_dump_mib_mac_wep(struct at76_priv *priv) -{ - int i; - int ret; - int key_len; - struct mib_mac_wep *m = kmalloc(sizeof(struct mib_mac_wep), GFP_KERNEL); - - if (!m) - return; - - ret = at76_get_mib(priv->udev, MIB_MAC_WEP, m, - sizeof(struct mib_mac_wep)); - if (ret < 0) { - wiphy_err(priv->hw->wiphy, - "at76_get_mib (MAC_WEP) failed: %d\n", ret); - goto exit; - } - - at76_dbg(DBG_MIB, "%s: MIB MAC_WEP: priv_invoked %u def_key_id %u " - "key_len %u excl_unencr %u wep_icv_err %u wep_excluded %u " - "encr_level %u key %d", wiphy_name(priv->hw->wiphy), - m->privacy_invoked, m->wep_default_key_id, - m->wep_key_mapping_len, m->exclude_unencrypted, - le32_to_cpu(m->wep_icv_error_count), - le32_to_cpu(m->wep_excluded_count), m->encryption_level, - m->wep_default_key_id); - - key_len = (m->encryption_level == 1) ? - WEP_SMALL_KEY_LEN : WEP_LARGE_KEY_LEN; - - for (i = 0; i < WEP_KEYS; i++) - at76_dbg(DBG_MIB, "%s: MIB MAC_WEP: key %d: %*phD", - wiphy_name(priv->hw->wiphy), i, - key_len, m->wep_default_keyvalue[i]); -exit: - kfree(m); -} - -static void at76_dump_mib_mac_mgmt(struct at76_priv *priv) -{ - int ret; - struct mib_mac_mgmt *m = kmalloc(sizeof(struct mib_mac_mgmt), - GFP_KERNEL); - - if (!m) - return; - - ret = at76_get_mib(priv->udev, MIB_MAC_MGMT, m, - sizeof(struct mib_mac_mgmt)); - if (ret < 0) { - wiphy_err(priv->hw->wiphy, - "at76_get_mib (MAC_MGMT) failed: %d\n", ret); - goto exit; - } - - at76_dbg(DBG_MIB, "%s: MIB MAC_MGMT: beacon_period %d CFP_max_duration " - "%d medium_occupancy_limit %d station_id 0x%x ATIM_window %d " - "CFP_mode %d privacy_opt_impl %d DTIM_period %d CFP_period %d " - "current_bssid %pM current_essid %*phD current_bss_type %d " - "pm_mode %d ibss_change %d res %d " - "multi_domain_capability_implemented %d " - "international_roaming %d country_string %.3s", - wiphy_name(priv->hw->wiphy), le16_to_cpu(m->beacon_period), - le16_to_cpu(m->CFP_max_duration), - le16_to_cpu(m->medium_occupancy_limit), - le16_to_cpu(m->station_id), le16_to_cpu(m->ATIM_window), - m->CFP_mode, m->privacy_option_implemented, m->DTIM_period, - m->CFP_period, m->current_bssid, - IW_ESSID_MAX_SIZE, m->current_essid, - m->current_bss_type, m->power_mgmt_mode, m->ibss_change, - m->res, m->multi_domain_capability_implemented, - m->multi_domain_capability_enabled, m->country_string); -exit: - kfree(m); -} - -static void at76_dump_mib_mac(struct at76_priv *priv) -{ - int ret; - struct mib_mac *m = kmalloc(sizeof(struct mib_mac), GFP_KERNEL); - - if (!m) - return; - - ret = at76_get_mib(priv->udev, MIB_MAC, m, sizeof(struct mib_mac)); - if (ret < 0) { - wiphy_err(priv->hw->wiphy, - "at76_get_mib (MAC) failed: %d\n", ret); - goto exit; - } - - at76_dbg(DBG_MIB, "%s: MIB MAC: max_tx_msdu_lifetime %d " - "max_rx_lifetime %d frag_threshold %d rts_threshold %d " - "cwmin %d cwmax %d short_retry_time %d long_retry_time %d " - "scan_type %d scan_channel %d probe_delay %u " - "min_channel_time %d max_channel_time %d listen_int %d " - "desired_ssid %*phD desired_bssid %pM desired_bsstype %d", - wiphy_name(priv->hw->wiphy), - le32_to_cpu(m->max_tx_msdu_lifetime), - le32_to_cpu(m->max_rx_lifetime), - le16_to_cpu(m->frag_threshold), le16_to_cpu(m->rts_threshold), - le16_to_cpu(m->cwmin), le16_to_cpu(m->cwmax), - m->short_retry_time, m->long_retry_time, m->scan_type, - m->scan_channel, le16_to_cpu(m->probe_delay), - le16_to_cpu(m->min_channel_time), - le16_to_cpu(m->max_channel_time), - le16_to_cpu(m->listen_interval), - IW_ESSID_MAX_SIZE, m->desired_ssid, - m->desired_bssid, m->desired_bsstype); -exit: - kfree(m); -} - -static void at76_dump_mib_phy(struct at76_priv *priv) -{ - int ret; - struct mib_phy *m = kmalloc(sizeof(struct mib_phy), GFP_KERNEL); - - if (!m) - return; - - ret = at76_get_mib(priv->udev, MIB_PHY, m, sizeof(struct mib_phy)); - if (ret < 0) { - wiphy_err(priv->hw->wiphy, - "at76_get_mib (PHY) failed: %d\n", ret); - goto exit; - } - - at76_dbg(DBG_MIB, "%s: MIB PHY: ed_threshold %d slot_time %d " - "sifs_time %d preamble_length %d plcp_header_length %d " - "mpdu_max_length %d cca_mode_supported %d operation_rate_set " - "0x%x 0x%x 0x%x 0x%x channel_id %d current_cca_mode %d " - "phy_type %d current_reg_domain %d", - wiphy_name(priv->hw->wiphy), le32_to_cpu(m->ed_threshold), - le16_to_cpu(m->slot_time), le16_to_cpu(m->sifs_time), - le16_to_cpu(m->preamble_length), - le16_to_cpu(m->plcp_header_length), - le16_to_cpu(m->mpdu_max_length), - le16_to_cpu(m->cca_mode_supported), m->operation_rate_set[0], - m->operation_rate_set[1], m->operation_rate_set[2], - m->operation_rate_set[3], m->channel_id, m->current_cca_mode, - m->phy_type, m->current_reg_domain); -exit: - kfree(m); -} - -static void at76_dump_mib_local(struct at76_priv *priv) -{ - int ret; - struct mib_local *m = kmalloc(sizeof(*m), GFP_KERNEL); - - if (!m) - return; - - ret = at76_get_mib(priv->udev, MIB_LOCAL, m, sizeof(*m)); - if (ret < 0) { - wiphy_err(priv->hw->wiphy, - "at76_get_mib (LOCAL) failed: %d\n", ret); - goto exit; - } - - at76_dbg(DBG_MIB, "%s: MIB LOCAL: beacon_enable %d " - "txautorate_fallback %d ssid_size %d promiscuous_mode %d " - "preamble_type %d", wiphy_name(priv->hw->wiphy), - m->beacon_enable, - m->txautorate_fallback, m->ssid_size, m->promiscuous_mode, - m->preamble_type); -exit: - kfree(m); -} - -static void at76_dump_mib_mdomain(struct at76_priv *priv) -{ - int ret; - struct mib_mdomain *m = kmalloc(sizeof(struct mib_mdomain), GFP_KERNEL); - - if (!m) - return; - - ret = at76_get_mib(priv->udev, MIB_MDOMAIN, m, - sizeof(struct mib_mdomain)); - if (ret < 0) { - wiphy_err(priv->hw->wiphy, - "at76_get_mib (MDOMAIN) failed: %d\n", ret); - goto exit; - } - - at76_dbg(DBG_MIB, "%s: MIB MDOMAIN: channel_list %*phD", - wiphy_name(priv->hw->wiphy), - (int)sizeof(m->channel_list), m->channel_list); - - at76_dbg(DBG_MIB, "%s: MIB MDOMAIN: tx_powerlevel %*phD", - wiphy_name(priv->hw->wiphy), - (int)sizeof(m->tx_powerlevel), m->tx_powerlevel); -exit: - kfree(m); -} - -/* Enable monitor mode */ -static int at76_start_monitor(struct at76_priv *priv) -{ - struct at76_req_scan scan; - int ret; - - memset(&scan, 0, sizeof(struct at76_req_scan)); - eth_broadcast_addr(scan.bssid); - - scan.channel = priv->channel; - scan.scan_type = SCAN_TYPE_PASSIVE; - scan.international_scan = 0; - scan.min_channel_time = cpu_to_le16(priv->scan_min_time); - scan.max_channel_time = cpu_to_le16(priv->scan_max_time); - scan.probe_delay = cpu_to_le16(0); - - ret = at76_set_card_command(priv->udev, CMD_SCAN, &scan, sizeof(scan)); - if (ret >= 0) - ret = at76_get_cmd_status(priv->udev, CMD_SCAN); - - return ret; -} - -/* Calculate padding from txbuf->wlength (which excludes the USB TX header), - likely to compensate a flaw in the AT76C503A USB part ... */ -static inline int at76_calc_padding(int wlen) -{ - /* add the USB TX header */ - wlen += AT76_TX_HDRLEN; - - wlen = wlen % 64; - - if (wlen < 50) - return 50 - wlen; - - if (wlen >= 61) - return 64 + 50 - wlen; - - return 0; -} - -static void at76_rx_callback(struct urb *urb) -{ - struct at76_priv *priv = urb->context; - - priv->rx_tasklet.data = (unsigned long)urb; - tasklet_schedule(&priv->rx_tasklet); -} - -static int at76_submit_rx_urb(struct at76_priv *priv) -{ - int ret; - int size; - struct sk_buff *skb = priv->rx_skb; - - if (!priv->rx_urb) { - wiphy_err(priv->hw->wiphy, "%s: priv->rx_urb is NULL\n", - __func__); - return -EFAULT; - } - - if (!skb) { - skb = dev_alloc_skb(sizeof(struct at76_rx_buffer)); - if (!skb) { - wiphy_err(priv->hw->wiphy, - "cannot allocate rx skbuff\n"); - ret = -ENOMEM; - goto exit; - } - priv->rx_skb = skb; - } else { - skb_push(skb, skb_headroom(skb)); - skb_trim(skb, 0); - } - - size = skb_tailroom(skb); - usb_fill_bulk_urb(priv->rx_urb, priv->udev, priv->rx_pipe, - skb_put(skb, size), size, at76_rx_callback, priv); - ret = usb_submit_urb(priv->rx_urb, GFP_ATOMIC); - if (ret < 0) { - if (ret == -ENODEV) - at76_dbg(DBG_DEVSTART, - "usb_submit_urb returned -ENODEV"); - else - wiphy_err(priv->hw->wiphy, - "rx, usb_submit_urb failed: %d\n", ret); - } - -exit: - if (ret < 0 && ret != -ENODEV) - wiphy_err(priv->hw->wiphy, - "cannot submit rx urb - please unload the driver and/or power cycle the device\n"); - - return ret; -} - -/* Download external firmware */ -static int at76_load_external_fw(struct usb_device *udev, struct fwentry *fwe) -{ - int ret; - int op_mode; - int blockno = 0; - int bsize; - u8 *block; - u8 *buf = fwe->extfw; - int size = fwe->extfw_size; - - if (!buf || !size) - return -ENOENT; - - op_mode = at76_get_op_mode(udev); - at76_dbg(DBG_DEVSTART, "opmode %d", op_mode); - - if (op_mode != OPMODE_NORMAL_NIC_WITHOUT_FLASH) { - dev_err(&udev->dev, "unexpected opmode %d\n", op_mode); - return -EINVAL; - } - - block = kmalloc(FW_BLOCK_SIZE, GFP_KERNEL); - if (!block) - return -ENOMEM; - - at76_dbg(DBG_DEVSTART, "downloading external firmware"); - - /* for fw >= 0.100, the device needs an extra empty block */ - do { - bsize = min_t(int, size, FW_BLOCK_SIZE); - memcpy(block, buf, bsize); - at76_dbg(DBG_DEVSTART, - "ext fw, size left = %5d, bsize = %4d, blockno = %2d", - size, bsize, blockno); - ret = at76_load_ext_fw_block(udev, blockno, block, bsize); - if (ret != bsize) { - dev_err(&udev->dev, - "loading %dth firmware block failed: %d\n", - blockno, ret); - ret = -EIO; - goto exit; - } - buf += bsize; - size -= bsize; - blockno++; - } while (bsize > 0); - - if (at76_is_505a(fwe->board_type)) { - at76_dbg(DBG_DEVSTART, "200 ms delay for 505a"); - schedule_timeout_interruptible(HZ / 5 + 1); - } - -exit: - kfree(block); - if (ret < 0) - dev_err(&udev->dev, - "downloading external firmware failed: %d\n", ret); - return ret; -} - -/* Download internal firmware */ -static int at76_load_internal_fw(struct usb_device *udev, struct fwentry *fwe) -{ - int ret; - int need_remap = !at76_is_505a(fwe->board_type); - - ret = at76_usbdfu_download(udev, fwe->intfw, fwe->intfw_size, - need_remap ? 0 : 2 * HZ); - - if (ret < 0) { - dev_err(&udev->dev, - "downloading internal fw failed with %d\n", ret); - goto exit; - } - - at76_dbg(DBG_DEVSTART, "sending REMAP"); - - /* no REMAP for 505A (see SF driver) */ - if (need_remap) { - ret = at76_remap(udev); - if (ret < 0) { - dev_err(&udev->dev, - "sending REMAP failed with %d\n", ret); - goto exit; - } - } - - at76_dbg(DBG_DEVSTART, "sleeping for 2 seconds"); - schedule_timeout_interruptible(2 * HZ + 1); - usb_reset_device(udev); - -exit: - return ret; -} - -static int at76_startup_device(struct at76_priv *priv) -{ - struct at76_card_config *ccfg = &priv->card_config; - int ret; - - at76_dbg(DBG_PARAMS, - "%s param: ssid %.*s (%*phD) mode %s ch %d wep %s key %d " - "keylen %d", wiphy_name(priv->hw->wiphy), priv->essid_size, - priv->essid, IW_ESSID_MAX_SIZE, priv->essid, - priv->iw_mode == IW_MODE_ADHOC ? "adhoc" : "infra", - priv->channel, priv->wep_enabled ? "enabled" : "disabled", - priv->wep_key_id, priv->wep_keys_len[priv->wep_key_id]); - at76_dbg(DBG_PARAMS, - "%s param: preamble %s rts %d retry %d frag %d " - "txrate %s auth_mode %d", wiphy_name(priv->hw->wiphy), - preambles[priv->preamble_type], priv->rts_threshold, - priv->short_retry_limit, priv->frag_threshold, - priv->txrate == TX_RATE_1MBIT ? "1MBit" : priv->txrate == - TX_RATE_2MBIT ? "2MBit" : priv->txrate == - TX_RATE_5_5MBIT ? "5.5MBit" : priv->txrate == - TX_RATE_11MBIT ? "11MBit" : priv->txrate == - TX_RATE_AUTO ? "auto" : "", priv->auth_mode); - at76_dbg(DBG_PARAMS, - "%s param: pm_mode %d pm_period %d auth_mode %s " - "scan_times %d %d scan_mode %s", - wiphy_name(priv->hw->wiphy), priv->pm_mode, priv->pm_period, - priv->auth_mode == WLAN_AUTH_OPEN ? "open" : "shared_secret", - priv->scan_min_time, priv->scan_max_time, - priv->scan_mode == SCAN_TYPE_ACTIVE ? "active" : "passive"); - - memset(ccfg, 0, sizeof(struct at76_card_config)); - ccfg->promiscuous_mode = 0; - ccfg->short_retry_limit = priv->short_retry_limit; - - if (priv->wep_enabled) { - if (priv->wep_keys_len[priv->wep_key_id] > WEP_SMALL_KEY_LEN) - ccfg->encryption_type = 2; - else - ccfg->encryption_type = 1; - - /* jal: always exclude unencrypted if WEP is active */ - ccfg->exclude_unencrypted = 1; - } else { - ccfg->exclude_unencrypted = 0; - ccfg->encryption_type = 0; - } - - ccfg->rts_threshold = cpu_to_le16(priv->rts_threshold); - ccfg->fragmentation_threshold = cpu_to_le16(priv->frag_threshold); - - memcpy(ccfg->basic_rate_set, hw_rates, 4); - /* jal: really needed, we do a set_mib for autorate later ??? */ - ccfg->auto_rate_fallback = (priv->txrate == TX_RATE_AUTO ? 1 : 0); - ccfg->channel = priv->channel; - ccfg->privacy_invoked = priv->wep_enabled; - memcpy(ccfg->current_ssid, priv->essid, IW_ESSID_MAX_SIZE); - ccfg->ssid_len = priv->essid_size; - - ccfg->wep_default_key_id = priv->wep_key_id; - memcpy(ccfg->wep_default_key_value, priv->wep_keys, - sizeof(priv->wep_keys)); - - ccfg->short_preamble = priv->preamble_type; - ccfg->beacon_period = cpu_to_le16(priv->beacon_period); - - ret = at76_set_card_command(priv->udev, CMD_STARTUP, &priv->card_config, - sizeof(struct at76_card_config)); - if (ret < 0) { - wiphy_err(priv->hw->wiphy, "at76_set_card_command failed: %d\n", - ret); - return ret; - } - - at76_wait_completion(priv, CMD_STARTUP); - - /* remove BSSID from previous run */ - eth_zero_addr(priv->bssid); - - priv->scanning = false; - - if (at76_set_radio(priv, 1) == 1) - at76_wait_completion(priv, CMD_RADIO_ON); - - ret = at76_set_preamble(priv, priv->preamble_type); - if (ret < 0) - return ret; - - ret = at76_set_frag(priv, priv->frag_threshold); - if (ret < 0) - return ret; - - ret = at76_set_rts(priv, priv->rts_threshold); - if (ret < 0) - return ret; - - ret = at76_set_autorate_fallback(priv, - priv->txrate == TX_RATE_AUTO ? 1 : 0); - if (ret < 0) - return ret; - - ret = at76_set_pm_mode(priv); - if (ret < 0) - return ret; - - if (at76_debug & DBG_MIB) { - at76_dump_mib_mac(priv); - at76_dump_mib_mac_addr(priv); - at76_dump_mib_mac_mgmt(priv); - at76_dump_mib_mac_wep(priv); - at76_dump_mib_mdomain(priv); - at76_dump_mib_phy(priv); - at76_dump_mib_local(priv); - } - - return 0; -} - -/* Enable or disable promiscuous mode */ -static void at76_work_set_promisc(struct work_struct *work) -{ - struct at76_priv *priv = container_of(work, struct at76_priv, - work_set_promisc); - int ret = 0; - - if (priv->device_unplugged) - return; - - mutex_lock(&priv->mtx); - - priv->mib_buf.type = MIB_LOCAL; - priv->mib_buf.size = 1; - priv->mib_buf.index = offsetof(struct mib_local, promiscuous_mode); - priv->mib_buf.data.byte = priv->promisc ? 1 : 0; - - ret = at76_set_mib(priv, &priv->mib_buf); - if (ret < 0) - wiphy_err(priv->hw->wiphy, - "set_mib (promiscuous_mode) failed: %d\n", ret); - - mutex_unlock(&priv->mtx); -} - -/* Submit Rx urb back to the device */ -static void at76_work_submit_rx(struct work_struct *work) -{ - struct at76_priv *priv = container_of(work, struct at76_priv, - work_submit_rx); - - mutex_lock(&priv->mtx); - at76_submit_rx_urb(priv); - mutex_unlock(&priv->mtx); -} - -/* This is a workaround to make scan working: - * currently mac80211 does not process frames with no frequency - * information. - * However during scan the HW performs a sweep by itself, and we - * are unable to know where the radio is actually tuned. - * This function tries to do its best to guess this information.. - * During scan, If the current frame is a beacon or a probe response, - * the channel information is extracted from it. - * When not scanning, for other frames, or if it happens that for - * whatever reason we fail to parse beacons and probe responses, this - * function returns the priv->channel information, that should be correct - * at least when we are not scanning. - */ -static inline int at76_guess_freq(struct at76_priv *priv) -{ - size_t el_off; - const u8 *el; - int channel = priv->channel; - int len = priv->rx_skb->len; - struct ieee80211_hdr *hdr = (void *)priv->rx_skb->data; - - if (!priv->scanning) - goto exit; - - if (len < 24) - goto exit; - - if (ieee80211_is_probe_resp(hdr->frame_control)) { - el_off = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); - el = ((struct ieee80211_mgmt *)hdr)->u.probe_resp.variable; - } else if (ieee80211_is_beacon(hdr->frame_control)) { - el_off = offsetof(struct ieee80211_mgmt, u.beacon.variable); - el = ((struct ieee80211_mgmt *)hdr)->u.beacon.variable; - } else { - goto exit; - } - len -= el_off; - - el = cfg80211_find_ie(WLAN_EID_DS_PARAMS, el, len); - if (el && el[1] > 0) - channel = el[2]; - -exit: - return ieee80211_channel_to_frequency(channel, IEEE80211_BAND_2GHZ); -} - -static void at76_rx_tasklet(unsigned long param) -{ - struct urb *urb = (struct urb *)param; - struct at76_priv *priv = urb->context; - struct at76_rx_buffer *buf; - struct ieee80211_rx_status rx_status = { 0 }; - - if (priv->device_unplugged) { - at76_dbg(DBG_DEVSTART, "device unplugged"); - at76_dbg(DBG_DEVSTART, "urb status %d", urb->status); - return; - } - - if (!priv->rx_skb || !priv->rx_skb->data) - return; - - buf = (struct at76_rx_buffer *)priv->rx_skb->data; - - if (urb->status != 0) { - if (urb->status != -ENOENT && urb->status != -ECONNRESET) - at76_dbg(DBG_URB, - "%s %s: - nonzero Rx bulk status received: %d", - __func__, wiphy_name(priv->hw->wiphy), - urb->status); - return; - } - - at76_dbg(DBG_RX_ATMEL_HDR, - "%s: rx frame: rate %d rssi %d noise %d link %d", - wiphy_name(priv->hw->wiphy), buf->rx_rate, buf->rssi, - buf->noise_level, buf->link_quality); - - skb_pull(priv->rx_skb, AT76_RX_HDRLEN); - skb_trim(priv->rx_skb, le16_to_cpu(buf->wlength)); - at76_dbg_dump(DBG_RX_DATA, priv->rx_skb->data, - priv->rx_skb->len, "RX: len=%d", priv->rx_skb->len); - - rx_status.signal = buf->rssi; - rx_status.flag |= RX_FLAG_DECRYPTED; - rx_status.flag |= RX_FLAG_IV_STRIPPED; - rx_status.band = IEEE80211_BAND_2GHZ; - rx_status.freq = at76_guess_freq(priv); - - at76_dbg(DBG_MAC80211, "calling ieee80211_rx_irqsafe(): %d/%d", - priv->rx_skb->len, priv->rx_skb->data_len); - memcpy(IEEE80211_SKB_RXCB(priv->rx_skb), &rx_status, sizeof(rx_status)); - ieee80211_rx_irqsafe(priv->hw, priv->rx_skb); - - /* Use a new skb for the next receive */ - priv->rx_skb = NULL; - - at76_submit_rx_urb(priv); -} - -/* Load firmware into kernel memory and parse it */ -static struct fwentry *at76_load_firmware(struct usb_device *udev, - enum board_type board_type) -{ - int ret; - char *str; - struct at76_fw_header *fwh; - struct fwentry *fwe = &firmwares[board_type]; - - mutex_lock(&fw_mutex); - - if (fwe->loaded) { - at76_dbg(DBG_FW, "re-using previously loaded fw"); - goto exit; - } - - at76_dbg(DBG_FW, "downloading firmware %s", fwe->fwname); - ret = request_firmware(&fwe->fw, fwe->fwname, &udev->dev); - if (ret < 0) { - dev_err(&udev->dev, "firmware %s not found!\n", - fwe->fwname); - dev_err(&udev->dev, - "you may need to download the firmware from http://developer.berlios.de/projects/at76c503a/\n"); - goto exit; - } - - at76_dbg(DBG_FW, "got it."); - fwh = (struct at76_fw_header *)(fwe->fw->data); - - if (fwe->fw->size <= sizeof(*fwh)) { - dev_err(&udev->dev, - "firmware is too short (0x%zx)\n", fwe->fw->size); - goto exit; - } - - /* CRC currently not checked */ - fwe->board_type = le32_to_cpu(fwh->board_type); - if (fwe->board_type != board_type) { - dev_err(&udev->dev, - "board type mismatch, requested %u, got %u\n", - board_type, fwe->board_type); - goto exit; - } - - fwe->fw_version.major = fwh->major; - fwe->fw_version.minor = fwh->minor; - fwe->fw_version.patch = fwh->patch; - fwe->fw_version.build = fwh->build; - - str = (char *)fwh + le32_to_cpu(fwh->str_offset); - fwe->intfw = (u8 *)fwh + le32_to_cpu(fwh->int_fw_offset); - fwe->intfw_size = le32_to_cpu(fwh->int_fw_len); - fwe->extfw = (u8 *)fwh + le32_to_cpu(fwh->ext_fw_offset); - fwe->extfw_size = le32_to_cpu(fwh->ext_fw_len); - - fwe->loaded = 1; - - dev_printk(KERN_DEBUG, &udev->dev, - "using firmware %s (version %d.%d.%d-%d)\n", - fwe->fwname, fwh->major, fwh->minor, fwh->patch, fwh->build); - - at76_dbg(DBG_DEVSTART, "board %u, int %d:%d, ext %d:%d", board_type, - le32_to_cpu(fwh->int_fw_offset), le32_to_cpu(fwh->int_fw_len), - le32_to_cpu(fwh->ext_fw_offset), le32_to_cpu(fwh->ext_fw_len)); - at76_dbg(DBG_DEVSTART, "firmware id %s", str); - -exit: - mutex_unlock(&fw_mutex); - - if (fwe->loaded) - return fwe; - else - return NULL; -} - -static int at76_join(struct at76_priv *priv) -{ - struct at76_req_join join; - int ret; - - memset(&join, 0, sizeof(struct at76_req_join)); - memcpy(join.essid, priv->essid, priv->essid_size); - join.essid_size = priv->essid_size; - memcpy(join.bssid, priv->bssid, ETH_ALEN); - join.bss_type = INFRASTRUCTURE_MODE; - join.channel = priv->channel; - join.timeout = cpu_to_le16(2000); - - at76_dbg(DBG_MAC80211, "%s: sending CMD_JOIN", __func__); - ret = at76_set_card_command(priv->udev, CMD_JOIN, &join, - sizeof(struct at76_req_join)); - - if (ret < 0) { - wiphy_err(priv->hw->wiphy, "at76_set_card_command failed: %d\n", - ret); - return 0; - } - - ret = at76_wait_completion(priv, CMD_JOIN); - at76_dbg(DBG_MAC80211, "%s: CMD_JOIN returned: 0x%02x", __func__, ret); - if (ret != CMD_STATUS_COMPLETE) { - wiphy_err(priv->hw->wiphy, "at76_wait_completion failed: %d\n", - ret); - return 0; - } - - at76_set_pm_mode(priv); - - return 0; -} - -static void at76_work_join_bssid(struct work_struct *work) -{ - struct at76_priv *priv = container_of(work, struct at76_priv, - work_join_bssid); - - if (priv->device_unplugged) - return; - - mutex_lock(&priv->mtx); - - if (is_valid_ether_addr(priv->bssid)) - at76_join(priv); - - mutex_unlock(&priv->mtx); -} - -static void at76_mac80211_tx_callback(struct urb *urb) -{ - struct at76_priv *priv = urb->context; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(priv->tx_skb); - - at76_dbg(DBG_MAC80211, "%s()", __func__); - - switch (urb->status) { - case 0: - /* success */ - info->flags |= IEEE80211_TX_STAT_ACK; - break; - case -ENOENT: - case -ECONNRESET: - /* fail, urb has been unlinked */ - /* FIXME: add error message */ - break; - default: - at76_dbg(DBG_URB, "%s - nonzero tx status received: %d", - __func__, urb->status); - break; - } - - memset(&info->status, 0, sizeof(info->status)); - - ieee80211_tx_status_irqsafe(priv->hw, priv->tx_skb); - - priv->tx_skb = NULL; - - ieee80211_wake_queues(priv->hw); -} - -static void at76_mac80211_tx(struct ieee80211_hw *hw, - struct ieee80211_tx_control *control, - struct sk_buff *skb) -{ - struct at76_priv *priv = hw->priv; - struct at76_tx_buffer *tx_buffer = priv->bulk_out_buffer; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; - int padding, submit_len, ret; - - at76_dbg(DBG_MAC80211, "%s()", __func__); - - if (priv->tx_urb->status == -EINPROGRESS) { - wiphy_err(priv->hw->wiphy, - "%s called while tx urb is pending\n", __func__); - dev_kfree_skb_any(skb); - return; - } - - /* The following code lines are important when the device is going to - * authenticate with a new bssid. The driver must send CMD_JOIN before - * an authentication frame is transmitted. For this to succeed, the - * correct bssid of the AP must be known. As mac80211 does not inform - * drivers about the bssid prior to the authentication process the - * following workaround is necessary. If the TX frame is an - * authentication frame extract the bssid and send the CMD_JOIN. */ - if (mgmt->frame_control & cpu_to_le16(IEEE80211_STYPE_AUTH)) { - if (!ether_addr_equal_64bits(priv->bssid, mgmt->bssid)) { - memcpy(priv->bssid, mgmt->bssid, ETH_ALEN); - ieee80211_queue_work(hw, &priv->work_join_bssid); - dev_kfree_skb_any(skb); - return; - } - } - - ieee80211_stop_queues(hw); - - at76_ledtrig_tx_activity(); /* tell ledtrigger we send a packet */ - - WARN_ON(priv->tx_skb != NULL); - - priv->tx_skb = skb; - padding = at76_calc_padding(skb->len); - submit_len = AT76_TX_HDRLEN + skb->len + padding; - - /* setup 'Atmel' header */ - memset(tx_buffer, 0, sizeof(*tx_buffer)); - tx_buffer->padding = padding; - tx_buffer->wlength = cpu_to_le16(skb->len); - tx_buffer->tx_rate = ieee80211_get_tx_rate(hw, info)->hw_value; - memset(tx_buffer->reserved, 0, sizeof(tx_buffer->reserved)); - memcpy(tx_buffer->packet, skb->data, skb->len); - - at76_dbg(DBG_TX_DATA, "%s tx: wlen 0x%x pad 0x%x rate %d hdr", - wiphy_name(priv->hw->wiphy), le16_to_cpu(tx_buffer->wlength), - tx_buffer->padding, tx_buffer->tx_rate); - - /* send stuff */ - at76_dbg_dump(DBG_TX_DATA_CONTENT, tx_buffer, submit_len, - "%s(): tx_buffer %d bytes:", __func__, submit_len); - usb_fill_bulk_urb(priv->tx_urb, priv->udev, priv->tx_pipe, tx_buffer, - submit_len, at76_mac80211_tx_callback, priv); - ret = usb_submit_urb(priv->tx_urb, GFP_ATOMIC); - if (ret) { - wiphy_err(priv->hw->wiphy, "error in tx submit urb: %d\n", ret); - if (ret == -EINVAL) - wiphy_err(priv->hw->wiphy, - "-EINVAL: tx urb %p hcpriv %p complete %p\n", - priv->tx_urb, - priv->tx_urb->hcpriv, priv->tx_urb->complete); - } -} - -static int at76_mac80211_start(struct ieee80211_hw *hw) -{ - struct at76_priv *priv = hw->priv; - int ret; - - at76_dbg(DBG_MAC80211, "%s()", __func__); - - mutex_lock(&priv->mtx); - - ret = at76_submit_rx_urb(priv); - if (ret < 0) { - wiphy_err(priv->hw->wiphy, "open: submit_rx_urb failed: %d\n", - ret); - goto error; - } - - at76_startup_device(priv); - - at76_start_monitor(priv); - -error: - mutex_unlock(&priv->mtx); - - return 0; -} - -static void at76_mac80211_stop(struct ieee80211_hw *hw) -{ - struct at76_priv *priv = hw->priv; - - at76_dbg(DBG_MAC80211, "%s()", __func__); - - cancel_delayed_work(&priv->dwork_hw_scan); - cancel_work_sync(&priv->work_join_bssid); - cancel_work_sync(&priv->work_set_promisc); - - mutex_lock(&priv->mtx); - - if (!priv->device_unplugged) { - /* We are called by "ifconfig ethX down", not because the - * device is not available anymore. */ - at76_set_radio(priv, 0); - - /* We unlink rx_urb because at76_open() re-submits it. - * If unplugged, at76_delete_device() takes care of it. */ - usb_kill_urb(priv->rx_urb); - } - - mutex_unlock(&priv->mtx); -} - -static int at76_add_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct at76_priv *priv = hw->priv; - int ret = 0; - - at76_dbg(DBG_MAC80211, "%s()", __func__); - - mutex_lock(&priv->mtx); - - switch (vif->type) { - case NL80211_IFTYPE_STATION: - priv->iw_mode = IW_MODE_INFRA; - break; - default: - ret = -EOPNOTSUPP; - goto exit; - } - -exit: - mutex_unlock(&priv->mtx); - - return ret; -} - -static void at76_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - at76_dbg(DBG_MAC80211, "%s()", __func__); -} - -static void at76_dwork_hw_scan(struct work_struct *work) -{ - struct at76_priv *priv = container_of(work, struct at76_priv, - dwork_hw_scan.work); - int ret; - - if (priv->device_unplugged) - return; - - mutex_lock(&priv->mtx); - - ret = at76_get_cmd_status(priv->udev, CMD_SCAN); - at76_dbg(DBG_MAC80211, "%s: CMD_SCAN status 0x%02x", __func__, ret); - - /* FIXME: add maximum time for scan to complete */ - - if (ret != CMD_STATUS_COMPLETE) { - ieee80211_queue_delayed_work(priv->hw, &priv->dwork_hw_scan, - SCAN_POLL_INTERVAL); - mutex_unlock(&priv->mtx); - return; - } - - if (is_valid_ether_addr(priv->bssid)) - at76_join(priv); - - priv->scanning = false; - - mutex_unlock(&priv->mtx); - - ieee80211_scan_completed(priv->hw, false); - - ieee80211_wake_queues(priv->hw); -} - -static int at76_hw_scan(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_scan_request *hw_req) -{ - struct cfg80211_scan_request *req = &hw_req->req; - struct at76_priv *priv = hw->priv; - struct at76_req_scan scan; - u8 *ssid = NULL; - int ret, len = 0; - - at76_dbg(DBG_MAC80211, "%s():", __func__); - - if (priv->device_unplugged) - return 0; - - mutex_lock(&priv->mtx); - - ieee80211_stop_queues(hw); - - memset(&scan, 0, sizeof(struct at76_req_scan)); - eth_broadcast_addr(scan.bssid); - - if (req->n_ssids) { - scan.scan_type = SCAN_TYPE_ACTIVE; - ssid = req->ssids[0].ssid; - len = req->ssids[0].ssid_len; - } else { - scan.scan_type = SCAN_TYPE_PASSIVE; - } - - if (len) { - memcpy(scan.essid, ssid, len); - scan.essid_size = len; - } - - scan.min_channel_time = cpu_to_le16(priv->scan_min_time); - scan.max_channel_time = cpu_to_le16(priv->scan_max_time); - scan.probe_delay = cpu_to_le16(priv->scan_min_time * 1000); - scan.international_scan = 0; - - at76_dbg(DBG_MAC80211, "%s: sending CMD_SCAN", __func__); - ret = at76_set_card_command(priv->udev, CMD_SCAN, &scan, sizeof(scan)); - - if (ret < 0) { - wiphy_err(priv->hw->wiphy, "CMD_SCAN failed: %d\n", ret); - goto exit; - } - - priv->scanning = true; - ieee80211_queue_delayed_work(priv->hw, &priv->dwork_hw_scan, - SCAN_POLL_INTERVAL); - -exit: - mutex_unlock(&priv->mtx); - - return 0; -} - -static int at76_config(struct ieee80211_hw *hw, u32 changed) -{ - struct at76_priv *priv = hw->priv; - - at76_dbg(DBG_MAC80211, "%s(): channel %d", - __func__, hw->conf.chandef.chan->hw_value); - at76_dbg_dump(DBG_MAC80211, priv->bssid, ETH_ALEN, "bssid:"); - - mutex_lock(&priv->mtx); - - priv->channel = hw->conf.chandef.chan->hw_value; - - if (is_valid_ether_addr(priv->bssid)) - at76_join(priv); - else - at76_start_monitor(priv); - - mutex_unlock(&priv->mtx); - - return 0; -} - -static void at76_bss_info_changed(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *conf, - u32 changed) -{ - struct at76_priv *priv = hw->priv; - - at76_dbg(DBG_MAC80211, "%s():", __func__); - - if (!(changed & BSS_CHANGED_BSSID)) - return; - - at76_dbg_dump(DBG_MAC80211, conf->bssid, ETH_ALEN, "bssid:"); - - mutex_lock(&priv->mtx); - - memcpy(priv->bssid, conf->bssid, ETH_ALEN); - - if (is_valid_ether_addr(priv->bssid)) - /* mac80211 is joining a bss */ - at76_join(priv); - - mutex_unlock(&priv->mtx); -} - -/* must be atomic */ -static void at76_configure_filter(struct ieee80211_hw *hw, - unsigned int changed_flags, - unsigned int *total_flags, u64 multicast) -{ - struct at76_priv *priv = hw->priv; - int flags; - - at76_dbg(DBG_MAC80211, "%s(): changed_flags=0x%08x " - "total_flags=0x%08x", - __func__, changed_flags, *total_flags); - - flags = changed_flags & AT76_SUPPORTED_FILTERS; - *total_flags = AT76_SUPPORTED_FILTERS; - - /* Bail out after updating flags to prevent a WARN_ON in mac80211. */ - if (priv->device_unplugged) - return; - - /* FIXME: access to priv->promisc should be protected with - * priv->mtx, but it's impossible because this function needs to be - * atomic */ - - if (flags && !priv->promisc) { - /* mac80211 wants us to enable promiscuous mode */ - priv->promisc = 1; - } else if (!flags && priv->promisc) { - /* we need to disable promiscuous mode */ - priv->promisc = 0; - } else - return; - - ieee80211_queue_work(hw, &priv->work_set_promisc); -} - -static int at76_set_wep(struct at76_priv *priv) -{ - int ret = 0; - struct mib_mac_wep *mib_data = &priv->mib_buf.data.wep_mib; - - priv->mib_buf.type = MIB_MAC_WEP; - priv->mib_buf.size = sizeof(struct mib_mac_wep); - priv->mib_buf.index = 0; - - memset(mib_data, 0, sizeof(*mib_data)); - - if (priv->wep_enabled) { - if (priv->wep_keys_len[priv->wep_key_id] > WEP_SMALL_KEY_LEN) - mib_data->encryption_level = 2; - else - mib_data->encryption_level = 1; - - /* always exclude unencrypted if WEP is active */ - mib_data->exclude_unencrypted = 1; - } else { - mib_data->exclude_unencrypted = 0; - mib_data->encryption_level = 0; - } - - mib_data->privacy_invoked = priv->wep_enabled; - mib_data->wep_default_key_id = priv->wep_key_id; - memcpy(mib_data->wep_default_keyvalue, priv->wep_keys, - sizeof(priv->wep_keys)); - - ret = at76_set_mib(priv, &priv->mib_buf); - - if (ret < 0) - wiphy_err(priv->hw->wiphy, - "set_mib (wep) failed: %d\n", ret); - - return ret; -} - -static int at76_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, - struct ieee80211_vif *vif, struct ieee80211_sta *sta, - struct ieee80211_key_conf *key) -{ - struct at76_priv *priv = hw->priv; - - int i; - - at76_dbg(DBG_MAC80211, "%s(): cmd %d key->cipher %d key->keyidx %d " - "key->keylen %d", - __func__, cmd, key->cipher, key->keyidx, key->keylen); - - if ((key->cipher != WLAN_CIPHER_SUITE_WEP40) && - (key->cipher != WLAN_CIPHER_SUITE_WEP104)) - return -EOPNOTSUPP; - - key->hw_key_idx = key->keyidx; - - mutex_lock(&priv->mtx); - - switch (cmd) { - case SET_KEY: - memcpy(priv->wep_keys[key->keyidx], key->key, key->keylen); - priv->wep_keys_len[key->keyidx] = key->keylen; - - /* FIXME: find out how to do this properly */ - priv->wep_key_id = key->keyidx; - - break; - case DISABLE_KEY: - default: - priv->wep_keys_len[key->keyidx] = 0; - break; - } - - priv->wep_enabled = 0; - - for (i = 0; i < WEP_KEYS; i++) { - if (priv->wep_keys_len[i] != 0) - priv->wep_enabled = 1; - } - - at76_set_wep(priv); - - mutex_unlock(&priv->mtx); - - return 0; -} - -static const struct ieee80211_ops at76_ops = { - .tx = at76_mac80211_tx, - .add_interface = at76_add_interface, - .remove_interface = at76_remove_interface, - .config = at76_config, - .bss_info_changed = at76_bss_info_changed, - .configure_filter = at76_configure_filter, - .start = at76_mac80211_start, - .stop = at76_mac80211_stop, - .hw_scan = at76_hw_scan, - .set_key = at76_set_key, -}; - -/* Allocate network device and initialize private data */ -static struct at76_priv *at76_alloc_new_device(struct usb_device *udev) -{ - struct ieee80211_hw *hw; - struct at76_priv *priv; - - hw = ieee80211_alloc_hw(sizeof(struct at76_priv), &at76_ops); - if (!hw) { - printk(KERN_ERR DRIVER_NAME ": could not register" - " ieee80211_hw\n"); - return NULL; - } - - priv = hw->priv; - priv->hw = hw; - - priv->udev = udev; - - mutex_init(&priv->mtx); - INIT_WORK(&priv->work_set_promisc, at76_work_set_promisc); - INIT_WORK(&priv->work_submit_rx, at76_work_submit_rx); - INIT_WORK(&priv->work_join_bssid, at76_work_join_bssid); - INIT_DELAYED_WORK(&priv->dwork_hw_scan, at76_dwork_hw_scan); - - tasklet_init(&priv->rx_tasklet, at76_rx_tasklet, 0); - - priv->pm_mode = AT76_PM_OFF; - priv->pm_period = 0; - - /* unit us */ - - return priv; -} - -static int at76_alloc_urbs(struct at76_priv *priv, - struct usb_interface *interface) -{ - struct usb_endpoint_descriptor *endpoint, *ep_in, *ep_out; - int i; - int buffer_size; - struct usb_host_interface *iface_desc; - - at76_dbg(DBG_PROC_ENTRY, "%s: ENTER", __func__); - - at76_dbg(DBG_URB, "%s: NumEndpoints %d ", __func__, - interface->altsetting[0].desc.bNumEndpoints); - - ep_in = NULL; - ep_out = NULL; - iface_desc = interface->cur_altsetting; - for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) { - endpoint = &iface_desc->endpoint[i].desc; - - at76_dbg(DBG_URB, "%s: %d. endpoint: addr 0x%x attr 0x%x", - __func__, i, endpoint->bEndpointAddress, - endpoint->bmAttributes); - - if (!ep_in && usb_endpoint_is_bulk_in(endpoint)) - ep_in = endpoint; - - if (!ep_out && usb_endpoint_is_bulk_out(endpoint)) - ep_out = endpoint; - } - - if (!ep_in || !ep_out) { - dev_err(&interface->dev, "bulk endpoints missing\n"); - return -ENXIO; - } - - priv->rx_pipe = usb_rcvbulkpipe(priv->udev, ep_in->bEndpointAddress); - priv->tx_pipe = usb_sndbulkpipe(priv->udev, ep_out->bEndpointAddress); - - priv->rx_urb = usb_alloc_urb(0, GFP_KERNEL); - priv->tx_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!priv->rx_urb || !priv->tx_urb) { - dev_err(&interface->dev, "cannot allocate URB\n"); - return -ENOMEM; - } - - buffer_size = sizeof(struct at76_tx_buffer) + MAX_PADDING_SIZE; - priv->bulk_out_buffer = kmalloc(buffer_size, GFP_KERNEL); - if (!priv->bulk_out_buffer) - return -ENOMEM; - - at76_dbg(DBG_PROC_ENTRY, "%s: EXIT", __func__); - - return 0; -} - -static struct ieee80211_rate at76_rates[] = { - { .bitrate = 10, .hw_value = TX_RATE_1MBIT, }, - { .bitrate = 20, .hw_value = TX_RATE_2MBIT, }, - { .bitrate = 55, .hw_value = TX_RATE_5_5MBIT, }, - { .bitrate = 110, .hw_value = TX_RATE_11MBIT, }, -}; - -static struct ieee80211_channel at76_channels[] = { - { .center_freq = 2412, .hw_value = 1 }, - { .center_freq = 2417, .hw_value = 2 }, - { .center_freq = 2422, .hw_value = 3 }, - { .center_freq = 2427, .hw_value = 4 }, - { .center_freq = 2432, .hw_value = 5 }, - { .center_freq = 2437, .hw_value = 6 }, - { .center_freq = 2442, .hw_value = 7 }, - { .center_freq = 2447, .hw_value = 8 }, - { .center_freq = 2452, .hw_value = 9 }, - { .center_freq = 2457, .hw_value = 10 }, - { .center_freq = 2462, .hw_value = 11 }, - { .center_freq = 2467, .hw_value = 12 }, - { .center_freq = 2472, .hw_value = 13 }, - { .center_freq = 2484, .hw_value = 14 } -}; - -static struct ieee80211_supported_band at76_supported_band = { - .channels = at76_channels, - .n_channels = ARRAY_SIZE(at76_channels), - .bitrates = at76_rates, - .n_bitrates = ARRAY_SIZE(at76_rates), -}; - -/* Register network device and initialize the hardware */ -static int at76_init_new_device(struct at76_priv *priv, - struct usb_interface *interface) -{ - struct wiphy *wiphy; - size_t len; - int ret; - - /* set up the endpoint information */ - /* check out the endpoints */ - - at76_dbg(DBG_DEVSTART, "USB interface: %d endpoints", - interface->cur_altsetting->desc.bNumEndpoints); - - ret = at76_alloc_urbs(priv, interface); - if (ret < 0) - goto exit; - - /* MAC address */ - ret = at76_get_hw_config(priv); - if (ret < 0) { - dev_err(&interface->dev, "cannot get MAC address\n"); - goto exit; - } - - priv->domain = at76_get_reg_domain(priv->regulatory_domain); - - priv->channel = DEF_CHANNEL; - priv->iw_mode = IW_MODE_INFRA; - priv->rts_threshold = DEF_RTS_THRESHOLD; - priv->frag_threshold = DEF_FRAG_THRESHOLD; - priv->short_retry_limit = DEF_SHORT_RETRY_LIMIT; - priv->txrate = TX_RATE_AUTO; - priv->preamble_type = PREAMBLE_TYPE_LONG; - priv->beacon_period = 100; - priv->auth_mode = WLAN_AUTH_OPEN; - priv->scan_min_time = DEF_SCAN_MIN_TIME; - priv->scan_max_time = DEF_SCAN_MAX_TIME; - priv->scan_mode = SCAN_TYPE_ACTIVE; - priv->device_unplugged = 0; - - /* mac80211 initialisation */ - wiphy = priv->hw->wiphy; - priv->hw->wiphy->max_scan_ssids = 1; - priv->hw->wiphy->max_scan_ie_len = 0; - priv->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); - priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &at76_supported_band; - ieee80211_hw_set(priv->hw, RX_INCLUDES_FCS); - ieee80211_hw_set(priv->hw, SIGNAL_UNSPEC); - priv->hw->max_signal = 100; - - SET_IEEE80211_DEV(priv->hw, &interface->dev); - SET_IEEE80211_PERM_ADDR(priv->hw, priv->mac_addr); - - len = sizeof(wiphy->fw_version); - snprintf(wiphy->fw_version, len, "%d.%d.%d-%d", - priv->fw_version.major, priv->fw_version.minor, - priv->fw_version.patch, priv->fw_version.build); - - wiphy->hw_version = priv->board_type; - - ret = ieee80211_register_hw(priv->hw); - if (ret) { - printk(KERN_ERR "cannot register mac80211 hw (status %d)!\n", - ret); - goto exit; - } - - priv->mac80211_registered = 1; - - wiphy_info(priv->hw->wiphy, "USB %s, MAC %pM, firmware %d.%d.%d-%d\n", - dev_name(&interface->dev), priv->mac_addr, - priv->fw_version.major, priv->fw_version.minor, - priv->fw_version.patch, priv->fw_version.build); - wiphy_info(priv->hw->wiphy, "regulatory domain 0x%02x: %s\n", - priv->regulatory_domain, priv->domain->name); - -exit: - return ret; -} - -static void at76_delete_device(struct at76_priv *priv) -{ - at76_dbg(DBG_PROC_ENTRY, "%s: ENTER", __func__); - - /* The device is gone, don't bother turning it off */ - priv->device_unplugged = 1; - - tasklet_kill(&priv->rx_tasklet); - - if (priv->mac80211_registered) - ieee80211_unregister_hw(priv->hw); - - if (priv->tx_urb) { - usb_kill_urb(priv->tx_urb); - usb_free_urb(priv->tx_urb); - } - if (priv->rx_urb) { - usb_kill_urb(priv->rx_urb); - usb_free_urb(priv->rx_urb); - } - - at76_dbg(DBG_PROC_ENTRY, "%s: unlinked urbs", __func__); - - kfree(priv->bulk_out_buffer); - - del_timer_sync(&ledtrig_tx_timer); - - kfree_skb(priv->rx_skb); - - at76_dbg(DBG_PROC_ENTRY, "%s: before freeing priv/ieee80211_hw", - __func__); - ieee80211_free_hw(priv->hw); - - at76_dbg(DBG_PROC_ENTRY, "%s: EXIT", __func__); -} - -static int at76_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - int ret; - struct at76_priv *priv; - struct fwentry *fwe; - struct usb_device *udev; - int op_mode; - int need_ext_fw = 0; - struct mib_fw_version *fwv = NULL; - int board_type = (int)id->driver_info; - - udev = usb_get_dev(interface_to_usbdev(interface)); - - fwv = kmalloc(sizeof(*fwv), GFP_KERNEL); - if (!fwv) { - ret = -ENOMEM; - goto exit; - } - - /* Load firmware into kernel memory */ - fwe = at76_load_firmware(udev, board_type); - if (!fwe) { - ret = -ENOENT; - goto exit; - } - - op_mode = at76_get_op_mode(udev); - - at76_dbg(DBG_DEVSTART, "opmode %d", op_mode); - - /* we get OPMODE_NONE with 2.4.23, SMC2662W-AR ??? - we get 204 with 2.4.23, Fiberline FL-WL240u (505A+RFMD2958) ??? */ - - if (op_mode == OPMODE_HW_CONFIG_MODE) { - dev_err(&interface->dev, - "cannot handle a device in HW_CONFIG_MODE\n"); - ret = -EBUSY; - goto exit; - } - - if (op_mode != OPMODE_NORMAL_NIC_WITH_FLASH - && op_mode != OPMODE_NORMAL_NIC_WITHOUT_FLASH) { - /* download internal firmware part */ - dev_printk(KERN_DEBUG, &interface->dev, - "downloading internal firmware\n"); - ret = at76_load_internal_fw(udev, fwe); - if (ret < 0) { - dev_err(&interface->dev, - "error %d downloading internal firmware\n", - ret); - goto exit; - } - usb_put_dev(udev); - goto exit; - } - - /* Internal firmware already inside the device. Get firmware - * version to test if external firmware is loaded. - * This works only for newer firmware, e.g. the Intersil 0.90.x - * says "control timeout on ep0in" and subsequent - * at76_get_op_mode() fail too :-( */ - - /* if version >= 0.100.x.y or device with built-in flash we can - * query the device for the fw version */ - if ((fwe->fw_version.major > 0 || fwe->fw_version.minor >= 100) - || (op_mode == OPMODE_NORMAL_NIC_WITH_FLASH)) { - ret = at76_get_mib(udev, MIB_FW_VERSION, fwv, sizeof(*fwv)); - if (ret < 0 || (fwv->major | fwv->minor) == 0) - need_ext_fw = 1; - } else - /* No way to check firmware version, reload to be sure */ - need_ext_fw = 1; - - if (need_ext_fw) { - dev_printk(KERN_DEBUG, &interface->dev, - "downloading external firmware\n"); - - ret = at76_load_external_fw(udev, fwe); - if (ret < 0) - goto exit; - - /* Re-check firmware version */ - ret = at76_get_mib(udev, MIB_FW_VERSION, fwv, sizeof(*fwv)); - if (ret < 0) { - dev_err(&interface->dev, - "error %d getting firmware version\n", ret); - goto exit; - } - } - - priv = at76_alloc_new_device(udev); - if (!priv) { - ret = -ENOMEM; - goto exit; - } - - usb_set_intfdata(interface, priv); - - memcpy(&priv->fw_version, fwv, sizeof(struct mib_fw_version)); - priv->board_type = board_type; - - ret = at76_init_new_device(priv, interface); - if (ret < 0) - at76_delete_device(priv); - -exit: - kfree(fwv); - if (ret < 0) - usb_put_dev(udev); - return ret; -} - -static void at76_disconnect(struct usb_interface *interface) -{ - struct at76_priv *priv; - - priv = usb_get_intfdata(interface); - usb_set_intfdata(interface, NULL); - - /* Disconnect after loading internal firmware */ - if (!priv) - return; - - wiphy_info(priv->hw->wiphy, "disconnecting\n"); - at76_delete_device(priv); - usb_put_dev(priv->udev); - dev_info(&interface->dev, "disconnected\n"); -} - -/* Structure for registering this driver with the USB subsystem */ -static struct usb_driver at76_driver = { - .name = DRIVER_NAME, - .probe = at76_probe, - .disconnect = at76_disconnect, - .id_table = dev_table, - .disable_hub_initiated_lpm = 1, -}; - -static int __init at76_mod_init(void) -{ - int result; - - printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION " loading\n"); - - mutex_init(&fw_mutex); - - /* register this driver with the USB subsystem */ - result = usb_register(&at76_driver); - if (result < 0) - printk(KERN_ERR DRIVER_NAME - ": usb_register failed (status %d)\n", result); - - led_trigger_register_simple("at76_usb-tx", &ledtrig_tx); - return result; -} - -static void __exit at76_mod_exit(void) -{ - int i; - - printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION " unloading\n"); - usb_deregister(&at76_driver); - for (i = 0; i < ARRAY_SIZE(firmwares); i++) - release_firmware(firmwares[i].fw); - led_trigger_unregister_simple(ledtrig_tx); -} - -module_param_named(debug, at76_debug, uint, 0600); -MODULE_PARM_DESC(debug, "Debugging level"); - -module_init(at76_mod_init); -module_exit(at76_mod_exit); - -MODULE_AUTHOR("Oliver Kurth "); -MODULE_AUTHOR("Joerg Albert "); -MODULE_AUTHOR("Alex "); -MODULE_AUTHOR("Nick Jones"); -MODULE_AUTHOR("Balint Seeber "); -MODULE_AUTHOR("Pavel Roskin "); -MODULE_AUTHOR("Guido Guenther "); -MODULE_AUTHOR("Kalle Valo "); -MODULE_AUTHOR("Sebastian Smolorz "); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/at76c50x-usb.h b/drivers/net/wireless/at76c50x-usb.h deleted file mode 100644 index ae03271f878e..000000000000 --- a/drivers/net/wireless/at76c50x-usb.h +++ /dev/null @@ -1,466 +0,0 @@ -/* - * Copyright (c) 2002,2003 Oliver Kurth - * (c) 2003,2004 Joerg Albert - * (c) 2007 Guido Guenther - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This driver was based on information from the Sourceforge driver - * released and maintained by Atmel: - * - * http://sourceforge.net/projects/atmelwlandriver/ - * - * Although the code was completely re-written, - * it would have been impossible without Atmel's decision to - * release an Open Source driver (unfortunately the firmware was - * kept binary only). Thanks for that decision to Atmel! - */ - -#ifndef _AT76_USB_H -#define _AT76_USB_H - -/* Board types */ -enum board_type { - BOARD_503_ISL3861 = 1, - BOARD_503_ISL3863 = 2, - BOARD_503 = 3, - BOARD_503_ACC = 4, - BOARD_505 = 5, - BOARD_505_2958 = 6, - BOARD_505A = 7, - BOARD_505AMX = 8 -}; - -#define CMD_STATUS_IDLE 0x00 -#define CMD_STATUS_COMPLETE 0x01 -#define CMD_STATUS_UNKNOWN 0x02 -#define CMD_STATUS_INVALID_PARAMETER 0x03 -#define CMD_STATUS_FUNCTION_NOT_SUPPORTED 0x04 -#define CMD_STATUS_TIME_OUT 0x07 -#define CMD_STATUS_IN_PROGRESS 0x08 -#define CMD_STATUS_HOST_FAILURE 0xff -#define CMD_STATUS_SCAN_FAILED 0xf0 - -/* answers to get op mode */ -#define OPMODE_NONE 0x00 -#define OPMODE_NORMAL_NIC_WITH_FLASH 0x01 -#define OPMODE_HW_CONFIG_MODE 0x02 -#define OPMODE_DFU_MODE_WITH_FLASH 0x03 -#define OPMODE_NORMAL_NIC_WITHOUT_FLASH 0x04 - -#define CMD_SET_MIB 0x01 -#define CMD_GET_MIB 0x02 -#define CMD_SCAN 0x03 -#define CMD_JOIN 0x04 -#define CMD_START_IBSS 0x05 -#define CMD_RADIO_ON 0x06 -#define CMD_RADIO_OFF 0x07 -#define CMD_STARTUP 0x0B - -#define MIB_LOCAL 0x01 -#define MIB_MAC_ADDR 0x02 -#define MIB_MAC 0x03 -#define MIB_MAC_MGMT 0x05 -#define MIB_MAC_WEP 0x06 -#define MIB_PHY 0x07 -#define MIB_FW_VERSION 0x08 -#define MIB_MDOMAIN 0x09 - -#define ADHOC_MODE 1 -#define INFRASTRUCTURE_MODE 2 - -/* values for struct mib_local, field preamble_type */ -#define PREAMBLE_TYPE_LONG 0 -#define PREAMBLE_TYPE_SHORT 1 -#define PREAMBLE_TYPE_AUTO 2 - -/* values for tx_rate */ -#define TX_RATE_1MBIT 0 -#define TX_RATE_2MBIT 1 -#define TX_RATE_5_5MBIT 2 -#define TX_RATE_11MBIT 3 -#define TX_RATE_AUTO 4 - -/* power management modes */ -#define AT76_PM_OFF 1 -#define AT76_PM_ON 2 -#define AT76_PM_SMART 3 - -struct hwcfg_r505 { - u8 cr39_values[14]; - u8 reserved1[14]; - u8 bb_cr[14]; - u8 pidvid[4]; - u8 mac_addr[ETH_ALEN]; - u8 regulatory_domain; - u8 reserved2[14]; - u8 cr15_values[14]; - u8 reserved3[3]; -} __packed; - -struct hwcfg_rfmd { - u8 cr20_values[14]; - u8 cr21_values[14]; - u8 bb_cr[14]; - u8 pidvid[4]; - u8 mac_addr[ETH_ALEN]; - u8 regulatory_domain; - u8 low_power_values[14]; - u8 normal_power_values[14]; - u8 reserved1[3]; -} __packed; - -struct hwcfg_intersil { - u8 mac_addr[ETH_ALEN]; - u8 cr31_values[14]; - u8 cr58_values[14]; - u8 pidvid[4]; - u8 regulatory_domain; - u8 reserved[1]; -} __packed; - -union at76_hwcfg { - struct hwcfg_intersil i; - struct hwcfg_rfmd r3; - struct hwcfg_r505 r5; -}; - -#define WEP_SMALL_KEY_LEN (40 / 8) -#define WEP_LARGE_KEY_LEN (104 / 8) -#define WEP_KEYS (4) - -struct at76_card_config { - u8 exclude_unencrypted; - u8 promiscuous_mode; - u8 short_retry_limit; - u8 encryption_type; - __le16 rts_threshold; - __le16 fragmentation_threshold; /* 256..2346 */ - u8 basic_rate_set[4]; - u8 auto_rate_fallback; /* 0,1 */ - u8 channel; - u8 privacy_invoked; - u8 wep_default_key_id; /* 0..3 */ - u8 current_ssid[32]; - u8 wep_default_key_value[4][WEP_LARGE_KEY_LEN]; - u8 ssid_len; - u8 short_preamble; - __le16 beacon_period; -} __packed; - -struct at76_command { - u8 cmd; - u8 reserved; - __le16 size; - u8 data[0]; -} __packed; - -/* Length of Atmel-specific Rx header before 802.11 frame */ -#define AT76_RX_HDRLEN offsetof(struct at76_rx_buffer, packet) - -struct at76_rx_buffer { - __le16 wlength; - u8 rx_rate; - u8 newbss; - u8 fragmentation; - u8 rssi; - u8 link_quality; - u8 noise_level; - __le32 rx_time; - u8 packet[IEEE80211_MAX_FRAG_THRESHOLD]; -} __packed; - -/* Length of Atmel-specific Tx header before 802.11 frame */ -#define AT76_TX_HDRLEN offsetof(struct at76_tx_buffer, packet) - -struct at76_tx_buffer { - __le16 wlength; - u8 tx_rate; - u8 padding; - u8 reserved[4]; - u8 packet[IEEE80211_MAX_FRAG_THRESHOLD]; -} __packed; - -/* defines for scan_type below */ -#define SCAN_TYPE_ACTIVE 0 -#define SCAN_TYPE_PASSIVE 1 - -struct at76_req_scan { - u8 bssid[ETH_ALEN]; - u8 essid[32]; - u8 scan_type; - u8 channel; - __le16 probe_delay; - __le16 min_channel_time; - __le16 max_channel_time; - u8 essid_size; - u8 international_scan; -} __packed; - -struct at76_req_ibss { - u8 bssid[ETH_ALEN]; - u8 essid[32]; - u8 bss_type; - u8 channel; - u8 essid_size; - u8 reserved[3]; -} __packed; - -struct at76_req_join { - u8 bssid[ETH_ALEN]; - u8 essid[32]; - u8 bss_type; - u8 channel; - __le16 timeout; - u8 essid_size; - u8 reserved; -} __packed; - -struct mib_local { - u16 reserved0; - u8 beacon_enable; - u8 txautorate_fallback; - u8 reserved1; - u8 ssid_size; - u8 promiscuous_mode; - u16 reserved2; - u8 preamble_type; - u16 reserved3; -} __packed; - -struct mib_mac_addr { - u8 mac_addr[ETH_ALEN]; - u8 res[2]; /* ??? */ - u8 group_addr[4][ETH_ALEN]; - u8 group_addr_status[4]; -} __packed; - -struct mib_mac { - __le32 max_tx_msdu_lifetime; - __le32 max_rx_lifetime; - __le16 frag_threshold; - __le16 rts_threshold; - __le16 cwmin; - __le16 cwmax; - u8 short_retry_time; - u8 long_retry_time; - u8 scan_type; /* active or passive */ - u8 scan_channel; - __le16 probe_delay; /* delay before ProbeReq in active scan, RO */ - __le16 min_channel_time; - __le16 max_channel_time; - __le16 listen_interval; - u8 desired_ssid[32]; - u8 desired_bssid[ETH_ALEN]; - u8 desired_bsstype; /* ad-hoc or infrastructure */ - u8 reserved2; -} __packed; - -struct mib_mac_mgmt { - __le16 beacon_period; - __le16 CFP_max_duration; - __le16 medium_occupancy_limit; - __le16 station_id; /* assoc id */ - __le16 ATIM_window; - u8 CFP_mode; - u8 privacy_option_implemented; - u8 DTIM_period; - u8 CFP_period; - u8 current_bssid[ETH_ALEN]; - u8 current_essid[32]; - u8 current_bss_type; - u8 power_mgmt_mode; - /* rfmd and 505 */ - u8 ibss_change; - u8 res; - u8 multi_domain_capability_implemented; - u8 multi_domain_capability_enabled; - u8 country_string[IEEE80211_COUNTRY_STRING_LEN]; - u8 reserved[3]; -} __packed; - -struct mib_mac_wep { - u8 privacy_invoked; /* 0 disable encr., 1 enable encr */ - u8 wep_default_key_id; - u8 wep_key_mapping_len; - u8 exclude_unencrypted; - __le32 wep_icv_error_count; - __le32 wep_excluded_count; - u8 wep_default_keyvalue[WEP_KEYS][WEP_LARGE_KEY_LEN]; - u8 encryption_level; /* 1 for 40bit, 2 for 104bit encryption */ -} __packed; - -struct mib_phy { - __le32 ed_threshold; - - __le16 slot_time; - __le16 sifs_time; - __le16 preamble_length; - __le16 plcp_header_length; - __le16 mpdu_max_length; - __le16 cca_mode_supported; - - u8 operation_rate_set[4]; - u8 channel_id; - u8 current_cca_mode; - u8 phy_type; - u8 current_reg_domain; -} __packed; - -struct mib_fw_version { - u8 major; - u8 minor; - u8 patch; - u8 build; -} __packed; - -struct mib_mdomain { - u8 tx_powerlevel[14]; - u8 channel_list[14]; /* 0 for invalid channels */ -} __packed; - -struct set_mib_buffer { - u8 type; - u8 size; - u8 index; - u8 reserved; - union { - u8 byte; - __le16 word; - u8 addr[ETH_ALEN]; - struct mib_mac_wep wep_mib; - } data; -} __packed; - -struct at76_fw_header { - __le32 crc; /* CRC32 of the whole image */ - __le32 board_type; /* firmware compatibility code */ - u8 build; /* firmware build number */ - u8 patch; /* firmware patch level */ - u8 minor; /* firmware minor version */ - u8 major; /* firmware major version */ - __le32 str_offset; /* offset of the copyright string */ - __le32 int_fw_offset; /* internal firmware image offset */ - __le32 int_fw_len; /* internal firmware image length */ - __le32 ext_fw_offset; /* external firmware image offset */ - __le32 ext_fw_len; /* external firmware image length */ -} __packed; - -/* a description of a regulatory domain and the allowed channels */ -struct reg_domain { - u16 code; - char const *name; - u32 channel_map; /* if bit N is set, channel (N+1) is allowed */ -}; - -/* Data for one loaded firmware file */ -struct fwentry { - const char *const fwname; - const struct firmware *fw; - int extfw_size; - int intfw_size; - /* pointer to loaded firmware, no need to free */ - u8 *extfw; /* external firmware, extfw_size bytes long */ - u8 *intfw; /* internal firmware, intfw_size bytes long */ - enum board_type board_type; /* board type */ - struct mib_fw_version fw_version; - int loaded; /* Loaded and parsed successfully */ -}; - -struct at76_priv { - struct usb_device *udev; /* USB device pointer */ - - struct sk_buff *rx_skb; /* skbuff for receiving data */ - struct sk_buff *tx_skb; /* skbuff for transmitting data */ - void *bulk_out_buffer; /* buffer for sending data */ - - struct urb *tx_urb; /* URB for sending data */ - struct urb *rx_urb; /* URB for receiving data */ - - unsigned int tx_pipe; /* bulk out pipe */ - unsigned int rx_pipe; /* bulk in pipe */ - - struct mutex mtx; /* locks this structure */ - - /* work queues */ - struct work_struct work_set_promisc; - struct work_struct work_submit_rx; - struct work_struct work_join_bssid; - struct delayed_work dwork_hw_scan; - - struct tasklet_struct rx_tasklet; - - /* the WEP stuff */ - int wep_enabled; /* 1 if WEP is enabled */ - int wep_key_id; /* key id to be used */ - u8 wep_keys[WEP_KEYS][WEP_LARGE_KEY_LEN]; /* WEP keys */ - u8 wep_keys_len[WEP_KEYS]; /* length of WEP keys */ - - int channel; - int iw_mode; - u8 bssid[ETH_ALEN]; - u8 essid[IW_ESSID_MAX_SIZE]; - int essid_size; - int radio_on; - int promisc; - - int preamble_type; /* 0 - long, 1 - short, 2 - auto */ - int auth_mode; /* authentication type: 0 open, 1 shared key */ - int txrate; /* 0,1,2,3 = 1,2,5.5,11 Mbps, 4 is auto */ - int frag_threshold; /* threshold for fragmentation of tx packets */ - int rts_threshold; /* threshold for RTS mechanism */ - int short_retry_limit; - - int scan_min_time; /* scan min channel time */ - int scan_max_time; /* scan max channel time */ - int scan_mode; /* SCAN_TYPE_ACTIVE, SCAN_TYPE_PASSIVE */ - int scan_need_any; /* if set, need to scan for any ESSID */ - bool scanning; /* if set, the scan is running */ - - u16 assoc_id; /* current association ID, if associated */ - - u8 pm_mode; /* power management mode */ - u32 pm_period; /* power management period in microseconds */ - - struct reg_domain const *domain; /* reg domain description */ - - /* These fields contain HW config provided by the device (not all of - * these fields are used by all board types) */ - u8 mac_addr[ETH_ALEN]; - u8 regulatory_domain; - - struct at76_card_config card_config; - - enum board_type board_type; - struct mib_fw_version fw_version; - - unsigned int device_unplugged:1; - unsigned int netdev_registered:1; - struct set_mib_buffer mib_buf; /* global buffer for set_mib calls */ - - int beacon_period; /* period of mgmt beacons, Kus */ - - struct ieee80211_hw *hw; - int mac80211_registered; -}; - -#define AT76_SUPPORTED_FILTERS 0 - -#define SCAN_POLL_INTERVAL (HZ / 4) - -#define CMD_COMPLETION_TIMEOUT (5 * HZ) - -#define DEF_RTS_THRESHOLD 1536 -#define DEF_FRAG_THRESHOLD 1536 -#define DEF_SHORT_RETRY_LIMIT 8 -#define DEF_CHANNEL 10 -#define DEF_SCAN_MIN_TIME 10 -#define DEF_SCAN_MAX_TIME 120 - -/* the max padding size for tx in bytes (see calc_padding) */ -#define MAX_PADDING_SIZE 53 - -#endif /* _AT76_USB_H */ diff --git a/drivers/net/wireless/atmel.c b/drivers/net/wireless/atmel.c deleted file mode 100644 index 6a1f03c271c1..000000000000 --- a/drivers/net/wireless/atmel.c +++ /dev/null @@ -1,4549 +0,0 @@ -/*** -*- linux-c -*- ********************************************************** - - Driver for Atmel at76c502 at76c504 and at76c506 wireless cards. - - Copyright 2000-2001 ATMEL Corporation. - Copyright 2003-2004 Simon Kelley. - - This code was developed from version 2.1.1 of the Atmel drivers, - released by Atmel corp. under the GPL in December 2002. It also - includes code from the Linux aironet drivers (C) Benjamin Reed, - and the Linux PCMCIA package, (C) David Hinds and the Linux wireless - extensions, (C) Jean Tourrilhes. - - The firmware module for reading the MAC address of the card comes from - net.russotto.AtmelMACFW, written by Matthew T. Russotto and copyright - by him. net.russotto.AtmelMACFW is used under the GPL license version 2. - This file contains the module in binary form and, under the terms - of the GPL, in source form. The source is located at the end of the file. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This software is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Atmel wireless lan drivers; if not, see - . - - For all queries about this code, please contact the current author, - Simon Kelley and not Atmel Corporation. - - Credit is due to HP UK and Cambridge Online Systems Ltd for supplying - hardware used during development of this driver. - -******************************************************************************/ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "atmel.h" - -#define DRIVER_MAJOR 0 -#define DRIVER_MINOR 98 - -MODULE_AUTHOR("Simon Kelley"); -MODULE_DESCRIPTION("Support for Atmel at76c50x 802.11 wireless ethernet cards."); -MODULE_LICENSE("GPL"); -MODULE_SUPPORTED_DEVICE("Atmel at76c50x wireless cards"); - -/* The name of the firmware file to be loaded - over-rides any automatic selection */ -static char *firmware = NULL; -module_param(firmware, charp, 0); - -/* table of firmware file names */ -static struct { - AtmelFWType fw_type; - const char *fw_file; - const char *fw_file_ext; -} fw_table[] = { - { ATMEL_FW_TYPE_502, "atmel_at76c502", "bin" }, - { ATMEL_FW_TYPE_502D, "atmel_at76c502d", "bin" }, - { ATMEL_FW_TYPE_502E, "atmel_at76c502e", "bin" }, - { ATMEL_FW_TYPE_502_3COM, "atmel_at76c502_3com", "bin" }, - { ATMEL_FW_TYPE_504, "atmel_at76c504", "bin" }, - { ATMEL_FW_TYPE_504_2958, "atmel_at76c504_2958", "bin" }, - { ATMEL_FW_TYPE_504A_2958, "atmel_at76c504a_2958", "bin" }, - { ATMEL_FW_TYPE_506, "atmel_at76c506", "bin" }, - { ATMEL_FW_TYPE_NONE, NULL, NULL } -}; -MODULE_FIRMWARE("atmel_at76c502-wpa.bin"); -MODULE_FIRMWARE("atmel_at76c502.bin"); -MODULE_FIRMWARE("atmel_at76c502d-wpa.bin"); -MODULE_FIRMWARE("atmel_at76c502d.bin"); -MODULE_FIRMWARE("atmel_at76c502e-wpa.bin"); -MODULE_FIRMWARE("atmel_at76c502e.bin"); -MODULE_FIRMWARE("atmel_at76c502_3com-wpa.bin"); -MODULE_FIRMWARE("atmel_at76c502_3com.bin"); -MODULE_FIRMWARE("atmel_at76c504-wpa.bin"); -MODULE_FIRMWARE("atmel_at76c504.bin"); -MODULE_FIRMWARE("atmel_at76c504_2958-wpa.bin"); -MODULE_FIRMWARE("atmel_at76c504_2958.bin"); -MODULE_FIRMWARE("atmel_at76c504a_2958-wpa.bin"); -MODULE_FIRMWARE("atmel_at76c504a_2958.bin"); -MODULE_FIRMWARE("atmel_at76c506-wpa.bin"); -MODULE_FIRMWARE("atmel_at76c506.bin"); - -#define MAX_SSID_LENGTH 32 -#define MGMT_JIFFIES (256 * HZ / 100) - -#define MAX_BSS_ENTRIES 64 - -/* registers */ -#define GCR 0x00 /* (SIR0) General Configuration Register */ -#define BSR 0x02 /* (SIR1) Bank Switching Select Register */ -#define AR 0x04 -#define DR 0x08 -#define MR1 0x12 /* Mirror Register 1 */ -#define MR2 0x14 /* Mirror Register 2 */ -#define MR3 0x16 /* Mirror Register 3 */ -#define MR4 0x18 /* Mirror Register 4 */ - -#define GPR1 0x0c -#define GPR2 0x0e -#define GPR3 0x10 -/* - * Constants for the GCR register. - */ -#define GCR_REMAP 0x0400 /* Remap internal SRAM to 0 */ -#define GCR_SWRES 0x0080 /* BIU reset (ARM and PAI are NOT reset) */ -#define GCR_CORES 0x0060 /* Core Reset (ARM and PAI are reset) */ -#define GCR_ENINT 0x0002 /* Enable Interrupts */ -#define GCR_ACKINT 0x0008 /* Acknowledge Interrupts */ - -#define BSS_SRAM 0x0200 /* AMBA module selection --> SRAM */ -#define BSS_IRAM 0x0100 /* AMBA module selection --> IRAM */ -/* - *Constants for the MR registers. - */ -#define MAC_INIT_COMPLETE 0x0001 /* MAC init has been completed */ -#define MAC_BOOT_COMPLETE 0x0010 /* MAC boot has been completed */ -#define MAC_INIT_OK 0x0002 /* MAC boot has been completed */ - -#define MIB_MAX_DATA_BYTES 212 -#define MIB_HEADER_SIZE 4 /* first four fields */ - -struct get_set_mib { - u8 type; - u8 size; - u8 index; - u8 reserved; - u8 data[MIB_MAX_DATA_BYTES]; -}; - -struct rx_desc { - u32 Next; - u16 MsduPos; - u16 MsduSize; - - u8 State; - u8 Status; - u8 Rate; - u8 Rssi; - u8 LinkQuality; - u8 PreambleType; - u16 Duration; - u32 RxTime; -}; - -#define RX_DESC_FLAG_VALID 0x80 -#define RX_DESC_FLAG_CONSUMED 0x40 -#define RX_DESC_FLAG_IDLE 0x00 - -#define RX_STATUS_SUCCESS 0x00 - -#define RX_DESC_MSDU_POS_OFFSET 4 -#define RX_DESC_MSDU_SIZE_OFFSET 6 -#define RX_DESC_FLAGS_OFFSET 8 -#define RX_DESC_STATUS_OFFSET 9 -#define RX_DESC_RSSI_OFFSET 11 -#define RX_DESC_LINK_QUALITY_OFFSET 12 -#define RX_DESC_PREAMBLE_TYPE_OFFSET 13 -#define RX_DESC_DURATION_OFFSET 14 -#define RX_DESC_RX_TIME_OFFSET 16 - -struct tx_desc { - u32 NextDescriptor; - u16 TxStartOfFrame; - u16 TxLength; - - u8 TxState; - u8 TxStatus; - u8 RetryCount; - - u8 TxRate; - - u8 KeyIndex; - u8 ChiperType; - u8 ChipreLength; - u8 Reserved1; - - u8 Reserved; - u8 PacketType; - u16 HostTxLength; -}; - -#define TX_DESC_NEXT_OFFSET 0 -#define TX_DESC_POS_OFFSET 4 -#define TX_DESC_SIZE_OFFSET 6 -#define TX_DESC_FLAGS_OFFSET 8 -#define TX_DESC_STATUS_OFFSET 9 -#define TX_DESC_RETRY_OFFSET 10 -#define TX_DESC_RATE_OFFSET 11 -#define TX_DESC_KEY_INDEX_OFFSET 12 -#define TX_DESC_CIPHER_TYPE_OFFSET 13 -#define TX_DESC_CIPHER_LENGTH_OFFSET 14 -#define TX_DESC_PACKET_TYPE_OFFSET 17 -#define TX_DESC_HOST_LENGTH_OFFSET 18 - -/* - * Host-MAC interface - */ - -#define TX_STATUS_SUCCESS 0x00 - -#define TX_FIRM_OWN 0x80 -#define TX_DONE 0x40 - -#define TX_ERROR 0x01 - -#define TX_PACKET_TYPE_DATA 0x01 -#define TX_PACKET_TYPE_MGMT 0x02 - -#define ISR_EMPTY 0x00 /* no bits set in ISR */ -#define ISR_TxCOMPLETE 0x01 /* packet transmitted */ -#define ISR_RxCOMPLETE 0x02 /* packet received */ -#define ISR_RxFRAMELOST 0x04 /* Rx Frame lost */ -#define ISR_FATAL_ERROR 0x08 /* Fatal error */ -#define ISR_COMMAND_COMPLETE 0x10 /* command completed */ -#define ISR_OUT_OF_RANGE 0x20 /* command completed */ -#define ISR_IBSS_MERGE 0x40 /* (4.1.2.30): IBSS merge */ -#define ISR_GENERIC_IRQ 0x80 - -#define Local_Mib_Type 0x01 -#define Mac_Address_Mib_Type 0x02 -#define Mac_Mib_Type 0x03 -#define Statistics_Mib_Type 0x04 -#define Mac_Mgmt_Mib_Type 0x05 -#define Mac_Wep_Mib_Type 0x06 -#define Phy_Mib_Type 0x07 -#define Multi_Domain_MIB 0x08 - -#define MAC_MGMT_MIB_CUR_BSSID_POS 14 -#define MAC_MIB_FRAG_THRESHOLD_POS 8 -#define MAC_MIB_RTS_THRESHOLD_POS 10 -#define MAC_MIB_SHORT_RETRY_POS 16 -#define MAC_MIB_LONG_RETRY_POS 17 -#define MAC_MIB_SHORT_RETRY_LIMIT_POS 16 -#define MAC_MGMT_MIB_BEACON_PER_POS 0 -#define MAC_MGMT_MIB_STATION_ID_POS 6 -#define MAC_MGMT_MIB_CUR_PRIVACY_POS 11 -#define MAC_MGMT_MIB_CUR_BSSID_POS 14 -#define MAC_MGMT_MIB_PS_MODE_POS 53 -#define MAC_MGMT_MIB_LISTEN_INTERVAL_POS 54 -#define MAC_MGMT_MIB_MULTI_DOMAIN_IMPLEMENTED 56 -#define MAC_MGMT_MIB_MULTI_DOMAIN_ENABLED 57 -#define PHY_MIB_CHANNEL_POS 14 -#define PHY_MIB_RATE_SET_POS 20 -#define PHY_MIB_REG_DOMAIN_POS 26 -#define LOCAL_MIB_AUTO_TX_RATE_POS 3 -#define LOCAL_MIB_SSID_SIZE 5 -#define LOCAL_MIB_TX_PROMISCUOUS_POS 6 -#define LOCAL_MIB_TX_MGMT_RATE_POS 7 -#define LOCAL_MIB_TX_CONTROL_RATE_POS 8 -#define LOCAL_MIB_PREAMBLE_TYPE 9 -#define MAC_ADDR_MIB_MAC_ADDR_POS 0 - -#define CMD_Set_MIB_Vars 0x01 -#define CMD_Get_MIB_Vars 0x02 -#define CMD_Scan 0x03 -#define CMD_Join 0x04 -#define CMD_Start 0x05 -#define CMD_EnableRadio 0x06 -#define CMD_DisableRadio 0x07 -#define CMD_SiteSurvey 0x0B - -#define CMD_STATUS_IDLE 0x00 -#define CMD_STATUS_COMPLETE 0x01 -#define CMD_STATUS_UNKNOWN 0x02 -#define CMD_STATUS_INVALID_PARAMETER 0x03 -#define CMD_STATUS_FUNCTION_NOT_SUPPORTED 0x04 -#define CMD_STATUS_TIME_OUT 0x07 -#define CMD_STATUS_IN_PROGRESS 0x08 -#define CMD_STATUS_REJECTED_RADIO_OFF 0x09 -#define CMD_STATUS_HOST_ERROR 0xFF -#define CMD_STATUS_BUSY 0xFE - -#define CMD_BLOCK_COMMAND_OFFSET 0 -#define CMD_BLOCK_STATUS_OFFSET 1 -#define CMD_BLOCK_PARAMETERS_OFFSET 4 - -#define SCAN_OPTIONS_SITE_SURVEY 0x80 - -#define MGMT_FRAME_BODY_OFFSET 24 -#define MAX_AUTHENTICATION_RETRIES 3 -#define MAX_ASSOCIATION_RETRIES 3 - -#define AUTHENTICATION_RESPONSE_TIME_OUT 1000 - -#define MAX_WIRELESS_BODY 2316 /* mtu is 2312, CRC is 4 */ -#define LOOP_RETRY_LIMIT 500000 - -#define ACTIVE_MODE 1 -#define PS_MODE 2 - -#define MAX_ENCRYPTION_KEYS 4 -#define MAX_ENCRYPTION_KEY_SIZE 40 - -/* - * 802.11 related definitions - */ - -/* - * Regulatory Domains - */ - -#define REG_DOMAIN_FCC 0x10 /* Channels 1-11 USA */ -#define REG_DOMAIN_DOC 0x20 /* Channel 1-11 Canada */ -#define REG_DOMAIN_ETSI 0x30 /* Channel 1-13 Europe (ex Spain/France) */ -#define REG_DOMAIN_SPAIN 0x31 /* Channel 10-11 Spain */ -#define REG_DOMAIN_FRANCE 0x32 /* Channel 10-13 France */ -#define REG_DOMAIN_MKK 0x40 /* Channel 14 Japan */ -#define REG_DOMAIN_MKK1 0x41 /* Channel 1-14 Japan(MKK1) */ -#define REG_DOMAIN_ISRAEL 0x50 /* Channel 3-9 ISRAEL */ - -#define BSS_TYPE_AD_HOC 1 -#define BSS_TYPE_INFRASTRUCTURE 2 - -#define SCAN_TYPE_ACTIVE 0 -#define SCAN_TYPE_PASSIVE 1 - -#define LONG_PREAMBLE 0 -#define SHORT_PREAMBLE 1 -#define AUTO_PREAMBLE 2 - -#define DATA_FRAME_WS_HEADER_SIZE 30 - -/* promiscuous mode control */ -#define PROM_MODE_OFF 0x0 -#define PROM_MODE_UNKNOWN 0x1 -#define PROM_MODE_CRC_FAILED 0x2 -#define PROM_MODE_DUPLICATED 0x4 -#define PROM_MODE_MGMT 0x8 -#define PROM_MODE_CTRL 0x10 -#define PROM_MODE_BAD_PROTOCOL 0x20 - -#define IFACE_INT_STATUS_OFFSET 0 -#define IFACE_INT_MASK_OFFSET 1 -#define IFACE_LOCKOUT_HOST_OFFSET 2 -#define IFACE_LOCKOUT_MAC_OFFSET 3 -#define IFACE_FUNC_CTRL_OFFSET 28 -#define IFACE_MAC_STAT_OFFSET 30 -#define IFACE_GENERIC_INT_TYPE_OFFSET 32 - -#define CIPHER_SUITE_NONE 0 -#define CIPHER_SUITE_WEP_64 1 -#define CIPHER_SUITE_TKIP 2 -#define CIPHER_SUITE_AES 3 -#define CIPHER_SUITE_CCX 4 -#define CIPHER_SUITE_WEP_128 5 - -/* - * IFACE MACROS & definitions - */ - -/* - * FuncCtrl field: - */ -#define FUNC_CTRL_TxENABLE 0x10 -#define FUNC_CTRL_RxENABLE 0x20 -#define FUNC_CTRL_INIT_COMPLETE 0x01 - -/* A stub firmware image which reads the MAC address from NVRAM on the card. - For copyright information and source see the end of this file. */ -static u8 mac_reader[] = { - 0x06, 0x00, 0x00, 0xea, 0x04, 0x00, 0x00, 0xea, 0x03, 0x00, 0x00, 0xea, 0x02, 0x00, 0x00, 0xea, - 0x01, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00, 0xea, 0xff, 0xff, 0xff, 0xea, 0xfe, 0xff, 0xff, 0xea, - 0xd3, 0x00, 0xa0, 0xe3, 0x00, 0xf0, 0x21, 0xe1, 0x0e, 0x04, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3, - 0x81, 0x11, 0xa0, 0xe1, 0x00, 0x10, 0x81, 0xe3, 0x00, 0x10, 0x80, 0xe5, 0x1c, 0x10, 0x90, 0xe5, - 0x10, 0x10, 0xc1, 0xe3, 0x1c, 0x10, 0x80, 0xe5, 0x01, 0x10, 0xa0, 0xe3, 0x08, 0x10, 0x80, 0xe5, - 0x02, 0x03, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3, 0xb0, 0x10, 0xc0, 0xe1, 0xb4, 0x10, 0xc0, 0xe1, - 0xb8, 0x10, 0xc0, 0xe1, 0xbc, 0x10, 0xc0, 0xe1, 0x56, 0xdc, 0xa0, 0xe3, 0x21, 0x00, 0x00, 0xeb, - 0x0a, 0x00, 0xa0, 0xe3, 0x1a, 0x00, 0x00, 0xeb, 0x10, 0x00, 0x00, 0xeb, 0x07, 0x00, 0x00, 0xeb, - 0x02, 0x03, 0xa0, 0xe3, 0x02, 0x14, 0xa0, 0xe3, 0xb4, 0x10, 0xc0, 0xe1, 0x4c, 0x10, 0x9f, 0xe5, - 0xbc, 0x10, 0xc0, 0xe1, 0x10, 0x10, 0xa0, 0xe3, 0xb8, 0x10, 0xc0, 0xe1, 0xfe, 0xff, 0xff, 0xea, - 0x00, 0x40, 0x2d, 0xe9, 0x00, 0x20, 0xa0, 0xe3, 0x02, 0x3c, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3, - 0x28, 0x00, 0x9f, 0xe5, 0x37, 0x00, 0x00, 0xeb, 0x00, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, - 0x00, 0x40, 0x2d, 0xe9, 0x12, 0x2e, 0xa0, 0xe3, 0x06, 0x30, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3, - 0x02, 0x04, 0xa0, 0xe3, 0x2f, 0x00, 0x00, 0xeb, 0x00, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, - 0x00, 0x02, 0x00, 0x02, 0x80, 0x01, 0x90, 0xe0, 0x01, 0x00, 0x00, 0x0a, 0x01, 0x00, 0x50, 0xe2, - 0xfc, 0xff, 0xff, 0xea, 0x1e, 0xff, 0x2f, 0xe1, 0x80, 0x10, 0xa0, 0xe3, 0xf3, 0x06, 0xa0, 0xe3, - 0x00, 0x10, 0x80, 0xe5, 0x00, 0x10, 0xa0, 0xe3, 0x00, 0x10, 0x80, 0xe5, 0x01, 0x10, 0xa0, 0xe3, - 0x04, 0x10, 0x80, 0xe5, 0x00, 0x10, 0x80, 0xe5, 0x0e, 0x34, 0xa0, 0xe3, 0x1c, 0x10, 0x93, 0xe5, - 0x02, 0x1a, 0x81, 0xe3, 0x1c, 0x10, 0x83, 0xe5, 0x58, 0x11, 0x9f, 0xe5, 0x30, 0x10, 0x80, 0xe5, - 0x54, 0x11, 0x9f, 0xe5, 0x34, 0x10, 0x80, 0xe5, 0x38, 0x10, 0x80, 0xe5, 0x3c, 0x10, 0x80, 0xe5, - 0x10, 0x10, 0x90, 0xe5, 0x08, 0x00, 0x90, 0xe5, 0x1e, 0xff, 0x2f, 0xe1, 0xf3, 0x16, 0xa0, 0xe3, - 0x08, 0x00, 0x91, 0xe5, 0x05, 0x00, 0xa0, 0xe3, 0x0c, 0x00, 0x81, 0xe5, 0x10, 0x00, 0x91, 0xe5, - 0x02, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0xff, 0x00, 0xa0, 0xe3, 0x0c, 0x00, 0x81, 0xe5, - 0x10, 0x00, 0x91, 0xe5, 0x02, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0x08, 0x00, 0x91, 0xe5, - 0x10, 0x00, 0x91, 0xe5, 0x01, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0x08, 0x00, 0x91, 0xe5, - 0xff, 0x00, 0x00, 0xe2, 0x1e, 0xff, 0x2f, 0xe1, 0x30, 0x40, 0x2d, 0xe9, 0x00, 0x50, 0xa0, 0xe1, - 0x03, 0x40, 0xa0, 0xe1, 0xa2, 0x02, 0xa0, 0xe1, 0x08, 0x00, 0x00, 0xe2, 0x03, 0x00, 0x80, 0xe2, - 0xd8, 0x10, 0x9f, 0xe5, 0x00, 0x00, 0xc1, 0xe5, 0x01, 0x20, 0xc1, 0xe5, 0xe2, 0xff, 0xff, 0xeb, - 0x01, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x1a, 0x14, 0x00, 0xa0, 0xe3, 0xc4, 0xff, 0xff, 0xeb, - 0x04, 0x20, 0xa0, 0xe1, 0x05, 0x10, 0xa0, 0xe1, 0x02, 0x00, 0xa0, 0xe3, 0x01, 0x00, 0x00, 0xeb, - 0x30, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 0x70, 0x40, 0x2d, 0xe9, 0xf3, 0x46, 0xa0, 0xe3, - 0x00, 0x30, 0xa0, 0xe3, 0x00, 0x00, 0x50, 0xe3, 0x08, 0x00, 0x00, 0x9a, 0x8c, 0x50, 0x9f, 0xe5, - 0x03, 0x60, 0xd5, 0xe7, 0x0c, 0x60, 0x84, 0xe5, 0x10, 0x60, 0x94, 0xe5, 0x02, 0x00, 0x16, 0xe3, - 0xfc, 0xff, 0xff, 0x0a, 0x01, 0x30, 0x83, 0xe2, 0x00, 0x00, 0x53, 0xe1, 0xf7, 0xff, 0xff, 0x3a, - 0xff, 0x30, 0xa0, 0xe3, 0x0c, 0x30, 0x84, 0xe5, 0x08, 0x00, 0x94, 0xe5, 0x10, 0x00, 0x94, 0xe5, - 0x01, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0x08, 0x00, 0x94, 0xe5, 0x00, 0x00, 0xa0, 0xe3, - 0x00, 0x00, 0x52, 0xe3, 0x0b, 0x00, 0x00, 0x9a, 0x10, 0x50, 0x94, 0xe5, 0x02, 0x00, 0x15, 0xe3, - 0xfc, 0xff, 0xff, 0x0a, 0x0c, 0x30, 0x84, 0xe5, 0x10, 0x50, 0x94, 0xe5, 0x01, 0x00, 0x15, 0xe3, - 0xfc, 0xff, 0xff, 0x0a, 0x08, 0x50, 0x94, 0xe5, 0x01, 0x50, 0xc1, 0xe4, 0x01, 0x00, 0x80, 0xe2, - 0x02, 0x00, 0x50, 0xe1, 0xf3, 0xff, 0xff, 0x3a, 0xc8, 0x00, 0xa0, 0xe3, 0x98, 0xff, 0xff, 0xeb, - 0x70, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 0x01, 0x0c, 0x00, 0x02, 0x01, 0x02, 0x00, 0x02, - 0x00, 0x01, 0x00, 0x02 -}; - -struct atmel_private { - void *card; /* Bus dependent structure varies for PCcard */ - int (*present_callback)(void *); /* And callback which uses it */ - char firmware_id[32]; - AtmelFWType firmware_type; - u8 *firmware; - int firmware_length; - struct timer_list management_timer; - struct net_device *dev; - struct device *sys_dev; - struct iw_statistics wstats; - spinlock_t irqlock, timerlock; /* spinlocks */ - enum { BUS_TYPE_PCCARD, BUS_TYPE_PCI } bus_type; - enum { - CARD_TYPE_PARALLEL_FLASH, - CARD_TYPE_SPI_FLASH, - CARD_TYPE_EEPROM - } card_type; - int do_rx_crc; /* If we need to CRC incoming packets */ - int probe_crc; /* set if we don't yet know */ - int crc_ok_cnt, crc_ko_cnt; /* counters for probing */ - u16 rx_desc_head; - u16 tx_desc_free, tx_desc_head, tx_desc_tail, tx_desc_previous; - u16 tx_free_mem, tx_buff_head, tx_buff_tail; - - u16 frag_seq, frag_len, frag_no; - u8 frag_source[6]; - - u8 wep_is_on, default_key, exclude_unencrypted, encryption_level; - u8 group_cipher_suite, pairwise_cipher_suite; - u8 wep_keys[MAX_ENCRYPTION_KEYS][MAX_ENCRYPTION_KEY_SIZE]; - int wep_key_len[MAX_ENCRYPTION_KEYS]; - int use_wpa, radio_on_broken; /* firmware dependent stuff. */ - - u16 host_info_base; - struct host_info_struct { - /* NB this is matched to the hardware, don't change. */ - u8 volatile int_status; - u8 volatile int_mask; - u8 volatile lockout_host; - u8 volatile lockout_mac; - - u16 tx_buff_pos; - u16 tx_buff_size; - u16 tx_desc_pos; - u16 tx_desc_count; - - u16 rx_buff_pos; - u16 rx_buff_size; - u16 rx_desc_pos; - u16 rx_desc_count; - - u16 build_version; - u16 command_pos; - - u16 major_version; - u16 minor_version; - - u16 func_ctrl; - u16 mac_status; - u16 generic_IRQ_type; - u8 reserved[2]; - } host_info; - - enum { - STATION_STATE_SCANNING, - STATION_STATE_JOINNING, - STATION_STATE_AUTHENTICATING, - STATION_STATE_ASSOCIATING, - STATION_STATE_READY, - STATION_STATE_REASSOCIATING, - STATION_STATE_DOWN, - STATION_STATE_MGMT_ERROR - } station_state; - - int operating_mode, power_mode; - time_t last_qual; - int beacons_this_sec; - int channel; - int reg_domain, config_reg_domain; - int tx_rate; - int auto_tx_rate; - int rts_threshold; - int frag_threshold; - int long_retry, short_retry; - int preamble; - int default_beacon_period, beacon_period, listen_interval; - int CurrentAuthentTransactionSeqNum, ExpectedAuthentTransactionSeqNum; - int AuthenticationRequestRetryCnt, AssociationRequestRetryCnt, ReAssociationRequestRetryCnt; - enum { - SITE_SURVEY_IDLE, - SITE_SURVEY_IN_PROGRESS, - SITE_SURVEY_COMPLETED - } site_survey_state; - unsigned long last_survey; - - int station_was_associated, station_is_associated; - int fast_scan; - - struct bss_info { - int channel; - int SSIDsize; - int RSSI; - int UsingWEP; - int preamble; - int beacon_period; - int BSStype; - u8 BSSID[6]; - u8 SSID[MAX_SSID_LENGTH]; - } BSSinfo[MAX_BSS_ENTRIES]; - int BSS_list_entries, current_BSS; - int connect_to_any_BSS; - int SSID_size, new_SSID_size; - u8 CurrentBSSID[6], BSSID[6]; - u8 SSID[MAX_SSID_LENGTH], new_SSID[MAX_SSID_LENGTH]; - u64 last_beacon_timestamp; - u8 rx_buf[MAX_WIRELESS_BODY]; -}; - -static u8 atmel_basic_rates[4] = {0x82, 0x84, 0x0b, 0x16}; - -static const struct { - int reg_domain; - int min, max; - char *name; -} channel_table[] = { { REG_DOMAIN_FCC, 1, 11, "USA" }, - { REG_DOMAIN_DOC, 1, 11, "Canada" }, - { REG_DOMAIN_ETSI, 1, 13, "Europe" }, - { REG_DOMAIN_SPAIN, 10, 11, "Spain" }, - { REG_DOMAIN_FRANCE, 10, 13, "France" }, - { REG_DOMAIN_MKK, 14, 14, "MKK" }, - { REG_DOMAIN_MKK1, 1, 14, "MKK1" }, - { REG_DOMAIN_ISRAEL, 3, 9, "Israel"} }; - -static void build_wpa_mib(struct atmel_private *priv); -static int atmel_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); -static void atmel_copy_to_card(struct net_device *dev, u16 dest, - const unsigned char *src, u16 len); -static void atmel_copy_to_host(struct net_device *dev, unsigned char *dest, - u16 src, u16 len); -static void atmel_set_gcr(struct net_device *dev, u16 mask); -static void atmel_clear_gcr(struct net_device *dev, u16 mask); -static int atmel_lock_mac(struct atmel_private *priv); -static void atmel_wmem32(struct atmel_private *priv, u16 pos, u32 data); -static void atmel_command_irq(struct atmel_private *priv); -static int atmel_validate_channel(struct atmel_private *priv, int channel); -static void atmel_management_frame(struct atmel_private *priv, - struct ieee80211_hdr *header, - u16 frame_len, u8 rssi); -static void atmel_management_timer(u_long a); -static void atmel_send_command(struct atmel_private *priv, int command, - void *cmd, int cmd_size); -static int atmel_send_command_wait(struct atmel_private *priv, int command, - void *cmd, int cmd_size); -static void atmel_transmit_management_frame(struct atmel_private *priv, - struct ieee80211_hdr *header, - u8 *body, int body_len); - -static u8 atmel_get_mib8(struct atmel_private *priv, u8 type, u8 index); -static void atmel_set_mib8(struct atmel_private *priv, u8 type, u8 index, - u8 data); -static void atmel_set_mib16(struct atmel_private *priv, u8 type, u8 index, - u16 data); -static void atmel_set_mib(struct atmel_private *priv, u8 type, u8 index, - u8 *data, int data_len); -static void atmel_get_mib(struct atmel_private *priv, u8 type, u8 index, - u8 *data, int data_len); -static void atmel_scan(struct atmel_private *priv, int specific_ssid); -static void atmel_join_bss(struct atmel_private *priv, int bss_index); -static void atmel_smooth_qual(struct atmel_private *priv); -static void atmel_writeAR(struct net_device *dev, u16 data); -static int probe_atmel_card(struct net_device *dev); -static int reset_atmel_card(struct net_device *dev); -static void atmel_enter_state(struct atmel_private *priv, int new_state); -int atmel_open (struct net_device *dev); - -static inline u16 atmel_hi(struct atmel_private *priv, u16 offset) -{ - return priv->host_info_base + offset; -} - -static inline u16 atmel_co(struct atmel_private *priv, u16 offset) -{ - return priv->host_info.command_pos + offset; -} - -static inline u16 atmel_rx(struct atmel_private *priv, u16 offset, u16 desc) -{ - return priv->host_info.rx_desc_pos + (sizeof(struct rx_desc) * desc) + offset; -} - -static inline u16 atmel_tx(struct atmel_private *priv, u16 offset, u16 desc) -{ - return priv->host_info.tx_desc_pos + (sizeof(struct tx_desc) * desc) + offset; -} - -static inline u8 atmel_read8(struct net_device *dev, u16 offset) -{ - return inb(dev->base_addr + offset); -} - -static inline void atmel_write8(struct net_device *dev, u16 offset, u8 data) -{ - outb(data, dev->base_addr + offset); -} - -static inline u16 atmel_read16(struct net_device *dev, u16 offset) -{ - return inw(dev->base_addr + offset); -} - -static inline void atmel_write16(struct net_device *dev, u16 offset, u16 data) -{ - outw(data, dev->base_addr + offset); -} - -static inline u8 atmel_rmem8(struct atmel_private *priv, u16 pos) -{ - atmel_writeAR(priv->dev, pos); - return atmel_read8(priv->dev, DR); -} - -static inline void atmel_wmem8(struct atmel_private *priv, u16 pos, u16 data) -{ - atmel_writeAR(priv->dev, pos); - atmel_write8(priv->dev, DR, data); -} - -static inline u16 atmel_rmem16(struct atmel_private *priv, u16 pos) -{ - atmel_writeAR(priv->dev, pos); - return atmel_read16(priv->dev, DR); -} - -static inline void atmel_wmem16(struct atmel_private *priv, u16 pos, u16 data) -{ - atmel_writeAR(priv->dev, pos); - atmel_write16(priv->dev, DR, data); -} - -static const struct iw_handler_def atmel_handler_def; - -static void tx_done_irq(struct atmel_private *priv) -{ - int i; - - for (i = 0; - atmel_rmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, priv->tx_desc_head)) == TX_DONE && - i < priv->host_info.tx_desc_count; - i++) { - u8 status = atmel_rmem8(priv, atmel_tx(priv, TX_DESC_STATUS_OFFSET, priv->tx_desc_head)); - u16 msdu_size = atmel_rmem16(priv, atmel_tx(priv, TX_DESC_SIZE_OFFSET, priv->tx_desc_head)); - u8 type = atmel_rmem8(priv, atmel_tx(priv, TX_DESC_PACKET_TYPE_OFFSET, priv->tx_desc_head)); - - atmel_wmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, priv->tx_desc_head), 0); - - priv->tx_free_mem += msdu_size; - priv->tx_desc_free++; - - if (priv->tx_buff_head + msdu_size > (priv->host_info.tx_buff_pos + priv->host_info.tx_buff_size)) - priv->tx_buff_head = 0; - else - priv->tx_buff_head += msdu_size; - - if (priv->tx_desc_head < (priv->host_info.tx_desc_count - 1)) - priv->tx_desc_head++ ; - else - priv->tx_desc_head = 0; - - if (type == TX_PACKET_TYPE_DATA) { - if (status == TX_STATUS_SUCCESS) - priv->dev->stats.tx_packets++; - else - priv->dev->stats.tx_errors++; - netif_wake_queue(priv->dev); - } - } -} - -static u16 find_tx_buff(struct atmel_private *priv, u16 len) -{ - u16 bottom_free = priv->host_info.tx_buff_size - priv->tx_buff_tail; - - if (priv->tx_desc_free == 3 || priv->tx_free_mem < len) - return 0; - - if (bottom_free >= len) - return priv->host_info.tx_buff_pos + priv->tx_buff_tail; - - if (priv->tx_free_mem - bottom_free >= len) { - priv->tx_buff_tail = 0; - return priv->host_info.tx_buff_pos; - } - - return 0; -} - -static void tx_update_descriptor(struct atmel_private *priv, int is_bcast, - u16 len, u16 buff, u8 type) -{ - atmel_wmem16(priv, atmel_tx(priv, TX_DESC_POS_OFFSET, priv->tx_desc_tail), buff); - atmel_wmem16(priv, atmel_tx(priv, TX_DESC_SIZE_OFFSET, priv->tx_desc_tail), len); - if (!priv->use_wpa) - atmel_wmem16(priv, atmel_tx(priv, TX_DESC_HOST_LENGTH_OFFSET, priv->tx_desc_tail), len); - atmel_wmem8(priv, atmel_tx(priv, TX_DESC_PACKET_TYPE_OFFSET, priv->tx_desc_tail), type); - atmel_wmem8(priv, atmel_tx(priv, TX_DESC_RATE_OFFSET, priv->tx_desc_tail), priv->tx_rate); - atmel_wmem8(priv, atmel_tx(priv, TX_DESC_RETRY_OFFSET, priv->tx_desc_tail), 0); - if (priv->use_wpa) { - int cipher_type, cipher_length; - if (is_bcast) { - cipher_type = priv->group_cipher_suite; - if (cipher_type == CIPHER_SUITE_WEP_64 || - cipher_type == CIPHER_SUITE_WEP_128) - cipher_length = 8; - else if (cipher_type == CIPHER_SUITE_TKIP) - cipher_length = 12; - else if (priv->pairwise_cipher_suite == CIPHER_SUITE_WEP_64 || - priv->pairwise_cipher_suite == CIPHER_SUITE_WEP_128) { - cipher_type = priv->pairwise_cipher_suite; - cipher_length = 8; - } else { - cipher_type = CIPHER_SUITE_NONE; - cipher_length = 0; - } - } else { - cipher_type = priv->pairwise_cipher_suite; - if (cipher_type == CIPHER_SUITE_WEP_64 || - cipher_type == CIPHER_SUITE_WEP_128) - cipher_length = 8; - else if (cipher_type == CIPHER_SUITE_TKIP) - cipher_length = 12; - else if (priv->group_cipher_suite == CIPHER_SUITE_WEP_64 || - priv->group_cipher_suite == CIPHER_SUITE_WEP_128) { - cipher_type = priv->group_cipher_suite; - cipher_length = 8; - } else { - cipher_type = CIPHER_SUITE_NONE; - cipher_length = 0; - } - } - - atmel_wmem8(priv, atmel_tx(priv, TX_DESC_CIPHER_TYPE_OFFSET, priv->tx_desc_tail), - cipher_type); - atmel_wmem8(priv, atmel_tx(priv, TX_DESC_CIPHER_LENGTH_OFFSET, priv->tx_desc_tail), - cipher_length); - } - atmel_wmem32(priv, atmel_tx(priv, TX_DESC_NEXT_OFFSET, priv->tx_desc_tail), 0x80000000L); - atmel_wmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, priv->tx_desc_tail), TX_FIRM_OWN); - if (priv->tx_desc_previous != priv->tx_desc_tail) - atmel_wmem32(priv, atmel_tx(priv, TX_DESC_NEXT_OFFSET, priv->tx_desc_previous), 0); - priv->tx_desc_previous = priv->tx_desc_tail; - if (priv->tx_desc_tail < (priv->host_info.tx_desc_count - 1)) - priv->tx_desc_tail++; - else - priv->tx_desc_tail = 0; - priv->tx_desc_free--; - priv->tx_free_mem -= len; -} - -static netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev) -{ - static const u8 SNAP_RFC1024[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; - struct atmel_private *priv = netdev_priv(dev); - struct ieee80211_hdr header; - unsigned long flags; - u16 buff, frame_ctl, len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN; - - if (priv->card && priv->present_callback && - !(*priv->present_callback)(priv->card)) { - dev->stats.tx_errors++; - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - - if (priv->station_state != STATION_STATE_READY) { - dev->stats.tx_errors++; - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - - /* first ensure the timer func cannot run */ - spin_lock_bh(&priv->timerlock); - /* then stop the hardware ISR */ - spin_lock_irqsave(&priv->irqlock, flags); - /* nb doing the above in the opposite order will deadlock */ - - /* The Wireless Header is 30 bytes. In the Ethernet packet we "cut" the - 12 first bytes (containing DA/SA) and put them in the appropriate - fields of the Wireless Header. Thus the packet length is then the - initial + 18 (+30-12) */ - - if (!(buff = find_tx_buff(priv, len + 18))) { - dev->stats.tx_dropped++; - spin_unlock_irqrestore(&priv->irqlock, flags); - spin_unlock_bh(&priv->timerlock); - netif_stop_queue(dev); - return NETDEV_TX_BUSY; - } - - frame_ctl = IEEE80211_FTYPE_DATA; - header.duration_id = 0; - header.seq_ctrl = 0; - if (priv->wep_is_on) - frame_ctl |= IEEE80211_FCTL_PROTECTED; - if (priv->operating_mode == IW_MODE_ADHOC) { - skb_copy_from_linear_data(skb, &header.addr1, ETH_ALEN); - memcpy(&header.addr2, dev->dev_addr, ETH_ALEN); - memcpy(&header.addr3, priv->BSSID, ETH_ALEN); - } else { - frame_ctl |= IEEE80211_FCTL_TODS; - memcpy(&header.addr1, priv->CurrentBSSID, ETH_ALEN); - memcpy(&header.addr2, dev->dev_addr, ETH_ALEN); - skb_copy_from_linear_data(skb, &header.addr3, ETH_ALEN); - } - - if (priv->use_wpa) - memcpy(&header.addr4, SNAP_RFC1024, ETH_ALEN); - - header.frame_control = cpu_to_le16(frame_ctl); - /* Copy the wireless header into the card */ - atmel_copy_to_card(dev, buff, (unsigned char *)&header, DATA_FRAME_WS_HEADER_SIZE); - /* Copy the packet sans its 802.3 header addresses which have been replaced */ - atmel_copy_to_card(dev, buff + DATA_FRAME_WS_HEADER_SIZE, skb->data + 12, len - 12); - priv->tx_buff_tail += len - 12 + DATA_FRAME_WS_HEADER_SIZE; - - /* low bit of first byte of destination tells us if broadcast */ - tx_update_descriptor(priv, *(skb->data) & 0x01, len + 18, buff, TX_PACKET_TYPE_DATA); - dev->stats.tx_bytes += len; - - spin_unlock_irqrestore(&priv->irqlock, flags); - spin_unlock_bh(&priv->timerlock); - dev_kfree_skb(skb); - - return NETDEV_TX_OK; -} - -static void atmel_transmit_management_frame(struct atmel_private *priv, - struct ieee80211_hdr *header, - u8 *body, int body_len) -{ - u16 buff; - int len = MGMT_FRAME_BODY_OFFSET + body_len; - - if (!(buff = find_tx_buff(priv, len))) - return; - - atmel_copy_to_card(priv->dev, buff, (u8 *)header, MGMT_FRAME_BODY_OFFSET); - atmel_copy_to_card(priv->dev, buff + MGMT_FRAME_BODY_OFFSET, body, body_len); - priv->tx_buff_tail += len; - tx_update_descriptor(priv, header->addr1[0] & 0x01, len, buff, TX_PACKET_TYPE_MGMT); -} - -static void fast_rx_path(struct atmel_private *priv, - struct ieee80211_hdr *header, - u16 msdu_size, u16 rx_packet_loc, u32 crc) -{ - /* fast path: unfragmented packet copy directly into skbuf */ - u8 mac4[6]; - struct sk_buff *skb; - unsigned char *skbp; - - /* get the final, mac 4 header field, this tells us encapsulation */ - atmel_copy_to_host(priv->dev, mac4, rx_packet_loc + 24, 6); - msdu_size -= 6; - - if (priv->do_rx_crc) { - crc = crc32_le(crc, mac4, 6); - msdu_size -= 4; - } - - if (!(skb = dev_alloc_skb(msdu_size + 14))) { - priv->dev->stats.rx_dropped++; - return; - } - - skb_reserve(skb, 2); - skbp = skb_put(skb, msdu_size + 12); - atmel_copy_to_host(priv->dev, skbp + 12, rx_packet_loc + 30, msdu_size); - - if (priv->do_rx_crc) { - u32 netcrc; - crc = crc32_le(crc, skbp + 12, msdu_size); - atmel_copy_to_host(priv->dev, (void *)&netcrc, rx_packet_loc + 30 + msdu_size, 4); - if ((crc ^ 0xffffffff) != netcrc) { - priv->dev->stats.rx_crc_errors++; - dev_kfree_skb(skb); - return; - } - } - - memcpy(skbp, header->addr1, ETH_ALEN); /* destination address */ - if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS) - memcpy(&skbp[ETH_ALEN], header->addr3, ETH_ALEN); - else - memcpy(&skbp[ETH_ALEN], header->addr2, ETH_ALEN); /* source address */ - - skb->protocol = eth_type_trans(skb, priv->dev); - skb->ip_summed = CHECKSUM_NONE; - netif_rx(skb); - priv->dev->stats.rx_bytes += 12 + msdu_size; - priv->dev->stats.rx_packets++; -} - -/* Test to see if the packet in card memory at packet_loc has a valid CRC - It doesn't matter that this is slow: it is only used to proble the first few - packets. */ -static int probe_crc(struct atmel_private *priv, u16 packet_loc, u16 msdu_size) -{ - int i = msdu_size - 4; - u32 netcrc, crc = 0xffffffff; - - if (msdu_size < 4) - return 0; - - atmel_copy_to_host(priv->dev, (void *)&netcrc, packet_loc + i, 4); - - atmel_writeAR(priv->dev, packet_loc); - while (i--) { - u8 octet = atmel_read8(priv->dev, DR); - crc = crc32_le(crc, &octet, 1); - } - - return (crc ^ 0xffffffff) == netcrc; -} - -static void frag_rx_path(struct atmel_private *priv, - struct ieee80211_hdr *header, - u16 msdu_size, u16 rx_packet_loc, u32 crc, u16 seq_no, - u8 frag_no, int more_frags) -{ - u8 mac4[ETH_ALEN]; - u8 source[ETH_ALEN]; - struct sk_buff *skb; - - if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS) - memcpy(source, header->addr3, ETH_ALEN); - else - memcpy(source, header->addr2, ETH_ALEN); - - rx_packet_loc += 24; /* skip header */ - - if (priv->do_rx_crc) - msdu_size -= 4; - - if (frag_no == 0) { /* first fragment */ - atmel_copy_to_host(priv->dev, mac4, rx_packet_loc, ETH_ALEN); - msdu_size -= ETH_ALEN; - rx_packet_loc += ETH_ALEN; - - if (priv->do_rx_crc) - crc = crc32_le(crc, mac4, 6); - - priv->frag_seq = seq_no; - priv->frag_no = 1; - priv->frag_len = msdu_size; - memcpy(priv->frag_source, source, ETH_ALEN); - memcpy(&priv->rx_buf[ETH_ALEN], source, ETH_ALEN); - memcpy(priv->rx_buf, header->addr1, ETH_ALEN); - - atmel_copy_to_host(priv->dev, &priv->rx_buf[12], rx_packet_loc, msdu_size); - - if (priv->do_rx_crc) { - u32 netcrc; - crc = crc32_le(crc, &priv->rx_buf[12], msdu_size); - atmel_copy_to_host(priv->dev, (void *)&netcrc, rx_packet_loc + msdu_size, 4); - if ((crc ^ 0xffffffff) != netcrc) { - priv->dev->stats.rx_crc_errors++; - eth_broadcast_addr(priv->frag_source); - } - } - - } else if (priv->frag_no == frag_no && - priv->frag_seq == seq_no && - memcmp(priv->frag_source, source, ETH_ALEN) == 0) { - - atmel_copy_to_host(priv->dev, &priv->rx_buf[12 + priv->frag_len], - rx_packet_loc, msdu_size); - if (priv->do_rx_crc) { - u32 netcrc; - crc = crc32_le(crc, - &priv->rx_buf[12 + priv->frag_len], - msdu_size); - atmel_copy_to_host(priv->dev, (void *)&netcrc, rx_packet_loc + msdu_size, 4); - if ((crc ^ 0xffffffff) != netcrc) { - priv->dev->stats.rx_crc_errors++; - eth_broadcast_addr(priv->frag_source); - more_frags = 1; /* don't send broken assembly */ - } - } - - priv->frag_len += msdu_size; - priv->frag_no++; - - if (!more_frags) { /* last one */ - eth_broadcast_addr(priv->frag_source); - if (!(skb = dev_alloc_skb(priv->frag_len + 14))) { - priv->dev->stats.rx_dropped++; - } else { - skb_reserve(skb, 2); - memcpy(skb_put(skb, priv->frag_len + 12), - priv->rx_buf, - priv->frag_len + 12); - skb->protocol = eth_type_trans(skb, priv->dev); - skb->ip_summed = CHECKSUM_NONE; - netif_rx(skb); - priv->dev->stats.rx_bytes += priv->frag_len + 12; - priv->dev->stats.rx_packets++; - } - } - } else - priv->wstats.discard.fragment++; -} - -static void rx_done_irq(struct atmel_private *priv) -{ - int i; - struct ieee80211_hdr header; - - for (i = 0; - atmel_rmem8(priv, atmel_rx(priv, RX_DESC_FLAGS_OFFSET, priv->rx_desc_head)) == RX_DESC_FLAG_VALID && - i < priv->host_info.rx_desc_count; - i++) { - - u16 msdu_size, rx_packet_loc, frame_ctl, seq_control; - u8 status = atmel_rmem8(priv, atmel_rx(priv, RX_DESC_STATUS_OFFSET, priv->rx_desc_head)); - u32 crc = 0xffffffff; - - if (status != RX_STATUS_SUCCESS) { - if (status == 0xc1) /* determined by experiment */ - priv->wstats.discard.nwid++; - else - priv->dev->stats.rx_errors++; - goto next; - } - - msdu_size = atmel_rmem16(priv, atmel_rx(priv, RX_DESC_MSDU_SIZE_OFFSET, priv->rx_desc_head)); - rx_packet_loc = atmel_rmem16(priv, atmel_rx(priv, RX_DESC_MSDU_POS_OFFSET, priv->rx_desc_head)); - - if (msdu_size < 30) { - priv->dev->stats.rx_errors++; - goto next; - } - - /* Get header as far as end of seq_ctrl */ - atmel_copy_to_host(priv->dev, (char *)&header, rx_packet_loc, 24); - frame_ctl = le16_to_cpu(header.frame_control); - seq_control = le16_to_cpu(header.seq_ctrl); - - /* probe for CRC use here if needed once five packets have - arrived with the same crc status, we assume we know what's - happening and stop probing */ - if (priv->probe_crc) { - if (!priv->wep_is_on || !(frame_ctl & IEEE80211_FCTL_PROTECTED)) { - priv->do_rx_crc = probe_crc(priv, rx_packet_loc, msdu_size); - } else { - priv->do_rx_crc = probe_crc(priv, rx_packet_loc + 24, msdu_size - 24); - } - if (priv->do_rx_crc) { - if (priv->crc_ok_cnt++ > 5) - priv->probe_crc = 0; - } else { - if (priv->crc_ko_cnt++ > 5) - priv->probe_crc = 0; - } - } - - /* don't CRC header when WEP in use */ - if (priv->do_rx_crc && (!priv->wep_is_on || !(frame_ctl & IEEE80211_FCTL_PROTECTED))) { - crc = crc32_le(0xffffffff, (unsigned char *)&header, 24); - } - msdu_size -= 24; /* header */ - - if ((frame_ctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) { - int more_fragments = frame_ctl & IEEE80211_FCTL_MOREFRAGS; - u8 packet_fragment_no = seq_control & IEEE80211_SCTL_FRAG; - u16 packet_sequence_no = (seq_control & IEEE80211_SCTL_SEQ) >> 4; - - if (!more_fragments && packet_fragment_no == 0) { - fast_rx_path(priv, &header, msdu_size, rx_packet_loc, crc); - } else { - frag_rx_path(priv, &header, msdu_size, rx_packet_loc, crc, - packet_sequence_no, packet_fragment_no, more_fragments); - } - } - - if ((frame_ctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) { - /* copy rest of packet into buffer */ - atmel_copy_to_host(priv->dev, (unsigned char *)&priv->rx_buf, rx_packet_loc + 24, msdu_size); - - /* we use the same buffer for frag reassembly and control packets */ - eth_broadcast_addr(priv->frag_source); - - if (priv->do_rx_crc) { - /* last 4 octets is crc */ - msdu_size -= 4; - crc = crc32_le(crc, (unsigned char *)&priv->rx_buf, msdu_size); - if ((crc ^ 0xffffffff) != (*((u32 *)&priv->rx_buf[msdu_size]))) { - priv->dev->stats.rx_crc_errors++; - goto next; - } - } - - atmel_management_frame(priv, &header, msdu_size, - atmel_rmem8(priv, atmel_rx(priv, RX_DESC_RSSI_OFFSET, priv->rx_desc_head))); - } - -next: - /* release descriptor */ - atmel_wmem8(priv, atmel_rx(priv, RX_DESC_FLAGS_OFFSET, priv->rx_desc_head), RX_DESC_FLAG_CONSUMED); - - if (priv->rx_desc_head < (priv->host_info.rx_desc_count - 1)) - priv->rx_desc_head++; - else - priv->rx_desc_head = 0; - } -} - -static irqreturn_t service_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = (struct net_device *) dev_id; - struct atmel_private *priv = netdev_priv(dev); - u8 isr; - int i = -1; - static const u8 irq_order[] = { - ISR_OUT_OF_RANGE, - ISR_RxCOMPLETE, - ISR_TxCOMPLETE, - ISR_RxFRAMELOST, - ISR_FATAL_ERROR, - ISR_COMMAND_COMPLETE, - ISR_IBSS_MERGE, - ISR_GENERIC_IRQ - }; - - if (priv->card && priv->present_callback && - !(*priv->present_callback)(priv->card)) - return IRQ_HANDLED; - - /* In this state upper-level code assumes it can mess with - the card unhampered by interrupts which may change register state. - Note that even though the card shouldn't generate interrupts - the inturrupt line may be shared. This allows card setup - to go on without disabling interrupts for a long time. */ - if (priv->station_state == STATION_STATE_DOWN) - return IRQ_NONE; - - atmel_clear_gcr(dev, GCR_ENINT); /* disable interrupts */ - - while (1) { - if (!atmel_lock_mac(priv)) { - /* failed to contact card */ - printk(KERN_ALERT "%s: failed to contact MAC.\n", dev->name); - return IRQ_HANDLED; - } - - isr = atmel_rmem8(priv, atmel_hi(priv, IFACE_INT_STATUS_OFFSET)); - atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 0); - - if (!isr) { - atmel_set_gcr(dev, GCR_ENINT); /* enable interrupts */ - return i == -1 ? IRQ_NONE : IRQ_HANDLED; - } - - atmel_set_gcr(dev, GCR_ACKINT); /* acknowledge interrupt */ - - for (i = 0; i < ARRAY_SIZE(irq_order); i++) - if (isr & irq_order[i]) - break; - - if (!atmel_lock_mac(priv)) { - /* failed to contact card */ - printk(KERN_ALERT "%s: failed to contact MAC.\n", dev->name); - return IRQ_HANDLED; - } - - isr = atmel_rmem8(priv, atmel_hi(priv, IFACE_INT_STATUS_OFFSET)); - isr ^= irq_order[i]; - atmel_wmem8(priv, atmel_hi(priv, IFACE_INT_STATUS_OFFSET), isr); - atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 0); - - switch (irq_order[i]) { - - case ISR_OUT_OF_RANGE: - if (priv->operating_mode == IW_MODE_INFRA && - priv->station_state == STATION_STATE_READY) { - priv->station_is_associated = 0; - atmel_scan(priv, 1); - } - break; - - case ISR_RxFRAMELOST: - priv->wstats.discard.misc++; - /* fall through */ - case ISR_RxCOMPLETE: - rx_done_irq(priv); - break; - - case ISR_TxCOMPLETE: - tx_done_irq(priv); - break; - - case ISR_FATAL_ERROR: - printk(KERN_ALERT "%s: *** FATAL error interrupt ***\n", dev->name); - atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); - break; - - case ISR_COMMAND_COMPLETE: - atmel_command_irq(priv); - break; - - case ISR_IBSS_MERGE: - atmel_get_mib(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_CUR_BSSID_POS, - priv->CurrentBSSID, 6); - /* The WPA stuff cares about the current AP address */ - if (priv->use_wpa) - build_wpa_mib(priv); - break; - case ISR_GENERIC_IRQ: - printk(KERN_INFO "%s: Generic_irq received.\n", dev->name); - break; - } - } -} - -static struct iw_statistics *atmel_get_wireless_stats(struct net_device *dev) -{ - struct atmel_private *priv = netdev_priv(dev); - - /* update the link quality here in case we are seeing no beacons - at all to drive the process */ - atmel_smooth_qual(priv); - - priv->wstats.status = priv->station_state; - - if (priv->operating_mode == IW_MODE_INFRA) { - if (priv->station_state != STATION_STATE_READY) { - priv->wstats.qual.qual = 0; - priv->wstats.qual.level = 0; - priv->wstats.qual.updated = (IW_QUAL_QUAL_INVALID - | IW_QUAL_LEVEL_INVALID); - } - priv->wstats.qual.noise = 0; - priv->wstats.qual.updated |= IW_QUAL_NOISE_INVALID; - } else { - /* Quality levels cannot be determined in ad-hoc mode, - because we can 'hear' more that one remote station. */ - priv->wstats.qual.qual = 0; - priv->wstats.qual.level = 0; - priv->wstats.qual.noise = 0; - priv->wstats.qual.updated = IW_QUAL_QUAL_INVALID - | IW_QUAL_LEVEL_INVALID - | IW_QUAL_NOISE_INVALID; - priv->wstats.miss.beacon = 0; - } - - return &priv->wstats; -} - -static int atmel_change_mtu(struct net_device *dev, int new_mtu) -{ - if ((new_mtu < 68) || (new_mtu > 2312)) - return -EINVAL; - dev->mtu = new_mtu; - return 0; -} - -static int atmel_set_mac_address(struct net_device *dev, void *p) -{ - struct sockaddr *addr = p; - - memcpy (dev->dev_addr, addr->sa_data, dev->addr_len); - return atmel_open(dev); -} - -EXPORT_SYMBOL(atmel_open); - -int atmel_open(struct net_device *dev) -{ - struct atmel_private *priv = netdev_priv(dev); - int i, channel, err; - - /* any scheduled timer is no longer needed and might screw things up.. */ - del_timer_sync(&priv->management_timer); - - /* Interrupts will not touch the card once in this state... */ - priv->station_state = STATION_STATE_DOWN; - - if (priv->new_SSID_size) { - memcpy(priv->SSID, priv->new_SSID, priv->new_SSID_size); - priv->SSID_size = priv->new_SSID_size; - priv->new_SSID_size = 0; - } - priv->BSS_list_entries = 0; - - priv->AuthenticationRequestRetryCnt = 0; - priv->AssociationRequestRetryCnt = 0; - priv->ReAssociationRequestRetryCnt = 0; - priv->CurrentAuthentTransactionSeqNum = 0x0001; - priv->ExpectedAuthentTransactionSeqNum = 0x0002; - - priv->site_survey_state = SITE_SURVEY_IDLE; - priv->station_is_associated = 0; - - err = reset_atmel_card(dev); - if (err) - return err; - - if (priv->config_reg_domain) { - priv->reg_domain = priv->config_reg_domain; - atmel_set_mib8(priv, Phy_Mib_Type, PHY_MIB_REG_DOMAIN_POS, priv->reg_domain); - } else { - priv->reg_domain = atmel_get_mib8(priv, Phy_Mib_Type, PHY_MIB_REG_DOMAIN_POS); - for (i = 0; i < ARRAY_SIZE(channel_table); i++) - if (priv->reg_domain == channel_table[i].reg_domain) - break; - if (i == ARRAY_SIZE(channel_table)) { - priv->reg_domain = REG_DOMAIN_MKK1; - printk(KERN_ALERT "%s: failed to get regulatory domain: assuming MKK1.\n", dev->name); - } - } - - if ((channel = atmel_validate_channel(priv, priv->channel))) - priv->channel = channel; - - /* this moves station_state on.... */ - atmel_scan(priv, 1); - - atmel_set_gcr(priv->dev, GCR_ENINT); /* enable interrupts */ - return 0; -} - -static int atmel_close(struct net_device *dev) -{ - struct atmel_private *priv = netdev_priv(dev); - - /* Send event to userspace that we are disassociating */ - if (priv->station_state == STATION_STATE_READY) { - union iwreq_data wrqu; - - wrqu.data.length = 0; - wrqu.data.flags = 0; - wrqu.ap_addr.sa_family = ARPHRD_ETHER; - eth_zero_addr(wrqu.ap_addr.sa_data); - wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL); - } - - atmel_enter_state(priv, STATION_STATE_DOWN); - - if (priv->bus_type == BUS_TYPE_PCCARD) - atmel_write16(dev, GCR, 0x0060); - atmel_write16(dev, GCR, 0x0040); - return 0; -} - -static int atmel_validate_channel(struct atmel_private *priv, int channel) -{ - /* check that channel is OK, if so return zero, - else return suitable default channel */ - int i; - - for (i = 0; i < ARRAY_SIZE(channel_table); i++) - if (priv->reg_domain == channel_table[i].reg_domain) { - if (channel >= channel_table[i].min && - channel <= channel_table[i].max) - return 0; - else - return channel_table[i].min; - } - return 0; -} - -static int atmel_proc_show(struct seq_file *m, void *v) -{ - struct atmel_private *priv = m->private; - int i; - char *s, *r, *c; - - seq_printf(m, "Driver version:\t\t%d.%d\n", DRIVER_MAJOR, DRIVER_MINOR); - - if (priv->station_state != STATION_STATE_DOWN) { - seq_printf(m, - "Firmware version:\t%d.%d build %d\n" - "Firmware location:\t", - priv->host_info.major_version, - priv->host_info.minor_version, - priv->host_info.build_version); - - if (priv->card_type != CARD_TYPE_EEPROM) - seq_puts(m, "on card\n"); - else if (priv->firmware) - seq_printf(m, "%s loaded by host\n", priv->firmware_id); - else - seq_printf(m, "%s loaded by hotplug\n", priv->firmware_id); - - switch (priv->card_type) { - case CARD_TYPE_PARALLEL_FLASH: - c = "Parallel flash"; - break; - case CARD_TYPE_SPI_FLASH: - c = "SPI flash\n"; - break; - case CARD_TYPE_EEPROM: - c = "EEPROM"; - break; - default: - c = ""; - } - - r = ""; - for (i = 0; i < ARRAY_SIZE(channel_table); i++) - if (priv->reg_domain == channel_table[i].reg_domain) - r = channel_table[i].name; - - seq_printf(m, "MAC memory type:\t%s\n", c); - seq_printf(m, "Regulatory domain:\t%s\n", r); - seq_printf(m, "Host CRC checking:\t%s\n", - priv->do_rx_crc ? "On" : "Off"); - seq_printf(m, "WPA-capable firmware:\t%s\n", - priv->use_wpa ? "Yes" : "No"); - } - - switch (priv->station_state) { - case STATION_STATE_SCANNING: - s = "Scanning"; - break; - case STATION_STATE_JOINNING: - s = "Joining"; - break; - case STATION_STATE_AUTHENTICATING: - s = "Authenticating"; - break; - case STATION_STATE_ASSOCIATING: - s = "Associating"; - break; - case STATION_STATE_READY: - s = "Ready"; - break; - case STATION_STATE_REASSOCIATING: - s = "Reassociating"; - break; - case STATION_STATE_MGMT_ERROR: - s = "Management error"; - break; - case STATION_STATE_DOWN: - s = "Down"; - break; - default: - s = ""; - } - - seq_printf(m, "Current state:\t\t%s\n", s); - return 0; -} - -static int atmel_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, atmel_proc_show, PDE_DATA(inode)); -} - -static const struct file_operations atmel_proc_fops = { - .open = atmel_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static const struct net_device_ops atmel_netdev_ops = { - .ndo_open = atmel_open, - .ndo_stop = atmel_close, - .ndo_change_mtu = atmel_change_mtu, - .ndo_set_mac_address = atmel_set_mac_address, - .ndo_start_xmit = start_tx, - .ndo_do_ioctl = atmel_ioctl, - .ndo_validate_addr = eth_validate_addr, -}; - -struct net_device *init_atmel_card(unsigned short irq, unsigned long port, - const AtmelFWType fw_type, - struct device *sys_dev, - int (*card_present)(void *), void *card) -{ - struct net_device *dev; - struct atmel_private *priv; - int rc; - - /* Create the network device object. */ - dev = alloc_etherdev(sizeof(*priv)); - if (!dev) - return NULL; - - if (dev_alloc_name(dev, dev->name) < 0) { - printk(KERN_ERR "atmel: Couldn't get name!\n"); - goto err_out_free; - } - - priv = netdev_priv(dev); - priv->dev = dev; - priv->sys_dev = sys_dev; - priv->present_callback = card_present; - priv->card = card; - priv->firmware = NULL; - priv->firmware_id[0] = '\0'; - priv->firmware_type = fw_type; - if (firmware) /* module parameter */ - strcpy(priv->firmware_id, firmware); - priv->bus_type = card_present ? BUS_TYPE_PCCARD : BUS_TYPE_PCI; - priv->station_state = STATION_STATE_DOWN; - priv->do_rx_crc = 0; - /* For PCMCIA cards, some chips need CRC, some don't - so we have to probe. */ - if (priv->bus_type == BUS_TYPE_PCCARD) { - priv->probe_crc = 1; - priv->crc_ok_cnt = priv->crc_ko_cnt = 0; - } else - priv->probe_crc = 0; - priv->last_qual = jiffies; - priv->last_beacon_timestamp = 0; - memset(priv->frag_source, 0xff, sizeof(priv->frag_source)); - eth_zero_addr(priv->BSSID); - priv->CurrentBSSID[0] = 0xFF; /* Initialize to something invalid.... */ - priv->station_was_associated = 0; - - priv->last_survey = jiffies; - priv->preamble = LONG_PREAMBLE; - priv->operating_mode = IW_MODE_INFRA; - priv->connect_to_any_BSS = 0; - priv->config_reg_domain = 0; - priv->reg_domain = 0; - priv->tx_rate = 3; - priv->auto_tx_rate = 1; - priv->channel = 4; - priv->power_mode = 0; - priv->SSID[0] = '\0'; - priv->SSID_size = 0; - priv->new_SSID_size = 0; - priv->frag_threshold = 2346; - priv->rts_threshold = 2347; - priv->short_retry = 7; - priv->long_retry = 4; - - priv->wep_is_on = 0; - priv->default_key = 0; - priv->encryption_level = 0; - priv->exclude_unencrypted = 0; - priv->group_cipher_suite = priv->pairwise_cipher_suite = CIPHER_SUITE_NONE; - priv->use_wpa = 0; - memset(priv->wep_keys, 0, sizeof(priv->wep_keys)); - memset(priv->wep_key_len, 0, sizeof(priv->wep_key_len)); - - priv->default_beacon_period = priv->beacon_period = 100; - priv->listen_interval = 1; - - init_timer(&priv->management_timer); - spin_lock_init(&priv->irqlock); - spin_lock_init(&priv->timerlock); - priv->management_timer.function = atmel_management_timer; - priv->management_timer.data = (unsigned long) dev; - - dev->netdev_ops = &atmel_netdev_ops; - dev->wireless_handlers = &atmel_handler_def; - dev->irq = irq; - dev->base_addr = port; - - SET_NETDEV_DEV(dev, sys_dev); - - if ((rc = request_irq(dev->irq, service_interrupt, IRQF_SHARED, dev->name, dev))) { - printk(KERN_ERR "%s: register interrupt %d failed, rc %d\n", dev->name, irq, rc); - goto err_out_free; - } - - if (!request_region(dev->base_addr, 32, - priv->bus_type == BUS_TYPE_PCCARD ? "atmel_cs" : "atmel_pci")) { - goto err_out_irq; - } - - if (register_netdev(dev)) - goto err_out_res; - - if (!probe_atmel_card(dev)) { - unregister_netdev(dev); - goto err_out_res; - } - - netif_carrier_off(dev); - - if (!proc_create_data("driver/atmel", 0, NULL, &atmel_proc_fops, priv)) - printk(KERN_WARNING "atmel: unable to create /proc entry.\n"); - - printk(KERN_INFO "%s: Atmel at76c50x. Version %d.%d. MAC %pM\n", - dev->name, DRIVER_MAJOR, DRIVER_MINOR, dev->dev_addr); - - return dev; - -err_out_res: - release_region(dev->base_addr, 32); -err_out_irq: - free_irq(dev->irq, dev); -err_out_free: - free_netdev(dev); - return NULL; -} - -EXPORT_SYMBOL(init_atmel_card); - -void stop_atmel_card(struct net_device *dev) -{ - struct atmel_private *priv = netdev_priv(dev); - - /* put a brick on it... */ - if (priv->bus_type == BUS_TYPE_PCCARD) - atmel_write16(dev, GCR, 0x0060); - atmel_write16(dev, GCR, 0x0040); - - del_timer_sync(&priv->management_timer); - unregister_netdev(dev); - remove_proc_entry("driver/atmel", NULL); - free_irq(dev->irq, dev); - kfree(priv->firmware); - release_region(dev->base_addr, 32); - free_netdev(dev); -} - -EXPORT_SYMBOL(stop_atmel_card); - -static int atmel_set_essid(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *dwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - - /* Check if we asked for `any' */ - if (dwrq->flags == 0) { - priv->connect_to_any_BSS = 1; - } else { - int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; - - priv->connect_to_any_BSS = 0; - - /* Check the size of the string */ - if (dwrq->length > MAX_SSID_LENGTH) - return -E2BIG; - if (index != 0) - return -EINVAL; - - memcpy(priv->new_SSID, extra, dwrq->length); - priv->new_SSID_size = dwrq->length; - } - - return -EINPROGRESS; -} - -static int atmel_get_essid(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *dwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - - /* Get the current SSID */ - if (priv->new_SSID_size != 0) { - memcpy(extra, priv->new_SSID, priv->new_SSID_size); - dwrq->length = priv->new_SSID_size; - } else { - memcpy(extra, priv->SSID, priv->SSID_size); - dwrq->length = priv->SSID_size; - } - - dwrq->flags = !priv->connect_to_any_BSS; /* active */ - - return 0; -} - -static int atmel_get_wap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *awrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - memcpy(awrq->sa_data, priv->CurrentBSSID, ETH_ALEN); - awrq->sa_family = ARPHRD_ETHER; - - return 0; -} - -static int atmel_set_encode(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *dwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - - /* Basic checking: do we have a key to set ? - * Note : with the new API, it's impossible to get a NULL pointer. - * Therefore, we need to check a key size == 0 instead. - * New version of iwconfig properly set the IW_ENCODE_NOKEY flag - * when no key is present (only change flags), but older versions - * don't do it. - Jean II */ - if (dwrq->length > 0) { - int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; - int current_index = priv->default_key; - /* Check the size of the key */ - if (dwrq->length > 13) { - return -EINVAL; - } - /* Check the index (none -> use current) */ - if (index < 0 || index >= 4) - index = current_index; - else - priv->default_key = index; - /* Set the length */ - if (dwrq->length > 5) - priv->wep_key_len[index] = 13; - else - if (dwrq->length > 0) - priv->wep_key_len[index] = 5; - else - /* Disable the key */ - priv->wep_key_len[index] = 0; - /* Check if the key is not marked as invalid */ - if (!(dwrq->flags & IW_ENCODE_NOKEY)) { - /* Cleanup */ - memset(priv->wep_keys[index], 0, 13); - /* Copy the key in the driver */ - memcpy(priv->wep_keys[index], extra, dwrq->length); - } - /* WE specify that if a valid key is set, encryption - * should be enabled (user may turn it off later) - * This is also how "iwconfig ethX key on" works */ - if (index == current_index && - priv->wep_key_len[index] > 0) { - priv->wep_is_on = 1; - priv->exclude_unencrypted = 1; - if (priv->wep_key_len[index] > 5) { - priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_128; - priv->encryption_level = 2; - } else { - priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_64; - priv->encryption_level = 1; - } - } - } else { - /* Do we want to just set the transmit key index ? */ - int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; - if (index >= 0 && index < 4) { - priv->default_key = index; - } else - /* Don't complain if only change the mode */ - if (!(dwrq->flags & IW_ENCODE_MODE)) - return -EINVAL; - } - /* Read the flags */ - if (dwrq->flags & IW_ENCODE_DISABLED) { - priv->wep_is_on = 0; - priv->encryption_level = 0; - priv->pairwise_cipher_suite = CIPHER_SUITE_NONE; - } else { - priv->wep_is_on = 1; - if (priv->wep_key_len[priv->default_key] > 5) { - priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_128; - priv->encryption_level = 2; - } else { - priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_64; - priv->encryption_level = 1; - } - } - if (dwrq->flags & IW_ENCODE_RESTRICTED) - priv->exclude_unencrypted = 1; - if (dwrq->flags & IW_ENCODE_OPEN) - priv->exclude_unencrypted = 0; - - return -EINPROGRESS; /* Call commit handler */ -} - -static int atmel_get_encode(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *dwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; - - if (!priv->wep_is_on) - dwrq->flags = IW_ENCODE_DISABLED; - else { - if (priv->exclude_unencrypted) - dwrq->flags = IW_ENCODE_RESTRICTED; - else - dwrq->flags = IW_ENCODE_OPEN; - } - /* Which key do we want ? -1 -> tx index */ - if (index < 0 || index >= 4) - index = priv->default_key; - dwrq->flags |= index + 1; - /* Copy the key to the user buffer */ - dwrq->length = priv->wep_key_len[index]; - if (dwrq->length > 16) { - dwrq->length = 0; - } else { - memset(extra, 0, 16); - memcpy(extra, priv->wep_keys[index], dwrq->length); - } - - return 0; -} - -static int atmel_set_encodeext(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - struct iw_point *encoding = &wrqu->encoding; - struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; - int idx, key_len, alg = ext->alg, set_key = 1; - - /* Determine and validate the key index */ - idx = encoding->flags & IW_ENCODE_INDEX; - if (idx) { - if (idx < 1 || idx > 4) - return -EINVAL; - idx--; - } else - idx = priv->default_key; - - if (encoding->flags & IW_ENCODE_DISABLED) - alg = IW_ENCODE_ALG_NONE; - - if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { - priv->default_key = idx; - set_key = ext->key_len > 0 ? 1 : 0; - } - - if (set_key) { - /* Set the requested key first */ - switch (alg) { - case IW_ENCODE_ALG_NONE: - priv->wep_is_on = 0; - priv->encryption_level = 0; - priv->pairwise_cipher_suite = CIPHER_SUITE_NONE; - break; - case IW_ENCODE_ALG_WEP: - if (ext->key_len > 5) { - priv->wep_key_len[idx] = 13; - priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_128; - priv->encryption_level = 2; - } else if (ext->key_len > 0) { - priv->wep_key_len[idx] = 5; - priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_64; - priv->encryption_level = 1; - } else { - return -EINVAL; - } - priv->wep_is_on = 1; - memset(priv->wep_keys[idx], 0, 13); - key_len = min ((int)ext->key_len, priv->wep_key_len[idx]); - memcpy(priv->wep_keys[idx], ext->key, key_len); - break; - default: - return -EINVAL; - } - } - - return -EINPROGRESS; -} - -static int atmel_get_encodeext(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - struct iw_point *encoding = &wrqu->encoding; - struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; - int idx, max_key_len; - - max_key_len = encoding->length - sizeof(*ext); - if (max_key_len < 0) - return -EINVAL; - - idx = encoding->flags & IW_ENCODE_INDEX; - if (idx) { - if (idx < 1 || idx > 4) - return -EINVAL; - idx--; - } else - idx = priv->default_key; - - encoding->flags = idx + 1; - memset(ext, 0, sizeof(*ext)); - - if (!priv->wep_is_on) { - ext->alg = IW_ENCODE_ALG_NONE; - ext->key_len = 0; - encoding->flags |= IW_ENCODE_DISABLED; - } else { - if (priv->encryption_level > 0) - ext->alg = IW_ENCODE_ALG_WEP; - else - return -EINVAL; - - ext->key_len = priv->wep_key_len[idx]; - memcpy(ext->key, priv->wep_keys[idx], ext->key_len); - encoding->flags |= IW_ENCODE_ENABLED; - } - - return 0; -} - -static int atmel_set_auth(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - struct iw_param *param = &wrqu->param; - - switch (param->flags & IW_AUTH_INDEX) { - case IW_AUTH_WPA_VERSION: - case IW_AUTH_CIPHER_PAIRWISE: - case IW_AUTH_CIPHER_GROUP: - case IW_AUTH_KEY_MGMT: - case IW_AUTH_RX_UNENCRYPTED_EAPOL: - case IW_AUTH_PRIVACY_INVOKED: - /* - * atmel does not use these parameters - */ - break; - - case IW_AUTH_DROP_UNENCRYPTED: - priv->exclude_unencrypted = param->value ? 1 : 0; - break; - - case IW_AUTH_80211_AUTH_ALG: { - if (param->value & IW_AUTH_ALG_SHARED_KEY) { - priv->exclude_unencrypted = 1; - } else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) { - priv->exclude_unencrypted = 0; - } else - return -EINVAL; - break; - } - - case IW_AUTH_WPA_ENABLED: - /* Silently accept disable of WPA */ - if (param->value > 0) - return -EOPNOTSUPP; - break; - - default: - return -EOPNOTSUPP; - } - return -EINPROGRESS; -} - -static int atmel_get_auth(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - struct iw_param *param = &wrqu->param; - - switch (param->flags & IW_AUTH_INDEX) { - case IW_AUTH_DROP_UNENCRYPTED: - param->value = priv->exclude_unencrypted; - break; - - case IW_AUTH_80211_AUTH_ALG: - if (priv->exclude_unencrypted == 1) - param->value = IW_AUTH_ALG_SHARED_KEY; - else - param->value = IW_AUTH_ALG_OPEN_SYSTEM; - break; - - case IW_AUTH_WPA_ENABLED: - param->value = 0; - break; - - default: - return -EOPNOTSUPP; - } - return 0; -} - - -static int atmel_get_name(struct net_device *dev, - struct iw_request_info *info, - char *cwrq, - char *extra) -{ - strcpy(cwrq, "IEEE 802.11-DS"); - return 0; -} - -static int atmel_set_rate(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - - if (vwrq->fixed == 0) { - priv->tx_rate = 3; - priv->auto_tx_rate = 1; - } else { - priv->auto_tx_rate = 0; - - /* Which type of value ? */ - if ((vwrq->value < 4) && (vwrq->value >= 0)) { - /* Setting by rate index */ - priv->tx_rate = vwrq->value; - } else { - /* Setting by frequency value */ - switch (vwrq->value) { - case 1000000: - priv->tx_rate = 0; - break; - case 2000000: - priv->tx_rate = 1; - break; - case 5500000: - priv->tx_rate = 2; - break; - case 11000000: - priv->tx_rate = 3; - break; - default: - return -EINVAL; - } - } - } - - return -EINPROGRESS; -} - -static int atmel_set_mode(struct net_device *dev, - struct iw_request_info *info, - __u32 *uwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - - if (*uwrq != IW_MODE_ADHOC && *uwrq != IW_MODE_INFRA) - return -EINVAL; - - priv->operating_mode = *uwrq; - return -EINPROGRESS; -} - -static int atmel_get_mode(struct net_device *dev, - struct iw_request_info *info, - __u32 *uwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - - *uwrq = priv->operating_mode; - return 0; -} - -static int atmel_get_rate(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - - if (priv->auto_tx_rate) { - vwrq->fixed = 0; - vwrq->value = 11000000; - } else { - vwrq->fixed = 1; - switch (priv->tx_rate) { - case 0: - vwrq->value = 1000000; - break; - case 1: - vwrq->value = 2000000; - break; - case 2: - vwrq->value = 5500000; - break; - case 3: - vwrq->value = 11000000; - break; - } - } - return 0; -} - -static int atmel_set_power(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - priv->power_mode = vwrq->disabled ? 0 : 1; - return -EINPROGRESS; -} - -static int atmel_get_power(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - vwrq->disabled = priv->power_mode ? 0 : 1; - vwrq->flags = IW_POWER_ON; - return 0; -} - -static int atmel_set_retry(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - - if (!vwrq->disabled && (vwrq->flags & IW_RETRY_LIMIT)) { - if (vwrq->flags & IW_RETRY_LONG) - priv->long_retry = vwrq->value; - else if (vwrq->flags & IW_RETRY_SHORT) - priv->short_retry = vwrq->value; - else { - /* No modifier : set both */ - priv->long_retry = vwrq->value; - priv->short_retry = vwrq->value; - } - return -EINPROGRESS; - } - - return -EINVAL; -} - -static int atmel_get_retry(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - - vwrq->disabled = 0; /* Can't be disabled */ - - /* Note : by default, display the short retry number */ - if (vwrq->flags & IW_RETRY_LONG) { - vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG; - vwrq->value = priv->long_retry; - } else { - vwrq->flags = IW_RETRY_LIMIT; - vwrq->value = priv->short_retry; - if (priv->long_retry != priv->short_retry) - vwrq->flags |= IW_RETRY_SHORT; - } - - return 0; -} - -static int atmel_set_rts(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - int rthr = vwrq->value; - - if (vwrq->disabled) - rthr = 2347; - if ((rthr < 0) || (rthr > 2347)) { - return -EINVAL; - } - priv->rts_threshold = rthr; - - return -EINPROGRESS; /* Call commit handler */ -} - -static int atmel_get_rts(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - - vwrq->value = priv->rts_threshold; - vwrq->disabled = (vwrq->value >= 2347); - vwrq->fixed = 1; - - return 0; -} - -static int atmel_set_frag(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - int fthr = vwrq->value; - - if (vwrq->disabled) - fthr = 2346; - if ((fthr < 256) || (fthr > 2346)) { - return -EINVAL; - } - fthr &= ~0x1; /* Get an even value - is it really needed ??? */ - priv->frag_threshold = fthr; - - return -EINPROGRESS; /* Call commit handler */ -} - -static int atmel_get_frag(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - - vwrq->value = priv->frag_threshold; - vwrq->disabled = (vwrq->value >= 2346); - vwrq->fixed = 1; - - return 0; -} - -static int atmel_set_freq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *fwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - int rc = -EINPROGRESS; /* Call commit handler */ - - /* If setting by frequency, convert to a channel */ - if (fwrq->e == 1) { - int f = fwrq->m / 100000; - - /* Hack to fall through... */ - fwrq->e = 0; - fwrq->m = ieee80211_frequency_to_channel(f); - } - /* Setting by channel number */ - if ((fwrq->m > 1000) || (fwrq->e > 0)) - rc = -EOPNOTSUPP; - else { - int channel = fwrq->m; - if (atmel_validate_channel(priv, channel) == 0) { - priv->channel = channel; - } else { - rc = -EINVAL; - } - } - return rc; -} - -static int atmel_get_freq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *fwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - - fwrq->m = priv->channel; - fwrq->e = 0; - return 0; -} - -static int atmel_set_scan(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *dwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - unsigned long flags; - - /* Note : you may have realised that, as this is a SET operation, - * this is privileged and therefore a normal user can't - * perform scanning. - * This is not an error, while the device perform scanning, - * traffic doesn't flow, so it's a perfect DoS... - * Jean II */ - - if (priv->station_state == STATION_STATE_DOWN) - return -EAGAIN; - - /* Timeout old surveys. */ - if (time_after(jiffies, priv->last_survey + 20 * HZ)) - priv->site_survey_state = SITE_SURVEY_IDLE; - priv->last_survey = jiffies; - - /* Initiate a scan command */ - if (priv->site_survey_state == SITE_SURVEY_IN_PROGRESS) - return -EBUSY; - - del_timer_sync(&priv->management_timer); - spin_lock_irqsave(&priv->irqlock, flags); - - priv->site_survey_state = SITE_SURVEY_IN_PROGRESS; - priv->fast_scan = 0; - atmel_scan(priv, 0); - spin_unlock_irqrestore(&priv->irqlock, flags); - - return 0; -} - -static int atmel_get_scan(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *dwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - int i; - char *current_ev = extra; - struct iw_event iwe; - - if (priv->site_survey_state != SITE_SURVEY_COMPLETED) - return -EAGAIN; - - for (i = 0; i < priv->BSS_list_entries; i++) { - iwe.cmd = SIOCGIWAP; - iwe.u.ap_addr.sa_family = ARPHRD_ETHER; - memcpy(iwe.u.ap_addr.sa_data, priv->BSSinfo[i].BSSID, ETH_ALEN); - current_ev = iwe_stream_add_event(info, current_ev, - extra + IW_SCAN_MAX_DATA, - &iwe, IW_EV_ADDR_LEN); - - iwe.u.data.length = priv->BSSinfo[i].SSIDsize; - if (iwe.u.data.length > 32) - iwe.u.data.length = 32; - iwe.cmd = SIOCGIWESSID; - iwe.u.data.flags = 1; - current_ev = iwe_stream_add_point(info, current_ev, - extra + IW_SCAN_MAX_DATA, - &iwe, priv->BSSinfo[i].SSID); - - iwe.cmd = SIOCGIWMODE; - iwe.u.mode = priv->BSSinfo[i].BSStype; - current_ev = iwe_stream_add_event(info, current_ev, - extra + IW_SCAN_MAX_DATA, - &iwe, IW_EV_UINT_LEN); - - iwe.cmd = SIOCGIWFREQ; - iwe.u.freq.m = priv->BSSinfo[i].channel; - iwe.u.freq.e = 0; - current_ev = iwe_stream_add_event(info, current_ev, - extra + IW_SCAN_MAX_DATA, - &iwe, IW_EV_FREQ_LEN); - - /* Add quality statistics */ - iwe.cmd = IWEVQUAL; - iwe.u.qual.level = priv->BSSinfo[i].RSSI; - iwe.u.qual.qual = iwe.u.qual.level; - /* iwe.u.qual.noise = SOMETHING */ - current_ev = iwe_stream_add_event(info, current_ev, - extra + IW_SCAN_MAX_DATA, - &iwe, IW_EV_QUAL_LEN); - - - iwe.cmd = SIOCGIWENCODE; - if (priv->BSSinfo[i].UsingWEP) - 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(info, current_ev, - extra + IW_SCAN_MAX_DATA, - &iwe, NULL); - } - - /* Length of data */ - dwrq->length = (current_ev - extra); - dwrq->flags = 0; - - return 0; -} - -static int atmel_get_range(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *dwrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - struct iw_range *range = (struct iw_range *) extra; - int k, i, j; - - dwrq->length = sizeof(struct iw_range); - memset(range, 0, sizeof(struct iw_range)); - range->min_nwid = 0x0000; - range->max_nwid = 0x0000; - range->num_channels = 0; - for (j = 0; j < ARRAY_SIZE(channel_table); j++) - if (priv->reg_domain == channel_table[j].reg_domain) { - range->num_channels = channel_table[j].max - channel_table[j].min + 1; - break; - } - if (range->num_channels != 0) { - for (k = 0, i = channel_table[j].min; i <= channel_table[j].max; i++) { - range->freq[k].i = i; /* List index */ - - /* Values in MHz -> * 10^5 * 10 */ - range->freq[k].m = 100000 * - ieee80211_channel_to_frequency(i, IEEE80211_BAND_2GHZ); - range->freq[k++].e = 1; - } - range->num_frequency = k; - } - - range->max_qual.qual = 100; - range->max_qual.level = 100; - range->max_qual.noise = 0; - range->max_qual.updated = IW_QUAL_NOISE_INVALID; - - range->avg_qual.qual = 50; - range->avg_qual.level = 50; - range->avg_qual.noise = 0; - range->avg_qual.updated = IW_QUAL_NOISE_INVALID; - - range->sensitivity = 0; - - range->bitrate[0] = 1000000; - range->bitrate[1] = 2000000; - range->bitrate[2] = 5500000; - range->bitrate[3] = 11000000; - range->num_bitrates = 4; - - range->min_rts = 0; - range->max_rts = 2347; - range->min_frag = 256; - range->max_frag = 2346; - - range->encoding_size[0] = 5; - range->encoding_size[1] = 13; - range->num_encoding_sizes = 2; - range->max_encoding_tokens = 4; - - range->pmp_flags = IW_POWER_ON; - range->pmt_flags = IW_POWER_ON; - range->pm_capa = 0; - - range->we_version_source = WIRELESS_EXT; - range->we_version_compiled = WIRELESS_EXT; - range->retry_capa = IW_RETRY_LIMIT ; - range->retry_flags = IW_RETRY_LIMIT; - range->r_time_flags = 0; - range->min_retry = 1; - range->max_retry = 65535; - - return 0; -} - -static int atmel_set_wap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *awrq, - char *extra) -{ - struct atmel_private *priv = netdev_priv(dev); - int i; - static const u8 any[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; - static const u8 off[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - unsigned long flags; - - if (awrq->sa_family != ARPHRD_ETHER) - return -EINVAL; - - if (!memcmp(any, awrq->sa_data, 6) || - !memcmp(off, awrq->sa_data, 6)) { - del_timer_sync(&priv->management_timer); - spin_lock_irqsave(&priv->irqlock, flags); - atmel_scan(priv, 1); - spin_unlock_irqrestore(&priv->irqlock, flags); - return 0; - } - - for (i = 0; i < priv->BSS_list_entries; i++) { - if (memcmp(priv->BSSinfo[i].BSSID, awrq->sa_data, 6) == 0) { - if (!priv->wep_is_on && priv->BSSinfo[i].UsingWEP) { - return -EINVAL; - } else if (priv->wep_is_on && !priv->BSSinfo[i].UsingWEP) { - return -EINVAL; - } else { - del_timer_sync(&priv->management_timer); - spin_lock_irqsave(&priv->irqlock, flags); - atmel_join_bss(priv, i); - spin_unlock_irqrestore(&priv->irqlock, flags); - return 0; - } - } - } - - return -EINVAL; -} - -static int atmel_config_commit(struct net_device *dev, - struct iw_request_info *info, /* NULL */ - void *zwrq, /* NULL */ - char *extra) /* NULL */ -{ - return atmel_open(dev); -} - -static const iw_handler atmel_handler[] = -{ - (iw_handler) atmel_config_commit, /* SIOCSIWCOMMIT */ - (iw_handler) atmel_get_name, /* SIOCGIWNAME */ - (iw_handler) NULL, /* SIOCSIWNWID */ - (iw_handler) NULL, /* SIOCGIWNWID */ - (iw_handler) atmel_set_freq, /* SIOCSIWFREQ */ - (iw_handler) atmel_get_freq, /* SIOCGIWFREQ */ - (iw_handler) atmel_set_mode, /* SIOCSIWMODE */ - (iw_handler) atmel_get_mode, /* SIOCGIWMODE */ - (iw_handler) NULL, /* SIOCSIWSENS */ - (iw_handler) NULL, /* SIOCGIWSENS */ - (iw_handler) NULL, /* SIOCSIWRANGE */ - (iw_handler) atmel_get_range, /* SIOCGIWRANGE */ - (iw_handler) NULL, /* SIOCSIWPRIV */ - (iw_handler) NULL, /* SIOCGIWPRIV */ - (iw_handler) NULL, /* SIOCSIWSTATS */ - (iw_handler) NULL, /* SIOCGIWSTATS */ - (iw_handler) NULL, /* SIOCSIWSPY */ - (iw_handler) NULL, /* SIOCGIWSPY */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) atmel_set_wap, /* SIOCSIWAP */ - (iw_handler) atmel_get_wap, /* SIOCGIWAP */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* SIOCGIWAPLIST */ - (iw_handler) atmel_set_scan, /* SIOCSIWSCAN */ - (iw_handler) atmel_get_scan, /* SIOCGIWSCAN */ - (iw_handler) atmel_set_essid, /* SIOCSIWESSID */ - (iw_handler) atmel_get_essid, /* SIOCGIWESSID */ - (iw_handler) NULL, /* SIOCSIWNICKN */ - (iw_handler) NULL, /* SIOCGIWNICKN */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) atmel_set_rate, /* SIOCSIWRATE */ - (iw_handler) atmel_get_rate, /* SIOCGIWRATE */ - (iw_handler) atmel_set_rts, /* SIOCSIWRTS */ - (iw_handler) atmel_get_rts, /* SIOCGIWRTS */ - (iw_handler) atmel_set_frag, /* SIOCSIWFRAG */ - (iw_handler) atmel_get_frag, /* SIOCGIWFRAG */ - (iw_handler) NULL, /* SIOCSIWTXPOW */ - (iw_handler) NULL, /* SIOCGIWTXPOW */ - (iw_handler) atmel_set_retry, /* SIOCSIWRETRY */ - (iw_handler) atmel_get_retry, /* SIOCGIWRETRY */ - (iw_handler) atmel_set_encode, /* SIOCSIWENCODE */ - (iw_handler) atmel_get_encode, /* SIOCGIWENCODE */ - (iw_handler) atmel_set_power, /* SIOCSIWPOWER */ - (iw_handler) atmel_get_power, /* SIOCGIWPOWER */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* SIOCSIWGENIE */ - (iw_handler) NULL, /* SIOCGIWGENIE */ - (iw_handler) atmel_set_auth, /* SIOCSIWAUTH */ - (iw_handler) atmel_get_auth, /* SIOCGIWAUTH */ - (iw_handler) atmel_set_encodeext, /* SIOCSIWENCODEEXT */ - (iw_handler) atmel_get_encodeext, /* SIOCGIWENCODEEXT */ - (iw_handler) NULL, /* SIOCSIWPMKSA */ -}; - -static const iw_handler atmel_private_handler[] = -{ - NULL, /* SIOCIWFIRSTPRIV */ -}; - -struct atmel_priv_ioctl { - char id[32]; - unsigned char __user *data; - unsigned short len; -}; - -#define ATMELFWL SIOCIWFIRSTPRIV -#define ATMELIDIFC ATMELFWL + 1 -#define ATMELRD ATMELFWL + 2 -#define ATMELMAGIC 0x51807 -#define REGDOMAINSZ 20 - -static const struct iw_priv_args atmel_private_args[] = { - { - .cmd = ATMELFWL, - .set_args = IW_PRIV_TYPE_BYTE - | IW_PRIV_SIZE_FIXED - | sizeof(struct atmel_priv_ioctl), - .get_args = IW_PRIV_TYPE_NONE, - .name = "atmelfwl" - }, { - .cmd = ATMELIDIFC, - .set_args = IW_PRIV_TYPE_NONE, - .get_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, - .name = "atmelidifc" - }, { - .cmd = ATMELRD, - .set_args = IW_PRIV_TYPE_CHAR | REGDOMAINSZ, - .get_args = IW_PRIV_TYPE_NONE, - .name = "regdomain" - }, -}; - -static const struct iw_handler_def atmel_handler_def = { - .num_standard = ARRAY_SIZE(atmel_handler), - .num_private = ARRAY_SIZE(atmel_private_handler), - .num_private_args = ARRAY_SIZE(atmel_private_args), - .standard = (iw_handler *) atmel_handler, - .private = (iw_handler *) atmel_private_handler, - .private_args = (struct iw_priv_args *) atmel_private_args, - .get_wireless_stats = atmel_get_wireless_stats -}; - -static int atmel_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) -{ - int i, rc = 0; - struct atmel_private *priv = netdev_priv(dev); - struct atmel_priv_ioctl com; - struct iwreq *wrq = (struct iwreq *) rq; - unsigned char *new_firmware; - char domain[REGDOMAINSZ + 1]; - - switch (cmd) { - case ATMELIDIFC: - wrq->u.param.value = ATMELMAGIC; - break; - - case ATMELFWL: - if (copy_from_user(&com, rq->ifr_data, sizeof(com))) { - rc = -EFAULT; - break; - } - - if (!capable(CAP_NET_ADMIN)) { - rc = -EPERM; - break; - } - - if (!(new_firmware = kmalloc(com.len, GFP_KERNEL))) { - rc = -ENOMEM; - break; - } - - if (copy_from_user(new_firmware, com.data, com.len)) { - kfree(new_firmware); - rc = -EFAULT; - break; - } - - kfree(priv->firmware); - - priv->firmware = new_firmware; - priv->firmware_length = com.len; - strncpy(priv->firmware_id, com.id, 31); - priv->firmware_id[31] = '\0'; - break; - - case ATMELRD: - if (copy_from_user(domain, rq->ifr_data, REGDOMAINSZ)) { - rc = -EFAULT; - break; - } - - if (!capable(CAP_NET_ADMIN)) { - rc = -EPERM; - break; - } - - domain[REGDOMAINSZ] = 0; - rc = -EINVAL; - for (i = 0; i < ARRAY_SIZE(channel_table); i++) { - if (!strcasecmp(channel_table[i].name, domain)) { - priv->config_reg_domain = channel_table[i].reg_domain; - rc = 0; - } - } - - if (rc == 0 && priv->station_state != STATION_STATE_DOWN) - rc = atmel_open(dev); - break; - - default: - rc = -EOPNOTSUPP; - } - - return rc; -} - -struct auth_body { - __le16 alg; - __le16 trans_seq; - __le16 status; - u8 el_id; - u8 chall_text_len; - u8 chall_text[253]; -}; - -static void atmel_enter_state(struct atmel_private *priv, int new_state) -{ - int old_state = priv->station_state; - - if (new_state == old_state) - return; - - priv->station_state = new_state; - - if (new_state == STATION_STATE_READY) { - netif_start_queue(priv->dev); - netif_carrier_on(priv->dev); - } - - if (old_state == STATION_STATE_READY) { - netif_carrier_off(priv->dev); - if (netif_running(priv->dev)) - netif_stop_queue(priv->dev); - priv->last_beacon_timestamp = 0; - } -} - -static void atmel_scan(struct atmel_private *priv, int specific_ssid) -{ - struct { - u8 BSSID[ETH_ALEN]; - u8 SSID[MAX_SSID_LENGTH]; - u8 scan_type; - u8 channel; - __le16 BSS_type; - __le16 min_channel_time; - __le16 max_channel_time; - u8 options; - u8 SSID_size; - } cmd; - - eth_broadcast_addr(cmd.BSSID); - - if (priv->fast_scan) { - cmd.SSID_size = priv->SSID_size; - memcpy(cmd.SSID, priv->SSID, priv->SSID_size); - cmd.min_channel_time = cpu_to_le16(10); - cmd.max_channel_time = cpu_to_le16(50); - } else { - priv->BSS_list_entries = 0; - cmd.SSID_size = 0; - cmd.min_channel_time = cpu_to_le16(10); - cmd.max_channel_time = cpu_to_le16(120); - } - - cmd.options = 0; - - if (!specific_ssid) - cmd.options |= SCAN_OPTIONS_SITE_SURVEY; - - cmd.channel = (priv->channel & 0x7f); - cmd.scan_type = SCAN_TYPE_ACTIVE; - cmd.BSS_type = cpu_to_le16(priv->operating_mode == IW_MODE_ADHOC ? - BSS_TYPE_AD_HOC : BSS_TYPE_INFRASTRUCTURE); - - atmel_send_command(priv, CMD_Scan, &cmd, sizeof(cmd)); - - /* This must come after all hardware access to avoid being messed up - by stuff happening in interrupt context after we leave STATE_DOWN */ - atmel_enter_state(priv, STATION_STATE_SCANNING); -} - -static void join(struct atmel_private *priv, int type) -{ - struct { - u8 BSSID[6]; - u8 SSID[MAX_SSID_LENGTH]; - u8 BSS_type; /* this is a short in a scan command - weird */ - u8 channel; - __le16 timeout; - u8 SSID_size; - u8 reserved; - } cmd; - - cmd.SSID_size = priv->SSID_size; - memcpy(cmd.SSID, priv->SSID, priv->SSID_size); - memcpy(cmd.BSSID, priv->CurrentBSSID, ETH_ALEN); - cmd.channel = (priv->channel & 0x7f); - cmd.BSS_type = type; - cmd.timeout = cpu_to_le16(2000); - - atmel_send_command(priv, CMD_Join, &cmd, sizeof(cmd)); -} - -static void start(struct atmel_private *priv, int type) -{ - struct { - u8 BSSID[6]; - u8 SSID[MAX_SSID_LENGTH]; - u8 BSS_type; - u8 channel; - u8 SSID_size; - u8 reserved[3]; - } cmd; - - cmd.SSID_size = priv->SSID_size; - memcpy(cmd.SSID, priv->SSID, priv->SSID_size); - memcpy(cmd.BSSID, priv->BSSID, ETH_ALEN); - cmd.BSS_type = type; - cmd.channel = (priv->channel & 0x7f); - - atmel_send_command(priv, CMD_Start, &cmd, sizeof(cmd)); -} - -static void handle_beacon_probe(struct atmel_private *priv, u16 capability, - u8 channel) -{ - int rejoin = 0; - int new = capability & WLAN_CAPABILITY_SHORT_PREAMBLE ? - SHORT_PREAMBLE : LONG_PREAMBLE; - - if (priv->preamble != new) { - priv->preamble = new; - rejoin = 1; - atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_PREAMBLE_TYPE, new); - } - - if (priv->channel != channel) { - priv->channel = channel; - rejoin = 1; - atmel_set_mib8(priv, Phy_Mib_Type, PHY_MIB_CHANNEL_POS, channel); - } - - if (rejoin) { - priv->station_is_associated = 0; - atmel_enter_state(priv, STATION_STATE_JOINNING); - - if (priv->operating_mode == IW_MODE_INFRA) - join(priv, BSS_TYPE_INFRASTRUCTURE); - else - join(priv, BSS_TYPE_AD_HOC); - } -} - -static void send_authentication_request(struct atmel_private *priv, u16 system, - u8 *challenge, int challenge_len) -{ - struct ieee80211_hdr header; - struct auth_body auth; - - header.frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH); - header.duration_id = cpu_to_le16(0x8000); - header.seq_ctrl = 0; - memcpy(header.addr1, priv->CurrentBSSID, ETH_ALEN); - memcpy(header.addr2, priv->dev->dev_addr, ETH_ALEN); - memcpy(header.addr3, priv->CurrentBSSID, ETH_ALEN); - - if (priv->wep_is_on && priv->CurrentAuthentTransactionSeqNum != 1) - /* no WEP for authentication frames with TrSeqNo 1 */ - header.frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); - - auth.alg = cpu_to_le16(system); - - auth.status = 0; - auth.trans_seq = cpu_to_le16(priv->CurrentAuthentTransactionSeqNum); - priv->ExpectedAuthentTransactionSeqNum = priv->CurrentAuthentTransactionSeqNum+1; - priv->CurrentAuthentTransactionSeqNum += 2; - - if (challenge_len != 0) { - auth.el_id = 16; /* challenge_text */ - auth.chall_text_len = challenge_len; - memcpy(auth.chall_text, challenge, challenge_len); - atmel_transmit_management_frame(priv, &header, (u8 *)&auth, 8 + challenge_len); - } else { - atmel_transmit_management_frame(priv, &header, (u8 *)&auth, 6); - } -} - -static void send_association_request(struct atmel_private *priv, int is_reassoc) -{ - u8 *ssid_el_p; - int bodysize; - struct ieee80211_hdr header; - struct ass_req_format { - __le16 capability; - __le16 listen_interval; - u8 ap[ETH_ALEN]; /* nothing after here directly accessible */ - u8 ssid_el_id; - u8 ssid_len; - u8 ssid[MAX_SSID_LENGTH]; - u8 sup_rates_el_id; - u8 sup_rates_len; - u8 rates[4]; - } body; - - header.frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | - (is_reassoc ? IEEE80211_STYPE_REASSOC_REQ : IEEE80211_STYPE_ASSOC_REQ)); - header.duration_id = cpu_to_le16(0x8000); - header.seq_ctrl = 0; - - memcpy(header.addr1, priv->CurrentBSSID, ETH_ALEN); - memcpy(header.addr2, priv->dev->dev_addr, ETH_ALEN); - memcpy(header.addr3, priv->CurrentBSSID, ETH_ALEN); - - body.capability = cpu_to_le16(WLAN_CAPABILITY_ESS); - if (priv->wep_is_on) - body.capability |= cpu_to_le16(WLAN_CAPABILITY_PRIVACY); - if (priv->preamble == SHORT_PREAMBLE) - body.capability |= cpu_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE); - - body.listen_interval = cpu_to_le16(priv->listen_interval * priv->beacon_period); - - /* current AP address - only in reassoc frame */ - if (is_reassoc) { - memcpy(body.ap, priv->CurrentBSSID, ETH_ALEN); - ssid_el_p = &body.ssid_el_id; - bodysize = 18 + priv->SSID_size; - } else { - ssid_el_p = &body.ap[0]; - bodysize = 12 + priv->SSID_size; - } - - ssid_el_p[0] = WLAN_EID_SSID; - ssid_el_p[1] = priv->SSID_size; - memcpy(ssid_el_p + 2, priv->SSID, priv->SSID_size); - ssid_el_p[2 + priv->SSID_size] = WLAN_EID_SUPP_RATES; - ssid_el_p[3 + priv->SSID_size] = 4; /* len of supported rates */ - memcpy(ssid_el_p + 4 + priv->SSID_size, atmel_basic_rates, 4); - - atmel_transmit_management_frame(priv, &header, (void *)&body, bodysize); -} - -static int is_frame_from_current_bss(struct atmel_private *priv, - struct ieee80211_hdr *header) -{ - if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS) - return memcmp(header->addr3, priv->CurrentBSSID, 6) == 0; - else - return memcmp(header->addr2, priv->CurrentBSSID, 6) == 0; -} - -static int retrieve_bss(struct atmel_private *priv) -{ - int i; - int max_rssi = -128; - int max_index = -1; - - if (priv->BSS_list_entries == 0) - return -1; - - if (priv->connect_to_any_BSS) { - /* Select a BSS with the max-RSSI but of the same type and of - the same WEP mode and that it is not marked as 'bad' (i.e. - we had previously failed to connect to this BSS with the - settings that we currently use) */ - priv->current_BSS = 0; - for (i = 0; i < priv->BSS_list_entries; i++) { - if (priv->operating_mode == priv->BSSinfo[i].BSStype && - ((!priv->wep_is_on && !priv->BSSinfo[i].UsingWEP) || - (priv->wep_is_on && priv->BSSinfo[i].UsingWEP)) && - !(priv->BSSinfo[i].channel & 0x80)) { - max_rssi = priv->BSSinfo[i].RSSI; - priv->current_BSS = max_index = i; - } - } - return max_index; - } - - for (i = 0; i < priv->BSS_list_entries; i++) { - if (priv->SSID_size == priv->BSSinfo[i].SSIDsize && - memcmp(priv->SSID, priv->BSSinfo[i].SSID, priv->SSID_size) == 0 && - priv->operating_mode == priv->BSSinfo[i].BSStype && - atmel_validate_channel(priv, priv->BSSinfo[i].channel) == 0) { - if (priv->BSSinfo[i].RSSI >= max_rssi) { - max_rssi = priv->BSSinfo[i].RSSI; - max_index = i; - } - } - } - return max_index; -} - -static void store_bss_info(struct atmel_private *priv, - struct ieee80211_hdr *header, u16 capability, - u16 beacon_period, u8 channel, u8 rssi, u8 ssid_len, - u8 *ssid, int is_beacon) -{ - u8 *bss = capability & WLAN_CAPABILITY_ESS ? header->addr2 : header->addr3; - int i, index; - - for (index = -1, i = 0; i < priv->BSS_list_entries; i++) - if (memcmp(bss, priv->BSSinfo[i].BSSID, ETH_ALEN) == 0) - index = i; - - /* If we process a probe and an entry from this BSS exists - we will update the BSS entry with the info from this BSS. - If we process a beacon we will only update RSSI */ - - if (index == -1) { - if (priv->BSS_list_entries == MAX_BSS_ENTRIES) - return; - index = priv->BSS_list_entries++; - memcpy(priv->BSSinfo[index].BSSID, bss, ETH_ALEN); - priv->BSSinfo[index].RSSI = rssi; - } else { - if (rssi > priv->BSSinfo[index].RSSI) - priv->BSSinfo[index].RSSI = rssi; - if (is_beacon) - return; - } - - priv->BSSinfo[index].channel = channel; - priv->BSSinfo[index].beacon_period = beacon_period; - priv->BSSinfo[index].UsingWEP = capability & WLAN_CAPABILITY_PRIVACY; - memcpy(priv->BSSinfo[index].SSID, ssid, ssid_len); - priv->BSSinfo[index].SSIDsize = ssid_len; - - if (capability & WLAN_CAPABILITY_IBSS) - priv->BSSinfo[index].BSStype = IW_MODE_ADHOC; - else if (capability & WLAN_CAPABILITY_ESS) - priv->BSSinfo[index].BSStype = IW_MODE_INFRA; - - priv->BSSinfo[index].preamble = capability & WLAN_CAPABILITY_SHORT_PREAMBLE ? - SHORT_PREAMBLE : LONG_PREAMBLE; -} - -static void authenticate(struct atmel_private *priv, u16 frame_len) -{ - struct auth_body *auth = (struct auth_body *)priv->rx_buf; - u16 status = le16_to_cpu(auth->status); - u16 trans_seq_no = le16_to_cpu(auth->trans_seq); - u16 system = le16_to_cpu(auth->alg); - - if (status == WLAN_STATUS_SUCCESS && !priv->wep_is_on) { - /* no WEP */ - if (priv->station_was_associated) { - atmel_enter_state(priv, STATION_STATE_REASSOCIATING); - send_association_request(priv, 1); - return; - } else { - atmel_enter_state(priv, STATION_STATE_ASSOCIATING); - send_association_request(priv, 0); - return; - } - } - - if (status == WLAN_STATUS_SUCCESS && priv->wep_is_on) { - int should_associate = 0; - /* WEP */ - if (trans_seq_no != priv->ExpectedAuthentTransactionSeqNum) - return; - - if (system == WLAN_AUTH_OPEN) { - if (trans_seq_no == 0x0002) { - should_associate = 1; - } - } else if (system == WLAN_AUTH_SHARED_KEY) { - if (trans_seq_no == 0x0002 && - auth->el_id == WLAN_EID_CHALLENGE) { - send_authentication_request(priv, system, auth->chall_text, auth->chall_text_len); - return; - } else if (trans_seq_no == 0x0004) { - should_associate = 1; - } - } - - if (should_associate) { - if (priv->station_was_associated) { - atmel_enter_state(priv, STATION_STATE_REASSOCIATING); - send_association_request(priv, 1); - return; - } else { - atmel_enter_state(priv, STATION_STATE_ASSOCIATING); - send_association_request(priv, 0); - return; - } - } - } - - if (status == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG) { - /* Flip back and forth between WEP auth modes until the max - * authentication tries has been exceeded. - */ - if (system == WLAN_AUTH_OPEN) { - priv->CurrentAuthentTransactionSeqNum = 0x001; - priv->exclude_unencrypted = 1; - send_authentication_request(priv, WLAN_AUTH_SHARED_KEY, NULL, 0); - return; - } else if (system == WLAN_AUTH_SHARED_KEY - && priv->wep_is_on) { - priv->CurrentAuthentTransactionSeqNum = 0x001; - priv->exclude_unencrypted = 0; - send_authentication_request(priv, WLAN_AUTH_OPEN, NULL, 0); - return; - } else if (priv->connect_to_any_BSS) { - int bss_index; - - priv->BSSinfo[(int)(priv->current_BSS)].channel |= 0x80; - - if ((bss_index = retrieve_bss(priv)) != -1) { - atmel_join_bss(priv, bss_index); - return; - } - } - } - - priv->AuthenticationRequestRetryCnt = 0; - atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); - priv->station_is_associated = 0; -} - -static void associate(struct atmel_private *priv, u16 frame_len, u16 subtype) -{ - struct ass_resp_format { - __le16 capability; - __le16 status; - __le16 ass_id; - u8 el_id; - u8 length; - u8 rates[4]; - } *ass_resp = (struct ass_resp_format *)priv->rx_buf; - - u16 status = le16_to_cpu(ass_resp->status); - u16 ass_id = le16_to_cpu(ass_resp->ass_id); - u16 rates_len = ass_resp->length > 4 ? 4 : ass_resp->length; - - union iwreq_data wrqu; - - if (frame_len < 8 + rates_len) - return; - - if (status == WLAN_STATUS_SUCCESS) { - if (subtype == IEEE80211_STYPE_ASSOC_RESP) - priv->AssociationRequestRetryCnt = 0; - else - priv->ReAssociationRequestRetryCnt = 0; - - atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, - MAC_MGMT_MIB_STATION_ID_POS, ass_id & 0x3fff); - atmel_set_mib(priv, Phy_Mib_Type, - PHY_MIB_RATE_SET_POS, ass_resp->rates, rates_len); - if (priv->power_mode == 0) { - priv->listen_interval = 1; - atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, - MAC_MGMT_MIB_PS_MODE_POS, ACTIVE_MODE); - atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, - MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 1); - } else { - priv->listen_interval = 2; - atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, - MAC_MGMT_MIB_PS_MODE_POS, PS_MODE); - atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, - MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 2); - } - - priv->station_is_associated = 1; - priv->station_was_associated = 1; - atmel_enter_state(priv, STATION_STATE_READY); - - /* Send association event to userspace */ - wrqu.data.length = 0; - wrqu.data.flags = 0; - memcpy(wrqu.ap_addr.sa_data, priv->CurrentBSSID, ETH_ALEN); - wrqu.ap_addr.sa_family = ARPHRD_ETHER; - wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL); - - return; - } - - if (subtype == IEEE80211_STYPE_ASSOC_RESP && - status != WLAN_STATUS_ASSOC_DENIED_RATES && - status != WLAN_STATUS_CAPS_UNSUPPORTED && - priv->AssociationRequestRetryCnt < MAX_ASSOCIATION_RETRIES) { - mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); - priv->AssociationRequestRetryCnt++; - send_association_request(priv, 0); - return; - } - - if (subtype == IEEE80211_STYPE_REASSOC_RESP && - status != WLAN_STATUS_ASSOC_DENIED_RATES && - status != WLAN_STATUS_CAPS_UNSUPPORTED && - priv->ReAssociationRequestRetryCnt < MAX_ASSOCIATION_RETRIES) { - mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); - priv->ReAssociationRequestRetryCnt++; - send_association_request(priv, 1); - return; - } - - atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); - priv->station_is_associated = 0; - - if (priv->connect_to_any_BSS) { - int bss_index; - priv->BSSinfo[(int)(priv->current_BSS)].channel |= 0x80; - - if ((bss_index = retrieve_bss(priv)) != -1) - atmel_join_bss(priv, bss_index); - } -} - -static void atmel_join_bss(struct atmel_private *priv, int bss_index) -{ - struct bss_info *bss = &priv->BSSinfo[bss_index]; - - memcpy(priv->CurrentBSSID, bss->BSSID, ETH_ALEN); - memcpy(priv->SSID, bss->SSID, priv->SSID_size = bss->SSIDsize); - - /* The WPA stuff cares about the current AP address */ - if (priv->use_wpa) - build_wpa_mib(priv); - - /* When switching to AdHoc turn OFF Power Save if needed */ - - if (bss->BSStype == IW_MODE_ADHOC && - priv->operating_mode != IW_MODE_ADHOC && - priv->power_mode) { - priv->power_mode = 0; - priv->listen_interval = 1; - atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, - MAC_MGMT_MIB_PS_MODE_POS, ACTIVE_MODE); - atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, - MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 1); - } - - priv->operating_mode = bss->BSStype; - priv->channel = bss->channel & 0x7f; - priv->beacon_period = bss->beacon_period; - - if (priv->preamble != bss->preamble) { - priv->preamble = bss->preamble; - atmel_set_mib8(priv, Local_Mib_Type, - LOCAL_MIB_PREAMBLE_TYPE, bss->preamble); - } - - if (!priv->wep_is_on && bss->UsingWEP) { - atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); - priv->station_is_associated = 0; - return; - } - - if (priv->wep_is_on && !bss->UsingWEP) { - atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); - priv->station_is_associated = 0; - return; - } - - atmel_enter_state(priv, STATION_STATE_JOINNING); - - if (priv->operating_mode == IW_MODE_INFRA) - join(priv, BSS_TYPE_INFRASTRUCTURE); - else - join(priv, BSS_TYPE_AD_HOC); -} - -static void restart_search(struct atmel_private *priv) -{ - int bss_index; - - if (!priv->connect_to_any_BSS) { - atmel_scan(priv, 1); - } else { - priv->BSSinfo[(int)(priv->current_BSS)].channel |= 0x80; - - if ((bss_index = retrieve_bss(priv)) != -1) - atmel_join_bss(priv, bss_index); - else - atmel_scan(priv, 0); - } -} - -static void smooth_rssi(struct atmel_private *priv, u8 rssi) -{ - u8 old = priv->wstats.qual.level; - u8 max_rssi = 42; /* 502-rmfd-revd max by experiment, default for now */ - - switch (priv->firmware_type) { - case ATMEL_FW_TYPE_502E: - max_rssi = 63; /* 502-rmfd-reve max by experiment */ - break; - default: - break; - } - - rssi = rssi * 100 / max_rssi; - if ((rssi + old) % 2) - priv->wstats.qual.level = (rssi + old) / 2 + 1; - else - priv->wstats.qual.level = (rssi + old) / 2; - priv->wstats.qual.updated |= IW_QUAL_LEVEL_UPDATED; - priv->wstats.qual.updated &= ~IW_QUAL_LEVEL_INVALID; -} - -static void atmel_smooth_qual(struct atmel_private *priv) -{ - unsigned long time_diff = (jiffies - priv->last_qual) / HZ; - while (time_diff--) { - priv->last_qual += HZ; - priv->wstats.qual.qual = priv->wstats.qual.qual / 2; - priv->wstats.qual.qual += - priv->beacons_this_sec * priv->beacon_period * (priv->wstats.qual.level + 100) / 4000; - priv->beacons_this_sec = 0; - } - priv->wstats.qual.updated |= IW_QUAL_QUAL_UPDATED; - priv->wstats.qual.updated &= ~IW_QUAL_QUAL_INVALID; -} - -/* deals with incoming management frames. */ -static void atmel_management_frame(struct atmel_private *priv, - struct ieee80211_hdr *header, - u16 frame_len, u8 rssi) -{ - u16 subtype; - - subtype = le16_to_cpu(header->frame_control) & IEEE80211_FCTL_STYPE; - switch (subtype) { - case IEEE80211_STYPE_BEACON: - case IEEE80211_STYPE_PROBE_RESP: - - /* beacon frame has multiple variable-length fields - - never let an engineer loose with a data structure design. */ - { - struct beacon_format { - __le64 timestamp; - __le16 interval; - __le16 capability; - u8 ssid_el_id; - u8 ssid_length; - /* ssid here */ - u8 rates_el_id; - u8 rates_length; - /* rates here */ - u8 ds_el_id; - u8 ds_length; - /* ds here */ - } *beacon = (struct beacon_format *)priv->rx_buf; - - u8 channel, rates_length, ssid_length; - u64 timestamp = le64_to_cpu(beacon->timestamp); - u16 beacon_interval = le16_to_cpu(beacon->interval); - u16 capability = le16_to_cpu(beacon->capability); - u8 *beaconp = priv->rx_buf; - ssid_length = beacon->ssid_length; - /* this blows chunks. */ - if (frame_len < 14 || frame_len < ssid_length + 15) - return; - rates_length = beaconp[beacon->ssid_length + 15]; - if (frame_len < ssid_length + rates_length + 18) - return; - if (ssid_length > MAX_SSID_LENGTH) - return; - channel = beaconp[ssid_length + rates_length + 18]; - - if (priv->station_state == STATION_STATE_READY) { - smooth_rssi(priv, rssi); - if (is_frame_from_current_bss(priv, header)) { - priv->beacons_this_sec++; - atmel_smooth_qual(priv); - if (priv->last_beacon_timestamp) { - /* Note truncate this to 32 bits - kernel can't divide a long long */ - u32 beacon_delay = timestamp - priv->last_beacon_timestamp; - int beacons = beacon_delay / (beacon_interval * 1000); - if (beacons > 1) - priv->wstats.miss.beacon += beacons - 1; - } - priv->last_beacon_timestamp = timestamp; - handle_beacon_probe(priv, capability, channel); - } - } - - if (priv->station_state == STATION_STATE_SCANNING) - store_bss_info(priv, header, capability, - beacon_interval, channel, rssi, - ssid_length, - &beacon->rates_el_id, - subtype == IEEE80211_STYPE_BEACON); - } - break; - - case IEEE80211_STYPE_AUTH: - - if (priv->station_state == STATION_STATE_AUTHENTICATING) - authenticate(priv, frame_len); - - break; - - case IEEE80211_STYPE_ASSOC_RESP: - case IEEE80211_STYPE_REASSOC_RESP: - - if (priv->station_state == STATION_STATE_ASSOCIATING || - priv->station_state == STATION_STATE_REASSOCIATING) - associate(priv, frame_len, subtype); - - break; - - case IEEE80211_STYPE_DISASSOC: - if (priv->station_is_associated && - priv->operating_mode == IW_MODE_INFRA && - is_frame_from_current_bss(priv, header)) { - priv->station_was_associated = 0; - priv->station_is_associated = 0; - - atmel_enter_state(priv, STATION_STATE_JOINNING); - join(priv, BSS_TYPE_INFRASTRUCTURE); - } - - break; - - case IEEE80211_STYPE_DEAUTH: - if (priv->operating_mode == IW_MODE_INFRA && - is_frame_from_current_bss(priv, header)) { - priv->station_was_associated = 0; - - atmel_enter_state(priv, STATION_STATE_JOINNING); - join(priv, BSS_TYPE_INFRASTRUCTURE); - } - - break; - } -} - -/* run when timer expires */ -static void atmel_management_timer(u_long a) -{ - struct net_device *dev = (struct net_device *) a; - struct atmel_private *priv = netdev_priv(dev); - unsigned long flags; - - /* Check if the card has been yanked. */ - if (priv->card && priv->present_callback && - !(*priv->present_callback)(priv->card)) - return; - - spin_lock_irqsave(&priv->irqlock, flags); - - switch (priv->station_state) { - - case STATION_STATE_AUTHENTICATING: - if (priv->AuthenticationRequestRetryCnt >= MAX_AUTHENTICATION_RETRIES) { - atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); - priv->station_is_associated = 0; - priv->AuthenticationRequestRetryCnt = 0; - restart_search(priv); - } else { - int auth = WLAN_AUTH_OPEN; - priv->AuthenticationRequestRetryCnt++; - priv->CurrentAuthentTransactionSeqNum = 0x0001; - mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); - if (priv->wep_is_on && priv->exclude_unencrypted) - auth = WLAN_AUTH_SHARED_KEY; - send_authentication_request(priv, auth, NULL, 0); - } - break; - - case STATION_STATE_ASSOCIATING: - if (priv->AssociationRequestRetryCnt == MAX_ASSOCIATION_RETRIES) { - atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); - priv->station_is_associated = 0; - priv->AssociationRequestRetryCnt = 0; - restart_search(priv); - } else { - priv->AssociationRequestRetryCnt++; - mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); - send_association_request(priv, 0); - } - break; - - case STATION_STATE_REASSOCIATING: - if (priv->ReAssociationRequestRetryCnt == MAX_ASSOCIATION_RETRIES) { - atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); - priv->station_is_associated = 0; - priv->ReAssociationRequestRetryCnt = 0; - restart_search(priv); - } else { - priv->ReAssociationRequestRetryCnt++; - mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); - send_association_request(priv, 1); - } - break; - - default: - break; - } - - spin_unlock_irqrestore(&priv->irqlock, flags); -} - -static void atmel_command_irq(struct atmel_private *priv) -{ - u8 status = atmel_rmem8(priv, atmel_co(priv, CMD_BLOCK_STATUS_OFFSET)); - u8 command = atmel_rmem8(priv, atmel_co(priv, CMD_BLOCK_COMMAND_OFFSET)); - int fast_scan; - union iwreq_data wrqu; - - if (status == CMD_STATUS_IDLE || - status == CMD_STATUS_IN_PROGRESS) - return; - - switch (command) { - case CMD_Start: - if (status == CMD_STATUS_COMPLETE) { - priv->station_was_associated = priv->station_is_associated; - atmel_get_mib(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_CUR_BSSID_POS, - (u8 *)priv->CurrentBSSID, 6); - atmel_enter_state(priv, STATION_STATE_READY); - } - break; - - case CMD_Scan: - fast_scan = priv->fast_scan; - priv->fast_scan = 0; - - if (status != CMD_STATUS_COMPLETE) { - atmel_scan(priv, 1); - } else { - int bss_index = retrieve_bss(priv); - int notify_scan_complete = 1; - if (bss_index != -1) { - atmel_join_bss(priv, bss_index); - } else if (priv->operating_mode == IW_MODE_ADHOC && - priv->SSID_size != 0) { - start(priv, BSS_TYPE_AD_HOC); - } else { - priv->fast_scan = !fast_scan; - atmel_scan(priv, 1); - notify_scan_complete = 0; - } - priv->site_survey_state = SITE_SURVEY_COMPLETED; - if (notify_scan_complete) { - wrqu.data.length = 0; - wrqu.data.flags = 0; - wireless_send_event(priv->dev, SIOCGIWSCAN, &wrqu, NULL); - } - } - break; - - case CMD_SiteSurvey: - priv->fast_scan = 0; - - if (status != CMD_STATUS_COMPLETE) - return; - - priv->site_survey_state = SITE_SURVEY_COMPLETED; - if (priv->station_is_associated) { - atmel_enter_state(priv, STATION_STATE_READY); - wrqu.data.length = 0; - wrqu.data.flags = 0; - wireless_send_event(priv->dev, SIOCGIWSCAN, &wrqu, NULL); - } else { - atmel_scan(priv, 1); - } - break; - - case CMD_Join: - if (status == CMD_STATUS_COMPLETE) { - if (priv->operating_mode == IW_MODE_ADHOC) { - priv->station_was_associated = priv->station_is_associated; - atmel_enter_state(priv, STATION_STATE_READY); - } else { - int auth = WLAN_AUTH_OPEN; - priv->AuthenticationRequestRetryCnt = 0; - atmel_enter_state(priv, STATION_STATE_AUTHENTICATING); - - mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); - priv->CurrentAuthentTransactionSeqNum = 0x0001; - if (priv->wep_is_on && priv->exclude_unencrypted) - auth = WLAN_AUTH_SHARED_KEY; - send_authentication_request(priv, auth, NULL, 0); - } - return; - } - - atmel_scan(priv, 1); - } -} - -static int atmel_wakeup_firmware(struct atmel_private *priv) -{ - struct host_info_struct *iface = &priv->host_info; - u16 mr1, mr3; - int i; - - if (priv->card_type == CARD_TYPE_SPI_FLASH) - atmel_set_gcr(priv->dev, GCR_REMAP); - - /* wake up on-board processor */ - atmel_clear_gcr(priv->dev, 0x0040); - atmel_write16(priv->dev, BSR, BSS_SRAM); - - if (priv->card_type == CARD_TYPE_SPI_FLASH) - mdelay(100); - - /* and wait for it */ - for (i = LOOP_RETRY_LIMIT; i; i--) { - mr1 = atmel_read16(priv->dev, MR1); - mr3 = atmel_read16(priv->dev, MR3); - - if (mr3 & MAC_BOOT_COMPLETE) - break; - if (mr1 & MAC_BOOT_COMPLETE && - priv->bus_type == BUS_TYPE_PCCARD) - break; - } - - if (i == 0) { - printk(KERN_ALERT "%s: MAC failed to boot.\n", priv->dev->name); - return -EIO; - } - - if ((priv->host_info_base = atmel_read16(priv->dev, MR2)) == 0xffff) { - printk(KERN_ALERT "%s: card missing.\n", priv->dev->name); - return -ENODEV; - } - - /* now check for completion of MAC initialization through - the FunCtrl field of the IFACE, poll MR1 to detect completion of - MAC initialization, check completion status, set interrupt mask, - enables interrupts and calls Tx and Rx initialization functions */ - - atmel_wmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET), FUNC_CTRL_INIT_COMPLETE); - - for (i = LOOP_RETRY_LIMIT; i; i--) { - mr1 = atmel_read16(priv->dev, MR1); - mr3 = atmel_read16(priv->dev, MR3); - - if (mr3 & MAC_INIT_COMPLETE) - break; - if (mr1 & MAC_INIT_COMPLETE && - priv->bus_type == BUS_TYPE_PCCARD) - break; - } - - if (i == 0) { - printk(KERN_ALERT "%s: MAC failed to initialise.\n", - priv->dev->name); - return -EIO; - } - - /* Check for MAC_INIT_OK only on the register that the MAC_INIT_OK was set */ - if ((mr3 & MAC_INIT_COMPLETE) && - !(atmel_read16(priv->dev, MR3) & MAC_INIT_OK)) { - printk(KERN_ALERT "%s: MAC failed MR3 self-test.\n", priv->dev->name); - return -EIO; - } - if ((mr1 & MAC_INIT_COMPLETE) && - !(atmel_read16(priv->dev, MR1) & MAC_INIT_OK)) { - printk(KERN_ALERT "%s: MAC failed MR1 self-test.\n", priv->dev->name); - return -EIO; - } - - atmel_copy_to_host(priv->dev, (unsigned char *)iface, - priv->host_info_base, sizeof(*iface)); - - iface->tx_buff_pos = le16_to_cpu(iface->tx_buff_pos); - iface->tx_buff_size = le16_to_cpu(iface->tx_buff_size); - iface->tx_desc_pos = le16_to_cpu(iface->tx_desc_pos); - iface->tx_desc_count = le16_to_cpu(iface->tx_desc_count); - iface->rx_buff_pos = le16_to_cpu(iface->rx_buff_pos); - iface->rx_buff_size = le16_to_cpu(iface->rx_buff_size); - iface->rx_desc_pos = le16_to_cpu(iface->rx_desc_pos); - iface->rx_desc_count = le16_to_cpu(iface->rx_desc_count); - iface->build_version = le16_to_cpu(iface->build_version); - iface->command_pos = le16_to_cpu(iface->command_pos); - iface->major_version = le16_to_cpu(iface->major_version); - iface->minor_version = le16_to_cpu(iface->minor_version); - iface->func_ctrl = le16_to_cpu(iface->func_ctrl); - iface->mac_status = le16_to_cpu(iface->mac_status); - - return 0; -} - -/* determine type of memory and MAC address */ -static int probe_atmel_card(struct net_device *dev) -{ - int rc = 0; - struct atmel_private *priv = netdev_priv(dev); - - /* reset pccard */ - if (priv->bus_type == BUS_TYPE_PCCARD) - atmel_write16(dev, GCR, 0x0060); - - atmel_write16(dev, GCR, 0x0040); - mdelay(500); - - if (atmel_read16(dev, MR2) == 0) { - /* No stored firmware so load a small stub which just - tells us the MAC address */ - int i; - priv->card_type = CARD_TYPE_EEPROM; - atmel_write16(dev, BSR, BSS_IRAM); - atmel_copy_to_card(dev, 0, mac_reader, sizeof(mac_reader)); - atmel_set_gcr(dev, GCR_REMAP); - atmel_clear_gcr(priv->dev, 0x0040); - atmel_write16(dev, BSR, BSS_SRAM); - for (i = LOOP_RETRY_LIMIT; i; i--) - if (atmel_read16(dev, MR3) & MAC_BOOT_COMPLETE) - break; - if (i == 0) { - printk(KERN_ALERT "%s: MAC failed to boot MAC address reader.\n", dev->name); - } else { - atmel_copy_to_host(dev, dev->dev_addr, atmel_read16(dev, MR2), 6); - /* got address, now squash it again until the network - interface is opened */ - if (priv->bus_type == BUS_TYPE_PCCARD) - atmel_write16(dev, GCR, 0x0060); - atmel_write16(dev, GCR, 0x0040); - rc = 1; - } - } else if (atmel_read16(dev, MR4) == 0) { - /* Mac address easy in this case. */ - priv->card_type = CARD_TYPE_PARALLEL_FLASH; - atmel_write16(dev, BSR, 1); - atmel_copy_to_host(dev, dev->dev_addr, 0xc000, 6); - atmel_write16(dev, BSR, 0x200); - rc = 1; - } else { - /* Standard firmware in flash, boot it up and ask - for the Mac Address */ - priv->card_type = CARD_TYPE_SPI_FLASH; - if (atmel_wakeup_firmware(priv) == 0) { - atmel_get_mib(priv, Mac_Address_Mib_Type, 0, dev->dev_addr, 6); - - /* got address, now squash it again until the network - interface is opened */ - if (priv->bus_type == BUS_TYPE_PCCARD) - atmel_write16(dev, GCR, 0x0060); - atmel_write16(dev, GCR, 0x0040); - rc = 1; - } - } - - if (rc) { - if (dev->dev_addr[0] == 0xFF) { - static const u8 default_mac[] = { - 0x00, 0x04, 0x25, 0x00, 0x00, 0x00 - }; - printk(KERN_ALERT "%s: *** Invalid MAC address. UPGRADE Firmware ****\n", dev->name); - memcpy(dev->dev_addr, default_mac, ETH_ALEN); - } - } - - return rc; -} - -/* Move the encyption information on the MIB structure. - This routine is for the pre-WPA firmware: later firmware has - a different format MIB and a different routine. */ -static void build_wep_mib(struct atmel_private *priv) -{ - struct { /* NB this is matched to the hardware, don't change. */ - u8 wep_is_on; - u8 default_key; /* 0..3 */ - u8 reserved; - u8 exclude_unencrypted; - - u32 WEPICV_error_count; - u32 WEP_excluded_count; - - u8 wep_keys[MAX_ENCRYPTION_KEYS][13]; - u8 encryption_level; /* 0, 1, 2 */ - u8 reserved2[3]; - } mib; - int i; - - mib.wep_is_on = priv->wep_is_on; - if (priv->wep_is_on) { - if (priv->wep_key_len[priv->default_key] > 5) - mib.encryption_level = 2; - else - mib.encryption_level = 1; - } else { - mib.encryption_level = 0; - } - - mib.default_key = priv->default_key; - mib.exclude_unencrypted = priv->exclude_unencrypted; - - for (i = 0; i < MAX_ENCRYPTION_KEYS; i++) - memcpy(mib.wep_keys[i], priv->wep_keys[i], 13); - - atmel_set_mib(priv, Mac_Wep_Mib_Type, 0, (u8 *)&mib, sizeof(mib)); -} - -static void build_wpa_mib(struct atmel_private *priv) -{ - /* This is for the later (WPA enabled) firmware. */ - - struct { /* NB this is matched to the hardware, don't change. */ - u8 cipher_default_key_value[MAX_ENCRYPTION_KEYS][MAX_ENCRYPTION_KEY_SIZE]; - u8 receiver_address[ETH_ALEN]; - u8 wep_is_on; - u8 default_key; /* 0..3 */ - u8 group_key; - u8 exclude_unencrypted; - u8 encryption_type; - u8 reserved; - - u32 WEPICV_error_count; - u32 WEP_excluded_count; - - u8 key_RSC[4][8]; - } mib; - - int i; - - mib.wep_is_on = priv->wep_is_on; - mib.exclude_unencrypted = priv->exclude_unencrypted; - memcpy(mib.receiver_address, priv->CurrentBSSID, ETH_ALEN); - - /* zero all the keys before adding in valid ones. */ - memset(mib.cipher_default_key_value, 0, sizeof(mib.cipher_default_key_value)); - - if (priv->wep_is_on) { - /* There's a comment in the Atmel code to the effect that this - is only valid when still using WEP, it may need to be set to - something to use WPA */ - memset(mib.key_RSC, 0, sizeof(mib.key_RSC)); - - mib.default_key = mib.group_key = 255; - for (i = 0; i < MAX_ENCRYPTION_KEYS; i++) { - if (priv->wep_key_len[i] > 0) { - memcpy(mib.cipher_default_key_value[i], priv->wep_keys[i], MAX_ENCRYPTION_KEY_SIZE); - if (i == priv->default_key) { - mib.default_key = i; - mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-1] = 7; - mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-2] = priv->pairwise_cipher_suite; - } else { - mib.group_key = i; - priv->group_cipher_suite = priv->pairwise_cipher_suite; - mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-1] = 1; - mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-2] = priv->group_cipher_suite; - } - } - } - if (mib.default_key == 255) - mib.default_key = mib.group_key != 255 ? mib.group_key : 0; - if (mib.group_key == 255) - mib.group_key = mib.default_key; - - } - - atmel_set_mib(priv, Mac_Wep_Mib_Type, 0, (u8 *)&mib, sizeof(mib)); -} - -static int reset_atmel_card(struct net_device *dev) -{ - /* do everything necessary to wake up the hardware, including - waiting for the lightning strike and throwing the knife switch.... - - set all the Mib values which matter in the card to match - their settings in the atmel_private structure. Some of these - can be altered on the fly, but many (WEP, infrastucture or ad-hoc) - can only be changed by tearing down the world and coming back through - here. - - This routine is also responsible for initialising some - hardware-specific fields in the atmel_private structure, - including a copy of the firmware's hostinfo structure - which is the route into the rest of the firmware datastructures. */ - - struct atmel_private *priv = netdev_priv(dev); - u8 configuration; - int old_state = priv->station_state; - int err = 0; - - /* data to add to the firmware names, in priority order - this implemenents firmware versioning */ - - static char *firmware_modifier[] = { - "-wpa", - "", - NULL - }; - - /* reset pccard */ - if (priv->bus_type == BUS_TYPE_PCCARD) - atmel_write16(priv->dev, GCR, 0x0060); - - /* stop card , disable interrupts */ - atmel_write16(priv->dev, GCR, 0x0040); - - if (priv->card_type == CARD_TYPE_EEPROM) { - /* copy in firmware if needed */ - const struct firmware *fw_entry = NULL; - const unsigned char *fw; - int len = priv->firmware_length; - if (!(fw = priv->firmware)) { - if (priv->firmware_type == ATMEL_FW_TYPE_NONE) { - if (strlen(priv->firmware_id) == 0) { - printk(KERN_INFO - "%s: card type is unknown: assuming at76c502 firmware is OK.\n", - dev->name); - printk(KERN_INFO - "%s: if not, use the firmware= module parameter.\n", - dev->name); - strcpy(priv->firmware_id, "atmel_at76c502.bin"); - } - err = request_firmware(&fw_entry, priv->firmware_id, priv->sys_dev); - if (err != 0) { - printk(KERN_ALERT - "%s: firmware %s is missing, cannot continue.\n", - dev->name, priv->firmware_id); - return err; - } - } else { - int fw_index = 0; - int success = 0; - - /* get firmware filename entry based on firmware type ID */ - while (fw_table[fw_index].fw_type != priv->firmware_type - && fw_table[fw_index].fw_type != ATMEL_FW_TYPE_NONE) - fw_index++; - - /* construct the actual firmware file name */ - if (fw_table[fw_index].fw_type != ATMEL_FW_TYPE_NONE) { - int i; - for (i = 0; firmware_modifier[i]; i++) { - snprintf(priv->firmware_id, 32, "%s%s.%s", fw_table[fw_index].fw_file, - firmware_modifier[i], fw_table[fw_index].fw_file_ext); - priv->firmware_id[31] = '\0'; - if (request_firmware(&fw_entry, priv->firmware_id, priv->sys_dev) == 0) { - success = 1; - break; - } - } - } - if (!success) { - printk(KERN_ALERT - "%s: firmware %s is missing, cannot start.\n", - dev->name, priv->firmware_id); - priv->firmware_id[0] = '\0'; - return -ENOENT; - } - } - - fw = fw_entry->data; - len = fw_entry->size; - } - - if (len <= 0x6000) { - atmel_write16(priv->dev, BSR, BSS_IRAM); - atmel_copy_to_card(priv->dev, 0, fw, len); - atmel_set_gcr(priv->dev, GCR_REMAP); - } else { - /* Remap */ - atmel_set_gcr(priv->dev, GCR_REMAP); - atmel_write16(priv->dev, BSR, BSS_IRAM); - atmel_copy_to_card(priv->dev, 0, fw, 0x6000); - atmel_write16(priv->dev, BSR, 0x2ff); - atmel_copy_to_card(priv->dev, 0x8000, &fw[0x6000], len - 0x6000); - } - - release_firmware(fw_entry); - } - - err = atmel_wakeup_firmware(priv); - if (err != 0) - return err; - - /* Check the version and set the correct flag for wpa stuff, - old and new firmware is incompatible. - The pre-wpa 3com firmware reports major version 5, - the wpa 3com firmware is major version 4 and doesn't need - the 3com broken-ness filter. */ - priv->use_wpa = (priv->host_info.major_version == 4); - priv->radio_on_broken = (priv->host_info.major_version == 5); - - /* unmask all irq sources */ - atmel_wmem8(priv, atmel_hi(priv, IFACE_INT_MASK_OFFSET), 0xff); - - /* int Tx system and enable Tx */ - atmel_wmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, 0), 0); - atmel_wmem32(priv, atmel_tx(priv, TX_DESC_NEXT_OFFSET, 0), 0x80000000L); - atmel_wmem16(priv, atmel_tx(priv, TX_DESC_POS_OFFSET, 0), 0); - atmel_wmem16(priv, atmel_tx(priv, TX_DESC_SIZE_OFFSET, 0), 0); - - priv->tx_desc_free = priv->host_info.tx_desc_count; - priv->tx_desc_head = 0; - priv->tx_desc_tail = 0; - priv->tx_desc_previous = 0; - priv->tx_free_mem = priv->host_info.tx_buff_size; - priv->tx_buff_head = 0; - priv->tx_buff_tail = 0; - - configuration = atmel_rmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET)); - atmel_wmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET), - configuration | FUNC_CTRL_TxENABLE); - - /* init Rx system and enable */ - priv->rx_desc_head = 0; - - configuration = atmel_rmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET)); - atmel_wmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET), - configuration | FUNC_CTRL_RxENABLE); - - if (!priv->radio_on_broken) { - if (atmel_send_command_wait(priv, CMD_EnableRadio, NULL, 0) == - CMD_STATUS_REJECTED_RADIO_OFF) { - printk(KERN_INFO "%s: cannot turn the radio on.\n", - dev->name); - return -EIO; - } - } - - /* set up enough MIB values to run. */ - atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_AUTO_TX_RATE_POS, priv->auto_tx_rate); - atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_TX_PROMISCUOUS_POS, PROM_MODE_OFF); - atmel_set_mib16(priv, Mac_Mib_Type, MAC_MIB_RTS_THRESHOLD_POS, priv->rts_threshold); - atmel_set_mib16(priv, Mac_Mib_Type, MAC_MIB_FRAG_THRESHOLD_POS, priv->frag_threshold); - atmel_set_mib8(priv, Mac_Mib_Type, MAC_MIB_SHORT_RETRY_POS, priv->short_retry); - atmel_set_mib8(priv, Mac_Mib_Type, MAC_MIB_LONG_RETRY_POS, priv->long_retry); - atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_PREAMBLE_TYPE, priv->preamble); - atmel_set_mib(priv, Mac_Address_Mib_Type, MAC_ADDR_MIB_MAC_ADDR_POS, - priv->dev->dev_addr, 6); - atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_PS_MODE_POS, ACTIVE_MODE); - atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 1); - atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_BEACON_PER_POS, priv->default_beacon_period); - atmel_set_mib(priv, Phy_Mib_Type, PHY_MIB_RATE_SET_POS, atmel_basic_rates, 4); - atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_CUR_PRIVACY_POS, priv->wep_is_on); - if (priv->use_wpa) - build_wpa_mib(priv); - else - build_wep_mib(priv); - - if (old_state == STATION_STATE_READY) { - union iwreq_data wrqu; - - wrqu.data.length = 0; - wrqu.data.flags = 0; - wrqu.ap_addr.sa_family = ARPHRD_ETHER; - eth_zero_addr(wrqu.ap_addr.sa_data); - wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL); - } - - return 0; -} - -static void atmel_send_command(struct atmel_private *priv, int command, - void *cmd, int cmd_size) -{ - if (cmd) - atmel_copy_to_card(priv->dev, atmel_co(priv, CMD_BLOCK_PARAMETERS_OFFSET), - cmd, cmd_size); - - atmel_wmem8(priv, atmel_co(priv, CMD_BLOCK_COMMAND_OFFSET), command); - atmel_wmem8(priv, atmel_co(priv, CMD_BLOCK_STATUS_OFFSET), 0); -} - -static int atmel_send_command_wait(struct atmel_private *priv, int command, - void *cmd, int cmd_size) -{ - int i, status; - - atmel_send_command(priv, command, cmd, cmd_size); - - for (i = 5000; i; i--) { - status = atmel_rmem8(priv, atmel_co(priv, CMD_BLOCK_STATUS_OFFSET)); - if (status != CMD_STATUS_IDLE && - status != CMD_STATUS_IN_PROGRESS) - break; - udelay(20); - } - - if (i == 0) { - printk(KERN_ALERT "%s: failed to contact MAC.\n", priv->dev->name); - status = CMD_STATUS_HOST_ERROR; - } else { - if (command != CMD_EnableRadio) - status = CMD_STATUS_COMPLETE; - } - - return status; -} - -static u8 atmel_get_mib8(struct atmel_private *priv, u8 type, u8 index) -{ - struct get_set_mib m; - m.type = type; - m.size = 1; - m.index = index; - - atmel_send_command_wait(priv, CMD_Get_MIB_Vars, &m, MIB_HEADER_SIZE + 1); - return atmel_rmem8(priv, atmel_co(priv, CMD_BLOCK_PARAMETERS_OFFSET + MIB_HEADER_SIZE)); -} - -static void atmel_set_mib8(struct atmel_private *priv, u8 type, u8 index, u8 data) -{ - struct get_set_mib m; - m.type = type; - m.size = 1; - m.index = index; - m.data[0] = data; - - atmel_send_command_wait(priv, CMD_Set_MIB_Vars, &m, MIB_HEADER_SIZE + 1); -} - -static void atmel_set_mib16(struct atmel_private *priv, u8 type, u8 index, - u16 data) -{ - struct get_set_mib m; - m.type = type; - m.size = 2; - m.index = index; - m.data[0] = data; - m.data[1] = data >> 8; - - atmel_send_command_wait(priv, CMD_Set_MIB_Vars, &m, MIB_HEADER_SIZE + 2); -} - -static void atmel_set_mib(struct atmel_private *priv, u8 type, u8 index, - u8 *data, int data_len) -{ - struct get_set_mib m; - m.type = type; - m.size = data_len; - m.index = index; - - if (data_len > MIB_MAX_DATA_BYTES) - printk(KERN_ALERT "%s: MIB buffer too small.\n", priv->dev->name); - - memcpy(m.data, data, data_len); - atmel_send_command_wait(priv, CMD_Set_MIB_Vars, &m, MIB_HEADER_SIZE + data_len); -} - -static void atmel_get_mib(struct atmel_private *priv, u8 type, u8 index, - u8 *data, int data_len) -{ - struct get_set_mib m; - m.type = type; - m.size = data_len; - m.index = index; - - if (data_len > MIB_MAX_DATA_BYTES) - printk(KERN_ALERT "%s: MIB buffer too small.\n", priv->dev->name); - - atmel_send_command_wait(priv, CMD_Get_MIB_Vars, &m, MIB_HEADER_SIZE + data_len); - atmel_copy_to_host(priv->dev, data, - atmel_co(priv, CMD_BLOCK_PARAMETERS_OFFSET + MIB_HEADER_SIZE), data_len); -} - -static void atmel_writeAR(struct net_device *dev, u16 data) -{ - int i; - outw(data, dev->base_addr + AR); - /* Address register appears to need some convincing..... */ - for (i = 0; data != inw(dev->base_addr + AR) && i < 10; i++) - outw(data, dev->base_addr + AR); -} - -static void atmel_copy_to_card(struct net_device *dev, u16 dest, - const unsigned char *src, u16 len) -{ - int i; - atmel_writeAR(dev, dest); - if (dest % 2) { - atmel_write8(dev, DR, *src); - src++; len--; - } - for (i = len; i > 1 ; i -= 2) { - u8 lb = *src++; - u8 hb = *src++; - atmel_write16(dev, DR, lb | (hb << 8)); - } - if (i) - atmel_write8(dev, DR, *src); -} - -static void atmel_copy_to_host(struct net_device *dev, unsigned char *dest, - u16 src, u16 len) -{ - int i; - atmel_writeAR(dev, src); - if (src % 2) { - *dest = atmel_read8(dev, DR); - dest++; len--; - } - for (i = len; i > 1 ; i -= 2) { - u16 hw = atmel_read16(dev, DR); - *dest++ = hw; - *dest++ = hw >> 8; - } - if (i) - *dest = atmel_read8(dev, DR); -} - -static void atmel_set_gcr(struct net_device *dev, u16 mask) -{ - outw(inw(dev->base_addr + GCR) | mask, dev->base_addr + GCR); -} - -static void atmel_clear_gcr(struct net_device *dev, u16 mask) -{ - outw(inw(dev->base_addr + GCR) & ~mask, dev->base_addr + GCR); -} - -static int atmel_lock_mac(struct atmel_private *priv) -{ - int i, j = 20; - retry: - for (i = 5000; i; i--) { - if (!atmel_rmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_HOST_OFFSET))) - break; - udelay(20); - } - - if (!i) - return 0; /* timed out */ - - atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 1); - if (atmel_rmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_HOST_OFFSET))) { - atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 0); - if (!j--) - return 0; /* timed out */ - goto retry; - } - - return 1; -} - -static void atmel_wmem32(struct atmel_private *priv, u16 pos, u32 data) -{ - atmel_writeAR(priv->dev, pos); - atmel_write16(priv->dev, DR, data); /* card is little-endian */ - atmel_write16(priv->dev, DR, data >> 16); -} - -/***************************************************************************/ -/* There follows the source form of the MAC address reading firmware */ -/***************************************************************************/ -#if 0 - -/* Copyright 2003 Matthew T. Russotto */ -/* But derived from the Atmel 76C502 firmware written by Atmel and */ -/* included in "atmel wireless lan drivers" package */ -/** - This file is part of net.russotto.AtmelMACFW, hereto referred to - as AtmelMACFW - - AtmelMACFW 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. - - AtmelMACFW is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with AtmelMACFW; if not, see . - -****************************************************************************/ -/* This firmware should work on the 76C502 RFMD, RFMD_D, and RFMD_E */ -/* It will probably work on the 76C504 and 76C502 RFMD_3COM */ -/* It only works on SPI EEPROM versions of the card. */ - -/* This firmware initializes the SPI controller and clock, reads the MAC */ -/* address from the EEPROM into SRAM, and puts the SRAM offset of the MAC */ -/* address in MR2, and sets MR3 to 0x10 to indicate it is done */ -/* It also puts a complete copy of the EEPROM in SRAM with the offset in */ -/* MR4, for investigational purposes (maybe we can determine chip type */ -/* from that?) */ - - .org 0 - .set MRBASE, 0x8000000 - .set CPSR_INITIAL, 0xD3 /* IRQ/FIQ disabled, ARM mode, Supervisor state */ - .set CPSR_USER, 0xD1 /* IRQ/FIQ disabled, ARM mode, USER state */ - .set SRAM_BASE, 0x02000000 - .set SP_BASE, 0x0F300000 - .set UNK_BASE, 0x0F000000 /* Some internal device, but which one? */ - .set SPI_CGEN_BASE, 0x0E000000 /* Some internal device, but which one? */ - .set UNK3_BASE, 0x02014000 /* Some internal device, but which one? */ - .set STACK_BASE, 0x5600 - .set SP_SR, 0x10 - .set SP_TDRE, 2 /* status register bit -- TDR empty */ - .set SP_RDRF, 1 /* status register bit -- RDR full */ - .set SP_SWRST, 0x80 - .set SP_SPIEN, 0x1 - .set SP_CR, 0 /* control register */ - .set SP_MR, 4 /* mode register */ - .set SP_RDR, 0x08 /* Read Data Register */ - .set SP_TDR, 0x0C /* Transmit Data Register */ - .set SP_CSR0, 0x30 /* chip select registers */ - .set SP_CSR1, 0x34 - .set SP_CSR2, 0x38 - .set SP_CSR3, 0x3C - .set NVRAM_CMD_RDSR, 5 /* read status register */ - .set NVRAM_CMD_READ, 3 /* read data */ - .set NVRAM_SR_RDY, 1 /* RDY bit. This bit is inverted */ - .set SPI_8CLOCKS, 0xFF /* Writing this to the TDR doesn't do anything to the - serial output, since SO is normally high. But it - does cause 8 clock cycles and thus 8 bits to be - clocked in to the chip. See Atmel's SPI - controller (e.g. AT91M55800) timing and 4K - SPI EEPROM manuals */ - - .set NVRAM_SCRATCH, 0x02000100 /* arbitrary area for scratchpad memory */ - .set NVRAM_IMAGE, 0x02000200 - .set NVRAM_LENGTH, 0x0200 - .set MAC_ADDRESS_MIB, SRAM_BASE - .set MAC_ADDRESS_LENGTH, 6 - .set MAC_BOOT_FLAG, 0x10 - .set MR1, 0 - .set MR2, 4 - .set MR3, 8 - .set MR4, 0xC -RESET_VECTOR: - b RESET_HANDLER -UNDEF_VECTOR: - b HALT1 -SWI_VECTOR: - b HALT1 -IABORT_VECTOR: - b HALT1 -DABORT_VECTOR: -RESERVED_VECTOR: - b HALT1 -IRQ_VECTOR: - b HALT1 -FIQ_VECTOR: - b HALT1 -HALT1: b HALT1 -RESET_HANDLER: - mov r0, #CPSR_INITIAL - msr CPSR_c, r0 /* This is probably unnecessary */ - -/* I'm guessing this is initializing clock generator electronics for SPI */ - ldr r0, =SPI_CGEN_BASE - mov r1, #0 - mov r1, r1, lsl #3 - orr r1, r1, #0 - str r1, [r0] - ldr r1, [r0, #28] - bic r1, r1, #16 - str r1, [r0, #28] - mov r1, #1 - str r1, [r0, #8] - - ldr r0, =MRBASE - mov r1, #0 - strh r1, [r0, #MR1] - strh r1, [r0, #MR2] - strh r1, [r0, #MR3] - strh r1, [r0, #MR4] - - mov sp, #STACK_BASE - bl SP_INIT - mov r0, #10 - bl DELAY9 - bl GET_MAC_ADDR - bl GET_WHOLE_NVRAM - ldr r0, =MRBASE - ldr r1, =MAC_ADDRESS_MIB - strh r1, [r0, #MR2] - ldr r1, =NVRAM_IMAGE - strh r1, [r0, #MR4] - mov r1, #MAC_BOOT_FLAG - strh r1, [r0, #MR3] -HALT2: b HALT2 -.func Get_Whole_NVRAM, GET_WHOLE_NVRAM -GET_WHOLE_NVRAM: - stmdb sp!, {lr} - mov r2, #0 /* 0th bytes of NVRAM */ - mov r3, #NVRAM_LENGTH - mov r1, #0 /* not used in routine */ - ldr r0, =NVRAM_IMAGE - bl NVRAM_XFER - ldmia sp!, {lr} - bx lr -.endfunc - -.func Get_MAC_Addr, GET_MAC_ADDR -GET_MAC_ADDR: - stmdb sp!, {lr} - mov r2, #0x120 /* address of MAC Address within NVRAM */ - mov r3, #MAC_ADDRESS_LENGTH - mov r1, #0 /* not used in routine */ - ldr r0, =MAC_ADDRESS_MIB - bl NVRAM_XFER - ldmia sp!, {lr} - bx lr -.endfunc -.ltorg -.func Delay9, DELAY9 -DELAY9: - adds r0, r0, r0, LSL #3 /* r0 = r0 * 9 */ -DELAYLOOP: - beq DELAY9_done - subs r0, r0, #1 - b DELAYLOOP -DELAY9_done: - bx lr -.endfunc - -.func SP_Init, SP_INIT -SP_INIT: - mov r1, #SP_SWRST - ldr r0, =SP_BASE - str r1, [r0, #SP_CR] /* reset the SPI */ - mov r1, #0 - str r1, [r0, #SP_CR] /* release SPI from reset state */ - mov r1, #SP_SPIEN - str r1, [r0, #SP_MR] /* set the SPI to MASTER mode*/ - str r1, [r0, #SP_CR] /* enable the SPI */ - -/* My guess would be this turns on the SPI clock */ - ldr r3, =SPI_CGEN_BASE - ldr r1, [r3, #28] - orr r1, r1, #0x2000 - str r1, [r3, #28] - - ldr r1, =0x2000c01 - str r1, [r0, #SP_CSR0] - ldr r1, =0x2000201 - str r1, [r0, #SP_CSR1] - str r1, [r0, #SP_CSR2] - str r1, [r0, #SP_CSR3] - ldr r1, [r0, #SP_SR] - ldr r0, [r0, #SP_RDR] - bx lr -.endfunc -.func NVRAM_Init, NVRAM_INIT -NVRAM_INIT: - ldr r1, =SP_BASE - ldr r0, [r1, #SP_RDR] - mov r0, #NVRAM_CMD_RDSR - str r0, [r1, #SP_TDR] -SP_loop1: - ldr r0, [r1, #SP_SR] - tst r0, #SP_TDRE - beq SP_loop1 - - mov r0, #SPI_8CLOCKS - str r0, [r1, #SP_TDR] -SP_loop2: - ldr r0, [r1, #SP_SR] - tst r0, #SP_TDRE - beq SP_loop2 - - ldr r0, [r1, #SP_RDR] -SP_loop3: - ldr r0, [r1, #SP_SR] - tst r0, #SP_RDRF - beq SP_loop3 - - ldr r0, [r1, #SP_RDR] - and r0, r0, #255 - bx lr -.endfunc - -.func NVRAM_Xfer, NVRAM_XFER - /* r0 = dest address */ - /* r1 = not used */ - /* r2 = src address within NVRAM */ - /* r3 = length */ -NVRAM_XFER: - stmdb sp!, {r4, r5, lr} - mov r5, r0 /* save r0 (dest address) */ - mov r4, r3 /* save r3 (length) */ - mov r0, r2, LSR #5 /* SPI memories put A8 in the command field */ - and r0, r0, #8 - add r0, r0, #NVRAM_CMD_READ - ldr r1, =NVRAM_SCRATCH - strb r0, [r1, #0] /* save command in NVRAM_SCRATCH[0] */ - strb r2, [r1, #1] /* save low byte of source address in NVRAM_SCRATCH[1] */ -_local1: - bl NVRAM_INIT - tst r0, #NVRAM_SR_RDY - bne _local1 - mov r0, #20 - bl DELAY9 - mov r2, r4 /* length */ - mov r1, r5 /* dest address */ - mov r0, #2 /* bytes to transfer in command */ - bl NVRAM_XFER2 - ldmia sp!, {r4, r5, lr} - bx lr -.endfunc - -.func NVRAM_Xfer2, NVRAM_XFER2 -NVRAM_XFER2: - stmdb sp!, {r4, r5, r6, lr} - ldr r4, =SP_BASE - mov r3, #0 - cmp r0, #0 - bls _local2 - ldr r5, =NVRAM_SCRATCH -_local4: - ldrb r6, [r5, r3] - str r6, [r4, #SP_TDR] -_local3: - ldr r6, [r4, #SP_SR] - tst r6, #SP_TDRE - beq _local3 - add r3, r3, #1 - cmp r3, r0 /* r0 is # of bytes to send out (command+addr) */ - blo _local4 -_local2: - mov r3, #SPI_8CLOCKS - str r3, [r4, #SP_TDR] - ldr r0, [r4, #SP_RDR] -_local5: - ldr r0, [r4, #SP_SR] - tst r0, #SP_RDRF - beq _local5 - ldr r0, [r4, #SP_RDR] /* what's this byte? It's the byte read while writing the TDR -- nonsense, because the NVRAM doesn't read and write at the same time */ - mov r0, #0 - cmp r2, #0 /* r2 is # of bytes to copy in */ - bls _local6 -_local7: - ldr r5, [r4, #SP_SR] - tst r5, #SP_TDRE - beq _local7 - str r3, [r4, #SP_TDR] /* r3 has SPI_8CLOCKS */ -_local8: - ldr r5, [r4, #SP_SR] - tst r5, #SP_RDRF - beq _local8 - ldr r5, [r4, #SP_RDR] /* but didn't we read this byte above? */ - strb r5, [r1], #1 /* postindexed */ - add r0, r0, #1 - cmp r0, r2 - blo _local7 /* since we don't send another address, the NVRAM must be capable of sequential reads */ -_local6: - mov r0, #200 - bl DELAY9 - ldmia sp!, {r4, r5, r6, lr} - bx lr -#endif diff --git a/drivers/net/wireless/atmel.h b/drivers/net/wireless/atmel.h deleted file mode 100644 index 96f7318cbb04..000000000000 --- a/drivers/net/wireless/atmel.h +++ /dev/null @@ -1,43 +0,0 @@ -/*** -*- linux-c -*- ********************************************************** - - Driver for Atmel at76c502 at76c504 and at76c506 wireless cards. - - Copyright 2005 Dan Williams and Red Hat, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This software is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Atmel wireless lan drivers; if not, see - . - -******************************************************************************/ - -#ifndef _ATMEL_H -#define _ATMEL_H - -typedef enum { - ATMEL_FW_TYPE_NONE = 0, - ATMEL_FW_TYPE_502, - ATMEL_FW_TYPE_502D, - ATMEL_FW_TYPE_502E, - ATMEL_FW_TYPE_502_3COM, - ATMEL_FW_TYPE_504, - ATMEL_FW_TYPE_504_2958, - ATMEL_FW_TYPE_504A_2958, - ATMEL_FW_TYPE_506 -} AtmelFWType; - -struct net_device *init_atmel_card(unsigned short, unsigned long, const AtmelFWType, struct device *, - int (*present_func)(void *), void * ); -void stop_atmel_card( struct net_device *); -int atmel_open( struct net_device * ); - -#endif diff --git a/drivers/net/wireless/atmel/Kconfig b/drivers/net/wireless/atmel/Kconfig new file mode 100644 index 000000000000..a43cfd163254 --- /dev/null +++ b/drivers/net/wireless/atmel/Kconfig @@ -0,0 +1,57 @@ +config WLAN_VENDOR_ATMEL + bool "Atmel devices" + default y + ---help--- + If you have a wireless card belonging to this class, say Y. + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about cards. If you say Y, you will be asked for + your specific card in the following questions. + +if WLAN_VENDOR_ATMEL + +config ATMEL + tristate "Atmel at76c50x chipset 802.11b support" + depends on CFG80211 && (PCI || PCMCIA) + select WIRELESS_EXT + select WEXT_PRIV + 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. + +config PCMCIA_ATMEL + tristate "Atmel at76c502/at76c504 PCMCIA cards" + depends on ATMEL && PCMCIA + select WIRELESS_EXT + select FW_LOADER + select CRC32 + ---help--- + Enable support for PCMCIA cards containing the + Atmel at76c502 and at76c504 chips. + +config AT76C50X_USB + tristate "Atmel at76c503/at76c505/at76c505a USB cards" + depends on MAC80211 && USB + select FW_LOADER + ---help--- + Enable support for USB Wireless devices using Atmel at76c503, + at76c505 or at76c505a chips. + +endif # WLAN_VENDOR_ATMEL diff --git a/drivers/net/wireless/atmel/Makefile b/drivers/net/wireless/atmel/Makefile new file mode 100644 index 000000000000..e62e345f7af6 --- /dev/null +++ b/drivers/net/wireless/atmel/Makefile @@ -0,0 +1,5 @@ +obj-$(CONFIG_ATMEL) += atmel.o +obj-$(CONFIG_PCI_ATMEL) += atmel_pci.o +obj-$(CONFIG_PCMCIA_ATMEL) += atmel_cs.o + +obj-$(CONFIG_AT76C50X_USB) += at76c50x-usb.o diff --git a/drivers/net/wireless/atmel/at76c50x-usb.c b/drivers/net/wireless/atmel/at76c50x-usb.c new file mode 100644 index 000000000000..dab25136214a --- /dev/null +++ b/drivers/net/wireless/atmel/at76c50x-usb.c @@ -0,0 +1,2617 @@ +/* + * at76c503/at76c505 USB driver + * + * Copyright (c) 2002 - 2003 Oliver Kurth + * Copyright (c) 2004 Joerg Albert + * Copyright (c) 2004 Nick Jones + * Copyright (c) 2004 Balint Seeber + * Copyright (c) 2007 Guido Guenther + * Copyright (c) 2007 Kalle Valo + * Copyright (c) 2010 Sebastian Smolorz + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This file is part of the Berlios driver for WLAN USB devices based on the + * Atmel AT76C503A/505/505A. + * + * Some iw_handler code was taken from airo.c, (C) 1999 Benjamin Reed + * + * TODO list is at the wiki: + * + * http://wireless.kernel.org/en/users/Drivers/at76c50x-usb#TODO + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "at76c50x-usb.h" + +/* Version information */ +#define DRIVER_NAME "at76c50x-usb" +#define DRIVER_VERSION "0.17" +#define DRIVER_DESC "Atmel at76x USB Wireless LAN Driver" + +/* at76_debug bits */ +#define DBG_PROGRESS 0x00000001 /* authentication/accociation */ +#define DBG_BSS_TABLE 0x00000002 /* show BSS table after scans */ +#define DBG_IOCTL 0x00000004 /* ioctl calls / settings */ +#define DBG_MAC_STATE 0x00000008 /* MAC state transitions */ +#define DBG_TX_DATA 0x00000010 /* tx header */ +#define DBG_TX_DATA_CONTENT 0x00000020 /* tx content */ +#define DBG_TX_MGMT 0x00000040 /* tx management */ +#define DBG_RX_DATA 0x00000080 /* rx data header */ +#define DBG_RX_DATA_CONTENT 0x00000100 /* rx data content */ +#define DBG_RX_MGMT 0x00000200 /* rx mgmt frame headers */ +#define DBG_RX_BEACON 0x00000400 /* rx beacon */ +#define DBG_RX_CTRL 0x00000800 /* rx control */ +#define DBG_RX_MGMT_CONTENT 0x00001000 /* rx mgmt content */ +#define DBG_RX_FRAGS 0x00002000 /* rx data fragment handling */ +#define DBG_DEVSTART 0x00004000 /* fw download, device start */ +#define DBG_URB 0x00008000 /* rx urb status, ... */ +#define DBG_RX_ATMEL_HDR 0x00010000 /* Atmel-specific Rx headers */ +#define DBG_PROC_ENTRY 0x00020000 /* procedure entries/exits */ +#define DBG_PM 0x00040000 /* power management settings */ +#define DBG_BSS_MATCH 0x00080000 /* BSS match failures */ +#define DBG_PARAMS 0x00100000 /* show configured parameters */ +#define DBG_WAIT_COMPLETE 0x00200000 /* command completion */ +#define DBG_RX_FRAGS_SKB 0x00400000 /* skb header of Rx fragments */ +#define DBG_BSS_TABLE_RM 0x00800000 /* purging bss table entries */ +#define DBG_MONITOR_MODE 0x01000000 /* monitor mode */ +#define DBG_MIB 0x02000000 /* dump all MIBs on startup */ +#define DBG_MGMT_TIMER 0x04000000 /* dump mgmt_timer ops */ +#define DBG_WE_EVENTS 0x08000000 /* dump wireless events */ +#define DBG_FW 0x10000000 /* firmware download */ +#define DBG_DFU 0x20000000 /* device firmware upgrade */ +#define DBG_CMD 0x40000000 +#define DBG_MAC80211 0x80000000 + +#define DBG_DEFAULTS 0 + +/* Use our own dbg macro */ +#define at76_dbg(bits, format, arg...) \ +do { \ + if (at76_debug & (bits)) \ + printk(KERN_DEBUG DRIVER_NAME ": " format "\n", ##arg); \ +} while (0) + +#define at76_dbg_dump(bits, buf, len, format, arg...) \ +do { \ + if (at76_debug & (bits)) { \ + printk(KERN_DEBUG DRIVER_NAME ": " format "\n", ##arg); \ + print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, buf, len); \ + } \ +} while (0) + +static uint at76_debug = DBG_DEFAULTS; + +/* Protect against concurrent firmware loading and parsing */ +static struct mutex fw_mutex; + +static struct fwentry firmwares[] = { + [0] = { "" }, + [BOARD_503_ISL3861] = { "atmel_at76c503-i3861.bin" }, + [BOARD_503_ISL3863] = { "atmel_at76c503-i3863.bin" }, + [BOARD_503] = { "atmel_at76c503-rfmd.bin" }, + [BOARD_503_ACC] = { "atmel_at76c503-rfmd-acc.bin" }, + [BOARD_505] = { "atmel_at76c505-rfmd.bin" }, + [BOARD_505_2958] = { "atmel_at76c505-rfmd2958.bin" }, + [BOARD_505A] = { "atmel_at76c505a-rfmd2958.bin" }, + [BOARD_505AMX] = { "atmel_at76c505amx-rfmd.bin" }, +}; +MODULE_FIRMWARE("atmel_at76c503-i3861.bin"); +MODULE_FIRMWARE("atmel_at76c503-i3863.bin"); +MODULE_FIRMWARE("atmel_at76c503-rfmd.bin"); +MODULE_FIRMWARE("atmel_at76c503-rfmd-acc.bin"); +MODULE_FIRMWARE("atmel_at76c505-rfmd.bin"); +MODULE_FIRMWARE("atmel_at76c505-rfmd2958.bin"); +MODULE_FIRMWARE("atmel_at76c505a-rfmd2958.bin"); +MODULE_FIRMWARE("atmel_at76c505amx-rfmd.bin"); + +#define USB_DEVICE_DATA(__ops) .driver_info = (kernel_ulong_t)(__ops) + +static struct usb_device_id dev_table[] = { + /* + * at76c503-i3861 + */ + /* Generic AT76C503/3861 device */ + { USB_DEVICE(0x03eb, 0x7603), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + /* Linksys WUSB11 v2.1/v2.6 */ + { USB_DEVICE(0x066b, 0x2211), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + /* Netgear MA101 rev. A */ + { USB_DEVICE(0x0864, 0x4100), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + /* Tekram U300C / Allnet ALL0193 */ + { USB_DEVICE(0x0b3b, 0x1612), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + /* HP HN210W J7801A */ + { USB_DEVICE(0x03f0, 0x011c), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + /* Sitecom/Z-Com/Zyxel M4Y-750 */ + { USB_DEVICE(0x0cde, 0x0001), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + /* Dynalink/Askey WLL013 (intersil) */ + { USB_DEVICE(0x069a, 0x0320), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + /* EZ connect 11Mpbs Wireless USB Adapter SMC2662W v1 */ + { USB_DEVICE(0x0d5c, 0xa001), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + /* BenQ AWL300 */ + { USB_DEVICE(0x04a5, 0x9000), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + /* Addtron AWU-120, Compex WLU11 */ + { USB_DEVICE(0x05dd, 0xff31), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + /* Intel AP310 AnyPoint II USB */ + { USB_DEVICE(0x8086, 0x0200), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + /* Dynalink L11U */ + { USB_DEVICE(0x0d8e, 0x7100), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + /* Arescom WL-210, FCC id 07J-GL2411USB */ + { USB_DEVICE(0x0d8e, 0x7110), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + /* I-O DATA WN-B11/USB */ + { USB_DEVICE(0x04bb, 0x0919), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + /* BT Voyager 1010 */ + { USB_DEVICE(0x069a, 0x0821), USB_DEVICE_DATA(BOARD_503_ISL3861) }, + /* + * at76c503-i3863 + */ + /* Generic AT76C503/3863 device */ + { USB_DEVICE(0x03eb, 0x7604), USB_DEVICE_DATA(BOARD_503_ISL3863) }, + /* Samsung SWL-2100U */ + { USB_DEVICE(0x055d, 0xa000), USB_DEVICE_DATA(BOARD_503_ISL3863) }, + /* + * at76c503-rfmd + */ + /* Generic AT76C503/RFMD device */ + { USB_DEVICE(0x03eb, 0x7605), USB_DEVICE_DATA(BOARD_503) }, + /* Dynalink/Askey WLL013 (rfmd) */ + { USB_DEVICE(0x069a, 0x0321), USB_DEVICE_DATA(BOARD_503) }, + /* Linksys WUSB11 v2.6 */ + { USB_DEVICE(0x077b, 0x2219), USB_DEVICE_DATA(BOARD_503) }, + /* Network Everywhere NWU11B */ + { USB_DEVICE(0x077b, 0x2227), USB_DEVICE_DATA(BOARD_503) }, + /* Netgear MA101 rev. B */ + { USB_DEVICE(0x0864, 0x4102), USB_DEVICE_DATA(BOARD_503) }, + /* D-Link DWL-120 rev. E */ + { USB_DEVICE(0x2001, 0x3200), USB_DEVICE_DATA(BOARD_503) }, + /* Actiontec 802UAT1, HWU01150-01UK */ + { USB_DEVICE(0x1668, 0x7605), USB_DEVICE_DATA(BOARD_503) }, + /* AirVast W-Buddie WN210 */ + { USB_DEVICE(0x03eb, 0x4102), USB_DEVICE_DATA(BOARD_503) }, + /* Dick Smith Electronics XH1153 802.11b USB adapter */ + { USB_DEVICE(0x1371, 0x5743), USB_DEVICE_DATA(BOARD_503) }, + /* CNet CNUSB611 */ + { USB_DEVICE(0x1371, 0x0001), USB_DEVICE_DATA(BOARD_503) }, + /* FiberLine FL-WL200U */ + { USB_DEVICE(0x1371, 0x0002), USB_DEVICE_DATA(BOARD_503) }, + /* BenQ AWL400 USB stick */ + { USB_DEVICE(0x04a5, 0x9001), USB_DEVICE_DATA(BOARD_503) }, + /* 3Com 3CRSHEW696 */ + { USB_DEVICE(0x0506, 0x0a01), USB_DEVICE_DATA(BOARD_503) }, + /* Siemens Santis ADSL WLAN USB adapter WLL 013 */ + { USB_DEVICE(0x0681, 0x001b), USB_DEVICE_DATA(BOARD_503) }, + /* Belkin F5D6050, version 2 */ + { USB_DEVICE(0x050d, 0x0050), USB_DEVICE_DATA(BOARD_503) }, + /* iBlitzz, BWU613 (not *B or *SB) */ + { USB_DEVICE(0x07b8, 0xb000), USB_DEVICE_DATA(BOARD_503) }, + /* Gigabyte GN-WLBM101 */ + { USB_DEVICE(0x1044, 0x8003), USB_DEVICE_DATA(BOARD_503) }, + /* Planex GW-US11S */ + { USB_DEVICE(0x2019, 0x3220), USB_DEVICE_DATA(BOARD_503) }, + /* Internal WLAN adapter in h5[4,5]xx series iPAQs */ + { USB_DEVICE(0x049f, 0x0032), USB_DEVICE_DATA(BOARD_503) }, + /* Corega Wireless LAN USB-11 mini */ + { USB_DEVICE(0x07aa, 0x0011), USB_DEVICE_DATA(BOARD_503) }, + /* Corega Wireless LAN USB-11 mini2 */ + { USB_DEVICE(0x07aa, 0x0018), USB_DEVICE_DATA(BOARD_503) }, + /* Uniden PCW100 */ + { USB_DEVICE(0x05dd, 0xff35), USB_DEVICE_DATA(BOARD_503) }, + /* + * at76c503-rfmd-acc + */ + /* SMC2664W */ + { USB_DEVICE(0x083a, 0x3501), USB_DEVICE_DATA(BOARD_503_ACC) }, + /* Belkin F5D6050, SMC2662W v2, SMC2662W-AR */ + { USB_DEVICE(0x0d5c, 0xa002), USB_DEVICE_DATA(BOARD_503_ACC) }, + /* + * at76c505-rfmd + */ + /* Generic AT76C505/RFMD */ + { USB_DEVICE(0x03eb, 0x7606), USB_DEVICE_DATA(BOARD_505) }, + /* + * at76c505-rfmd2958 + */ + /* Generic AT76C505/RFMD, OvisLink WL-1130USB */ + { USB_DEVICE(0x03eb, 0x7613), USB_DEVICE_DATA(BOARD_505_2958) }, + /* Fiberline FL-WL240U */ + { USB_DEVICE(0x1371, 0x0014), USB_DEVICE_DATA(BOARD_505_2958) }, + /* CNet CNUSB-611G */ + { USB_DEVICE(0x1371, 0x0013), USB_DEVICE_DATA(BOARD_505_2958) }, + /* Linksys WUSB11 v2.8 */ + { USB_DEVICE(0x1915, 0x2233), USB_DEVICE_DATA(BOARD_505_2958) }, + /* Xterasys XN-2122B, IBlitzz BWU613B/BWU613SB */ + { USB_DEVICE(0x12fd, 0x1001), USB_DEVICE_DATA(BOARD_505_2958) }, + /* Corega WLAN USB Stick 11 */ + { USB_DEVICE(0x07aa, 0x7613), USB_DEVICE_DATA(BOARD_505_2958) }, + /* Microstar MSI Box MS6978 */ + { USB_DEVICE(0x0db0, 0x1020), USB_DEVICE_DATA(BOARD_505_2958) }, + /* + * at76c505a-rfmd2958 + */ + /* Generic AT76C505A device */ + { USB_DEVICE(0x03eb, 0x7614), USB_DEVICE_DATA(BOARD_505A) }, + /* Generic AT76C505AS device */ + { USB_DEVICE(0x03eb, 0x7617), USB_DEVICE_DATA(BOARD_505A) }, + /* Siemens Gigaset USB WLAN Adapter 11 */ + { USB_DEVICE(0x1690, 0x0701), USB_DEVICE_DATA(BOARD_505A) }, + /* OQO Model 01+ Internal Wi-Fi */ + { USB_DEVICE(0x1557, 0x0002), USB_DEVICE_DATA(BOARD_505A) }, + /* + * at76c505amx-rfmd + */ + /* Generic AT76C505AMX device */ + { USB_DEVICE(0x03eb, 0x7615), USB_DEVICE_DATA(BOARD_505AMX) }, + { } +}; + +MODULE_DEVICE_TABLE(usb, dev_table); + +/* Supported rates of this hardware, bit 7 marks basic rates */ +static const u8 hw_rates[] = { 0x82, 0x84, 0x0b, 0x16 }; + +static const char *const preambles[] = { "long", "short", "auto" }; + +/* Firmware download */ +/* DFU states */ +#define STATE_IDLE 0x00 +#define STATE_DETACH 0x01 +#define STATE_DFU_IDLE 0x02 +#define STATE_DFU_DOWNLOAD_SYNC 0x03 +#define STATE_DFU_DOWNLOAD_BUSY 0x04 +#define STATE_DFU_DOWNLOAD_IDLE 0x05 +#define STATE_DFU_MANIFEST_SYNC 0x06 +#define STATE_DFU_MANIFEST 0x07 +#define STATE_DFU_MANIFEST_WAIT_RESET 0x08 +#define STATE_DFU_UPLOAD_IDLE 0x09 +#define STATE_DFU_ERROR 0x0a + +/* DFU commands */ +#define DFU_DETACH 0 +#define DFU_DNLOAD 1 +#define DFU_UPLOAD 2 +#define DFU_GETSTATUS 3 +#define DFU_CLRSTATUS 4 +#define DFU_GETSTATE 5 +#define DFU_ABORT 6 + +#define FW_BLOCK_SIZE 1024 + +struct dfu_status { + unsigned char status; + unsigned char poll_timeout[3]; + unsigned char state; + unsigned char string; +} __packed; + +static inline int at76_is_intersil(enum board_type board) +{ + return (board == BOARD_503_ISL3861 || board == BOARD_503_ISL3863); +} + +static inline int at76_is_503rfmd(enum board_type board) +{ + return (board == BOARD_503 || board == BOARD_503_ACC); +} + +static inline int at76_is_505a(enum board_type board) +{ + return (board == BOARD_505A || board == BOARD_505AMX); +} + +/* Load a block of the first (internal) part of the firmware */ +static int at76_load_int_fw_block(struct usb_device *udev, int blockno, + void *block, int size) +{ + return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), DFU_DNLOAD, + USB_TYPE_CLASS | USB_DIR_OUT | + USB_RECIP_INTERFACE, blockno, 0, block, size, + USB_CTRL_GET_TIMEOUT); +} + +static int at76_dfu_get_status(struct usb_device *udev, + struct dfu_status *status) +{ + int ret; + + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), DFU_GETSTATUS, + USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE, + 0, 0, status, sizeof(struct dfu_status), + USB_CTRL_GET_TIMEOUT); + return ret; +} + +static int at76_dfu_get_state(struct usb_device *udev, u8 *state) +{ + int ret; + + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), DFU_GETSTATE, + USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE, + 0, 0, state, 1, USB_CTRL_GET_TIMEOUT); + return ret; +} + +/* Convert timeout from the DFU status to jiffies */ +static inline unsigned long at76_get_timeout(struct dfu_status *s) +{ + return msecs_to_jiffies((s->poll_timeout[2] << 16) + | (s->poll_timeout[1] << 8) + | (s->poll_timeout[0])); +} + +/* Load internal firmware from the buffer. If manifest_sync_timeout > 0, use + * its value in jiffies in the MANIFEST_SYNC state. */ +static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size, + int manifest_sync_timeout) +{ + int ret = 0; + int need_dfu_state = 1; + int is_done = 0; + u32 dfu_timeout = 0; + int bsize = 0; + int blockno = 0; + struct dfu_status *dfu_stat_buf = NULL; + u8 *dfu_state = NULL; + u8 *block = NULL; + + at76_dbg(DBG_DFU, "%s( %p, %u, %d)", __func__, buf, size, + manifest_sync_timeout); + + if (!size) { + dev_err(&udev->dev, "FW buffer length invalid!\n"); + return -EINVAL; + } + + dfu_stat_buf = kmalloc(sizeof(struct dfu_status), GFP_KERNEL); + if (!dfu_stat_buf) { + ret = -ENOMEM; + goto exit; + } + + block = kmalloc(FW_BLOCK_SIZE, GFP_KERNEL); + if (!block) { + ret = -ENOMEM; + goto exit; + } + + dfu_state = kmalloc(sizeof(u8), GFP_KERNEL); + if (!dfu_state) { + ret = -ENOMEM; + goto exit; + } + *dfu_state = 0; + + do { + if (need_dfu_state) { + ret = at76_dfu_get_state(udev, dfu_state); + if (ret < 0) { + dev_err(&udev->dev, + "cannot get DFU state: %d\n", ret); + goto exit; + } + need_dfu_state = 0; + } + + switch (*dfu_state) { + case STATE_DFU_DOWNLOAD_SYNC: + at76_dbg(DBG_DFU, "STATE_DFU_DOWNLOAD_SYNC"); + ret = at76_dfu_get_status(udev, dfu_stat_buf); + if (ret >= 0) { + *dfu_state = dfu_stat_buf->state; + dfu_timeout = at76_get_timeout(dfu_stat_buf); + need_dfu_state = 0; + } else + dev_err(&udev->dev, + "at76_dfu_get_status returned %d\n", + ret); + break; + + case STATE_DFU_DOWNLOAD_BUSY: + at76_dbg(DBG_DFU, "STATE_DFU_DOWNLOAD_BUSY"); + need_dfu_state = 1; + + at76_dbg(DBG_DFU, "DFU: Resetting device"); + schedule_timeout_interruptible(dfu_timeout); + break; + + case STATE_DFU_DOWNLOAD_IDLE: + at76_dbg(DBG_DFU, "DOWNLOAD..."); + /* fall through */ + case STATE_DFU_IDLE: + at76_dbg(DBG_DFU, "DFU IDLE"); + + bsize = min_t(int, size, FW_BLOCK_SIZE); + memcpy(block, buf, bsize); + at76_dbg(DBG_DFU, "int fw, size left = %5d, " + "bsize = %4d, blockno = %2d", size, bsize, + blockno); + ret = + at76_load_int_fw_block(udev, blockno, block, bsize); + buf += bsize; + size -= bsize; + blockno++; + + if (ret != bsize) + dev_err(&udev->dev, + "at76_load_int_fw_block returned %d\n", + ret); + need_dfu_state = 1; + break; + + case STATE_DFU_MANIFEST_SYNC: + at76_dbg(DBG_DFU, "STATE_DFU_MANIFEST_SYNC"); + + ret = at76_dfu_get_status(udev, dfu_stat_buf); + if (ret < 0) + break; + + *dfu_state = dfu_stat_buf->state; + dfu_timeout = at76_get_timeout(dfu_stat_buf); + need_dfu_state = 0; + + /* override the timeout from the status response, + needed for AT76C505A */ + if (manifest_sync_timeout > 0) + dfu_timeout = manifest_sync_timeout; + + at76_dbg(DBG_DFU, "DFU: Waiting for manifest phase"); + schedule_timeout_interruptible(dfu_timeout); + break; + + case STATE_DFU_MANIFEST: + at76_dbg(DBG_DFU, "STATE_DFU_MANIFEST"); + is_done = 1; + break; + + case STATE_DFU_MANIFEST_WAIT_RESET: + at76_dbg(DBG_DFU, "STATE_DFU_MANIFEST_WAIT_RESET"); + is_done = 1; + break; + + case STATE_DFU_UPLOAD_IDLE: + at76_dbg(DBG_DFU, "STATE_DFU_UPLOAD_IDLE"); + break; + + case STATE_DFU_ERROR: + at76_dbg(DBG_DFU, "STATE_DFU_ERROR"); + ret = -EPIPE; + break; + + default: + at76_dbg(DBG_DFU, "DFU UNKNOWN STATE (%d)", *dfu_state); + ret = -EINVAL; + break; + } + } while (!is_done && (ret >= 0)); + +exit: + kfree(dfu_state); + kfree(block); + kfree(dfu_stat_buf); + + if (ret >= 0) + ret = 0; + + return ret; +} + +/* LED trigger */ +static int tx_activity; +static void at76_ledtrig_tx_timerfunc(unsigned long data); +static DEFINE_TIMER(ledtrig_tx_timer, at76_ledtrig_tx_timerfunc, 0, 0); +DEFINE_LED_TRIGGER(ledtrig_tx); + +static void at76_ledtrig_tx_timerfunc(unsigned long data) +{ + static int tx_lastactivity; + + if (tx_lastactivity != tx_activity) { + tx_lastactivity = tx_activity; + led_trigger_event(ledtrig_tx, LED_FULL); + mod_timer(&ledtrig_tx_timer, jiffies + HZ / 4); + } else + led_trigger_event(ledtrig_tx, LED_OFF); +} + +static void at76_ledtrig_tx_activity(void) +{ + tx_activity++; + if (!timer_pending(&ledtrig_tx_timer)) + mod_timer(&ledtrig_tx_timer, jiffies + HZ / 4); +} + +static int at76_remap(struct usb_device *udev) +{ + int ret; + ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x0a, + USB_TYPE_VENDOR | USB_DIR_OUT | + USB_RECIP_INTERFACE, 0, 0, NULL, 0, + USB_CTRL_GET_TIMEOUT); + if (ret < 0) + return ret; + return 0; +} + +static int at76_get_op_mode(struct usb_device *udev) +{ + int ret; + u8 saved; + u8 *op_mode; + + op_mode = kmalloc(1, GFP_NOIO); + if (!op_mode) + return -ENOMEM; + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x33, + USB_TYPE_VENDOR | USB_DIR_IN | + USB_RECIP_INTERFACE, 0x01, 0, op_mode, 1, + USB_CTRL_GET_TIMEOUT); + saved = *op_mode; + kfree(op_mode); + + if (ret < 0) + return ret; + else if (ret < 1) + return -EIO; + else + return saved; +} + +/* Load a block of the second ("external") part of the firmware */ +static inline int at76_load_ext_fw_block(struct usb_device *udev, int blockno, + void *block, int size) +{ + return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x0e, + USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, + 0x0802, blockno, block, size, + USB_CTRL_GET_TIMEOUT); +} + +static inline int at76_get_hw_cfg(struct usb_device *udev, + union at76_hwcfg *buf, int buf_size) +{ + return usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x33, + USB_TYPE_VENDOR | USB_DIR_IN | + USB_RECIP_INTERFACE, 0x0a02, 0, + buf, buf_size, USB_CTRL_GET_TIMEOUT); +} + +/* Intersil boards use a different "value" for GetHWConfig requests */ +static inline int at76_get_hw_cfg_intersil(struct usb_device *udev, + union at76_hwcfg *buf, int buf_size) +{ + return usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x33, + USB_TYPE_VENDOR | USB_DIR_IN | + USB_RECIP_INTERFACE, 0x0902, 0, + buf, buf_size, USB_CTRL_GET_TIMEOUT); +} + +/* Get the hardware configuration for the adapter and put it to the appropriate + * fields of 'priv' (the GetHWConfig request and interpretation of the result + * depends on the board type) */ +static int at76_get_hw_config(struct at76_priv *priv) +{ + int ret; + union at76_hwcfg *hwcfg = kmalloc(sizeof(*hwcfg), GFP_KERNEL); + + if (!hwcfg) + return -ENOMEM; + + if (at76_is_intersil(priv->board_type)) { + ret = at76_get_hw_cfg_intersil(priv->udev, hwcfg, + sizeof(hwcfg->i)); + if (ret < 0) + goto exit; + memcpy(priv->mac_addr, hwcfg->i.mac_addr, ETH_ALEN); + priv->regulatory_domain = hwcfg->i.regulatory_domain; + } else if (at76_is_503rfmd(priv->board_type)) { + ret = at76_get_hw_cfg(priv->udev, hwcfg, sizeof(hwcfg->r3)); + if (ret < 0) + goto exit; + memcpy(priv->mac_addr, hwcfg->r3.mac_addr, ETH_ALEN); + priv->regulatory_domain = hwcfg->r3.regulatory_domain; + } else { + ret = at76_get_hw_cfg(priv->udev, hwcfg, sizeof(hwcfg->r5)); + if (ret < 0) + goto exit; + memcpy(priv->mac_addr, hwcfg->r5.mac_addr, ETH_ALEN); + priv->regulatory_domain = hwcfg->r5.regulatory_domain; + } + +exit: + kfree(hwcfg); + if (ret < 0) + wiphy_err(priv->hw->wiphy, "cannot get HW Config (error %d)\n", + ret); + + return ret; +} + +static struct reg_domain const *at76_get_reg_domain(u16 code) +{ + int i; + static struct reg_domain const fd_tab[] = { + { 0x10, "FCC (USA)", 0x7ff }, /* ch 1-11 */ + { 0x20, "IC (Canada)", 0x7ff }, /* ch 1-11 */ + { 0x30, "ETSI (most of Europe)", 0x1fff }, /* ch 1-13 */ + { 0x31, "Spain", 0x600 }, /* ch 10-11 */ + { 0x32, "France", 0x1e00 }, /* ch 10-13 */ + { 0x40, "MKK (Japan)", 0x2000 }, /* ch 14 */ + { 0x41, "MKK1 (Japan)", 0x3fff }, /* ch 1-14 */ + { 0x50, "Israel", 0x3fc }, /* ch 3-9 */ + { 0x00, "", 0xffffffff } /* ch 1-32 */ + }; + + /* Last entry is fallback for unknown domain code */ + for (i = 0; i < ARRAY_SIZE(fd_tab) - 1; i++) + if (code == fd_tab[i].code) + break; + + return &fd_tab[i]; +} + +static inline int at76_get_mib(struct usb_device *udev, u16 mib, void *buf, + int buf_size) +{ + int ret; + + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x33, + USB_TYPE_VENDOR | USB_DIR_IN | + USB_RECIP_INTERFACE, mib << 8, 0, buf, buf_size, + USB_CTRL_GET_TIMEOUT); + if (ret >= 0 && ret != buf_size) + return -EIO; + return ret; +} + +/* Return positive number for status, negative for an error */ +static inline int at76_get_cmd_status(struct usb_device *udev, u8 cmd) +{ + u8 *stat_buf; + int ret; + + stat_buf = kmalloc(40, GFP_NOIO); + if (!stat_buf) + return -ENOMEM; + + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x22, + USB_TYPE_VENDOR | USB_DIR_IN | + USB_RECIP_INTERFACE, cmd, 0, stat_buf, + 40, USB_CTRL_GET_TIMEOUT); + if (ret >= 0) + ret = stat_buf[5]; + kfree(stat_buf); + + return ret; +} + +#define MAKE_CMD_CASE(c) case (c): return #c +static const char *at76_get_cmd_string(u8 cmd_status) +{ + switch (cmd_status) { + MAKE_CMD_CASE(CMD_SET_MIB); + MAKE_CMD_CASE(CMD_GET_MIB); + MAKE_CMD_CASE(CMD_SCAN); + MAKE_CMD_CASE(CMD_JOIN); + MAKE_CMD_CASE(CMD_START_IBSS); + MAKE_CMD_CASE(CMD_RADIO_ON); + MAKE_CMD_CASE(CMD_RADIO_OFF); + MAKE_CMD_CASE(CMD_STARTUP); + } + + return "UNKNOWN"; +} + +static int at76_set_card_command(struct usb_device *udev, u8 cmd, void *buf, + int buf_size) +{ + int ret; + struct at76_command *cmd_buf = kmalloc(sizeof(struct at76_command) + + buf_size, GFP_KERNEL); + + if (!cmd_buf) + return -ENOMEM; + + cmd_buf->cmd = cmd; + cmd_buf->reserved = 0; + cmd_buf->size = cpu_to_le16(buf_size); + memcpy(cmd_buf->data, buf, buf_size); + + at76_dbg_dump(DBG_CMD, cmd_buf, sizeof(struct at76_command) + buf_size, + "issuing command %s (0x%02x)", + at76_get_cmd_string(cmd), cmd); + + ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x0e, + USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, + 0, 0, cmd_buf, + sizeof(struct at76_command) + buf_size, + USB_CTRL_GET_TIMEOUT); + kfree(cmd_buf); + return ret; +} + +#define MAKE_CMD_STATUS_CASE(c) case (c): return #c +static const char *at76_get_cmd_status_string(u8 cmd_status) +{ + switch (cmd_status) { + MAKE_CMD_STATUS_CASE(CMD_STATUS_IDLE); + MAKE_CMD_STATUS_CASE(CMD_STATUS_COMPLETE); + MAKE_CMD_STATUS_CASE(CMD_STATUS_UNKNOWN); + MAKE_CMD_STATUS_CASE(CMD_STATUS_INVALID_PARAMETER); + MAKE_CMD_STATUS_CASE(CMD_STATUS_FUNCTION_NOT_SUPPORTED); + MAKE_CMD_STATUS_CASE(CMD_STATUS_TIME_OUT); + MAKE_CMD_STATUS_CASE(CMD_STATUS_IN_PROGRESS); + MAKE_CMD_STATUS_CASE(CMD_STATUS_HOST_FAILURE); + MAKE_CMD_STATUS_CASE(CMD_STATUS_SCAN_FAILED); + } + + return "UNKNOWN"; +} + +/* Wait until the command is completed */ +static int at76_wait_completion(struct at76_priv *priv, int cmd) +{ + int status = 0; + unsigned long timeout = jiffies + CMD_COMPLETION_TIMEOUT; + + do { + status = at76_get_cmd_status(priv->udev, cmd); + if (status < 0) { + wiphy_err(priv->hw->wiphy, + "at76_get_cmd_status failed: %d\n", + status); + break; + } + + at76_dbg(DBG_WAIT_COMPLETE, + "%s: Waiting on cmd %d, status = %d (%s)", + wiphy_name(priv->hw->wiphy), cmd, status, + at76_get_cmd_status_string(status)); + + if (status != CMD_STATUS_IN_PROGRESS + && status != CMD_STATUS_IDLE) + break; + + schedule_timeout_interruptible(HZ / 10); /* 100 ms */ + if (time_after(jiffies, timeout)) { + wiphy_err(priv->hw->wiphy, + "completion timeout for command %d\n", cmd); + status = -ETIMEDOUT; + break; + } + } while (1); + + return status; +} + +static int at76_set_mib(struct at76_priv *priv, struct set_mib_buffer *buf) +{ + int ret; + + ret = at76_set_card_command(priv->udev, CMD_SET_MIB, buf, + offsetof(struct set_mib_buffer, + data) + buf->size); + if (ret < 0) + return ret; + + ret = at76_wait_completion(priv, CMD_SET_MIB); + if (ret != CMD_STATUS_COMPLETE) { + wiphy_info(priv->hw->wiphy, + "set_mib: at76_wait_completion failed with %d\n", + ret); + ret = -EIO; + } + + return ret; +} + +/* Return < 0 on error, == 0 if no command sent, == 1 if cmd sent */ +static int at76_set_radio(struct at76_priv *priv, int enable) +{ + int ret; + int cmd; + + if (priv->radio_on == enable) + return 0; + + cmd = enable ? CMD_RADIO_ON : CMD_RADIO_OFF; + + ret = at76_set_card_command(priv->udev, cmd, NULL, 0); + if (ret < 0) + wiphy_err(priv->hw->wiphy, + "at76_set_card_command(%d) failed: %d\n", cmd, ret); + else + ret = 1; + + priv->radio_on = enable; + return ret; +} + +/* Set current power save mode (AT76_PM_OFF/AT76_PM_ON/AT76_PM_SMART) */ +static int at76_set_pm_mode(struct at76_priv *priv) +{ + int ret = 0; + + priv->mib_buf.type = MIB_MAC_MGMT; + priv->mib_buf.size = 1; + priv->mib_buf.index = offsetof(struct mib_mac_mgmt, power_mgmt_mode); + priv->mib_buf.data.byte = priv->pm_mode; + + ret = at76_set_mib(priv, &priv->mib_buf); + if (ret < 0) + wiphy_err(priv->hw->wiphy, "set_mib (pm_mode) failed: %d\n", + ret); + + return ret; +} + +static int at76_set_preamble(struct at76_priv *priv, u8 type) +{ + int ret = 0; + + priv->mib_buf.type = MIB_LOCAL; + priv->mib_buf.size = 1; + priv->mib_buf.index = offsetof(struct mib_local, preamble_type); + priv->mib_buf.data.byte = type; + + ret = at76_set_mib(priv, &priv->mib_buf); + if (ret < 0) + wiphy_err(priv->hw->wiphy, "set_mib (preamble) failed: %d\n", + ret); + + return ret; +} + +static int at76_set_frag(struct at76_priv *priv, u16 size) +{ + int ret = 0; + + priv->mib_buf.type = MIB_MAC; + priv->mib_buf.size = 2; + priv->mib_buf.index = offsetof(struct mib_mac, frag_threshold); + priv->mib_buf.data.word = cpu_to_le16(size); + + ret = at76_set_mib(priv, &priv->mib_buf); + if (ret < 0) + wiphy_err(priv->hw->wiphy, + "set_mib (frag threshold) failed: %d\n", ret); + + return ret; +} + +static int at76_set_rts(struct at76_priv *priv, u16 size) +{ + int ret = 0; + + priv->mib_buf.type = MIB_MAC; + priv->mib_buf.size = 2; + priv->mib_buf.index = offsetof(struct mib_mac, rts_threshold); + priv->mib_buf.data.word = cpu_to_le16(size); + + ret = at76_set_mib(priv, &priv->mib_buf); + if (ret < 0) + wiphy_err(priv->hw->wiphy, "set_mib (rts) failed: %d\n", ret); + + return ret; +} + +static int at76_set_autorate_fallback(struct at76_priv *priv, int onoff) +{ + int ret = 0; + + priv->mib_buf.type = MIB_LOCAL; + priv->mib_buf.size = 1; + priv->mib_buf.index = offsetof(struct mib_local, txautorate_fallback); + priv->mib_buf.data.byte = onoff; + + ret = at76_set_mib(priv, &priv->mib_buf); + if (ret < 0) + wiphy_err(priv->hw->wiphy, + "set_mib (autorate fallback) failed: %d\n", ret); + + return ret; +} + +static void at76_dump_mib_mac_addr(struct at76_priv *priv) +{ + int i; + int ret; + struct mib_mac_addr *m = kmalloc(sizeof(struct mib_mac_addr), + GFP_KERNEL); + + if (!m) + return; + + ret = at76_get_mib(priv->udev, MIB_MAC_ADDR, m, + sizeof(struct mib_mac_addr)); + if (ret < 0) { + wiphy_err(priv->hw->wiphy, + "at76_get_mib (MAC_ADDR) failed: %d\n", ret); + goto exit; + } + + at76_dbg(DBG_MIB, "%s: MIB MAC_ADDR: mac_addr %pM res 0x%x 0x%x", + wiphy_name(priv->hw->wiphy), + m->mac_addr, m->res[0], m->res[1]); + for (i = 0; i < ARRAY_SIZE(m->group_addr); i++) + at76_dbg(DBG_MIB, "%s: MIB MAC_ADDR: group addr %d: %pM, " + "status %d", wiphy_name(priv->hw->wiphy), i, + m->group_addr[i], m->group_addr_status[i]); +exit: + kfree(m); +} + +static void at76_dump_mib_mac_wep(struct at76_priv *priv) +{ + int i; + int ret; + int key_len; + struct mib_mac_wep *m = kmalloc(sizeof(struct mib_mac_wep), GFP_KERNEL); + + if (!m) + return; + + ret = at76_get_mib(priv->udev, MIB_MAC_WEP, m, + sizeof(struct mib_mac_wep)); + if (ret < 0) { + wiphy_err(priv->hw->wiphy, + "at76_get_mib (MAC_WEP) failed: %d\n", ret); + goto exit; + } + + at76_dbg(DBG_MIB, "%s: MIB MAC_WEP: priv_invoked %u def_key_id %u " + "key_len %u excl_unencr %u wep_icv_err %u wep_excluded %u " + "encr_level %u key %d", wiphy_name(priv->hw->wiphy), + m->privacy_invoked, m->wep_default_key_id, + m->wep_key_mapping_len, m->exclude_unencrypted, + le32_to_cpu(m->wep_icv_error_count), + le32_to_cpu(m->wep_excluded_count), m->encryption_level, + m->wep_default_key_id); + + key_len = (m->encryption_level == 1) ? + WEP_SMALL_KEY_LEN : WEP_LARGE_KEY_LEN; + + for (i = 0; i < WEP_KEYS; i++) + at76_dbg(DBG_MIB, "%s: MIB MAC_WEP: key %d: %*phD", + wiphy_name(priv->hw->wiphy), i, + key_len, m->wep_default_keyvalue[i]); +exit: + kfree(m); +} + +static void at76_dump_mib_mac_mgmt(struct at76_priv *priv) +{ + int ret; + struct mib_mac_mgmt *m = kmalloc(sizeof(struct mib_mac_mgmt), + GFP_KERNEL); + + if (!m) + return; + + ret = at76_get_mib(priv->udev, MIB_MAC_MGMT, m, + sizeof(struct mib_mac_mgmt)); + if (ret < 0) { + wiphy_err(priv->hw->wiphy, + "at76_get_mib (MAC_MGMT) failed: %d\n", ret); + goto exit; + } + + at76_dbg(DBG_MIB, "%s: MIB MAC_MGMT: beacon_period %d CFP_max_duration " + "%d medium_occupancy_limit %d station_id 0x%x ATIM_window %d " + "CFP_mode %d privacy_opt_impl %d DTIM_period %d CFP_period %d " + "current_bssid %pM current_essid %*phD current_bss_type %d " + "pm_mode %d ibss_change %d res %d " + "multi_domain_capability_implemented %d " + "international_roaming %d country_string %.3s", + wiphy_name(priv->hw->wiphy), le16_to_cpu(m->beacon_period), + le16_to_cpu(m->CFP_max_duration), + le16_to_cpu(m->medium_occupancy_limit), + le16_to_cpu(m->station_id), le16_to_cpu(m->ATIM_window), + m->CFP_mode, m->privacy_option_implemented, m->DTIM_period, + m->CFP_period, m->current_bssid, + IW_ESSID_MAX_SIZE, m->current_essid, + m->current_bss_type, m->power_mgmt_mode, m->ibss_change, + m->res, m->multi_domain_capability_implemented, + m->multi_domain_capability_enabled, m->country_string); +exit: + kfree(m); +} + +static void at76_dump_mib_mac(struct at76_priv *priv) +{ + int ret; + struct mib_mac *m = kmalloc(sizeof(struct mib_mac), GFP_KERNEL); + + if (!m) + return; + + ret = at76_get_mib(priv->udev, MIB_MAC, m, sizeof(struct mib_mac)); + if (ret < 0) { + wiphy_err(priv->hw->wiphy, + "at76_get_mib (MAC) failed: %d\n", ret); + goto exit; + } + + at76_dbg(DBG_MIB, "%s: MIB MAC: max_tx_msdu_lifetime %d " + "max_rx_lifetime %d frag_threshold %d rts_threshold %d " + "cwmin %d cwmax %d short_retry_time %d long_retry_time %d " + "scan_type %d scan_channel %d probe_delay %u " + "min_channel_time %d max_channel_time %d listen_int %d " + "desired_ssid %*phD desired_bssid %pM desired_bsstype %d", + wiphy_name(priv->hw->wiphy), + le32_to_cpu(m->max_tx_msdu_lifetime), + le32_to_cpu(m->max_rx_lifetime), + le16_to_cpu(m->frag_threshold), le16_to_cpu(m->rts_threshold), + le16_to_cpu(m->cwmin), le16_to_cpu(m->cwmax), + m->short_retry_time, m->long_retry_time, m->scan_type, + m->scan_channel, le16_to_cpu(m->probe_delay), + le16_to_cpu(m->min_channel_time), + le16_to_cpu(m->max_channel_time), + le16_to_cpu(m->listen_interval), + IW_ESSID_MAX_SIZE, m->desired_ssid, + m->desired_bssid, m->desired_bsstype); +exit: + kfree(m); +} + +static void at76_dump_mib_phy(struct at76_priv *priv) +{ + int ret; + struct mib_phy *m = kmalloc(sizeof(struct mib_phy), GFP_KERNEL); + + if (!m) + return; + + ret = at76_get_mib(priv->udev, MIB_PHY, m, sizeof(struct mib_phy)); + if (ret < 0) { + wiphy_err(priv->hw->wiphy, + "at76_get_mib (PHY) failed: %d\n", ret); + goto exit; + } + + at76_dbg(DBG_MIB, "%s: MIB PHY: ed_threshold %d slot_time %d " + "sifs_time %d preamble_length %d plcp_header_length %d " + "mpdu_max_length %d cca_mode_supported %d operation_rate_set " + "0x%x 0x%x 0x%x 0x%x channel_id %d current_cca_mode %d " + "phy_type %d current_reg_domain %d", + wiphy_name(priv->hw->wiphy), le32_to_cpu(m->ed_threshold), + le16_to_cpu(m->slot_time), le16_to_cpu(m->sifs_time), + le16_to_cpu(m->preamble_length), + le16_to_cpu(m->plcp_header_length), + le16_to_cpu(m->mpdu_max_length), + le16_to_cpu(m->cca_mode_supported), m->operation_rate_set[0], + m->operation_rate_set[1], m->operation_rate_set[2], + m->operation_rate_set[3], m->channel_id, m->current_cca_mode, + m->phy_type, m->current_reg_domain); +exit: + kfree(m); +} + +static void at76_dump_mib_local(struct at76_priv *priv) +{ + int ret; + struct mib_local *m = kmalloc(sizeof(*m), GFP_KERNEL); + + if (!m) + return; + + ret = at76_get_mib(priv->udev, MIB_LOCAL, m, sizeof(*m)); + if (ret < 0) { + wiphy_err(priv->hw->wiphy, + "at76_get_mib (LOCAL) failed: %d\n", ret); + goto exit; + } + + at76_dbg(DBG_MIB, "%s: MIB LOCAL: beacon_enable %d " + "txautorate_fallback %d ssid_size %d promiscuous_mode %d " + "preamble_type %d", wiphy_name(priv->hw->wiphy), + m->beacon_enable, + m->txautorate_fallback, m->ssid_size, m->promiscuous_mode, + m->preamble_type); +exit: + kfree(m); +} + +static void at76_dump_mib_mdomain(struct at76_priv *priv) +{ + int ret; + struct mib_mdomain *m = kmalloc(sizeof(struct mib_mdomain), GFP_KERNEL); + + if (!m) + return; + + ret = at76_get_mib(priv->udev, MIB_MDOMAIN, m, + sizeof(struct mib_mdomain)); + if (ret < 0) { + wiphy_err(priv->hw->wiphy, + "at76_get_mib (MDOMAIN) failed: %d\n", ret); + goto exit; + } + + at76_dbg(DBG_MIB, "%s: MIB MDOMAIN: channel_list %*phD", + wiphy_name(priv->hw->wiphy), + (int)sizeof(m->channel_list), m->channel_list); + + at76_dbg(DBG_MIB, "%s: MIB MDOMAIN: tx_powerlevel %*phD", + wiphy_name(priv->hw->wiphy), + (int)sizeof(m->tx_powerlevel), m->tx_powerlevel); +exit: + kfree(m); +} + +/* Enable monitor mode */ +static int at76_start_monitor(struct at76_priv *priv) +{ + struct at76_req_scan scan; + int ret; + + memset(&scan, 0, sizeof(struct at76_req_scan)); + eth_broadcast_addr(scan.bssid); + + scan.channel = priv->channel; + scan.scan_type = SCAN_TYPE_PASSIVE; + scan.international_scan = 0; + scan.min_channel_time = cpu_to_le16(priv->scan_min_time); + scan.max_channel_time = cpu_to_le16(priv->scan_max_time); + scan.probe_delay = cpu_to_le16(0); + + ret = at76_set_card_command(priv->udev, CMD_SCAN, &scan, sizeof(scan)); + if (ret >= 0) + ret = at76_get_cmd_status(priv->udev, CMD_SCAN); + + return ret; +} + +/* Calculate padding from txbuf->wlength (which excludes the USB TX header), + likely to compensate a flaw in the AT76C503A USB part ... */ +static inline int at76_calc_padding(int wlen) +{ + /* add the USB TX header */ + wlen += AT76_TX_HDRLEN; + + wlen = wlen % 64; + + if (wlen < 50) + return 50 - wlen; + + if (wlen >= 61) + return 64 + 50 - wlen; + + return 0; +} + +static void at76_rx_callback(struct urb *urb) +{ + struct at76_priv *priv = urb->context; + + priv->rx_tasklet.data = (unsigned long)urb; + tasklet_schedule(&priv->rx_tasklet); +} + +static int at76_submit_rx_urb(struct at76_priv *priv) +{ + int ret; + int size; + struct sk_buff *skb = priv->rx_skb; + + if (!priv->rx_urb) { + wiphy_err(priv->hw->wiphy, "%s: priv->rx_urb is NULL\n", + __func__); + return -EFAULT; + } + + if (!skb) { + skb = dev_alloc_skb(sizeof(struct at76_rx_buffer)); + if (!skb) { + wiphy_err(priv->hw->wiphy, + "cannot allocate rx skbuff\n"); + ret = -ENOMEM; + goto exit; + } + priv->rx_skb = skb; + } else { + skb_push(skb, skb_headroom(skb)); + skb_trim(skb, 0); + } + + size = skb_tailroom(skb); + usb_fill_bulk_urb(priv->rx_urb, priv->udev, priv->rx_pipe, + skb_put(skb, size), size, at76_rx_callback, priv); + ret = usb_submit_urb(priv->rx_urb, GFP_ATOMIC); + if (ret < 0) { + if (ret == -ENODEV) + at76_dbg(DBG_DEVSTART, + "usb_submit_urb returned -ENODEV"); + else + wiphy_err(priv->hw->wiphy, + "rx, usb_submit_urb failed: %d\n", ret); + } + +exit: + if (ret < 0 && ret != -ENODEV) + wiphy_err(priv->hw->wiphy, + "cannot submit rx urb - please unload the driver and/or power cycle the device\n"); + + return ret; +} + +/* Download external firmware */ +static int at76_load_external_fw(struct usb_device *udev, struct fwentry *fwe) +{ + int ret; + int op_mode; + int blockno = 0; + int bsize; + u8 *block; + u8 *buf = fwe->extfw; + int size = fwe->extfw_size; + + if (!buf || !size) + return -ENOENT; + + op_mode = at76_get_op_mode(udev); + at76_dbg(DBG_DEVSTART, "opmode %d", op_mode); + + if (op_mode != OPMODE_NORMAL_NIC_WITHOUT_FLASH) { + dev_err(&udev->dev, "unexpected opmode %d\n", op_mode); + return -EINVAL; + } + + block = kmalloc(FW_BLOCK_SIZE, GFP_KERNEL); + if (!block) + return -ENOMEM; + + at76_dbg(DBG_DEVSTART, "downloading external firmware"); + + /* for fw >= 0.100, the device needs an extra empty block */ + do { + bsize = min_t(int, size, FW_BLOCK_SIZE); + memcpy(block, buf, bsize); + at76_dbg(DBG_DEVSTART, + "ext fw, size left = %5d, bsize = %4d, blockno = %2d", + size, bsize, blockno); + ret = at76_load_ext_fw_block(udev, blockno, block, bsize); + if (ret != bsize) { + dev_err(&udev->dev, + "loading %dth firmware block failed: %d\n", + blockno, ret); + ret = -EIO; + goto exit; + } + buf += bsize; + size -= bsize; + blockno++; + } while (bsize > 0); + + if (at76_is_505a(fwe->board_type)) { + at76_dbg(DBG_DEVSTART, "200 ms delay for 505a"); + schedule_timeout_interruptible(HZ / 5 + 1); + } + +exit: + kfree(block); + if (ret < 0) + dev_err(&udev->dev, + "downloading external firmware failed: %d\n", ret); + return ret; +} + +/* Download internal firmware */ +static int at76_load_internal_fw(struct usb_device *udev, struct fwentry *fwe) +{ + int ret; + int need_remap = !at76_is_505a(fwe->board_type); + + ret = at76_usbdfu_download(udev, fwe->intfw, fwe->intfw_size, + need_remap ? 0 : 2 * HZ); + + if (ret < 0) { + dev_err(&udev->dev, + "downloading internal fw failed with %d\n", ret); + goto exit; + } + + at76_dbg(DBG_DEVSTART, "sending REMAP"); + + /* no REMAP for 505A (see SF driver) */ + if (need_remap) { + ret = at76_remap(udev); + if (ret < 0) { + dev_err(&udev->dev, + "sending REMAP failed with %d\n", ret); + goto exit; + } + } + + at76_dbg(DBG_DEVSTART, "sleeping for 2 seconds"); + schedule_timeout_interruptible(2 * HZ + 1); + usb_reset_device(udev); + +exit: + return ret; +} + +static int at76_startup_device(struct at76_priv *priv) +{ + struct at76_card_config *ccfg = &priv->card_config; + int ret; + + at76_dbg(DBG_PARAMS, + "%s param: ssid %.*s (%*phD) mode %s ch %d wep %s key %d " + "keylen %d", wiphy_name(priv->hw->wiphy), priv->essid_size, + priv->essid, IW_ESSID_MAX_SIZE, priv->essid, + priv->iw_mode == IW_MODE_ADHOC ? "adhoc" : "infra", + priv->channel, priv->wep_enabled ? "enabled" : "disabled", + priv->wep_key_id, priv->wep_keys_len[priv->wep_key_id]); + at76_dbg(DBG_PARAMS, + "%s param: preamble %s rts %d retry %d frag %d " + "txrate %s auth_mode %d", wiphy_name(priv->hw->wiphy), + preambles[priv->preamble_type], priv->rts_threshold, + priv->short_retry_limit, priv->frag_threshold, + priv->txrate == TX_RATE_1MBIT ? "1MBit" : priv->txrate == + TX_RATE_2MBIT ? "2MBit" : priv->txrate == + TX_RATE_5_5MBIT ? "5.5MBit" : priv->txrate == + TX_RATE_11MBIT ? "11MBit" : priv->txrate == + TX_RATE_AUTO ? "auto" : "", priv->auth_mode); + at76_dbg(DBG_PARAMS, + "%s param: pm_mode %d pm_period %d auth_mode %s " + "scan_times %d %d scan_mode %s", + wiphy_name(priv->hw->wiphy), priv->pm_mode, priv->pm_period, + priv->auth_mode == WLAN_AUTH_OPEN ? "open" : "shared_secret", + priv->scan_min_time, priv->scan_max_time, + priv->scan_mode == SCAN_TYPE_ACTIVE ? "active" : "passive"); + + memset(ccfg, 0, sizeof(struct at76_card_config)); + ccfg->promiscuous_mode = 0; + ccfg->short_retry_limit = priv->short_retry_limit; + + if (priv->wep_enabled) { + if (priv->wep_keys_len[priv->wep_key_id] > WEP_SMALL_KEY_LEN) + ccfg->encryption_type = 2; + else + ccfg->encryption_type = 1; + + /* jal: always exclude unencrypted if WEP is active */ + ccfg->exclude_unencrypted = 1; + } else { + ccfg->exclude_unencrypted = 0; + ccfg->encryption_type = 0; + } + + ccfg->rts_threshold = cpu_to_le16(priv->rts_threshold); + ccfg->fragmentation_threshold = cpu_to_le16(priv->frag_threshold); + + memcpy(ccfg->basic_rate_set, hw_rates, 4); + /* jal: really needed, we do a set_mib for autorate later ??? */ + ccfg->auto_rate_fallback = (priv->txrate == TX_RATE_AUTO ? 1 : 0); + ccfg->channel = priv->channel; + ccfg->privacy_invoked = priv->wep_enabled; + memcpy(ccfg->current_ssid, priv->essid, IW_ESSID_MAX_SIZE); + ccfg->ssid_len = priv->essid_size; + + ccfg->wep_default_key_id = priv->wep_key_id; + memcpy(ccfg->wep_default_key_value, priv->wep_keys, + sizeof(priv->wep_keys)); + + ccfg->short_preamble = priv->preamble_type; + ccfg->beacon_period = cpu_to_le16(priv->beacon_period); + + ret = at76_set_card_command(priv->udev, CMD_STARTUP, &priv->card_config, + sizeof(struct at76_card_config)); + if (ret < 0) { + wiphy_err(priv->hw->wiphy, "at76_set_card_command failed: %d\n", + ret); + return ret; + } + + at76_wait_completion(priv, CMD_STARTUP); + + /* remove BSSID from previous run */ + eth_zero_addr(priv->bssid); + + priv->scanning = false; + + if (at76_set_radio(priv, 1) == 1) + at76_wait_completion(priv, CMD_RADIO_ON); + + ret = at76_set_preamble(priv, priv->preamble_type); + if (ret < 0) + return ret; + + ret = at76_set_frag(priv, priv->frag_threshold); + if (ret < 0) + return ret; + + ret = at76_set_rts(priv, priv->rts_threshold); + if (ret < 0) + return ret; + + ret = at76_set_autorate_fallback(priv, + priv->txrate == TX_RATE_AUTO ? 1 : 0); + if (ret < 0) + return ret; + + ret = at76_set_pm_mode(priv); + if (ret < 0) + return ret; + + if (at76_debug & DBG_MIB) { + at76_dump_mib_mac(priv); + at76_dump_mib_mac_addr(priv); + at76_dump_mib_mac_mgmt(priv); + at76_dump_mib_mac_wep(priv); + at76_dump_mib_mdomain(priv); + at76_dump_mib_phy(priv); + at76_dump_mib_local(priv); + } + + return 0; +} + +/* Enable or disable promiscuous mode */ +static void at76_work_set_promisc(struct work_struct *work) +{ + struct at76_priv *priv = container_of(work, struct at76_priv, + work_set_promisc); + int ret = 0; + + if (priv->device_unplugged) + return; + + mutex_lock(&priv->mtx); + + priv->mib_buf.type = MIB_LOCAL; + priv->mib_buf.size = 1; + priv->mib_buf.index = offsetof(struct mib_local, promiscuous_mode); + priv->mib_buf.data.byte = priv->promisc ? 1 : 0; + + ret = at76_set_mib(priv, &priv->mib_buf); + if (ret < 0) + wiphy_err(priv->hw->wiphy, + "set_mib (promiscuous_mode) failed: %d\n", ret); + + mutex_unlock(&priv->mtx); +} + +/* Submit Rx urb back to the device */ +static void at76_work_submit_rx(struct work_struct *work) +{ + struct at76_priv *priv = container_of(work, struct at76_priv, + work_submit_rx); + + mutex_lock(&priv->mtx); + at76_submit_rx_urb(priv); + mutex_unlock(&priv->mtx); +} + +/* This is a workaround to make scan working: + * currently mac80211 does not process frames with no frequency + * information. + * However during scan the HW performs a sweep by itself, and we + * are unable to know where the radio is actually tuned. + * This function tries to do its best to guess this information.. + * During scan, If the current frame is a beacon or a probe response, + * the channel information is extracted from it. + * When not scanning, for other frames, or if it happens that for + * whatever reason we fail to parse beacons and probe responses, this + * function returns the priv->channel information, that should be correct + * at least when we are not scanning. + */ +static inline int at76_guess_freq(struct at76_priv *priv) +{ + size_t el_off; + const u8 *el; + int channel = priv->channel; + int len = priv->rx_skb->len; + struct ieee80211_hdr *hdr = (void *)priv->rx_skb->data; + + if (!priv->scanning) + goto exit; + + if (len < 24) + goto exit; + + if (ieee80211_is_probe_resp(hdr->frame_control)) { + el_off = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); + el = ((struct ieee80211_mgmt *)hdr)->u.probe_resp.variable; + } else if (ieee80211_is_beacon(hdr->frame_control)) { + el_off = offsetof(struct ieee80211_mgmt, u.beacon.variable); + el = ((struct ieee80211_mgmt *)hdr)->u.beacon.variable; + } else { + goto exit; + } + len -= el_off; + + el = cfg80211_find_ie(WLAN_EID_DS_PARAMS, el, len); + if (el && el[1] > 0) + channel = el[2]; + +exit: + return ieee80211_channel_to_frequency(channel, IEEE80211_BAND_2GHZ); +} + +static void at76_rx_tasklet(unsigned long param) +{ + struct urb *urb = (struct urb *)param; + struct at76_priv *priv = urb->context; + struct at76_rx_buffer *buf; + struct ieee80211_rx_status rx_status = { 0 }; + + if (priv->device_unplugged) { + at76_dbg(DBG_DEVSTART, "device unplugged"); + at76_dbg(DBG_DEVSTART, "urb status %d", urb->status); + return; + } + + if (!priv->rx_skb || !priv->rx_skb->data) + return; + + buf = (struct at76_rx_buffer *)priv->rx_skb->data; + + if (urb->status != 0) { + if (urb->status != -ENOENT && urb->status != -ECONNRESET) + at76_dbg(DBG_URB, + "%s %s: - nonzero Rx bulk status received: %d", + __func__, wiphy_name(priv->hw->wiphy), + urb->status); + return; + } + + at76_dbg(DBG_RX_ATMEL_HDR, + "%s: rx frame: rate %d rssi %d noise %d link %d", + wiphy_name(priv->hw->wiphy), buf->rx_rate, buf->rssi, + buf->noise_level, buf->link_quality); + + skb_pull(priv->rx_skb, AT76_RX_HDRLEN); + skb_trim(priv->rx_skb, le16_to_cpu(buf->wlength)); + at76_dbg_dump(DBG_RX_DATA, priv->rx_skb->data, + priv->rx_skb->len, "RX: len=%d", priv->rx_skb->len); + + rx_status.signal = buf->rssi; + rx_status.flag |= RX_FLAG_DECRYPTED; + rx_status.flag |= RX_FLAG_IV_STRIPPED; + rx_status.band = IEEE80211_BAND_2GHZ; + rx_status.freq = at76_guess_freq(priv); + + at76_dbg(DBG_MAC80211, "calling ieee80211_rx_irqsafe(): %d/%d", + priv->rx_skb->len, priv->rx_skb->data_len); + memcpy(IEEE80211_SKB_RXCB(priv->rx_skb), &rx_status, sizeof(rx_status)); + ieee80211_rx_irqsafe(priv->hw, priv->rx_skb); + + /* Use a new skb for the next receive */ + priv->rx_skb = NULL; + + at76_submit_rx_urb(priv); +} + +/* Load firmware into kernel memory and parse it */ +static struct fwentry *at76_load_firmware(struct usb_device *udev, + enum board_type board_type) +{ + int ret; + char *str; + struct at76_fw_header *fwh; + struct fwentry *fwe = &firmwares[board_type]; + + mutex_lock(&fw_mutex); + + if (fwe->loaded) { + at76_dbg(DBG_FW, "re-using previously loaded fw"); + goto exit; + } + + at76_dbg(DBG_FW, "downloading firmware %s", fwe->fwname); + ret = request_firmware(&fwe->fw, fwe->fwname, &udev->dev); + if (ret < 0) { + dev_err(&udev->dev, "firmware %s not found!\n", + fwe->fwname); + dev_err(&udev->dev, + "you may need to download the firmware from http://developer.berlios.de/projects/at76c503a/\n"); + goto exit; + } + + at76_dbg(DBG_FW, "got it."); + fwh = (struct at76_fw_header *)(fwe->fw->data); + + if (fwe->fw->size <= sizeof(*fwh)) { + dev_err(&udev->dev, + "firmware is too short (0x%zx)\n", fwe->fw->size); + goto exit; + } + + /* CRC currently not checked */ + fwe->board_type = le32_to_cpu(fwh->board_type); + if (fwe->board_type != board_type) { + dev_err(&udev->dev, + "board type mismatch, requested %u, got %u\n", + board_type, fwe->board_type); + goto exit; + } + + fwe->fw_version.major = fwh->major; + fwe->fw_version.minor = fwh->minor; + fwe->fw_version.patch = fwh->patch; + fwe->fw_version.build = fwh->build; + + str = (char *)fwh + le32_to_cpu(fwh->str_offset); + fwe->intfw = (u8 *)fwh + le32_to_cpu(fwh->int_fw_offset); + fwe->intfw_size = le32_to_cpu(fwh->int_fw_len); + fwe->extfw = (u8 *)fwh + le32_to_cpu(fwh->ext_fw_offset); + fwe->extfw_size = le32_to_cpu(fwh->ext_fw_len); + + fwe->loaded = 1; + + dev_printk(KERN_DEBUG, &udev->dev, + "using firmware %s (version %d.%d.%d-%d)\n", + fwe->fwname, fwh->major, fwh->minor, fwh->patch, fwh->build); + + at76_dbg(DBG_DEVSTART, "board %u, int %d:%d, ext %d:%d", board_type, + le32_to_cpu(fwh->int_fw_offset), le32_to_cpu(fwh->int_fw_len), + le32_to_cpu(fwh->ext_fw_offset), le32_to_cpu(fwh->ext_fw_len)); + at76_dbg(DBG_DEVSTART, "firmware id %s", str); + +exit: + mutex_unlock(&fw_mutex); + + if (fwe->loaded) + return fwe; + else + return NULL; +} + +static int at76_join(struct at76_priv *priv) +{ + struct at76_req_join join; + int ret; + + memset(&join, 0, sizeof(struct at76_req_join)); + memcpy(join.essid, priv->essid, priv->essid_size); + join.essid_size = priv->essid_size; + memcpy(join.bssid, priv->bssid, ETH_ALEN); + join.bss_type = INFRASTRUCTURE_MODE; + join.channel = priv->channel; + join.timeout = cpu_to_le16(2000); + + at76_dbg(DBG_MAC80211, "%s: sending CMD_JOIN", __func__); + ret = at76_set_card_command(priv->udev, CMD_JOIN, &join, + sizeof(struct at76_req_join)); + + if (ret < 0) { + wiphy_err(priv->hw->wiphy, "at76_set_card_command failed: %d\n", + ret); + return 0; + } + + ret = at76_wait_completion(priv, CMD_JOIN); + at76_dbg(DBG_MAC80211, "%s: CMD_JOIN returned: 0x%02x", __func__, ret); + if (ret != CMD_STATUS_COMPLETE) { + wiphy_err(priv->hw->wiphy, "at76_wait_completion failed: %d\n", + ret); + return 0; + } + + at76_set_pm_mode(priv); + + return 0; +} + +static void at76_work_join_bssid(struct work_struct *work) +{ + struct at76_priv *priv = container_of(work, struct at76_priv, + work_join_bssid); + + if (priv->device_unplugged) + return; + + mutex_lock(&priv->mtx); + + if (is_valid_ether_addr(priv->bssid)) + at76_join(priv); + + mutex_unlock(&priv->mtx); +} + +static void at76_mac80211_tx_callback(struct urb *urb) +{ + struct at76_priv *priv = urb->context; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(priv->tx_skb); + + at76_dbg(DBG_MAC80211, "%s()", __func__); + + switch (urb->status) { + case 0: + /* success */ + info->flags |= IEEE80211_TX_STAT_ACK; + break; + case -ENOENT: + case -ECONNRESET: + /* fail, urb has been unlinked */ + /* FIXME: add error message */ + break; + default: + at76_dbg(DBG_URB, "%s - nonzero tx status received: %d", + __func__, urb->status); + break; + } + + memset(&info->status, 0, sizeof(info->status)); + + ieee80211_tx_status_irqsafe(priv->hw, priv->tx_skb); + + priv->tx_skb = NULL; + + ieee80211_wake_queues(priv->hw); +} + +static void at76_mac80211_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct at76_priv *priv = hw->priv; + struct at76_tx_buffer *tx_buffer = priv->bulk_out_buffer; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; + int padding, submit_len, ret; + + at76_dbg(DBG_MAC80211, "%s()", __func__); + + if (priv->tx_urb->status == -EINPROGRESS) { + wiphy_err(priv->hw->wiphy, + "%s called while tx urb is pending\n", __func__); + dev_kfree_skb_any(skb); + return; + } + + /* The following code lines are important when the device is going to + * authenticate with a new bssid. The driver must send CMD_JOIN before + * an authentication frame is transmitted. For this to succeed, the + * correct bssid of the AP must be known. As mac80211 does not inform + * drivers about the bssid prior to the authentication process the + * following workaround is necessary. If the TX frame is an + * authentication frame extract the bssid and send the CMD_JOIN. */ + if (mgmt->frame_control & cpu_to_le16(IEEE80211_STYPE_AUTH)) { + if (!ether_addr_equal_64bits(priv->bssid, mgmt->bssid)) { + memcpy(priv->bssid, mgmt->bssid, ETH_ALEN); + ieee80211_queue_work(hw, &priv->work_join_bssid); + dev_kfree_skb_any(skb); + return; + } + } + + ieee80211_stop_queues(hw); + + at76_ledtrig_tx_activity(); /* tell ledtrigger we send a packet */ + + WARN_ON(priv->tx_skb != NULL); + + priv->tx_skb = skb; + padding = at76_calc_padding(skb->len); + submit_len = AT76_TX_HDRLEN + skb->len + padding; + + /* setup 'Atmel' header */ + memset(tx_buffer, 0, sizeof(*tx_buffer)); + tx_buffer->padding = padding; + tx_buffer->wlength = cpu_to_le16(skb->len); + tx_buffer->tx_rate = ieee80211_get_tx_rate(hw, info)->hw_value; + memset(tx_buffer->reserved, 0, sizeof(tx_buffer->reserved)); + memcpy(tx_buffer->packet, skb->data, skb->len); + + at76_dbg(DBG_TX_DATA, "%s tx: wlen 0x%x pad 0x%x rate %d hdr", + wiphy_name(priv->hw->wiphy), le16_to_cpu(tx_buffer->wlength), + tx_buffer->padding, tx_buffer->tx_rate); + + /* send stuff */ + at76_dbg_dump(DBG_TX_DATA_CONTENT, tx_buffer, submit_len, + "%s(): tx_buffer %d bytes:", __func__, submit_len); + usb_fill_bulk_urb(priv->tx_urb, priv->udev, priv->tx_pipe, tx_buffer, + submit_len, at76_mac80211_tx_callback, priv); + ret = usb_submit_urb(priv->tx_urb, GFP_ATOMIC); + if (ret) { + wiphy_err(priv->hw->wiphy, "error in tx submit urb: %d\n", ret); + if (ret == -EINVAL) + wiphy_err(priv->hw->wiphy, + "-EINVAL: tx urb %p hcpriv %p complete %p\n", + priv->tx_urb, + priv->tx_urb->hcpriv, priv->tx_urb->complete); + } +} + +static int at76_mac80211_start(struct ieee80211_hw *hw) +{ + struct at76_priv *priv = hw->priv; + int ret; + + at76_dbg(DBG_MAC80211, "%s()", __func__); + + mutex_lock(&priv->mtx); + + ret = at76_submit_rx_urb(priv); + if (ret < 0) { + wiphy_err(priv->hw->wiphy, "open: submit_rx_urb failed: %d\n", + ret); + goto error; + } + + at76_startup_device(priv); + + at76_start_monitor(priv); + +error: + mutex_unlock(&priv->mtx); + + return 0; +} + +static void at76_mac80211_stop(struct ieee80211_hw *hw) +{ + struct at76_priv *priv = hw->priv; + + at76_dbg(DBG_MAC80211, "%s()", __func__); + + cancel_delayed_work(&priv->dwork_hw_scan); + cancel_work_sync(&priv->work_join_bssid); + cancel_work_sync(&priv->work_set_promisc); + + mutex_lock(&priv->mtx); + + if (!priv->device_unplugged) { + /* We are called by "ifconfig ethX down", not because the + * device is not available anymore. */ + at76_set_radio(priv, 0); + + /* We unlink rx_urb because at76_open() re-submits it. + * If unplugged, at76_delete_device() takes care of it. */ + usb_kill_urb(priv->rx_urb); + } + + mutex_unlock(&priv->mtx); +} + +static int at76_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct at76_priv *priv = hw->priv; + int ret = 0; + + at76_dbg(DBG_MAC80211, "%s()", __func__); + + mutex_lock(&priv->mtx); + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + priv->iw_mode = IW_MODE_INFRA; + break; + default: + ret = -EOPNOTSUPP; + goto exit; + } + +exit: + mutex_unlock(&priv->mtx); + + return ret; +} + +static void at76_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + at76_dbg(DBG_MAC80211, "%s()", __func__); +} + +static void at76_dwork_hw_scan(struct work_struct *work) +{ + struct at76_priv *priv = container_of(work, struct at76_priv, + dwork_hw_scan.work); + int ret; + + if (priv->device_unplugged) + return; + + mutex_lock(&priv->mtx); + + ret = at76_get_cmd_status(priv->udev, CMD_SCAN); + at76_dbg(DBG_MAC80211, "%s: CMD_SCAN status 0x%02x", __func__, ret); + + /* FIXME: add maximum time for scan to complete */ + + if (ret != CMD_STATUS_COMPLETE) { + ieee80211_queue_delayed_work(priv->hw, &priv->dwork_hw_scan, + SCAN_POLL_INTERVAL); + mutex_unlock(&priv->mtx); + return; + } + + if (is_valid_ether_addr(priv->bssid)) + at76_join(priv); + + priv->scanning = false; + + mutex_unlock(&priv->mtx); + + ieee80211_scan_completed(priv->hw, false); + + ieee80211_wake_queues(priv->hw); +} + +static int at76_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_scan_request *hw_req) +{ + struct cfg80211_scan_request *req = &hw_req->req; + struct at76_priv *priv = hw->priv; + struct at76_req_scan scan; + u8 *ssid = NULL; + int ret, len = 0; + + at76_dbg(DBG_MAC80211, "%s():", __func__); + + if (priv->device_unplugged) + return 0; + + mutex_lock(&priv->mtx); + + ieee80211_stop_queues(hw); + + memset(&scan, 0, sizeof(struct at76_req_scan)); + eth_broadcast_addr(scan.bssid); + + if (req->n_ssids) { + scan.scan_type = SCAN_TYPE_ACTIVE; + ssid = req->ssids[0].ssid; + len = req->ssids[0].ssid_len; + } else { + scan.scan_type = SCAN_TYPE_PASSIVE; + } + + if (len) { + memcpy(scan.essid, ssid, len); + scan.essid_size = len; + } + + scan.min_channel_time = cpu_to_le16(priv->scan_min_time); + scan.max_channel_time = cpu_to_le16(priv->scan_max_time); + scan.probe_delay = cpu_to_le16(priv->scan_min_time * 1000); + scan.international_scan = 0; + + at76_dbg(DBG_MAC80211, "%s: sending CMD_SCAN", __func__); + ret = at76_set_card_command(priv->udev, CMD_SCAN, &scan, sizeof(scan)); + + if (ret < 0) { + wiphy_err(priv->hw->wiphy, "CMD_SCAN failed: %d\n", ret); + goto exit; + } + + priv->scanning = true; + ieee80211_queue_delayed_work(priv->hw, &priv->dwork_hw_scan, + SCAN_POLL_INTERVAL); + +exit: + mutex_unlock(&priv->mtx); + + return 0; +} + +static int at76_config(struct ieee80211_hw *hw, u32 changed) +{ + struct at76_priv *priv = hw->priv; + + at76_dbg(DBG_MAC80211, "%s(): channel %d", + __func__, hw->conf.chandef.chan->hw_value); + at76_dbg_dump(DBG_MAC80211, priv->bssid, ETH_ALEN, "bssid:"); + + mutex_lock(&priv->mtx); + + priv->channel = hw->conf.chandef.chan->hw_value; + + if (is_valid_ether_addr(priv->bssid)) + at76_join(priv); + else + at76_start_monitor(priv); + + mutex_unlock(&priv->mtx); + + return 0; +} + +static void at76_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *conf, + u32 changed) +{ + struct at76_priv *priv = hw->priv; + + at76_dbg(DBG_MAC80211, "%s():", __func__); + + if (!(changed & BSS_CHANGED_BSSID)) + return; + + at76_dbg_dump(DBG_MAC80211, conf->bssid, ETH_ALEN, "bssid:"); + + mutex_lock(&priv->mtx); + + memcpy(priv->bssid, conf->bssid, ETH_ALEN); + + if (is_valid_ether_addr(priv->bssid)) + /* mac80211 is joining a bss */ + at76_join(priv); + + mutex_unlock(&priv->mtx); +} + +/* must be atomic */ +static void at76_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, u64 multicast) +{ + struct at76_priv *priv = hw->priv; + int flags; + + at76_dbg(DBG_MAC80211, "%s(): changed_flags=0x%08x " + "total_flags=0x%08x", + __func__, changed_flags, *total_flags); + + flags = changed_flags & AT76_SUPPORTED_FILTERS; + *total_flags = AT76_SUPPORTED_FILTERS; + + /* Bail out after updating flags to prevent a WARN_ON in mac80211. */ + if (priv->device_unplugged) + return; + + /* FIXME: access to priv->promisc should be protected with + * priv->mtx, but it's impossible because this function needs to be + * atomic */ + + if (flags && !priv->promisc) { + /* mac80211 wants us to enable promiscuous mode */ + priv->promisc = 1; + } else if (!flags && priv->promisc) { + /* we need to disable promiscuous mode */ + priv->promisc = 0; + } else + return; + + ieee80211_queue_work(hw, &priv->work_set_promisc); +} + +static int at76_set_wep(struct at76_priv *priv) +{ + int ret = 0; + struct mib_mac_wep *mib_data = &priv->mib_buf.data.wep_mib; + + priv->mib_buf.type = MIB_MAC_WEP; + priv->mib_buf.size = sizeof(struct mib_mac_wep); + priv->mib_buf.index = 0; + + memset(mib_data, 0, sizeof(*mib_data)); + + if (priv->wep_enabled) { + if (priv->wep_keys_len[priv->wep_key_id] > WEP_SMALL_KEY_LEN) + mib_data->encryption_level = 2; + else + mib_data->encryption_level = 1; + + /* always exclude unencrypted if WEP is active */ + mib_data->exclude_unencrypted = 1; + } else { + mib_data->exclude_unencrypted = 0; + mib_data->encryption_level = 0; + } + + mib_data->privacy_invoked = priv->wep_enabled; + mib_data->wep_default_key_id = priv->wep_key_id; + memcpy(mib_data->wep_default_keyvalue, priv->wep_keys, + sizeof(priv->wep_keys)); + + ret = at76_set_mib(priv, &priv->mib_buf); + + if (ret < 0) + wiphy_err(priv->hw->wiphy, + "set_mib (wep) failed: %d\n", ret); + + return ret; +} + +static int at76_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct at76_priv *priv = hw->priv; + + int i; + + at76_dbg(DBG_MAC80211, "%s(): cmd %d key->cipher %d key->keyidx %d " + "key->keylen %d", + __func__, cmd, key->cipher, key->keyidx, key->keylen); + + if ((key->cipher != WLAN_CIPHER_SUITE_WEP40) && + (key->cipher != WLAN_CIPHER_SUITE_WEP104)) + return -EOPNOTSUPP; + + key->hw_key_idx = key->keyidx; + + mutex_lock(&priv->mtx); + + switch (cmd) { + case SET_KEY: + memcpy(priv->wep_keys[key->keyidx], key->key, key->keylen); + priv->wep_keys_len[key->keyidx] = key->keylen; + + /* FIXME: find out how to do this properly */ + priv->wep_key_id = key->keyidx; + + break; + case DISABLE_KEY: + default: + priv->wep_keys_len[key->keyidx] = 0; + break; + } + + priv->wep_enabled = 0; + + for (i = 0; i < WEP_KEYS; i++) { + if (priv->wep_keys_len[i] != 0) + priv->wep_enabled = 1; + } + + at76_set_wep(priv); + + mutex_unlock(&priv->mtx); + + return 0; +} + +static const struct ieee80211_ops at76_ops = { + .tx = at76_mac80211_tx, + .add_interface = at76_add_interface, + .remove_interface = at76_remove_interface, + .config = at76_config, + .bss_info_changed = at76_bss_info_changed, + .configure_filter = at76_configure_filter, + .start = at76_mac80211_start, + .stop = at76_mac80211_stop, + .hw_scan = at76_hw_scan, + .set_key = at76_set_key, +}; + +/* Allocate network device and initialize private data */ +static struct at76_priv *at76_alloc_new_device(struct usb_device *udev) +{ + struct ieee80211_hw *hw; + struct at76_priv *priv; + + hw = ieee80211_alloc_hw(sizeof(struct at76_priv), &at76_ops); + if (!hw) { + printk(KERN_ERR DRIVER_NAME ": could not register" + " ieee80211_hw\n"); + return NULL; + } + + priv = hw->priv; + priv->hw = hw; + + priv->udev = udev; + + mutex_init(&priv->mtx); + INIT_WORK(&priv->work_set_promisc, at76_work_set_promisc); + INIT_WORK(&priv->work_submit_rx, at76_work_submit_rx); + INIT_WORK(&priv->work_join_bssid, at76_work_join_bssid); + INIT_DELAYED_WORK(&priv->dwork_hw_scan, at76_dwork_hw_scan); + + tasklet_init(&priv->rx_tasklet, at76_rx_tasklet, 0); + + priv->pm_mode = AT76_PM_OFF; + priv->pm_period = 0; + + /* unit us */ + + return priv; +} + +static int at76_alloc_urbs(struct at76_priv *priv, + struct usb_interface *interface) +{ + struct usb_endpoint_descriptor *endpoint, *ep_in, *ep_out; + int i; + int buffer_size; + struct usb_host_interface *iface_desc; + + at76_dbg(DBG_PROC_ENTRY, "%s: ENTER", __func__); + + at76_dbg(DBG_URB, "%s: NumEndpoints %d ", __func__, + interface->altsetting[0].desc.bNumEndpoints); + + ep_in = NULL; + ep_out = NULL; + iface_desc = interface->cur_altsetting; + for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) { + endpoint = &iface_desc->endpoint[i].desc; + + at76_dbg(DBG_URB, "%s: %d. endpoint: addr 0x%x attr 0x%x", + __func__, i, endpoint->bEndpointAddress, + endpoint->bmAttributes); + + if (!ep_in && usb_endpoint_is_bulk_in(endpoint)) + ep_in = endpoint; + + if (!ep_out && usb_endpoint_is_bulk_out(endpoint)) + ep_out = endpoint; + } + + if (!ep_in || !ep_out) { + dev_err(&interface->dev, "bulk endpoints missing\n"); + return -ENXIO; + } + + priv->rx_pipe = usb_rcvbulkpipe(priv->udev, ep_in->bEndpointAddress); + priv->tx_pipe = usb_sndbulkpipe(priv->udev, ep_out->bEndpointAddress); + + priv->rx_urb = usb_alloc_urb(0, GFP_KERNEL); + priv->tx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!priv->rx_urb || !priv->tx_urb) { + dev_err(&interface->dev, "cannot allocate URB\n"); + return -ENOMEM; + } + + buffer_size = sizeof(struct at76_tx_buffer) + MAX_PADDING_SIZE; + priv->bulk_out_buffer = kmalloc(buffer_size, GFP_KERNEL); + if (!priv->bulk_out_buffer) + return -ENOMEM; + + at76_dbg(DBG_PROC_ENTRY, "%s: EXIT", __func__); + + return 0; +} + +static struct ieee80211_rate at76_rates[] = { + { .bitrate = 10, .hw_value = TX_RATE_1MBIT, }, + { .bitrate = 20, .hw_value = TX_RATE_2MBIT, }, + { .bitrate = 55, .hw_value = TX_RATE_5_5MBIT, }, + { .bitrate = 110, .hw_value = TX_RATE_11MBIT, }, +}; + +static struct ieee80211_channel at76_channels[] = { + { .center_freq = 2412, .hw_value = 1 }, + { .center_freq = 2417, .hw_value = 2 }, + { .center_freq = 2422, .hw_value = 3 }, + { .center_freq = 2427, .hw_value = 4 }, + { .center_freq = 2432, .hw_value = 5 }, + { .center_freq = 2437, .hw_value = 6 }, + { .center_freq = 2442, .hw_value = 7 }, + { .center_freq = 2447, .hw_value = 8 }, + { .center_freq = 2452, .hw_value = 9 }, + { .center_freq = 2457, .hw_value = 10 }, + { .center_freq = 2462, .hw_value = 11 }, + { .center_freq = 2467, .hw_value = 12 }, + { .center_freq = 2472, .hw_value = 13 }, + { .center_freq = 2484, .hw_value = 14 } +}; + +static struct ieee80211_supported_band at76_supported_band = { + .channels = at76_channels, + .n_channels = ARRAY_SIZE(at76_channels), + .bitrates = at76_rates, + .n_bitrates = ARRAY_SIZE(at76_rates), +}; + +/* Register network device and initialize the hardware */ +static int at76_init_new_device(struct at76_priv *priv, + struct usb_interface *interface) +{ + struct wiphy *wiphy; + size_t len; + int ret; + + /* set up the endpoint information */ + /* check out the endpoints */ + + at76_dbg(DBG_DEVSTART, "USB interface: %d endpoints", + interface->cur_altsetting->desc.bNumEndpoints); + + ret = at76_alloc_urbs(priv, interface); + if (ret < 0) + goto exit; + + /* MAC address */ + ret = at76_get_hw_config(priv); + if (ret < 0) { + dev_err(&interface->dev, "cannot get MAC address\n"); + goto exit; + } + + priv->domain = at76_get_reg_domain(priv->regulatory_domain); + + priv->channel = DEF_CHANNEL; + priv->iw_mode = IW_MODE_INFRA; + priv->rts_threshold = DEF_RTS_THRESHOLD; + priv->frag_threshold = DEF_FRAG_THRESHOLD; + priv->short_retry_limit = DEF_SHORT_RETRY_LIMIT; + priv->txrate = TX_RATE_AUTO; + priv->preamble_type = PREAMBLE_TYPE_LONG; + priv->beacon_period = 100; + priv->auth_mode = WLAN_AUTH_OPEN; + priv->scan_min_time = DEF_SCAN_MIN_TIME; + priv->scan_max_time = DEF_SCAN_MAX_TIME; + priv->scan_mode = SCAN_TYPE_ACTIVE; + priv->device_unplugged = 0; + + /* mac80211 initialisation */ + wiphy = priv->hw->wiphy; + priv->hw->wiphy->max_scan_ssids = 1; + priv->hw->wiphy->max_scan_ie_len = 0; + priv->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); + priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &at76_supported_band; + ieee80211_hw_set(priv->hw, RX_INCLUDES_FCS); + ieee80211_hw_set(priv->hw, SIGNAL_UNSPEC); + priv->hw->max_signal = 100; + + SET_IEEE80211_DEV(priv->hw, &interface->dev); + SET_IEEE80211_PERM_ADDR(priv->hw, priv->mac_addr); + + len = sizeof(wiphy->fw_version); + snprintf(wiphy->fw_version, len, "%d.%d.%d-%d", + priv->fw_version.major, priv->fw_version.minor, + priv->fw_version.patch, priv->fw_version.build); + + wiphy->hw_version = priv->board_type; + + ret = ieee80211_register_hw(priv->hw); + if (ret) { + printk(KERN_ERR "cannot register mac80211 hw (status %d)!\n", + ret); + goto exit; + } + + priv->mac80211_registered = 1; + + wiphy_info(priv->hw->wiphy, "USB %s, MAC %pM, firmware %d.%d.%d-%d\n", + dev_name(&interface->dev), priv->mac_addr, + priv->fw_version.major, priv->fw_version.minor, + priv->fw_version.patch, priv->fw_version.build); + wiphy_info(priv->hw->wiphy, "regulatory domain 0x%02x: %s\n", + priv->regulatory_domain, priv->domain->name); + +exit: + return ret; +} + +static void at76_delete_device(struct at76_priv *priv) +{ + at76_dbg(DBG_PROC_ENTRY, "%s: ENTER", __func__); + + /* The device is gone, don't bother turning it off */ + priv->device_unplugged = 1; + + tasklet_kill(&priv->rx_tasklet); + + if (priv->mac80211_registered) + ieee80211_unregister_hw(priv->hw); + + if (priv->tx_urb) { + usb_kill_urb(priv->tx_urb); + usb_free_urb(priv->tx_urb); + } + if (priv->rx_urb) { + usb_kill_urb(priv->rx_urb); + usb_free_urb(priv->rx_urb); + } + + at76_dbg(DBG_PROC_ENTRY, "%s: unlinked urbs", __func__); + + kfree(priv->bulk_out_buffer); + + del_timer_sync(&ledtrig_tx_timer); + + kfree_skb(priv->rx_skb); + + at76_dbg(DBG_PROC_ENTRY, "%s: before freeing priv/ieee80211_hw", + __func__); + ieee80211_free_hw(priv->hw); + + at76_dbg(DBG_PROC_ENTRY, "%s: EXIT", __func__); +} + +static int at76_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + int ret; + struct at76_priv *priv; + struct fwentry *fwe; + struct usb_device *udev; + int op_mode; + int need_ext_fw = 0; + struct mib_fw_version *fwv = NULL; + int board_type = (int)id->driver_info; + + udev = usb_get_dev(interface_to_usbdev(interface)); + + fwv = kmalloc(sizeof(*fwv), GFP_KERNEL); + if (!fwv) { + ret = -ENOMEM; + goto exit; + } + + /* Load firmware into kernel memory */ + fwe = at76_load_firmware(udev, board_type); + if (!fwe) { + ret = -ENOENT; + goto exit; + } + + op_mode = at76_get_op_mode(udev); + + at76_dbg(DBG_DEVSTART, "opmode %d", op_mode); + + /* we get OPMODE_NONE with 2.4.23, SMC2662W-AR ??? + we get 204 with 2.4.23, Fiberline FL-WL240u (505A+RFMD2958) ??? */ + + if (op_mode == OPMODE_HW_CONFIG_MODE) { + dev_err(&interface->dev, + "cannot handle a device in HW_CONFIG_MODE\n"); + ret = -EBUSY; + goto exit; + } + + if (op_mode != OPMODE_NORMAL_NIC_WITH_FLASH + && op_mode != OPMODE_NORMAL_NIC_WITHOUT_FLASH) { + /* download internal firmware part */ + dev_printk(KERN_DEBUG, &interface->dev, + "downloading internal firmware\n"); + ret = at76_load_internal_fw(udev, fwe); + if (ret < 0) { + dev_err(&interface->dev, + "error %d downloading internal firmware\n", + ret); + goto exit; + } + usb_put_dev(udev); + goto exit; + } + + /* Internal firmware already inside the device. Get firmware + * version to test if external firmware is loaded. + * This works only for newer firmware, e.g. the Intersil 0.90.x + * says "control timeout on ep0in" and subsequent + * at76_get_op_mode() fail too :-( */ + + /* if version >= 0.100.x.y or device with built-in flash we can + * query the device for the fw version */ + if ((fwe->fw_version.major > 0 || fwe->fw_version.minor >= 100) + || (op_mode == OPMODE_NORMAL_NIC_WITH_FLASH)) { + ret = at76_get_mib(udev, MIB_FW_VERSION, fwv, sizeof(*fwv)); + if (ret < 0 || (fwv->major | fwv->minor) == 0) + need_ext_fw = 1; + } else + /* No way to check firmware version, reload to be sure */ + need_ext_fw = 1; + + if (need_ext_fw) { + dev_printk(KERN_DEBUG, &interface->dev, + "downloading external firmware\n"); + + ret = at76_load_external_fw(udev, fwe); + if (ret < 0) + goto exit; + + /* Re-check firmware version */ + ret = at76_get_mib(udev, MIB_FW_VERSION, fwv, sizeof(*fwv)); + if (ret < 0) { + dev_err(&interface->dev, + "error %d getting firmware version\n", ret); + goto exit; + } + } + + priv = at76_alloc_new_device(udev); + if (!priv) { + ret = -ENOMEM; + goto exit; + } + + usb_set_intfdata(interface, priv); + + memcpy(&priv->fw_version, fwv, sizeof(struct mib_fw_version)); + priv->board_type = board_type; + + ret = at76_init_new_device(priv, interface); + if (ret < 0) + at76_delete_device(priv); + +exit: + kfree(fwv); + if (ret < 0) + usb_put_dev(udev); + return ret; +} + +static void at76_disconnect(struct usb_interface *interface) +{ + struct at76_priv *priv; + + priv = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + + /* Disconnect after loading internal firmware */ + if (!priv) + return; + + wiphy_info(priv->hw->wiphy, "disconnecting\n"); + at76_delete_device(priv); + usb_put_dev(priv->udev); + dev_info(&interface->dev, "disconnected\n"); +} + +/* Structure for registering this driver with the USB subsystem */ +static struct usb_driver at76_driver = { + .name = DRIVER_NAME, + .probe = at76_probe, + .disconnect = at76_disconnect, + .id_table = dev_table, + .disable_hub_initiated_lpm = 1, +}; + +static int __init at76_mod_init(void) +{ + int result; + + printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION " loading\n"); + + mutex_init(&fw_mutex); + + /* register this driver with the USB subsystem */ + result = usb_register(&at76_driver); + if (result < 0) + printk(KERN_ERR DRIVER_NAME + ": usb_register failed (status %d)\n", result); + + led_trigger_register_simple("at76_usb-tx", &ledtrig_tx); + return result; +} + +static void __exit at76_mod_exit(void) +{ + int i; + + printk(KERN_INFO DRIVER_DESC " " DRIVER_VERSION " unloading\n"); + usb_deregister(&at76_driver); + for (i = 0; i < ARRAY_SIZE(firmwares); i++) + release_firmware(firmwares[i].fw); + led_trigger_unregister_simple(ledtrig_tx); +} + +module_param_named(debug, at76_debug, uint, 0600); +MODULE_PARM_DESC(debug, "Debugging level"); + +module_init(at76_mod_init); +module_exit(at76_mod_exit); + +MODULE_AUTHOR("Oliver Kurth "); +MODULE_AUTHOR("Joerg Albert "); +MODULE_AUTHOR("Alex "); +MODULE_AUTHOR("Nick Jones"); +MODULE_AUTHOR("Balint Seeber "); +MODULE_AUTHOR("Pavel Roskin "); +MODULE_AUTHOR("Guido Guenther "); +MODULE_AUTHOR("Kalle Valo "); +MODULE_AUTHOR("Sebastian Smolorz "); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/atmel/at76c50x-usb.h b/drivers/net/wireless/atmel/at76c50x-usb.h new file mode 100644 index 000000000000..ae03271f878e --- /dev/null +++ b/drivers/net/wireless/atmel/at76c50x-usb.h @@ -0,0 +1,466 @@ +/* + * Copyright (c) 2002,2003 Oliver Kurth + * (c) 2003,2004 Joerg Albert + * (c) 2007 Guido Guenther + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This driver was based on information from the Sourceforge driver + * released and maintained by Atmel: + * + * http://sourceforge.net/projects/atmelwlandriver/ + * + * Although the code was completely re-written, + * it would have been impossible without Atmel's decision to + * release an Open Source driver (unfortunately the firmware was + * kept binary only). Thanks for that decision to Atmel! + */ + +#ifndef _AT76_USB_H +#define _AT76_USB_H + +/* Board types */ +enum board_type { + BOARD_503_ISL3861 = 1, + BOARD_503_ISL3863 = 2, + BOARD_503 = 3, + BOARD_503_ACC = 4, + BOARD_505 = 5, + BOARD_505_2958 = 6, + BOARD_505A = 7, + BOARD_505AMX = 8 +}; + +#define CMD_STATUS_IDLE 0x00 +#define CMD_STATUS_COMPLETE 0x01 +#define CMD_STATUS_UNKNOWN 0x02 +#define CMD_STATUS_INVALID_PARAMETER 0x03 +#define CMD_STATUS_FUNCTION_NOT_SUPPORTED 0x04 +#define CMD_STATUS_TIME_OUT 0x07 +#define CMD_STATUS_IN_PROGRESS 0x08 +#define CMD_STATUS_HOST_FAILURE 0xff +#define CMD_STATUS_SCAN_FAILED 0xf0 + +/* answers to get op mode */ +#define OPMODE_NONE 0x00 +#define OPMODE_NORMAL_NIC_WITH_FLASH 0x01 +#define OPMODE_HW_CONFIG_MODE 0x02 +#define OPMODE_DFU_MODE_WITH_FLASH 0x03 +#define OPMODE_NORMAL_NIC_WITHOUT_FLASH 0x04 + +#define CMD_SET_MIB 0x01 +#define CMD_GET_MIB 0x02 +#define CMD_SCAN 0x03 +#define CMD_JOIN 0x04 +#define CMD_START_IBSS 0x05 +#define CMD_RADIO_ON 0x06 +#define CMD_RADIO_OFF 0x07 +#define CMD_STARTUP 0x0B + +#define MIB_LOCAL 0x01 +#define MIB_MAC_ADDR 0x02 +#define MIB_MAC 0x03 +#define MIB_MAC_MGMT 0x05 +#define MIB_MAC_WEP 0x06 +#define MIB_PHY 0x07 +#define MIB_FW_VERSION 0x08 +#define MIB_MDOMAIN 0x09 + +#define ADHOC_MODE 1 +#define INFRASTRUCTURE_MODE 2 + +/* values for struct mib_local, field preamble_type */ +#define PREAMBLE_TYPE_LONG 0 +#define PREAMBLE_TYPE_SHORT 1 +#define PREAMBLE_TYPE_AUTO 2 + +/* values for tx_rate */ +#define TX_RATE_1MBIT 0 +#define TX_RATE_2MBIT 1 +#define TX_RATE_5_5MBIT 2 +#define TX_RATE_11MBIT 3 +#define TX_RATE_AUTO 4 + +/* power management modes */ +#define AT76_PM_OFF 1 +#define AT76_PM_ON 2 +#define AT76_PM_SMART 3 + +struct hwcfg_r505 { + u8 cr39_values[14]; + u8 reserved1[14]; + u8 bb_cr[14]; + u8 pidvid[4]; + u8 mac_addr[ETH_ALEN]; + u8 regulatory_domain; + u8 reserved2[14]; + u8 cr15_values[14]; + u8 reserved3[3]; +} __packed; + +struct hwcfg_rfmd { + u8 cr20_values[14]; + u8 cr21_values[14]; + u8 bb_cr[14]; + u8 pidvid[4]; + u8 mac_addr[ETH_ALEN]; + u8 regulatory_domain; + u8 low_power_values[14]; + u8 normal_power_values[14]; + u8 reserved1[3]; +} __packed; + +struct hwcfg_intersil { + u8 mac_addr[ETH_ALEN]; + u8 cr31_values[14]; + u8 cr58_values[14]; + u8 pidvid[4]; + u8 regulatory_domain; + u8 reserved[1]; +} __packed; + +union at76_hwcfg { + struct hwcfg_intersil i; + struct hwcfg_rfmd r3; + struct hwcfg_r505 r5; +}; + +#define WEP_SMALL_KEY_LEN (40 / 8) +#define WEP_LARGE_KEY_LEN (104 / 8) +#define WEP_KEYS (4) + +struct at76_card_config { + u8 exclude_unencrypted; + u8 promiscuous_mode; + u8 short_retry_limit; + u8 encryption_type; + __le16 rts_threshold; + __le16 fragmentation_threshold; /* 256..2346 */ + u8 basic_rate_set[4]; + u8 auto_rate_fallback; /* 0,1 */ + u8 channel; + u8 privacy_invoked; + u8 wep_default_key_id; /* 0..3 */ + u8 current_ssid[32]; + u8 wep_default_key_value[4][WEP_LARGE_KEY_LEN]; + u8 ssid_len; + u8 short_preamble; + __le16 beacon_period; +} __packed; + +struct at76_command { + u8 cmd; + u8 reserved; + __le16 size; + u8 data[0]; +} __packed; + +/* Length of Atmel-specific Rx header before 802.11 frame */ +#define AT76_RX_HDRLEN offsetof(struct at76_rx_buffer, packet) + +struct at76_rx_buffer { + __le16 wlength; + u8 rx_rate; + u8 newbss; + u8 fragmentation; + u8 rssi; + u8 link_quality; + u8 noise_level; + __le32 rx_time; + u8 packet[IEEE80211_MAX_FRAG_THRESHOLD]; +} __packed; + +/* Length of Atmel-specific Tx header before 802.11 frame */ +#define AT76_TX_HDRLEN offsetof(struct at76_tx_buffer, packet) + +struct at76_tx_buffer { + __le16 wlength; + u8 tx_rate; + u8 padding; + u8 reserved[4]; + u8 packet[IEEE80211_MAX_FRAG_THRESHOLD]; +} __packed; + +/* defines for scan_type below */ +#define SCAN_TYPE_ACTIVE 0 +#define SCAN_TYPE_PASSIVE 1 + +struct at76_req_scan { + u8 bssid[ETH_ALEN]; + u8 essid[32]; + u8 scan_type; + u8 channel; + __le16 probe_delay; + __le16 min_channel_time; + __le16 max_channel_time; + u8 essid_size; + u8 international_scan; +} __packed; + +struct at76_req_ibss { + u8 bssid[ETH_ALEN]; + u8 essid[32]; + u8 bss_type; + u8 channel; + u8 essid_size; + u8 reserved[3]; +} __packed; + +struct at76_req_join { + u8 bssid[ETH_ALEN]; + u8 essid[32]; + u8 bss_type; + u8 channel; + __le16 timeout; + u8 essid_size; + u8 reserved; +} __packed; + +struct mib_local { + u16 reserved0; + u8 beacon_enable; + u8 txautorate_fallback; + u8 reserved1; + u8 ssid_size; + u8 promiscuous_mode; + u16 reserved2; + u8 preamble_type; + u16 reserved3; +} __packed; + +struct mib_mac_addr { + u8 mac_addr[ETH_ALEN]; + u8 res[2]; /* ??? */ + u8 group_addr[4][ETH_ALEN]; + u8 group_addr_status[4]; +} __packed; + +struct mib_mac { + __le32 max_tx_msdu_lifetime; + __le32 max_rx_lifetime; + __le16 frag_threshold; + __le16 rts_threshold; + __le16 cwmin; + __le16 cwmax; + u8 short_retry_time; + u8 long_retry_time; + u8 scan_type; /* active or passive */ + u8 scan_channel; + __le16 probe_delay; /* delay before ProbeReq in active scan, RO */ + __le16 min_channel_time; + __le16 max_channel_time; + __le16 listen_interval; + u8 desired_ssid[32]; + u8 desired_bssid[ETH_ALEN]; + u8 desired_bsstype; /* ad-hoc or infrastructure */ + u8 reserved2; +} __packed; + +struct mib_mac_mgmt { + __le16 beacon_period; + __le16 CFP_max_duration; + __le16 medium_occupancy_limit; + __le16 station_id; /* assoc id */ + __le16 ATIM_window; + u8 CFP_mode; + u8 privacy_option_implemented; + u8 DTIM_period; + u8 CFP_period; + u8 current_bssid[ETH_ALEN]; + u8 current_essid[32]; + u8 current_bss_type; + u8 power_mgmt_mode; + /* rfmd and 505 */ + u8 ibss_change; + u8 res; + u8 multi_domain_capability_implemented; + u8 multi_domain_capability_enabled; + u8 country_string[IEEE80211_COUNTRY_STRING_LEN]; + u8 reserved[3]; +} __packed; + +struct mib_mac_wep { + u8 privacy_invoked; /* 0 disable encr., 1 enable encr */ + u8 wep_default_key_id; + u8 wep_key_mapping_len; + u8 exclude_unencrypted; + __le32 wep_icv_error_count; + __le32 wep_excluded_count; + u8 wep_default_keyvalue[WEP_KEYS][WEP_LARGE_KEY_LEN]; + u8 encryption_level; /* 1 for 40bit, 2 for 104bit encryption */ +} __packed; + +struct mib_phy { + __le32 ed_threshold; + + __le16 slot_time; + __le16 sifs_time; + __le16 preamble_length; + __le16 plcp_header_length; + __le16 mpdu_max_length; + __le16 cca_mode_supported; + + u8 operation_rate_set[4]; + u8 channel_id; + u8 current_cca_mode; + u8 phy_type; + u8 current_reg_domain; +} __packed; + +struct mib_fw_version { + u8 major; + u8 minor; + u8 patch; + u8 build; +} __packed; + +struct mib_mdomain { + u8 tx_powerlevel[14]; + u8 channel_list[14]; /* 0 for invalid channels */ +} __packed; + +struct set_mib_buffer { + u8 type; + u8 size; + u8 index; + u8 reserved; + union { + u8 byte; + __le16 word; + u8 addr[ETH_ALEN]; + struct mib_mac_wep wep_mib; + } data; +} __packed; + +struct at76_fw_header { + __le32 crc; /* CRC32 of the whole image */ + __le32 board_type; /* firmware compatibility code */ + u8 build; /* firmware build number */ + u8 patch; /* firmware patch level */ + u8 minor; /* firmware minor version */ + u8 major; /* firmware major version */ + __le32 str_offset; /* offset of the copyright string */ + __le32 int_fw_offset; /* internal firmware image offset */ + __le32 int_fw_len; /* internal firmware image length */ + __le32 ext_fw_offset; /* external firmware image offset */ + __le32 ext_fw_len; /* external firmware image length */ +} __packed; + +/* a description of a regulatory domain and the allowed channels */ +struct reg_domain { + u16 code; + char const *name; + u32 channel_map; /* if bit N is set, channel (N+1) is allowed */ +}; + +/* Data for one loaded firmware file */ +struct fwentry { + const char *const fwname; + const struct firmware *fw; + int extfw_size; + int intfw_size; + /* pointer to loaded firmware, no need to free */ + u8 *extfw; /* external firmware, extfw_size bytes long */ + u8 *intfw; /* internal firmware, intfw_size bytes long */ + enum board_type board_type; /* board type */ + struct mib_fw_version fw_version; + int loaded; /* Loaded and parsed successfully */ +}; + +struct at76_priv { + struct usb_device *udev; /* USB device pointer */ + + struct sk_buff *rx_skb; /* skbuff for receiving data */ + struct sk_buff *tx_skb; /* skbuff for transmitting data */ + void *bulk_out_buffer; /* buffer for sending data */ + + struct urb *tx_urb; /* URB for sending data */ + struct urb *rx_urb; /* URB for receiving data */ + + unsigned int tx_pipe; /* bulk out pipe */ + unsigned int rx_pipe; /* bulk in pipe */ + + struct mutex mtx; /* locks this structure */ + + /* work queues */ + struct work_struct work_set_promisc; + struct work_struct work_submit_rx; + struct work_struct work_join_bssid; + struct delayed_work dwork_hw_scan; + + struct tasklet_struct rx_tasklet; + + /* the WEP stuff */ + int wep_enabled; /* 1 if WEP is enabled */ + int wep_key_id; /* key id to be used */ + u8 wep_keys[WEP_KEYS][WEP_LARGE_KEY_LEN]; /* WEP keys */ + u8 wep_keys_len[WEP_KEYS]; /* length of WEP keys */ + + int channel; + int iw_mode; + u8 bssid[ETH_ALEN]; + u8 essid[IW_ESSID_MAX_SIZE]; + int essid_size; + int radio_on; + int promisc; + + int preamble_type; /* 0 - long, 1 - short, 2 - auto */ + int auth_mode; /* authentication type: 0 open, 1 shared key */ + int txrate; /* 0,1,2,3 = 1,2,5.5,11 Mbps, 4 is auto */ + int frag_threshold; /* threshold for fragmentation of tx packets */ + int rts_threshold; /* threshold for RTS mechanism */ + int short_retry_limit; + + int scan_min_time; /* scan min channel time */ + int scan_max_time; /* scan max channel time */ + int scan_mode; /* SCAN_TYPE_ACTIVE, SCAN_TYPE_PASSIVE */ + int scan_need_any; /* if set, need to scan for any ESSID */ + bool scanning; /* if set, the scan is running */ + + u16 assoc_id; /* current association ID, if associated */ + + u8 pm_mode; /* power management mode */ + u32 pm_period; /* power management period in microseconds */ + + struct reg_domain const *domain; /* reg domain description */ + + /* These fields contain HW config provided by the device (not all of + * these fields are used by all board types) */ + u8 mac_addr[ETH_ALEN]; + u8 regulatory_domain; + + struct at76_card_config card_config; + + enum board_type board_type; + struct mib_fw_version fw_version; + + unsigned int device_unplugged:1; + unsigned int netdev_registered:1; + struct set_mib_buffer mib_buf; /* global buffer for set_mib calls */ + + int beacon_period; /* period of mgmt beacons, Kus */ + + struct ieee80211_hw *hw; + int mac80211_registered; +}; + +#define AT76_SUPPORTED_FILTERS 0 + +#define SCAN_POLL_INTERVAL (HZ / 4) + +#define CMD_COMPLETION_TIMEOUT (5 * HZ) + +#define DEF_RTS_THRESHOLD 1536 +#define DEF_FRAG_THRESHOLD 1536 +#define DEF_SHORT_RETRY_LIMIT 8 +#define DEF_CHANNEL 10 +#define DEF_SCAN_MIN_TIME 10 +#define DEF_SCAN_MAX_TIME 120 + +/* the max padding size for tx in bytes (see calc_padding) */ +#define MAX_PADDING_SIZE 53 + +#endif /* _AT76_USB_H */ diff --git a/drivers/net/wireless/atmel/atmel.c b/drivers/net/wireless/atmel/atmel.c new file mode 100644 index 000000000000..6a1f03c271c1 --- /dev/null +++ b/drivers/net/wireless/atmel/atmel.c @@ -0,0 +1,4549 @@ +/*** -*- linux-c -*- ********************************************************** + + Driver for Atmel at76c502 at76c504 and at76c506 wireless cards. + + Copyright 2000-2001 ATMEL Corporation. + Copyright 2003-2004 Simon Kelley. + + This code was developed from version 2.1.1 of the Atmel drivers, + released by Atmel corp. under the GPL in December 2002. It also + includes code from the Linux aironet drivers (C) Benjamin Reed, + and the Linux PCMCIA package, (C) David Hinds and the Linux wireless + extensions, (C) Jean Tourrilhes. + + The firmware module for reading the MAC address of the card comes from + net.russotto.AtmelMACFW, written by Matthew T. Russotto and copyright + by him. net.russotto.AtmelMACFW is used under the GPL license version 2. + This file contains the module in binary form and, under the terms + of the GPL, in source form. The source is located at the end of the file. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This software is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Atmel wireless lan drivers; if not, see + . + + For all queries about this code, please contact the current author, + Simon Kelley and not Atmel Corporation. + + Credit is due to HP UK and Cambridge Online Systems Ltd for supplying + hardware used during development of this driver. + +******************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "atmel.h" + +#define DRIVER_MAJOR 0 +#define DRIVER_MINOR 98 + +MODULE_AUTHOR("Simon Kelley"); +MODULE_DESCRIPTION("Support for Atmel at76c50x 802.11 wireless ethernet cards."); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("Atmel at76c50x wireless cards"); + +/* The name of the firmware file to be loaded + over-rides any automatic selection */ +static char *firmware = NULL; +module_param(firmware, charp, 0); + +/* table of firmware file names */ +static struct { + AtmelFWType fw_type; + const char *fw_file; + const char *fw_file_ext; +} fw_table[] = { + { ATMEL_FW_TYPE_502, "atmel_at76c502", "bin" }, + { ATMEL_FW_TYPE_502D, "atmel_at76c502d", "bin" }, + { ATMEL_FW_TYPE_502E, "atmel_at76c502e", "bin" }, + { ATMEL_FW_TYPE_502_3COM, "atmel_at76c502_3com", "bin" }, + { ATMEL_FW_TYPE_504, "atmel_at76c504", "bin" }, + { ATMEL_FW_TYPE_504_2958, "atmel_at76c504_2958", "bin" }, + { ATMEL_FW_TYPE_504A_2958, "atmel_at76c504a_2958", "bin" }, + { ATMEL_FW_TYPE_506, "atmel_at76c506", "bin" }, + { ATMEL_FW_TYPE_NONE, NULL, NULL } +}; +MODULE_FIRMWARE("atmel_at76c502-wpa.bin"); +MODULE_FIRMWARE("atmel_at76c502.bin"); +MODULE_FIRMWARE("atmel_at76c502d-wpa.bin"); +MODULE_FIRMWARE("atmel_at76c502d.bin"); +MODULE_FIRMWARE("atmel_at76c502e-wpa.bin"); +MODULE_FIRMWARE("atmel_at76c502e.bin"); +MODULE_FIRMWARE("atmel_at76c502_3com-wpa.bin"); +MODULE_FIRMWARE("atmel_at76c502_3com.bin"); +MODULE_FIRMWARE("atmel_at76c504-wpa.bin"); +MODULE_FIRMWARE("atmel_at76c504.bin"); +MODULE_FIRMWARE("atmel_at76c504_2958-wpa.bin"); +MODULE_FIRMWARE("atmel_at76c504_2958.bin"); +MODULE_FIRMWARE("atmel_at76c504a_2958-wpa.bin"); +MODULE_FIRMWARE("atmel_at76c504a_2958.bin"); +MODULE_FIRMWARE("atmel_at76c506-wpa.bin"); +MODULE_FIRMWARE("atmel_at76c506.bin"); + +#define MAX_SSID_LENGTH 32 +#define MGMT_JIFFIES (256 * HZ / 100) + +#define MAX_BSS_ENTRIES 64 + +/* registers */ +#define GCR 0x00 /* (SIR0) General Configuration Register */ +#define BSR 0x02 /* (SIR1) Bank Switching Select Register */ +#define AR 0x04 +#define DR 0x08 +#define MR1 0x12 /* Mirror Register 1 */ +#define MR2 0x14 /* Mirror Register 2 */ +#define MR3 0x16 /* Mirror Register 3 */ +#define MR4 0x18 /* Mirror Register 4 */ + +#define GPR1 0x0c +#define GPR2 0x0e +#define GPR3 0x10 +/* + * Constants for the GCR register. + */ +#define GCR_REMAP 0x0400 /* Remap internal SRAM to 0 */ +#define GCR_SWRES 0x0080 /* BIU reset (ARM and PAI are NOT reset) */ +#define GCR_CORES 0x0060 /* Core Reset (ARM and PAI are reset) */ +#define GCR_ENINT 0x0002 /* Enable Interrupts */ +#define GCR_ACKINT 0x0008 /* Acknowledge Interrupts */ + +#define BSS_SRAM 0x0200 /* AMBA module selection --> SRAM */ +#define BSS_IRAM 0x0100 /* AMBA module selection --> IRAM */ +/* + *Constants for the MR registers. + */ +#define MAC_INIT_COMPLETE 0x0001 /* MAC init has been completed */ +#define MAC_BOOT_COMPLETE 0x0010 /* MAC boot has been completed */ +#define MAC_INIT_OK 0x0002 /* MAC boot has been completed */ + +#define MIB_MAX_DATA_BYTES 212 +#define MIB_HEADER_SIZE 4 /* first four fields */ + +struct get_set_mib { + u8 type; + u8 size; + u8 index; + u8 reserved; + u8 data[MIB_MAX_DATA_BYTES]; +}; + +struct rx_desc { + u32 Next; + u16 MsduPos; + u16 MsduSize; + + u8 State; + u8 Status; + u8 Rate; + u8 Rssi; + u8 LinkQuality; + u8 PreambleType; + u16 Duration; + u32 RxTime; +}; + +#define RX_DESC_FLAG_VALID 0x80 +#define RX_DESC_FLAG_CONSUMED 0x40 +#define RX_DESC_FLAG_IDLE 0x00 + +#define RX_STATUS_SUCCESS 0x00 + +#define RX_DESC_MSDU_POS_OFFSET 4 +#define RX_DESC_MSDU_SIZE_OFFSET 6 +#define RX_DESC_FLAGS_OFFSET 8 +#define RX_DESC_STATUS_OFFSET 9 +#define RX_DESC_RSSI_OFFSET 11 +#define RX_DESC_LINK_QUALITY_OFFSET 12 +#define RX_DESC_PREAMBLE_TYPE_OFFSET 13 +#define RX_DESC_DURATION_OFFSET 14 +#define RX_DESC_RX_TIME_OFFSET 16 + +struct tx_desc { + u32 NextDescriptor; + u16 TxStartOfFrame; + u16 TxLength; + + u8 TxState; + u8 TxStatus; + u8 RetryCount; + + u8 TxRate; + + u8 KeyIndex; + u8 ChiperType; + u8 ChipreLength; + u8 Reserved1; + + u8 Reserved; + u8 PacketType; + u16 HostTxLength; +}; + +#define TX_DESC_NEXT_OFFSET 0 +#define TX_DESC_POS_OFFSET 4 +#define TX_DESC_SIZE_OFFSET 6 +#define TX_DESC_FLAGS_OFFSET 8 +#define TX_DESC_STATUS_OFFSET 9 +#define TX_DESC_RETRY_OFFSET 10 +#define TX_DESC_RATE_OFFSET 11 +#define TX_DESC_KEY_INDEX_OFFSET 12 +#define TX_DESC_CIPHER_TYPE_OFFSET 13 +#define TX_DESC_CIPHER_LENGTH_OFFSET 14 +#define TX_DESC_PACKET_TYPE_OFFSET 17 +#define TX_DESC_HOST_LENGTH_OFFSET 18 + +/* + * Host-MAC interface + */ + +#define TX_STATUS_SUCCESS 0x00 + +#define TX_FIRM_OWN 0x80 +#define TX_DONE 0x40 + +#define TX_ERROR 0x01 + +#define TX_PACKET_TYPE_DATA 0x01 +#define TX_PACKET_TYPE_MGMT 0x02 + +#define ISR_EMPTY 0x00 /* no bits set in ISR */ +#define ISR_TxCOMPLETE 0x01 /* packet transmitted */ +#define ISR_RxCOMPLETE 0x02 /* packet received */ +#define ISR_RxFRAMELOST 0x04 /* Rx Frame lost */ +#define ISR_FATAL_ERROR 0x08 /* Fatal error */ +#define ISR_COMMAND_COMPLETE 0x10 /* command completed */ +#define ISR_OUT_OF_RANGE 0x20 /* command completed */ +#define ISR_IBSS_MERGE 0x40 /* (4.1.2.30): IBSS merge */ +#define ISR_GENERIC_IRQ 0x80 + +#define Local_Mib_Type 0x01 +#define Mac_Address_Mib_Type 0x02 +#define Mac_Mib_Type 0x03 +#define Statistics_Mib_Type 0x04 +#define Mac_Mgmt_Mib_Type 0x05 +#define Mac_Wep_Mib_Type 0x06 +#define Phy_Mib_Type 0x07 +#define Multi_Domain_MIB 0x08 + +#define MAC_MGMT_MIB_CUR_BSSID_POS 14 +#define MAC_MIB_FRAG_THRESHOLD_POS 8 +#define MAC_MIB_RTS_THRESHOLD_POS 10 +#define MAC_MIB_SHORT_RETRY_POS 16 +#define MAC_MIB_LONG_RETRY_POS 17 +#define MAC_MIB_SHORT_RETRY_LIMIT_POS 16 +#define MAC_MGMT_MIB_BEACON_PER_POS 0 +#define MAC_MGMT_MIB_STATION_ID_POS 6 +#define MAC_MGMT_MIB_CUR_PRIVACY_POS 11 +#define MAC_MGMT_MIB_CUR_BSSID_POS 14 +#define MAC_MGMT_MIB_PS_MODE_POS 53 +#define MAC_MGMT_MIB_LISTEN_INTERVAL_POS 54 +#define MAC_MGMT_MIB_MULTI_DOMAIN_IMPLEMENTED 56 +#define MAC_MGMT_MIB_MULTI_DOMAIN_ENABLED 57 +#define PHY_MIB_CHANNEL_POS 14 +#define PHY_MIB_RATE_SET_POS 20 +#define PHY_MIB_REG_DOMAIN_POS 26 +#define LOCAL_MIB_AUTO_TX_RATE_POS 3 +#define LOCAL_MIB_SSID_SIZE 5 +#define LOCAL_MIB_TX_PROMISCUOUS_POS 6 +#define LOCAL_MIB_TX_MGMT_RATE_POS 7 +#define LOCAL_MIB_TX_CONTROL_RATE_POS 8 +#define LOCAL_MIB_PREAMBLE_TYPE 9 +#define MAC_ADDR_MIB_MAC_ADDR_POS 0 + +#define CMD_Set_MIB_Vars 0x01 +#define CMD_Get_MIB_Vars 0x02 +#define CMD_Scan 0x03 +#define CMD_Join 0x04 +#define CMD_Start 0x05 +#define CMD_EnableRadio 0x06 +#define CMD_DisableRadio 0x07 +#define CMD_SiteSurvey 0x0B + +#define CMD_STATUS_IDLE 0x00 +#define CMD_STATUS_COMPLETE 0x01 +#define CMD_STATUS_UNKNOWN 0x02 +#define CMD_STATUS_INVALID_PARAMETER 0x03 +#define CMD_STATUS_FUNCTION_NOT_SUPPORTED 0x04 +#define CMD_STATUS_TIME_OUT 0x07 +#define CMD_STATUS_IN_PROGRESS 0x08 +#define CMD_STATUS_REJECTED_RADIO_OFF 0x09 +#define CMD_STATUS_HOST_ERROR 0xFF +#define CMD_STATUS_BUSY 0xFE + +#define CMD_BLOCK_COMMAND_OFFSET 0 +#define CMD_BLOCK_STATUS_OFFSET 1 +#define CMD_BLOCK_PARAMETERS_OFFSET 4 + +#define SCAN_OPTIONS_SITE_SURVEY 0x80 + +#define MGMT_FRAME_BODY_OFFSET 24 +#define MAX_AUTHENTICATION_RETRIES 3 +#define MAX_ASSOCIATION_RETRIES 3 + +#define AUTHENTICATION_RESPONSE_TIME_OUT 1000 + +#define MAX_WIRELESS_BODY 2316 /* mtu is 2312, CRC is 4 */ +#define LOOP_RETRY_LIMIT 500000 + +#define ACTIVE_MODE 1 +#define PS_MODE 2 + +#define MAX_ENCRYPTION_KEYS 4 +#define MAX_ENCRYPTION_KEY_SIZE 40 + +/* + * 802.11 related definitions + */ + +/* + * Regulatory Domains + */ + +#define REG_DOMAIN_FCC 0x10 /* Channels 1-11 USA */ +#define REG_DOMAIN_DOC 0x20 /* Channel 1-11 Canada */ +#define REG_DOMAIN_ETSI 0x30 /* Channel 1-13 Europe (ex Spain/France) */ +#define REG_DOMAIN_SPAIN 0x31 /* Channel 10-11 Spain */ +#define REG_DOMAIN_FRANCE 0x32 /* Channel 10-13 France */ +#define REG_DOMAIN_MKK 0x40 /* Channel 14 Japan */ +#define REG_DOMAIN_MKK1 0x41 /* Channel 1-14 Japan(MKK1) */ +#define REG_DOMAIN_ISRAEL 0x50 /* Channel 3-9 ISRAEL */ + +#define BSS_TYPE_AD_HOC 1 +#define BSS_TYPE_INFRASTRUCTURE 2 + +#define SCAN_TYPE_ACTIVE 0 +#define SCAN_TYPE_PASSIVE 1 + +#define LONG_PREAMBLE 0 +#define SHORT_PREAMBLE 1 +#define AUTO_PREAMBLE 2 + +#define DATA_FRAME_WS_HEADER_SIZE 30 + +/* promiscuous mode control */ +#define PROM_MODE_OFF 0x0 +#define PROM_MODE_UNKNOWN 0x1 +#define PROM_MODE_CRC_FAILED 0x2 +#define PROM_MODE_DUPLICATED 0x4 +#define PROM_MODE_MGMT 0x8 +#define PROM_MODE_CTRL 0x10 +#define PROM_MODE_BAD_PROTOCOL 0x20 + +#define IFACE_INT_STATUS_OFFSET 0 +#define IFACE_INT_MASK_OFFSET 1 +#define IFACE_LOCKOUT_HOST_OFFSET 2 +#define IFACE_LOCKOUT_MAC_OFFSET 3 +#define IFACE_FUNC_CTRL_OFFSET 28 +#define IFACE_MAC_STAT_OFFSET 30 +#define IFACE_GENERIC_INT_TYPE_OFFSET 32 + +#define CIPHER_SUITE_NONE 0 +#define CIPHER_SUITE_WEP_64 1 +#define CIPHER_SUITE_TKIP 2 +#define CIPHER_SUITE_AES 3 +#define CIPHER_SUITE_CCX 4 +#define CIPHER_SUITE_WEP_128 5 + +/* + * IFACE MACROS & definitions + */ + +/* + * FuncCtrl field: + */ +#define FUNC_CTRL_TxENABLE 0x10 +#define FUNC_CTRL_RxENABLE 0x20 +#define FUNC_CTRL_INIT_COMPLETE 0x01 + +/* A stub firmware image which reads the MAC address from NVRAM on the card. + For copyright information and source see the end of this file. */ +static u8 mac_reader[] = { + 0x06, 0x00, 0x00, 0xea, 0x04, 0x00, 0x00, 0xea, 0x03, 0x00, 0x00, 0xea, 0x02, 0x00, 0x00, 0xea, + 0x01, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00, 0xea, 0xff, 0xff, 0xff, 0xea, 0xfe, 0xff, 0xff, 0xea, + 0xd3, 0x00, 0xa0, 0xe3, 0x00, 0xf0, 0x21, 0xe1, 0x0e, 0x04, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3, + 0x81, 0x11, 0xa0, 0xe1, 0x00, 0x10, 0x81, 0xe3, 0x00, 0x10, 0x80, 0xe5, 0x1c, 0x10, 0x90, 0xe5, + 0x10, 0x10, 0xc1, 0xe3, 0x1c, 0x10, 0x80, 0xe5, 0x01, 0x10, 0xa0, 0xe3, 0x08, 0x10, 0x80, 0xe5, + 0x02, 0x03, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3, 0xb0, 0x10, 0xc0, 0xe1, 0xb4, 0x10, 0xc0, 0xe1, + 0xb8, 0x10, 0xc0, 0xe1, 0xbc, 0x10, 0xc0, 0xe1, 0x56, 0xdc, 0xa0, 0xe3, 0x21, 0x00, 0x00, 0xeb, + 0x0a, 0x00, 0xa0, 0xe3, 0x1a, 0x00, 0x00, 0xeb, 0x10, 0x00, 0x00, 0xeb, 0x07, 0x00, 0x00, 0xeb, + 0x02, 0x03, 0xa0, 0xe3, 0x02, 0x14, 0xa0, 0xe3, 0xb4, 0x10, 0xc0, 0xe1, 0x4c, 0x10, 0x9f, 0xe5, + 0xbc, 0x10, 0xc0, 0xe1, 0x10, 0x10, 0xa0, 0xe3, 0xb8, 0x10, 0xc0, 0xe1, 0xfe, 0xff, 0xff, 0xea, + 0x00, 0x40, 0x2d, 0xe9, 0x00, 0x20, 0xa0, 0xe3, 0x02, 0x3c, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3, + 0x28, 0x00, 0x9f, 0xe5, 0x37, 0x00, 0x00, 0xeb, 0x00, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, + 0x00, 0x40, 0x2d, 0xe9, 0x12, 0x2e, 0xa0, 0xe3, 0x06, 0x30, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3, + 0x02, 0x04, 0xa0, 0xe3, 0x2f, 0x00, 0x00, 0xeb, 0x00, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, + 0x00, 0x02, 0x00, 0x02, 0x80, 0x01, 0x90, 0xe0, 0x01, 0x00, 0x00, 0x0a, 0x01, 0x00, 0x50, 0xe2, + 0xfc, 0xff, 0xff, 0xea, 0x1e, 0xff, 0x2f, 0xe1, 0x80, 0x10, 0xa0, 0xe3, 0xf3, 0x06, 0xa0, 0xe3, + 0x00, 0x10, 0x80, 0xe5, 0x00, 0x10, 0xa0, 0xe3, 0x00, 0x10, 0x80, 0xe5, 0x01, 0x10, 0xa0, 0xe3, + 0x04, 0x10, 0x80, 0xe5, 0x00, 0x10, 0x80, 0xe5, 0x0e, 0x34, 0xa0, 0xe3, 0x1c, 0x10, 0x93, 0xe5, + 0x02, 0x1a, 0x81, 0xe3, 0x1c, 0x10, 0x83, 0xe5, 0x58, 0x11, 0x9f, 0xe5, 0x30, 0x10, 0x80, 0xe5, + 0x54, 0x11, 0x9f, 0xe5, 0x34, 0x10, 0x80, 0xe5, 0x38, 0x10, 0x80, 0xe5, 0x3c, 0x10, 0x80, 0xe5, + 0x10, 0x10, 0x90, 0xe5, 0x08, 0x00, 0x90, 0xe5, 0x1e, 0xff, 0x2f, 0xe1, 0xf3, 0x16, 0xa0, 0xe3, + 0x08, 0x00, 0x91, 0xe5, 0x05, 0x00, 0xa0, 0xe3, 0x0c, 0x00, 0x81, 0xe5, 0x10, 0x00, 0x91, 0xe5, + 0x02, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0xff, 0x00, 0xa0, 0xe3, 0x0c, 0x00, 0x81, 0xe5, + 0x10, 0x00, 0x91, 0xe5, 0x02, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0x08, 0x00, 0x91, 0xe5, + 0x10, 0x00, 0x91, 0xe5, 0x01, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0x08, 0x00, 0x91, 0xe5, + 0xff, 0x00, 0x00, 0xe2, 0x1e, 0xff, 0x2f, 0xe1, 0x30, 0x40, 0x2d, 0xe9, 0x00, 0x50, 0xa0, 0xe1, + 0x03, 0x40, 0xa0, 0xe1, 0xa2, 0x02, 0xa0, 0xe1, 0x08, 0x00, 0x00, 0xe2, 0x03, 0x00, 0x80, 0xe2, + 0xd8, 0x10, 0x9f, 0xe5, 0x00, 0x00, 0xc1, 0xe5, 0x01, 0x20, 0xc1, 0xe5, 0xe2, 0xff, 0xff, 0xeb, + 0x01, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x1a, 0x14, 0x00, 0xa0, 0xe3, 0xc4, 0xff, 0xff, 0xeb, + 0x04, 0x20, 0xa0, 0xe1, 0x05, 0x10, 0xa0, 0xe1, 0x02, 0x00, 0xa0, 0xe3, 0x01, 0x00, 0x00, 0xeb, + 0x30, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 0x70, 0x40, 0x2d, 0xe9, 0xf3, 0x46, 0xa0, 0xe3, + 0x00, 0x30, 0xa0, 0xe3, 0x00, 0x00, 0x50, 0xe3, 0x08, 0x00, 0x00, 0x9a, 0x8c, 0x50, 0x9f, 0xe5, + 0x03, 0x60, 0xd5, 0xe7, 0x0c, 0x60, 0x84, 0xe5, 0x10, 0x60, 0x94, 0xe5, 0x02, 0x00, 0x16, 0xe3, + 0xfc, 0xff, 0xff, 0x0a, 0x01, 0x30, 0x83, 0xe2, 0x00, 0x00, 0x53, 0xe1, 0xf7, 0xff, 0xff, 0x3a, + 0xff, 0x30, 0xa0, 0xe3, 0x0c, 0x30, 0x84, 0xe5, 0x08, 0x00, 0x94, 0xe5, 0x10, 0x00, 0x94, 0xe5, + 0x01, 0x00, 0x10, 0xe3, 0xfc, 0xff, 0xff, 0x0a, 0x08, 0x00, 0x94, 0xe5, 0x00, 0x00, 0xa0, 0xe3, + 0x00, 0x00, 0x52, 0xe3, 0x0b, 0x00, 0x00, 0x9a, 0x10, 0x50, 0x94, 0xe5, 0x02, 0x00, 0x15, 0xe3, + 0xfc, 0xff, 0xff, 0x0a, 0x0c, 0x30, 0x84, 0xe5, 0x10, 0x50, 0x94, 0xe5, 0x01, 0x00, 0x15, 0xe3, + 0xfc, 0xff, 0xff, 0x0a, 0x08, 0x50, 0x94, 0xe5, 0x01, 0x50, 0xc1, 0xe4, 0x01, 0x00, 0x80, 0xe2, + 0x02, 0x00, 0x50, 0xe1, 0xf3, 0xff, 0xff, 0x3a, 0xc8, 0x00, 0xa0, 0xe3, 0x98, 0xff, 0xff, 0xeb, + 0x70, 0x40, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 0x01, 0x0c, 0x00, 0x02, 0x01, 0x02, 0x00, 0x02, + 0x00, 0x01, 0x00, 0x02 +}; + +struct atmel_private { + void *card; /* Bus dependent structure varies for PCcard */ + int (*present_callback)(void *); /* And callback which uses it */ + char firmware_id[32]; + AtmelFWType firmware_type; + u8 *firmware; + int firmware_length; + struct timer_list management_timer; + struct net_device *dev; + struct device *sys_dev; + struct iw_statistics wstats; + spinlock_t irqlock, timerlock; /* spinlocks */ + enum { BUS_TYPE_PCCARD, BUS_TYPE_PCI } bus_type; + enum { + CARD_TYPE_PARALLEL_FLASH, + CARD_TYPE_SPI_FLASH, + CARD_TYPE_EEPROM + } card_type; + int do_rx_crc; /* If we need to CRC incoming packets */ + int probe_crc; /* set if we don't yet know */ + int crc_ok_cnt, crc_ko_cnt; /* counters for probing */ + u16 rx_desc_head; + u16 tx_desc_free, tx_desc_head, tx_desc_tail, tx_desc_previous; + u16 tx_free_mem, tx_buff_head, tx_buff_tail; + + u16 frag_seq, frag_len, frag_no; + u8 frag_source[6]; + + u8 wep_is_on, default_key, exclude_unencrypted, encryption_level; + u8 group_cipher_suite, pairwise_cipher_suite; + u8 wep_keys[MAX_ENCRYPTION_KEYS][MAX_ENCRYPTION_KEY_SIZE]; + int wep_key_len[MAX_ENCRYPTION_KEYS]; + int use_wpa, radio_on_broken; /* firmware dependent stuff. */ + + u16 host_info_base; + struct host_info_struct { + /* NB this is matched to the hardware, don't change. */ + u8 volatile int_status; + u8 volatile int_mask; + u8 volatile lockout_host; + u8 volatile lockout_mac; + + u16 tx_buff_pos; + u16 tx_buff_size; + u16 tx_desc_pos; + u16 tx_desc_count; + + u16 rx_buff_pos; + u16 rx_buff_size; + u16 rx_desc_pos; + u16 rx_desc_count; + + u16 build_version; + u16 command_pos; + + u16 major_version; + u16 minor_version; + + u16 func_ctrl; + u16 mac_status; + u16 generic_IRQ_type; + u8 reserved[2]; + } host_info; + + enum { + STATION_STATE_SCANNING, + STATION_STATE_JOINNING, + STATION_STATE_AUTHENTICATING, + STATION_STATE_ASSOCIATING, + STATION_STATE_READY, + STATION_STATE_REASSOCIATING, + STATION_STATE_DOWN, + STATION_STATE_MGMT_ERROR + } station_state; + + int operating_mode, power_mode; + time_t last_qual; + int beacons_this_sec; + int channel; + int reg_domain, config_reg_domain; + int tx_rate; + int auto_tx_rate; + int rts_threshold; + int frag_threshold; + int long_retry, short_retry; + int preamble; + int default_beacon_period, beacon_period, listen_interval; + int CurrentAuthentTransactionSeqNum, ExpectedAuthentTransactionSeqNum; + int AuthenticationRequestRetryCnt, AssociationRequestRetryCnt, ReAssociationRequestRetryCnt; + enum { + SITE_SURVEY_IDLE, + SITE_SURVEY_IN_PROGRESS, + SITE_SURVEY_COMPLETED + } site_survey_state; + unsigned long last_survey; + + int station_was_associated, station_is_associated; + int fast_scan; + + struct bss_info { + int channel; + int SSIDsize; + int RSSI; + int UsingWEP; + int preamble; + int beacon_period; + int BSStype; + u8 BSSID[6]; + u8 SSID[MAX_SSID_LENGTH]; + } BSSinfo[MAX_BSS_ENTRIES]; + int BSS_list_entries, current_BSS; + int connect_to_any_BSS; + int SSID_size, new_SSID_size; + u8 CurrentBSSID[6], BSSID[6]; + u8 SSID[MAX_SSID_LENGTH], new_SSID[MAX_SSID_LENGTH]; + u64 last_beacon_timestamp; + u8 rx_buf[MAX_WIRELESS_BODY]; +}; + +static u8 atmel_basic_rates[4] = {0x82, 0x84, 0x0b, 0x16}; + +static const struct { + int reg_domain; + int min, max; + char *name; +} channel_table[] = { { REG_DOMAIN_FCC, 1, 11, "USA" }, + { REG_DOMAIN_DOC, 1, 11, "Canada" }, + { REG_DOMAIN_ETSI, 1, 13, "Europe" }, + { REG_DOMAIN_SPAIN, 10, 11, "Spain" }, + { REG_DOMAIN_FRANCE, 10, 13, "France" }, + { REG_DOMAIN_MKK, 14, 14, "MKK" }, + { REG_DOMAIN_MKK1, 1, 14, "MKK1" }, + { REG_DOMAIN_ISRAEL, 3, 9, "Israel"} }; + +static void build_wpa_mib(struct atmel_private *priv); +static int atmel_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static void atmel_copy_to_card(struct net_device *dev, u16 dest, + const unsigned char *src, u16 len); +static void atmel_copy_to_host(struct net_device *dev, unsigned char *dest, + u16 src, u16 len); +static void atmel_set_gcr(struct net_device *dev, u16 mask); +static void atmel_clear_gcr(struct net_device *dev, u16 mask); +static int atmel_lock_mac(struct atmel_private *priv); +static void atmel_wmem32(struct atmel_private *priv, u16 pos, u32 data); +static void atmel_command_irq(struct atmel_private *priv); +static int atmel_validate_channel(struct atmel_private *priv, int channel); +static void atmel_management_frame(struct atmel_private *priv, + struct ieee80211_hdr *header, + u16 frame_len, u8 rssi); +static void atmel_management_timer(u_long a); +static void atmel_send_command(struct atmel_private *priv, int command, + void *cmd, int cmd_size); +static int atmel_send_command_wait(struct atmel_private *priv, int command, + void *cmd, int cmd_size); +static void atmel_transmit_management_frame(struct atmel_private *priv, + struct ieee80211_hdr *header, + u8 *body, int body_len); + +static u8 atmel_get_mib8(struct atmel_private *priv, u8 type, u8 index); +static void atmel_set_mib8(struct atmel_private *priv, u8 type, u8 index, + u8 data); +static void atmel_set_mib16(struct atmel_private *priv, u8 type, u8 index, + u16 data); +static void atmel_set_mib(struct atmel_private *priv, u8 type, u8 index, + u8 *data, int data_len); +static void atmel_get_mib(struct atmel_private *priv, u8 type, u8 index, + u8 *data, int data_len); +static void atmel_scan(struct atmel_private *priv, int specific_ssid); +static void atmel_join_bss(struct atmel_private *priv, int bss_index); +static void atmel_smooth_qual(struct atmel_private *priv); +static void atmel_writeAR(struct net_device *dev, u16 data); +static int probe_atmel_card(struct net_device *dev); +static int reset_atmel_card(struct net_device *dev); +static void atmel_enter_state(struct atmel_private *priv, int new_state); +int atmel_open (struct net_device *dev); + +static inline u16 atmel_hi(struct atmel_private *priv, u16 offset) +{ + return priv->host_info_base + offset; +} + +static inline u16 atmel_co(struct atmel_private *priv, u16 offset) +{ + return priv->host_info.command_pos + offset; +} + +static inline u16 atmel_rx(struct atmel_private *priv, u16 offset, u16 desc) +{ + return priv->host_info.rx_desc_pos + (sizeof(struct rx_desc) * desc) + offset; +} + +static inline u16 atmel_tx(struct atmel_private *priv, u16 offset, u16 desc) +{ + return priv->host_info.tx_desc_pos + (sizeof(struct tx_desc) * desc) + offset; +} + +static inline u8 atmel_read8(struct net_device *dev, u16 offset) +{ + return inb(dev->base_addr + offset); +} + +static inline void atmel_write8(struct net_device *dev, u16 offset, u8 data) +{ + outb(data, dev->base_addr + offset); +} + +static inline u16 atmel_read16(struct net_device *dev, u16 offset) +{ + return inw(dev->base_addr + offset); +} + +static inline void atmel_write16(struct net_device *dev, u16 offset, u16 data) +{ + outw(data, dev->base_addr + offset); +} + +static inline u8 atmel_rmem8(struct atmel_private *priv, u16 pos) +{ + atmel_writeAR(priv->dev, pos); + return atmel_read8(priv->dev, DR); +} + +static inline void atmel_wmem8(struct atmel_private *priv, u16 pos, u16 data) +{ + atmel_writeAR(priv->dev, pos); + atmel_write8(priv->dev, DR, data); +} + +static inline u16 atmel_rmem16(struct atmel_private *priv, u16 pos) +{ + atmel_writeAR(priv->dev, pos); + return atmel_read16(priv->dev, DR); +} + +static inline void atmel_wmem16(struct atmel_private *priv, u16 pos, u16 data) +{ + atmel_writeAR(priv->dev, pos); + atmel_write16(priv->dev, DR, data); +} + +static const struct iw_handler_def atmel_handler_def; + +static void tx_done_irq(struct atmel_private *priv) +{ + int i; + + for (i = 0; + atmel_rmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, priv->tx_desc_head)) == TX_DONE && + i < priv->host_info.tx_desc_count; + i++) { + u8 status = atmel_rmem8(priv, atmel_tx(priv, TX_DESC_STATUS_OFFSET, priv->tx_desc_head)); + u16 msdu_size = atmel_rmem16(priv, atmel_tx(priv, TX_DESC_SIZE_OFFSET, priv->tx_desc_head)); + u8 type = atmel_rmem8(priv, atmel_tx(priv, TX_DESC_PACKET_TYPE_OFFSET, priv->tx_desc_head)); + + atmel_wmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, priv->tx_desc_head), 0); + + priv->tx_free_mem += msdu_size; + priv->tx_desc_free++; + + if (priv->tx_buff_head + msdu_size > (priv->host_info.tx_buff_pos + priv->host_info.tx_buff_size)) + priv->tx_buff_head = 0; + else + priv->tx_buff_head += msdu_size; + + if (priv->tx_desc_head < (priv->host_info.tx_desc_count - 1)) + priv->tx_desc_head++ ; + else + priv->tx_desc_head = 0; + + if (type == TX_PACKET_TYPE_DATA) { + if (status == TX_STATUS_SUCCESS) + priv->dev->stats.tx_packets++; + else + priv->dev->stats.tx_errors++; + netif_wake_queue(priv->dev); + } + } +} + +static u16 find_tx_buff(struct atmel_private *priv, u16 len) +{ + u16 bottom_free = priv->host_info.tx_buff_size - priv->tx_buff_tail; + + if (priv->tx_desc_free == 3 || priv->tx_free_mem < len) + return 0; + + if (bottom_free >= len) + return priv->host_info.tx_buff_pos + priv->tx_buff_tail; + + if (priv->tx_free_mem - bottom_free >= len) { + priv->tx_buff_tail = 0; + return priv->host_info.tx_buff_pos; + } + + return 0; +} + +static void tx_update_descriptor(struct atmel_private *priv, int is_bcast, + u16 len, u16 buff, u8 type) +{ + atmel_wmem16(priv, atmel_tx(priv, TX_DESC_POS_OFFSET, priv->tx_desc_tail), buff); + atmel_wmem16(priv, atmel_tx(priv, TX_DESC_SIZE_OFFSET, priv->tx_desc_tail), len); + if (!priv->use_wpa) + atmel_wmem16(priv, atmel_tx(priv, TX_DESC_HOST_LENGTH_OFFSET, priv->tx_desc_tail), len); + atmel_wmem8(priv, atmel_tx(priv, TX_DESC_PACKET_TYPE_OFFSET, priv->tx_desc_tail), type); + atmel_wmem8(priv, atmel_tx(priv, TX_DESC_RATE_OFFSET, priv->tx_desc_tail), priv->tx_rate); + atmel_wmem8(priv, atmel_tx(priv, TX_DESC_RETRY_OFFSET, priv->tx_desc_tail), 0); + if (priv->use_wpa) { + int cipher_type, cipher_length; + if (is_bcast) { + cipher_type = priv->group_cipher_suite; + if (cipher_type == CIPHER_SUITE_WEP_64 || + cipher_type == CIPHER_SUITE_WEP_128) + cipher_length = 8; + else if (cipher_type == CIPHER_SUITE_TKIP) + cipher_length = 12; + else if (priv->pairwise_cipher_suite == CIPHER_SUITE_WEP_64 || + priv->pairwise_cipher_suite == CIPHER_SUITE_WEP_128) { + cipher_type = priv->pairwise_cipher_suite; + cipher_length = 8; + } else { + cipher_type = CIPHER_SUITE_NONE; + cipher_length = 0; + } + } else { + cipher_type = priv->pairwise_cipher_suite; + if (cipher_type == CIPHER_SUITE_WEP_64 || + cipher_type == CIPHER_SUITE_WEP_128) + cipher_length = 8; + else if (cipher_type == CIPHER_SUITE_TKIP) + cipher_length = 12; + else if (priv->group_cipher_suite == CIPHER_SUITE_WEP_64 || + priv->group_cipher_suite == CIPHER_SUITE_WEP_128) { + cipher_type = priv->group_cipher_suite; + cipher_length = 8; + } else { + cipher_type = CIPHER_SUITE_NONE; + cipher_length = 0; + } + } + + atmel_wmem8(priv, atmel_tx(priv, TX_DESC_CIPHER_TYPE_OFFSET, priv->tx_desc_tail), + cipher_type); + atmel_wmem8(priv, atmel_tx(priv, TX_DESC_CIPHER_LENGTH_OFFSET, priv->tx_desc_tail), + cipher_length); + } + atmel_wmem32(priv, atmel_tx(priv, TX_DESC_NEXT_OFFSET, priv->tx_desc_tail), 0x80000000L); + atmel_wmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, priv->tx_desc_tail), TX_FIRM_OWN); + if (priv->tx_desc_previous != priv->tx_desc_tail) + atmel_wmem32(priv, atmel_tx(priv, TX_DESC_NEXT_OFFSET, priv->tx_desc_previous), 0); + priv->tx_desc_previous = priv->tx_desc_tail; + if (priv->tx_desc_tail < (priv->host_info.tx_desc_count - 1)) + priv->tx_desc_tail++; + else + priv->tx_desc_tail = 0; + priv->tx_desc_free--; + priv->tx_free_mem -= len; +} + +static netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev) +{ + static const u8 SNAP_RFC1024[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; + struct atmel_private *priv = netdev_priv(dev); + struct ieee80211_hdr header; + unsigned long flags; + u16 buff, frame_ctl, len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN; + + if (priv->card && priv->present_callback && + !(*priv->present_callback)(priv->card)) { + dev->stats.tx_errors++; + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + + if (priv->station_state != STATION_STATE_READY) { + dev->stats.tx_errors++; + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + + /* first ensure the timer func cannot run */ + spin_lock_bh(&priv->timerlock); + /* then stop the hardware ISR */ + spin_lock_irqsave(&priv->irqlock, flags); + /* nb doing the above in the opposite order will deadlock */ + + /* The Wireless Header is 30 bytes. In the Ethernet packet we "cut" the + 12 first bytes (containing DA/SA) and put them in the appropriate + fields of the Wireless Header. Thus the packet length is then the + initial + 18 (+30-12) */ + + if (!(buff = find_tx_buff(priv, len + 18))) { + dev->stats.tx_dropped++; + spin_unlock_irqrestore(&priv->irqlock, flags); + spin_unlock_bh(&priv->timerlock); + netif_stop_queue(dev); + return NETDEV_TX_BUSY; + } + + frame_ctl = IEEE80211_FTYPE_DATA; + header.duration_id = 0; + header.seq_ctrl = 0; + if (priv->wep_is_on) + frame_ctl |= IEEE80211_FCTL_PROTECTED; + if (priv->operating_mode == IW_MODE_ADHOC) { + skb_copy_from_linear_data(skb, &header.addr1, ETH_ALEN); + memcpy(&header.addr2, dev->dev_addr, ETH_ALEN); + memcpy(&header.addr3, priv->BSSID, ETH_ALEN); + } else { + frame_ctl |= IEEE80211_FCTL_TODS; + memcpy(&header.addr1, priv->CurrentBSSID, ETH_ALEN); + memcpy(&header.addr2, dev->dev_addr, ETH_ALEN); + skb_copy_from_linear_data(skb, &header.addr3, ETH_ALEN); + } + + if (priv->use_wpa) + memcpy(&header.addr4, SNAP_RFC1024, ETH_ALEN); + + header.frame_control = cpu_to_le16(frame_ctl); + /* Copy the wireless header into the card */ + atmel_copy_to_card(dev, buff, (unsigned char *)&header, DATA_FRAME_WS_HEADER_SIZE); + /* Copy the packet sans its 802.3 header addresses which have been replaced */ + atmel_copy_to_card(dev, buff + DATA_FRAME_WS_HEADER_SIZE, skb->data + 12, len - 12); + priv->tx_buff_tail += len - 12 + DATA_FRAME_WS_HEADER_SIZE; + + /* low bit of first byte of destination tells us if broadcast */ + tx_update_descriptor(priv, *(skb->data) & 0x01, len + 18, buff, TX_PACKET_TYPE_DATA); + dev->stats.tx_bytes += len; + + spin_unlock_irqrestore(&priv->irqlock, flags); + spin_unlock_bh(&priv->timerlock); + dev_kfree_skb(skb); + + return NETDEV_TX_OK; +} + +static void atmel_transmit_management_frame(struct atmel_private *priv, + struct ieee80211_hdr *header, + u8 *body, int body_len) +{ + u16 buff; + int len = MGMT_FRAME_BODY_OFFSET + body_len; + + if (!(buff = find_tx_buff(priv, len))) + return; + + atmel_copy_to_card(priv->dev, buff, (u8 *)header, MGMT_FRAME_BODY_OFFSET); + atmel_copy_to_card(priv->dev, buff + MGMT_FRAME_BODY_OFFSET, body, body_len); + priv->tx_buff_tail += len; + tx_update_descriptor(priv, header->addr1[0] & 0x01, len, buff, TX_PACKET_TYPE_MGMT); +} + +static void fast_rx_path(struct atmel_private *priv, + struct ieee80211_hdr *header, + u16 msdu_size, u16 rx_packet_loc, u32 crc) +{ + /* fast path: unfragmented packet copy directly into skbuf */ + u8 mac4[6]; + struct sk_buff *skb; + unsigned char *skbp; + + /* get the final, mac 4 header field, this tells us encapsulation */ + atmel_copy_to_host(priv->dev, mac4, rx_packet_loc + 24, 6); + msdu_size -= 6; + + if (priv->do_rx_crc) { + crc = crc32_le(crc, mac4, 6); + msdu_size -= 4; + } + + if (!(skb = dev_alloc_skb(msdu_size + 14))) { + priv->dev->stats.rx_dropped++; + return; + } + + skb_reserve(skb, 2); + skbp = skb_put(skb, msdu_size + 12); + atmel_copy_to_host(priv->dev, skbp + 12, rx_packet_loc + 30, msdu_size); + + if (priv->do_rx_crc) { + u32 netcrc; + crc = crc32_le(crc, skbp + 12, msdu_size); + atmel_copy_to_host(priv->dev, (void *)&netcrc, rx_packet_loc + 30 + msdu_size, 4); + if ((crc ^ 0xffffffff) != netcrc) { + priv->dev->stats.rx_crc_errors++; + dev_kfree_skb(skb); + return; + } + } + + memcpy(skbp, header->addr1, ETH_ALEN); /* destination address */ + if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS) + memcpy(&skbp[ETH_ALEN], header->addr3, ETH_ALEN); + else + memcpy(&skbp[ETH_ALEN], header->addr2, ETH_ALEN); /* source address */ + + skb->protocol = eth_type_trans(skb, priv->dev); + skb->ip_summed = CHECKSUM_NONE; + netif_rx(skb); + priv->dev->stats.rx_bytes += 12 + msdu_size; + priv->dev->stats.rx_packets++; +} + +/* Test to see if the packet in card memory at packet_loc has a valid CRC + It doesn't matter that this is slow: it is only used to proble the first few + packets. */ +static int probe_crc(struct atmel_private *priv, u16 packet_loc, u16 msdu_size) +{ + int i = msdu_size - 4; + u32 netcrc, crc = 0xffffffff; + + if (msdu_size < 4) + return 0; + + atmel_copy_to_host(priv->dev, (void *)&netcrc, packet_loc + i, 4); + + atmel_writeAR(priv->dev, packet_loc); + while (i--) { + u8 octet = atmel_read8(priv->dev, DR); + crc = crc32_le(crc, &octet, 1); + } + + return (crc ^ 0xffffffff) == netcrc; +} + +static void frag_rx_path(struct atmel_private *priv, + struct ieee80211_hdr *header, + u16 msdu_size, u16 rx_packet_loc, u32 crc, u16 seq_no, + u8 frag_no, int more_frags) +{ + u8 mac4[ETH_ALEN]; + u8 source[ETH_ALEN]; + struct sk_buff *skb; + + if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS) + memcpy(source, header->addr3, ETH_ALEN); + else + memcpy(source, header->addr2, ETH_ALEN); + + rx_packet_loc += 24; /* skip header */ + + if (priv->do_rx_crc) + msdu_size -= 4; + + if (frag_no == 0) { /* first fragment */ + atmel_copy_to_host(priv->dev, mac4, rx_packet_loc, ETH_ALEN); + msdu_size -= ETH_ALEN; + rx_packet_loc += ETH_ALEN; + + if (priv->do_rx_crc) + crc = crc32_le(crc, mac4, 6); + + priv->frag_seq = seq_no; + priv->frag_no = 1; + priv->frag_len = msdu_size; + memcpy(priv->frag_source, source, ETH_ALEN); + memcpy(&priv->rx_buf[ETH_ALEN], source, ETH_ALEN); + memcpy(priv->rx_buf, header->addr1, ETH_ALEN); + + atmel_copy_to_host(priv->dev, &priv->rx_buf[12], rx_packet_loc, msdu_size); + + if (priv->do_rx_crc) { + u32 netcrc; + crc = crc32_le(crc, &priv->rx_buf[12], msdu_size); + atmel_copy_to_host(priv->dev, (void *)&netcrc, rx_packet_loc + msdu_size, 4); + if ((crc ^ 0xffffffff) != netcrc) { + priv->dev->stats.rx_crc_errors++; + eth_broadcast_addr(priv->frag_source); + } + } + + } else if (priv->frag_no == frag_no && + priv->frag_seq == seq_no && + memcmp(priv->frag_source, source, ETH_ALEN) == 0) { + + atmel_copy_to_host(priv->dev, &priv->rx_buf[12 + priv->frag_len], + rx_packet_loc, msdu_size); + if (priv->do_rx_crc) { + u32 netcrc; + crc = crc32_le(crc, + &priv->rx_buf[12 + priv->frag_len], + msdu_size); + atmel_copy_to_host(priv->dev, (void *)&netcrc, rx_packet_loc + msdu_size, 4); + if ((crc ^ 0xffffffff) != netcrc) { + priv->dev->stats.rx_crc_errors++; + eth_broadcast_addr(priv->frag_source); + more_frags = 1; /* don't send broken assembly */ + } + } + + priv->frag_len += msdu_size; + priv->frag_no++; + + if (!more_frags) { /* last one */ + eth_broadcast_addr(priv->frag_source); + if (!(skb = dev_alloc_skb(priv->frag_len + 14))) { + priv->dev->stats.rx_dropped++; + } else { + skb_reserve(skb, 2); + memcpy(skb_put(skb, priv->frag_len + 12), + priv->rx_buf, + priv->frag_len + 12); + skb->protocol = eth_type_trans(skb, priv->dev); + skb->ip_summed = CHECKSUM_NONE; + netif_rx(skb); + priv->dev->stats.rx_bytes += priv->frag_len + 12; + priv->dev->stats.rx_packets++; + } + } + } else + priv->wstats.discard.fragment++; +} + +static void rx_done_irq(struct atmel_private *priv) +{ + int i; + struct ieee80211_hdr header; + + for (i = 0; + atmel_rmem8(priv, atmel_rx(priv, RX_DESC_FLAGS_OFFSET, priv->rx_desc_head)) == RX_DESC_FLAG_VALID && + i < priv->host_info.rx_desc_count; + i++) { + + u16 msdu_size, rx_packet_loc, frame_ctl, seq_control; + u8 status = atmel_rmem8(priv, atmel_rx(priv, RX_DESC_STATUS_OFFSET, priv->rx_desc_head)); + u32 crc = 0xffffffff; + + if (status != RX_STATUS_SUCCESS) { + if (status == 0xc1) /* determined by experiment */ + priv->wstats.discard.nwid++; + else + priv->dev->stats.rx_errors++; + goto next; + } + + msdu_size = atmel_rmem16(priv, atmel_rx(priv, RX_DESC_MSDU_SIZE_OFFSET, priv->rx_desc_head)); + rx_packet_loc = atmel_rmem16(priv, atmel_rx(priv, RX_DESC_MSDU_POS_OFFSET, priv->rx_desc_head)); + + if (msdu_size < 30) { + priv->dev->stats.rx_errors++; + goto next; + } + + /* Get header as far as end of seq_ctrl */ + atmel_copy_to_host(priv->dev, (char *)&header, rx_packet_loc, 24); + frame_ctl = le16_to_cpu(header.frame_control); + seq_control = le16_to_cpu(header.seq_ctrl); + + /* probe for CRC use here if needed once five packets have + arrived with the same crc status, we assume we know what's + happening and stop probing */ + if (priv->probe_crc) { + if (!priv->wep_is_on || !(frame_ctl & IEEE80211_FCTL_PROTECTED)) { + priv->do_rx_crc = probe_crc(priv, rx_packet_loc, msdu_size); + } else { + priv->do_rx_crc = probe_crc(priv, rx_packet_loc + 24, msdu_size - 24); + } + if (priv->do_rx_crc) { + if (priv->crc_ok_cnt++ > 5) + priv->probe_crc = 0; + } else { + if (priv->crc_ko_cnt++ > 5) + priv->probe_crc = 0; + } + } + + /* don't CRC header when WEP in use */ + if (priv->do_rx_crc && (!priv->wep_is_on || !(frame_ctl & IEEE80211_FCTL_PROTECTED))) { + crc = crc32_le(0xffffffff, (unsigned char *)&header, 24); + } + msdu_size -= 24; /* header */ + + if ((frame_ctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) { + int more_fragments = frame_ctl & IEEE80211_FCTL_MOREFRAGS; + u8 packet_fragment_no = seq_control & IEEE80211_SCTL_FRAG; + u16 packet_sequence_no = (seq_control & IEEE80211_SCTL_SEQ) >> 4; + + if (!more_fragments && packet_fragment_no == 0) { + fast_rx_path(priv, &header, msdu_size, rx_packet_loc, crc); + } else { + frag_rx_path(priv, &header, msdu_size, rx_packet_loc, crc, + packet_sequence_no, packet_fragment_no, more_fragments); + } + } + + if ((frame_ctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) { + /* copy rest of packet into buffer */ + atmel_copy_to_host(priv->dev, (unsigned char *)&priv->rx_buf, rx_packet_loc + 24, msdu_size); + + /* we use the same buffer for frag reassembly and control packets */ + eth_broadcast_addr(priv->frag_source); + + if (priv->do_rx_crc) { + /* last 4 octets is crc */ + msdu_size -= 4; + crc = crc32_le(crc, (unsigned char *)&priv->rx_buf, msdu_size); + if ((crc ^ 0xffffffff) != (*((u32 *)&priv->rx_buf[msdu_size]))) { + priv->dev->stats.rx_crc_errors++; + goto next; + } + } + + atmel_management_frame(priv, &header, msdu_size, + atmel_rmem8(priv, atmel_rx(priv, RX_DESC_RSSI_OFFSET, priv->rx_desc_head))); + } + +next: + /* release descriptor */ + atmel_wmem8(priv, atmel_rx(priv, RX_DESC_FLAGS_OFFSET, priv->rx_desc_head), RX_DESC_FLAG_CONSUMED); + + if (priv->rx_desc_head < (priv->host_info.rx_desc_count - 1)) + priv->rx_desc_head++; + else + priv->rx_desc_head = 0; + } +} + +static irqreturn_t service_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *) dev_id; + struct atmel_private *priv = netdev_priv(dev); + u8 isr; + int i = -1; + static const u8 irq_order[] = { + ISR_OUT_OF_RANGE, + ISR_RxCOMPLETE, + ISR_TxCOMPLETE, + ISR_RxFRAMELOST, + ISR_FATAL_ERROR, + ISR_COMMAND_COMPLETE, + ISR_IBSS_MERGE, + ISR_GENERIC_IRQ + }; + + if (priv->card && priv->present_callback && + !(*priv->present_callback)(priv->card)) + return IRQ_HANDLED; + + /* In this state upper-level code assumes it can mess with + the card unhampered by interrupts which may change register state. + Note that even though the card shouldn't generate interrupts + the inturrupt line may be shared. This allows card setup + to go on without disabling interrupts for a long time. */ + if (priv->station_state == STATION_STATE_DOWN) + return IRQ_NONE; + + atmel_clear_gcr(dev, GCR_ENINT); /* disable interrupts */ + + while (1) { + if (!atmel_lock_mac(priv)) { + /* failed to contact card */ + printk(KERN_ALERT "%s: failed to contact MAC.\n", dev->name); + return IRQ_HANDLED; + } + + isr = atmel_rmem8(priv, atmel_hi(priv, IFACE_INT_STATUS_OFFSET)); + atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 0); + + if (!isr) { + atmel_set_gcr(dev, GCR_ENINT); /* enable interrupts */ + return i == -1 ? IRQ_NONE : IRQ_HANDLED; + } + + atmel_set_gcr(dev, GCR_ACKINT); /* acknowledge interrupt */ + + for (i = 0; i < ARRAY_SIZE(irq_order); i++) + if (isr & irq_order[i]) + break; + + if (!atmel_lock_mac(priv)) { + /* failed to contact card */ + printk(KERN_ALERT "%s: failed to contact MAC.\n", dev->name); + return IRQ_HANDLED; + } + + isr = atmel_rmem8(priv, atmel_hi(priv, IFACE_INT_STATUS_OFFSET)); + isr ^= irq_order[i]; + atmel_wmem8(priv, atmel_hi(priv, IFACE_INT_STATUS_OFFSET), isr); + atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 0); + + switch (irq_order[i]) { + + case ISR_OUT_OF_RANGE: + if (priv->operating_mode == IW_MODE_INFRA && + priv->station_state == STATION_STATE_READY) { + priv->station_is_associated = 0; + atmel_scan(priv, 1); + } + break; + + case ISR_RxFRAMELOST: + priv->wstats.discard.misc++; + /* fall through */ + case ISR_RxCOMPLETE: + rx_done_irq(priv); + break; + + case ISR_TxCOMPLETE: + tx_done_irq(priv); + break; + + case ISR_FATAL_ERROR: + printk(KERN_ALERT "%s: *** FATAL error interrupt ***\n", dev->name); + atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); + break; + + case ISR_COMMAND_COMPLETE: + atmel_command_irq(priv); + break; + + case ISR_IBSS_MERGE: + atmel_get_mib(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_CUR_BSSID_POS, + priv->CurrentBSSID, 6); + /* The WPA stuff cares about the current AP address */ + if (priv->use_wpa) + build_wpa_mib(priv); + break; + case ISR_GENERIC_IRQ: + printk(KERN_INFO "%s: Generic_irq received.\n", dev->name); + break; + } + } +} + +static struct iw_statistics *atmel_get_wireless_stats(struct net_device *dev) +{ + struct atmel_private *priv = netdev_priv(dev); + + /* update the link quality here in case we are seeing no beacons + at all to drive the process */ + atmel_smooth_qual(priv); + + priv->wstats.status = priv->station_state; + + if (priv->operating_mode == IW_MODE_INFRA) { + if (priv->station_state != STATION_STATE_READY) { + priv->wstats.qual.qual = 0; + priv->wstats.qual.level = 0; + priv->wstats.qual.updated = (IW_QUAL_QUAL_INVALID + | IW_QUAL_LEVEL_INVALID); + } + priv->wstats.qual.noise = 0; + priv->wstats.qual.updated |= IW_QUAL_NOISE_INVALID; + } else { + /* Quality levels cannot be determined in ad-hoc mode, + because we can 'hear' more that one remote station. */ + priv->wstats.qual.qual = 0; + priv->wstats.qual.level = 0; + priv->wstats.qual.noise = 0; + priv->wstats.qual.updated = IW_QUAL_QUAL_INVALID + | IW_QUAL_LEVEL_INVALID + | IW_QUAL_NOISE_INVALID; + priv->wstats.miss.beacon = 0; + } + + return &priv->wstats; +} + +static int atmel_change_mtu(struct net_device *dev, int new_mtu) +{ + if ((new_mtu < 68) || (new_mtu > 2312)) + return -EINVAL; + dev->mtu = new_mtu; + return 0; +} + +static int atmel_set_mac_address(struct net_device *dev, void *p) +{ + struct sockaddr *addr = p; + + memcpy (dev->dev_addr, addr->sa_data, dev->addr_len); + return atmel_open(dev); +} + +EXPORT_SYMBOL(atmel_open); + +int atmel_open(struct net_device *dev) +{ + struct atmel_private *priv = netdev_priv(dev); + int i, channel, err; + + /* any scheduled timer is no longer needed and might screw things up.. */ + del_timer_sync(&priv->management_timer); + + /* Interrupts will not touch the card once in this state... */ + priv->station_state = STATION_STATE_DOWN; + + if (priv->new_SSID_size) { + memcpy(priv->SSID, priv->new_SSID, priv->new_SSID_size); + priv->SSID_size = priv->new_SSID_size; + priv->new_SSID_size = 0; + } + priv->BSS_list_entries = 0; + + priv->AuthenticationRequestRetryCnt = 0; + priv->AssociationRequestRetryCnt = 0; + priv->ReAssociationRequestRetryCnt = 0; + priv->CurrentAuthentTransactionSeqNum = 0x0001; + priv->ExpectedAuthentTransactionSeqNum = 0x0002; + + priv->site_survey_state = SITE_SURVEY_IDLE; + priv->station_is_associated = 0; + + err = reset_atmel_card(dev); + if (err) + return err; + + if (priv->config_reg_domain) { + priv->reg_domain = priv->config_reg_domain; + atmel_set_mib8(priv, Phy_Mib_Type, PHY_MIB_REG_DOMAIN_POS, priv->reg_domain); + } else { + priv->reg_domain = atmel_get_mib8(priv, Phy_Mib_Type, PHY_MIB_REG_DOMAIN_POS); + for (i = 0; i < ARRAY_SIZE(channel_table); i++) + if (priv->reg_domain == channel_table[i].reg_domain) + break; + if (i == ARRAY_SIZE(channel_table)) { + priv->reg_domain = REG_DOMAIN_MKK1; + printk(KERN_ALERT "%s: failed to get regulatory domain: assuming MKK1.\n", dev->name); + } + } + + if ((channel = atmel_validate_channel(priv, priv->channel))) + priv->channel = channel; + + /* this moves station_state on.... */ + atmel_scan(priv, 1); + + atmel_set_gcr(priv->dev, GCR_ENINT); /* enable interrupts */ + return 0; +} + +static int atmel_close(struct net_device *dev) +{ + struct atmel_private *priv = netdev_priv(dev); + + /* Send event to userspace that we are disassociating */ + if (priv->station_state == STATION_STATE_READY) { + union iwreq_data wrqu; + + wrqu.data.length = 0; + wrqu.data.flags = 0; + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + eth_zero_addr(wrqu.ap_addr.sa_data); + wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL); + } + + atmel_enter_state(priv, STATION_STATE_DOWN); + + if (priv->bus_type == BUS_TYPE_PCCARD) + atmel_write16(dev, GCR, 0x0060); + atmel_write16(dev, GCR, 0x0040); + return 0; +} + +static int atmel_validate_channel(struct atmel_private *priv, int channel) +{ + /* check that channel is OK, if so return zero, + else return suitable default channel */ + int i; + + for (i = 0; i < ARRAY_SIZE(channel_table); i++) + if (priv->reg_domain == channel_table[i].reg_domain) { + if (channel >= channel_table[i].min && + channel <= channel_table[i].max) + return 0; + else + return channel_table[i].min; + } + return 0; +} + +static int atmel_proc_show(struct seq_file *m, void *v) +{ + struct atmel_private *priv = m->private; + int i; + char *s, *r, *c; + + seq_printf(m, "Driver version:\t\t%d.%d\n", DRIVER_MAJOR, DRIVER_MINOR); + + if (priv->station_state != STATION_STATE_DOWN) { + seq_printf(m, + "Firmware version:\t%d.%d build %d\n" + "Firmware location:\t", + priv->host_info.major_version, + priv->host_info.minor_version, + priv->host_info.build_version); + + if (priv->card_type != CARD_TYPE_EEPROM) + seq_puts(m, "on card\n"); + else if (priv->firmware) + seq_printf(m, "%s loaded by host\n", priv->firmware_id); + else + seq_printf(m, "%s loaded by hotplug\n", priv->firmware_id); + + switch (priv->card_type) { + case CARD_TYPE_PARALLEL_FLASH: + c = "Parallel flash"; + break; + case CARD_TYPE_SPI_FLASH: + c = "SPI flash\n"; + break; + case CARD_TYPE_EEPROM: + c = "EEPROM"; + break; + default: + c = ""; + } + + r = ""; + for (i = 0; i < ARRAY_SIZE(channel_table); i++) + if (priv->reg_domain == channel_table[i].reg_domain) + r = channel_table[i].name; + + seq_printf(m, "MAC memory type:\t%s\n", c); + seq_printf(m, "Regulatory domain:\t%s\n", r); + seq_printf(m, "Host CRC checking:\t%s\n", + priv->do_rx_crc ? "On" : "Off"); + seq_printf(m, "WPA-capable firmware:\t%s\n", + priv->use_wpa ? "Yes" : "No"); + } + + switch (priv->station_state) { + case STATION_STATE_SCANNING: + s = "Scanning"; + break; + case STATION_STATE_JOINNING: + s = "Joining"; + break; + case STATION_STATE_AUTHENTICATING: + s = "Authenticating"; + break; + case STATION_STATE_ASSOCIATING: + s = "Associating"; + break; + case STATION_STATE_READY: + s = "Ready"; + break; + case STATION_STATE_REASSOCIATING: + s = "Reassociating"; + break; + case STATION_STATE_MGMT_ERROR: + s = "Management error"; + break; + case STATION_STATE_DOWN: + s = "Down"; + break; + default: + s = ""; + } + + seq_printf(m, "Current state:\t\t%s\n", s); + return 0; +} + +static int atmel_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, atmel_proc_show, PDE_DATA(inode)); +} + +static const struct file_operations atmel_proc_fops = { + .open = atmel_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static const struct net_device_ops atmel_netdev_ops = { + .ndo_open = atmel_open, + .ndo_stop = atmel_close, + .ndo_change_mtu = atmel_change_mtu, + .ndo_set_mac_address = atmel_set_mac_address, + .ndo_start_xmit = start_tx, + .ndo_do_ioctl = atmel_ioctl, + .ndo_validate_addr = eth_validate_addr, +}; + +struct net_device *init_atmel_card(unsigned short irq, unsigned long port, + const AtmelFWType fw_type, + struct device *sys_dev, + int (*card_present)(void *), void *card) +{ + struct net_device *dev; + struct atmel_private *priv; + int rc; + + /* Create the network device object. */ + dev = alloc_etherdev(sizeof(*priv)); + if (!dev) + return NULL; + + if (dev_alloc_name(dev, dev->name) < 0) { + printk(KERN_ERR "atmel: Couldn't get name!\n"); + goto err_out_free; + } + + priv = netdev_priv(dev); + priv->dev = dev; + priv->sys_dev = sys_dev; + priv->present_callback = card_present; + priv->card = card; + priv->firmware = NULL; + priv->firmware_id[0] = '\0'; + priv->firmware_type = fw_type; + if (firmware) /* module parameter */ + strcpy(priv->firmware_id, firmware); + priv->bus_type = card_present ? BUS_TYPE_PCCARD : BUS_TYPE_PCI; + priv->station_state = STATION_STATE_DOWN; + priv->do_rx_crc = 0; + /* For PCMCIA cards, some chips need CRC, some don't + so we have to probe. */ + if (priv->bus_type == BUS_TYPE_PCCARD) { + priv->probe_crc = 1; + priv->crc_ok_cnt = priv->crc_ko_cnt = 0; + } else + priv->probe_crc = 0; + priv->last_qual = jiffies; + priv->last_beacon_timestamp = 0; + memset(priv->frag_source, 0xff, sizeof(priv->frag_source)); + eth_zero_addr(priv->BSSID); + priv->CurrentBSSID[0] = 0xFF; /* Initialize to something invalid.... */ + priv->station_was_associated = 0; + + priv->last_survey = jiffies; + priv->preamble = LONG_PREAMBLE; + priv->operating_mode = IW_MODE_INFRA; + priv->connect_to_any_BSS = 0; + priv->config_reg_domain = 0; + priv->reg_domain = 0; + priv->tx_rate = 3; + priv->auto_tx_rate = 1; + priv->channel = 4; + priv->power_mode = 0; + priv->SSID[0] = '\0'; + priv->SSID_size = 0; + priv->new_SSID_size = 0; + priv->frag_threshold = 2346; + priv->rts_threshold = 2347; + priv->short_retry = 7; + priv->long_retry = 4; + + priv->wep_is_on = 0; + priv->default_key = 0; + priv->encryption_level = 0; + priv->exclude_unencrypted = 0; + priv->group_cipher_suite = priv->pairwise_cipher_suite = CIPHER_SUITE_NONE; + priv->use_wpa = 0; + memset(priv->wep_keys, 0, sizeof(priv->wep_keys)); + memset(priv->wep_key_len, 0, sizeof(priv->wep_key_len)); + + priv->default_beacon_period = priv->beacon_period = 100; + priv->listen_interval = 1; + + init_timer(&priv->management_timer); + spin_lock_init(&priv->irqlock); + spin_lock_init(&priv->timerlock); + priv->management_timer.function = atmel_management_timer; + priv->management_timer.data = (unsigned long) dev; + + dev->netdev_ops = &atmel_netdev_ops; + dev->wireless_handlers = &atmel_handler_def; + dev->irq = irq; + dev->base_addr = port; + + SET_NETDEV_DEV(dev, sys_dev); + + if ((rc = request_irq(dev->irq, service_interrupt, IRQF_SHARED, dev->name, dev))) { + printk(KERN_ERR "%s: register interrupt %d failed, rc %d\n", dev->name, irq, rc); + goto err_out_free; + } + + if (!request_region(dev->base_addr, 32, + priv->bus_type == BUS_TYPE_PCCARD ? "atmel_cs" : "atmel_pci")) { + goto err_out_irq; + } + + if (register_netdev(dev)) + goto err_out_res; + + if (!probe_atmel_card(dev)) { + unregister_netdev(dev); + goto err_out_res; + } + + netif_carrier_off(dev); + + if (!proc_create_data("driver/atmel", 0, NULL, &atmel_proc_fops, priv)) + printk(KERN_WARNING "atmel: unable to create /proc entry.\n"); + + printk(KERN_INFO "%s: Atmel at76c50x. Version %d.%d. MAC %pM\n", + dev->name, DRIVER_MAJOR, DRIVER_MINOR, dev->dev_addr); + + return dev; + +err_out_res: + release_region(dev->base_addr, 32); +err_out_irq: + free_irq(dev->irq, dev); +err_out_free: + free_netdev(dev); + return NULL; +} + +EXPORT_SYMBOL(init_atmel_card); + +void stop_atmel_card(struct net_device *dev) +{ + struct atmel_private *priv = netdev_priv(dev); + + /* put a brick on it... */ + if (priv->bus_type == BUS_TYPE_PCCARD) + atmel_write16(dev, GCR, 0x0060); + atmel_write16(dev, GCR, 0x0040); + + del_timer_sync(&priv->management_timer); + unregister_netdev(dev); + remove_proc_entry("driver/atmel", NULL); + free_irq(dev->irq, dev); + kfree(priv->firmware); + release_region(dev->base_addr, 32); + free_netdev(dev); +} + +EXPORT_SYMBOL(stop_atmel_card); + +static int atmel_set_essid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + + /* Check if we asked for `any' */ + if (dwrq->flags == 0) { + priv->connect_to_any_BSS = 1; + } else { + int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + + priv->connect_to_any_BSS = 0; + + /* Check the size of the string */ + if (dwrq->length > MAX_SSID_LENGTH) + return -E2BIG; + if (index != 0) + return -EINVAL; + + memcpy(priv->new_SSID, extra, dwrq->length); + priv->new_SSID_size = dwrq->length; + } + + return -EINPROGRESS; +} + +static int atmel_get_essid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + + /* Get the current SSID */ + if (priv->new_SSID_size != 0) { + memcpy(extra, priv->new_SSID, priv->new_SSID_size); + dwrq->length = priv->new_SSID_size; + } else { + memcpy(extra, priv->SSID, priv->SSID_size); + dwrq->length = priv->SSID_size; + } + + dwrq->flags = !priv->connect_to_any_BSS; /* active */ + + return 0; +} + +static int atmel_get_wap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *awrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + memcpy(awrq->sa_data, priv->CurrentBSSID, ETH_ALEN); + awrq->sa_family = ARPHRD_ETHER; + + return 0; +} + +static int atmel_set_encode(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + + /* Basic checking: do we have a key to set ? + * Note : with the new API, it's impossible to get a NULL pointer. + * Therefore, we need to check a key size == 0 instead. + * New version of iwconfig properly set the IW_ENCODE_NOKEY flag + * when no key is present (only change flags), but older versions + * don't do it. - Jean II */ + if (dwrq->length > 0) { + int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + int current_index = priv->default_key; + /* Check the size of the key */ + if (dwrq->length > 13) { + return -EINVAL; + } + /* Check the index (none -> use current) */ + if (index < 0 || index >= 4) + index = current_index; + else + priv->default_key = index; + /* Set the length */ + if (dwrq->length > 5) + priv->wep_key_len[index] = 13; + else + if (dwrq->length > 0) + priv->wep_key_len[index] = 5; + else + /* Disable the key */ + priv->wep_key_len[index] = 0; + /* Check if the key is not marked as invalid */ + if (!(dwrq->flags & IW_ENCODE_NOKEY)) { + /* Cleanup */ + memset(priv->wep_keys[index], 0, 13); + /* Copy the key in the driver */ + memcpy(priv->wep_keys[index], extra, dwrq->length); + } + /* WE specify that if a valid key is set, encryption + * should be enabled (user may turn it off later) + * This is also how "iwconfig ethX key on" works */ + if (index == current_index && + priv->wep_key_len[index] > 0) { + priv->wep_is_on = 1; + priv->exclude_unencrypted = 1; + if (priv->wep_key_len[index] > 5) { + priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_128; + priv->encryption_level = 2; + } else { + priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_64; + priv->encryption_level = 1; + } + } + } else { + /* Do we want to just set the transmit key index ? */ + int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + if (index >= 0 && index < 4) { + priv->default_key = index; + } else + /* Don't complain if only change the mode */ + if (!(dwrq->flags & IW_ENCODE_MODE)) + return -EINVAL; + } + /* Read the flags */ + if (dwrq->flags & IW_ENCODE_DISABLED) { + priv->wep_is_on = 0; + priv->encryption_level = 0; + priv->pairwise_cipher_suite = CIPHER_SUITE_NONE; + } else { + priv->wep_is_on = 1; + if (priv->wep_key_len[priv->default_key] > 5) { + priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_128; + priv->encryption_level = 2; + } else { + priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_64; + priv->encryption_level = 1; + } + } + if (dwrq->flags & IW_ENCODE_RESTRICTED) + priv->exclude_unencrypted = 1; + if (dwrq->flags & IW_ENCODE_OPEN) + priv->exclude_unencrypted = 0; + + return -EINPROGRESS; /* Call commit handler */ +} + +static int atmel_get_encode(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + + if (!priv->wep_is_on) + dwrq->flags = IW_ENCODE_DISABLED; + else { + if (priv->exclude_unencrypted) + dwrq->flags = IW_ENCODE_RESTRICTED; + else + dwrq->flags = IW_ENCODE_OPEN; + } + /* Which key do we want ? -1 -> tx index */ + if (index < 0 || index >= 4) + index = priv->default_key; + dwrq->flags |= index + 1; + /* Copy the key to the user buffer */ + dwrq->length = priv->wep_key_len[index]; + if (dwrq->length > 16) { + dwrq->length = 0; + } else { + memset(extra, 0, 16); + memcpy(extra, priv->wep_keys[index], dwrq->length); + } + + return 0; +} + +static int atmel_set_encodeext(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + struct iw_point *encoding = &wrqu->encoding; + struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; + int idx, key_len, alg = ext->alg, set_key = 1; + + /* Determine and validate the key index */ + idx = encoding->flags & IW_ENCODE_INDEX; + if (idx) { + if (idx < 1 || idx > 4) + return -EINVAL; + idx--; + } else + idx = priv->default_key; + + if (encoding->flags & IW_ENCODE_DISABLED) + alg = IW_ENCODE_ALG_NONE; + + if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { + priv->default_key = idx; + set_key = ext->key_len > 0 ? 1 : 0; + } + + if (set_key) { + /* Set the requested key first */ + switch (alg) { + case IW_ENCODE_ALG_NONE: + priv->wep_is_on = 0; + priv->encryption_level = 0; + priv->pairwise_cipher_suite = CIPHER_SUITE_NONE; + break; + case IW_ENCODE_ALG_WEP: + if (ext->key_len > 5) { + priv->wep_key_len[idx] = 13; + priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_128; + priv->encryption_level = 2; + } else if (ext->key_len > 0) { + priv->wep_key_len[idx] = 5; + priv->pairwise_cipher_suite = CIPHER_SUITE_WEP_64; + priv->encryption_level = 1; + } else { + return -EINVAL; + } + priv->wep_is_on = 1; + memset(priv->wep_keys[idx], 0, 13); + key_len = min ((int)ext->key_len, priv->wep_key_len[idx]); + memcpy(priv->wep_keys[idx], ext->key, key_len); + break; + default: + return -EINVAL; + } + } + + return -EINPROGRESS; +} + +static int atmel_get_encodeext(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + struct iw_point *encoding = &wrqu->encoding; + struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; + int idx, max_key_len; + + max_key_len = encoding->length - sizeof(*ext); + if (max_key_len < 0) + return -EINVAL; + + idx = encoding->flags & IW_ENCODE_INDEX; + if (idx) { + if (idx < 1 || idx > 4) + return -EINVAL; + idx--; + } else + idx = priv->default_key; + + encoding->flags = idx + 1; + memset(ext, 0, sizeof(*ext)); + + if (!priv->wep_is_on) { + ext->alg = IW_ENCODE_ALG_NONE; + ext->key_len = 0; + encoding->flags |= IW_ENCODE_DISABLED; + } else { + if (priv->encryption_level > 0) + ext->alg = IW_ENCODE_ALG_WEP; + else + return -EINVAL; + + ext->key_len = priv->wep_key_len[idx]; + memcpy(ext->key, priv->wep_keys[idx], ext->key_len); + encoding->flags |= IW_ENCODE_ENABLED; + } + + return 0; +} + +static int atmel_set_auth(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + struct iw_param *param = &wrqu->param; + + switch (param->flags & IW_AUTH_INDEX) { + case IW_AUTH_WPA_VERSION: + case IW_AUTH_CIPHER_PAIRWISE: + case IW_AUTH_CIPHER_GROUP: + case IW_AUTH_KEY_MGMT: + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + case IW_AUTH_PRIVACY_INVOKED: + /* + * atmel does not use these parameters + */ + break; + + case IW_AUTH_DROP_UNENCRYPTED: + priv->exclude_unencrypted = param->value ? 1 : 0; + break; + + case IW_AUTH_80211_AUTH_ALG: { + if (param->value & IW_AUTH_ALG_SHARED_KEY) { + priv->exclude_unencrypted = 1; + } else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM) { + priv->exclude_unencrypted = 0; + } else + return -EINVAL; + break; + } + + case IW_AUTH_WPA_ENABLED: + /* Silently accept disable of WPA */ + if (param->value > 0) + return -EOPNOTSUPP; + break; + + default: + return -EOPNOTSUPP; + } + return -EINPROGRESS; +} + +static int atmel_get_auth(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + struct iw_param *param = &wrqu->param; + + switch (param->flags & IW_AUTH_INDEX) { + case IW_AUTH_DROP_UNENCRYPTED: + param->value = priv->exclude_unencrypted; + break; + + case IW_AUTH_80211_AUTH_ALG: + if (priv->exclude_unencrypted == 1) + param->value = IW_AUTH_ALG_SHARED_KEY; + else + param->value = IW_AUTH_ALG_OPEN_SYSTEM; + break; + + case IW_AUTH_WPA_ENABLED: + param->value = 0; + break; + + default: + return -EOPNOTSUPP; + } + return 0; +} + + +static int atmel_get_name(struct net_device *dev, + struct iw_request_info *info, + char *cwrq, + char *extra) +{ + strcpy(cwrq, "IEEE 802.11-DS"); + return 0; +} + +static int atmel_set_rate(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + + if (vwrq->fixed == 0) { + priv->tx_rate = 3; + priv->auto_tx_rate = 1; + } else { + priv->auto_tx_rate = 0; + + /* Which type of value ? */ + if ((vwrq->value < 4) && (vwrq->value >= 0)) { + /* Setting by rate index */ + priv->tx_rate = vwrq->value; + } else { + /* Setting by frequency value */ + switch (vwrq->value) { + case 1000000: + priv->tx_rate = 0; + break; + case 2000000: + priv->tx_rate = 1; + break; + case 5500000: + priv->tx_rate = 2; + break; + case 11000000: + priv->tx_rate = 3; + break; + default: + return -EINVAL; + } + } + } + + return -EINPROGRESS; +} + +static int atmel_set_mode(struct net_device *dev, + struct iw_request_info *info, + __u32 *uwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + + if (*uwrq != IW_MODE_ADHOC && *uwrq != IW_MODE_INFRA) + return -EINVAL; + + priv->operating_mode = *uwrq; + return -EINPROGRESS; +} + +static int atmel_get_mode(struct net_device *dev, + struct iw_request_info *info, + __u32 *uwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + + *uwrq = priv->operating_mode; + return 0; +} + +static int atmel_get_rate(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + + if (priv->auto_tx_rate) { + vwrq->fixed = 0; + vwrq->value = 11000000; + } else { + vwrq->fixed = 1; + switch (priv->tx_rate) { + case 0: + vwrq->value = 1000000; + break; + case 1: + vwrq->value = 2000000; + break; + case 2: + vwrq->value = 5500000; + break; + case 3: + vwrq->value = 11000000; + break; + } + } + return 0; +} + +static int atmel_set_power(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + priv->power_mode = vwrq->disabled ? 0 : 1; + return -EINPROGRESS; +} + +static int atmel_get_power(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + vwrq->disabled = priv->power_mode ? 0 : 1; + vwrq->flags = IW_POWER_ON; + return 0; +} + +static int atmel_set_retry(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + + if (!vwrq->disabled && (vwrq->flags & IW_RETRY_LIMIT)) { + if (vwrq->flags & IW_RETRY_LONG) + priv->long_retry = vwrq->value; + else if (vwrq->flags & IW_RETRY_SHORT) + priv->short_retry = vwrq->value; + else { + /* No modifier : set both */ + priv->long_retry = vwrq->value; + priv->short_retry = vwrq->value; + } + return -EINPROGRESS; + } + + return -EINVAL; +} + +static int atmel_get_retry(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + + vwrq->disabled = 0; /* Can't be disabled */ + + /* Note : by default, display the short retry number */ + if (vwrq->flags & IW_RETRY_LONG) { + vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG; + vwrq->value = priv->long_retry; + } else { + vwrq->flags = IW_RETRY_LIMIT; + vwrq->value = priv->short_retry; + if (priv->long_retry != priv->short_retry) + vwrq->flags |= IW_RETRY_SHORT; + } + + return 0; +} + +static int atmel_set_rts(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + int rthr = vwrq->value; + + if (vwrq->disabled) + rthr = 2347; + if ((rthr < 0) || (rthr > 2347)) { + return -EINVAL; + } + priv->rts_threshold = rthr; + + return -EINPROGRESS; /* Call commit handler */ +} + +static int atmel_get_rts(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + + vwrq->value = priv->rts_threshold; + vwrq->disabled = (vwrq->value >= 2347); + vwrq->fixed = 1; + + return 0; +} + +static int atmel_set_frag(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + int fthr = vwrq->value; + + if (vwrq->disabled) + fthr = 2346; + if ((fthr < 256) || (fthr > 2346)) { + return -EINVAL; + } + fthr &= ~0x1; /* Get an even value - is it really needed ??? */ + priv->frag_threshold = fthr; + + return -EINPROGRESS; /* Call commit handler */ +} + +static int atmel_get_frag(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + + vwrq->value = priv->frag_threshold; + vwrq->disabled = (vwrq->value >= 2346); + vwrq->fixed = 1; + + return 0; +} + +static int atmel_set_freq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *fwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + int rc = -EINPROGRESS; /* Call commit handler */ + + /* If setting by frequency, convert to a channel */ + if (fwrq->e == 1) { + int f = fwrq->m / 100000; + + /* Hack to fall through... */ + fwrq->e = 0; + fwrq->m = ieee80211_frequency_to_channel(f); + } + /* Setting by channel number */ + if ((fwrq->m > 1000) || (fwrq->e > 0)) + rc = -EOPNOTSUPP; + else { + int channel = fwrq->m; + if (atmel_validate_channel(priv, channel) == 0) { + priv->channel = channel; + } else { + rc = -EINVAL; + } + } + return rc; +} + +static int atmel_get_freq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *fwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + + fwrq->m = priv->channel; + fwrq->e = 0; + return 0; +} + +static int atmel_set_scan(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + unsigned long flags; + + /* Note : you may have realised that, as this is a SET operation, + * this is privileged and therefore a normal user can't + * perform scanning. + * This is not an error, while the device perform scanning, + * traffic doesn't flow, so it's a perfect DoS... + * Jean II */ + + if (priv->station_state == STATION_STATE_DOWN) + return -EAGAIN; + + /* Timeout old surveys. */ + if (time_after(jiffies, priv->last_survey + 20 * HZ)) + priv->site_survey_state = SITE_SURVEY_IDLE; + priv->last_survey = jiffies; + + /* Initiate a scan command */ + if (priv->site_survey_state == SITE_SURVEY_IN_PROGRESS) + return -EBUSY; + + del_timer_sync(&priv->management_timer); + spin_lock_irqsave(&priv->irqlock, flags); + + priv->site_survey_state = SITE_SURVEY_IN_PROGRESS; + priv->fast_scan = 0; + atmel_scan(priv, 0); + spin_unlock_irqrestore(&priv->irqlock, flags); + + return 0; +} + +static int atmel_get_scan(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + int i; + char *current_ev = extra; + struct iw_event iwe; + + if (priv->site_survey_state != SITE_SURVEY_COMPLETED) + return -EAGAIN; + + for (i = 0; i < priv->BSS_list_entries; i++) { + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(iwe.u.ap_addr.sa_data, priv->BSSinfo[i].BSSID, ETH_ALEN); + current_ev = iwe_stream_add_event(info, current_ev, + extra + IW_SCAN_MAX_DATA, + &iwe, IW_EV_ADDR_LEN); + + iwe.u.data.length = priv->BSSinfo[i].SSIDsize; + if (iwe.u.data.length > 32) + iwe.u.data.length = 32; + iwe.cmd = SIOCGIWESSID; + iwe.u.data.flags = 1; + current_ev = iwe_stream_add_point(info, current_ev, + extra + IW_SCAN_MAX_DATA, + &iwe, priv->BSSinfo[i].SSID); + + iwe.cmd = SIOCGIWMODE; + iwe.u.mode = priv->BSSinfo[i].BSStype; + current_ev = iwe_stream_add_event(info, current_ev, + extra + IW_SCAN_MAX_DATA, + &iwe, IW_EV_UINT_LEN); + + iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = priv->BSSinfo[i].channel; + iwe.u.freq.e = 0; + current_ev = iwe_stream_add_event(info, current_ev, + extra + IW_SCAN_MAX_DATA, + &iwe, IW_EV_FREQ_LEN); + + /* Add quality statistics */ + iwe.cmd = IWEVQUAL; + iwe.u.qual.level = priv->BSSinfo[i].RSSI; + iwe.u.qual.qual = iwe.u.qual.level; + /* iwe.u.qual.noise = SOMETHING */ + current_ev = iwe_stream_add_event(info, current_ev, + extra + IW_SCAN_MAX_DATA, + &iwe, IW_EV_QUAL_LEN); + + + iwe.cmd = SIOCGIWENCODE; + if (priv->BSSinfo[i].UsingWEP) + 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(info, current_ev, + extra + IW_SCAN_MAX_DATA, + &iwe, NULL); + } + + /* Length of data */ + dwrq->length = (current_ev - extra); + dwrq->flags = 0; + + return 0; +} + +static int atmel_get_range(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + struct iw_range *range = (struct iw_range *) extra; + int k, i, j; + + dwrq->length = sizeof(struct iw_range); + memset(range, 0, sizeof(struct iw_range)); + range->min_nwid = 0x0000; + range->max_nwid = 0x0000; + range->num_channels = 0; + for (j = 0; j < ARRAY_SIZE(channel_table); j++) + if (priv->reg_domain == channel_table[j].reg_domain) { + range->num_channels = channel_table[j].max - channel_table[j].min + 1; + break; + } + if (range->num_channels != 0) { + for (k = 0, i = channel_table[j].min; i <= channel_table[j].max; i++) { + range->freq[k].i = i; /* List index */ + + /* Values in MHz -> * 10^5 * 10 */ + range->freq[k].m = 100000 * + ieee80211_channel_to_frequency(i, IEEE80211_BAND_2GHZ); + range->freq[k++].e = 1; + } + range->num_frequency = k; + } + + range->max_qual.qual = 100; + range->max_qual.level = 100; + range->max_qual.noise = 0; + range->max_qual.updated = IW_QUAL_NOISE_INVALID; + + range->avg_qual.qual = 50; + range->avg_qual.level = 50; + range->avg_qual.noise = 0; + range->avg_qual.updated = IW_QUAL_NOISE_INVALID; + + range->sensitivity = 0; + + range->bitrate[0] = 1000000; + range->bitrate[1] = 2000000; + range->bitrate[2] = 5500000; + range->bitrate[3] = 11000000; + range->num_bitrates = 4; + + range->min_rts = 0; + range->max_rts = 2347; + range->min_frag = 256; + range->max_frag = 2346; + + range->encoding_size[0] = 5; + range->encoding_size[1] = 13; + range->num_encoding_sizes = 2; + range->max_encoding_tokens = 4; + + range->pmp_flags = IW_POWER_ON; + range->pmt_flags = IW_POWER_ON; + range->pm_capa = 0; + + range->we_version_source = WIRELESS_EXT; + range->we_version_compiled = WIRELESS_EXT; + range->retry_capa = IW_RETRY_LIMIT ; + range->retry_flags = IW_RETRY_LIMIT; + range->r_time_flags = 0; + range->min_retry = 1; + range->max_retry = 65535; + + return 0; +} + +static int atmel_set_wap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *awrq, + char *extra) +{ + struct atmel_private *priv = netdev_priv(dev); + int i; + static const u8 any[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + static const u8 off[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + unsigned long flags; + + if (awrq->sa_family != ARPHRD_ETHER) + return -EINVAL; + + if (!memcmp(any, awrq->sa_data, 6) || + !memcmp(off, awrq->sa_data, 6)) { + del_timer_sync(&priv->management_timer); + spin_lock_irqsave(&priv->irqlock, flags); + atmel_scan(priv, 1); + spin_unlock_irqrestore(&priv->irqlock, flags); + return 0; + } + + for (i = 0; i < priv->BSS_list_entries; i++) { + if (memcmp(priv->BSSinfo[i].BSSID, awrq->sa_data, 6) == 0) { + if (!priv->wep_is_on && priv->BSSinfo[i].UsingWEP) { + return -EINVAL; + } else if (priv->wep_is_on && !priv->BSSinfo[i].UsingWEP) { + return -EINVAL; + } else { + del_timer_sync(&priv->management_timer); + spin_lock_irqsave(&priv->irqlock, flags); + atmel_join_bss(priv, i); + spin_unlock_irqrestore(&priv->irqlock, flags); + return 0; + } + } + } + + return -EINVAL; +} + +static int atmel_config_commit(struct net_device *dev, + struct iw_request_info *info, /* NULL */ + void *zwrq, /* NULL */ + char *extra) /* NULL */ +{ + return atmel_open(dev); +} + +static const iw_handler atmel_handler[] = +{ + (iw_handler) atmel_config_commit, /* SIOCSIWCOMMIT */ + (iw_handler) atmel_get_name, /* SIOCGIWNAME */ + (iw_handler) NULL, /* SIOCSIWNWID */ + (iw_handler) NULL, /* SIOCGIWNWID */ + (iw_handler) atmel_set_freq, /* SIOCSIWFREQ */ + (iw_handler) atmel_get_freq, /* SIOCGIWFREQ */ + (iw_handler) atmel_set_mode, /* SIOCSIWMODE */ + (iw_handler) atmel_get_mode, /* SIOCGIWMODE */ + (iw_handler) NULL, /* SIOCSIWSENS */ + (iw_handler) NULL, /* SIOCGIWSENS */ + (iw_handler) NULL, /* SIOCSIWRANGE */ + (iw_handler) atmel_get_range, /* SIOCGIWRANGE */ + (iw_handler) NULL, /* SIOCSIWPRIV */ + (iw_handler) NULL, /* SIOCGIWPRIV */ + (iw_handler) NULL, /* SIOCSIWSTATS */ + (iw_handler) NULL, /* SIOCGIWSTATS */ + (iw_handler) NULL, /* SIOCSIWSPY */ + (iw_handler) NULL, /* SIOCGIWSPY */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) atmel_set_wap, /* SIOCSIWAP */ + (iw_handler) atmel_get_wap, /* SIOCGIWAP */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* SIOCGIWAPLIST */ + (iw_handler) atmel_set_scan, /* SIOCSIWSCAN */ + (iw_handler) atmel_get_scan, /* SIOCGIWSCAN */ + (iw_handler) atmel_set_essid, /* SIOCSIWESSID */ + (iw_handler) atmel_get_essid, /* SIOCGIWESSID */ + (iw_handler) NULL, /* SIOCSIWNICKN */ + (iw_handler) NULL, /* SIOCGIWNICKN */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) atmel_set_rate, /* SIOCSIWRATE */ + (iw_handler) atmel_get_rate, /* SIOCGIWRATE */ + (iw_handler) atmel_set_rts, /* SIOCSIWRTS */ + (iw_handler) atmel_get_rts, /* SIOCGIWRTS */ + (iw_handler) atmel_set_frag, /* SIOCSIWFRAG */ + (iw_handler) atmel_get_frag, /* SIOCGIWFRAG */ + (iw_handler) NULL, /* SIOCSIWTXPOW */ + (iw_handler) NULL, /* SIOCGIWTXPOW */ + (iw_handler) atmel_set_retry, /* SIOCSIWRETRY */ + (iw_handler) atmel_get_retry, /* SIOCGIWRETRY */ + (iw_handler) atmel_set_encode, /* SIOCSIWENCODE */ + (iw_handler) atmel_get_encode, /* SIOCGIWENCODE */ + (iw_handler) atmel_set_power, /* SIOCSIWPOWER */ + (iw_handler) atmel_get_power, /* SIOCGIWPOWER */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* SIOCSIWGENIE */ + (iw_handler) NULL, /* SIOCGIWGENIE */ + (iw_handler) atmel_set_auth, /* SIOCSIWAUTH */ + (iw_handler) atmel_get_auth, /* SIOCGIWAUTH */ + (iw_handler) atmel_set_encodeext, /* SIOCSIWENCODEEXT */ + (iw_handler) atmel_get_encodeext, /* SIOCGIWENCODEEXT */ + (iw_handler) NULL, /* SIOCSIWPMKSA */ +}; + +static const iw_handler atmel_private_handler[] = +{ + NULL, /* SIOCIWFIRSTPRIV */ +}; + +struct atmel_priv_ioctl { + char id[32]; + unsigned char __user *data; + unsigned short len; +}; + +#define ATMELFWL SIOCIWFIRSTPRIV +#define ATMELIDIFC ATMELFWL + 1 +#define ATMELRD ATMELFWL + 2 +#define ATMELMAGIC 0x51807 +#define REGDOMAINSZ 20 + +static const struct iw_priv_args atmel_private_args[] = { + { + .cmd = ATMELFWL, + .set_args = IW_PRIV_TYPE_BYTE + | IW_PRIV_SIZE_FIXED + | sizeof(struct atmel_priv_ioctl), + .get_args = IW_PRIV_TYPE_NONE, + .name = "atmelfwl" + }, { + .cmd = ATMELIDIFC, + .set_args = IW_PRIV_TYPE_NONE, + .get_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + .name = "atmelidifc" + }, { + .cmd = ATMELRD, + .set_args = IW_PRIV_TYPE_CHAR | REGDOMAINSZ, + .get_args = IW_PRIV_TYPE_NONE, + .name = "regdomain" + }, +}; + +static const struct iw_handler_def atmel_handler_def = { + .num_standard = ARRAY_SIZE(atmel_handler), + .num_private = ARRAY_SIZE(atmel_private_handler), + .num_private_args = ARRAY_SIZE(atmel_private_args), + .standard = (iw_handler *) atmel_handler, + .private = (iw_handler *) atmel_private_handler, + .private_args = (struct iw_priv_args *) atmel_private_args, + .get_wireless_stats = atmel_get_wireless_stats +}; + +static int atmel_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + int i, rc = 0; + struct atmel_private *priv = netdev_priv(dev); + struct atmel_priv_ioctl com; + struct iwreq *wrq = (struct iwreq *) rq; + unsigned char *new_firmware; + char domain[REGDOMAINSZ + 1]; + + switch (cmd) { + case ATMELIDIFC: + wrq->u.param.value = ATMELMAGIC; + break; + + case ATMELFWL: + if (copy_from_user(&com, rq->ifr_data, sizeof(com))) { + rc = -EFAULT; + break; + } + + if (!capable(CAP_NET_ADMIN)) { + rc = -EPERM; + break; + } + + if (!(new_firmware = kmalloc(com.len, GFP_KERNEL))) { + rc = -ENOMEM; + break; + } + + if (copy_from_user(new_firmware, com.data, com.len)) { + kfree(new_firmware); + rc = -EFAULT; + break; + } + + kfree(priv->firmware); + + priv->firmware = new_firmware; + priv->firmware_length = com.len; + strncpy(priv->firmware_id, com.id, 31); + priv->firmware_id[31] = '\0'; + break; + + case ATMELRD: + if (copy_from_user(domain, rq->ifr_data, REGDOMAINSZ)) { + rc = -EFAULT; + break; + } + + if (!capable(CAP_NET_ADMIN)) { + rc = -EPERM; + break; + } + + domain[REGDOMAINSZ] = 0; + rc = -EINVAL; + for (i = 0; i < ARRAY_SIZE(channel_table); i++) { + if (!strcasecmp(channel_table[i].name, domain)) { + priv->config_reg_domain = channel_table[i].reg_domain; + rc = 0; + } + } + + if (rc == 0 && priv->station_state != STATION_STATE_DOWN) + rc = atmel_open(dev); + break; + + default: + rc = -EOPNOTSUPP; + } + + return rc; +} + +struct auth_body { + __le16 alg; + __le16 trans_seq; + __le16 status; + u8 el_id; + u8 chall_text_len; + u8 chall_text[253]; +}; + +static void atmel_enter_state(struct atmel_private *priv, int new_state) +{ + int old_state = priv->station_state; + + if (new_state == old_state) + return; + + priv->station_state = new_state; + + if (new_state == STATION_STATE_READY) { + netif_start_queue(priv->dev); + netif_carrier_on(priv->dev); + } + + if (old_state == STATION_STATE_READY) { + netif_carrier_off(priv->dev); + if (netif_running(priv->dev)) + netif_stop_queue(priv->dev); + priv->last_beacon_timestamp = 0; + } +} + +static void atmel_scan(struct atmel_private *priv, int specific_ssid) +{ + struct { + u8 BSSID[ETH_ALEN]; + u8 SSID[MAX_SSID_LENGTH]; + u8 scan_type; + u8 channel; + __le16 BSS_type; + __le16 min_channel_time; + __le16 max_channel_time; + u8 options; + u8 SSID_size; + } cmd; + + eth_broadcast_addr(cmd.BSSID); + + if (priv->fast_scan) { + cmd.SSID_size = priv->SSID_size; + memcpy(cmd.SSID, priv->SSID, priv->SSID_size); + cmd.min_channel_time = cpu_to_le16(10); + cmd.max_channel_time = cpu_to_le16(50); + } else { + priv->BSS_list_entries = 0; + cmd.SSID_size = 0; + cmd.min_channel_time = cpu_to_le16(10); + cmd.max_channel_time = cpu_to_le16(120); + } + + cmd.options = 0; + + if (!specific_ssid) + cmd.options |= SCAN_OPTIONS_SITE_SURVEY; + + cmd.channel = (priv->channel & 0x7f); + cmd.scan_type = SCAN_TYPE_ACTIVE; + cmd.BSS_type = cpu_to_le16(priv->operating_mode == IW_MODE_ADHOC ? + BSS_TYPE_AD_HOC : BSS_TYPE_INFRASTRUCTURE); + + atmel_send_command(priv, CMD_Scan, &cmd, sizeof(cmd)); + + /* This must come after all hardware access to avoid being messed up + by stuff happening in interrupt context after we leave STATE_DOWN */ + atmel_enter_state(priv, STATION_STATE_SCANNING); +} + +static void join(struct atmel_private *priv, int type) +{ + struct { + u8 BSSID[6]; + u8 SSID[MAX_SSID_LENGTH]; + u8 BSS_type; /* this is a short in a scan command - weird */ + u8 channel; + __le16 timeout; + u8 SSID_size; + u8 reserved; + } cmd; + + cmd.SSID_size = priv->SSID_size; + memcpy(cmd.SSID, priv->SSID, priv->SSID_size); + memcpy(cmd.BSSID, priv->CurrentBSSID, ETH_ALEN); + cmd.channel = (priv->channel & 0x7f); + cmd.BSS_type = type; + cmd.timeout = cpu_to_le16(2000); + + atmel_send_command(priv, CMD_Join, &cmd, sizeof(cmd)); +} + +static void start(struct atmel_private *priv, int type) +{ + struct { + u8 BSSID[6]; + u8 SSID[MAX_SSID_LENGTH]; + u8 BSS_type; + u8 channel; + u8 SSID_size; + u8 reserved[3]; + } cmd; + + cmd.SSID_size = priv->SSID_size; + memcpy(cmd.SSID, priv->SSID, priv->SSID_size); + memcpy(cmd.BSSID, priv->BSSID, ETH_ALEN); + cmd.BSS_type = type; + cmd.channel = (priv->channel & 0x7f); + + atmel_send_command(priv, CMD_Start, &cmd, sizeof(cmd)); +} + +static void handle_beacon_probe(struct atmel_private *priv, u16 capability, + u8 channel) +{ + int rejoin = 0; + int new = capability & WLAN_CAPABILITY_SHORT_PREAMBLE ? + SHORT_PREAMBLE : LONG_PREAMBLE; + + if (priv->preamble != new) { + priv->preamble = new; + rejoin = 1; + atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_PREAMBLE_TYPE, new); + } + + if (priv->channel != channel) { + priv->channel = channel; + rejoin = 1; + atmel_set_mib8(priv, Phy_Mib_Type, PHY_MIB_CHANNEL_POS, channel); + } + + if (rejoin) { + priv->station_is_associated = 0; + atmel_enter_state(priv, STATION_STATE_JOINNING); + + if (priv->operating_mode == IW_MODE_INFRA) + join(priv, BSS_TYPE_INFRASTRUCTURE); + else + join(priv, BSS_TYPE_AD_HOC); + } +} + +static void send_authentication_request(struct atmel_private *priv, u16 system, + u8 *challenge, int challenge_len) +{ + struct ieee80211_hdr header; + struct auth_body auth; + + header.frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH); + header.duration_id = cpu_to_le16(0x8000); + header.seq_ctrl = 0; + memcpy(header.addr1, priv->CurrentBSSID, ETH_ALEN); + memcpy(header.addr2, priv->dev->dev_addr, ETH_ALEN); + memcpy(header.addr3, priv->CurrentBSSID, ETH_ALEN); + + if (priv->wep_is_on && priv->CurrentAuthentTransactionSeqNum != 1) + /* no WEP for authentication frames with TrSeqNo 1 */ + header.frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); + + auth.alg = cpu_to_le16(system); + + auth.status = 0; + auth.trans_seq = cpu_to_le16(priv->CurrentAuthentTransactionSeqNum); + priv->ExpectedAuthentTransactionSeqNum = priv->CurrentAuthentTransactionSeqNum+1; + priv->CurrentAuthentTransactionSeqNum += 2; + + if (challenge_len != 0) { + auth.el_id = 16; /* challenge_text */ + auth.chall_text_len = challenge_len; + memcpy(auth.chall_text, challenge, challenge_len); + atmel_transmit_management_frame(priv, &header, (u8 *)&auth, 8 + challenge_len); + } else { + atmel_transmit_management_frame(priv, &header, (u8 *)&auth, 6); + } +} + +static void send_association_request(struct atmel_private *priv, int is_reassoc) +{ + u8 *ssid_el_p; + int bodysize; + struct ieee80211_hdr header; + struct ass_req_format { + __le16 capability; + __le16 listen_interval; + u8 ap[ETH_ALEN]; /* nothing after here directly accessible */ + u8 ssid_el_id; + u8 ssid_len; + u8 ssid[MAX_SSID_LENGTH]; + u8 sup_rates_el_id; + u8 sup_rates_len; + u8 rates[4]; + } body; + + header.frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + (is_reassoc ? IEEE80211_STYPE_REASSOC_REQ : IEEE80211_STYPE_ASSOC_REQ)); + header.duration_id = cpu_to_le16(0x8000); + header.seq_ctrl = 0; + + memcpy(header.addr1, priv->CurrentBSSID, ETH_ALEN); + memcpy(header.addr2, priv->dev->dev_addr, ETH_ALEN); + memcpy(header.addr3, priv->CurrentBSSID, ETH_ALEN); + + body.capability = cpu_to_le16(WLAN_CAPABILITY_ESS); + if (priv->wep_is_on) + body.capability |= cpu_to_le16(WLAN_CAPABILITY_PRIVACY); + if (priv->preamble == SHORT_PREAMBLE) + body.capability |= cpu_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE); + + body.listen_interval = cpu_to_le16(priv->listen_interval * priv->beacon_period); + + /* current AP address - only in reassoc frame */ + if (is_reassoc) { + memcpy(body.ap, priv->CurrentBSSID, ETH_ALEN); + ssid_el_p = &body.ssid_el_id; + bodysize = 18 + priv->SSID_size; + } else { + ssid_el_p = &body.ap[0]; + bodysize = 12 + priv->SSID_size; + } + + ssid_el_p[0] = WLAN_EID_SSID; + ssid_el_p[1] = priv->SSID_size; + memcpy(ssid_el_p + 2, priv->SSID, priv->SSID_size); + ssid_el_p[2 + priv->SSID_size] = WLAN_EID_SUPP_RATES; + ssid_el_p[3 + priv->SSID_size] = 4; /* len of supported rates */ + memcpy(ssid_el_p + 4 + priv->SSID_size, atmel_basic_rates, 4); + + atmel_transmit_management_frame(priv, &header, (void *)&body, bodysize); +} + +static int is_frame_from_current_bss(struct atmel_private *priv, + struct ieee80211_hdr *header) +{ + if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS) + return memcmp(header->addr3, priv->CurrentBSSID, 6) == 0; + else + return memcmp(header->addr2, priv->CurrentBSSID, 6) == 0; +} + +static int retrieve_bss(struct atmel_private *priv) +{ + int i; + int max_rssi = -128; + int max_index = -1; + + if (priv->BSS_list_entries == 0) + return -1; + + if (priv->connect_to_any_BSS) { + /* Select a BSS with the max-RSSI but of the same type and of + the same WEP mode and that it is not marked as 'bad' (i.e. + we had previously failed to connect to this BSS with the + settings that we currently use) */ + priv->current_BSS = 0; + for (i = 0; i < priv->BSS_list_entries; i++) { + if (priv->operating_mode == priv->BSSinfo[i].BSStype && + ((!priv->wep_is_on && !priv->BSSinfo[i].UsingWEP) || + (priv->wep_is_on && priv->BSSinfo[i].UsingWEP)) && + !(priv->BSSinfo[i].channel & 0x80)) { + max_rssi = priv->BSSinfo[i].RSSI; + priv->current_BSS = max_index = i; + } + } + return max_index; + } + + for (i = 0; i < priv->BSS_list_entries; i++) { + if (priv->SSID_size == priv->BSSinfo[i].SSIDsize && + memcmp(priv->SSID, priv->BSSinfo[i].SSID, priv->SSID_size) == 0 && + priv->operating_mode == priv->BSSinfo[i].BSStype && + atmel_validate_channel(priv, priv->BSSinfo[i].channel) == 0) { + if (priv->BSSinfo[i].RSSI >= max_rssi) { + max_rssi = priv->BSSinfo[i].RSSI; + max_index = i; + } + } + } + return max_index; +} + +static void store_bss_info(struct atmel_private *priv, + struct ieee80211_hdr *header, u16 capability, + u16 beacon_period, u8 channel, u8 rssi, u8 ssid_len, + u8 *ssid, int is_beacon) +{ + u8 *bss = capability & WLAN_CAPABILITY_ESS ? header->addr2 : header->addr3; + int i, index; + + for (index = -1, i = 0; i < priv->BSS_list_entries; i++) + if (memcmp(bss, priv->BSSinfo[i].BSSID, ETH_ALEN) == 0) + index = i; + + /* If we process a probe and an entry from this BSS exists + we will update the BSS entry with the info from this BSS. + If we process a beacon we will only update RSSI */ + + if (index == -1) { + if (priv->BSS_list_entries == MAX_BSS_ENTRIES) + return; + index = priv->BSS_list_entries++; + memcpy(priv->BSSinfo[index].BSSID, bss, ETH_ALEN); + priv->BSSinfo[index].RSSI = rssi; + } else { + if (rssi > priv->BSSinfo[index].RSSI) + priv->BSSinfo[index].RSSI = rssi; + if (is_beacon) + return; + } + + priv->BSSinfo[index].channel = channel; + priv->BSSinfo[index].beacon_period = beacon_period; + priv->BSSinfo[index].UsingWEP = capability & WLAN_CAPABILITY_PRIVACY; + memcpy(priv->BSSinfo[index].SSID, ssid, ssid_len); + priv->BSSinfo[index].SSIDsize = ssid_len; + + if (capability & WLAN_CAPABILITY_IBSS) + priv->BSSinfo[index].BSStype = IW_MODE_ADHOC; + else if (capability & WLAN_CAPABILITY_ESS) + priv->BSSinfo[index].BSStype = IW_MODE_INFRA; + + priv->BSSinfo[index].preamble = capability & WLAN_CAPABILITY_SHORT_PREAMBLE ? + SHORT_PREAMBLE : LONG_PREAMBLE; +} + +static void authenticate(struct atmel_private *priv, u16 frame_len) +{ + struct auth_body *auth = (struct auth_body *)priv->rx_buf; + u16 status = le16_to_cpu(auth->status); + u16 trans_seq_no = le16_to_cpu(auth->trans_seq); + u16 system = le16_to_cpu(auth->alg); + + if (status == WLAN_STATUS_SUCCESS && !priv->wep_is_on) { + /* no WEP */ + if (priv->station_was_associated) { + atmel_enter_state(priv, STATION_STATE_REASSOCIATING); + send_association_request(priv, 1); + return; + } else { + atmel_enter_state(priv, STATION_STATE_ASSOCIATING); + send_association_request(priv, 0); + return; + } + } + + if (status == WLAN_STATUS_SUCCESS && priv->wep_is_on) { + int should_associate = 0; + /* WEP */ + if (trans_seq_no != priv->ExpectedAuthentTransactionSeqNum) + return; + + if (system == WLAN_AUTH_OPEN) { + if (trans_seq_no == 0x0002) { + should_associate = 1; + } + } else if (system == WLAN_AUTH_SHARED_KEY) { + if (trans_seq_no == 0x0002 && + auth->el_id == WLAN_EID_CHALLENGE) { + send_authentication_request(priv, system, auth->chall_text, auth->chall_text_len); + return; + } else if (trans_seq_no == 0x0004) { + should_associate = 1; + } + } + + if (should_associate) { + if (priv->station_was_associated) { + atmel_enter_state(priv, STATION_STATE_REASSOCIATING); + send_association_request(priv, 1); + return; + } else { + atmel_enter_state(priv, STATION_STATE_ASSOCIATING); + send_association_request(priv, 0); + return; + } + } + } + + if (status == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG) { + /* Flip back and forth between WEP auth modes until the max + * authentication tries has been exceeded. + */ + if (system == WLAN_AUTH_OPEN) { + priv->CurrentAuthentTransactionSeqNum = 0x001; + priv->exclude_unencrypted = 1; + send_authentication_request(priv, WLAN_AUTH_SHARED_KEY, NULL, 0); + return; + } else if (system == WLAN_AUTH_SHARED_KEY + && priv->wep_is_on) { + priv->CurrentAuthentTransactionSeqNum = 0x001; + priv->exclude_unencrypted = 0; + send_authentication_request(priv, WLAN_AUTH_OPEN, NULL, 0); + return; + } else if (priv->connect_to_any_BSS) { + int bss_index; + + priv->BSSinfo[(int)(priv->current_BSS)].channel |= 0x80; + + if ((bss_index = retrieve_bss(priv)) != -1) { + atmel_join_bss(priv, bss_index); + return; + } + } + } + + priv->AuthenticationRequestRetryCnt = 0; + atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); + priv->station_is_associated = 0; +} + +static void associate(struct atmel_private *priv, u16 frame_len, u16 subtype) +{ + struct ass_resp_format { + __le16 capability; + __le16 status; + __le16 ass_id; + u8 el_id; + u8 length; + u8 rates[4]; + } *ass_resp = (struct ass_resp_format *)priv->rx_buf; + + u16 status = le16_to_cpu(ass_resp->status); + u16 ass_id = le16_to_cpu(ass_resp->ass_id); + u16 rates_len = ass_resp->length > 4 ? 4 : ass_resp->length; + + union iwreq_data wrqu; + + if (frame_len < 8 + rates_len) + return; + + if (status == WLAN_STATUS_SUCCESS) { + if (subtype == IEEE80211_STYPE_ASSOC_RESP) + priv->AssociationRequestRetryCnt = 0; + else + priv->ReAssociationRequestRetryCnt = 0; + + atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, + MAC_MGMT_MIB_STATION_ID_POS, ass_id & 0x3fff); + atmel_set_mib(priv, Phy_Mib_Type, + PHY_MIB_RATE_SET_POS, ass_resp->rates, rates_len); + if (priv->power_mode == 0) { + priv->listen_interval = 1; + atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, + MAC_MGMT_MIB_PS_MODE_POS, ACTIVE_MODE); + atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, + MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 1); + } else { + priv->listen_interval = 2; + atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, + MAC_MGMT_MIB_PS_MODE_POS, PS_MODE); + atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, + MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 2); + } + + priv->station_is_associated = 1; + priv->station_was_associated = 1; + atmel_enter_state(priv, STATION_STATE_READY); + + /* Send association event to userspace */ + wrqu.data.length = 0; + wrqu.data.flags = 0; + memcpy(wrqu.ap_addr.sa_data, priv->CurrentBSSID, ETH_ALEN); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL); + + return; + } + + if (subtype == IEEE80211_STYPE_ASSOC_RESP && + status != WLAN_STATUS_ASSOC_DENIED_RATES && + status != WLAN_STATUS_CAPS_UNSUPPORTED && + priv->AssociationRequestRetryCnt < MAX_ASSOCIATION_RETRIES) { + mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); + priv->AssociationRequestRetryCnt++; + send_association_request(priv, 0); + return; + } + + if (subtype == IEEE80211_STYPE_REASSOC_RESP && + status != WLAN_STATUS_ASSOC_DENIED_RATES && + status != WLAN_STATUS_CAPS_UNSUPPORTED && + priv->ReAssociationRequestRetryCnt < MAX_ASSOCIATION_RETRIES) { + mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); + priv->ReAssociationRequestRetryCnt++; + send_association_request(priv, 1); + return; + } + + atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); + priv->station_is_associated = 0; + + if (priv->connect_to_any_BSS) { + int bss_index; + priv->BSSinfo[(int)(priv->current_BSS)].channel |= 0x80; + + if ((bss_index = retrieve_bss(priv)) != -1) + atmel_join_bss(priv, bss_index); + } +} + +static void atmel_join_bss(struct atmel_private *priv, int bss_index) +{ + struct bss_info *bss = &priv->BSSinfo[bss_index]; + + memcpy(priv->CurrentBSSID, bss->BSSID, ETH_ALEN); + memcpy(priv->SSID, bss->SSID, priv->SSID_size = bss->SSIDsize); + + /* The WPA stuff cares about the current AP address */ + if (priv->use_wpa) + build_wpa_mib(priv); + + /* When switching to AdHoc turn OFF Power Save if needed */ + + if (bss->BSStype == IW_MODE_ADHOC && + priv->operating_mode != IW_MODE_ADHOC && + priv->power_mode) { + priv->power_mode = 0; + priv->listen_interval = 1; + atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, + MAC_MGMT_MIB_PS_MODE_POS, ACTIVE_MODE); + atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, + MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 1); + } + + priv->operating_mode = bss->BSStype; + priv->channel = bss->channel & 0x7f; + priv->beacon_period = bss->beacon_period; + + if (priv->preamble != bss->preamble) { + priv->preamble = bss->preamble; + atmel_set_mib8(priv, Local_Mib_Type, + LOCAL_MIB_PREAMBLE_TYPE, bss->preamble); + } + + if (!priv->wep_is_on && bss->UsingWEP) { + atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); + priv->station_is_associated = 0; + return; + } + + if (priv->wep_is_on && !bss->UsingWEP) { + atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); + priv->station_is_associated = 0; + return; + } + + atmel_enter_state(priv, STATION_STATE_JOINNING); + + if (priv->operating_mode == IW_MODE_INFRA) + join(priv, BSS_TYPE_INFRASTRUCTURE); + else + join(priv, BSS_TYPE_AD_HOC); +} + +static void restart_search(struct atmel_private *priv) +{ + int bss_index; + + if (!priv->connect_to_any_BSS) { + atmel_scan(priv, 1); + } else { + priv->BSSinfo[(int)(priv->current_BSS)].channel |= 0x80; + + if ((bss_index = retrieve_bss(priv)) != -1) + atmel_join_bss(priv, bss_index); + else + atmel_scan(priv, 0); + } +} + +static void smooth_rssi(struct atmel_private *priv, u8 rssi) +{ + u8 old = priv->wstats.qual.level; + u8 max_rssi = 42; /* 502-rmfd-revd max by experiment, default for now */ + + switch (priv->firmware_type) { + case ATMEL_FW_TYPE_502E: + max_rssi = 63; /* 502-rmfd-reve max by experiment */ + break; + default: + break; + } + + rssi = rssi * 100 / max_rssi; + if ((rssi + old) % 2) + priv->wstats.qual.level = (rssi + old) / 2 + 1; + else + priv->wstats.qual.level = (rssi + old) / 2; + priv->wstats.qual.updated |= IW_QUAL_LEVEL_UPDATED; + priv->wstats.qual.updated &= ~IW_QUAL_LEVEL_INVALID; +} + +static void atmel_smooth_qual(struct atmel_private *priv) +{ + unsigned long time_diff = (jiffies - priv->last_qual) / HZ; + while (time_diff--) { + priv->last_qual += HZ; + priv->wstats.qual.qual = priv->wstats.qual.qual / 2; + priv->wstats.qual.qual += + priv->beacons_this_sec * priv->beacon_period * (priv->wstats.qual.level + 100) / 4000; + priv->beacons_this_sec = 0; + } + priv->wstats.qual.updated |= IW_QUAL_QUAL_UPDATED; + priv->wstats.qual.updated &= ~IW_QUAL_QUAL_INVALID; +} + +/* deals with incoming management frames. */ +static void atmel_management_frame(struct atmel_private *priv, + struct ieee80211_hdr *header, + u16 frame_len, u8 rssi) +{ + u16 subtype; + + subtype = le16_to_cpu(header->frame_control) & IEEE80211_FCTL_STYPE; + switch (subtype) { + case IEEE80211_STYPE_BEACON: + case IEEE80211_STYPE_PROBE_RESP: + + /* beacon frame has multiple variable-length fields - + never let an engineer loose with a data structure design. */ + { + struct beacon_format { + __le64 timestamp; + __le16 interval; + __le16 capability; + u8 ssid_el_id; + u8 ssid_length; + /* ssid here */ + u8 rates_el_id; + u8 rates_length; + /* rates here */ + u8 ds_el_id; + u8 ds_length; + /* ds here */ + } *beacon = (struct beacon_format *)priv->rx_buf; + + u8 channel, rates_length, ssid_length; + u64 timestamp = le64_to_cpu(beacon->timestamp); + u16 beacon_interval = le16_to_cpu(beacon->interval); + u16 capability = le16_to_cpu(beacon->capability); + u8 *beaconp = priv->rx_buf; + ssid_length = beacon->ssid_length; + /* this blows chunks. */ + if (frame_len < 14 || frame_len < ssid_length + 15) + return; + rates_length = beaconp[beacon->ssid_length + 15]; + if (frame_len < ssid_length + rates_length + 18) + return; + if (ssid_length > MAX_SSID_LENGTH) + return; + channel = beaconp[ssid_length + rates_length + 18]; + + if (priv->station_state == STATION_STATE_READY) { + smooth_rssi(priv, rssi); + if (is_frame_from_current_bss(priv, header)) { + priv->beacons_this_sec++; + atmel_smooth_qual(priv); + if (priv->last_beacon_timestamp) { + /* Note truncate this to 32 bits - kernel can't divide a long long */ + u32 beacon_delay = timestamp - priv->last_beacon_timestamp; + int beacons = beacon_delay / (beacon_interval * 1000); + if (beacons > 1) + priv->wstats.miss.beacon += beacons - 1; + } + priv->last_beacon_timestamp = timestamp; + handle_beacon_probe(priv, capability, channel); + } + } + + if (priv->station_state == STATION_STATE_SCANNING) + store_bss_info(priv, header, capability, + beacon_interval, channel, rssi, + ssid_length, + &beacon->rates_el_id, + subtype == IEEE80211_STYPE_BEACON); + } + break; + + case IEEE80211_STYPE_AUTH: + + if (priv->station_state == STATION_STATE_AUTHENTICATING) + authenticate(priv, frame_len); + + break; + + case IEEE80211_STYPE_ASSOC_RESP: + case IEEE80211_STYPE_REASSOC_RESP: + + if (priv->station_state == STATION_STATE_ASSOCIATING || + priv->station_state == STATION_STATE_REASSOCIATING) + associate(priv, frame_len, subtype); + + break; + + case IEEE80211_STYPE_DISASSOC: + if (priv->station_is_associated && + priv->operating_mode == IW_MODE_INFRA && + is_frame_from_current_bss(priv, header)) { + priv->station_was_associated = 0; + priv->station_is_associated = 0; + + atmel_enter_state(priv, STATION_STATE_JOINNING); + join(priv, BSS_TYPE_INFRASTRUCTURE); + } + + break; + + case IEEE80211_STYPE_DEAUTH: + if (priv->operating_mode == IW_MODE_INFRA && + is_frame_from_current_bss(priv, header)) { + priv->station_was_associated = 0; + + atmel_enter_state(priv, STATION_STATE_JOINNING); + join(priv, BSS_TYPE_INFRASTRUCTURE); + } + + break; + } +} + +/* run when timer expires */ +static void atmel_management_timer(u_long a) +{ + struct net_device *dev = (struct net_device *) a; + struct atmel_private *priv = netdev_priv(dev); + unsigned long flags; + + /* Check if the card has been yanked. */ + if (priv->card && priv->present_callback && + !(*priv->present_callback)(priv->card)) + return; + + spin_lock_irqsave(&priv->irqlock, flags); + + switch (priv->station_state) { + + case STATION_STATE_AUTHENTICATING: + if (priv->AuthenticationRequestRetryCnt >= MAX_AUTHENTICATION_RETRIES) { + atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); + priv->station_is_associated = 0; + priv->AuthenticationRequestRetryCnt = 0; + restart_search(priv); + } else { + int auth = WLAN_AUTH_OPEN; + priv->AuthenticationRequestRetryCnt++; + priv->CurrentAuthentTransactionSeqNum = 0x0001; + mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); + if (priv->wep_is_on && priv->exclude_unencrypted) + auth = WLAN_AUTH_SHARED_KEY; + send_authentication_request(priv, auth, NULL, 0); + } + break; + + case STATION_STATE_ASSOCIATING: + if (priv->AssociationRequestRetryCnt == MAX_ASSOCIATION_RETRIES) { + atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); + priv->station_is_associated = 0; + priv->AssociationRequestRetryCnt = 0; + restart_search(priv); + } else { + priv->AssociationRequestRetryCnt++; + mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); + send_association_request(priv, 0); + } + break; + + case STATION_STATE_REASSOCIATING: + if (priv->ReAssociationRequestRetryCnt == MAX_ASSOCIATION_RETRIES) { + atmel_enter_state(priv, STATION_STATE_MGMT_ERROR); + priv->station_is_associated = 0; + priv->ReAssociationRequestRetryCnt = 0; + restart_search(priv); + } else { + priv->ReAssociationRequestRetryCnt++; + mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); + send_association_request(priv, 1); + } + break; + + default: + break; + } + + spin_unlock_irqrestore(&priv->irqlock, flags); +} + +static void atmel_command_irq(struct atmel_private *priv) +{ + u8 status = atmel_rmem8(priv, atmel_co(priv, CMD_BLOCK_STATUS_OFFSET)); + u8 command = atmel_rmem8(priv, atmel_co(priv, CMD_BLOCK_COMMAND_OFFSET)); + int fast_scan; + union iwreq_data wrqu; + + if (status == CMD_STATUS_IDLE || + status == CMD_STATUS_IN_PROGRESS) + return; + + switch (command) { + case CMD_Start: + if (status == CMD_STATUS_COMPLETE) { + priv->station_was_associated = priv->station_is_associated; + atmel_get_mib(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_CUR_BSSID_POS, + (u8 *)priv->CurrentBSSID, 6); + atmel_enter_state(priv, STATION_STATE_READY); + } + break; + + case CMD_Scan: + fast_scan = priv->fast_scan; + priv->fast_scan = 0; + + if (status != CMD_STATUS_COMPLETE) { + atmel_scan(priv, 1); + } else { + int bss_index = retrieve_bss(priv); + int notify_scan_complete = 1; + if (bss_index != -1) { + atmel_join_bss(priv, bss_index); + } else if (priv->operating_mode == IW_MODE_ADHOC && + priv->SSID_size != 0) { + start(priv, BSS_TYPE_AD_HOC); + } else { + priv->fast_scan = !fast_scan; + atmel_scan(priv, 1); + notify_scan_complete = 0; + } + priv->site_survey_state = SITE_SURVEY_COMPLETED; + if (notify_scan_complete) { + wrqu.data.length = 0; + wrqu.data.flags = 0; + wireless_send_event(priv->dev, SIOCGIWSCAN, &wrqu, NULL); + } + } + break; + + case CMD_SiteSurvey: + priv->fast_scan = 0; + + if (status != CMD_STATUS_COMPLETE) + return; + + priv->site_survey_state = SITE_SURVEY_COMPLETED; + if (priv->station_is_associated) { + atmel_enter_state(priv, STATION_STATE_READY); + wrqu.data.length = 0; + wrqu.data.flags = 0; + wireless_send_event(priv->dev, SIOCGIWSCAN, &wrqu, NULL); + } else { + atmel_scan(priv, 1); + } + break; + + case CMD_Join: + if (status == CMD_STATUS_COMPLETE) { + if (priv->operating_mode == IW_MODE_ADHOC) { + priv->station_was_associated = priv->station_is_associated; + atmel_enter_state(priv, STATION_STATE_READY); + } else { + int auth = WLAN_AUTH_OPEN; + priv->AuthenticationRequestRetryCnt = 0; + atmel_enter_state(priv, STATION_STATE_AUTHENTICATING); + + mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); + priv->CurrentAuthentTransactionSeqNum = 0x0001; + if (priv->wep_is_on && priv->exclude_unencrypted) + auth = WLAN_AUTH_SHARED_KEY; + send_authentication_request(priv, auth, NULL, 0); + } + return; + } + + atmel_scan(priv, 1); + } +} + +static int atmel_wakeup_firmware(struct atmel_private *priv) +{ + struct host_info_struct *iface = &priv->host_info; + u16 mr1, mr3; + int i; + + if (priv->card_type == CARD_TYPE_SPI_FLASH) + atmel_set_gcr(priv->dev, GCR_REMAP); + + /* wake up on-board processor */ + atmel_clear_gcr(priv->dev, 0x0040); + atmel_write16(priv->dev, BSR, BSS_SRAM); + + if (priv->card_type == CARD_TYPE_SPI_FLASH) + mdelay(100); + + /* and wait for it */ + for (i = LOOP_RETRY_LIMIT; i; i--) { + mr1 = atmel_read16(priv->dev, MR1); + mr3 = atmel_read16(priv->dev, MR3); + + if (mr3 & MAC_BOOT_COMPLETE) + break; + if (mr1 & MAC_BOOT_COMPLETE && + priv->bus_type == BUS_TYPE_PCCARD) + break; + } + + if (i == 0) { + printk(KERN_ALERT "%s: MAC failed to boot.\n", priv->dev->name); + return -EIO; + } + + if ((priv->host_info_base = atmel_read16(priv->dev, MR2)) == 0xffff) { + printk(KERN_ALERT "%s: card missing.\n", priv->dev->name); + return -ENODEV; + } + + /* now check for completion of MAC initialization through + the FunCtrl field of the IFACE, poll MR1 to detect completion of + MAC initialization, check completion status, set interrupt mask, + enables interrupts and calls Tx and Rx initialization functions */ + + atmel_wmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET), FUNC_CTRL_INIT_COMPLETE); + + for (i = LOOP_RETRY_LIMIT; i; i--) { + mr1 = atmel_read16(priv->dev, MR1); + mr3 = atmel_read16(priv->dev, MR3); + + if (mr3 & MAC_INIT_COMPLETE) + break; + if (mr1 & MAC_INIT_COMPLETE && + priv->bus_type == BUS_TYPE_PCCARD) + break; + } + + if (i == 0) { + printk(KERN_ALERT "%s: MAC failed to initialise.\n", + priv->dev->name); + return -EIO; + } + + /* Check for MAC_INIT_OK only on the register that the MAC_INIT_OK was set */ + if ((mr3 & MAC_INIT_COMPLETE) && + !(atmel_read16(priv->dev, MR3) & MAC_INIT_OK)) { + printk(KERN_ALERT "%s: MAC failed MR3 self-test.\n", priv->dev->name); + return -EIO; + } + if ((mr1 & MAC_INIT_COMPLETE) && + !(atmel_read16(priv->dev, MR1) & MAC_INIT_OK)) { + printk(KERN_ALERT "%s: MAC failed MR1 self-test.\n", priv->dev->name); + return -EIO; + } + + atmel_copy_to_host(priv->dev, (unsigned char *)iface, + priv->host_info_base, sizeof(*iface)); + + iface->tx_buff_pos = le16_to_cpu(iface->tx_buff_pos); + iface->tx_buff_size = le16_to_cpu(iface->tx_buff_size); + iface->tx_desc_pos = le16_to_cpu(iface->tx_desc_pos); + iface->tx_desc_count = le16_to_cpu(iface->tx_desc_count); + iface->rx_buff_pos = le16_to_cpu(iface->rx_buff_pos); + iface->rx_buff_size = le16_to_cpu(iface->rx_buff_size); + iface->rx_desc_pos = le16_to_cpu(iface->rx_desc_pos); + iface->rx_desc_count = le16_to_cpu(iface->rx_desc_count); + iface->build_version = le16_to_cpu(iface->build_version); + iface->command_pos = le16_to_cpu(iface->command_pos); + iface->major_version = le16_to_cpu(iface->major_version); + iface->minor_version = le16_to_cpu(iface->minor_version); + iface->func_ctrl = le16_to_cpu(iface->func_ctrl); + iface->mac_status = le16_to_cpu(iface->mac_status); + + return 0; +} + +/* determine type of memory and MAC address */ +static int probe_atmel_card(struct net_device *dev) +{ + int rc = 0; + struct atmel_private *priv = netdev_priv(dev); + + /* reset pccard */ + if (priv->bus_type == BUS_TYPE_PCCARD) + atmel_write16(dev, GCR, 0x0060); + + atmel_write16(dev, GCR, 0x0040); + mdelay(500); + + if (atmel_read16(dev, MR2) == 0) { + /* No stored firmware so load a small stub which just + tells us the MAC address */ + int i; + priv->card_type = CARD_TYPE_EEPROM; + atmel_write16(dev, BSR, BSS_IRAM); + atmel_copy_to_card(dev, 0, mac_reader, sizeof(mac_reader)); + atmel_set_gcr(dev, GCR_REMAP); + atmel_clear_gcr(priv->dev, 0x0040); + atmel_write16(dev, BSR, BSS_SRAM); + for (i = LOOP_RETRY_LIMIT; i; i--) + if (atmel_read16(dev, MR3) & MAC_BOOT_COMPLETE) + break; + if (i == 0) { + printk(KERN_ALERT "%s: MAC failed to boot MAC address reader.\n", dev->name); + } else { + atmel_copy_to_host(dev, dev->dev_addr, atmel_read16(dev, MR2), 6); + /* got address, now squash it again until the network + interface is opened */ + if (priv->bus_type == BUS_TYPE_PCCARD) + atmel_write16(dev, GCR, 0x0060); + atmel_write16(dev, GCR, 0x0040); + rc = 1; + } + } else if (atmel_read16(dev, MR4) == 0) { + /* Mac address easy in this case. */ + priv->card_type = CARD_TYPE_PARALLEL_FLASH; + atmel_write16(dev, BSR, 1); + atmel_copy_to_host(dev, dev->dev_addr, 0xc000, 6); + atmel_write16(dev, BSR, 0x200); + rc = 1; + } else { + /* Standard firmware in flash, boot it up and ask + for the Mac Address */ + priv->card_type = CARD_TYPE_SPI_FLASH; + if (atmel_wakeup_firmware(priv) == 0) { + atmel_get_mib(priv, Mac_Address_Mib_Type, 0, dev->dev_addr, 6); + + /* got address, now squash it again until the network + interface is opened */ + if (priv->bus_type == BUS_TYPE_PCCARD) + atmel_write16(dev, GCR, 0x0060); + atmel_write16(dev, GCR, 0x0040); + rc = 1; + } + } + + if (rc) { + if (dev->dev_addr[0] == 0xFF) { + static const u8 default_mac[] = { + 0x00, 0x04, 0x25, 0x00, 0x00, 0x00 + }; + printk(KERN_ALERT "%s: *** Invalid MAC address. UPGRADE Firmware ****\n", dev->name); + memcpy(dev->dev_addr, default_mac, ETH_ALEN); + } + } + + return rc; +} + +/* Move the encyption information on the MIB structure. + This routine is for the pre-WPA firmware: later firmware has + a different format MIB and a different routine. */ +static void build_wep_mib(struct atmel_private *priv) +{ + struct { /* NB this is matched to the hardware, don't change. */ + u8 wep_is_on; + u8 default_key; /* 0..3 */ + u8 reserved; + u8 exclude_unencrypted; + + u32 WEPICV_error_count; + u32 WEP_excluded_count; + + u8 wep_keys[MAX_ENCRYPTION_KEYS][13]; + u8 encryption_level; /* 0, 1, 2 */ + u8 reserved2[3]; + } mib; + int i; + + mib.wep_is_on = priv->wep_is_on; + if (priv->wep_is_on) { + if (priv->wep_key_len[priv->default_key] > 5) + mib.encryption_level = 2; + else + mib.encryption_level = 1; + } else { + mib.encryption_level = 0; + } + + mib.default_key = priv->default_key; + mib.exclude_unencrypted = priv->exclude_unencrypted; + + for (i = 0; i < MAX_ENCRYPTION_KEYS; i++) + memcpy(mib.wep_keys[i], priv->wep_keys[i], 13); + + atmel_set_mib(priv, Mac_Wep_Mib_Type, 0, (u8 *)&mib, sizeof(mib)); +} + +static void build_wpa_mib(struct atmel_private *priv) +{ + /* This is for the later (WPA enabled) firmware. */ + + struct { /* NB this is matched to the hardware, don't change. */ + u8 cipher_default_key_value[MAX_ENCRYPTION_KEYS][MAX_ENCRYPTION_KEY_SIZE]; + u8 receiver_address[ETH_ALEN]; + u8 wep_is_on; + u8 default_key; /* 0..3 */ + u8 group_key; + u8 exclude_unencrypted; + u8 encryption_type; + u8 reserved; + + u32 WEPICV_error_count; + u32 WEP_excluded_count; + + u8 key_RSC[4][8]; + } mib; + + int i; + + mib.wep_is_on = priv->wep_is_on; + mib.exclude_unencrypted = priv->exclude_unencrypted; + memcpy(mib.receiver_address, priv->CurrentBSSID, ETH_ALEN); + + /* zero all the keys before adding in valid ones. */ + memset(mib.cipher_default_key_value, 0, sizeof(mib.cipher_default_key_value)); + + if (priv->wep_is_on) { + /* There's a comment in the Atmel code to the effect that this + is only valid when still using WEP, it may need to be set to + something to use WPA */ + memset(mib.key_RSC, 0, sizeof(mib.key_RSC)); + + mib.default_key = mib.group_key = 255; + for (i = 0; i < MAX_ENCRYPTION_KEYS; i++) { + if (priv->wep_key_len[i] > 0) { + memcpy(mib.cipher_default_key_value[i], priv->wep_keys[i], MAX_ENCRYPTION_KEY_SIZE); + if (i == priv->default_key) { + mib.default_key = i; + mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-1] = 7; + mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-2] = priv->pairwise_cipher_suite; + } else { + mib.group_key = i; + priv->group_cipher_suite = priv->pairwise_cipher_suite; + mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-1] = 1; + mib.cipher_default_key_value[i][MAX_ENCRYPTION_KEY_SIZE-2] = priv->group_cipher_suite; + } + } + } + if (mib.default_key == 255) + mib.default_key = mib.group_key != 255 ? mib.group_key : 0; + if (mib.group_key == 255) + mib.group_key = mib.default_key; + + } + + atmel_set_mib(priv, Mac_Wep_Mib_Type, 0, (u8 *)&mib, sizeof(mib)); +} + +static int reset_atmel_card(struct net_device *dev) +{ + /* do everything necessary to wake up the hardware, including + waiting for the lightning strike and throwing the knife switch.... + + set all the Mib values which matter in the card to match + their settings in the atmel_private structure. Some of these + can be altered on the fly, but many (WEP, infrastucture or ad-hoc) + can only be changed by tearing down the world and coming back through + here. + + This routine is also responsible for initialising some + hardware-specific fields in the atmel_private structure, + including a copy of the firmware's hostinfo structure + which is the route into the rest of the firmware datastructures. */ + + struct atmel_private *priv = netdev_priv(dev); + u8 configuration; + int old_state = priv->station_state; + int err = 0; + + /* data to add to the firmware names, in priority order + this implemenents firmware versioning */ + + static char *firmware_modifier[] = { + "-wpa", + "", + NULL + }; + + /* reset pccard */ + if (priv->bus_type == BUS_TYPE_PCCARD) + atmel_write16(priv->dev, GCR, 0x0060); + + /* stop card , disable interrupts */ + atmel_write16(priv->dev, GCR, 0x0040); + + if (priv->card_type == CARD_TYPE_EEPROM) { + /* copy in firmware if needed */ + const struct firmware *fw_entry = NULL; + const unsigned char *fw; + int len = priv->firmware_length; + if (!(fw = priv->firmware)) { + if (priv->firmware_type == ATMEL_FW_TYPE_NONE) { + if (strlen(priv->firmware_id) == 0) { + printk(KERN_INFO + "%s: card type is unknown: assuming at76c502 firmware is OK.\n", + dev->name); + printk(KERN_INFO + "%s: if not, use the firmware= module parameter.\n", + dev->name); + strcpy(priv->firmware_id, "atmel_at76c502.bin"); + } + err = request_firmware(&fw_entry, priv->firmware_id, priv->sys_dev); + if (err != 0) { + printk(KERN_ALERT + "%s: firmware %s is missing, cannot continue.\n", + dev->name, priv->firmware_id); + return err; + } + } else { + int fw_index = 0; + int success = 0; + + /* get firmware filename entry based on firmware type ID */ + while (fw_table[fw_index].fw_type != priv->firmware_type + && fw_table[fw_index].fw_type != ATMEL_FW_TYPE_NONE) + fw_index++; + + /* construct the actual firmware file name */ + if (fw_table[fw_index].fw_type != ATMEL_FW_TYPE_NONE) { + int i; + for (i = 0; firmware_modifier[i]; i++) { + snprintf(priv->firmware_id, 32, "%s%s.%s", fw_table[fw_index].fw_file, + firmware_modifier[i], fw_table[fw_index].fw_file_ext); + priv->firmware_id[31] = '\0'; + if (request_firmware(&fw_entry, priv->firmware_id, priv->sys_dev) == 0) { + success = 1; + break; + } + } + } + if (!success) { + printk(KERN_ALERT + "%s: firmware %s is missing, cannot start.\n", + dev->name, priv->firmware_id); + priv->firmware_id[0] = '\0'; + return -ENOENT; + } + } + + fw = fw_entry->data; + len = fw_entry->size; + } + + if (len <= 0x6000) { + atmel_write16(priv->dev, BSR, BSS_IRAM); + atmel_copy_to_card(priv->dev, 0, fw, len); + atmel_set_gcr(priv->dev, GCR_REMAP); + } else { + /* Remap */ + atmel_set_gcr(priv->dev, GCR_REMAP); + atmel_write16(priv->dev, BSR, BSS_IRAM); + atmel_copy_to_card(priv->dev, 0, fw, 0x6000); + atmel_write16(priv->dev, BSR, 0x2ff); + atmel_copy_to_card(priv->dev, 0x8000, &fw[0x6000], len - 0x6000); + } + + release_firmware(fw_entry); + } + + err = atmel_wakeup_firmware(priv); + if (err != 0) + return err; + + /* Check the version and set the correct flag for wpa stuff, + old and new firmware is incompatible. + The pre-wpa 3com firmware reports major version 5, + the wpa 3com firmware is major version 4 and doesn't need + the 3com broken-ness filter. */ + priv->use_wpa = (priv->host_info.major_version == 4); + priv->radio_on_broken = (priv->host_info.major_version == 5); + + /* unmask all irq sources */ + atmel_wmem8(priv, atmel_hi(priv, IFACE_INT_MASK_OFFSET), 0xff); + + /* int Tx system and enable Tx */ + atmel_wmem8(priv, atmel_tx(priv, TX_DESC_FLAGS_OFFSET, 0), 0); + atmel_wmem32(priv, atmel_tx(priv, TX_DESC_NEXT_OFFSET, 0), 0x80000000L); + atmel_wmem16(priv, atmel_tx(priv, TX_DESC_POS_OFFSET, 0), 0); + atmel_wmem16(priv, atmel_tx(priv, TX_DESC_SIZE_OFFSET, 0), 0); + + priv->tx_desc_free = priv->host_info.tx_desc_count; + priv->tx_desc_head = 0; + priv->tx_desc_tail = 0; + priv->tx_desc_previous = 0; + priv->tx_free_mem = priv->host_info.tx_buff_size; + priv->tx_buff_head = 0; + priv->tx_buff_tail = 0; + + configuration = atmel_rmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET)); + atmel_wmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET), + configuration | FUNC_CTRL_TxENABLE); + + /* init Rx system and enable */ + priv->rx_desc_head = 0; + + configuration = atmel_rmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET)); + atmel_wmem8(priv, atmel_hi(priv, IFACE_FUNC_CTRL_OFFSET), + configuration | FUNC_CTRL_RxENABLE); + + if (!priv->radio_on_broken) { + if (atmel_send_command_wait(priv, CMD_EnableRadio, NULL, 0) == + CMD_STATUS_REJECTED_RADIO_OFF) { + printk(KERN_INFO "%s: cannot turn the radio on.\n", + dev->name); + return -EIO; + } + } + + /* set up enough MIB values to run. */ + atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_AUTO_TX_RATE_POS, priv->auto_tx_rate); + atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_TX_PROMISCUOUS_POS, PROM_MODE_OFF); + atmel_set_mib16(priv, Mac_Mib_Type, MAC_MIB_RTS_THRESHOLD_POS, priv->rts_threshold); + atmel_set_mib16(priv, Mac_Mib_Type, MAC_MIB_FRAG_THRESHOLD_POS, priv->frag_threshold); + atmel_set_mib8(priv, Mac_Mib_Type, MAC_MIB_SHORT_RETRY_POS, priv->short_retry); + atmel_set_mib8(priv, Mac_Mib_Type, MAC_MIB_LONG_RETRY_POS, priv->long_retry); + atmel_set_mib8(priv, Local_Mib_Type, LOCAL_MIB_PREAMBLE_TYPE, priv->preamble); + atmel_set_mib(priv, Mac_Address_Mib_Type, MAC_ADDR_MIB_MAC_ADDR_POS, + priv->dev->dev_addr, 6); + atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_PS_MODE_POS, ACTIVE_MODE); + atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_LISTEN_INTERVAL_POS, 1); + atmel_set_mib16(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_BEACON_PER_POS, priv->default_beacon_period); + atmel_set_mib(priv, Phy_Mib_Type, PHY_MIB_RATE_SET_POS, atmel_basic_rates, 4); + atmel_set_mib8(priv, Mac_Mgmt_Mib_Type, MAC_MGMT_MIB_CUR_PRIVACY_POS, priv->wep_is_on); + if (priv->use_wpa) + build_wpa_mib(priv); + else + build_wep_mib(priv); + + if (old_state == STATION_STATE_READY) { + union iwreq_data wrqu; + + wrqu.data.length = 0; + wrqu.data.flags = 0; + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + eth_zero_addr(wrqu.ap_addr.sa_data); + wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL); + } + + return 0; +} + +static void atmel_send_command(struct atmel_private *priv, int command, + void *cmd, int cmd_size) +{ + if (cmd) + atmel_copy_to_card(priv->dev, atmel_co(priv, CMD_BLOCK_PARAMETERS_OFFSET), + cmd, cmd_size); + + atmel_wmem8(priv, atmel_co(priv, CMD_BLOCK_COMMAND_OFFSET), command); + atmel_wmem8(priv, atmel_co(priv, CMD_BLOCK_STATUS_OFFSET), 0); +} + +static int atmel_send_command_wait(struct atmel_private *priv, int command, + void *cmd, int cmd_size) +{ + int i, status; + + atmel_send_command(priv, command, cmd, cmd_size); + + for (i = 5000; i; i--) { + status = atmel_rmem8(priv, atmel_co(priv, CMD_BLOCK_STATUS_OFFSET)); + if (status != CMD_STATUS_IDLE && + status != CMD_STATUS_IN_PROGRESS) + break; + udelay(20); + } + + if (i == 0) { + printk(KERN_ALERT "%s: failed to contact MAC.\n", priv->dev->name); + status = CMD_STATUS_HOST_ERROR; + } else { + if (command != CMD_EnableRadio) + status = CMD_STATUS_COMPLETE; + } + + return status; +} + +static u8 atmel_get_mib8(struct atmel_private *priv, u8 type, u8 index) +{ + struct get_set_mib m; + m.type = type; + m.size = 1; + m.index = index; + + atmel_send_command_wait(priv, CMD_Get_MIB_Vars, &m, MIB_HEADER_SIZE + 1); + return atmel_rmem8(priv, atmel_co(priv, CMD_BLOCK_PARAMETERS_OFFSET + MIB_HEADER_SIZE)); +} + +static void atmel_set_mib8(struct atmel_private *priv, u8 type, u8 index, u8 data) +{ + struct get_set_mib m; + m.type = type; + m.size = 1; + m.index = index; + m.data[0] = data; + + atmel_send_command_wait(priv, CMD_Set_MIB_Vars, &m, MIB_HEADER_SIZE + 1); +} + +static void atmel_set_mib16(struct atmel_private *priv, u8 type, u8 index, + u16 data) +{ + struct get_set_mib m; + m.type = type; + m.size = 2; + m.index = index; + m.data[0] = data; + m.data[1] = data >> 8; + + atmel_send_command_wait(priv, CMD_Set_MIB_Vars, &m, MIB_HEADER_SIZE + 2); +} + +static void atmel_set_mib(struct atmel_private *priv, u8 type, u8 index, + u8 *data, int data_len) +{ + struct get_set_mib m; + m.type = type; + m.size = data_len; + m.index = index; + + if (data_len > MIB_MAX_DATA_BYTES) + printk(KERN_ALERT "%s: MIB buffer too small.\n", priv->dev->name); + + memcpy(m.data, data, data_len); + atmel_send_command_wait(priv, CMD_Set_MIB_Vars, &m, MIB_HEADER_SIZE + data_len); +} + +static void atmel_get_mib(struct atmel_private *priv, u8 type, u8 index, + u8 *data, int data_len) +{ + struct get_set_mib m; + m.type = type; + m.size = data_len; + m.index = index; + + if (data_len > MIB_MAX_DATA_BYTES) + printk(KERN_ALERT "%s: MIB buffer too small.\n", priv->dev->name); + + atmel_send_command_wait(priv, CMD_Get_MIB_Vars, &m, MIB_HEADER_SIZE + data_len); + atmel_copy_to_host(priv->dev, data, + atmel_co(priv, CMD_BLOCK_PARAMETERS_OFFSET + MIB_HEADER_SIZE), data_len); +} + +static void atmel_writeAR(struct net_device *dev, u16 data) +{ + int i; + outw(data, dev->base_addr + AR); + /* Address register appears to need some convincing..... */ + for (i = 0; data != inw(dev->base_addr + AR) && i < 10; i++) + outw(data, dev->base_addr + AR); +} + +static void atmel_copy_to_card(struct net_device *dev, u16 dest, + const unsigned char *src, u16 len) +{ + int i; + atmel_writeAR(dev, dest); + if (dest % 2) { + atmel_write8(dev, DR, *src); + src++; len--; + } + for (i = len; i > 1 ; i -= 2) { + u8 lb = *src++; + u8 hb = *src++; + atmel_write16(dev, DR, lb | (hb << 8)); + } + if (i) + atmel_write8(dev, DR, *src); +} + +static void atmel_copy_to_host(struct net_device *dev, unsigned char *dest, + u16 src, u16 len) +{ + int i; + atmel_writeAR(dev, src); + if (src % 2) { + *dest = atmel_read8(dev, DR); + dest++; len--; + } + for (i = len; i > 1 ; i -= 2) { + u16 hw = atmel_read16(dev, DR); + *dest++ = hw; + *dest++ = hw >> 8; + } + if (i) + *dest = atmel_read8(dev, DR); +} + +static void atmel_set_gcr(struct net_device *dev, u16 mask) +{ + outw(inw(dev->base_addr + GCR) | mask, dev->base_addr + GCR); +} + +static void atmel_clear_gcr(struct net_device *dev, u16 mask) +{ + outw(inw(dev->base_addr + GCR) & ~mask, dev->base_addr + GCR); +} + +static int atmel_lock_mac(struct atmel_private *priv) +{ + int i, j = 20; + retry: + for (i = 5000; i; i--) { + if (!atmel_rmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_HOST_OFFSET))) + break; + udelay(20); + } + + if (!i) + return 0; /* timed out */ + + atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 1); + if (atmel_rmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_HOST_OFFSET))) { + atmel_wmem8(priv, atmel_hi(priv, IFACE_LOCKOUT_MAC_OFFSET), 0); + if (!j--) + return 0; /* timed out */ + goto retry; + } + + return 1; +} + +static void atmel_wmem32(struct atmel_private *priv, u16 pos, u32 data) +{ + atmel_writeAR(priv->dev, pos); + atmel_write16(priv->dev, DR, data); /* card is little-endian */ + atmel_write16(priv->dev, DR, data >> 16); +} + +/***************************************************************************/ +/* There follows the source form of the MAC address reading firmware */ +/***************************************************************************/ +#if 0 + +/* Copyright 2003 Matthew T. Russotto */ +/* But derived from the Atmel 76C502 firmware written by Atmel and */ +/* included in "atmel wireless lan drivers" package */ +/** + This file is part of net.russotto.AtmelMACFW, hereto referred to + as AtmelMACFW + + AtmelMACFW 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. + + AtmelMACFW is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with AtmelMACFW; if not, see . + +****************************************************************************/ +/* This firmware should work on the 76C502 RFMD, RFMD_D, and RFMD_E */ +/* It will probably work on the 76C504 and 76C502 RFMD_3COM */ +/* It only works on SPI EEPROM versions of the card. */ + +/* This firmware initializes the SPI controller and clock, reads the MAC */ +/* address from the EEPROM into SRAM, and puts the SRAM offset of the MAC */ +/* address in MR2, and sets MR3 to 0x10 to indicate it is done */ +/* It also puts a complete copy of the EEPROM in SRAM with the offset in */ +/* MR4, for investigational purposes (maybe we can determine chip type */ +/* from that?) */ + + .org 0 + .set MRBASE, 0x8000000 + .set CPSR_INITIAL, 0xD3 /* IRQ/FIQ disabled, ARM mode, Supervisor state */ + .set CPSR_USER, 0xD1 /* IRQ/FIQ disabled, ARM mode, USER state */ + .set SRAM_BASE, 0x02000000 + .set SP_BASE, 0x0F300000 + .set UNK_BASE, 0x0F000000 /* Some internal device, but which one? */ + .set SPI_CGEN_BASE, 0x0E000000 /* Some internal device, but which one? */ + .set UNK3_BASE, 0x02014000 /* Some internal device, but which one? */ + .set STACK_BASE, 0x5600 + .set SP_SR, 0x10 + .set SP_TDRE, 2 /* status register bit -- TDR empty */ + .set SP_RDRF, 1 /* status register bit -- RDR full */ + .set SP_SWRST, 0x80 + .set SP_SPIEN, 0x1 + .set SP_CR, 0 /* control register */ + .set SP_MR, 4 /* mode register */ + .set SP_RDR, 0x08 /* Read Data Register */ + .set SP_TDR, 0x0C /* Transmit Data Register */ + .set SP_CSR0, 0x30 /* chip select registers */ + .set SP_CSR1, 0x34 + .set SP_CSR2, 0x38 + .set SP_CSR3, 0x3C + .set NVRAM_CMD_RDSR, 5 /* read status register */ + .set NVRAM_CMD_READ, 3 /* read data */ + .set NVRAM_SR_RDY, 1 /* RDY bit. This bit is inverted */ + .set SPI_8CLOCKS, 0xFF /* Writing this to the TDR doesn't do anything to the + serial output, since SO is normally high. But it + does cause 8 clock cycles and thus 8 bits to be + clocked in to the chip. See Atmel's SPI + controller (e.g. AT91M55800) timing and 4K + SPI EEPROM manuals */ + + .set NVRAM_SCRATCH, 0x02000100 /* arbitrary area for scratchpad memory */ + .set NVRAM_IMAGE, 0x02000200 + .set NVRAM_LENGTH, 0x0200 + .set MAC_ADDRESS_MIB, SRAM_BASE + .set MAC_ADDRESS_LENGTH, 6 + .set MAC_BOOT_FLAG, 0x10 + .set MR1, 0 + .set MR2, 4 + .set MR3, 8 + .set MR4, 0xC +RESET_VECTOR: + b RESET_HANDLER +UNDEF_VECTOR: + b HALT1 +SWI_VECTOR: + b HALT1 +IABORT_VECTOR: + b HALT1 +DABORT_VECTOR: +RESERVED_VECTOR: + b HALT1 +IRQ_VECTOR: + b HALT1 +FIQ_VECTOR: + b HALT1 +HALT1: b HALT1 +RESET_HANDLER: + mov r0, #CPSR_INITIAL + msr CPSR_c, r0 /* This is probably unnecessary */ + +/* I'm guessing this is initializing clock generator electronics for SPI */ + ldr r0, =SPI_CGEN_BASE + mov r1, #0 + mov r1, r1, lsl #3 + orr r1, r1, #0 + str r1, [r0] + ldr r1, [r0, #28] + bic r1, r1, #16 + str r1, [r0, #28] + mov r1, #1 + str r1, [r0, #8] + + ldr r0, =MRBASE + mov r1, #0 + strh r1, [r0, #MR1] + strh r1, [r0, #MR2] + strh r1, [r0, #MR3] + strh r1, [r0, #MR4] + + mov sp, #STACK_BASE + bl SP_INIT + mov r0, #10 + bl DELAY9 + bl GET_MAC_ADDR + bl GET_WHOLE_NVRAM + ldr r0, =MRBASE + ldr r1, =MAC_ADDRESS_MIB + strh r1, [r0, #MR2] + ldr r1, =NVRAM_IMAGE + strh r1, [r0, #MR4] + mov r1, #MAC_BOOT_FLAG + strh r1, [r0, #MR3] +HALT2: b HALT2 +.func Get_Whole_NVRAM, GET_WHOLE_NVRAM +GET_WHOLE_NVRAM: + stmdb sp!, {lr} + mov r2, #0 /* 0th bytes of NVRAM */ + mov r3, #NVRAM_LENGTH + mov r1, #0 /* not used in routine */ + ldr r0, =NVRAM_IMAGE + bl NVRAM_XFER + ldmia sp!, {lr} + bx lr +.endfunc + +.func Get_MAC_Addr, GET_MAC_ADDR +GET_MAC_ADDR: + stmdb sp!, {lr} + mov r2, #0x120 /* address of MAC Address within NVRAM */ + mov r3, #MAC_ADDRESS_LENGTH + mov r1, #0 /* not used in routine */ + ldr r0, =MAC_ADDRESS_MIB + bl NVRAM_XFER + ldmia sp!, {lr} + bx lr +.endfunc +.ltorg +.func Delay9, DELAY9 +DELAY9: + adds r0, r0, r0, LSL #3 /* r0 = r0 * 9 */ +DELAYLOOP: + beq DELAY9_done + subs r0, r0, #1 + b DELAYLOOP +DELAY9_done: + bx lr +.endfunc + +.func SP_Init, SP_INIT +SP_INIT: + mov r1, #SP_SWRST + ldr r0, =SP_BASE + str r1, [r0, #SP_CR] /* reset the SPI */ + mov r1, #0 + str r1, [r0, #SP_CR] /* release SPI from reset state */ + mov r1, #SP_SPIEN + str r1, [r0, #SP_MR] /* set the SPI to MASTER mode*/ + str r1, [r0, #SP_CR] /* enable the SPI */ + +/* My guess would be this turns on the SPI clock */ + ldr r3, =SPI_CGEN_BASE + ldr r1, [r3, #28] + orr r1, r1, #0x2000 + str r1, [r3, #28] + + ldr r1, =0x2000c01 + str r1, [r0, #SP_CSR0] + ldr r1, =0x2000201 + str r1, [r0, #SP_CSR1] + str r1, [r0, #SP_CSR2] + str r1, [r0, #SP_CSR3] + ldr r1, [r0, #SP_SR] + ldr r0, [r0, #SP_RDR] + bx lr +.endfunc +.func NVRAM_Init, NVRAM_INIT +NVRAM_INIT: + ldr r1, =SP_BASE + ldr r0, [r1, #SP_RDR] + mov r0, #NVRAM_CMD_RDSR + str r0, [r1, #SP_TDR] +SP_loop1: + ldr r0, [r1, #SP_SR] + tst r0, #SP_TDRE + beq SP_loop1 + + mov r0, #SPI_8CLOCKS + str r0, [r1, #SP_TDR] +SP_loop2: + ldr r0, [r1, #SP_SR] + tst r0, #SP_TDRE + beq SP_loop2 + + ldr r0, [r1, #SP_RDR] +SP_loop3: + ldr r0, [r1, #SP_SR] + tst r0, #SP_RDRF + beq SP_loop3 + + ldr r0, [r1, #SP_RDR] + and r0, r0, #255 + bx lr +.endfunc + +.func NVRAM_Xfer, NVRAM_XFER + /* r0 = dest address */ + /* r1 = not used */ + /* r2 = src address within NVRAM */ + /* r3 = length */ +NVRAM_XFER: + stmdb sp!, {r4, r5, lr} + mov r5, r0 /* save r0 (dest address) */ + mov r4, r3 /* save r3 (length) */ + mov r0, r2, LSR #5 /* SPI memories put A8 in the command field */ + and r0, r0, #8 + add r0, r0, #NVRAM_CMD_READ + ldr r1, =NVRAM_SCRATCH + strb r0, [r1, #0] /* save command in NVRAM_SCRATCH[0] */ + strb r2, [r1, #1] /* save low byte of source address in NVRAM_SCRATCH[1] */ +_local1: + bl NVRAM_INIT + tst r0, #NVRAM_SR_RDY + bne _local1 + mov r0, #20 + bl DELAY9 + mov r2, r4 /* length */ + mov r1, r5 /* dest address */ + mov r0, #2 /* bytes to transfer in command */ + bl NVRAM_XFER2 + ldmia sp!, {r4, r5, lr} + bx lr +.endfunc + +.func NVRAM_Xfer2, NVRAM_XFER2 +NVRAM_XFER2: + stmdb sp!, {r4, r5, r6, lr} + ldr r4, =SP_BASE + mov r3, #0 + cmp r0, #0 + bls _local2 + ldr r5, =NVRAM_SCRATCH +_local4: + ldrb r6, [r5, r3] + str r6, [r4, #SP_TDR] +_local3: + ldr r6, [r4, #SP_SR] + tst r6, #SP_TDRE + beq _local3 + add r3, r3, #1 + cmp r3, r0 /* r0 is # of bytes to send out (command+addr) */ + blo _local4 +_local2: + mov r3, #SPI_8CLOCKS + str r3, [r4, #SP_TDR] + ldr r0, [r4, #SP_RDR] +_local5: + ldr r0, [r4, #SP_SR] + tst r0, #SP_RDRF + beq _local5 + ldr r0, [r4, #SP_RDR] /* what's this byte? It's the byte read while writing the TDR -- nonsense, because the NVRAM doesn't read and write at the same time */ + mov r0, #0 + cmp r2, #0 /* r2 is # of bytes to copy in */ + bls _local6 +_local7: + ldr r5, [r4, #SP_SR] + tst r5, #SP_TDRE + beq _local7 + str r3, [r4, #SP_TDR] /* r3 has SPI_8CLOCKS */ +_local8: + ldr r5, [r4, #SP_SR] + tst r5, #SP_RDRF + beq _local8 + ldr r5, [r4, #SP_RDR] /* but didn't we read this byte above? */ + strb r5, [r1], #1 /* postindexed */ + add r0, r0, #1 + cmp r0, r2 + blo _local7 /* since we don't send another address, the NVRAM must be capable of sequential reads */ +_local6: + mov r0, #200 + bl DELAY9 + ldmia sp!, {r4, r5, r6, lr} + bx lr +#endif diff --git a/drivers/net/wireless/atmel/atmel.h b/drivers/net/wireless/atmel/atmel.h new file mode 100644 index 000000000000..96f7318cbb04 --- /dev/null +++ b/drivers/net/wireless/atmel/atmel.h @@ -0,0 +1,43 @@ +/*** -*- linux-c -*- ********************************************************** + + Driver for Atmel at76c502 at76c504 and at76c506 wireless cards. + + Copyright 2005 Dan Williams and Red Hat, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This software is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Atmel wireless lan drivers; if not, see + . + +******************************************************************************/ + +#ifndef _ATMEL_H +#define _ATMEL_H + +typedef enum { + ATMEL_FW_TYPE_NONE = 0, + ATMEL_FW_TYPE_502, + ATMEL_FW_TYPE_502D, + ATMEL_FW_TYPE_502E, + ATMEL_FW_TYPE_502_3COM, + ATMEL_FW_TYPE_504, + ATMEL_FW_TYPE_504_2958, + ATMEL_FW_TYPE_504A_2958, + ATMEL_FW_TYPE_506 +} AtmelFWType; + +struct net_device *init_atmel_card(unsigned short, unsigned long, const AtmelFWType, struct device *, + int (*present_func)(void *), void * ); +void stop_atmel_card( struct net_device *); +int atmel_open( struct net_device * ); + +#endif diff --git a/drivers/net/wireless/atmel/atmel_cs.c b/drivers/net/wireless/atmel/atmel_cs.c new file mode 100644 index 000000000000..7afc9c5329fb --- /dev/null +++ b/drivers/net/wireless/atmel/atmel_cs.c @@ -0,0 +1,286 @@ +/*** -*- linux-c -*- ********************************************************** + + Driver for Atmel at76c502 at76c504 and at76c506 wireless cards. + + Copyright 2000-2001 ATMEL Corporation. + Copyright 2003 Simon Kelley. + + This code was developed from version 2.1.1 of the Atmel drivers, + released by Atmel corp. under the GPL in December 2002. It also + includes code from the Linux aironet drivers (C) Benjamin Reed, + and the Linux PCMCIA package, (C) David Hinds. + + For all queries about this code, please contact the current author, + Simon Kelley and not Atmel Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This software is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Atmel wireless lan drivers; if not, see + . + +******************************************************************************/ + +#ifdef __IN_PCMCIA_PACKAGE__ +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "atmel.h" + + +/*====================================================================*/ + +MODULE_AUTHOR("Simon Kelley"); +MODULE_DESCRIPTION("Support for Atmel at76c50x 802.11 wireless ethernet cards."); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("Atmel at76c50x PCMCIA cards"); + +/*====================================================================*/ + +static int atmel_config(struct pcmcia_device *link); +static void atmel_release(struct pcmcia_device *link); + +static void atmel_detach(struct pcmcia_device *p_dev); + +struct local_info { + struct net_device *eth_dev; +}; + +static int atmel_probe(struct pcmcia_device *p_dev) +{ + struct local_info *local; + + dev_dbg(&p_dev->dev, "atmel_attach()\n"); + + /* Allocate space for private device-specific data */ + local = kzalloc(sizeof(*local), GFP_KERNEL); + if (!local) + return -ENOMEM; + + p_dev->priv = local; + + return atmel_config(p_dev); +} /* atmel_attach */ + +static void atmel_detach(struct pcmcia_device *link) +{ + dev_dbg(&link->dev, "atmel_detach\n"); + + atmel_release(link); + + kfree(link->priv); +} + +/* Call-back function to interrogate PCMCIA-specific information + about the current existence of the card */ +static int card_present(void *arg) +{ + struct pcmcia_device *link = (struct pcmcia_device *)arg; + + if (pcmcia_dev_present(link)) + return 1; + + return 0; +} + +static int atmel_config_check(struct pcmcia_device *p_dev, void *priv_data) +{ + if (p_dev->config_index == 0) + return -EINVAL; + + return pcmcia_request_io(p_dev); +} + +static int atmel_config(struct pcmcia_device *link) +{ + struct local_info *dev; + int ret; + const struct pcmcia_device_id *did; + + dev = link->priv; + did = dev_get_drvdata(&link->dev); + + dev_dbg(&link->dev, "atmel_config\n"); + + link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_VPP | + CONF_AUTO_AUDIO | CONF_AUTO_SET_IO; + + if (pcmcia_loop_config(link, atmel_config_check, NULL)) + goto failed; + + if (!link->irq) { + dev_err(&link->dev, "atmel: cannot assign IRQ: check that CONFIG_ISA is set in kernel config."); + goto failed; + } + + ret = pcmcia_enable_device(link); + if (ret) + goto failed; + + ((struct local_info *)link->priv)->eth_dev = + init_atmel_card(link->irq, + link->resource[0]->start, + did ? did->driver_info : ATMEL_FW_TYPE_NONE, + &link->dev, + card_present, + link); + if (!((struct local_info *)link->priv)->eth_dev) + goto failed; + + + return 0; + + failed: + atmel_release(link); + return -ENODEV; +} + +static void atmel_release(struct pcmcia_device *link) +{ + struct net_device *dev = ((struct local_info *)link->priv)->eth_dev; + + dev_dbg(&link->dev, "atmel_release\n"); + + if (dev) + stop_atmel_card(dev); + ((struct local_info *)link->priv)->eth_dev = NULL; + + pcmcia_disable_device(link); +} + +static int atmel_suspend(struct pcmcia_device *link) +{ + struct local_info *local = link->priv; + + netif_device_detach(local->eth_dev); + + return 0; +} + +static int atmel_resume(struct pcmcia_device *link) +{ + struct local_info *local = link->priv; + + atmel_open(local->eth_dev); + netif_device_attach(local->eth_dev); + + return 0; +} + +/*====================================================================*/ +/* We use the driver_info field to store the correct firmware type for a card. */ + +#define PCMCIA_DEVICE_MANF_CARD_INFO(manf, card, info) { \ + .match_flags = PCMCIA_DEV_ID_MATCH_MANF_ID| \ + PCMCIA_DEV_ID_MATCH_CARD_ID, \ + .manf_id = (manf), \ + .card_id = (card), \ + .driver_info = (kernel_ulong_t)(info), } + +#define PCMCIA_DEVICE_PROD_ID12_INFO(v1, v2, vh1, vh2, info) { \ + .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \ + PCMCIA_DEV_ID_MATCH_PROD_ID2, \ + .prod_id = { (v1), (v2), NULL, NULL }, \ + .prod_id_hash = { (vh1), (vh2), 0, 0 }, \ + .driver_info = (kernel_ulong_t)(info), } + +static const struct pcmcia_device_id atmel_ids[] = { + PCMCIA_DEVICE_MANF_CARD_INFO(0x0101, 0x0620, ATMEL_FW_TYPE_502_3COM), + PCMCIA_DEVICE_MANF_CARD_INFO(0x0101, 0x0696, ATMEL_FW_TYPE_502_3COM), + PCMCIA_DEVICE_MANF_CARD_INFO(0x01bf, 0x3302, ATMEL_FW_TYPE_502E), + PCMCIA_DEVICE_MANF_CARD_INFO(0xd601, 0x0007, ATMEL_FW_TYPE_502), + PCMCIA_DEVICE_PROD_ID12_INFO("11WAVE", "11WP611AL-E", 0x9eb2da1f, 0xc9a0d3f9, ATMEL_FW_TYPE_502E), + PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C502AR", 0xabda4164, 0x41b37e1f, ATMEL_FW_TYPE_502), + PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C502AR_D", 0xabda4164, 0x3675d704, ATMEL_FW_TYPE_502D), + PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C502AR_E", 0xabda4164, 0x4172e792, ATMEL_FW_TYPE_502E), + PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C504_R", 0xabda4164, 0x917f3d72, ATMEL_FW_TYPE_504_2958), + PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C504", 0xabda4164, 0x5040670a, ATMEL_FW_TYPE_504), + PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C504A", 0xabda4164, 0xe15ed87f, ATMEL_FW_TYPE_504A_2958), + PCMCIA_DEVICE_PROD_ID12_INFO("BT", "Voyager 1020 Laptop Adapter", 0xae49b86a, 0x1e957cd5, ATMEL_FW_TYPE_502), + PCMCIA_DEVICE_PROD_ID12_INFO("CNet", "CNWLC 11Mbps Wireless PC Card V-5", 0xbc477dde, 0x502fae6b, ATMEL_FW_TYPE_502E), + PCMCIA_DEVICE_PROD_ID12_INFO("IEEE 802.11b", "Wireless LAN PC Card", 0x5b878724, 0x122f1df6, ATMEL_FW_TYPE_502), + PCMCIA_DEVICE_PROD_ID12_INFO("IEEE 802.11b", "Wireless LAN Card S", 0x5b878724, 0x5fba533a, ATMEL_FW_TYPE_504_2958), + PCMCIA_DEVICE_PROD_ID12_INFO("OEM", "11Mbps Wireless LAN PC Card V-3", 0xfea54c90, 0x1c5b0f68, ATMEL_FW_TYPE_502), + PCMCIA_DEVICE_PROD_ID12_INFO("SMC", "2632W", 0xc4f8b18b, 0x30f38774, ATMEL_FW_TYPE_502D), + PCMCIA_DEVICE_PROD_ID12_INFO("SMC", "2632W-V2", 0xc4f8b18b, 0x172d1377, ATMEL_FW_TYPE_502), + PCMCIA_DEVICE_PROD_ID12_INFO("Wireless", "PC_CARD", 0xa407ecdd, 0x119f6314, ATMEL_FW_TYPE_502D), + PCMCIA_DEVICE_PROD_ID12_INFO("WLAN", "802.11b PC CARD", 0x575c516c, 0xb1f6dbc4, ATMEL_FW_TYPE_502D), + PCMCIA_DEVICE_PROD_ID12_INFO("LG", "LW2100N", 0xb474d43a, 0x6b1fec94, ATMEL_FW_TYPE_502E), + PCMCIA_DEVICE_NULL +}; + +MODULE_DEVICE_TABLE(pcmcia, atmel_ids); + +static struct pcmcia_driver atmel_driver = { + .owner = THIS_MODULE, + .name = "atmel_cs", + .probe = atmel_probe, + .remove = atmel_detach, + .id_table = atmel_ids, + .suspend = atmel_suspend, + .resume = atmel_resume, +}; +module_pcmcia_driver(atmel_driver); + +/* + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + In addition: + + 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. +*/ diff --git a/drivers/net/wireless/atmel/atmel_pci.c b/drivers/net/wireless/atmel/atmel_pci.c new file mode 100644 index 000000000000..bcf1f274a251 --- /dev/null +++ b/drivers/net/wireless/atmel/atmel_pci.c @@ -0,0 +1,76 @@ +/*** -*- linux-c -*- ********************************************************** + + Driver for Atmel at76c502 at76c504 and at76c506 wireless cards. + + Copyright 2004 Simon Kelley. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This software is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Atmel wireless lan drivers; if not, see + . + +******************************************************************************/ +#include +#include +#include +#include +#include "atmel.h" + +MODULE_AUTHOR("Simon Kelley"); +MODULE_DESCRIPTION("Support for Atmel at76c50x 802.11 wireless ethernet cards."); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("Atmel at76c506 PCI wireless cards"); + +static const struct pci_device_id card_ids[] = { + { 0x1114, 0x0506, PCI_ANY_ID, PCI_ANY_ID }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, card_ids); + +static int atmel_pci_probe(struct pci_dev *, const struct pci_device_id *); +static void atmel_pci_remove(struct pci_dev *); + +static struct pci_driver atmel_driver = { + .name = "atmel", + .id_table = card_ids, + .probe = atmel_pci_probe, + .remove = atmel_pci_remove, +}; + + +static int atmel_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *pent) +{ + struct net_device *dev; + + if (pci_enable_device(pdev)) + return -ENODEV; + + pci_set_master(pdev); + + dev = init_atmel_card(pdev->irq, pdev->resource[1].start, + ATMEL_FW_TYPE_506, + &pdev->dev, NULL, NULL); + if (!dev) + return -ENODEV; + + pci_set_drvdata(pdev, dev); + return 0; +} + +static void atmel_pci_remove(struct pci_dev *pdev) +{ + stop_atmel_card(pci_get_drvdata(pdev)); +} + +module_pci_driver(atmel_driver); diff --git a/drivers/net/wireless/atmel_cs.c b/drivers/net/wireless/atmel_cs.c deleted file mode 100644 index 7afc9c5329fb..000000000000 --- a/drivers/net/wireless/atmel_cs.c +++ /dev/null @@ -1,286 +0,0 @@ -/*** -*- linux-c -*- ********************************************************** - - Driver for Atmel at76c502 at76c504 and at76c506 wireless cards. - - Copyright 2000-2001 ATMEL Corporation. - Copyright 2003 Simon Kelley. - - This code was developed from version 2.1.1 of the Atmel drivers, - released by Atmel corp. under the GPL in December 2002. It also - includes code from the Linux aironet drivers (C) Benjamin Reed, - and the Linux PCMCIA package, (C) David Hinds. - - For all queries about this code, please contact the current author, - Simon Kelley and not Atmel Corporation. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This software is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Atmel wireless lan drivers; if not, see - . - -******************************************************************************/ - -#ifdef __IN_PCMCIA_PACKAGE__ -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include "atmel.h" - - -/*====================================================================*/ - -MODULE_AUTHOR("Simon Kelley"); -MODULE_DESCRIPTION("Support for Atmel at76c50x 802.11 wireless ethernet cards."); -MODULE_LICENSE("GPL"); -MODULE_SUPPORTED_DEVICE("Atmel at76c50x PCMCIA cards"); - -/*====================================================================*/ - -static int atmel_config(struct pcmcia_device *link); -static void atmel_release(struct pcmcia_device *link); - -static void atmel_detach(struct pcmcia_device *p_dev); - -struct local_info { - struct net_device *eth_dev; -}; - -static int atmel_probe(struct pcmcia_device *p_dev) -{ - struct local_info *local; - - dev_dbg(&p_dev->dev, "atmel_attach()\n"); - - /* Allocate space for private device-specific data */ - local = kzalloc(sizeof(*local), GFP_KERNEL); - if (!local) - return -ENOMEM; - - p_dev->priv = local; - - return atmel_config(p_dev); -} /* atmel_attach */ - -static void atmel_detach(struct pcmcia_device *link) -{ - dev_dbg(&link->dev, "atmel_detach\n"); - - atmel_release(link); - - kfree(link->priv); -} - -/* Call-back function to interrogate PCMCIA-specific information - about the current existence of the card */ -static int card_present(void *arg) -{ - struct pcmcia_device *link = (struct pcmcia_device *)arg; - - if (pcmcia_dev_present(link)) - return 1; - - return 0; -} - -static int atmel_config_check(struct pcmcia_device *p_dev, void *priv_data) -{ - if (p_dev->config_index == 0) - return -EINVAL; - - return pcmcia_request_io(p_dev); -} - -static int atmel_config(struct pcmcia_device *link) -{ - struct local_info *dev; - int ret; - const struct pcmcia_device_id *did; - - dev = link->priv; - did = dev_get_drvdata(&link->dev); - - dev_dbg(&link->dev, "atmel_config\n"); - - link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_VPP | - CONF_AUTO_AUDIO | CONF_AUTO_SET_IO; - - if (pcmcia_loop_config(link, atmel_config_check, NULL)) - goto failed; - - if (!link->irq) { - dev_err(&link->dev, "atmel: cannot assign IRQ: check that CONFIG_ISA is set in kernel config."); - goto failed; - } - - ret = pcmcia_enable_device(link); - if (ret) - goto failed; - - ((struct local_info *)link->priv)->eth_dev = - init_atmel_card(link->irq, - link->resource[0]->start, - did ? did->driver_info : ATMEL_FW_TYPE_NONE, - &link->dev, - card_present, - link); - if (!((struct local_info *)link->priv)->eth_dev) - goto failed; - - - return 0; - - failed: - atmel_release(link); - return -ENODEV; -} - -static void atmel_release(struct pcmcia_device *link) -{ - struct net_device *dev = ((struct local_info *)link->priv)->eth_dev; - - dev_dbg(&link->dev, "atmel_release\n"); - - if (dev) - stop_atmel_card(dev); - ((struct local_info *)link->priv)->eth_dev = NULL; - - pcmcia_disable_device(link); -} - -static int atmel_suspend(struct pcmcia_device *link) -{ - struct local_info *local = link->priv; - - netif_device_detach(local->eth_dev); - - return 0; -} - -static int atmel_resume(struct pcmcia_device *link) -{ - struct local_info *local = link->priv; - - atmel_open(local->eth_dev); - netif_device_attach(local->eth_dev); - - return 0; -} - -/*====================================================================*/ -/* We use the driver_info field to store the correct firmware type for a card. */ - -#define PCMCIA_DEVICE_MANF_CARD_INFO(manf, card, info) { \ - .match_flags = PCMCIA_DEV_ID_MATCH_MANF_ID| \ - PCMCIA_DEV_ID_MATCH_CARD_ID, \ - .manf_id = (manf), \ - .card_id = (card), \ - .driver_info = (kernel_ulong_t)(info), } - -#define PCMCIA_DEVICE_PROD_ID12_INFO(v1, v2, vh1, vh2, info) { \ - .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \ - PCMCIA_DEV_ID_MATCH_PROD_ID2, \ - .prod_id = { (v1), (v2), NULL, NULL }, \ - .prod_id_hash = { (vh1), (vh2), 0, 0 }, \ - .driver_info = (kernel_ulong_t)(info), } - -static const struct pcmcia_device_id atmel_ids[] = { - PCMCIA_DEVICE_MANF_CARD_INFO(0x0101, 0x0620, ATMEL_FW_TYPE_502_3COM), - PCMCIA_DEVICE_MANF_CARD_INFO(0x0101, 0x0696, ATMEL_FW_TYPE_502_3COM), - PCMCIA_DEVICE_MANF_CARD_INFO(0x01bf, 0x3302, ATMEL_FW_TYPE_502E), - PCMCIA_DEVICE_MANF_CARD_INFO(0xd601, 0x0007, ATMEL_FW_TYPE_502), - PCMCIA_DEVICE_PROD_ID12_INFO("11WAVE", "11WP611AL-E", 0x9eb2da1f, 0xc9a0d3f9, ATMEL_FW_TYPE_502E), - PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C502AR", 0xabda4164, 0x41b37e1f, ATMEL_FW_TYPE_502), - PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C502AR_D", 0xabda4164, 0x3675d704, ATMEL_FW_TYPE_502D), - PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C502AR_E", 0xabda4164, 0x4172e792, ATMEL_FW_TYPE_502E), - PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C504_R", 0xabda4164, 0x917f3d72, ATMEL_FW_TYPE_504_2958), - PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C504", 0xabda4164, 0x5040670a, ATMEL_FW_TYPE_504), - PCMCIA_DEVICE_PROD_ID12_INFO("ATMEL", "AT76C504A", 0xabda4164, 0xe15ed87f, ATMEL_FW_TYPE_504A_2958), - PCMCIA_DEVICE_PROD_ID12_INFO("BT", "Voyager 1020 Laptop Adapter", 0xae49b86a, 0x1e957cd5, ATMEL_FW_TYPE_502), - PCMCIA_DEVICE_PROD_ID12_INFO("CNet", "CNWLC 11Mbps Wireless PC Card V-5", 0xbc477dde, 0x502fae6b, ATMEL_FW_TYPE_502E), - PCMCIA_DEVICE_PROD_ID12_INFO("IEEE 802.11b", "Wireless LAN PC Card", 0x5b878724, 0x122f1df6, ATMEL_FW_TYPE_502), - PCMCIA_DEVICE_PROD_ID12_INFO("IEEE 802.11b", "Wireless LAN Card S", 0x5b878724, 0x5fba533a, ATMEL_FW_TYPE_504_2958), - PCMCIA_DEVICE_PROD_ID12_INFO("OEM", "11Mbps Wireless LAN PC Card V-3", 0xfea54c90, 0x1c5b0f68, ATMEL_FW_TYPE_502), - PCMCIA_DEVICE_PROD_ID12_INFO("SMC", "2632W", 0xc4f8b18b, 0x30f38774, ATMEL_FW_TYPE_502D), - PCMCIA_DEVICE_PROD_ID12_INFO("SMC", "2632W-V2", 0xc4f8b18b, 0x172d1377, ATMEL_FW_TYPE_502), - PCMCIA_DEVICE_PROD_ID12_INFO("Wireless", "PC_CARD", 0xa407ecdd, 0x119f6314, ATMEL_FW_TYPE_502D), - PCMCIA_DEVICE_PROD_ID12_INFO("WLAN", "802.11b PC CARD", 0x575c516c, 0xb1f6dbc4, ATMEL_FW_TYPE_502D), - PCMCIA_DEVICE_PROD_ID12_INFO("LG", "LW2100N", 0xb474d43a, 0x6b1fec94, ATMEL_FW_TYPE_502E), - PCMCIA_DEVICE_NULL -}; - -MODULE_DEVICE_TABLE(pcmcia, atmel_ids); - -static struct pcmcia_driver atmel_driver = { - .owner = THIS_MODULE, - .name = "atmel_cs", - .probe = atmel_probe, - .remove = atmel_detach, - .id_table = atmel_ids, - .suspend = atmel_suspend, - .resume = atmel_resume, -}; -module_pcmcia_driver(atmel_driver); - -/* - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - In addition: - - 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. -*/ diff --git a/drivers/net/wireless/atmel_pci.c b/drivers/net/wireless/atmel_pci.c deleted file mode 100644 index bcf1f274a251..000000000000 --- a/drivers/net/wireless/atmel_pci.c +++ /dev/null @@ -1,76 +0,0 @@ -/*** -*- linux-c -*- ********************************************************** - - Driver for Atmel at76c502 at76c504 and at76c506 wireless cards. - - Copyright 2004 Simon Kelley. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This software is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Atmel wireless lan drivers; if not, see - . - -******************************************************************************/ -#include -#include -#include -#include -#include "atmel.h" - -MODULE_AUTHOR("Simon Kelley"); -MODULE_DESCRIPTION("Support for Atmel at76c50x 802.11 wireless ethernet cards."); -MODULE_LICENSE("GPL"); -MODULE_SUPPORTED_DEVICE("Atmel at76c506 PCI wireless cards"); - -static const struct pci_device_id card_ids[] = { - { 0x1114, 0x0506, PCI_ANY_ID, PCI_ANY_ID }, - { 0, } -}; - -MODULE_DEVICE_TABLE(pci, card_ids); - -static int atmel_pci_probe(struct pci_dev *, const struct pci_device_id *); -static void atmel_pci_remove(struct pci_dev *); - -static struct pci_driver atmel_driver = { - .name = "atmel", - .id_table = card_ids, - .probe = atmel_pci_probe, - .remove = atmel_pci_remove, -}; - - -static int atmel_pci_probe(struct pci_dev *pdev, - const struct pci_device_id *pent) -{ - struct net_device *dev; - - if (pci_enable_device(pdev)) - return -ENODEV; - - pci_set_master(pdev); - - dev = init_atmel_card(pdev->irq, pdev->resource[1].start, - ATMEL_FW_TYPE_506, - &pdev->dev, NULL, NULL); - if (!dev) - return -ENODEV; - - pci_set_drvdata(pdev, dev); - return 0; -} - -static void atmel_pci_remove(struct pci_dev *pdev) -{ - stop_atmel_card(pci_get_drvdata(pdev)); -} - -module_pci_driver(atmel_driver);